diff --git a/src/citadel/scene/__init__.py b/src/citadel/scene/__init__.py index d806c9b..07a6a09 100644 --- a/src/citadel/scene/__init__.py +++ b/src/citadel/scene/__init__.py @@ -1,18 +1,44 @@ -from .action import Action, Publish, TasmotaShutter +import json + from .scene import Scene +from .action import Publish, TasmotaShutter +from .watch import Watch class Sleep(Scene): ON = [ - Publish('cmnd/tasmota/screen/Screen/POWER').Payload('OFF').Qos(2)\ + Publish('cmnd/tasmota/screen/Screen/POWER').Payload('OFF').Qos(1)\ .CheckResponse('stat/tasmota/screen/Screen/POWER', 'OFF', 3), - Publish('cmnd/tasmota/light/LivingroomFireplace/POWER').Payload('OFF').Qos(2), + Publish('cmnd/tasmota/light/LivingroomFireplace/POWER').Payload('OFF').Qos(1), + + #Shutter TasmotaShutter('Bedroom1x1').Close(), TasmotaShutter('Bedroom1x2').Close(), TasmotaShutter('Staircase1').Close(), TasmotaShutter('Bathroom').Close(), + + #Chambre + Publish('zwave2mqtt/7/67/1/1/set').Payload('17').Qos(1)\ + .CheckResponse('zwave2mqtt/7/67/1/1', lambda p: json.loads(p)['value'] == 17, 3), + #RdC + Publish('zwave2mqtt/6/67/1/1/set').Payload('19').Qos(1)\ + .CheckResponse('zwave2mqtt/6/67/1/1', lambda p: json.loads(p)['value'] == 19, 3), + Publish('zwave2mqtt/5/67/1/1/set').Payload('19').Qos(1)\ + .CheckResponse('zwave2mqtt/5/67/1/1', lambda p: json.loads(p)['value'] == 19, 3), ] WATCH = [ - ('stat/tasmota/screen/Screen/POWER', 'OFF') + #HomeCinema + Watch('stat/tasmota/screen/Screen/POWER', 'OFF'), + #Lights + Watch('stat/tasmota/light/LivingroomFireplace/POWER', 'OFF'), + #Shutter + Watch('cmnd/tasmota/shutter/Bedroom1x1/ShutterOpen'), + Watch('cmnd/tasmota/shutter/Bedroom1x2/ShutterOpen'), + Watch('cmnd/tasmota/shutter/Staircase1/ShutterOpen'), + Watch('cmnd/tasmota/shutter/Bathroom/ShutterOpen'), + #Thermostat + Watch('zwave2mqtt/7/67/1/1', lambda p: json.loads(p)['value'] == 17), + Watch('zwave2mqtt/6/67/1/1', lambda p: json.loads(p)['value'] == 19), + Watch('zwave2mqtt/5/67/1/1', lambda p: json.loads(p)['value'] == 19), ] diff --git a/src/citadel/scene/__main__.py b/src/citadel/scene/__main__.py index fefe078..97816d4 100644 --- a/src/citadel/scene/__main__.py +++ b/src/citadel/scene/__main__.py @@ -8,10 +8,10 @@ from citadel.mqtt import Configuration from . import Sleep def main(\ - mqtt_user: str = typer.Option(... , envvar="DEVICES_MQTT_USER"),\ - mqtt_pwd: str = typer.Option(... , envvar="DEVICES_MQTT_PWD"),\ - mqtt_host: str = typer.Option(... , envvar="DEVICES_MQTT_HOST"),\ - mqtt_port: int = typer.Option(... , envvar="DEVICES_MQTT_PORT"),\ + mqtt_user: str = typer.Option(... , envvar="SCENE_MQTT_USER"),\ + mqtt_pwd: str = typer.Option(... , envvar="SCENE_MQTT_PWD"),\ + mqtt_host: str = typer.Option(... , envvar="SCENE_MQTT_HOST"),\ + mqtt_port: int = typer.Option(... , envvar="SCENE_MQTT_PORT"),\ is_systemd: bool = typer.Option(False, help="Is running as systemd unit", envvar="LAUNCHED_BY_SYSTEMD")\ ): diff --git a/src/citadel/scene/scene.py b/src/citadel/scene/scene.py index 8f88506..10a3c8e 100644 --- a/src/citadel/scene/scene.py +++ b/src/citadel/scene/scene.py @@ -10,6 +10,7 @@ import paho.mqtt.client as mqtt from citadel.mqtt import Configuration from .action import Action +from .watch import Watch class Scene(threading.Thread, mqtt.Client): """ A scene run as a thread, has his own mqtt session @@ -47,6 +48,7 @@ class Scene(threading.Thread, mqtt.Client): logger = logging.getLogger('scene.%s' % self.mqtt_name) self.logger = logger + # we need a queue for publishing message from different thread self.__pub_queue = queue.Queue() self.__state = Scene.STATE_DEACTIVATE @@ -66,13 +68,10 @@ class Scene(threading.Thread, mqtt.Client): self.subscribe(self.TOPIC_CMND.format(mqtt_name=self.mqtt_name), 2) for watch in self.WATCH: - self.subscribe(watch[0], 1) + self.subscribe(watch.topic, 1) run = True while run: - #Mqtt client loop - self.loop() - #Check publish queue try: (args, kwargs) = self.__pub_queue.get(block=False) @@ -104,20 +103,23 @@ class Scene(threading.Thread, mqtt.Client): self.__state = Scene.STATE_ACTIVATE self.__send_state() + #Mqtt client loop + self.loop() + def threaded_publish(self, *args, **kwargs): self.__pub_queue.put((args, kwargs)) def on_message(self, mqttc, obj, msg): - payload = msg.payload.decode().lower() + payload = msg.payload.decode() topic = msg.topic # on CMND if topic == self.TOPIC_CMND.format(mqtt_name=self.mqtt_name): - if payload == 'on': + if payload.lower() == 'on': self.logger.info('CMND activate ON') self.__activate() - elif payload == 'off': + elif payload.lower() == 'off': self.logger.info('CMND activate OFF') self.__deactivate() elif payload == '': @@ -126,18 +128,12 @@ class Scene(threading.Thread, mqtt.Client): else: self.logger.warn('CMND activate invalide') - elif self.scene_state and self.__check_watched(topic, payload): - self.logger.info('Watched topic %s with invalid payload %s deactivating', topic, payload) - self.__deactivate() - - def __check_watched(self, topic, payload): - """ return True if is a watched topic with an invalid payload """ - for watch in self.WATCH: - if topic == watch[0]: - if payload != watch[1].lower(): - return True - return False - return False + elif self.scene_state == Scene.STATE_ACTIVATE: + for watch in self.WATCH: + if topic == watch.topic and not watch.check(topic, payload): + self.logger.info('Watched topic %s with invalid payload %s deactivating', topic, payload) + self.__deactivate() + break def __activate(self): if self.scene_state: diff --git a/src/citadel/scene/watch.py b/src/citadel/scene/watch.py new file mode 100644 index 0000000..fc7108f --- /dev/null +++ b/src/citadel/scene/watch.py @@ -0,0 +1,23 @@ +from typing import Union, Callable + +class Watch(object): + + def __init__(self, topic: str, payload: Union[str, Callable[[str],bool]] = None): + self.__topic = topic + + if callable(payload): + self.__check_payload = payload + elif payload == None: + self.__check_payload = lambda _: False + else: + self.__check_payload = lambda x: x == payload + + @property + def topic(self) -> str: + return self.__topic + + def check(self, topic: str, payload: str) -> bool: + if topic == self.topic and self.__check_payload(payload): + return True + else: + return False