rework database

This commit is contained in:
Manuel Fritsch 2021-01-22 23:25:20 +01:00
parent 05e08d0dc6
commit 296c2c69f0
46 changed files with 1402 additions and 2390 deletions

View file

@ -1,2 +1,3 @@
recursive-include cbpi/config * recursive-include cbpi/config *
recursive-include cbpi/extension * recursive-include cbpi/extension *
recursive-include cbpi/static *

View file

@ -0,0 +1 @@
__version__ = "4.0.0.8"

View file

@ -1,4 +1,5 @@
__all__ = ["CBPiActor", __all__ = ["CBPiActor",
"CBPiActor2",
"CBPiExtension", "CBPiExtension",
"Property", "Property",
"PropertyType", "PropertyType",
@ -9,12 +10,14 @@ __all__ = ["CBPiActor",
"parameters", "parameters",
"background_task", "background_task",
"CBPiKettleLogic", "CBPiKettleLogic",
"CBPiKettleLogic2",
"CBPiSimpleStep", "CBPiSimpleStep",
"CBPiException", "CBPiException",
"KettleException", "KettleException",
"SensorException", "SensorException",
"ActorException", "ActorException",
"CBPiSensor", "CBPiSensor",
"CBPiSensor2",
"CBPiStep"] "CBPiStep"]
from cbpi.api.actor import * from cbpi.api.actor import *

View file

@ -1,8 +1,8 @@
from abc import ABCMeta from abc import ABCMeta
import asyncio
from cbpi.api.extension import CBPiExtension from cbpi.api.extension import CBPiExtension
__all__ = ["CBPiActor"] __all__ = ["CBPiActor", "CBPiActor2"]
import logging import logging
@ -45,3 +45,57 @@ class CBPiActor(CBPiExtension, metaclass=ABCMeta):
pass pass
class CBPiActor2(metaclass=ABCMeta):
def __init__(self, cbpi, id, props):
self.cbpi = cbpi
self.id = id
self.props = props
self.logger = logging.getLogger(__file__)
self.data_logger = None
self.state = False
self.running = False
def init(self):
pass
def log_data(self, value):
self.cbpi.log.log_data(self.id, value)
async def run(self):
while self.running:
print("RUNNING ACTOR")
await asyncio.sleep(1)
def get_state(self):
print("########STATE", self.state)
return dict(state=self.state)
async def start(self):
print("START UP ACTOR")
self.running = True
async def stop(self):
self.running = False
async def on(self, power):
'''
Code to switch the actor on. Power is provided as integer value
:param power: power value between 0 and 100
:return: None
'''
pass
async def off(self):
'''
Code to switch the actor off
:return: None
'''
pass

View file

@ -1,5 +1,7 @@
from cbpi.api.extension import CBPiExtension from cbpi.api.extension import CBPiExtension
from abc import ABCMeta
import logging
import asyncio
class CBPiKettleLogic(CBPiExtension): class CBPiKettleLogic(CBPiExtension):
@ -36,3 +38,60 @@ class CBPiKettleLogic(CBPiExtension):
''' '''
pass pass
class CBPiKettleLogic2(metaclass=ABCMeta):
def __init__(self, cbpi, id, props):
self.cbpi = cbpi
self.id = id
self.props = props
self.logger = logging.getLogger(__file__)
self.data_logger = None
self.state = False
self.running = False
def init(self):
pass
def log_data(self, value):
self.cbpi.log.log_data(self.id, value)
async def run(self):
while self.running:
print("RUNNING KETTLE")
await asyncio.sleep(1)
def get_state(self):
print("########STATE", self.state)
return dict(state=self.state)
async def start(self):
print("")
print("")
print("")
print("##################START UP KETTLE")
print("")
print("")
self.running = True
async def stop(self):
self.running = False
async def on(self, power):
'''
Code to switch the actor on. Power is provided as integer value
:param power: power value between 0 and 100
:return: None
'''
pass
async def off(self):
'''
Code to switch the actor off
:return: None
'''
pass

View file

@ -1,6 +1,5 @@
import logging import logging
from abc import ABCMeta from abc import abstractmethod, ABCMeta
from cbpi.api.extension import CBPiExtension from cbpi.api.extension import CBPiExtension
@ -32,3 +31,40 @@ class CBPiSensor(CBPiExtension, metaclass=ABCMeta):
def get_unit(self): def get_unit(self):
pass pass
class CBPiSensor2(metaclass=ABCMeta):
def __init__(self, cbpi, id, props):
self.cbpi = cbpi
self.id = id
self.props = props
self.logger = logging.getLogger(__file__)
self.data_logger = None
self.state = False
self.running = False
def init(self):
pass
def log_data(self, value):
self.cbpi.log.log_data(self.id, value)
@abstractmethod
async def run(self):
self.logger.warning("Sensor Init not implemented")
def get_state(self):
pass
def get_value(self):
pass
def get_unit(self):
pass
async def start(self):
self.running = True
async def stop(self):
self.running = False

View file

@ -39,7 +39,7 @@ class CBPiStep(metaclass=ABCMeta):
pass pass
async def update(self, props): async def update(self, props):
await self.cbpi.step2.update_props(self.id, props) await self.cbpi.step.update_props(self.id, props)
async def run(self): async def run(self):
while self.running: while self.running:

View file

@ -14,6 +14,7 @@ import pathlib
import shutil import shutil
def create_plugin_file(): def create_plugin_file():
import os.path import os.path
if os.path.exists(os.path.join(".", 'config', "plugin_list.txt")) is False: if os.path.exists(os.path.join(".", 'config', "plugin_list.txt")) is False:
@ -144,10 +145,16 @@ def main():
parser = argparse.ArgumentParser(description='Welcome to CraftBeerPi 4') parser = argparse.ArgumentParser(description='Welcome to CraftBeerPi 4')
parser.add_argument("action", type=str, help="start,stop,restart,setup,plugins") parser.add_argument("action", type=str, help="start,stop,restart,setup,plugins")
parser.add_argument('--debug', dest='debug', action='store_true')
parser.add_argument("--name", type=str, help="Plugin name") parser.add_argument("--name", type=str, help="Plugin name")
args = parser.parse_args() args = parser.parse_args()
if args.debug is True:
level =logging.DEBUG
else:
level =logging.INFO
#logging.basicConfig(level=logging.INFO, filename='./logs/app.log', filemode='a', format='%(asctime)s - %(levelname)s - %(name)s - %(message)s') #logging.basicConfig(level=logging.INFO, filename='./logs/app.log', filemode='a', format='%(asctime)s - %(levelname)s - %(name)s - %(message)s')
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(name)s - %(message)s') logging.basicConfig(level=level, format='%(asctime)s - %(levelname)s - %(name)s - %(message)s')
if args.action == "setup": if args.action == "setup":
print("Setting up CBPi") print("Setting up CBPi")

View file

@ -1,6 +1,6 @@
name: CraftBeerPi name: CraftBeerPi
version: 4.0.1_alpha version: 4.0.8
#: /myext #: /myext

View file

@ -1,157 +1,43 @@
import asyncio from cbpi.controller.basic_controller import BasicController
import logging import logging
from tabulate import tabulate
from voluptuous import Schema class ActorController(BasicController):
from cbpi.api import *
from cbpi.controller.crud_controller import CRUDController
from cbpi.database.model import ActorModel
class ActorController(CRUDController):
'''
The main actor controller
'''
model = ActorModel
def __init__(self, cbpi): def __init__(self, cbpi):
super(ActorController, self).__init__(cbpi)
self.cbpi = cbpi
self.state = False;
self.logger = logging.getLogger(__name__)
self.cbpi.register(self)
self.types = {}
self.actors = {}
async def init(self):
"""
This method initializes all actors during startup. It creates actor instances
:return: super(ActorController, self).__init__(cbpi, "actor.json")
"""
await super(ActorController, self).init()
for id, value in self.cache.items():
await self._init_actor(value)
def get_state(self):
return dict(items=self.cache,types=self.types)
async def _init_actor(self, actor):
async def on(self, id):
try: try:
if actor.type in self.types: item = self.find_by_id(id)
cfg = actor.config.copy() instance = item.get("instance")
cfg.update(dict(cbpi=self.cbpi, id=id, name=actor.name)) await instance.on()
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())))
except Exception as e: except Exception as e:
logging.error("Faild to switch on Actor {} {}".format(id, e))
self.logger.error("Failed to init actor %s - Reason %s" % (actor.id, str(e))) async def off(self, id):
try:
item = self.find_by_id(id)
instance = item.get("instance")
await instance.off()
except Exception as e:
logging.error("Faild to switch on Actor {} {}".format(id, e))
async def _stop_actor(self, actor): async def toogle(self, id):
actor.instance.stop() try:
await self.cbpi.bus.fire(topic="actor/%s/stopped" % actor.id, id=actor.id) item = self.find_by_id(id)
instance = item.get("instance")
@on_event(topic="actor/+/switch/on") await instance.toggle()
async def on(self, actor_id, power=100, **kwargs) -> None: except Exception as e:
""" logging.error("Faild to switch on Actor {} {}".format(id, e))
Method to switch an actor on.
Supporting Event Topic "actor/+/on"
:param actor_id: the actor id
:param future
:param power: as integer value between 1 and 100
:param kwargs:
:return:
"""
actor_id = int(actor_id)
if actor_id in self.cache:
self.logger.debug("ON %s" % actor_id)
actor = self.cache[actor_id].instance
actor.on(power)
await self.cbpi.bus.fire("actor/%s/on/ok" % actor_id)
@on_event(topic="actor/+/toggle") def create_dict(self, data):
async def toggle(self, actor_id, power=100, time=None, **kwargs) -> None: try:
""" instance = data.get("instance")
Method to toggle an actor on or off state = state=instance.get_state()
Supporting Event Topic "actor/+/toggle" except Exception as e:
logging.error("Faild to crate actor dict {} ".format(e))
:param actor_id: the actor actor_id state = dict()
:param power: the power as integer between 0 and 100 return dict(name=data.get("name"), id=data.get("id"), type=data.get("type"), state=state,props=data.get("props", []))
:return:
"""
self.logger.debug("TOGGLE %s" % actor_id)
actor_id = int(actor_id)
if actor_id in self.cache:
actor = self.cache[actor_id].instance
if actor.state is True:
await self.off(actor_id=actor_id)
else:
await self.on(actor_id=actor_id)
if time is not None:
async def time_toggle(cbpi, actor_id, time):
await asyncio.sleep(time)
await cbpi.bus.fire("actor/%s/off" % actor_id, actor_id = actor_id)
await self.cbpi.job.start_job(time_toggle(self.cbpi, actor_id, time), "actor_%s_time_toggle" % actor_id, "actor_toggle")
@on_event(topic="actor/+/off")
async def off(self, actor_id, **kwargs) -> None:
"""
Method to switch and actor off
Supporting Event Topic "actor/+/off"
:param actor_id: the actor actor_id
:param kwargs:
"""
self.logger.debug("OFF %s" % actor_id)
actor_id = int(actor_id)
if actor_id in self.cache:
actor = self.cache[actor_id].instance
actor.off()
await self.cbpi.bus.fire("actor/%s/off/ok" % actor_id)
@on_event(topic="actor/+/action")
async def call_action(self, actor_id, data, **kwargs) -> None:
schema = Schema({"name":str, "parameter":dict})
schema(data)
name = data.get("name")
parameter = data.get("parameter")
actor = self.cache[actor_id].instance.__getattribute__(name)(**parameter)
async def _post_add_callback(self, m):
'''
:param m:
:return:
'''
await self._init_actor(m)
pass
async def _pre_delete_callback(self, actor_id):
if hasattr(self.cache[int(actor_id)], "instance") and self.cache[int(actor_id)].instance is not None:
await self._stop_actor(self.cache[int(actor_id)])
async def _pre_update_callback(self, actor):
if hasattr(actor, "instance") and actor.instance is not None:
await self._stop_actor(actor)
async def _post_update_callback(self, actor):
await self._init_actor(actor)

View file

