mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-09 17:07:43 +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 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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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))
|
||||||
|
|
|
@ -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()
|
|
||||||
'''
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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__)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
||||||
'''
|
|
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/
|
* 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.
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="toctree-l1"><a class="reference internal" href="standards.html">Standard & Guidelines</a><ul>
|
<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-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>
|
||||||
|
|
|
@ -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 & Guidelines</a><ul>
|
<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-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>
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
89
main2.py
89
main2.py
|
@ -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
1
run.py
|
@ -4,5 +4,4 @@ from core.craftbeerpi import CraftBeerPi
|
||||||
|
|
||||||
|
|
||||||
cbpi = CraftBeerPi()
|
cbpi = CraftBeerPi()
|
||||||
cbpi.setup()
|
|
||||||
cbpi.start()
|
cbpi.start()
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue