loading extension from external python packages

This commit is contained in:
manuel83 2018-12-31 00:22:00 +01:00
parent 28f87c6c3f
commit ce2d942771
23 changed files with 633 additions and 992 deletions

File diff suppressed because it is too large Load diff

3
config/plugin_list.txt Normal file
View file

@ -0,0 +1,3 @@
cbpi-actor
cbpi-actor
cbpi-actor

View file

@ -9,10 +9,11 @@ from core.database.model import ActorModel
from core.http_endpoints.http_api import HttpAPI from core.http_endpoints.http_api import HttpAPI
from utils.encoder import ComplexEncoder from utils.encoder import ComplexEncoder
auth = True
class ActorHttp(HttpAPI): class ActorHttp(HttpAPI):
@request_mapping(path="/{id:\d+}/on", auth_required=False) @request_mapping(path="/{id:\d+}/on", auth_required=auth)
async def http_on(self, request) -> web.Response: async def http_on(self, request) -> web.Response:
""" """
:param request: :param request:
@ -27,7 +28,7 @@ class ActorHttp(HttpAPI):
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/{id:\d+}/off", auth_required=False) @request_mapping(path="/{id:\d+}/off", auth_required=auth)
async def http_off(self, request) -> web.Response: async def http_off(self, request) -> web.Response:
""" """
:param request: :param request:
@ -37,7 +38,7 @@ class ActorHttp(HttpAPI):
await self.cbpi.bus.fire(topic="actor/%s/off" % id, id=id) await self.cbpi.bus.fire(topic="actor/%s/off" % id, id=id)
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/{id:\d+}/toggle", auth_required=False) @request_mapping(path="/{id:\d+}/toggle", auth_required=auth)
async def http_toggle(self, request) -> web.Response: async def http_toggle(self, request) -> web.Response:
""" """
:param request: :param request:
@ -78,6 +79,7 @@ class ActorController(ActorHttp, CRUDController):
await super(ActorController, self).init() await super(ActorController, self).init()
for id, value in self.cache.items(): for id, value in self.cache.items():
await self._init_actor(value) await self._init_actor(value)
async def _init_actor(self, actor): async def _init_actor(self, actor):
@ -85,10 +87,13 @@ class ActorController(ActorHttp, CRUDController):
cfg = actor.config.copy() cfg = actor.config.copy()
cfg.update(dict(cbpi=self.cbpi, id=id, name=actor.name)) cfg.update(dict(cbpi=self.cbpi, id=id, name=actor.name))
clazz = self.types[actor.type]["class"]; clazz = self.types[actor.type]["class"];
self.cache[actor.id].instance = clazz(**cfg) self.cache[actor.id].instance = clazz(**cfg)
self.cache[actor.id].instance.init() self.cache[actor.id].instance.init()
await self.cbpi.bus.fire(topic="actor/%s/initialized" % actor.id, id=actor.id) await self.cbpi.bus.fire(topic="actor/%s/initialized" % actor.id, id=actor.id)
else:
self.logger.error("Actor type '%s' not found (Available Actor Types: %s)" % (actor.type, ', '.join(self.types.keys())))
async def _stop_actor(self, actor): async def _stop_actor(self, actor):

View file

@ -51,7 +51,7 @@ class ConfigController(ConfigHTTPController):
self.cache[value.name] = value self.cache[value.name] = value
async def get(self, name, default=None): def get(self, name, default=None):
self.logger.info("GET CONFIG VALUE %s (default %s)" % (name,default)) self.logger.info("GET CONFIG VALUE %s (default %s)" % (name,default))
if name in self.cache and self.cache[name].value is not None: if name in self.cache and self.cache[name].value is not None:
return self.cache[name].value return self.cache[name].value

View file

@ -1,16 +1,12 @@
import imp
import importlib
import logging import logging
import os import os
import sys
from importlib import import_module from importlib import import_module
from pprint import pprint
import aiohttp import aiohttp
import yaml import yaml
from aiohttp import web from aiohttp import web
from cbpi_api import * from cbpi_api import *
from core.utils.utils import load_config, json_dumps from core.utils.utils import load_config, json_dumps
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -42,20 +38,17 @@ class PluginController():
if os.path.isdir("./core/extension/" + filename) is False or filename == "__pycache__": if os.path.isdir("./core/extension/" + filename) is False or filename == "__pycache__":
continue continue
try: try:
logger.info("Trying to load plugin %s" % filename) logger.info("Trying to load plugin %s" % filename)
data = load_config("./core/extension/%s/config.yaml" % filename) data = load_config("./core/extension/%s/config.yaml" % filename)
if(data.get("version") == 4): if(data.get("version") == 4):
self.modules[filename] = import_module("core.extension.%s" % (filename)) self.modules[filename] = import_module("core.extension.%s" % (filename))
self.modules[filename].setup(self.cbpi) self.modules[filename].setup(self.cbpi)
logger.info("Plugin %s loaded successful" % filename) logger.info("Plugin %s loaded successful" % filename)
else: else:
logger.warning("Plguin %s is not supporting version 4" % filename) logger.warning("Plugin %s is not supporting version 4" % filename)
except Exception as e: except Exception as e:
@ -63,14 +56,25 @@ class PluginController():
def load_plugins_from_evn(self): def load_plugins_from_evn(self):
plugins = []
plugins = ["cbpi-actor"] with open('./config/plugin_list.txt') as f:
plugins = f.read().splitlines()
plugins = list(set(plugins))
for p in plugins: for p in plugins:
logger.debug("Load Plugin %s" % p)
try:
logger.info("Try to load plugin: %s " % p)
self.modules[p] = import_module(p)
self.modules[p].setup(self.cbpi)
self.modules[p] = import_module(p)
self.modules[p].setup(self.cbpi)
logger.info("Plugin %s loaded successfully" % p)
except Exception as e:
logger.error("FAILED to load plugin %s " % p)
logger.error(e)
@request_mapping(path="/plugins", method="GET", auth_required=False) @request_mapping(path="/plugins", method="GET", auth_required=False)
@ -99,11 +103,10 @@ class PluginController():
:param clazz: actor class :param clazz: actor class
:return: None :return: None
''' '''
logger.info("Register %s Class %s" % (name, clazz.__name__))
if issubclass(clazz, CBPiActor): if issubclass(clazz, CBPiActor):
self.cbpi.actor.types[name] = {"class": clazz, "config": self._parse_props(clazz)} self.cbpi.actor.types[name] = {"class": clazz, "config": self._parse_props(clazz)}
if issubclass(clazz, CBPiSensor): if issubclass(clazz, CBPiSensor):
self.cbpi.sensor.types[name] = {"class": clazz, "config": self._parse_props(clazz)} self.cbpi.sensor.types[name] = {"class": clazz, "config": self._parse_props(clazz)}

View file

@ -48,5 +48,4 @@ class SensorController(CRUDController, HttpAPI):
async def get_value(self, id): async def get_value(self, id):
return self.cache[id].instance.value return self.cache[id].instance.value

View file

@ -22,7 +22,7 @@ from core.controller.plugin_controller import PluginController
from core.controller.sensor_controller import SensorController from core.controller.sensor_controller import SensorController
from core.controller.system_controller import SystemController from core.controller.system_controller import SystemController
from core.database.model import DBModel from core.database.model import DBModel
from core.eventbus import EventBus from core.eventbus import CBPiEventBus
from core.http_endpoints.http_login import Login from core.http_endpoints.http_login import Login
from core.utils import * from core.utils import *
from core.websocket import WebSocket from core.websocket import WebSocket
@ -50,7 +50,7 @@ class CraftBeerPi():
logger.info("Init CraftBeerPI") logger.info("Init CraftBeerPI")
policy = auth.SessionTktAuthentication(urandom(32), 60, include_ip=True) policy = auth.SessionTktAuthentication(urandom(32), 60, include_ip=True)
middlewares = [session_middleware(EncryptedCookieStorage(urandom(32))), auth.auth_middleware(policy)] middlewares = [web.normalize_path_middleware(), session_middleware(EncryptedCookieStorage(urandom(32))), auth.auth_middleware(policy)]
self.app = web.Application(middlewares=middlewares) self.app = web.Application(middlewares=middlewares)
self.initializer = [] self.initializer = []
self.shutdown = False self.shutdown = False
@ -65,7 +65,7 @@ class CraftBeerPi():
setup(self.app, self) setup(self.app, self)
self.bus = EventBus(self.app.loop, self) self.bus = CBPiEventBus(self.app.loop, self)
self.ws = WebSocket(self) self.ws = WebSocket(self)
self.actor = ActorController(self) self.actor = ActorController(self)
self.sensor = SensorController(self) self.sensor = SensorController(self)
@ -75,14 +75,7 @@ class CraftBeerPi():
self.kettle = KettleController(self) self.kettle = KettleController(self)
self.step = StepController(self) self.step = StepController(self)
self.notification = NotificationController(self) self.notification = NotificationController(self)
self.login = Login(self) self.login = Login(self)
self.plugin.load_plugins()
self.plugin.load_plugins_from_evn()
self.register_events(self.ws) self.register_events(self.ws)
@ -202,9 +195,11 @@ class CraftBeerPi():
sub = web.Application() sub = web.Application()
sub.add_routes(routes) sub.add_routes(routes)
if static is not None: if static is not None:
sub.add_routes([web.static('/static', static, show_index=True)]) sub.add_routes([web.static('/static', static, show_index=False)])
self.app.add_subapp(url_prefix, sub) self.app.add_subapp(url_prefix, sub)
else: else:
self.app.add_routes(routes) self.app.add_routes(routes)
@ -249,53 +244,43 @@ class CraftBeerPi():
#async def init_database(app):
#self.app.on_startup.append(call_initializer)
async def call_initializer(self, app):
self.initializer = sorted(self.initializer, key=lambda k: k['order'])
for i in self.initializer:
logger.info("CALL INITIALIZER %s - %s " % (i["name"], i["method"].__name__))
await i["method"]()
def _print_logo(self):
from pyfiglet import Figlet from pyfiglet import Figlet
f = Figlet(font='big') f = Figlet(font='big')
print("") logger.info("\n%s" % f.renderText("%s %s" % (self.config.get("name"), self.config.get("version"))))
print(f.renderText("%s %s" % (self.config.get("name"), self.config.get("version"))))
print("")
async def init_database(app):
await DBModel.test_connection()
async def init_controller(app): async def init_serivces(self):
await self.sensor.init()
await self.step.init()
await self.actor.init()
await self.kettle.init()
await self.config2.init()
self._print_logo()
await DBModel.test_connection()
await self.config2.init()
self.plugin.load_plugins()
self.plugin.load_plugins_from_evn()
await self.sensor.init()
await self.step.init()
await self.actor.init()
await self.kettle.init()
await self.call_initializer(self.app)
print(self.sensor.info()) logger.info(self.sensor.info())
logger.info(self.actor.info())
print(self.actor.info())
import pprint
#pprint.pprint(self.bus.dump())
async def load_plugins(app):
#await PluginController.load_plugin_list()
#self.plugin.load_plugins()
pass
async def call_initializer(app):
self.initializer = sorted(self.initializer, key=lambda k: k['order'])
for i in self.initializer:
logger.info("CALL INITIALIZER %s - %s " % (i["name"], i["method"].__name__))
await i["method"]()
self.app.on_startup.append(init_database)
self.app.on_startup.append(call_initializer)
self.app.on_startup.append(load_plugins)
self.app.on_startup.append(init_controller)
self._swagger_setup() self._swagger_setup()
return self.app
def start(self): def start(self):
web.run_app(self.app, port=self.config.get("port", 8080)) web.run_app(self.init_serivces(), port=self.config.get("port", 8080))

View file