@ -0,0 +1,148 @@
import logging
import os.path
import json
import sys, os
import shortuuid
import asyncio
from tabulate import tabulate
class BasicController:
def __init__(self, cbpi, file):
self.name = self.__class__.__name__
self.cbpi = cbpi
self.cbpi.register(self)
self.service = self
self.types = {}
self.logger = logging.getLogger(__name__)
self.data = []
self.autostart = True
self._loop = asyncio.get_event_loop()
self.path = os.path.join(".", 'config', file)
self.cbpi.app.on_cleanup.append(self.shutdown)
async def init(self):
await self.load()
async def load(self):
logging.info("{} Load ".format(self.name))
with open(self.path) as json_file:
data = json.load(json_file)
self.data = data["data"]
if self.autostart is True:
for d in self.data:
logging.info("{} Starting ".format(self.name))
await self.start(d.get("id"))
async def save(self):
logging.info("{} Save ".format(self.name))
data = dict(data=list(map(lambda x: self.create_dict(x), self.data)))
with open(self.path, "w") as file:
json.dump(data, file, indent=4, sort_keys=True)
await self.push_udpate()
async def push_udpate(self):
await self.cbpi.bus.fire("sensor/update", data=list(map(lambda x: self.create_dict(x), self.data)))
def create_dict(self, data):
return dict(name=data.get("name"), id=data.get("id"), type=data.get("type"), status=data.get("status"),props=data.get("props", []))
def find_by_id(self, id):
return next((item for item in self.data if item["id"] == id), None)
def get_index_by_id(self, id):
return next((i for i, item in enumerate(self.data) if item["id"] == id), None)
async def shutdown(self, app):
logging.info("{} Shutdown ".format(self.name))
tasks = []
for item in self.data:
if item.get("instance") is not None and item.get("instance").running is True:
await item.get("instance").stop()
tasks.append(item.get("instance").task)
await asyncio.gather(*tasks)
await self.save()
async def stop(self, id):
logging.info("{} Stop Id {} ".format(self.name, id))
try:
item = self.find_by_id(id)
instance = item.get("instance")
await instance.stop()
await instance.task
except Exception as e:
logging.error("{} Cant stop {} - {}".format(self.name, id, e))
async def start(self, id):
logging.info("{} Start Id {} ".format(self.name, id))
try:
item = self.find_by_id(id)
instance = item.get("instance")
if instance is not None and instance.running is True:
logging.warning("{} already running {}".format(self.name, id))
return
type = item["type"]
print(type)
print(self.types)
clazz = self.types[type]["class"]
item["instance"] = clazz(self.cbpi, item["id"], {})
print(item["instance"])
await item["instance"].start()
item["instance"].task = self._loop.create_task(item["instance"].run())
logging.info("Sensor started {}".format(id))
except Exception as e:
logging.error("{} Cant start {} - {}".format(self.name, id, e))
def get_types(self):
logging.info("{} Get Types".format(self.name))
result = {}
for key, value in self.types.items():
result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions"))
return result
def get_state(self):
logging.info("{} Get State".format(self.name))
return {"data": list(map(lambda x: self.create_dict(x), self.data)), "types":self.get_types()}
async def add(self, data):
logging.info("{} Add".format(self.name))
id = shortuuid.uuid()
item = {**data, "id": id, "instance": None , "name": data.get("name"), "props": data.get("props", {})}
self.data.append(item)
if self.autostart is True:
await self.start(id)
await self.save()
return item
async def update(self, id, data) -> dict:
logging.info("{} Get Update".format(self.name))
await self.stop(id)
if self.autostart is True:
await self.start(id)
self.data = list(map(lambda old: {**old, **data} if old["id"] == id else old, self.data))
await self.save()
return self.find_by_id(id)
async def delete(self, id) -> None:
logging.info("{} Delete".format(self.name))
await self.stop(id)
self.data = list(filter(lambda x: x["id"] != id, self.data))
await self.save()
async def call_action(self, id, action, parameter) -> None:
logging.info("{} call all Action {} {}".format(self.name, id, action))
try:
item = self.find_by_id(id)
print(item)
instance = item.get("instance")
await instance.__getattribute__(action)(**parameter)
except Exception as e:
logging.error("{} Faild to call action on {} {} {}".format(self.name, id, action, e))

View file

@ -1,169 +0,0 @@
import pprint
from abc import ABCMeta
from cbpi.api import *
class CRUDController(metaclass=ABCMeta):
cache = {}
caching = True
name = None
def __init__(self, cbpi):
self.cbpi = cbpi
self.cache = {}
async def init(self):
'''
:return:
'''
if self.caching is True:
self.cache = await self.model.get_all()
async def get_all(self, force_db_update=False):
'''
:param force_db_update:
:return:
'''
if self.caching is False or force_db_update:
self.cache = await self.model.get_all()
return self.cache
async def get_one(self, id):
'''
:param id:
:return:
'''
if id not in self.cache:
raise CBPiException("%s with id %s not found" % (self.name,id))
return self.cache.get(id)
async def _pre_add_callback(self, data):
'''
:param data:
:return:
'''
pass
async def _post_add_callback(self, m):
'''
:param m:
:return:
'''
pass
async def add(self, **data):
'''
:param data:
:return:
'''
await self._pre_add_callback(data)
m = await self.model.insert(**data)
self.cache[m.id] = m
await self._post_add_callback(m)
await self.cbpi.bus.fire(topic="actor/%s/added" % m.id, actor=m)
return m
async def _pre_update_callback(self, m):
pass
async def _post_update_callback(self, m):
pass
async def update(self, id, data):
'''
:param id:
:param data:
:return:
'''
self.logger.debug("Update Sensor %s - %s " % (id, data))
id = int(id)
if self.caching is True and id not in self.cache:
self.logger.debug("%s %s Not in Cache" % (self.name, id))
raise CBPiException("%s with id %s not found" % (self.name,id))
data["id"] = id
try:
### DELETE INSTANCE BEFORE UPDATE
del data["instance"]
except Exception as e:
pass
if self.caching is True:
await self._pre_update_callback(self.cache[id])
self.cache[id].__dict__.update(**data)
m = self.cache[id] = await self.model.update(**self.cache[id].__dict__)
await self._post_update_callback(self.cache[id])
else:
m = await self.model.update(**data)
return m
async def _pre_delete_callback(self, m):
'''
:param m:
:return:
'''
pass
async def _post_delete_callback(self, id):
'''
:param id:
:return:
'''
pass
async def delete(self, id):
'''
:param id:
:return:
'''
if id not in self.cache:
raise CBPiException("%s with id %s not found" % (self.name,id))
await self._pre_delete_callback(id)
m = await self.model.delete(id)
await self._post_delete_callback(id)
try:
if self.caching is True:
del self.cache[int(id)]
except Exception as e:
pass
await self.cbpi.bus.fire(topic="actor/%s/deleted" % id, id=id)
async def delete_all(self):
'''
:return:
'''
self.model.delete_all()
if self.caching is True:
self.cache = {}
#self.cbpi.push_ws("DELETE_ALL_%s" % self.key, None)

View file

@ -1,23 +1,18 @@
import logging import logging
import json import json
from cbpi.controller.crud_controller import CRUDController
from cbpi.database.model import DashboardModel, DashboardContentModel
import os import os
class DashboardController(CRUDController): class DashboardController():
model = DashboardModel
name = "Dashboard"
def __init__(self, cbpi): def __init__(self, cbpi):
self.caching = False self.caching = False
super(DashboardController, self).__init__(cbpi)
self.cbpi = cbpi self.cbpi = cbpi
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.cbpi.register(self) self.cbpi.register(self)
def get_state(self): async def init(self):
return dict(items=self.cache) pass
async def get_content(self, dashboard_id): async def get_content(self, dashboard_id):
try: try:
@ -27,11 +22,9 @@ class DashboardController(CRUDController):
except: except:
return {} return {}
async def add_content(self, dashboard_id, data): async def add_content(self, dashboard_id, data):
with open('./config/dashboard/cbpi_dashboard_%s.json' % dashboard_id, 'w') as outfile: with open('./config/dashboard/cbpi_dashboard_%s.json' % dashboard_id, 'w') as outfile:
json.dump(data, outfile, indent=4, sort_keys=True) json.dump(data, outfile, indent=4, sort_keys=True)
return {"status": "OK"} return {"status": "OK"}
async def delete_content(self, dashboard_id): async def delete_content(self, dashboard_id):

View file

@ -1,179 +1,41 @@
import re from cbpi.controller.basic_controller import BasicController
from cbpi.api import *
from cbpi.controller.crud_controller import CRUDController
from cbpi.database.model import KettleModel
from cbpi.job.aiohttp import get_scheduler_from_app
import logging import logging
from tabulate import tabulate
class KettleController(CRUDController): class KettleController(BasicController):
'''
The main kettle controller
'''
model = KettleModel
def __init__(self, cbpi): def __init__(self, cbpi):
super(KettleController, self).__init__(cbpi) super(KettleController, self).__init__(cbpi, "kettle.json")
self.cbpi = cbpi self.autostart = False
self.types = {}
self.logger = logging.getLogger(__name__)
self.cbpi.register(self)
async def init(self): async def on(self, id):
''' try:
This method initializes all actors during startup. It creates actor instances item = self.find_by_id(id)
instance = item.get("instance")
await instance.start()
except Exception as e:
logging.error("Faild to switch on KettleLogic {} {}".format(id, e))
:return: async def off(self, id):
''' try:
await super(KettleController, self).init() item = self.find_by_id(id)
for key, value in self.cache.items(): instance = item.get("instance")
value.state = False await instance.stop()
except Exception as e:
logging.error("Faild to switch on KettleLogic {} {}".format(id, e))
async def set_target_temp(self, id, target_temp):
try:
item = self.find_by_id(id)
item["target_temp"] = target_temp
await self.save()
except Exception as e:
logging.error("Faild to set Target Temp {} {}".format(id, e))
def get_state(self): def create_dict(self, data):
return dict(items=self.cache,types=self.types) try:
instance = data.get("instance")
async def toggle_automtic(self, id): state = dict(state=instance.get_state())
''' except Exception as e:
logging.error("Faild to create KettleLogic dict {} ".format(e))
Convenience Method to toggle automatic state = dict()
return dict(name=data.get("name"), id=data.get("id"), type=data.get("type"), sensor=data.get("sensor"), heater=data.get("heater"), agitator=data.get("agitator"), target_temp=data.get("target_temp"), state=state,props=data.get("props", []))
:param id: kettle id as int
:return: (boolean, string)
'''
kettle = await self.get_one(id)
if kettle is None:
raise KettleException("Kettle not found")
if kettle.logic is None:
raise CBPiExtension("Logic not found for kettle id: %s" % id)
await self.cbpi.bus.fire(topic="kettle/%s/automatic" % id, id=id)
@on_event(topic="job/+/done")
async def job_stop(self, key, **kwargs) -> None:
match = re.match("kettle_logic_(\d+)", key)
if match is not None:
kid = match.group(1)
kettle = self.cache[int(kid)]
kettle.instance = None
kettle.state = False
await self.cbpi.bus.fire(topic="kettle/%s/logic/stop" % kid)
@on_event(topic="kettle/+/automatic")
async def handle_automtic_event(self, id, **kwargs):
'''
Method to handle the event 'kettle/+/automatic'
:param id: The kettle id
:param kwargs:
:return: None
'''
id = int(id)
if id in self.cache:
kettle = self.cache[id]
if hasattr(kettle, "instance") is False:
kettle.instance = None
self._is_logic_running(id)
if kettle.instance is None:
if kettle.logic in self.types:
clazz = self.types.get("CustomKettleLogic")["class"]
cfg = kettle.config.copy()
cfg.update(dict(cbpi=self.cbpi))
kettle.instance = clazz(**cfg)
await self.cbpi.job.start_job(kettle.instance.run(), "kettle_logic_%s" % kettle.id, "kettle_logic%s" % id)
kettle.state = True
await self.cbpi.bus.fire(topic="kettle/%s/logic/start" % id)
else:
kettle.instance.running = False
kettle.instance = None
kettle.state = False
await self.cbpi.bus.fire(topic="kettle/%s/logic/stop" % id)
def _is_logic_running(self, kettle_id):
scheduler = get_scheduler_from_app(self.cbpi.app)
async def heater_on(self, id):
'''
Convenience Method to switch the heater of a kettle on
:param id: the kettle id
:return: (boolean, string)
'''
kettle = await self.get_one(id)
if kettle is None:
raise KettleException("Kettle not found")
if kettle.sensor is None:
raise ActorException("Actor not defined for kettle id %s" % id)
heater_id = kettle.heater
await self.cbpi.bus.fire(topic="actor/%s/switch/on" % heater_id, actor_id=heater_id, power=99)
async def heater_off(self, id):
'''
Convenience Method to switch the heater of a kettle off
:param id:
:return:
'''
kettle = await self.get_one(id)
if kettle is None:
raise KettleException("Kettle not found")
if kettle.sensor is None:
raise ActorException("Actor not defined for kettle id %s" % id)
heater_id = kettle.heater
await self.cbpi.bus.fire(topic="actor/%s/switch/off" % heater_id, actor_id=heater_id, power=99)
async def agitator_on(self, id):
kettle = await self.get_one(id)
if kettle is None:
raise KettleException("Kettle not found")
if kettle.sensor is None:
raise ActorException("Actor not defined for kettle id %s" % id)
agitator_id = kettle.agitator
await self.cbpi.bus.fire(topic="actor/%s/switch/on" % agitator_id, actor_id=agitator_id, power=99)
async def agitator_off(self, id):
kettle = await self.get_one(id)
if kettle is None:
raise KettleException("Kettle not found")
if kettle.sensor is None:
raise ActorException("Actor not defined for kettle id %s" % id)
agitator_id = kettle.agitator
await self.cbpi.bus.fire(topic="actor/%s/switch/off" % agitator_id, actor_id=agitator_id, power=99)
async def get_traget_temp(self, id):
kettle = await self.get_one(id)
if kettle is None:
raise KettleException("Kettle Not Found")
return kettle.target_temp
async def get_temp(self, id):
kettle = await self.get_one(id)
if kettle is None:
raise KettleException("Kettle Not Found")
if kettle.sensor is None:
raise SensorException("Sensor not defined for kettle id %s" % id)
sensor_id = kettle.sensor
return await self.cbpi.sensor.get_value(sensor_id)
@on_event(topic="kettle/+/targettemp")
async def set_target_temp(self, kettle_id, target_temp, **kwargs) -> None:
kettle = self.cache[int(kettle_id)]
kettle.target_temp = int(target_temp)
await self.model.update(**kettle.__dict__)
await self.cbpi.bus.fire("kettle/%s/targettemp/set" % kettle_id)

View file

@ -7,7 +7,6 @@ from time import strftime, localtime
import pandas as pd import pandas as pd
import zipfile import zipfile
class LogController: class LogController:
def __init__(self, cbpi): def __init__(self, cbpi):

View file

@ -18,9 +18,10 @@ class PluginController():
def __init__(self, cbpi): def __init__(self, cbpi):
self.cbpi = cbpi self.cbpi = cbpi
self.plugins = {}
self.plugins = load_config("./config/plugin_list.txt") self.plugins = load_config("./config/plugin_list.txt")
if self.plugins is None:
self.plugins = {}
async def load_plugin_list(self): async def load_plugin_list(self):
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
@ -98,7 +99,7 @@ class PluginController():
self.modules[filename] = import_module( self.modules[filename] = import_module(
"cbpi.extension.%s" % (filename)) "cbpi.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( logger.warning(
"Plugin %s is not supporting version 4" % filename) "Plugin %s is not supporting version 4" % filename)
@ -116,7 +117,7 @@ class PluginController():
self.modules[p] = import_module(p) self.modules[p] = import_module(p)
self.modules[p].setup(self.cbpi) self.modules[p].setup(self.cbpi)
logger.info("Plugin %s loaded successfully" % p) #logger.info("Plugin %s loaded successfully" % p)
except Exception as e: except Exception as e:
logger.error("FAILED to load plugin %s " % p) logger.error("FAILED to load plugin %s " % p)
logger.error(e) logger.error(e)
@ -128,22 +129,19 @@ class PluginController():
:param clazz: actor class :param clazz: actor class
:return: None :return: None
''' '''
logger.info("Register %s Class %s" % (name, clazz.__name__)) logger.debug("Register %s Class %s" % (name, clazz.__name__))
if issubclass(clazz, CBPiActor):
# self.cbpi.actor.types[name] = {"class": clazz, "config": self._parse_props(clazz)}
self.cbpi.actor.types[name] = self._parse_props(clazz)
if issubclass(clazz, CBPiSensor): if issubclass(clazz, CBPiActor2):
self.cbpi.sensor.types[name] = self._parse_props(clazz) self.cbpi.actor.types[name] = self._parse_step_props(clazz,name)
if issubclass(clazz, CBPiKettleLogic): if issubclass(clazz, CBPiKettleLogic2):
self.cbpi.kettle.types[name] = self._parse_props(clazz) self.cbpi.kettle.types[name] = self._parse_step_props(clazz,name)
if issubclass(clazz, CBPiSimpleStep): if issubclass(clazz, CBPiSensor2):
self.cbpi.step.types[name] = self._parse_props(clazz) self.cbpi.sensor.types[name] = self._parse_step_props(clazz,name)
if issubclass(clazz, CBPiStep): if issubclass(clazz, CBPiStep):
self.cbpi.step2.types[name] = self._parse_step_props(clazz,name) self.cbpi.step.types[name] = self._parse_step_props(clazz,name)
if issubclass(clazz, CBPiExtension): if issubclass(clazz, CBPiExtension):
self.c = clazz(self.cbpi) self.c = clazz(self.cbpi)
@ -165,8 +163,7 @@ class PluginController():
def _parse_step_props(self, cls, name): def _parse_step_props(self, cls, name):
result = {"name": name, "class": cls, result = {"name": name, "class": cls, "properties": [], "actions": []}
"properties": [], "actions": []}
if hasattr(cls, "cbpi_parameters"): if hasattr(cls, "cbpi_parameters"):
parameters = [] parameters = []
@ -174,7 +171,9 @@ class PluginController():
parameters.append(self._parse_property_object(p)) parameters.append(self._parse_property_object(p))
result["properties"] = parameters result["properties"] = parameters
for method_name, method in cls.__dict__.items(): for method_name, method in cls.__dict__.items():
if hasattr(method, "action"): if hasattr(method, "action"):
print(method_name)
key = method.__getattribute__("key") key = method.__getattribute__("key")
parameters = [] parameters = []
for p in method.__getattribute__("parameters"): for p in method.__getattribute__("parameters"):

View file

@ -1,84 +1,5 @@
from cbpi.controller.basic_controller import BasicController
import logging class SensorController(BasicController):
from cbpi.controller.crud_controller import CRUDController
from cbpi.database.model import SensorModel
from cbpi.job.aiohttp import get_scheduler_from_app
class SensorController(CRUDController):
model = SensorModel
def __init__(self, cbpi): def __init__(self, cbpi):
self.cbpi = cbpi super(SensorController, self).__init__(cbpi, "sensor.json")
self.cbpi.register(self)
self.service = self
self.types = {}
self.logger = logging.getLogger(__name__)
self.sensors = {}
async def init(self):
'''
This method initializes all actors during startup. It creates actor instances
:return:
'''
await super(SensorController, self).init()
for id, value in self.cache.items():
await self.init_sensor(value)
def get_state(self):
return dict(items=self.cache,types=self.types)
async def init_sensor(self, sensor):
print("INIT SENSOR")
if sensor.type in self.types:
cfg = sensor.config.copy()
cfg.update(dict(cbpi=self.cbpi, id=sensor.id, name=sensor.name))
clazz = self.types[sensor.type]["class"];
self.cache[sensor.id].instance = clazz(**cfg)
self.cache[sensor.id].instance.init()
scheduler = get_scheduler_from_app(self.cbpi.app)
self.cache[sensor.id].instance.job = await self.cbpi.job.start_job(self.cache[sensor.id].instance.run(self.cbpi), sensor.name, "sensor")
await self.cbpi.bus.fire(topic="sensor/%s/initialized" % sensor.id, id=sensor.id)
else:
self.logger.error("Sensor type '%s' not found (Available Sensor Types: %s)" % (sensor.type, ', '.join(self.types.keys())))
async def stop_sensor(self, sensor):
sensor.instance.stop()
await self.cbpi.bus.fire(topic="sensor/%s/stopped" % sensor.id, id=sensor.id)
async def get_value(self, sensor_id):
sensor_id = int(sensor_id)
return self.cache[sensor_id].instance.value
async def _post_add_callback(self, m):
'''
:param m:
:return:
'''
await self.init_sensor(m)
pass
async def _pre_delete_callback(self, sensor_id):
if int(sensor_id) not in self.cache:
return
if hasattr(self.cache[int(sensor_id)], "instance") and self.cache[int(sensor_id)].instance is not None:
await self.stop_sensor(self.cache[int(sensor_id)])
async def _pre_update_callback(self, sensor):
if hasattr(sensor, "instance") and sensor.instance is not None:
await self.stop_sensor(sensor)
async def _post_update_callback(self, sensor):
await self.init_sensor(sensor)

View file

@ -1,194 +1,245 @@
import asyncio
from tabulate import tabulate
import json
import copy
import shortuuid
import logging import logging
import time import os.path
from cbpi.api import * from ..api.step import CBPiStep
from cbpi.controller.crud_controller import CRUDController
from cbpi.database.model import StepModel
class StepController(CRUDController):
class StepController:
def __init__(self, cbpi): def __init__(self, cbpi):
self.model = StepModel
self.caching = True
self.is_stopping = False
self.cbpi = cbpi self.cbpi = cbpi
self.current_task = None self.woohoo = "HALLLO"
self.is_next = False
self.types = {}
self.current_step = None
self.current_job = None
self.cbpi.register(self)
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.starttime = None self.path = os.path.join(".", 'config', "step_data.json")
self._loop = asyncio.get_event_loop()
self.basic_data = {}
self.step = None
self.types = {}
def is_running(self): self.cbpi.app.on_cleanup.append(self.shutdown)
if self.current_step is not None:
return True
else:
return False
def _get_manged_fields_as_array(self, type_cfg):
result = []
for f in type_cfg.get("properties"):
result.append(f.get("name"))
return result
async def init(self): async def init(self):
logging.info("INIT STEP Controller")
self.load(startActive=True)
# load all steps into cache def load(self, startActive=False):
self.cache = await self.model.get_all()
# create file if not exists
if os.path.exists(self.path) is False:
with open(self.path, "w") as file:
json.dump(dict(basic={}, profile=[]), file, indent=4, sort_keys=True)
for key, value in self.cache.items(): #load from json file
with open(self.path) as json_file:
data = json.load(json_file)
self.basic_data = data["basic"]
self.profile = data["profile"]
# get step type as string # Start step after start up
step_type = self.types.get(value.type) self.profile = list(map(lambda item: {**item, "instance": self.create_step(item.get("id"), item.get("type"), item.get("name"), item.get("props", {}))}, self.profile))
if startActive is True:
active_step = self.find_by_status("A")
if active_step is not None:
self._loop.create_task(self.start_step(active_step))
# if step has state async def add(self, data):
if value.stepstate is not None: logging.info("Add step")
cfg = value.stepstate.copy() id = shortuuid.uuid()
else: item = {**{"status": "I", "props": {}}, **data, "id": id, "instance": self.create_step(id, data.get("type"), data.get("name"), data.get("props", {}))}
cfg = {} self.profile.append(item)
await self.save()
return item
# set managed fields async def update(self, id, data):
cfg.update(dict(cbpi=self.cbpi, id=value.id, managed_fields=self._get_manged_fields_as_array(step_type))) logging.info("update step")
if value.config is not None: self.profile = list(map(lambda old: {**old, **data} if old["id"] == id else old, self.profile))
# set config values await self.save()
cfg.update(**value.config) return self.find_by_id(id)
# create step instance
value.instance = step_type["class"](**cfg)
async def get_all(self, force_db_update: bool = True): async def save(self):
return self.cache logging.debug("save profile")
data = dict(basic=self.basic_data, profile=list(map(lambda x: dict(name=x["name"], type=x.get("type"), id=x["id"], status=x["status"],props=x["props"]), self.profile)))
with open(self.path, "w") as file:
json.dump(data, file, indent=4, sort_keys=True)
await self.push_udpate()
def find_next_step(self): async def start(self):
# filter # already running
inactive_steps = {k: v for k, v in self.cache.items() if v.state == 'I'} if self.find_by_status("A") is not None:
if len(inactive_steps) == 0: logging.error("Steps already running")
return None
return min(inactive_steps, key=lambda x: inactive_steps[x].order)
@on_event("step/start")
async def start(self, **kwargs):
if self.is_running() is False:
next_step_id = self.find_next_step()
if next_step_id:
next_step = self.cache[next_step_id]
next_step.state = 'A'
next_step.stepstate = next_step.config
next_step.start = int(time.time())
await self.model.update(**next_step.__dict__)
self.current_step = next_step
# start the step job
self.current_job = await self.cbpi.job.start_job(self.current_step.instance.run(), next_step.name, "step")
await self.cbpi.bus.fire("step/%s/started" % self.current_step.id, step=next_step)
else:
await self.cbpi.bus.fire("step/brewing/finished")
else:
self.logger.error("Process Already Running")
async def next(self, **kwargs):
if self.current_step is not None:
self.is_next = True
self.current_step.instance.stop()
@on_event("job/step/done")
async def step_done(self, **kwargs):
if self.cbpi.shutdown:
return return
if self.is_stopping: # Find next inactive step
self.is_stopping = False step = self.find_by_status("P")
if step is not None:
logging.info("Resume step")
await self.start_step(step)
await self.save()
return return
if self.current_step is not None: step = self.find_by_status("I")
if step is not None:
logging.info("Start Step")
self.current_step.state = "D" await self.start_step(step)
await self.model.update_state(self.current_step.id, "D", int(time.time())) await self.save()
await self.cbpi.bus.fire("step/%s/done" % self.current_step.id, step=self.current_step) return
self.current_step = None
# start the next step logging.info("BREWING COMPLETE")
await self.start()
@on_event("step/stop") async def next(self):
async def stop_all(self, **kwargs): logging.info("Trigger Next")
# RESET DB step = self.find_by_status("A")
await self.model.reset_all_steps() if step is not None:
# RELOAD all Steps from DB into cache and initialize Instances instance = step.get("instance")
await self.init() if instance is not None:
await self.cbpi.bus.fire("step/brewing/stopped") logging.info("Next")
instance.next()
@on_event("step/clear") await instance.task
async def clear_all(self, **kwargs):
await self.model.delete_all()
self.cbpi.notify(key="Steps Cleared", message="Steps cleared successfully", type="success")
async def _pre_add_callback(self, data):
order = await self.model.get_max_order()
data["order"] = 1 if order is None else order + 1
data["state"] = "I"
data["stepstate"] = {}
return await super()._pre_add_callback(data)
async def init_step(self, value: StepModel):
step_type = self.types.get(value.type)
# if step has state
if value.stepstate is not None:
cfg = value.stepstate.copy()
else: else:
cfg = {} logging.info("No Step is running")
# set managed fields async def resume(self):
cfg.update(dict(cbpi=self.cbpi, id=value.id, managed_fields=self._get_manged_fields_as_array(step_type))) step = self.find_by_status("P")
# set config values if step is not None:
cfg.update(**value.config) instance = step.get("instance")
# create step instance if instance is not None:
value.instance = step_type["class"](**cfg) await self.start_step(step)
return value else:
logging.info("Nothing to resume")
async def _post_add_callback(self, m: StepModel) -> None: async def stop(self):
self.cache[m.id] = await self.init_step(m) logging.info("STOP STEP")
step = self.find_by_status("A")
if step != None and step.get("instance") is not None:
logging.info("CALLING STOP STEP")
instance = step.get("instance")
instance.stop()
# wait for task to be finished
await instance.task
logging.info("STEP STOPPED")
step["status"] = "P"
await self.save()
async def _post_update_callback(self, m: StepModel) -> None: async def reset_all(self):
''' step = self.find_by_status("A")
:param m: step model if step is not None:
:return: None logging.error("Please stop before reset")
''' return
self.cache[m.id] = await self.init_step(m) for item in self.profile:
logging.info("Reset %s" % item.get("name"))
item["status"] = "I"
await item["instance"].reset()
await self.push_udpate()
@on_event("step/sort") def create_step(self, id, type, name, props):
async def sort(self, topic: 'str', data: 'dict', **kwargs):
# update order in cache try:
for id, order in data.items(): type_cfg = self.types.get(type)
self.cache[int(id)].order = order clazz = type_cfg.get("class")
return clazz(self.cbpi, id, name, {**props})
except:
pass
# update oder in database def create_dict(self, data):
await self.model.sort(data) return dict(name=data["name"], id=data["id"], type=data.get("type"), status=data["status"],props=data["props"], state_text=data["instance"].get_state())
async def get_state(self): def get_types2(self):
return dict(items=await self.get_all(),types=self.types,is_running=self.is_running(),current_step=self.current_step) result = {}
for key, value in self.types.items():
result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions"))
return result
@on_event(topic="step/action") def get_state(self):
async def call_action(self, name, parameter, **kwargs) -> None: return {"basic": self.basic_data, "profile": list(map(lambda x: self.create_dict(x), self.profile)), "types":self.get_types2()}
print(name, parameter)
if self.current_step is not None:
self.current_step.instance.__getattribute__(name)(**parameter) async def move(self, id, direction):
index = self.get_index_by_id(id)
if direction not in [-1, 1]:
self.logger.error("Cant move. Direction 1 and -1 allowed")
return
self.profile[index], self.profile[index+direction] = self.profile[index+direction], self.profile[index]
await self.save()
await self.push_udpate()
async def delete(self, id):
step = self.find_by_id(id)
if step.get("status") == "A":
logging.error("Cant delete active Step %s", id)
return
self.profile = list(filter(lambda x: x["id"] != id, self.profile))
await self.save()
async def shutdown(self, app):
logging.info("Mash Profile Shutdonw")
for p in self.profile:
instance = p.get("instance")
# Stopping all running task
if instance.task != None and instance.task.done() is False:
logging.info("Stop Step")
instance.stop()
await instance.task
await self.save()
def done(self, task):
id, reason = task.result()
if reason == "MAX_EXCEPTIONS":
step_current = self.find_by_id(id)
step_current["status"] = "E"
self._loop.create_task(self.save())
return
if reason == "NEXT":
step_current = self.find_by_status("A")
if step_current is not None:
step_current["status"] = "D"
async def wrapper():
## TODO DONT CALL SAVE
await self.save()
await self.start()
self._loop.create_task(wrapper())
def find_by_status(self, status):
return next((item for item in self.profile if item["status"] == status), None)
def find_by_id(self, id):
return next((item for item in self.profile if item["id"] == id), None)
def get_index_by_id(self, id):
return next((i for i, item in enumerate(self.profile) if item["id"] == id), None)
async def push_udpate(self):
await self.cbpi.bus.fire("step/update", data=list(map(lambda x: self.create_dict(x), self.profile)))
async def start_step(self,step):
logging.info("Start Step")
step.get("instance").start()
step["instance"].task = self._loop.create_task(step["instance"].run(), name=step["name"])
step["instance"].task .add_done_callback(self.done)
step["status"] = "A"
async def update_props(self, id, props):
logging.info("SAVE PROPS")
step = self.find_by_id(id)
step["props"] = props
await self.save()
await self.push_udpate()
async def save_basic(self, data):
logging.info("SAVE Basic Data")
self.basic_data = {**self.basic_data, **data,}
await self.save()
await self.push_udpate()

