diff --git a/cbpi/__init__.py b/cbpi/__init__.py index b809b20..72f4575 100644 --- a/cbpi/__init__.py +++ b/cbpi/__init__.py @@ -1,3 +1,3 @@ -__version__ = "4.4.3" -__codename__ = "Yeast Starter" +__version__ = "4.5.0.a1" +__codename__ = "Summer break" diff --git a/cbpi/api/actor.py b/cbpi/api/actor.py index 941b729..55e5f5a 100644 --- a/cbpi/api/actor.py +++ b/cbpi/api/actor.py @@ -22,6 +22,7 @@ class CBPiActor(metaclass=ABCMeta): self.running = False self.power = 100 self.output = 100 + self.maxoutput = 100 self.timer = 0 def init(self): @@ -72,7 +73,7 @@ class CBPiActor(metaclass=ABCMeta): async def add_config_value(self, name, value, type: ConfigType, description, options=None): await self.cbpi.add(name, value, type, description, options=None) - async def on(self, power, output): + async def on(self, power, output=None): ''' Code to switch the actor on. Power is provided as integer value diff --git a/cbpi/api/base.py b/cbpi/api/base.py index f0aead6..db08826 100644 --- a/cbpi/api/base.py +++ b/cbpi/api/base.py @@ -65,7 +65,7 @@ class CBPiBase(metaclass=ABCMeta): logging.error("Failed to read actor state in step - actor {}".format(id)) return False - async def actor_on(self,id,power=100, output=100): + async def actor_on(self,id,power=100, output=None): try: await self.cbpi.actor.on(id,power, output) diff --git a/cbpi/api/dataclasses.py b/cbpi/api/dataclasses.py index e837cc4..a355cf9 100644 --- a/cbpi/api/dataclasses.py +++ b/cbpi/api/dataclasses.py @@ -56,15 +56,16 @@ class Actor: props: Props = Props() state: bool = False power: int = 100 - output: int = 100 + maxoutput: int = 100 + output: int = maxoutput timer: int = 0 type: str = None instance: str = None def __str__(self): - return "name={} props={}, state={}, type={}, power={}, output={}, timer={}".format(self.name, self.props, self.state, self.type, self.power, self.output, self.timer) + return "name={} props={}, state={}, type={}, power={}, output={}, maxoutput={}, timer={}".format(self.name, self.props, self.state, self.type, self.power, self.output, self.maxoutput, self.timer) def to_dict(self): - return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state(), power=self.power, output=self.output, timer=self.timer) + return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state(), power=self.power, output=self.output, maxoutput=self.maxoutput, timer=self.timer) class DataType(Enum): VALUE="value" diff --git a/cbpi/controller/actor_controller.py b/cbpi/controller/actor_controller.py index ced8ae9..a3b0cb7 100644 --- a/cbpi/controller/actor_controller.py +++ b/cbpi/controller/actor_controller.py @@ -26,7 +26,10 @@ class ActorController(BasicController): else: output = 100 if item.instance.state is False: - await item.instance.on(power,output) + try: + await item.instance.on(power, output) + except: + await item.instance.on(power) #await self.push_udpate() self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))),self.sorting) self.cbpi.push_update("cbpi/actorupdate/{}".format(id), item.to_dict(), True) @@ -62,6 +65,10 @@ class ActorController(BasicController): try: item = self.find_by_id(id) await item.instance.set_power(power) + output = round(item.maxoutput*power/100) + if item.output != output: + item.output = output + except Exception as e: logging.error("Failed to set power {} {}".format(id, e)) @@ -69,14 +76,22 @@ class ActorController(BasicController): try: item = self.find_by_id(id) await item.instance.set_output(output) + if item.output != output: + item.output=output + power=round(output/item.maxoutput*100) + if item.power != power: + await item.instance.set_power(power) except Exception as e: logging.error("Failed to set output {} {}".format(id, e)) - async def actor_update(self, id, power, output): + async def actor_update(self, id, power, output=None, maxoutput=None): try: item = self.find_by_id(id) + if maxoutput: + item.maxoutput=maxoutput item.power = round(power) - item.output = round(output) + if output: + item.output = round(output) #await self.push_udpate() self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))),self.sorting) self.cbpi.push_update("cbpi/actorupdate/{}".format(id), item.to_dict()) diff --git a/cbpi/extension/mqtt_actor/__init__.py b/cbpi/extension/mqtt_actor/__init__.py index bab41c6..4c4c3c5 100644 --- a/cbpi/extension/mqtt_actor/__init__.py +++ b/cbpi/extension/mqtt_actor/__init__.py @@ -3,6 +3,7 @@ from cbpi.api import * from .mqtt_actor import MQTTActor from .generic_mqtt_actor import GenericMqttActor from .tasmota_mqtt_actor import TasmotaMqttActor +from .output_mqtt_actor import OutputMQTTActor def setup(cbpi): ''' @@ -15,4 +16,5 @@ def setup(cbpi): if str(cbpi.static_config.get("mqtt", False)).lower() == "true": cbpi.plugin.register("MQTTActor", MQTTActor) cbpi.plugin.register("MQTT Actor (Generic)", GenericMqttActor) + cbpi.plugin.register("MQTT Actor (Output)", OutputMQTTActor) cbpi.plugin.register("MQTT Actor (Tasmota)", TasmotaMqttActor) \ No newline at end of file diff --git a/cbpi/extension/mqtt_actor/output_mqtt_actor.py b/cbpi/extension/mqtt_actor/output_mqtt_actor.py new file mode 100644 index 0000000..919113a --- /dev/null +++ b/cbpi/extension/mqtt_actor/output_mqtt_actor.py @@ -0,0 +1,91 @@ +import asyncio +import json +from cbpi.api import parameters, Property, CBPiActor +from cbpi.api import * +import logging + +@parameters([Property.Text(label="Topic", configurable=True, description = "MQTT Topic"), + Property.Number(label="MaxOutput",configurable=True,description="Max Output Value")]) +class OutputMQTTActor(CBPiActor): + + # Custom property which can be configured by the user + @action("Set Power", parameters=[Property.Number(label="Power", configurable=True, description="Power Setting [0-100]")]) + async def setpower(self,Power = 100 ,**kwargs): + self.power=int(Power) + if self.power < 0: + self.power = 0 + if self.power > 100: + self.power = 100 + self.output=round(self.maxoutput*self.power/100) + await self.set_power(self.power) + + + @action("Set Output", parameters=[Property.Number(label="Output", configurable=True, description="Output Setting [0-MaxOutput]")]) + async def setoutput(self,Output = 100 ,**kwargs): + self.output=int(Output) + if self.output < 0: + self.output = 0 + if self.output > self.maxoutput: + self.output = self.maxoutput + await self.set_output(self.output) + + def __init__(self, cbpi, id, props): + super(OutputMQTTActor, self).__init__(cbpi, id, props) + + async def on_start(self): + self.topic = self.props.get("Topic", None) + self.power = 100 + self.maxoutput = int(self.props.get("MaxOutput", 100)) + self.output=self.maxoutput + await self.cbpi.actor.actor_update(self.id,self.power, self.output, self.maxoutput) + await self.off() + self.state = False + + async def on(self, power=None, output=None): + if power is not None: + if power != self.power: + power = min(100, power) + power = max(0, power) + self.power = round(power) + if output is not None: + if output != self.output: + output = min(self.maxoutput, output) + output = max(0, output) + self.output = round(output) + await self.cbpi.satellite.publish(self.topic, json.dumps( + {"state": "on", "power": self.power, "output": self.output}), True) + self.state = True + pass + + async def off(self): + self.state = False + await self.cbpi.satellite.publish(self.topic, json.dumps( + {"state": "off", "power": 0, "output": 0}), True) + pass + + async def run(self): + while self.running: + await asyncio.sleep(1) + + def get_state(self): + return self.state + + async def set_power(self, power): + self.power = round(power) + self.output = round(self.maxoutput*self.power/100) + if self.state == True: + await self.on(power, self.output) + else: + await self.off() + await self.cbpi.actor.actor_update(self.id,power, self.output) + pass + + async def set_output(self, output): + self.output = round(output) + self.power=round(self.output/self.maxoutput*100) + if self.state == True: + await self.on(self.power, self.output) + else: + await self.off() + await self.cbpi.actor.actor_update(self.id,self.power, self.output) + pass \ No newline at end of file diff --git a/cbpi/http_endpoints/http_actor.py b/cbpi/http_endpoints/http_actor.py index e551dba..e53e2f0 100644 --- a/cbpi/http_endpoints/http_actor.py +++ b/cbpi/http_endpoints/http_actor.py @@ -1,6 +1,7 @@ from cbpi.api.dataclasses import Actor, Props from aiohttp import web from cbpi.api import * +import logging auth = False class ActorHttpEndpoints(): @@ -283,4 +284,37 @@ class ActorHttpEndpoints(): id = request.match_info['id'] data = await request.json() await self.controller.set_power(id,data.get("power")) + return web.Response(status=204) + + @request_mapping(path="/{id}/set_output", method="POST", auth_required=auth) + async def http_set_output(self, request) -> web.Response: + """ + + --- + description: Set actor output + tags: + - Actor + parameters: + - name: "id" + in: "path" + description: "Actor ID" + required: true + type: "integer" + format: "int64" + - in: body + name: body + description: Set Output + required: true + schema: + type: object + properties: + temp: + type: integer + responses: + "204": + description: successful operation + """ + id = request.match_info['id'] + data = await request.json() + await self.controller.set_output(id,data.get("output")) return web.Response(status=204) \ No newline at end of file