mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-21 22:48:16 +01:00
loading extension from external python packages
This commit is contained in:
parent
28f87c6c3f
commit
ce2d942771
23 changed files with 633 additions and 992 deletions
1249
.idea/workspace.xml
1249
.idea/workspace.xml
File diff suppressed because it is too large
Load diff
3
config/plugin_list.txt
Normal file
3
config/plugin_list.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
cbpi-actor
|
||||
cbpi-actor
|
||||
cbpi-actor
|
|
@ -9,10 +9,11 @@ from core.database.model import ActorModel
|
|||
from core.http_endpoints.http_api import HttpAPI
|
||||
from utils.encoder import ComplexEncoder
|
||||
|
||||
auth = True
|
||||
|
||||
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:
|
||||
"""
|
||||
:param request:
|
||||
|
@ -27,7 +28,7 @@ class ActorHttp(HttpAPI):
|
|||
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:
|
||||
"""
|
||||
:param request:
|
||||
|
@ -37,7 +38,7 @@ class ActorHttp(HttpAPI):
|
|||
await self.cbpi.bus.fire(topic="actor/%s/off" % id, id=id)
|
||||
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:
|
||||
"""
|
||||
:param request:
|
||||
|
@ -78,6 +79,7 @@ class ActorController(ActorHttp, CRUDController):
|
|||
await super(ActorController, self).init()
|
||||
|
||||
for id, value in self.cache.items():
|
||||
|
||||
await self._init_actor(value)
|
||||
|
||||
async def _init_actor(self, actor):
|
||||
|
@ -85,10 +87,13 @@ class ActorController(ActorHttp, CRUDController):
|
|||
cfg = actor.config.copy()
|
||||
cfg.update(dict(cbpi=self.cbpi, id=id, name=actor.name))
|
||||
clazz = self.types[actor.type]["class"];
|
||||
|
||||
self.cache[actor.id].instance = clazz(**cfg)
|
||||
self.cache[actor.id].instance.init()
|
||||
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):
|
||||
|
|
|
@ -51,7 +51,7 @@ class ConfigController(ConfigHTTPController):
|
|||
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))
|
||||
if name in self.cache and self.cache[name].value is not None:
|
||||
return self.cache[name].value
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import imp
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from importlib import import_module
|
||||
from pprint import pprint
|
||||
|
||||
import aiohttp
|
||||
import yaml
|
||||
from aiohttp import web
|
||||
|
||||
from cbpi_api import *
|
||||
|
||||
from core.utils.utils import load_config, json_dumps
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -42,20 +38,17 @@ class PluginController():
|
|||
if os.path.isdir("./core/extension/" + filename) is False or filename == "__pycache__":
|
||||
continue
|
||||
try:
|
||||
|
||||
logger.info("Trying to load plugin %s" % filename)
|
||||
|
||||
data = load_config("./core/extension/%s/config.yaml" % filename)
|
||||
|
||||
|
||||
if(data.get("version") == 4):
|
||||
|
||||
self.modules[filename] = import_module("core.extension.%s" % (filename))
|
||||
self.modules[filename].setup(self.cbpi)
|
||||
|
||||
logger.info("Plugin %s loaded successful" % filename)
|
||||
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:
|
||||
|
@ -63,14 +56,25 @@ class PluginController():
|
|||
|
||||
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:
|
||||
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)
|
||||
|
@ -99,11 +103,10 @@ class PluginController():
|
|||
:param clazz: actor class
|
||||
:return: None
|
||||
'''
|
||||
|
||||
logger.info("Register %s Class %s" % (name, clazz.__name__))
|
||||
if issubclass(clazz, CBPiActor):
|
||||
self.cbpi.actor.types[name] = {"class": clazz, "config": self._parse_props(clazz)}
|
||||
|
||||
|
||||
if issubclass(clazz, CBPiSensor):
|
||||
self.cbpi.sensor.types[name] = {"class": clazz, "config": self._parse_props(clazz)}
|
||||
|
||||
|
|
|
@ -48,5 +48,4 @@ class SensorController(CRUDController, HttpAPI):
|
|||
|
||||
|
||||
async def get_value(self, id):
|
||||
|
||||
return self.cache[id].instance.value
|
|
@ -22,7 +22,7 @@ from core.controller.plugin_controller import PluginController
|
|||
from core.controller.sensor_controller import SensorController
|
||||
from core.controller.system_controller import SystemController
|
||||
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.utils import *
|
||||
from core.websocket import WebSocket
|
||||
|
@ -50,7 +50,7 @@ class CraftBeerPi():
|
|||
|
||||
logger.info("Init CraftBeerPI")
|
||||
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.initializer = []
|
||||
self.shutdown = False
|
||||
|
@ -65,7 +65,7 @@ class CraftBeerPi():
|
|||
setup(self.app, self)
|
||||
|
||||
|
||||
self.bus = EventBus(self.app.loop, self)
|
||||
self.bus = CBPiEventBus(self.app.loop, self)
|
||||
self.ws = WebSocket(self)
|
||||
self.actor = ActorController(self)
|
||||
self.sensor = SensorController(self)
|
||||
|
@ -75,14 +75,7 @@ class CraftBeerPi():
|
|||
self.kettle = KettleController(self)
|
||||
self.step = StepController(self)
|
||||
self.notification = NotificationController(self)
|
||||
|
||||
|
||||
|
||||
|
||||
self.login = Login(self)
|
||||
|
||||
self.plugin.load_plugins()
|
||||
self.plugin.load_plugins_from_evn()
|
||||
self.register_events(self.ws)
|
||||
|
||||
|
||||
|
@ -202,9 +195,11 @@ class CraftBeerPi():
|
|||
sub = web.Application()
|
||||
sub.add_routes(routes)
|
||||
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)
|
||||
else:
|
||||
|
||||
|
||||
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
|
||||
f = Figlet(font='big')
|
||||
print("")
|
||||
print(f.renderText("%s %s" % (self.config.get("name"), self.config.get("version"))))
|
||||
print("")
|
||||
async def init_database(app):
|
||||
await DBModel.test_connection()
|
||||
logger.info("\n%s" % f.renderText("%s %s" % (self.config.get("name"), self.config.get("version"))))
|
||||
|
||||
async def init_controller(app):
|
||||
await self.sensor.init()
|
||||
await self.step.init()
|
||||
await self.actor.init()
|
||||
await self.kettle.init()
|
||||
await self.config2.init()
|
||||
async def init_serivces(self):
|
||||
|
||||
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())
|
||||
|
||||
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)
|
||||
logger.info(self.sensor.info())
|
||||
logger.info(self.actor.info())
|
||||
|
||||
self._swagger_setup()
|
||||
|
||||
return self.app
|
||||
|
||||
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))
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import json
|
||||
|
||||
import aiosqlite
|
||||
|
||||
from core.database.orm_framework import DBModel
|
||||
TEST_DB = "./craftbeerpi.db"
|
||||
DATABASE_FILE = "./craftbeerpi.db"
|
||||
|
||||
class ActorModel(DBModel):
|
||||
__fields__ = ["name", "type", "config"]
|
||||
|
@ -11,8 +9,6 @@ class ActorModel(DBModel):
|
|||
__json_fields__ = ["config"]
|
||||
|
||||
|
||||
|
||||
|
||||
class SensorModel(DBModel):
|
||||
__fields__ = ["name", "type", "config"]
|
||||
__table_name__ = "sensor"
|
||||
|
@ -37,7 +33,7 @@ class StepModel(DBModel):
|
|||
|
||||
@classmethod
|
||||
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))
|
||||
await db.commit()
|
||||
|
||||
|
@ -45,7 +41,7 @@ class StepModel(DBModel):
|
|||
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 = 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:
|
||||
|
@ -57,48 +53,7 @@ class StepModel(DBModel):
|
|||
|
||||
@classmethod
|
||||
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__)
|
||||
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()
|
||||
'''
|
|
@ -1,10 +1,8 @@
|
|||
import json
|
||||
|
||||
import aiosqlite
|
||||
import os
|
||||
# Thats the name of the database file
|
||||
TEST_DB = "./craftbeerpi.db"
|
||||
|
||||
DATABASE_FILE = "./craftbeerpi.db"
|
||||
|
||||
|
||||
class DBModel(object):
|
||||
|
@ -36,7 +34,7 @@ class DBModel(object):
|
|||
@classmethod
|
||||
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)
|
||||
this_directory = os.path.dirname(__file__)
|
||||
qry = open(os.path.join(this_directory, '../sql/create_table_user.sql'), 'r').read()
|
||||
|
@ -49,7 +47,7 @@ class DBModel(object):
|
|||
result = []
|
||||
else:
|
||||
result = {}
|
||||
async with aiosqlite.connect(TEST_DB) as db:
|
||||
async with aiosqlite.connect(DATABASE_FILE) as db:
|
||||
|
||||
if cls.__order_by__ is not None:
|
||||
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
|
||||
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 = DBModel.dict_factory
|
||||
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
|
||||
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.commit()
|
||||
|
||||
@classmethod
|
||||
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:
|
||||
query = "INSERT INTO %s (%s, %s) VALUES (?, %s)" % (
|
||||
cls.__table_name__,
|
||||
|
@ -129,7 +127,7 @@ class DBModel(object):
|
|||
|
||||
@classmethod
|
||||
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__)
|
||||
|
||||
data = ()
|
||||
|
@ -150,4 +148,3 @@ class DBModel(object):
|
|||
for idx, col in enumerate(cursor.description):
|
||||
d[col[0]] = row[idx]
|
||||
return d
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import inspect
|
|||
import logging
|
||||
|
||||
|
||||
class EventBus(object):
|
||||
class CBPiEventBus(object):
|
||||
class Node(object):
|
||||
__slots__ = '_children', '_content'
|
||||
|
||||
|
@ -33,9 +33,9 @@ class EventBus(object):
|
|||
self.timeout = timeout
|
||||
for key, value in results.items():
|
||||
if value.done() is True:
|
||||
self.results[key] = EventBus.Result(value.result(), True)
|
||||
self.results[key] = CBPiEventBus.Result(value.result(), True)
|
||||
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))
|
||||
|
||||
async def fire(self, topic: str, timeout=1, **kwargs):
|
||||
print("FIRE")
|
||||
|
||||
futures = {}
|
||||
|
||||
async def wait(futures):
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import logging
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
|
||||
from cbpi_api import *
|
||||
|
||||
from core.utils.utils import json_dumps
|
||||
|
||||
|
||||
class HttpAPI():
|
||||
def __init__(self, cbpi):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -20,12 +20,16 @@ class Login():
|
|||
|
||||
@request_mapping(path="/login",name="Login", method="POST", auth_required=False)
|
||||
async def login_view(self, request):
|
||||
|
||||
print("TRY LOGIN")
|
||||
params = await request.post()
|
||||
|
||||
|
||||
user = params.get('username', None)
|
||||
password = params.get('password', None)
|
||||
print("UUSEr", user, password, str(self.db[user]))
|
||||
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
|
||||
await auth.remember(request, user)
|
||||
|
|
|
@ -77,31 +77,4 @@ class WebSocket:
|
|||
|
||||
self.logger.info("Web Socket Close")
|
||||
|
||||
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
|
||||
'''
|
||||
return ws
|
BIN
craftbeerpi.db
BIN
craftbeerpi.db
Binary file not shown.
|
@ -9,10 +9,10 @@ As a main framework CraftBeerPi is based on `aiohttp`
|
|||
|
||||
* 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.
|
||||
This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily.
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@
|
|||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="standards.html">Standard & Guidelines</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#websocket">WebSocket</a></li>
|
||||
</ul>
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
<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 & Guidelines</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="#websocket">WebSocket</a></li>
|
||||
</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>
|
||||
</ul>
|
||||
<div class="section" id="eventbus">
|
||||
<h3>EventBus<a class="headerlink" href="#eventbus" title="Permalink to this headline">¶</a></h3>
|
||||
<p>One core concept of CraftBeerPi 4.x is the EventBus.
|
||||
<h3>CBPiEventBus<a class="headerlink" href="#eventbus" title="Permalink to this headline">¶</a></h3>
|
||||
<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.
|
||||
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>
|
||||
|
|
|
@ -9,10 +9,10 @@ As a main framework CraftBeerPi is based on `aiohttp`
|
|||
|
||||
* 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.
|
||||
This makes sure that all components are loosely coupled. New plugins can listen on events and extend or change the functionality easily.
|
||||
|
||||
|
|
89
main2.py
89
main2.py
|
@ -1,90 +1,7 @@
|
|||
import asyncio
|
||||
import re
|
||||
|
||||
from core.eventbus3 import EventBus
|
||||
|
||||
|
||||
async def waiter(event):
|
||||
print('waiting for it ...')
|
||||
await asyncio.sleep(4)
|
||||
print('... got it!')
|
||||
event.set()
|
||||
with open('./config/plugin_list.txt') as f:
|
||||
required = f.read().splitlines()
|
||||
|
||||
async def main(loop):
|
||||
# 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))
|
||||
print(required)
|
||||
|
||||
|
|
1
run.py
1
run.py
|
@ -4,5 +4,4 @@ from core.craftbeerpi import CraftBeerPi
|
|||
|
||||
|
||||
cbpi = CraftBeerPi()
|
||||
cbpi.setup()
|
||||
cbpi.start()
|
|
@ -1,3 +1,4 @@
|
|||
import aiohttp
|
||||
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
|
||||
|
||||
from core.craftbeerpi import CraftBeerPi
|
||||
|
@ -11,14 +12,20 @@ class MyAppTestCase(AioHTTPTestCase):
|
|||
async def get_application(self):
|
||||
self.cbpi = CraftBeerPi()
|
||||
self.cbpi.setup()
|
||||
|
||||
await self.cbpi.init_serivces()
|
||||
return self.cbpi.app
|
||||
|
||||
|
||||
@unittest_run_loop
|
||||
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
|
||||
i = await self.cbpi.actor.get_one(1)
|
||||
assert i.instance.state is True
|
||||
|
|
Loading…
Reference in a new issue