View file

@ -1,251 +0,0 @@
import asyncio
from tabulate import tabulate
import json
import copy
import shortuuid
import logging
import os.path
from ..api.step import CBPiStep
class Step2(CBPiStep):
async def execute(self):
print(self.props)
await self.update(self.props)
print("HALLO")
#raise Exception("RROR")
class StepControllerNg:
def __init__(self, cbpi):
self.cbpi = cbpi
self.woohoo = "HALLLO"
self.logger = logging.getLogger(__name__)
self.path = os.path.join(".", 'config', "step_data.json")
self._loop = asyncio.get_event_loop()
self.basic_data = {}
self.step = None
self.types = {}
self.cbpi.app.on_cleanup.append(self.shutdown)
async def init(self):
logging.info("INIT STEP Controller")
self.load(startActive=True)
def load(self, startActive=False):
# create file if not exists
if os.path.exists(self.path) is False:
with open(self.path, "w") as file:
json.dump(dict(basic={}, profile=[]), file, indent=4, sort_keys=True)
#load from json file
with open(self.path) as json_file:
data = json.load(json_file)
self.basic_data = data["basic"]
self.profile = data["profile"]
# Start step after start up
self.profile = list(map(lambda item: {**item, "instance": self.create_step(item.get("id"), item.get("type"), item.get("name"), item.get("props", {}))}, self.profile))
if startActive is True:
active_step = self.find_by_status("A")
if active_step is not None:
self._loop.create_task(self.start_step(active_step))
async def add(self, data):
logging.info("Add step")
print(data)
id = shortuuid.uuid()
item = {**{"status": "I", "props": {}}, **data, "id": id, "instance": self.create_step(id, data.get("type"), data.get("name"), data.get("props", {}))}
self.profile.append(item)
await self.save()
return item
async def update(self, id, data):
logging.info("update step")
print(id, data)
#if "instance" in data: del data["instance"]
self.profile = list(map(lambda old: {**old, **data} if old["id"] == id else old, self.profile))
print(tabulate(self.profile))
await self.save()
return self.find_by_id(id)
async def save(self):
logging.debug("save profile")
data = dict(basic=self.basic_data, profile=list(map(lambda x: dict(name=x["name"], type=x.get("type"), id=x["id"], status=x["status"],props=x["props"]), self.profile)))
with open(self.path, "w") as file:
json.dump(data, file, indent=4, sort_keys=True)
await self.push_udpate()
async def start(self):
# already running
if self.find_by_status("A") is not None:
logging.error("Steps already running")
return
# Find next inactive step
step = self.find_by_status("P")
if step is not None:
logging.info("Resume step")
await self.start_step(step)
await self.save()
return
step = self.find_by_status("I")
if step is not None:
logging.info("Start Step")
await self.start_step(step)
await self.save()
return
logging.info("BREWING COMPLETE")
async def next(self):
logging.info("Trigger Next")
step = self.find_by_status("A")
if step is not None:
instance = step.get("instance")
if instance is not None:
logging.info("Next")
instance.next()
await instance.task
else:
logging.info("No Step is running")
async def resume(self):
step = self.find_by_status("P")
if step is not None:
instance = step.get("instance")
if instance is not None:
await self.start_step(step)
else:
logging.info("Nothing to resume")
async def stop(self):
logging.info("STOP STEP")
step = self.find_by_status("A")
if step != None and step.get("instance") is not None:
logging.info("CALLING STOP STEP")
instance = step.get("instance")
instance.stop()
# wait for task to be finished
await instance.task
logging.info("STEP STOPPED")
step["status"] = "P"
await self.save()
async def reset_all(self):
step = self.find_by_status("A")
if step is not None:
logging.error("Please stop before reset")
return
for item in self.profile:
logging.info("Reset %s" % item.get("name"))
item["status"] = "I"
await item["instance"].reset()
await self.push_udpate()
def create_step(self, id, type, name, props):
type_cfg = self.types.get(type)
clazz = type_cfg.get("class")
return clazz(self.cbpi, id, name, {**props})
def create_dict(self, data):
return dict(name=data["name"], id=data["id"], type=data.get("type"), status=data["status"],props=data["props"], state_text=data["instance"].get_state())
def get_types2(self):
result = {}
for key, value in self.types.items():
print(value)
result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions"))
return result
def get_state(self):
return {"basic": self.basic_data, "profile": list(map(lambda x: self.create_dict(x), self.profile)), "types":self.get_types2()}
async def move(self, id, direction):
index = self.get_index_by_id(id)
if direction not in [-1, 1]:
self.logger.error("Cant move. Direction 1 and -1 allowed")
return
self.profile[index], self.profile[index+direction] = self.profile[index+direction], self.profile[index]
self.save()
await self.push_udpate()
async def delete(self, id):
step = self.find_by_id(id)
if step.get("status") == "A":
logging.error("Cant delete active Step %s", id)
return
self.profile = list(filter(lambda x: x["id"] != id, self.profile))
await self.save()
async def shutdown(self, app):
logging.info("Mash Profile Shutdonw")
for p in self.profile:
instance = p.get("instance")
# Stopping all running task
if instance.task != None and instance.task.done() is False:
logging.info("Stop Step")
instance.stop()
await instance.task
await self.save()
def done(self, task):
id, reason = task.result()
print(id, reason)
if reason == "MAX_EXCEPTIONS":
step_current = self.find_by_id(id)
step_current["status"] = "E"
self._loop.create_task(self.save())
return
if reason == "NEXT":
step_current = self.find_by_status("A")
if step_current is not None:
step_current["status"] = "D"
async def wrapper():
await self.save()
await self.start()
self._loop.create_task(wrapper())
def find_by_status(self, status):
return next((item for item in self.profile if item["status"] == status), None)
def find_by_id(self, id):
return next((item for item in self.profile if item["id"] == id), None)
def get_index_by_id(self, id):
return next((i for i, item in enumerate(self.profile) if item["id"] == id), None)
async def push_udpate(self):
print("PUS UPDATE")
await self.cbpi.bus.fire("step/update", data=list(map(lambda x: self.create_dict(x), self.profile)))
async def start_step(self,step):
logging.info("############# start step")
step.get("instance").start()
step["instance"].task = self._loop.create_task(step["instance"].run(), name=step["name"])
print(step["instance"].task)
step["instance"].task .add_done_callback(self.done)
step["status"] = "A"
async def update_props(self, id, props):
logging.info("SAVE PROPS")
print(id, props)
step = self.find_by_id(id)
step["props"] = props
await self.save()
await self.push_udpate()

View file

@ -1,28 +0,0 @@
import logging
from cbpi.database.model import TranslationModel
class TranslationController(object):
def __init__(self, cbpi):
self.cbpi = cbpi
self._cache = {}
self.logger = logging.getLogger(__name__)
async def init(self):
self._cache = await TranslationModel.get_all()
def get_all(self):
return self._cache
async def add_key(self, locale, key):
try:
if locale not in self._cache or key not in self._cache[locale]:
await TranslationModel.add_key(locale, key)
self._cache = await TranslationModel.get_all()
except Exception as e:
self.logger.error("Error during adding translation key %s - %s - %s" % (key, locale, str(e)))

View file