@ -1,9 +1,7 @@
import json import json
import aiosqlite import aiosqlite
from core.database.orm_framework import DBModel from core.database.orm_framework import DBModel
TEST_DB = "./craftbeerpi.db" DATABASE_FILE = "./craftbeerpi.db"
class ActorModel(DBModel): class ActorModel(DBModel):
__fields__ = ["name", "type", "config"] __fields__ = ["name", "type", "config"]
@ -11,8 +9,6 @@ class ActorModel(DBModel):
__json_fields__ = ["config"] __json_fields__ = ["config"]
class SensorModel(DBModel): class SensorModel(DBModel):
__fields__ = ["name", "type", "config"] __fields__ = ["name", "type", "config"]
__table_name__ = "sensor" __table_name__ = "sensor"
@ -37,7 +33,7 @@ class StepModel(DBModel):
@classmethod @classmethod
async def update_step_state(cls, step_id, state): async def update_step_state(cls, step_id, state):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
cursor = await db.execute("UPDATE %s SET stepstate = ? WHERE id = ?" % cls.__table_name__, (json.dumps(state), step_id)) cursor = await db.execute("UPDATE %s SET stepstate = ? WHERE id = ?" % cls.__table_name__, (json.dumps(state), step_id))
await db.commit() await db.commit()
@ -45,7 +41,7 @@ class StepModel(DBModel):
async def get_by_state(cls, state, order=True): async def get_by_state(cls, state, order=True):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
db.row_factory = aiosqlite.Row db.row_factory = aiosqlite.Row
db.row_factory = DBModel.dict_factory db.row_factory = DBModel.dict_factory
async with db.execute("SELECT * FROM %s WHERE state = ? ORDER BY %s.'order'" % (cls.__table_name__, cls.__table_name__,), state) as cursor: async with db.execute("SELECT * FROM %s WHERE state = ? ORDER BY %s.'order'" % (cls.__table_name__, cls.__table_name__,), state) as cursor:
@ -57,48 +53,7 @@ class StepModel(DBModel):
@classmethod @classmethod
async def reset_all_steps(cls): async def reset_all_steps(cls):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
cursor = await db.execute("UPDATE %s SET state = 'I', stepstate = NULL , start = NULL, end = NULL " % cls.__table_name__) cursor = await db.execute("UPDATE %s SET state = 'I', stepstate = NULL , start = NULL, end = NULL " % cls.__table_name__)
await db.commit() await db.commit()
'''
@classmethod
def sort(cls, new_order):
cur = get_db().cursor()
for key, value in new_order.items():
cur.execute("UPDATE %s SET '%s' = ? WHERE id = ?" % (cls.__table_name__, "order"), (value, key))
get_db().commit()
@classmethod
def get_max_order(cls):
cur = get_db().cursor()
cur.execute("SELECT max(step.'order') as 'order' FROM %s" % cls.__table_name__)
r = cur.fetchone()
return r.get("order")
@classmethod
def delete_all(cls):
cur = get_db().cursor()
cur.execute("DELETE FROM %s" % cls.__table_name__)
get_db().commit()
@classmethod
def get_by_state(cls, state, order=True):
cur = get_db().cursor()
cur.execute("SELECT * FROM %s WHERE state = ? ORDER BY %s.'order'" % (cls.__table_name__, cls.__table_name__,), state)
r = cur.fetchone()
if r is not None:
return cls(r)
else:
return None
@classmethod
def reset_all_steps(cls):
cur = get_db().cursor()
cur.execute("UPDATE %s SET state = 'I', stepstate = NULL , start = NULL, end = NULL " % cls.__table_name__)
get_db().commit()
'''

View file

