diff --git a/cbpi/__init__.py b/cbpi/__init__.py index 4c7fb7b..37ab84d 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1 +1 @@ -__version__ = "4.0.0.15" \ No newline at end of file +__version__ = "4.0.0.16" \ No newline at end of file diff --git a/cbpi/api/__init__.py b/cbpi/api/__init__.py index d629a95..c265b9a 100644 --- a/cbpi/api/__init__.py +++ b/cbpi/api/__init__.py @@ -9,7 +9,6 @@ __all__ = ["CBPiActor", "parameters", "background_task", "CBPiKettleLogic", - "CBPiSimpleStep", "CBPiException", "KettleException", "SensorException", diff --git a/cbpi/api/actor.py b/cbpi/api/actor.py index 6b91169..a26619c 100644 --- a/cbpi/api/actor.py +++ b/cbpi/api/actor.py @@ -1,6 +1,5 @@ from abc import ABCMeta import asyncio -from cbpi.api.extension import CBPiExtension from cbpi.api.config import ConfigType __all__ = ["CBPiActor"] diff --git a/cbpi/api/step.py b/cbpi/api/step.py index 9e09c13..a6943da 100644 --- a/cbpi/api/step.py +++ b/cbpi/api/step.py @@ -4,11 +4,12 @@ import asyncio import logging from abc import abstractmethod, ABCMeta import logging +from cbpi.api.config import ConfigType class CBPiStep(metaclass=ABCMeta): def __init__(self, cbpi, id, name, props) : self.cbpi = cbpi - self.props = {"wohoo": 0, "count": 5, **props} + self.props = {**props} self.id = id self.name = name self.status = 0 @@ -45,168 +46,62 @@ class CBPiStep(metaclass=ABCMeta): while self.running: try: await self.execute() - except: + except Exception as e: self._exception_count += 1 logging.error("Step has thrown exception") if self._exception_count >= self._max_exceptions: self.stop_reason = "MAX_EXCEPTIONS" return (self.id, self.stop_reason) await asyncio.sleep(1) - + return (self.id, self.stop_reason) + @abstractmethod async def execute(self): pass -class CBPiSimpleStep(metaclass=ABCMeta): + def get_static_config_value(self,name,default): + return self.cbpi.static_config.get(name, default) - managed_fields = [] + def get_config_value(self,name,default): + return self.cbpi.config.get(name, default=default) - def __init__(self, cbpi="", managed_fields=[], id="", name="", *args, **kwargs): - self.logger = logging.getLogger(__name__) - self._exception_count = 0 - self._interval = 0.1 - self._max_exceptions = 2 - self.__dirty = False - self.cbpi = cbpi - self.id = id - self.name = name + async def set_config_value(self,name,value): + return await self.cbpi.config.set(name,value) - if managed_fields: - self.managed_fields = managed_fields - for a in managed_fields: - super(CBPiSimpleStep, self).__setattr__(a, kwargs.get(a, None)) + async def add_config_value(self, name, value, type: ConfigType, description, options=None): + await self.cbpi.add(name, value, type, description, options=None) - self.is_stopped = False - self.is_next = False - self.start = time.time() + def get_kettle(self,id): + return self.cbpi.kettle.find_by_id(id) - self.logger.info(self.__repr__()) + async def set_target_temp(self,id, temp): + await self.cbpi.kettle.set_target_temp(id, temp) - def __repr__(self) -> str: - mf = {} - has_cbpi = True if self.cbpi is not None else False - for f in self.managed_fields: - mf[f] = super(CBPiSimpleStep, self).__getattribute__(f) - return json.dumps(dict(type=self.__class__.__name__, id=self.id, name=self.name, has_link_to_cbpi=has_cbpi, managed_fields=mf)) + def get_sensor(self,id): + return self.cbpi.sensor.find_by_id(id) - def get_status(self): - pass + def get_actor(self,id): + return self.cbpi.actor.find_by_id(id) - def running(self): - ''' - Method checks if the step should continue running. - The method will return False if the step is requested to stop or the next step should start + def get_actor_state(self,id): + try: + actor = self.cbpi.actor.find_by_id(id) + return actor.get("instance").get_state() + except: + logging.error("Faild to read actor state in step - actor {}".format(id)) + return None + + async def actor_on(self,id): - :return: True if the step is running. Otherwise False. - ''' - if self.is_next is True: - return False + try: + await self.cbpi.actor.on(id) + except: + pass - if self.is_stopped is True: - return False - - return True - - async def run(self): - - #while self.running(): - # print(".... Step %s ...." % self.id) - # await asyncio.sleep(0.1) - ''' - This method in running in the background. It invokes the run_cycle method in the configured interval - It checks if a managed variable was modified in the last exection cycle. If yes, the method will persisit the new value of the - managed property - - :return: None - ''' - - while self.running(): - - try: - await self.run_cycle() - except Exception as e: - logging.exception("CBPiSimpleStep Error") - self._exception_count = self._exception_count + 1 - if self._exception_count == self._max_exceptions: - self.logger.error("Step Exception limit exceeded. Stopping Step") - self.stop() - - await asyncio.sleep(self._interval) - - if self.is_dirty(): - # Now we have to store the managed props - state = {} - for field in self.managed_fields: - - state[field] = self.__getattribute__(field) - - await self.cbpi.step.model.update_step_state(self.id, state) - await self.cbpi.bus.fire("step/update") - self.reset_dirty() - - - @abstractmethod - async def run_cycle(self): - ''' - This method is executed in the defined interval. - That the place to put your step logic. - The method need to be overwritten in the Ccstom step implementaion - - :return: None - ''' - - print("NOTING IMPLEMENTED") - pass - - def next(self): - - ''' - Request to stop the the step - - :return: None - ''' - - self.is_next = True - - def stop(self): - ''' - Request to stop the step - - :return: None - ''' - self.is_stopped = True - - def reset(self): - ''' - Reset the step. This method needs to be overwritten by the custom step implementation - - :return: None - ''' - pass - - def is_dirty(self): - - ''' - Check if a managed variable has a new value - - :return: True if at least one managed variable has a new value assigend. Otherwise False - ''' - return self.__dirty - - def reset_dirty(self): - ''' - Reset the dirty flag - - :return: - ''' - - self.__dirty = False - - def __setattr__(self, name, value): - if name != "_Step__dirty" and name in self.managed_fields: - self.__dirty = True - super(CBPiSimpleStep, self).__setattr__(name, value) - else: - super(CBPiSimpleStep, self).__setattr__(name, value) + async def actor_off(self,id): + try: + await self.cbpi.actor.off(id) + except: + pass \ No newline at end of file diff --git a/cbpi/config/config.yaml b/cbpi/config/config.yaml index e4407dc..a7116f8 100644 --- a/cbpi/config/config.yaml +++ b/cbpi/config/config.yaml @@ -10,5 +10,5 @@ username: cbpi password: 123 plugins: -- cbpi4-ui +- cbpi4ui diff --git a/cbpi/config/step_data.json b/cbpi/config/step_data.json index 0eaecf6..4bd1d70 100644 --- a/cbpi/config/step_data.json +++ b/cbpi/config/step_data.json @@ -1,20 +1,8 @@ { "basic": { - "name": "WOOHOo" + "name": "" }, "profile": [ - { - "id": "MSXuATqL56EAeCXrg3XLuY", - "name": "Test", - "props": { - "Param1": 123, - "Param2": "HALLO", - "Param3": 1, - "count": 6, - "wohoo": 0 - }, - "status": "D", - "type": "CustomStep2" - } + ] } \ No newline at end of file diff --git a/cbpi/controller/basic_controller.py b/cbpi/controller/basic_controller.py index 9df4915..22ae342 100644 --- a/cbpi/controller/basic_controller.py +++ b/cbpi/controller/basic_controller.py @@ -90,7 +90,6 @@ class BasicController: type = item["type"] clazz = self.types[type]["class"] - print("#####",item) item["instance"] = clazz(self.cbpi, item["id"], item["props"]) await item["instance"].start() item["instance"].task = self._loop.create_task(item["instance"].run()) diff --git a/cbpi/controller/step_controller.py b/cbpi/controller/step_controller.py index 58029a5..8edf9c6 100644 --- a/cbpi/controller/step_controller.py +++ b/cbpi/controller/step_controller.py @@ -227,7 +227,7 @@ class StepController: 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 = self._loop.create_task(step["instance"].run()) step["instance"].task .add_done_callback(self.done) step["status"] = "A" diff --git a/cbpi/extension/dummystep/__init__.py b/cbpi/extension/dummystep/__init__.py deleted file mode 100644 index ae59001..0000000 --- a/cbpi/extension/dummystep/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -import asyncio -import time - -from cbpi.api import * - - -@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 Step2(CBPiStep): - - @action(key="name2", parameters=[]) - async def action2(self, **kwargs): - print("CALL ACTION") - - @action(key="name", parameters=[Property.Number(label="Test", configurable=True)]) - async def action(self, **kwargs): - print("CALL ACTION") - - async def execute(self): - count = self.props.get("count", 0) - self.state_msg = "COUNT %s" % count - - self.props["count"] += 1 - await self.update(self.props) - - if count >= 5: - self.next() - - async def reset(self): - self.props["count"] = 0 - -def setup(cbpi): - ''' - This method is called by the server during startup - Here you need to register your plugins at the server - - :param cbpi: the cbpi core - :return: - ''' - cbpi.plugin.register("CustomStep2", Step2) - diff --git a/cbpi/extension/dummystep/config.yaml b/cbpi/extension/dummystep/config.yaml deleted file mode 100644 index 52bcfb1..0000000 --- a/cbpi/extension/dummystep/config.yaml +++ /dev/null @@ -1,3 +0,0 @@ -name: DummyStep -version: 4 -active: true \ No newline at end of file diff --git a/cbpi/extension/gpioactor/__init__.py b/cbpi/extension/gpioactor/__init__.py index 50a15ae..84ef4a6 100644 --- a/cbpi/extension/gpioactor/__init__.py +++ b/cbpi/extension/gpioactor/__init__.py @@ -20,17 +20,17 @@ except Exception: import RPi.GPIO as GPIO -@parameters([Property.Select(label="GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]), Property.Select(label="Inverted", options=["Yes", "No"])]) +@parameters([Property.Select(label="GPIO", description="The GPIO Pin", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]), Property.Select(label="Inverted", description="No: Active on high; Yes: Active on low", options=["Yes", "No"])]) class GPIOActor(CBPiActor): def get_GPIO_state(self, state): # ON if state == 1: - return 0 if self.inverted == False else 1 + return 1 if self.inverted == False else 0 # OFF if state == 0: - return 1 if self.inverted == False else 0 + return 0 if self.inverted == False else 1 async def start(self): await super().start() diff --git a/cbpi/extension/mashstep/__init__.py b/cbpi/extension/mashstep/__init__.py new file mode 100644 index 0000000..ec748ee --- /dev/null +++ b/cbpi/extension/mashstep/__init__.py @@ -0,0 +1,38 @@ +import asyncio +import time +import random +from cbpi.api import * + + +@parameters([Property.Number(label="Timer", configurable=True), + Property.Number(label="Temp", configurable=True), + Property.Kettle(label="Kettle")]) +class MashStep(CBPiStep): + + + async def execute(self): + try: + kid = self.props.get("Kettle", None) + kettle = self.get_kettle(kid) + actor = self.get_actor(kettle.get("heater")) + print(self.get_actor_state(kettle.get("heater"))) + await self.cbpi.kettle.set_target_temp(kid, random.randint(0,50)) + if self.v is True: + await self.actor_on(kettle.get("heater")) + else: + await self.actor_off(kettle.get("heater")) + self.v = not self.v + except: + pass + + +def setup(cbpi): + ''' + This method is called by the server during startup + Here you need to register your plugins at the server + + :param cbpi: the cbpi core + :return: + ''' + cbpi.plugin.register("MashStep", MashStep) + diff --git a/cbpi/extension/mashstep/config.yaml b/cbpi/extension/mashstep/config.yaml new file mode 100644 index 0000000..c188586 --- /dev/null +++ b/cbpi/extension/mashstep/config.yaml @@ -0,0 +1,3 @@ +name: MashStep +version: 4 +active: true \ No newline at end of file diff --git a/config/cbpi_dashboard_1.json b/config/cbpi_dashboard_1.json index f2628ea..eabd696 100644 --- a/config/cbpi_dashboard_1.json +++ b/config/cbpi_dashboard_1.json @@ -41,6 +41,29 @@ "type": "ActorButton", "x": 505, "y": 140 + }, + { + "id": "3cae292a-f12d-4c9e-8e0e-2fd93a9b253e", + "name": "TargetTemp", + "props": { + "color": "#fff", + "kettle": "oHxKz3z5RjbsxfSz6KUgov", + "size": "12", + "unit": "\u00b0" + }, + "type": "TargetTemp", + "x": 160, + "y": 75 + }, + { + "id": "bb90e1ab-7b2d-4623-8df3-3139f91b7087", + "name": "Steps", + "props": { + "width": "200" + }, + "type": "Steps", + "x": 595, + "y": 50 } ], "pathes": [ diff --git a/config/kettle.json b/config/kettle.json index f10a774..e55c3bd 100644 --- a/config/kettle.json +++ b/config/kettle.json @@ -2,40 +2,13 @@ "data": [ { "agitator": "EsmZwWi9Qp3bzmXqq7N3Ly", - "heater": "YwGzXvWMpmbLb6XobesL8n", + "heater": "EsmZwWi9Qp3bzmXqq7N3Ly", "id": "oHxKz3z5RjbsxfSz6KUgov", "name": "MashTun", "props": {}, "sensor": "8ohkXvFA9UrkHLsxQL38wu", "state": {}, - "target_temp": 52, - "type": "CustomKettleLogic" - }, - { - "agitator": "", - "heater": "", - "id": "WxAkesrkqiHH3Gywc4fMci", - "name": "HLT", - "props": { - "Param2": "13", - "Param3": 1, - "Param4": "", - "Param5": "8BLRqagLicCdEBDdc77Sgr" - }, - "sensor": "", - "state": {}, - "target_temp": null, - "type": "" - }, - { - "agitator": "", - "heater": "NjammuygecdvMpoGYc3rXt", - "id": "a7bWex85Z9Td4atwgazpXW", - "name": "Boil", - "props": {}, - "sensor": "", - "state": {}, - "target_temp": 55, + "target_temp": 25, "type": "CustomKettleLogic" } ] diff --git a/config/step_data.json b/config/step_data.json index 4fdb233..6c26721 100644 --- a/config/step_data.json +++ b/config/step_data.json @@ -4,43 +4,15 @@ }, "profile": [ { - "id": "T2y34Mbex9KjNWXhzfCRby", - "name": "MashIn", + "id": "Gkjdsu45XPcfJimz4yHc4w", + "name": "Test", "props": { - "Param1": 123, - "Param2": "HALLO", - "Param3": 1, - "count": 1, - "wohoo": 0 + "Kettle": "oHxKz3z5RjbsxfSz6KUgov", + "Temp": "2", + "Timer": "1" }, "status": "P", - "type": "CustomStep2" - }, - { - "id": "RjS8Zb2GGpUtNsqHsES3yF", - "name": "Step2", - "props": { - "Param1": 123, - "Param2": "HALLO", - "Param3": 1, - "count": 0, - "wohoo": 0 - }, - "status": "I", - "type": "CustomStep2" - }, - { - "id": "WkZG4fDNxZdtZ7uoTsSHhR", - "name": "Mash Step 1", - "props": { - "Param1": 123, - "Param2": "HALLO", - "Param3": 1, - "count": 0, - "wohoo": 0 - }, - "status": "I", - "type": "CustomStep2" + "type": "MashStep" } ] } \ No newline at end of file diff --git a/setup.py b/setup.py index e32db7b..a124bd8 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ setup(name='cbpi', 'shortuuid==1.0.1', 'tabulate==0.8.7', 'asyncio-mqtt', - 'cbpi4-ui', + 'cbpi4ui', ], dependency_links=[ 'https://testpypi.python.org/pypi',