@ -1,7 +1,7 @@
import logging import logging
from os import urandom from os import urandom
import os import os
from cbpi import __version__
from aiohttp import web from aiohttp import web
from aiohttp_auth import auth from aiohttp_auth import auth
from aiohttp_session import session_middleware from aiohttp_session import session_middleware
@ -19,8 +19,9 @@ from cbpi.controller.notification_controller import NotificationController
from cbpi.controller.plugin_controller import PluginController from cbpi.controller.plugin_controller import PluginController
from cbpi.controller.sensor_controller import SensorController from cbpi.controller.sensor_controller import SensorController
from cbpi.controller.step_controller import StepController from cbpi.controller.step_controller import StepController
from cbpi.controller.step_controller_ng import StepControllerNg
from cbpi.controller.system_controller import SystemController from cbpi.controller.system_controller import SystemController
from cbpi.controller.log_file_controller import LogController from cbpi.controller.log_file_controller import LogController
from cbpi.database.model import DBModel from cbpi.database.model import DBModel
from cbpi.eventbus import CBPiEventBus from cbpi.eventbus import CBPiEventBus
@ -28,14 +29,14 @@ from cbpi.http_endpoints.http_login import Login
from cbpi.utils import * from cbpi.utils import *
from cbpi.websocket import CBPiWebSocket from cbpi.websocket import CBPiWebSocket
from cbpi.http_endpoints.http_actor import ActorHttpEndpoints from cbpi.http_endpoints.http_actor import ActorHttpEndpoints
from cbpi.http_endpoints.http_config import ConfigHttpEndpoints from cbpi.http_endpoints.http_config import ConfigHttpEndpoints
from cbpi.http_endpoints.http_dashboard import DashBoardHttpEndpoints from cbpi.http_endpoints.http_dashboard import DashBoardHttpEndpoints
from cbpi.http_endpoints.http_kettle import KettleHttpEndpoints from cbpi.http_endpoints.http_kettle import KettleHttpEndpoints
from cbpi.http_endpoints.http_sensor import SensorHttpEndpoints from cbpi.http_endpoints.http_sensor import SensorHttpEndpoints
from cbpi.http_endpoints.http_step import StepHttpEndpoints from cbpi.http_endpoints.http_step import StepHttpEndpoints
from cbpi.http_endpoints.http_step2 import StepHttpEndpoints2
from cbpi.controller.translation_controller import TranslationController
from cbpi.http_endpoints.http_translation import TranslationHttpEndpoint
from cbpi.http_endpoints.http_plugin import PluginHttpEndpoints from cbpi.http_endpoints.http_plugin import PluginHttpEndpoints
from cbpi.http_endpoints.http_system import SystemHttpEndpoints from cbpi.http_endpoints.http_system import SystemHttpEndpoints
from cbpi.http_endpoints.http_log import LogHttpEndpoints from cbpi.http_endpoints.http_log import LogHttpEndpoints
@ -68,7 +69,7 @@ class CraftBeerPi():
def __init__(self): def __init__(self):
self.version = "4.0.0.1" self.version = __version__
self.static_config = load_config("./config/config.yaml") self.static_config = load_config("./config/config.yaml")
self.database_file = "./craftbeerpi.db" self.database_file = "./craftbeerpi.db"
@ -87,26 +88,24 @@ class CraftBeerPi():
self.config = ConfigController(self) self.config = ConfigController(self)
self.ws = CBPiWebSocket(self) self.ws = CBPiWebSocket(self)
self.translation = TranslationController(self)
self.actor = ActorController(self) self.actor = ActorController(self)
self.sensor = SensorController(self) self.sensor = SensorController(self)
self.plugin = PluginController(self) self.plugin = PluginController(self)
self.log = LogController(self) self.log = LogController(self)
self.system = SystemController(self) self.system = SystemController(self)
self.kettle = KettleController(self) self.kettle = KettleController(self)
self.step = StepController(self) self.step = StepController(self)
self.step2 = StepControllerNg(self)
self.dashboard = DashboardController(self) self.dashboard = DashboardController(self)
self.http_step = StepHttpEndpoints(self) self.http_step = StepHttpEndpoints(self)
self.http_step2 = StepHttpEndpoints2(self)
self.http_sensor = SensorHttpEndpoints(self) self.http_sensor = SensorHttpEndpoints(self)
self.http_config = ConfigHttpEndpoints(self) self.http_config = ConfigHttpEndpoints(self)
self.http_actor = ActorHttpEndpoints(self) self.http_actor = ActorHttpEndpoints(self)
self.http_kettle = KettleHttpEndpoints(self) self.http_kettle = KettleHttpEndpoints(self)
self.http_dashboard = DashBoardHttpEndpoints(self) self.http_dashboard = DashBoardHttpEndpoints(self)
self.http_translation = TranslationHttpEndpoint(self)
self.http_plugin = PluginHttpEndpoints(self) self.http_plugin = PluginHttpEndpoints(self)
self.http_system = SystemHttpEndpoints(self) self.http_system = SystemHttpEndpoints(self)
self.notification = NotificationController(self) self.notification = NotificationController(self)
@ -156,7 +155,7 @@ class CraftBeerPi():
http_method = method.__getattribute__("method") http_method = method.__getattribute__("method")
path = method.__getattribute__("path") path = method.__getattribute__("path")
class_name = method.__self__.__class__.__name__ class_name = method.__self__.__class__.__name__
logger.info("Register Endpoint : %s.%s %s %s%s " % (class_name, method.__name__, http_method, url_prefix, path)) logger.debug("Register Endpoint : %s.%s %s %s%s " % (class_name, method.__name__, http_method, url_prefix, path))
def add_post(): def add_post():
routes.append(web.post(method.__getattribute__("path"), method)) routes.append(web.post(method.__getattribute__("path"), method))
@ -178,7 +177,6 @@ class CraftBeerPi():
} }
switcher[http_method]() switcher[http_method]()
print("URL PREFIX", url_prefix)
if url_prefix != "/": if url_prefix != "/":
logger.debug("URL Prefix: %s " % (url_prefix,)) logger.debug("URL Prefix: %s " % (url_prefix,))
sub = web.Application() sub = web.Application()
@ -199,12 +197,10 @@ class CraftBeerPi():
long_description = """ long_description = """
This is the api for CraftBeerPi This is the api for CraftBeerPi
""" """
print("SWAGGER.......")
setup_swagger(self.app, setup_swagger(self.app,
description=long_description, description=long_description,
title=self.static_config.get("name", "CraftBeerPi 4.0"), title="CraftBeerPi",
api_version=self.static_config.get("version", "4.0"), api_version=self.version,
contact="info@craftbeerpi.com") contact="info@craftbeerpi.com")
def notify(self, key: str, message: str, type: str = "info") -> None: def notify(self, key: str, message: str, type: str = "info") -> None:
@ -227,13 +223,15 @@ class CraftBeerPi():
def _print_logo(self): def _print_logo(self):
from pyfiglet import Figlet from pyfiglet import Figlet
f = Figlet(font='big') f = Figlet(font='big')
logger.info("\n%s" % f.renderText("%s %s" % (self.static_config.get("name"), self.static_config.get("version")))) logger.info("\n%s" % f.renderText("CraftBeerPi %s " % self.version))
logger.info("www.CraftBeerPi.com")
logger.info("(c) 2021 Manuel Fritsch")
def _setup_http_index(self): def _setup_http_index(self):
async def http_index(request): async def http_index(request):
url = self.config.static.get("index_url") url = self.config.static.get("index_url")
py
if url is not None: if url is not None:
raise web.HTTPFound(url) raise web.HTTPFound(url)
@ -250,12 +248,11 @@ class CraftBeerPi():
await self.job.init() await self.job.init()
await DBModel.setup() await DBModel.setup()
await self.config.init() await self.config.init()
await self.translation.init()
self._setup_http_index() self._setup_http_index()
self.plugin.load_plugins() self.plugin.load_plugins()
self.plugin.load_plugins_from_evn() self.plugin.load_plugins_from_evn()
await self.sensor.init() await self.sensor.init()
await self.step2.init() await self.step.init()
await self.actor.init() await self.actor.init()
await self.kettle.init() await self.kettle.init()
await self.call_initializer(self.app) await self.call_initializer(self.app)

View file

@ -19,25 +19,30 @@ except Exception:
patcher.start() patcher.start()
import RPi.GPIO as GPIO import RPi.GPIO as GPIO
class CustomActor(CBPiActor): class CustomActor(CBPiActor2):
my_name = ""
# Custom property which can be configured by the user # Custom property which can be configured by the user
@action("test", parameters={}) @action("test", parameters={})
def action1(self): async def action1(self, **kwargs):
print("ACTION !", kwargs)
self.my_name = kwargs.get("name")
pass pass
def init(self): def init(self):
print("INIT")
self.state = False self.state = False
pass pass
def on(self, power=0): async def on(self, power=0):
logger.info("ACTOR %s ON" % self.id) logger.info("ACTOR 1111 %s ON" % self.id)
self.state = True self.state = True
def off(self): async def off(self):
logger.info("ACTOR %s OFF " % self.id) logger.info("ACTOR %s OFF " % self.id)
self.state = False self.state = False
@ -45,6 +50,9 @@ class CustomActor(CBPiActor):
return self.state return self.state
async def run(self):
pass
class GPIOActor(CBPiActor): class GPIOActor(CBPiActor):

View file

@ -2,70 +2,15 @@ import asyncio
from cbpi.api import * from cbpi.api import *
class CustomLogic(CBPiKettleLogic): class CustomLogic(CBPiKettleLogic2):
test = Property.Number(label="Test") pass
running = True @action(key="test", parameters=[])
async def action1(self, **kwargs):
print("ACTION")
async def wait_for_event(self, topic, callback=None, timeout=None):
future_obj = self.cbpi.app.loop.create_future()
async def default_callback(id, **kwargs):
future_obj.set_result("HELLO")
if callback is None:
self.cbpi.bus.register(topic=topic, method=default_callback)
else:
callback.future = future_obj
self.cbpi.bus.register(topic=topic, method=callback)
if timeout is not None:
try:
await asyncio.wait_for(future_obj, timeout=timeout)
return future_obj.result()
except asyncio.TimeoutError:
pass
else:
await future_obj
return future_obj.result()
async def run(self):
'''
async def my_callback(value, **kwargs):
if value == 5:
self.cbpi.bus.unregister(my_callback)
kwargs["future"].set_result("AMAZING")
else:
pass
result = await self.wait_for_event("sensor/1/data", callback=my_callback)
'''
value = 0
while self.running:
value = value + 1
print(value)
if value >= 10:
break
await asyncio.sleep(1)
def setup(cbpi): def setup(cbpi):

View file

@ -1,10 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio import asyncio
import random
import re
from aiohttp import web from aiohttp import web
from cbpi.api import * from cbpi.api import *
import re
import random
class CustomSensor(CBPiSensor): class CustomSensor(CBPiSensor):
# Custom Properties which will can be configured by the user # Custom Properties which will can be configured by the user
@ -48,6 +50,34 @@ class CustomSensor(CBPiSensor):
self.log_data(self.value) self.log_data(self.value)
await cbpi.bus.fire("sensor/%s/data" % self.id, value=self.value) await cbpi.bus.fire("sensor/%s/data" % self.id, value=self.value)
@parameters([Property.Number(label="Param1", configurable=True),
Property.Text(label="Param2", configurable=True, default_value="HALLO"),
Property.Select(label="Param3", options=[1,2,4]),
Property.Sensor(label="Param4"),
Property.Actor(label="Param5")])
class CustomSensor2(CBPiSensor2):
@action(key="Test", parameters=[])
async def action1(self, **kwargs):
print("ACTION!", kwargs)
@action(key="Test1", parameters=[])
async def action2(self, **kwargs):
print("ACTION!", kwargs)
@action(key="Test2", parameters=[])
async def action3(self, **kwargs):
print("ACTION!", kwargs)
async def run(self):
while self.running is True:
print("HALLO")
await asyncio.sleep(1)
def setup(cbpi): def setup(cbpi):
@ -58,5 +88,5 @@ def setup(cbpi):
:param cbpi: the cbpi core :param cbpi: the cbpi core
:return: :return:
''' '''
cbpi.plugin.register("CustomSensor2", CustomSensor2)
cbpi.plugin.register("CustomSensor", CustomSensor) #cbpi.plugin.register("CustomSensor", CustomSensor)

View file

@ -4,59 +4,33 @@ import time
from cbpi.api import * from cbpi.api import *
class CustomStepCBPi(CBPiSimpleStep): @parameters([Property.Number(label="Param1", configurable=True),
Property.Text(label="Param2", configurable=True, default_value="HALLO"),
name1 = Property.Number(label="Test", configurable=True) Property.Select(label="Param3", options=[1,2,4]),
timer_end = Property.Number(label="Test", default_value=None) Property.Sensor(label="Param4"),
temp = Property.Number(label="Temperature", default_value=50, configurable=True) Property.Actor(label="Param5")])
i = 0
@action(key="name", parameters=None)
def test(self, **kwargs):
self.name="WOOHOO"
def get_status(self):
return "Status: %s Temp" % self.temp
async def run_cycle(self):
self.next()
'''
print("RUN", self.name1, self.managed_fields, self.timer_end)
self.i = self.i + 1
if self.timer_end is None:
self.timer_end = time.time() + 10
if self.i == 10:
self.next()
'''
#self.cbpi.notify(key="step", message="HELLO FROM STEP")
@parameters([Property.Number(label="Test", configurable=True), Property.Text(label="Test", configurable=True, default_value="HALLO")])
class Step2(CBPiStep): class Step2(CBPiStep):
i = 0 @action(key="name2", parameters=[])
async def action2(self, **kwargs):
print("CALL ACTION")
@action(key="name", parameters=[Property.Number(label="Test", configurable=True)]) @action(key="name", parameters=[Property.Number(label="Test", configurable=True)])
async def action(self, **kwargs): async def action(self, **kwargs):
print("HALLO") print("CALL ACTION")
async def execute(self): async def execute(self):
count = self.props.get("count", 0)
self.state_msg = "COUNT %s" % count
print(self.props) self.props["count"] += 1
self.i += 1
print(self.i)
self.state_msg = "COUNT %s" % self.i
await self.update(self.props) await self.update(self.props)
print("JETZT GEHTS LO")
#raise Exception("RROR")
if count >= 5:
self.next()
async def reset(self):
self.props["count"] = 0
def setup(cbpi): def setup(cbpi):
''' '''
@ -67,4 +41,4 @@ def setup(cbpi):
:return: :return:
''' '''
cbpi.plugin.register("CustomStep2", Step2) cbpi.plugin.register("CustomStep2", Step2)
cbpi.plugin.register("CustomStepCBPi", CustomStepCBPi)

View file

@ -31,5 +31,10 @@ def setup(cbpi):
:param cbpi: the cbpi core :param cbpi: the cbpi core
:return: :return:
''' '''
print("MQTT")
print("###################")
print("###################")
print("###################")
print("###################")
client = CBPiMqttClient(cbpi) client = CBPiMqttClient(cbpi)

View file