@ -1,10 +1,8 @@
import json import json
import aiosqlite import aiosqlite
import os import os
# Thats the name of the database file
TEST_DB = "./craftbeerpi.db"
DATABASE_FILE = "./craftbeerpi.db"
class DBModel(object): class DBModel(object):
@ -36,7 +34,7 @@ class DBModel(object):
@classmethod @classmethod
async def test_connection(self): async def test_connection(self):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
assert isinstance(db, aiosqlite.Connection) assert isinstance(db, aiosqlite.Connection)
this_directory = os.path.dirname(__file__) this_directory = os.path.dirname(__file__)
qry = open(os.path.join(this_directory, '../sql/create_table_user.sql'), 'r').read() qry = open(os.path.join(this_directory, '../sql/create_table_user.sql'), 'r').read()
@ -49,7 +47,7 @@ class DBModel(object):
result = [] result = []
else: else:
result = {} result = {}
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
if cls.__order_by__ is not None: if cls.__order_by__ is not None:
sql = "SELECT * FROM %s ORDER BY %s.'%s'" % (cls.__table_name__, cls.__table_name__, cls.__order_by__) sql = "SELECT * FROM %s ORDER BY %s.'%s'" % (cls.__table_name__, cls.__table_name__, cls.__order_by__)
@ -71,7 +69,7 @@ class DBModel(object):
@classmethod @classmethod
async def get_one(cls, id): async def get_one(cls, id):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
db.row_factory = aiosqlite.Row db.row_factory = aiosqlite.Row
db.row_factory = DBModel.dict_factory db.row_factory = DBModel.dict_factory
async with db.execute("SELECT * FROM %s WHERE %s = ?" % (cls.__table_name__, cls.__priamry_key__), (id,)) as cursor: async with db.execute("SELECT * FROM %s WHERE %s = ?" % (cls.__table_name__, cls.__priamry_key__), (id,)) as cursor:
@ -83,14 +81,14 @@ class DBModel(object):
@classmethod @classmethod
async def delete(cls, id): async def delete(cls, id):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
await db.execute("DELETE FROM %s WHERE %s = ? " % (cls.__table_name__, cls.__priamry_key__), (id,)) await db.execute("DELETE FROM %s WHERE %s = ? " % (cls.__table_name__, cls.__priamry_key__), (id,))
await db.commit() await db.commit()
@classmethod @classmethod
async def insert(cls, **kwargs): async def insert(cls, **kwargs):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
if cls.__priamry_key__ is not None and cls.__priamry_key__ in kwargs: if cls.__priamry_key__ is not None and cls.__priamry_key__ in kwargs:
query = "INSERT INTO %s (%s, %s) VALUES (?, %s)" % ( query = "INSERT INTO %s (%s, %s) VALUES (?, %s)" % (
cls.__table_name__, cls.__table_name__,
@ -129,7 +127,7 @@ class DBModel(object):
@classmethod @classmethod
async def update(cls, **kwargs): async def update(cls, **kwargs):
async with aiosqlite.connect(TEST_DB) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
query = 'UPDATE %s SET %s WHERE %s = ?' % (cls.__table_name__, ', '.join("'%s' = ?" % str(x) for x in cls.__fields__), cls.__priamry_key__) query = 'UPDATE %s SET %s WHERE %s = ?' % (cls.__table_name__, ', '.join("'%s' = ?" % str(x) for x in cls.__fields__), cls.__priamry_key__)
data = () data = ()
@ -150,4 +148,3 @@ class DBModel(object):
for idx, col in enumerate(cursor.description): for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx] d[col[0]] = row[idx]
return d return d

View file

@ -3,7 +3,7 @@ import inspect
import logging import logging
class EventBus(object): class CBPiEventBus(object):
class Node(object): class Node(object):
__slots__ = '_children', '_content' __slots__ = '_children', '_content'
@ -33,9 +33,9 @@ class EventBus(object):
self.timeout = timeout self.timeout = timeout
for key, value in results.items(): for key, value in results.items():
if value.done() is True: if value.done() is True:
self.results[key] = EventBus.Result(value.result(), True) self.results[key] = CBPiEventBus.Result(value.result(), True)
else: else:
self.results[key] = EventBus.Result(None, False) self.results[key] = CBPiEventBus.Result(None, False)
@ -104,7 +104,7 @@ class EventBus(object):
self.loop.create_task(self.fire(topic=topic, timeout=timeout, **kwargs)) self.loop.create_task(self.fire(topic=topic, timeout=timeout, **kwargs))
async def fire(self, topic: str, timeout=1, **kwargs): async def fire(self, topic: str, timeout=1, **kwargs):
print("FIRE")
futures = {} futures = {}
async def wait(futures): async def wait(futures):

View file

@ -1,13 +1,8 @@
import logging import logging
from aiohttp import web from aiohttp import web
from cbpi_api import * from cbpi_api import *
from core.utils.utils import json_dumps from core.utils.utils import json_dumps
class HttpAPI(): class HttpAPI():
def __init__(self, cbpi): def __init__(self, cbpi):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)

View file

@ -20,12 +20,16 @@ class Login():
@request_mapping(path="/login",name="Login", method="POST", auth_required=False) @request_mapping(path="/login",name="Login", method="POST", auth_required=False)
async def login_view(self, request): async def login_view(self, request):
print("TRY LOGIN")
params = await request.post() params = await request.post()
user = params.get('username', None) user = params.get('username', None)
password = params.get('password', None)
print("UUSEr", user, password, str(self.db[user]))
if (user in self.db and if (user in self.db and
params.get('password', None) == self.db[user]): params.get('password', None) == str(self.db[user])):
# User is in our database, remember their login details # User is in our database, remember their login details
await auth.remember(request, user) await auth.remember(request, user)

