Watch state change

This commit is contained in:
Nicolas Duhamel 2021-04-02 11:16:39 +02:00
parent cb03c12d16
commit b9fd9c40ae
4 changed files with 72 additions and 27 deletions

View File

@ -1,18 +1,44 @@
from .action import Action, Publish, TasmotaShutter import json
from .scene import Scene from .scene import Scene
from .action import Publish, TasmotaShutter
from .watch import Watch
class Sleep(Scene): class Sleep(Scene):
ON = [ 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), .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('Bedroom1x1').Close(),
TasmotaShutter('Bedroom1x2').Close(), TasmotaShutter('Bedroom1x2').Close(),
TasmotaShutter('Staircase1').Close(), TasmotaShutter('Staircase1').Close(),
TasmotaShutter('Bathroom').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 = [ 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),
] ]

View File

@ -8,10 +8,10 @@ from citadel.mqtt import Configuration
from . import Sleep from . import Sleep
def main(\ def main(\
mqtt_user: str = typer.Option(... , envvar="DEVICES_MQTT_USER"),\ mqtt_user: str = typer.Option(... , envvar="SCENE_MQTT_USER"),\
mqtt_pwd: str = typer.Option(... , envvar="DEVICES_MQTT_PWD"),\ mqtt_pwd: str = typer.Option(... , envvar="SCENE_MQTT_PWD"),\
mqtt_host: str = typer.Option(... , envvar="DEVICES_MQTT_HOST"),\ mqtt_host: str = typer.Option(... , envvar="SCENE_MQTT_HOST"),\
mqtt_port: int = typer.Option(... , envvar="DEVICES_MQTT_PORT"),\ 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")\ is_systemd: bool = typer.Option(False, help="Is running as systemd unit", envvar="LAUNCHED_BY_SYSTEMD")\
): ):

View File

@ -10,6 +10,7 @@ import paho.mqtt.client as mqtt
from citadel.mqtt import Configuration from citadel.mqtt import Configuration
from .action import Action from .action import Action
from .watch import Watch
class Scene(threading.Thread, mqtt.Client): class Scene(threading.Thread, mqtt.Client):
""" A scene run as a thread, has his own mqtt session """ 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) logger = logging.getLogger('scene.%s' % self.mqtt_name)
self.logger = logger self.logger = logger
# we need a queue for publishing message from different thread
self.__pub_queue = queue.Queue() self.__pub_queue = queue.Queue()
self.__state = Scene.STATE_DEACTIVATE 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) self.subscribe(self.TOPIC_CMND.format(mqtt_name=self.mqtt_name), 2)
for watch in self.WATCH: for watch in self.WATCH:
self.subscribe(watch[0], 1) self.subscribe(watch.topic, 1)
run = True run = True
while run: while run:
#Mqtt client loop
self.loop()
#Check publish queue #Check publish queue
try: try:
(args, kwargs) = self.__pub_queue.get(block=False) (args, kwargs) = self.__pub_queue.get(block=False)
@ -104,20 +103,23 @@ class Scene(threading.Thread, mqtt.Client):
self.__state = Scene.STATE_ACTIVATE self.__state = Scene.STATE_ACTIVATE
self.__send_state() self.__send_state()
#Mqtt client loop
self.loop()
def threaded_publish(self, *args, **kwargs): def threaded_publish(self, *args, **kwargs):
self.__pub_queue.put((args, kwargs)) self.__pub_queue.put((args, kwargs))
def on_message(self, mqttc, obj, msg): def on_message(self, mqttc, obj, msg):
payload = msg.payload.decode().lower() payload = msg.payload.decode()
topic = msg.topic topic = msg.topic
# on CMND # on CMND
if topic == self.TOPIC_CMND.format(mqtt_name=self.mqtt_name): 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.logger.info('CMND activate ON')
self.__activate() self.__activate()
elif payload == 'off': elif payload.lower() == 'off':
self.logger.info('CMND activate OFF') self.logger.info('CMND activate OFF')
self.__deactivate() self.__deactivate()
elif payload == '': elif payload == '':
@ -126,18 +128,12 @@ class Scene(threading.Thread, mqtt.Client):
else: else:
self.logger.warn('CMND activate invalide') self.logger.warn('CMND activate invalide')
elif self.scene_state and self.__check_watched(topic, payload): 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.logger.info('Watched topic %s with invalid payload %s deactivating', topic, payload)
self.__deactivate() self.__deactivate()
break
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
def __activate(self): def __activate(self):
if self.scene_state: if self.scene_state:

View File

@ -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