@ -1,30 +1,14 @@
from aiohttp import web from aiohttp import web
from cbpi.api import * from cbpi.api import *
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
auth = False auth = False
class ActorHttpEndpoints(HttpCrudEndpoints): class ActorHttpEndpoints():
def __init__(self, cbpi): def __init__(self, cbpi):
super().__init__(cbpi) self.cbpi = cbpi
self.controller = cbpi.actor self.controller = cbpi.actor
self.cbpi.register(self, "/actor") self.cbpi.register(self, "/actor")
@request_mapping(path="/types", auth_required=False)
async def get_types(self, request):
"""
---
description: Get all actor types
tags:
- Actor
responses:
"200":
description: successful operation
"""
return await super().get_types(request)
@request_mapping(path="/", auth_required=False) @request_mapping(path="/", auth_required=False)
async def http_get_all(self, request): async def http_get_all(self, request):
""" """
@ -37,29 +21,8 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
"204": "204":
description: successful operation description: successful operation
""" """
return await super().http_get_all(request) return web.json_response(data=self.controller.get_state())
@request_mapping(path="/{id:\d+}", auth_required=False)
async def http_get_one(self, request):
"""
---
description: Get one Actor
tags:
- Actor
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
return await super().http_get_one(request)
@request_mapping(path="/", method="POST", auth_required=False) @request_mapping(path="/", method="POST", auth_required=False)
async def http_add(self, request): async def http_add(self, request):
@ -73,21 +36,30 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
name: body name: body
description: Created an actor description: Created an actor
required: true required: true
schema: schema:
type: object type: object
properties: properties:
name: name:
type: string type: string
type: type:
type: string type: string
config: props:
type: object type: object
example:
name: "Actor 1"
type: "CustomActor"
props: {}
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
data = await request.json()
response_data = await self.controller.add(data)
return await super().http_add(request) return web.json_response(data=self.controller.create_dict(response_data))
@request_mapping(path="/{id}", method="PUT", auth_required=False) @request_mapping(path="/{id}", method="PUT", auth_required=False)
@ -116,14 +88,14 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
type: type:
type: string type: string
config: config:
type: object props: object
responses: responses:
"200": "200":
description: successful operation description: successful operation
""" """
print(".........") id = request.match_info['id']
print(request) data = await request.json()
return await super().http_update(request) return web.json_response(data=self.controller.create_dict(await self.controller.update(id, data)))
@request_mapping(path="/{id}", method="DELETE", auth_required=False) @request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete_one(self, request): async def http_delete_one(self, request):
@ -137,15 +109,16 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
in: "path" in: "path"
description: "Actor ID" description: "Actor ID"
required: true required: true
type: "integer" type: "string"
format: "int64"
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
return await super().http_delete_one(request) id = request.match_info['id']
await self.controller.delete(id)
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/on", method="POST", auth_required=auth) @request_mapping(path="/{id}/on", method="POST", auth_required=False)
async def http_on(self, request) -> web.Response: async def http_on(self, request) -> web.Response:
""" """
@ -159,27 +132,24 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
in: "path" in: "path"
description: "Actor ID" description: "Actor ID"
required: true required: true
type: "integer" type: "string"
format: "int64"
responses: responses:
"204": "204":
description: successful operation description: successful operation
"405": "405":
description: invalid HTTP Met description: invalid HTTP Met
""" """
actor_id = int(request.match_info['id']) id = request.match_info['id']
result = await self.cbpi.bus.fire(topic="actor/%s/switch/on" % actor_id, actor_id=actor_id, power=99) await self.controller.on(id)
for key, value in result.results.items():
pass
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/{id}/off", method="POST", auth_required=False)
@request_mapping(path="/{id:\d+}/off", method="POST", auth_required=auth)
async def http_off(self, request) -> web.Response: async def http_off(self, request) -> web.Response:
""" """
--- ---
description: Switch actor off description: Switch actor on
tags: tags:
- Actor - Actor
@ -188,48 +158,20 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
in: "path" in: "path"
description: "Actor ID" description: "Actor ID"
required: true required: true
type: "integer" type: "string"
format: "int64"
responses: responses:
"204": "204":
description: successful operation description: successful operation
"405": "405":
description: invalid HTTP Met description: invalid HTTP Met
""" """
actor_id = int(request.match_info['id']) id = request.match_info['id']
await self.cbpi.bus.fire(topic="actor/%s/off" % actor_id, actor_id=actor_id) await self.controller.off(id)
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/{id:\d+}/toggle", method="POST", auth_required=auth)
async def http_toggle(self, request) -> web.Response:
"""
--- @request_mapping(path="/{id}/action", method="POST", auth_required=auth)
description: Toogle an actor on or off
tags:
- Actor
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
actor_id = int(request.match_info['id'])
if await request.text():
data = await request.json()
await self.cbpi.bus.fire(topic="actor/%s/toggle" % actor_id, time=data.get("time"), actor_id=actor_id)
else:
await self.cbpi.bus.fire(topic="actor/%s/toggle" % actor_id, actor_id=actor_id)
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/action", method="POST", auth_required=auth)
async def http_action(self, request) -> web.Response: async def http_action(self, request) -> web.Response:
""" """
@ -253,13 +195,14 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
properties: properties:
name: name:
type: string type: string
config: parameter:
type: object type: object
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
actor_id = int(request.match_info['id']) actor_id = request.match_info['id']
data = await request.json()
await self.controller.call_action(actor_id, data.get("name"), data.get("parameter"))
await self.cbpi.bus.fire(topic="actor/%s/action" % actor_id, actor_id=actor_id, data=await request.json())
return web.Response(status=204) return web.Response(status=204)

View file

@ -13,114 +13,8 @@ class DashBoardHttpEndpoints(HttpCrudEndpoints):
self.controller = cbpi.dashboard self.controller = cbpi.dashboard
self.cbpi.register(self, "/dashboard") self.cbpi.register(self, "/dashboard")
@request_mapping(path="/", auth_required=False)
async def http_get_all(self, request):
"""
---
description: Get all dashboards
tags:
- Dashboard
responses:
"200":
description: successful operation
"""
return await super().http_get_all(request)
@request_mapping(path="/{id:\d+}", auth_required=False)
async def http_get_one(self, request):
"""
---
description: Get one Dashboard by id
tags:
- Dashboard
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "integer"
format: "int64"
responses:
"200":
description: successful operation
"""
return await super().http_get_one(request)
@request_mapping(path="/", method="POST", auth_required=False)
async def http_add(self, request):
"""
---
description: Create a new Dashboard
tags:
- Dashboard
parameters:
- in: body
name: body
description: Create a new Dashboard
required: false
schema:
type: object
properties:
name:
type: string
responses:
"200":
description: successful operation
"""
return await super().http_add(request)
@request_mapping(path="/{id:\d+}", method="PUT", auth_required=False)
async def http_update(self, request):
"""
---
description: Update a Dashboard
tags:
- Dashboard
parameters:
- name: "id"
in: "path"
description: "Dashboard ID"
required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Update a dashboard
required: false
schema:
type: object
properties:
name:
type: string
responses:
"200":
description: successful operation
"""
return await super().http_update(request)
@request_mapping(path="/{id:\d+}", method="DELETE", auth_required=False)
async def http_delete_one(self, request):
"""
---
description: Delete a Dashboard
tags:
- Dashboard
parameters:
- name: "id"
in: "path"
description: "Dashboard ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
id = request.match_info['id']
await self.cbpi.dashboard.delete_dashboard(id)
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/content", auth_required=False) @request_mapping(path="/{id:\d+}/content", auth_required=False)
async def get_content(self, request): async def get_content(self, request):
@ -141,8 +35,6 @@ class DashBoardHttpEndpoints(HttpCrudEndpoints):
description: successful operation description: successful operation
""" """
dashboard_id = int(request.match_info['id']) dashboard_id = int(request.match_info['id'])
return web.json_response(await self.cbpi.dashboard.get_content(dashboard_id), dumps=json_dumps) return web.json_response(await self.cbpi.dashboard.get_content(dashboard_id), dumps=json_dumps)

View file

@ -1,34 +1,229 @@
from aiohttp import web from aiohttp import web
from cbpi.api import * from cbpi.api import *
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
auth = False auth = False
class KettleHttpEndpoints():
class KettleHttpEndpoints(HttpCrudEndpoints): def __init__(self, cbpi):
self.cbpi = cbpi
@request_mapping(path="/types", auth_required=False) self.controller = cbpi.kettle
async def get_types(self, request): self.cbpi.register(self, "/kettle")
return await super().get_types(request)
@request_mapping(path="/", auth_required=False) @request_mapping(path="/", auth_required=False)
async def http_get_all(self, request): async def http_get_all(self, request):
""" """
--- ---
description: Get all kettles description: Switch actor on
tags: tags:
- Kettle - Kettle
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
return await super().http_get_all(request) return web.json_response(data=self.controller.get_state())
@request_mapping(path="/{id:\d+}", auth_required=False)
async def http_get_one(self, request): @request_mapping(path="/", method="POST", auth_required=False)
async def http_add(self, request):
""" """
--- ---
description: Get Kettle by Id description: add one Actor
tags:
- Kettle
parameters:
- in: body
name: body
description: Created an actor
required: true
schema:
type: object
properties:
name:
type: string
sensor:
type: "integer"
format: "int64"
heater:
type: "integer"
format: "int64"
agitator:
type: "integer"
format: "int64"
target_temp:
type: "integer"
format: "int64"
type:
type: string
props:
type: object
example:
name: "Kettle 1"
type: "CustomKettleLogic"
props: {}
responses:
"204":
description: successful operation
"""
data = await request.json()
response_data = await self.controller.add(data)
return web.json_response(data=self.controller.create_dict(response_data))
@request_mapping(path="/{id}", method="PUT", auth_required=False)
async def http_update(self, request):
"""
---
description: Update an actor
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Update an actor
required: false
schema:
type: object
properties:
name:
type: string
type:
type: string
config:
props: object
responses:
"200":
description: successful operation
"""
id = request.match_info['id']
data = await request.json()
return web.json_response(data=self.controller.create_dict(await self.controller.update(id, data)))
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete_one(self, request):
"""
---
description: Delete an actor
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "string"
responses:
"204":
description: successful operation
"""
id = request.match_info['id']
await self.controller.delete(id)
return web.Response(status=204)
@request_mapping(path="/{id}/on", method="POST", auth_required=False)
async def http_on(self, request) -> web.Response:
"""
---
description: Switch actor on
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "string"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
id = request.match_info['id']
await self.controller.start(id)
return web.Response(status=204)
@request_mapping(path="/{id}/off", method="POST", auth_required=False)
async def http_off(self, request) -> web.Response:
"""
---
description: Switch actor on
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "string"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
id = request.match_info['id']
await self.controller.off(id)
return web.Response(status=204)
@request_mapping(path="/{id}/action", method="POST", auth_required=auth)
async def http_action(self, request) -> web.Response:
"""
---
description: Toogle an actor on or off
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Update an actor
required: false
schema:
type: object
properties:
name:
type: string
parameter:
type: object
responses:
"204":
description: successful operation
"""
actor_id = request.match_info['id']
data = await request.json()
await self.controller.call_action(actor_id, data.get("name"), data.get("parameter"))
return web.Response(status=204)
@request_mapping(path="/{id}/target_temp", method="POST", auth_required=auth)
async def http_target(self, request) -> web.Response:
"""
---
description: Toogle an actor on or off
tags: tags:
- Kettle - Kettle
parameters: parameters:
@ -42,287 +237,7 @@ class KettleHttpEndpoints(HttpCrudEndpoints):
"204": "204":
description: successful operation description: successful operation
""" """
return await super().http_get_one(request) id = request.match_info['id']
#data = await request.json()
@request_mapping(path="/", method="POST", auth_required=False) await self.controller.set_target_temp(id,999)
async def http_add(self, request):
"""
---
description: add a kettle
tags:
- Kettle
parameters:
- in: body
name: body
description: Created an kettle
required: false
schema:
type: object
properties:
name:
type: string
sensor:
type: "integer"
format: "int64"
heater:
type: "integer"
format: "int64"
agitator:
type: "integer"
format: "int64"
target_temp:
type: "integer"
format: "int64"
logic:
type: string
config:
type: object
responses:
"204":
description: successful operation
"""
return await super().http_add(request)
@request_mapping(path="/{id}", method="PUT", auth_required=False)
async def http_update(self, request):
"""
---
description: Update a kettle
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Created an kettle
required: false
schema:
type: object
properties:
name:
type: string
sensor:
type: "integer"
format: "int64"
heater:
type: "integer"
format: "int64"
agitator:
type: "integer"
format: "int64"
target_temp:
type: "integer"
format: "int64"
logic:
type: string
config:
type: object
responses:
"204":
description: successful operation
"""
return await super().http_update(request)
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete_one(self, request):
"""
---
description: Delete a kettle
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
return await super().http_delete_one(request)
def __init__(self, cbpi):
super().__init__(cbpi)
self.controller = cbpi.kettle
self.cbpi.register(self, "/kettle")
@request_mapping(path="/{id:\d+}/automatic", method="POST", auth_required=False)
async def http_automatic(self, request):
"""
---
description: Toggle Automatic
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
await self.controller.toggle_automtic(int(request.match_info['id']))
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/heater/on", auth_required=False)
async def http_heater_on(self, request):
"""
---
description: Kettle Heater on
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
await self.controller.heater_on(int(request.match_info['id']))
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/heater/off", auth_required=False)
async def http_heater_off(self, request):
"""
---
description: Kettle Heater off
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
await self.controller.heater_off(int(request.match_info['id']))
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/agitator/on", auth_required=False)
async def http_agitator_on(self, request):
"""
---
description: Kettle Agitator on
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
await self.controller.agitator_on(int(request.match_info['id']))
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/agitator/off", auth_required=False)
async def http_agitator_off(self, request):
"""
---
description: Kettle Agitator off
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
await self.controller.agitator_off(int(request.match_info['id']))
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/targettemp", auth_required=False)
async def http_taget_temp(self, request):
"""
---
description: Get Target Temp of kettle
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
kettle_id = int(request.match_info['id'])
temp = await self.controller.get_traget_temp(kettle_id)
return web.json_response(data=dict(target_temp=temp, kettle_id=kettle_id))
@request_mapping(path="/{id:\d+}/temp/{temp:\d+}", method="PUT", auth_required=False)
async def http_set_taget_temp(self, request):
"""
---
description: Get Target Temp of kettle
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
kettle_id = int(request.match_info['id'])
target_temp = int(request.match_info['temp'])
await self.cbpi.bus.fire(topic="kettle/%s/targettemp" % kettle_id, kettle_id=kettle_id, target_temp=target_temp)
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/temp", auth_required=False)
async def http_temp(self, request):
"""
---
description: Get Temp of kettle
tags:
- Kettle
parameters:
- name: "id"
in: "path"
description: "Kettle ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
kettle_id = int(request.match_info['id'])
temp = await self.controller.get_temp(kettle_id)
return web.Response(status=204) return web.Response(status=204)