View file

@ -78,30 +78,3 @@ class WebSocket:
self.logger.info("Web Socket Close") self.logger.info("Web Socket Close")
return ws return ws
'''
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
_ws.append(ws)
c = len(_ws) - 1
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data == 'close':
await ws.close()
else:
await ws.send_str(msg.data)
elif msg.type == aiohttp.WSMsgType.ERROR:
print('ws connection closed with exception %s' %
ws.exception())
del _ws[c]
print('websocket connection closed')
return ws
'''

Binary file not shown.

View file

@ -9,10 +9,10 @@ As a main framework CraftBeerPi is based on `aiohttp`
* aioHTTP https://aiohttp.readthedocs.io/en/stable/ * aioHTTP https://aiohttp.readthedocs.io/en/stable/
EventBus CBPiEventBus
-------- --------
One core concept of CraftBeerPi 4.x is the EventBus. One core concept of CraftBeerPi 4.x is the CBPiEventBus.
It should be avoided to call method on a controller directly. Events should be fired and listener methods should be used. It should be avoided to call method on a controller directly. Events should be fired and listener methods should be used.
This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily. This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily.

View file

@ -187,7 +187,7 @@
</li> </li>
<li class="toctree-l1"><a class="reference internal" href="standards.html">Standard &amp; Guidelines</a><ul> <li class="toctree-l1"><a class="reference internal" href="standards.html">Standard &amp; Guidelines</a><ul>
<li class="toctree-l2"><a class="reference internal" href="standards.html#python">Python</a><ul> <li class="toctree-l2"><a class="reference internal" href="standards.html#python">Python</a><ul>
<li class="toctree-l3"><a class="reference internal" href="standards.html#eventbus">EventBus</a></li> <li class="toctree-l3"><a class="reference internal" href="standards.html#eventbus">CBPiEventBus</a></li>
<li class="toctree-l3"><a class="reference internal" href="standards.html#http-endpoints">HTTP Endpoints</a></li> <li class="toctree-l3"><a class="reference internal" href="standards.html#http-endpoints">HTTP Endpoints</a></li>
<li class="toctree-l3"><a class="reference internal" href="standards.html#websocket">WebSocket</a></li> <li class="toctree-l3"><a class="reference internal" href="standards.html#websocket">WebSocket</a></li>
</ul> </ul>

View file

@ -87,7 +87,7 @@
<li class="toctree-l1"><a class="reference internal" href="properties.html">Properties</a></li> <li class="toctree-l1"><a class="reference internal" href="properties.html">Properties</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Standard &amp; Guidelines</a><ul> <li class="toctree-l1 current"><a class="current reference internal" href="#">Standard &amp; Guidelines</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#python">Python</a><ul> <li class="toctree-l2"><a class="reference internal" href="#python">Python</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#eventbus">EventBus</a></li> <li class="toctree-l3"><a class="reference internal" href="#eventbus">CBPiEventBus</a></li>
<li class="toctree-l3"><a class="reference internal" href="#http-endpoints">HTTP Endpoints</a></li> <li class="toctree-l3"><a class="reference internal" href="#http-endpoints">HTTP Endpoints</a></li>
<li class="toctree-l3"><a class="reference internal" href="#websocket">WebSocket</a></li> <li class="toctree-l3"><a class="reference internal" href="#websocket">WebSocket</a></li>
</ul> </ul>
@ -170,8 +170,8 @@ As a main framework CraftBeerPi is based on <cite>aiohttp</cite></p>
<li>aioHTTP <a class="reference external" href="https://aiohttp.readthedocs.io/en/stable/">https://aiohttp.readthedocs.io/en/stable/</a></li> <li>aioHTTP <a class="reference external" href="https://aiohttp.readthedocs.io/en/stable/">https://aiohttp.readthedocs.io/en/stable/</a></li>
</ul> </ul>
<div class="section" id="eventbus"> <div class="section" id="eventbus">
<h3>EventBus<a class="headerlink" href="#eventbus" title="Permalink to this headline"></a></h3> <h3>CBPiEventBus<a class="headerlink" href="#eventbus" title="Permalink to this headline"></a></h3>
<p>One core concept of CraftBeerPi 4.x is the EventBus. <p>One core concept of CraftBeerPi 4.x is the CBPiEventBus.
It should be avoided to call method on a controller directly. Events should be fired and listener methods should be used. It should be avoided to call method on a controller directly. Events should be fired and listener methods should be used.
This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily.</p> This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily.</p>
<p>Here an example how to fire an event</p> <p>Here an example how to fire an event</p>

