From 20abf8e8c00895fe531febf23ed586e12c773f7f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 9 Jul 2020 21:11:52 +0200 Subject: [PATCH] . --- mqtt.py | 5 +- workers/eq3bt.py | 280 +++++++++++++++++++++++++++++++++++++++++++ workers/linuxhost.py | 98 +++++++++++++++ workers/mpd.py | 129 ++++++++++++++++++++ workers/msgw.py | 55 +++++++++ workers/msgw2.py | 55 +++++++++ workers_manager.py | 16 ++- 7 files changed, 630 insertions(+), 8 deletions(-) create mode 100644 workers/eq3bt.py create mode 100644 workers/linuxhost.py create mode 100644 workers/mpd.py create mode 100644 workers/msgw.py create mode 100644 workers/msgw2.py diff --git a/mqtt.py b/mqtt.py index 4947dba..5791e9b 100644 --- a/mqtt.py +++ b/mqtt.py @@ -130,8 +130,9 @@ class MqttConfigMessage(MqttMessage): BINARY_SENSOR = 'binary_sensor' def __init__(self, component, name, payload=None, retain=False): - if 'name' not in payload: payload['name'] = name - super().__init__("{}/{}/config".format(component, name), json.dumps(payload), retain) + # if 'name' not in payload: payload['name'] = name + # super().__init__("{}/{}/config".format(component, name), json.dumps(payload), retain) + super().__init__("{}/{}".format(component, name), payload, retain) @property def retain(self): diff --git a/workers/eq3bt.py b/workers/eq3bt.py new file mode 100644 index 0000000..29ca69a --- /dev/null +++ b/workers/eq3bt.py @@ -0,0 +1,280 @@ +from builtins import staticmethod +import logging + +from mqtt import MqttMessage, MqttConfigMessage + +from workers.base import BaseWorker +import logger + +from datetime import datetime, timedelta +import time + + + +REQUIREMENTS = ['python-eq3bt'] +monitoredAttrs = ["low_battery", "valve_state", "target_temperature", "window_open", "locked"] +_LOGGER = logger.get(__name__) + + + +STATE_AWAY = 'away' +STATE_BOOST = 'boost' +STATE_AUTO = 'auto' +STATE_MANUAL = 'manual' +STATE_ON = 'on' +STATE_OFF = 'off' + + + +class Eq3BtWorker(BaseWorker): + class ModesMapper(): + def __init__(self): + from eq3bt import Mode + + self._mapped_modes = { + Mode.Closed: STATE_OFF, + Mode.Open: STATE_ON, + Mode.Auto: STATE_AUTO, + Mode.Manual: STATE_MANUAL, + Mode.Away: STATE_AWAY, + Mode.Boost: STATE_BOOST, + } + + self._reverse_modes = {v: k for k, v in self._mapped_modes.items()} + + def get_mapping(self, mode): + if mode < 0: + return None + return self._mapped_modes[mode] + + def get_reverse_mapping(self, mode): + return self._reverse_modes[mode] + + + + def format_topic(self, *args, separator='/'): + return separator.join([*args]) + + + + def mqttHomieDevice(self,device, name, nodes): + return [ + MqttConfigMessage(self.format_topic(device), "$state", payload="ready"), + MqttConfigMessage(self.format_topic(device), "$homie", payload="3.0"), + MqttConfigMessage(self.format_topic(device), "$name", payload=name), + MqttConfigMessage(self.format_topic(device), "$nodes", payload=nodes) + ] + + + + def mqttHomieNode(self, device, label, name, properties): + return [ + MqttConfigMessage(self.format_topic(device, label), "$name", payload=name), + MqttConfigMessage(self.format_topic(device, label), "$properties", payload=properties), + ] + + + + def mqttHomieProperty(self, device, node, prop, name, dtype, settable = False, retained = False, unit = None, dformat = None): + retval = [ + MqttConfigMessage(self.format_topic(device, node, prop), "$name", payload=name), + MqttConfigMessage(self.format_topic(device, node, prop), "$datatype", payload=dtype), + MqttConfigMessage(self.format_topic(device, node, prop), "$settable", payload="true" if settable else "false"), + MqttConfigMessage(self.format_topic(device, node, prop), "$retained", payload="true" if retained else "false") + ] + if unit is not None: + retval.append(MqttConfigMessage(self.format_topic(device, node, prop), "$unit", payload=unit)) + if dformat is not None: + retval.append(MqttConfigMessage(self.format_topic(device, node, prop), "$format", payload=dformat)) + return retval + + + + def _setup(self): + from eq3bt import Thermostat + + _LOGGER.info("Adding %d %s devices", len(self.devices), repr(self)) + for name, mac in self.devices.items(): + _LOGGER.debug("Adding %s device '%s' (%s)", repr(self), name, mac) + self.devices[name] = {"mac": mac, "thermostat": Thermostat(mac)} + + self._modes_mapper = self.ModesMapper() + self._command_queue = [] + + + + def config(self): + ret = [] + for name, data in self.devices.items(): + ret += self.config_device(name, data["mac"]) + return ret + + + + def config_device(self, name, mac): + from eq3bt import eq3btsmart + _LOGGER.debug("Configuring device " + str(name)) + retval = [] + retval = self.mqttHomieDevice(name, "Smart Thermostat: " + str(name) + " (" + str(mac)+ ")", "status,climate") + + retval += self.mqttHomieNode(name, "status", "Device Status", "low_battery,window_open,locked,last_updated") + retval += self.mqttHomieProperty(name, "status", "low_battery", "Low Battery", "boolean", False, True) + retval += self.mqttHomieProperty(name, "status", "window_open", "Window Open", "boolean", False, True) + retval += self.mqttHomieProperty(name, "status", "locked", "Lock", "boolean", True, True) + retval += self.mqttHomieProperty(name, "status", "last_updated", "Last Updated", "string", False, True) + + retval += self.mqttHomieNode(name, "climate", "Climate Control", "mode,target_temperature,comfort_temperature,eco_temperature,temperature_offset,valve_state") + retval += self.mqttHomieProperty(name, "climate", "mode", "Climate Mode", "enum", True, True, dformat = "auto,manual,boost,away,on,off") + retval += self.mqttHomieProperty(name, "climate", "target_temperature", "Target Temperature", "float", True, True, dformat = str(eq3btsmart.EQ3BT_MIN_TEMP) + ":" + str(eq3btsmart.EQ3BT_MAX_TEMP)) + retval += self.mqttHomieProperty(name, "climate", "comfort_temperature", "Comfort Temperature", "float", True, True, dformat = str(eq3btsmart.EQ3BT_MIN_TEMP) + ":" + str(eq3btsmart.EQ3BT_MAX_TEMP)) + retval += self.mqttHomieProperty(name, "climate", "eco_temperature", "ECO Temperature", "float", True, True, dformat = str(eq3btsmart.EQ3BT_MIN_TEMP) + ":" + str(eq3btsmart.EQ3BT_MAX_TEMP)) + retval += self.mqttHomieProperty(name, "climate", "temperature_offset", "Temperature Offset", "float", True, True) + retval += self.mqttHomieProperty(name, "climate", "valve_state", "Valve State", "integer", False, True) + + return retval + + + + def run(self, mqtt): + _LOGGER.info("Starting eq3bt daemon") + from bluepy import btle + self.time_next_update = datetime.now() + commandTryCounter = 0 + updateTryCounter = 0 + while True: + try: + time_now = datetime.now() + + if time_now > self.time_next_update: + _LOGGER.info("Updating %d %s devices", len(self.devices), repr(self)) + updateTryCounter += 1 + if updateTryCounter <= 3: + for name, data in self.devices.items(): + _LOGGER.debug("Updating %s device '%s' (%s)", repr(self), name, data["mac"]) + ret = self.update_device_state(name, data["thermostat"]) + mqtt.publish(ret) + _LOGGER.debug("Updating %s device '%s' finished", repr(self), name) + updateTryCounter -= 1 + else: + _LOGGER.error("Retried update too many times, dropping for now...") + updateTryCounter = 0 + self.time_next_update = time_now + timedelta(seconds=self.update_interval) + + if len(self._command_queue) > 0: + _LOGGER.info("Executing command") + commandTryCounter += 1 + if commandTryCounter <= 5: + command = self._command_queue[0] + if command(): + self.time_next_update = time_now + del self._command_queue[0] + commandTryCounter -= 1 + _LOGGER.debug("Executing command finished") + else: + _LOGGER.error("Retried command too many times, dropping command...") + del self._command_queue[0] + commandTryCounter = 0 + + time.sleep(1.0) + + except btle.BTLEException as e: + import traceback + _LOGGER.error("Caught BTLEException: {}".format(e)) + _LOGGER.error(traceback.format_exc()) + import subprocess + _LOGGER.error("Reseting hci0") + try: + subprocess.run(["/bin/hciconfig", "hci0", "down"], timeout=10) + time.sleep(1.0) + subprocess.run(["/bin/hciconfig", "hci0", "up"], timeout=10) + except Exception as e: + _LOGGER.error("Caught exception while reseting hci0: {}".format(e)) + _LOGGER.error(traceback.format_exc()) + time.sleep(10.0) + + + except Exception as e: + import traceback + _LOGGER.error("Caught exception in Eq3BtWorker.run(): {}".format(e)) + _LOGGER.error(traceback.format_exc()) + time.sleep(1.0) + + + + + + ''' + def status_update(self): + from bluepy import btle + + ret = [] + _LOGGER.info("Updating %d %s devices", len(self.devices), repr(self)) + for name, data in self.devices.items(): + _LOGGER.debug("Updating %s device '%s' (%s)", repr(self), name, data["mac"]) + try: + ret += self.update_device_state(name, data["thermostat"]) + except btle.BTLEException as e: + logger.log_exception(_LOGGER, "Error during update of %s device '%s' (%s): %s", repr(self), name, data["mac"], type(e).__name__, suppress=True) + + _LOGGER.info("Updating %d %s devices finshed", len(self.devices), repr(self)) + return ret + ''' + + + + def on_command(self, topic, value): + retval = [] + try: + value = value.decode('utf-8') + device_name, nodename, propname, operation = topic.split('/') + _LOGGER.debug("Command \"%s\", on %s.%s.%s: %s", operation, device_name, nodename, propname, value) + device = self.devices[device_name]["thermostat"] + + def _cmd_fun(): + if propname == "locked" and operation == "set": + device.locked = True + elif value == "false" or value == "off": + device.locked = False + elif propname == "mode" and operation == "set": + device.mode = self._modes_mapper.get_reverse_mapping(value) + elif propname == "target_temperature" and operation == "set": + device.target_temperature = float(value) + elif propname == "comfort_temperature" and operation == "set": + device.comfort_temperature = float(value) + elif propname == "eco_temperature" and operation == "set": + device.eco_temperature = float(value) + elif propname == "temperature_offset" and operation == "set": + device.temperature_offset = float(value) + elif propname == "valve_state" and operation == "set": + device.valve_state = float(value) + return True + + self._command_queue.append(_cmd_fun) + except Exception as e: + import traceback + _LOGGER.error("Caught exception in Eq3BtWorker.on_command(): {}".format(e)) + _LOGGER.error(traceback.format_exc()) + return retval + + + + def update_device_state(self, name, thermostat): + from datetime import datetime + thermostat.update() + ret = [] + + ret.append(MqttMessage(topic=self.format_topic(name, 'status', 'low_battery'), payload="true" if thermostat.low_battery else "false")) + ret.append(MqttMessage(topic=self.format_topic(name, 'status', 'window_open'), payload="true" if thermostat.window_open else "false")) + ret.append(MqttMessage(topic=self.format_topic(name, 'status', 'locked'), payload="true" if thermostat.locked else "false")) + + ha_mode = self._modes_mapper.get_mapping(thermostat.mode) + ret.append(MqttMessage(topic=self.format_topic(name, 'climate', 'mode'), payload=ha_mode)) + ret.append(MqttMessage(topic=self.format_topic(name, 'climate', 'target_temperature'), payload=thermostat.target_temperature)) + ret.append(MqttMessage(topic=self.format_topic(name, 'climate', 'comfort_temperature'), payload=thermostat.comfort_temperature)) + ret.append(MqttMessage(topic=self.format_topic(name, 'climate', 'eco_temperature'), payload=thermostat.eco_temperature)) + ret.append(MqttMessage(topic=self.format_topic(name, 'climate', 'temperature_offset'), payload=thermostat.temperature_offset)) + ret.append(MqttMessage(topic=self.format_topic(name, 'climate', 'valve_state'), payload=thermostat.valve_state)) + + ret.append(MqttMessage(topic=self.format_topic(name, 'status', 'last_updated'), payload=datetime.isoformat(datetime.now()))) + + return ret diff --git a/workers/linuxhost.py b/workers/linuxhost.py new file mode 100644 index 0000000..3dad746 --- /dev/null +++ b/workers/linuxhost.py @@ -0,0 +1,98 @@ +from mqtt import MqttMessage, MqttConfigMessage + +from workers.base import BaseWorker +import logger +import subprocess +import traceback + +REQUIREMENTS = [] +_LOGGER = logger.get(__name__) + + +class LinuxhostWorker(BaseWorker): + + def mqttHomieDevice(self, name, nodes): + return [ + MqttConfigMessage(self.format_topic(), "$state", payload="ready"), + MqttConfigMessage(self.format_topic(), "$homie", payload="3.0"), + MqttConfigMessage(self.format_topic(), "$name", payload=name), + MqttConfigMessage(self.format_topic(), "$nodes", payload=nodes) + ] + + + def mqttHomieNode(self, label, name, properties): + return [ + MqttConfigMessage(self.format_topic(label), "$name", payload=name), + MqttConfigMessage(self.format_topic(label), "$properties", payload=properties), + ] + + + def mqttHomieProperty(self, node, prop, name, dtype, settable = False, retained = False, unit = None, dformat = None): + retval = [ + MqttConfigMessage(self.format_topic(node, prop), "$name", payload=name), + MqttConfigMessage(self.format_topic(node, prop), "$datatype", payload=dtype), + MqttConfigMessage(self.format_topic(node, prop), "$settable", payload="true" if settable else "false"), + MqttConfigMessage(self.format_topic(node, prop), "$retained", payload="true" if retained else "false") + ] + if unit is not None: + retval.append(MqttConfigMessage(self.format_topic(node, prop), "$unit", payload=unit)) + if dformat is not None: + retval.append(MqttConfigMessage(self.format_topic(node, prop), "$format", payload=dformat)) + return retval + + + def _setup(self): + pass + + + def config(self): + _LOGGER.debug("Configuring") + retval = self.mqttHomieDevice("Linux Host", "xserver") + retval += self.mqttHomieNode("xserver", "XServer", "screensaver") + retval += self.mqttHomieProperty("xserver", "screensaver", "Screensaver", "enum", True, False, dformat="activate,deactivate") + return retval + + + def status_update(self): + retval = [] + try: + pass + except Exception as e: + _LOGGER.error("Caught exception in LinuxhostWorker.status_update(): {}".format(e)) + traceback.print_exc(); + return retval + + + def on_command(self, topic, value): + retval = [] + try: + value = value.decode('utf-8') + _, nodename, propname, operation = topic.split('/') + _LOGGER.debug("Command \"%s\", on %s.%s: %s", operation, nodename, propname, value) + if propname == "screensaver" and operation == "set": + if value == "activate" and self._enableScreensaver() == 0: + retval.append(MqttMessage(topic=self.format_topic("xserver", "screensaver"), payload="activate", retain=False)) + elif value == "deactivate" and self._disableScreensaver() == 0: + retval.append(MqttMessage(topic=self.format_topic("xserver", "screensaver"), payload="deactivate", retain=False)) + except Exception as e: + _LOGGER.error("Caught exception in LinuxhostWorker.on_command(): {}".format(e)) + traceback.print_exc(); + return retval + + + def _enableScreensaver(self): + try: + _LOGGER.info('Enable Screensaver') + return subprocess.call("DISPLAY=:0.0 sudo -u pi /usr/bin/xscreensaver-command -activate", shell=True) + except Exception as e: + logging.error("Caught Exception in LinuxhostWorker.enableScreensaver: " + str(e)) + return -1 + + + def _disableScreensaver(self): + try: + _LOGGER.info('Disable Screensaver') + return subprocess.call("DISPLAY=:0.0 sudo -u pi /usr/bin/xscreensaver-command -deactivate", shell=True) + except Exception as e: + logging.error("Caught Exception in LinuxhostWorker.disableScreensaver: " + str(e)) + return -1 diff --git a/workers/mpd.py b/workers/mpd.py new file mode 100644 index 0000000..59c5c30 --- /dev/null +++ b/workers/mpd.py @@ -0,0 +1,129 @@ +from mqtt import MqttMessage, MqttConfigMessage + +from workers.base import BaseWorker +import logger +import subprocess +import traceback + +REQUIREMENTS = [] +_LOGGER = logger.get(__name__) + + +class MpdWorker(BaseWorker): + + def mqttHomieDevice(self, name, nodes): + return [ + MqttConfigMessage(self.format_topic(), "$state", payload="ready"), + MqttConfigMessage(self.format_topic(), "$homie", payload="3.0"), + MqttConfigMessage(self.format_topic(), "$name", payload=name), + MqttConfigMessage(self.format_topic(), "$nodes", payload=nodes) + ] + + + def mqttHomieNode(self, label, name, properties): + return [ + MqttConfigMessage(self.format_topic(label), "$name", payload=name), + MqttConfigMessage(self.format_topic(label), "$properties", payload=properties), + ] + + + def mqttHomieProperty(self, node, prop, name, dtype, settable = False, retained = False, unit = None, dformat = None): + retval = [ + MqttConfigMessage(self.format_topic(node, prop), "$name", payload=name), + MqttConfigMessage(self.format_topic(node, prop), "$datatype", payload=dtype), + MqttConfigMessage(self.format_topic(node, prop), "$settable", payload="true" if settable else "false"), + MqttConfigMessage(self.format_topic(node, prop), "$retained", payload="true" if retained else "false") + ] + if unit is not None: + retval.append(MqttConfigMessage(self.format_topic(node, prop), "$unit", payload=unit)) + if dformat is not None: + retval.append(MqttConfigMessage(self.format_topic(node, prop), "$format", payload=dformat)) + return retval + + + def _setup(self): + if not hasattr(self, 'mpc_path'): + self.mpc_path = '/usr/bin/mpc' + + + def config(self): + _LOGGER.debug("Configuring") + retval = self.mqttHomieDevice("Media Player Daemon (MyRoom)", "controls") + retval += self.mqttHomieNode("controls", "Player Controls", "volume,control,play,playlist,current") + retval += self.mqttHomieProperty("controls", "volume", "Volume", "integer", True, True, "%", "0:100") + retval += self.mqttHomieProperty("controls", "control", "Media Control", "enum", True, True, dformat = "play,pause,stop") + retval += self.mqttHomieProperty("controls", "play", "Play Button", "boolean", True, True) + retval += self.mqttHomieProperty("controls", "playlist", "Playlist", "string", True, True) + retval += self.mqttHomieProperty("controls", "current", "Currently Playing", "string", False, False) + return retval + + + def status_update(self): + retval = [] + try: + retval.append(MqttMessage(topic=self.format_topic("controls", "current"), payload=self.mpc_get_current(), retain=True)) + except Exception as e: + _LOGGER.error("Caught exception in MpdWorker.on_command(): {}".format(e)) + traceback.print_exc(); + return retval + + + def on_command(self, topic, value): + retval = [] + try: + value = value.decode('utf-8') + _, nodename, propname, operation = topic.split('/') + _LOGGER.debug("Command \"%s\", on %s.%s: %s", operation, nodename, propname, value) + if propname == "volume" and operation == "set": + if self.mpc_set_volume(value): + retval.append(MqttMessage(topic=self.format_topic("controls", "volume"), payload=value, retain=True)) + elif propname == "control" and operation == "set": + if (value == "play" or value == "PLAY") and self.call_mpc(["play"]) == 0: + retval.append(MqttMessage(topic=self.format_topic("controls", "control"), payload=value, retain=True)) + elif (value == "pause" or value == "PAUSE") and self.call_mpc(["pause"]) == 0: + retval.append(MqttMessage(topic=self.format_topic("controls", "control"), payload=value, retain=True)) + elif (value == "stop" or value == "STOP") and self.call_mpc(["stop"]) == 0: + retval.append(MqttMessage(topic=self.format_topic("controls", "control"), payload=value, retain=True)) + elif propname == "play" and operation == "set": + if (value == "true" or value == "on") and self.call_mpc(["play"]) == 0: + retval.append(MqttMessage(topic=self.format_topic("controls", "play"), payload="true", retain=True)) + elif (value == "false" or value == "off") and self.call_mpc(["stop"]) == 0: + retval.append(MqttMessage(topic=self.format_topic("controls", "play"), payload="false", retain=True)) + elif propname == "playlist" and operation == "set": + if self.mpc_set_playlist(value): + retval.append(MqttMessage(topic=self.format_topic("controls", "playlist"), payload=value, retain=True)) + retval.append(MqttMessage(topic=self.format_topic("controls", "current"), payload=self.mpc_get_current(), retain=True)) + except Exception as e: + _LOGGER.error("Caught exception in MpdWorker.on_command(): {}".format(e)) + traceback.print_exc(); + return retval + + + def run_mpc(self, args): + args = [self.mpc_path] + args + result = subprocess.run(args, stdout=subprocess.PIPE) + _LOGGER.debug("run_mpc: %s -> %s", str(args), str(result)) + return (result.returncode, result.stdout) + + + def call_mpc(self, args): + args = [self.mpc_path] + args + _LOGGER.debug("call_mpc: %s", str(args)) + return subprocess.call(args) + + + def mpc_set_volume(self, volume): + return self.call_mpc(["volume", str(volume)]) == 0 + + + def mpc_set_playlist(self, pl): + return self.call_mpc(["load", str(pl)]) == 0 + + + def mpc_get_current(self): + retcode, out = self.run_mpc(["current"]) + if retcode == 0: + return out + else: + return "" + diff --git a/workers/msgw.py b/workers/msgw.py new file mode 100644 index 0000000..8550545 --- /dev/null +++ b/workers/msgw.py @@ -0,0 +1,55 @@ +from mqtt import MqttMessage + +from workers.base import BaseWorker +import logger +import traceback + +REQUIREMENTS = ['pyserial'] +_LOGGER = logger.get(__name__) + + +class MsgwWorker(BaseWorker): + + def _setup(self): + _LOGGER.debug("Setting up mysensors-mqtt") + import serial + self.ser = serial.Serial(self.port, self.baudrate, timeout=10) + if not hasattr(self, 'encoding'): + self.encoding = 'utf-8' + + def config(self): + return [] + + def run(self, mqtt): + _LOGGER.debug("Starting mysensors-mqtt at: %s" % self.ser.name) + while True: + try: + line = self.ser.readline() + if not line: + continue + splited_line = self.format_topic(line.decode(self.encoding).strip()).split(';') + _LOGGER.debug("MSGW.serial.read: %s" % line) + topic = '/'.join(splited_line[0:-1]) + payload = ''.join(splited_line[-1]) + mqtt.publish([MqttMessage(topic=topic, payload=payload)]) + except Exception as e: + _LOGGER.error("Caught exception in MsgwWorker.run(): {}".format(e)) + traceback.print_exc(); + except serial.serialutil.SerialException as e: + _LOGGER.error("Caught exception in MsgwWorker.run(): {}".format(e)) + traceback.print_exc(); + self.ser = serial.Serial(self.port, self.baudrate, timeout=10) + + + def on_command(self, topic, value): + _LOGGER.debug("MsgwWorker.on_command({}, {})".format(topic, value)) + try: + splited_topic = topic.split('/') + line = str.encode(splited_topic[-5]+";"+splited_topic[-4]+";"+splited_topic[-3]+";"+splited_topic[-2]+";"+splited_topic[-1]+";")+value+b'\n' + _LOGGER.debug("MSGW.serial.write: %s" % line) + self.ser.write(line) + except Exception as e: + _LOGGER.error("Caught exception in MsgwWorker.on_command(): {}".format(e)) + traceback.print_exc() + return [] + diff --git a/workers/msgw2.py b/workers/msgw2.py new file mode 100644 index 0000000..ceee1f0 --- /dev/null +++ b/workers/msgw2.py @@ -0,0 +1,55 @@ +from mqtt import MqttMessage + +from workers.base import BaseWorker +import logger +import traceback + +REQUIREMENTS = ['pyserial'] +_LOGGER = logger.get(__name__) + + +class Msgw2Worker(BaseWorker): + + def _setup(self): + _LOGGER.debug("Setting up mysensors-2-mqtt") + import serial + self.ser = serial.Serial(self.port, self.baudrate, timeout=10) + if not hasattr(self, 'encoding'): + self.encoding = 'utf-8' + + def config(self): + return [] + + def run(self, mqtt): + _LOGGER.debug("Starting mysensors-2-mqtt at: %s" % self.ser.name) + while True: + try: + line = self.ser.readline() + if not line: + continue + splited_line = self.format_topic(line.decode(self.encoding).strip()).split(';') + _LOGGER.debug("MSGW2.serial.read: %s" % line) + topic = '/'.join(splited_line[0:-1]) + payload = ''.join(splited_line[-1]) + mqtt.publish([MqttMessage(topic=topic, payload=payload)]) + except Exception as e: + _LOGGER.error("Caught exception in Msgw2Worker.run(): {}".format(e)) + traceback.print_exc(); + except serial.serialutil.SerialException as e: + _LOGGER.error("Caught exception in MsgwWorker.run(): {}".format(e)) + traceback.print_exc(); + self.ser = serial.Serial(self.port, self.baudrate, timeout=10) + + + def on_command(self, topic, value): + _LOGGER.debug("MsgwWorker2.on_command({}, {})".format(topic, value)) + try: + splited_topic = topic.split('/') + line = str.encode(splited_topic[-5]+";"+splited_topic[-4]+";"+splited_topic[-3]+";"+splited_topic[-2]+";"+splited_topic[-1]+";")+value+b'\n' + _LOGGER.info("MSGW2.serial.write: %s" % line) + self.ser.write(line) + except Exception as e: + _LOGGER.error("Caught exception in Msgw2Worker.on_command(): {}".format(e)) + traceback.print_exc() + return [] + diff --git a/workers_manager.py b/workers_manager.py index 821dd58..06d907e 100644 --- a/workers_manager.py +++ b/workers_manager.py @@ -83,10 +83,14 @@ class WorkersManager: raise "%s cannot be initialized, it has to define run or status_update method" % worker_name if 'topic_subscription' in worker_config: - self._mqtt_callbacks.append(( - worker_config['topic_subscription'], - partial(self._on_command_wrapper, worker_obj) - )) + topics = worker_config['topic_subscription'] + if isinstance(topics, str): + topics = [topics] + for topic in topics: + self._mqtt_callbacks.append(( + topic, + partial(self._on_command_wrapper, worker_obj) + )) if 'topic_subscription' in self._config: for (callback_name, options) in self._config['topic_subscription'].items(): @@ -148,6 +152,6 @@ class WorkersManager: for command in self._config_commands: messages = command.execute() for msg in messages: - msg.topic = "{}/{}".format(self._config['sensor_config'].get('topic', 'homeassistant'), msg.topic) + #msg.topic = "{}/{}".format(self._config['sensor_config'].get('topic', 'homeassistant'), msg.topic) msg.retain = self._config['sensor_config'].get('retain', True) - mqtt.publish(messages) \ No newline at end of file + mqtt.publish(messages)