View file

@ -1,186 +1,72 @@
import asyncio
import os
from aiohttp import web from aiohttp import web
from cbpi.api import *
auth = False
from cbpi.api import request_mapping class SensorHttpEndpoints():
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
class SensorHttpEndpoints(HttpCrudEndpoints):
def __init__(self, cbpi): def __init__(self, cbpi):
super().__init__(cbpi) self.cbpi = cbpi
self.controller = cbpi.sensor self.controller = cbpi.sensor
self.cbpi.register(self, "/sensor") self.cbpi.register(self, "/sensor")
@request_mapping(path="/types", auth_required=False)
async def get_types(self, request):
"""
---
description: Get all sensor types
tags:
- Sensor
responses:
"200":
description: successful operation
"""
return await super().get_types(request)
@request_mapping(path="/", auth_required=False) @request_mapping(path="/", auth_required=False)
async def http_get_all(self, request): async def http_get_all(self, request):
""" """
--- ---
description: Get all sensor description: Switch actor on
tags: tags:
- Sensor - Sensor
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
return await super().http_get_all(request) return web.json_response(data=self.controller.get_state())
@request_mapping(path="/{id:\d+}", auth_required=False)
async def http_get_one(self, request):
"""
---
description: Get an sensor
tags:
- Sensor
parameters:
- name: "id"
in: "path"
description: "Sensor ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
return await super().http_get_one(request)
@request_mapping(path="/", method="POST", auth_required=False) @request_mapping(path="/", method="POST", auth_required=False)
async def http_add(self, request): async def http_add(self, request):
""" """
--- ---
description: Get one sensor description: add one Actor
tags: tags:
- Sensor - Sensor
parameters: parameters:
- in: body - in: body
name: body name: body
description: Created an sensor description: Created an actor
required: false required: true
schema: schema:
type: object type: object
properties: properties:
name: name:
type: string type: string
type: type:
type: string type: string
config: props:
type: object type: object
example:
name: "Actor 1"
type: "CustomActor"
props: {}
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
return await super().http_add(request) data = await request.json()
response_data = await self.controller.add(data)
return web.json_response(data=self.controller.create_dict(response_data))
@request_mapping(path="/{id}", method="PUT", auth_required=False) @request_mapping(path="/{id}", method="PUT", auth_required=False)
async def http_update(self, request): async def http_update(self, request):
""" """
--- ---
description: Update an sensor description: Update an actor
tags:
- Sensor
parameters:
- name: "id"
in: "path"
description: "Sensor ID"
required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Update an sensor
required: false
schema:
type: object
properties:
name:
type: string
type:
type: string
config:
type: object
responses:
"200":
description: successful operation
"""
return await super().http_update(request)
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete_one(self, request):
"""
---
description: Delete an sensor
tags:
- Sensor
parameters:
- name: "id"
in: "path"
description: "Sensor ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
return await super().http_delete_one(request)
@request_mapping(path="/{id:\d+}/log", auth_required=False)
async def http_get_log(self, request):
sensor_id = request.match_info['id']
resp = web.StreamResponse(status=200, reason='OK', headers={'Content-Type': 'text/html'})
await resp.prepare(request)
for filename in sorted(os.listdir("./logs/sensors"), reverse=True):
if filename.startswith("sensor_%s" % sensor_id):
with open(os.path.join("./logs/sensors/%s" % filename), 'r') as myfile:
await resp.write(str.encode(myfile.read()))
return resp
@request_mapping(path="/{id:\d+}/log", method="DELETE", auth_required=False)
async def http_clear_log(self, request):
sensor_id = request.match_info['id']
for filename in sorted(os.listdir("./logs/sensors"), reverse=True):
if filename == "sensor_%s.log" % sensor_id:
with open(os.path.join("./logs/sensors/%s" % filename), 'w'):
pass
continue
if filename.startswith("sensor_%s" % sensor_id):
os.remove(os.path.join("./logs/sensors/%s" % filename))
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/action", method="POST", auth_required=False)
async def http_action(self, request) -> web.Response:
"""
---
description: Execute action on sensor
tags: tags:
- Sensor - Sensor
parameters: parameters:
@ -199,13 +85,122 @@ class SensorHttpEndpoints(HttpCrudEndpoints):
properties: properties:
name: name:
type: string type: string
type:
type: string
config: config:
props: object
responses:
"200":
description: successful operation
"""
id = request.match_info['id']
data = await request.json()
return web.json_response(data=self.controller.create_dict(await self.controller.update(id, data)))
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete_one(self, request):
"""
---
description: Delete an actor
tags:
- Sensor
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "string"
responses:
"204":
description: successful operation
"""
id = request.match_info['id']
await self.controller.delete(id)
return web.Response(status=204)
@request_mapping(path="/{id}/on", method="POST", auth_required=False)
async def http_on(self, request) -> web.Response:
"""
---
description: Switch actor on
tags:
- Sensor
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "string"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
id = request.match_info['id']
await self.controller.on(id)
return web.Response(status=204)
@request_mapping(path="/{id}/off", method="POST", auth_required=False)
async def http_off(self, request) -> web.Response:
"""
---
description: Switch actor on
tags:
- Sensor
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "string"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
id = request.match_info['id']
await self.controller.off(id)
return web.Response(status=204)
@request_mapping(path="/{id}/action", method="POST", auth_required=auth)
async def http_action(self, request) -> web.Response:
"""
---
description: Toogle an actor on or off
tags:
- Sensor
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Update an actor
required: false
schema:
type: object
properties:
name:
type: string
parameter:
type: object type: object
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
sensor_id = int(request.match_info['id']) actor_id = request.match_info['id']
data = await request.json()
await self.controller.call_action(actor_id, data.get("name"), data.get("parameter"))
await self.cbpi.bus.fire(topic="sensor/%s/action" % sensor_id, sensor_id=sensor_id, data=await request.json())
return web.Response(status=204) return web.Response(status=204)

View file

@ -1,257 +1,211 @@
from aiohttp import web from aiohttp import web
from cbpi.api import * from cbpi.api import *
class StepHttpEndpoints():
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
class StepHttpEndpoints(HttpCrudEndpoints):
def __init__(self, cbpi): def __init__(self, cbpi):
super().__init__(cbpi) self.cbpi = cbpi
self.controller = cbpi.step self.controller = cbpi.step
self.cbpi.register(self, "/step") self.cbpi.register(self, "/step2")
def create_dict(self, data):
return dict(name=data["name"], id=data["id"], type=data.get("type"), status=data["status"],props=data["props"], state_text=data["instance"].get_state())
@request_mapping(path="/types", auth_required=False) @request_mapping(path="/", auth_required=False)
async def get_types(self, request): async def http_get_all(self, request):
""" """
--- ---
description: Get all step types description: Get all steps
tags: tags:
- Step - Step
responses: responses:
"200": "200":
description: successful operation description: successful operation
""" """
return await super().get_types(request) return web.json_response(data=self.controller.get_state())
@request_mapping(path="/", auth_required=False)
async def http_get_all(self, request):
"""
---
description: Get all steps
tags:
- Step
responses:
"204":
description: successful operation
"""
return await super().http_get_all(request)
@request_mapping(path="/{id:\d+}", auth_required=False)
async def http_get_one(self, request):
"""
---
description: Get one step
tags:
- Step
parameters:
- name: "id"
in: "path"
description: "step ID"
required: true
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Met
"""
return await super().http_get_one(request)
@request_mapping(path="/", method="POST", auth_required=False) @request_mapping(path="/", method="POST", auth_required=False)
async def http_add(self, request): async def http_add(self, request):
""" """
--- ---
description: Get one step description: Add
tags: tags:
- Step - Step
parameters: parameters:
- in: body - in: body
name: body name: body
description: Created an step description: Created an step
required: false
schema:
type: object
properties:
name:
type: string
type:
type: string
config:
type: object
responses:
"204":
description: successful operation
"""
return await super().http_add(request)
@request_mapping(path="/{id}", method="PUT", auth_required=False)
async def http_update(self, request):
"""
---
description: Update an step
tags:
- Step
parameters:
- name: "id"
in: "path"
description: "step ID"
required: true required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Update an step
required: false
schema: schema:
type: object type: object
properties:
name:
type: string
type:
type: string
config:
type: object
responses: responses:
"200": "200":
description: successful operation description: successful operation
""" """
return await super().http_update(request)
@request_mapping(path="/{id}", method="DELETE", auth_required=False) data = await request.json()
async def http_delete_one(self, request): result = await self.controller.add(data)
return web.json_response(self.create_dict(result))
@request_mapping(path="/{id}", method="PUT", auth_required=False)
async def http_update(self, request):
""" """
--- ---
description: Delete a step description: Update
tags: tags:
- Step - Step
parameters: parameters:
- name: "id" - in: body
in: "path" name: body
description: "Step ID" description: Created an kettle
required: true required: false
type: "integer" schema:
format: "int64" type: object
responses: responses:
"204": "200":
description: successful operation
"""
return await super().http_delete_one(request)
@request_mapping(path="/", method="DELETE", auth_required=False)
async def http_delete_all(self, request):
"""
---
description: Delete all step
tags:
- Step
responses:
"204":
description: successful operation description: successful operation
""" """
await self.cbpi.bus.fire("step/clear")
return web.Response(status=204)
@request_mapping(path="/action", method="POST", auth_required=False, json_schema={"action": str, "parameter": dict})
async def http_action(self, request):
"""
---
description: Call Step Action
tags:
- Step
parameters:
- in: body
name: body
description: Step Action
required: true
schema:
type: object
properties:
action:
type: string
parameter:
type: object
produces:
- application/json
responses:
"204":
description: successful operation
"405":
description: invalid HTTP Method
"""
data = await request.json() data = await request.json()
await self.cbpi.bus.fire("step/action", name=data["action"], parameter=data["parameter"]) id = request.match_info['id']
return web.Response(text="OK") result = await self.controller.update(id, data)
print("RESULT", result)
return web.json_response(self.create_dict(result))
@request_mapping(path="/start", auth_required=False) @request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_start(self, request): async def http_delete(self, request):
""" """
--- ---
description: Start Brewing Process description: Delete
tags: tags:
- Step - Step
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
if self.controller.is_running(): id = request.match_info['id']
raise CBPiException("Brewing Process Already Running") await self.controller.delete(id)
print("FIRE START FROM HTTP")
await self.cbpi.bus.fire("step/start")
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/next", method="POST", auth_required=False)
@request_mapping(path="/reset", auth_required=False)
async def http_reset(self, request):
"""
---
description: Reset Brewing Process
tags:
- Step
responses:
"204":
description: successful operation
"""
await self.cbpi.bus.fire("step/reset")
return web.Response(text="OK")
@request_mapping(path="/next", auth_required=False)
async def http_next(self, request): async def http_next(self, request):
""" """
--- ---
description: Start next step description: Next
tags: tags:
- Step - Step
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
await self.cbpi.bus.fire("step/next")
await self.controller.next()
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/stop", auth_required=False)
async def http_stop(self, request): @request_mapping(path="/move", method="PUT", auth_required=False)
async def http_move(self, request):
""" """
--- ---
description: Stop next step description: Move
tags: tags:
- Step - Step
parameters:
- in: body
name: body
description: Created an kettle
required: false
schema:
type: object
properties:
id:
type: string
direction:
type: "integer"
format: "int64"
responses: responses:
"204": "204":
description: successful operation description: successful operation
""" """
await self.cbpi.bus.fire("step/stop")
return web.Response(status=204)
@request_mapping(path="/sort", method="POST", auth_required=False)
async def http_sort(self, request):
data = await request.json() data = await request.json()
await self.cbpi.bus.fire("step/sort", data=data) await self.controller.move(data["id"], data["direction"])
return web.Response(status=204) return web.Response(status=204)
@request_mapping(path="/start", method="POST", auth_required=False)
async def http_start(self, request):
"""
---
description: Move
tags:
- Step
responses:
"204":
description: successful operation
"""
await self.controller.start()
return web.Response(status=204)
@request_mapping(path="/stop", method="POST", auth_required=False)
async def http_stop(self, request):
"""
---
description: Stop Step
tags:
- Step
responses:
"204":
description: successful operation
"""
await self.controller.stop()
return web.Response(status=204)
@request_mapping(path="/reset", method="POST", auth_required=False)
async def http_reset(self, request):
"""
---
description: Move
tags:
- Step
responses:
"204":
description: successful operation
"""
await self.controller.reset_all()
return web.Response(status=204)
@request_mapping(path="/basic", method="PUT", auth_required=False)
async def http_save_basic(self, request):
"""
---
description: Move
tags:
- Step
responses:
"204":
description: successful operation
"""
data = await request.json()
await self.controller.save_basic(data)
return web.Response(status=204)

View file