View file

@ -9,10 +9,10 @@ As a main framework CraftBeerPi is based on `aiohttp`
* aioHTTP https://aiohttp.readthedocs.io/en/stable/ * aioHTTP https://aiohttp.readthedocs.io/en/stable/
EventBus CBPiEventBus
-------- --------
One core concept of CraftBeerPi 4.x is the EventBus. One core concept of CraftBeerPi 4.x is the CBPiEventBus.
It should be avoided to call method on a controller directly. Events should be fired and listener methods should be used. It should be avoided to call method on a controller directly. Events should be fired and listener methods should be used.
This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily. This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily.

View file

@ -1,90 +1,7 @@
import asyncio
import re
from core.eventbus3 import EventBus
async def waiter(event): with open('./config/plugin_list.txt') as f:
print('waiting for it ...') required = f.read().splitlines()
await asyncio.sleep(4)
print('... got it!')
event.set()
async def main(loop): print(required)
# Create an Event object.
event = asyncio.Event()
# Spawn a Task to wait until 'event' is set.
#waiter_task = asyncio.create_task(waiter(event))
waiter_task = loop.create_task(waiter(event))
print("WAIT FOR EVENT")
await event.wait()
# Wait until the waiter task is finished.
await waiter_task
#loop = asyncio.get_event_loop()
#loop.run_until_complete(main(loop))
#loop.close()
bus = EventBus(None)
def test(**kwargs):
print("")
print("------------> HALLO WILD CARD")
print("")
print(hex(id(test)))
print("BUS",bus)
def test2(**kwargs):
print("------------> HALLO NAME")
class Name():
def test(self, **kwargs):
print("---->OK")
bus.unregister(self.test)
print("#################### ID2", hex(id(n.test)))
n = Name()
print("the ID", hex(id(n.test)))
id1 = bus.register("test/#", n.test)
print("the ID2", hex(id(n.test)))
if n.test == n.test:
print("SAME")
id1 = bus.register("test/#", test)
id2 = bus.register("test/name", test2)
print(id1, id2)
from gtts import gTTS
print(hex(id(test2)))
print(bus.get_callbacks("test/name"))
bus.fire("test/name")
bus.fire("test/name")
bus.unregister(test2)
bus.fire("test/name")
m0 = re.match("kettle_logic_(\d+)", "kettle_1logic_22")
print(m0)
#print(m0.group(1))

1
run.py
View file

@ -4,5 +4,4 @@ from core.craftbeerpi import CraftBeerPi
cbpi = CraftBeerPi() cbpi = CraftBeerPi()
cbpi.setup()
cbpi.start() cbpi.start()

View file

View file

@ -1,3 +1,4 @@
import aiohttp
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
from core.craftbeerpi import CraftBeerPi from core.craftbeerpi import CraftBeerPi
@ -11,14 +12,20 @@ class MyAppTestCase(AioHTTPTestCase):
async def get_application(self): async def get_application(self):
self.cbpi = CraftBeerPi() self.cbpi = CraftBeerPi()
self.cbpi.setup() self.cbpi.setup()
await self.cbpi.init_serivces()
return self.cbpi.app return self.cbpi.app
@unittest_run_loop @unittest_run_loop
async def test_example(self): async def test_example(self):
resp = await self.client.request("GET", "/actor/1/on") resp = await self.client.post(path="/login", data={"username": "cbpi", "password": "123"})
print("resp.status",resp.status)
assert resp.status == 200
resp = await self.client.request("GET", "/actor/1/on")
print("resp.status", resp.status)
assert resp.status == 204 assert resp.status == 204
i = await self.cbpi.actor.get_one(1) i = await self.cbpi.actor.get_one(1)
assert i.instance.state is True assert i.instance.state is True