@ -1,200 +0,0 @@
from aiohttp import web
from cbpi.api import *
from cbpi.http_endpoints.http_curd_endpoints import HttpCrudEndpoints
class StepHttpEndpoints2():
def __init__(self, cbpi):
self.cbpi = cbpi
self.controller = cbpi.step2
self.cbpi.register(self, "/step2")
def create_dict(self, data):
return dict(name=data["name"], id=data["id"], type=data.get("type"), status=data["status"],props=data["props"], state_text=data["instance"].get_state())
@request_mapping(path="/", auth_required=False)
async def http_get_all(self, request):
"""
---
description: Get all steps
tags:
- Step2
responses:
"200":
description: successful operation
"""
return web.json_response(data=self.controller.get_state())
@request_mapping(path="/", method="POST", auth_required=False)
async def http_add(self, request):
"""
---
description: Add
tags:
- Step2
parameters:
- in: body
name: body
description: Created an step
required: true
schema:
type: object
responses:
"200":
description: successful operation
"""
data = await request.json()
result = await self.controller.add(data)
print("RESULT", result)
return web.json_response(self.create_dict(result))
@request_mapping(path="/{id}", method="PUT", auth_required=False)
async def http_update(self, request):
"""
---
description: Update
tags:
- Step2
parameters:
- in: body
name: body
description: Created an kettle
required: false
schema:
type: object
responses:
"200":
description: successful operation
"""
data = await request.json()
id = request.match_info['id']
result = await self.controller.update(id, data)
print("RESULT", result)
return web.json_response(self.create_dict(result))
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete(self, request):
"""
---
description: Delete
tags:
- Step2
responses:
"204":
description: successful operation
"""
id = request.match_info['id']
await self.controller.delete(id)
return web.Response(status=204)
@request_mapping(path="/next", method="POST", auth_required=False)
async def http_next(self, request):
"""
---
description: Next
tags:
- Step2
responses:
"204":
description: successful operation
"""
await self.controller.next()
return web.Response(status=204)
@request_mapping(path="/move", method="PUT", auth_required=False)
async def http_move(self, request):
"""
---
description: Move
tags:
- Step2
parameters:
- in: body
name: body
description: Created an kettle
required: false
schema:
type: object
properties:
id:
type: string
direction:
type: "integer"
format: "int64"
responses:
"204":
description: successful operation
"""
data = await request.json()
print("MOVE", data)
await self.controller.move(data["id"], data["direction"])
return web.Response(status=204)
@request_mapping(path="/start", method="POST", auth_required=False)
async def http_start(self, request):
"""
---
description: Move
tags:
- Step2
responses:
"204":
description: successful operation
"""
await self.controller.start()
return web.Response(status=204)
@request_mapping(path="/stop", method="POST", auth_required=False)
async def http_stop(self, request):
"""
---
description: Stop Step
tags:
- Step2
responses:
"204":
description: successful operation
"""
await self.controller.stop()
return web.Response(status=204)
@request_mapping(path="/reset", method="POST", auth_required=False)
async def http_reset(self, request):
"""
---
description: Move
tags:
- Step2
responses:
"204":
description: successful operation
"""
print("RESE HTTP")
await self.controller.reset_all()
return web.Response(status=204)

View file

@ -26,9 +26,7 @@ class SystemHttpEndpoints:
actor=self.cbpi.actor.get_state(), actor=self.cbpi.actor.get_state(),
sensor=self.cbpi.sensor.get_state(), sensor=self.cbpi.sensor.get_state(),
kettle=self.cbpi.kettle.get_state(), kettle=self.cbpi.kettle.get_state(),
step=self.cbpi.step2.get_state(), step=self.cbpi.step.get_state(),
dashboard=self.cbpi.dashboard.get_state(),
translations=self.cbpi.translation.get_all(),
config=self.cbpi.config.get_state()) config=self.cbpi.config.get_state())
, dumps=json_dumps) , dumps=json_dumps)

View file

@ -1,54 +0,0 @@
from aiohttp import web
from aiohttp_auth import auth
from cbpi.api import *
class TranslationHttpEndpoint():
def __init__(self,cbpi):
self.cbpi = cbpi
self.cbpi.register(self, url_prefix="/translation")
@request_mapping(path="/missing_key", method="POST", auth_required=False)
async def missing_key(self, request):
"""
---
description: Add missing translation key
tags:
- Translation
parameters:
- in: body
name: body
description: missing key data
required: true
schema:
type: object
properties:
locale:
type: string
key:
type: string
responses:
"204":
description: successful operation
"""
data = await request.json()
await self.cbpi.translation.add_key(**data)
return web.Response(status=204)
@request_mapping(path="/", auth_required=False)
async def http_get_all(self, request):
"""
---
description: Get all translations
tags:
- Translation
responses:
"200":
description: successful operation
"""
return web.json_response(data=self.cbpi.translation.get_all())

View file

@ -2,7 +2,7 @@
python3.7 setup.py clean --all python3.7 setup.py clean --all
#build #build
python setup.py sdist python3 setup.py sdist
#Upload #Upload
twine upload dist/* twine upload dist/*

25
config/actor.json Normal file
View file

@ -0,0 +1,25 @@
{
"data": [
{
"id": "9NkxVioA7FfZi6waegi246",
"name": "Manuel",
"props": {},
"state": {},
"type": "CustomActor"
},
{
"id": "5hk68r3pFBe6JoRXzavLCA",
"name": "Actor 1",
"props": {},
"state": {},
"type": "CustomActor"
},
{
"id": "YgVFNsXncfMMoHD7U6TvP6",
"name": "111",
"props": {},
"state": {},
"type": "CustomActor"
}
]
}

View file

@ -244,6 +244,36 @@
"type": "Text", "type": "Text",
"x": 615, "x": 615,
"y": 280 "y": 280
},
{
"id": "3a9e422f-8d55-4360-8f16-807f9a657988",
"name": "Steps",
"props": {
"width": "400"
},
"type": "Steps",
"x": 20,
"y": 430
},
{
"id": "3be00e94-4e06-4a6b-9b8d-c832be73386a",
"name": "Led",
"props": {
"actor": 1
},
"type": "Led",
"x": 440,
"y": 215
},
{
"id": "d896b230-8dab-4c33-b73a-1dd74e6de906",
"name": "Led",
"props": {
"actor": 1
},
"type": "Led",
"x": 625,
"y": 215
} }
], ],
"pathes": [ "pathes": [

26
config/kettle.json Normal file
View file

@ -0,0 +1,26 @@
{
"data": [
{
"agitator": "9NkxVioA7FfZi6waegi246",
"heater": "9NkxVioA7FfZi6waegi246",
"id": "gJ6jCupRmpxRsweY9nANTp",
"name": "Kettle 233312312",
"props": {},
"sensor": "TPpjzj9YXh6yYzvyJycmig",
"state": {},
"target_temp": 22,
"type": "CustomKettleLogic"
},
{
"agitator": "9NkxVioA7FfZi6waegi246",
"heater": "9NkxVioA7FfZi6waegi246",
"id": "RMjMvwphxt3aiMrTnHbpcB",
"name": "Test",
"props": {},
"sensor": "TPpjzj9YXh6yYzvyJycmig",
"state": {},
"target_temp": 22,
"type": "CustomKettleLogic"
}
]
}

21
config/sensor.json Normal file
View file

@ -0,0 +1,21 @@
{
"data": [
{
"id": "TPpjzj9YXh6yYzvyJycmig",
"name": "AMAZING22211111123123",
"props": {
"param1": "HALLO",
"param2": "Test"
},
"status": null,
"type": "CustomSensor2"
},
{
"id": "2rAviwweTUY27Y8yZKftWA",
"name": "Testasdfasdf",
"props": {},
"status": null,
"type": "CustomSensor2"
}
]
}

View file

@ -1,31 +1,56 @@
{ {
"basic": {}, "basic": {
"name": "Weissbier"
},
"profile": [ "profile": [
{ {
"id": "eopJy6oxGqrNuRNtiAPXvN", "id": "eopJy6oxGqrNuRNtiAPXvN",
"name": "AMAZING", "name": "Step1",
"props": { "props": {
"count": 5, "Param1": "1",
"Param2": "HALLO",
"Param3": 1,
"count": 8,
"wohoo": 0 "wohoo": 0
}, },
"status": "P", "status": "P",
"type": "CustomStep2" "type": "CustomStep2"
}, },
{ {
"id": "duxvgLknKLjGYhdm9TKqUE", "id": "hyXYDBUAENgwD7yNwaeLe7",
"name": "Manuel", "name": "Step2",
"props": { "props": {
"count": 5, "Param1": "123",
"Param2": "Parameter2",
"Param3": 2,
"count": 0,
"wohoo": 0 "wohoo": 0
}, },
"status": "I", "status": "I",
"type": "CustomStep2" "type": "CustomStep2"
}, },
{ {
"id": "hyXYDBUAENgwD7yNwaeLe7", "id": "iJHU9FgeGBtvDhraEHUoP2",
"name": "HALLO", "name": "Step3",
"props": { "props": {
"count": 5, "Param1": 123,
"Param2": "HALLO",
"Param3": 2,
"Param5": 1,
"count": 0,
"wohoo": 0
},
"status": "I",
"type": "CustomStep2"
},
{
"id": "duxvgLknKLjGYhdm9TKqUE",
"name": "Step4",
"props": {
"Param1": "1222",
"Param2": "HELLO",
"Param3": 2,
"count": 0,
"wohoo": 0 "wohoo": 0
}, },
"status": "I", "status": "I",

Binary file not shown.

View file

@ -1 +0,0 @@
{"people": [{"name": "Scott", "website": "stackabuse.com", "from": "Nebraska"}, {"name": "Larry", "website": "google.com", "from": "Michigan"}, {"name": "Tim", "website": "apple.com", "from": "Alabama"}]}

View file

@ -1,40 +0,0 @@
aiohttp==3.7.3
aiohttp-auth==0.1.1
aiohttp-route-decorator==0.1.4
aiohttp-security==0.4.0
aiohttp-session==2.9.0
aiohttp-swagger==1.0.15
aiojobs==0.3.0
aiosqlite==0.16.0
asn1crypto==1.4.0
astroid==2.4.2
async-timeout==3.0.1
attrs==20.3.0
certifi==2020.12.5
cffi==1.14.4
chardet==3.0.4
cryptography==3.3.1
idna==2.10
isort==5.7.0
Jinja2==2.11.2
lazy-object-proxy==1.4.3
MarkupSafe==1.1.1
mccabe==0.6.1
multidict==4.7.6
numpy==1.19.4
pandas==1.2.0
pycparser==2.20
pyfiglet==0.8.post1
pylint==2.6.0
python-dateutil==2.8.1
pytz==2020.5
PyYAML==5.3.1
requests==2.25.1
six==1.15.0
ticket-auth==0.1.4
toml==0.10.2
typing-extensions==3.7.4.3
urllib3==1.26.2
voluptuous==0.12.1
wrapt==1.12.1
yarl==1.6.3

View file

@ -1,51 +0,0 @@
function noop() {}
export default function (url, opts) {
opts = opts || {};
var ws, num=0, $={};
var max = opts.maxAttempts || Infinity;
$.open = function () {
ws = new WebSocket(url, opts.protocols || []);
ws.onmessage = opts.onmessage || noop;
ws.onopen = function (e) {
(opts.onopen || noop)(e);
num = 0;
};
ws.onclose = function (e) {
e.code === 1e3 || e.code === 1005 || $.reconnect(e);
(opts.onclose || noop)(e);
};
ws.onerror = function (e) {
(e && e.code==='ECONNREFUSED') ? $.reconnect(e) : (opts.onerror || noop)(e);
};
};
$.reconnect = function (e) {
(num++ < max) ? setTimeout(function () {
(opts.onreconnect || noop)(e);
$.open();
}, opts.timeout || 1e3) : (opts.onmaximum || noop)(e);
};
$.json = function (x) {
ws.send(JSON.stringify(x));
};
$.send = function (x) {
ws.send(x);
};
$.close = function (x, y) {
ws.close(x || 1e3, y);
};
$.open(); // init
return $;
}

View file

View file

@ -1,7 +1,8 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
from cbpi import __version__
setup(name='cbpi', setup(name='cbpi',
version='4.0.0.5', version=__version__,
description='CraftBeerPi', description='CraftBeerPi',
author='Manuel Fritsch', author='Manuel Fritsch',
author_email='manuel@craftbeerpi.com', author_email='manuel@craftbeerpi.com',
@ -14,19 +15,21 @@ setup(name='cbpi',
'cbpi': ['*','*.txt', '*.rst', '*.yaml']}, 'cbpi': ['*','*.txt', '*.rst', '*.yaml']},
install_requires=[ install_requires=[
"aiohttp==3.4.4", "aiohttp==3.7.3",
"aiohttp-auth==0.1.1", "aiohttp-auth==0.1.1",
"aiohttp-route-decorator==0.1.4", "aiohttp-route-decorator==0.1.4",
"aiohttp-security==0.4.0", "aiohttp-security==0.4.0",
"aiohttp-session==2.7.0", "aiohttp-session==2.9.0",
"aiohttp-swagger==1.0.5", "aiohttp-swagger==1.0.15",
"aiojobs==0.2.2", "aiojobs==0.3.0",
"aiosqlite==0.7.0", "aiosqlite==0.16.0",
"cryptography==2.3.1", "cryptography==3.3.1",
"requests==2.22.0", "requests==2.25.1",
"voluptuous==0.11.5", "voluptuous==0.12.1",
"pyfiglet==0.7.6", "pyfiglet==0.8.post1",
'pandas==0.25.0' 'pandas==1.2.0',
'shortuuid==1.0.1',
'tabulate==0.8.7'
], ],
dependency_links=[ dependency_links=[
'https://testpypi.python.org/pypi' 'https://testpypi.python.org/pypi'