Example #1
0
class BasePlugin:
    mqttClient = None

    def CS_2_DZ_decoder(self, topic, message):
        success = 0
        Domoticz.Debug("CS_Decoder")
        EUI = str(topic.split("device/")[1].split("/")[0])
        Domoticz.Debug("EUI : " + EUI)

        ############
        ## Devices
        payload = json.loads(message)
        Domoticz.Debug("Payload:\n" + str(payload))
        if not 'loRaSNR' in str(payload):
            Domoticz.Log(
                "Decoding LoRa message problem. Check ChirpStack application.")
            return success
        RSSI = payload['rxInfo'][0]['loRaSNR']
        RSSI = int(round(mapFromTo(int(RSSI), -20, 0, 0, 10),
                         0))  #Domoticz : between 0 and 10
        Domoticz.Debug("RSSI : " + str(RSSI))
        CS_objects = json.loads(payload['objectJSON'])
        if (len(CS_objects) == 0):
            Domoticz.Log(
                "Decoding cayenne message problem. Check ChirpStack application."
            )
            return success
        #Domoticz.Debug(str(CS_objects))#{'temperatureSensor': {'1': 12}, 'humiditySensor': {'1': 80}}

        #Reorganize data
        data = []
        for obj in CS_objects:
            if (str(obj) in CS_cayenne_to_DZ):
                for channel in CS_objects[str(obj)]:
                    #Get the value of the sensor
                    value = str(CS_objects[str(obj)][str(channel)])
                    #build DATA : [Channel, value, [Corresp.DZ_TypeName, Corresp.DZ_pType,DZ_sType, Corresp.FancyName, Corresp.UNIT_ID] ]
                    data.append([channel, value, CS_cayenne_to_DZ[str(obj)]])
            else:
                Domoticz.Log("Unfound/Unproccessable Cayenne Data Type : " +
                             str(obj))

        Domoticz.Debug("DATA structure:" + str(data))
        #[['1', '12', ['Temperature', 243, 17, 'temp']], ['2', '14', ['Temperature', 243, 17, 'temp']], ['3', '15', ['Temperature', 243, 17, 'temp']], ['1', '80', ['Humidity', 243, 3, 'hum']]]
        # #aggregate several sensor types into one sensor. Decide whi one is nvalue, which one is svalue
        # # TODO
        data_ag = data
        #Devices ID
        d_ids = []
        d_units = []
        for x in Devices:
            d_ids.append([
                str(x),
                str(Devices[x].ID),
                str(Devices[x].Name),
                str(Devices[x].DeviceID)
            ])
            d_units.append(int(x))
        Domoticz.Debug(str(d_ids))
        Domoticz.Debug(str(d_units))

        #Device
        for d in range(0, len(data_ag)):
            #   Case there is Type_Name assigned
            if data_ag[d][2][0] != "":
                #Shape the values
                device_fancyname = str(data_ag[d][2][3])
                device_id = "eui-" + EUI + "-" + str(
                    data_ag[d]
                    [0]) + "-" + device_fancyname  #eui-a8d4fdf51239f322-1-hum
                device_Name = "Unknown " + device_fancyname
                device_TypeName = str(data_ag[d][2][0])

                Domoticz.Debug("===================")
                Domoticz.Debug("device_id: " + str(device_id))
                #If not exist, create
                if (device_id not in str(d_ids)):
                    #Find a new unit ID
                    device_Unit = 0
                    for u in range(1, 255):
                        if u not in d_units:
                            device_Unit = u
                            d_units.append(u)
                            break
                    if device_Unit == 0:
                        Domoticz.Log(
                            "ERROR - No more UNIT ID to allocate. Device not created"
                        )
                        return 0
                    Domoticz.Debug("device_Name: " + str(device_Name))
                    Domoticz.Debug("device_TypeName: " + str(device_TypeName))
                    if self.create_new_devices == "True":
                        Domoticz.Log(
                            "ChirpStack Plugin - Create NEW device with ID=" +
                            str(device_id) + ", Name=" + str(device_Name) +
                            ", Type=" + str(device_TypeName))
                        #Create
                        Domoticz.Device(Name=device_Name,
                                        TypeName=device_TypeName,
                                        Unit=device_Unit,
                                        DeviceID=device_id).Create()
                    else:
                        Domoticz.Log(
                            "ChirpStack Plugin - DISBALED [Create device " +
                            str(device_id) + "]")
                for x in Devices:
                    #Check if now exists, and update
                    if (str(Devices[x].DeviceID) == device_id):
                        device_svalue = str(data_ag[d][1])
                        #Update
                        Domoticz.Log("Chirpstack Plugin - Update device (ID=" +
                                     str(Devices[x].ID) + ") value =" +
                                     data_ag[d][1])
                        Devices[x].Update(nValue=int(device_svalue),
                                          sValue=str(device_svalue),
                                          SignalLevel=RSSI)
                Domoticz.Debug("===================")
        return success

    def onStart(self):
        self.debugging = Parameters["Mode6"]
        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)
        DumpConfigToLog()
        Domoticz.Log("Chirpstack2Domoticz Plugin started")
        #App ID Base TOPIC
        if (Parameters["Mode1"].strip() != ""):
            self.base_topic = "application/" + Parameters["Mode1"].strip()
        else:
            Domoticz.Log(
                "Warning : No Application ID submitted. It will subscribe to all application topics"
            )
            self.base_topic = "application"
        self.subscribed_for_devices = False
        self.create_new_devices = Parameters["Mode2"].strip()
        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()

        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port,
                                     mqtt_client_id, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)
        self.api = API(Devices, self.publishToMqtt)

    def checkDevices(self):
        Domoticz.Debug("checkDevices called")

    def onStop(self):
        Domoticz.Debug("onStop called")
        #self.uninstall()

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("onCommand: " + Command + ", level (" + str(Level) +
                       ") Color:" + Color)

        message = None
        device = Devices[
            Unit]  #Devices is Domoticz collection of devices for this hardware
        device_params = device.DeviceID.split('_')
        entity_id = device_params[0]
        Domoticz.Debug("OnCommand --> Partie desctivee")
        # if (self.devices_manager.get_device_by_id(entity_id) != None):
        #     message = self.devices_manager.handle_command(Devices, device, Command, Level, Color)
        # elif(self.groups_manager.get_group_by_deviceid(device.DeviceID) != None):
        #     message = self.groups_manager.handle_command(device, Command, Level, Color)
        # else:
        #     Domoticz.Log('Can\'t process command from device "' + device.Name + '"')

        # if (message != None):
        #     self.publishToMqtt(message['topic'], message['payload'])

    def publishToMqtt(self, topic, payload):
        self.mqttClient.publish(self.base_topic + '/' + topic, payload)

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onDeviceModified(self, unit):
        if (unit == 255):
            self.api.handle_request(Devices[unit].sValue)
            return

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    def onMQTTConnected(self):
        #if connected subscirbe to topic
        self.mqttClient.subscribe([self.base_topic + "/#"])
        Domoticz.Log("MQTT subscribed to : " + self.base_topic + "/#")

    def onMQTTDisconnected(self):
        self.subscribed_for_devices = False

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("On MQTT Publish")
        payload = json.dumps(message)
        Domoticz.Debug("MQTT message: " + topic + " " + str(payload))
        topic = topic.replace(self.base_topic + '/', '')
        #self.api.handle_mqtt_message(topic, message)
        #Decode the pyaload and message. --> Will automatically add sensors
        self.CS_2_DZ_decoder(topic, payload)
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        Domoticz.Debug("onStart called")
        self.base_topic = Parameters["Mode1"].strip()
        self.pairing_enabled = True if Parameters["Mode2"] == 'true' else False
        self.subscribed_for_devices = False

        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()
        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port,
                                     mqtt_client_id, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

        self.available_devices = DeviceStorage.getInstance()

    def checkDevices(self):
        Domoticz.Debug("checkDevices called")

    def onStop(self):
        Domoticz.Debug("onStop called")

    def handlePairingMode(self):
        permit_join = 'true' if self.pairing_enabled else 'false'
        self.mqttClient.publish(self.base_topic + '/bridge/config/permit_join',
                                permit_join)

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("onCommand: " + Command + ", level (" + str(Level) +
                       ") Color:" + Color)

        device = Devices[
            Unit]  #Devices is Domoticz collection of devices for this hardware
        device_params = device.DeviceID.split('_')
        device_id = device_params[0]
        alias = device_params[1]
        device_data = self.available_devices.get_device_by_id(device_id)

        if (device_data == None):
            Domoticz.Log('Device ' + device.Name +
                         ' does not have registered zigbee2mqtt device')
            return

        model = device_data['model']

        if (model in adapter_by_model):
            adapter = adapter_by_model[model](Devices)
            message = adapter.handleCommand(alias, device, device_data,
                                            Command, Level, Color)

            if (message != None):
                self.mqttClient.publish(
                    self.base_topic + '/' + message['topic'],
                    message['payload'])
        else:
            Domoticz.Log('Device ' + device.Name +
                         ' does not have adapter (model: "' + model + '"')

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    def onMQTTConnected(self):
        self.mqttClient.subscribe([self.base_topic + '/bridge/#'])

    def onMQTTDisconnected(self):
        self.subscribed_for_devices = False

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT message: " + topic + " " + str(message))

        if (topic == self.base_topic + '/bridge/config/permit_join'
                or topic == self.base_topic + '/bridge/config/devices'):
            return

        if (topic == self.base_topic + '/bridge/config'):
            permit_join = 'enabled' if message['permit_join'] else 'disabled'
            Domoticz.Debug('Zigbee2mqtt log level is ' + message['log_level'])
            Domoticz.Log('Joining new devices is ' + permit_join +
                         ' on the zigbee bridge')
            return

        if (topic == self.base_topic + '/bridge/state'):
            Domoticz.Log('Zigbee2mqtt bridge is ' + message)

            if message == 'online':
                self.mqttClient.publish(
                    self.base_topic + '/bridge/config/devices', '')
                self.handlePairingMode()

            return

        if (topic == self.base_topic + '/bridge/log'):
            if message['type'] == 'devices':
                Domoticz.Log('Received available devices list from bridge')

                self.available_devices.clear()
                self.available_devices.update(Devices, message['message'])

                if self.subscribed_for_devices == False:
                    self.mqttClient.subscribe([self.base_topic + '/+'])
                    self.subscribed_for_devices = True

            if message['type'] == 'device_connected' or message[
                    'type'] == 'device_removed':
                self.mqttClient.publish(
                    self.base_topic + '/bridge/config/devices', '')

            return

        device_name = topic.replace(self.base_topic + "/", "")
        device_data = self.available_devices.get_device_by_name(device_name)

        if (device_data != None):
            model = device_data['model']

            if (model in adapter_by_model):
                zigbee_message = ZigbeeMessage(message)
                adapter = adapter_by_model[model](Devices)
                adapter.handleMqttMessage(device_data, zigbee_message)
            else:
                Domoticz.Debug('Unsupported zigbee device type with model "' +
                               model + '"')
        else:
            Domoticz.Debug('Unhandled message from zigbee2mqtt: ' + topic +
                           ' ' + str(message))
Example #3
0
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        Domoticz.Debug("onStart called")
        self.install()
        self.base_topic = Parameters["Mode1"].strip()
        self.subscribed_for_devices = False

        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()
        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port,
                                     mqtt_client_id, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

        self.api = API(Devices, self.publishToMqtt)
        self.devices_manager = DevicesManager()
        self.groups_manager = GroupsManager()

    def checkDevices(self):
        Domoticz.Debug("checkDevices called")

    def onStop(self):
        Domoticz.Debug("onStop called")
        self.uninstall()

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("onCommand: " + Command + ", level (" + str(Level) +
                       ") Color:" + Color)

        message = None
        device = Devices[
            Unit]  #Devices is Domoticz collection of devices for this hardware
        device_params = device.DeviceID.split('_')
        entity_id = device_params[0]

        if (self.devices_manager.get_device_by_id(entity_id) != None):
            message = self.devices_manager.handle_command(
                Devices, device, Command, Level, Color)
        elif (self.groups_manager.get_group_by_deviceid(device.DeviceID) !=
              None):
            message = self.groups_manager.handle_command(
                device, Command, Level, Color)
        else:
            Domoticz.Log('Can\'t process command from device "' + device.Name +
                         '"')

        if (message != None):
            self.publishToMqtt(message['topic'], message['payload'])

    def publishToMqtt(self, topic, payload):
        self.mqttClient.publish(self.base_topic + '/' + topic, payload)

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onDeviceModified(self, unit):
        if (unit == 255):
            self.api.handle_request(Devices[unit].sValue)
            return

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    def onMQTTConnected(self):
        self.mqttClient.subscribe([self.base_topic + '/bridge/#'])

    def onMQTTDisconnected(self):
        self.subscribed_for_devices = False

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT message: " + topic + " " + str(message))
        topic = topic.replace(self.base_topic + '/', '')

        self.api.handle_mqtt_message(topic, message)

        if (topic == 'bridge/config/permit_join'
                or topic == 'bridge/config/devices'):
            return

        if (topic == 'bridge/config'):
            permit_join = 'enabled' if message['permit_join'] else 'disabled'
            Domoticz.Debug('Zigbee2mqtt log level is ' + message['log_level'])
            Domoticz.Log('Joining new devices is ' + permit_join +
                         ' on the zigbee bridge')
            return

        if (topic == 'bridge/state'):
            Domoticz.Log('Zigbee2mqtt bridge is ' + message)

            if message == 'online':
                self.publishToMqtt('bridge/config/devices', '')
                self.publishToMqtt('bridge/config/groups', '')

            return

        if (topic == 'bridge/log'):
            if message['type'] == 'devices':
                Domoticz.Log('Received available devices list from bridge')

                self.devices_manager.clear()
                self.devices_manager.update(Devices, message['message'])

                if self.subscribed_for_devices == False:
                    self.mqttClient.subscribe([self.base_topic + '/+'])
                    self.subscribed_for_devices = True

            if message['type'] == 'groups':
                Domoticz.Log('Received groups list from bridge')
                self.groups_manager.register_groups(Devices,
                                                    message['message'])

            if message['type'] == 'device_connected' or message[
                    'type'] == 'device_removed':
                self.publishToMqtt('bridge/config/devices', '')

            if message['type'] == 'ota_update':
                Domoticz.Log(message['message'])

            return

        if (self.devices_manager.get_device_by_name(topic) != None):
            self.devices_manager.handle_mqtt_message(Devices, topic, message)
        elif (self.groups_manager.get_group_by_name(topic) != None):
            self.groups_manager.handle_mqtt_message(topic, message)

    def install(self):
        Domoticz.Log('Installing plugin custom page...')

        try:
            source_path = os.path.dirname(
                os.path.abspath(__file__)) + '/frontend'
            templates_path = os.path.abspath(source_path +
                                             '/../../../www/templates')
            dst_plugin_path = templates_path + '/zigbee2mqtt'

            Domoticz.Debug('Copying files from ' + source_path + ' to ' +
                           templates_path)

            if not (os.path.isdir(dst_plugin_path)):
                os.makedirs(dst_plugin_path)

            copy2(source_path + '/zigbee2mqtt.html', templates_path)
            copy2(source_path + '/zigbee2mqtt.js', templates_path)
            copy2(source_path + '/zigbee_devices.js', dst_plugin_path)
            copy2(source_path + '/zigbee_groups.js', dst_plugin_path)
            copy2(source_path + '/libs/leaflet.js', dst_plugin_path)
            copy2(source_path + '/libs/leaflet.css', dst_plugin_path)
            copy2(source_path + '/libs/viz.js', dst_plugin_path)
            copy2(source_path + '/libs/viz.full.render.js', dst_plugin_path)

            Domoticz.Log('Installing plugin custom page completed.')
        except Exception as e:
            Domoticz.Error('Error during installing plugin custom page')
            Domoticz.Error(repr(e))

    def uninstall(self):
        Domoticz.Log('Uninstalling plugin custom page...')

        try:
            templates_path = os.path.abspath(
                os.path.dirname(os.path.abspath(__file__)) +
                '/../../www/templates')
            dst_plugin_path = templates_path + '/zigbee2mqtt'

            Domoticz.Debug('Removing files from ' + templates_path)

            if (os.path.isdir(dst_plugin_path)):
                rmtree(dst_plugin_path)

            if os.path.exists(templates_path + "/zigbee2mqtt.html"):
                os.remove(templates_path + "/zigbee2mqtt.html")

            if os.path.exists(templates_path + "/zigbee2mqtt.js"):
                os.remove(templates_path + "/zigbee2mqtt.js")

            Domoticz.Log('Uninstalling plugin custom page completed.')
        except Exception as e:
            Domoticz.Error('Error during uninstalling plugin custom page')
            Domoticz.Error(repr(e))
Example #4
0
class BasePlugin:
    enabled = False
    mqttConn = None
    counter = 0

    def __init__(self):
        return

    def onStart(self):
        if Parameters["Mode6"] == "Debug":
            Domoticz.Debugging(1)
        DumpConfigToLog()
        self.devices = {}

        Parameters["Username"] = "******"
        Parameters["Port"] = "8884"
        Parameters["Protocol"] = "MQTTS"

        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port,
                                     "NHCDomoticz", self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level, Hue):
        for uuid, device in self.devices.items():
            if device.getUnit() == Unit:
                device.handleCommand(Command, Level, Hue)

    #MQTT forwards..
    def onConnect(self, Connection, Status, Description):
        self.mqttClient.onConnect(Connection, Status, Description)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    # MQTT stuff
    def onMQTTConnected(self):
        self.mqttClient.subscribe(['hobby/control/devices/rsp'])
        self.mqttClient.subscribe(['hobby/control/devices/evt'])
        self.mqttClient.subscribe(['hobby/control/devices/err'])

        # fetch all devices
        self.mqttClient.publish('hobby/control/devices/cmd',
                                '{"Method": "devices.list"}')

    def onMQTTDisconnected(self):
        Domoticz.Log("onMQTT disconnected")

    def onMQTTSubscribed(self):
        Domoticz.Log("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        if topic == "hobby/control/devices/rsp":
            if message["Params"]:
                nhcDeviceIDs = []
                if message["Params"][0]["Devices"]:
                    for device in message["Params"][0]["Devices"]:
                        if device["Type"] == "action":
                            uuid = device["Uuid"]
                            self.devices[uuid] = Device(
                                Devices, device, self.mqttClient)
                            self.devices[uuid].handleMessage(topic, device)
        if topic == "hobby/control/devices/evt":
            if message["Method"] == "devices.status":
                if message["Params"]:
                    if message["Params"][0]["Devices"]:
                        for device in message["Params"][0]["Devices"]:
                            uuid = device["Uuid"]
                            if uuid in self.devices:
                                self.devices[uuid].handleMessage(topic, device)
        if topic == "hobby/control/devices/err":
            Domoticz.Log("Err")
Example #5
0
class Plugin:

    mqttClient = None
    tasmotaHandler = None

    def __init__(self):
        return

    def onStart(self):
        if errmsg == "":
            try:
                Domoticz.Heartbeat(10)
                self.debugging = Parameters["Mode6"]
                if self.debugging == "Verbose":
                    Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
                if self.debugging == "Debug":
                    Domoticz.Debugging(2)

                global pluginDebug
                pluginDebug = False
                setTasmotaDebug(True)
                setMqttDebug(False)

                Debug("Plugin::onStart: Parameters: {}".format(
                    repr(Parameters)))
                self.mqttserveraddress = Parameters["Address"].strip()
                self.mqttserverport = Parameters["Port"].strip()
                self.mqttClient = MqttClient(
                    self.mqttserveraddress, self.mqttserverport,
                    Parameters["Mode5"], self.onMQTTConnected,
                    self.onMQTTDisconnected, self.onMQTTPublish,
                    self.onMQTTSubscribed)
                self.mqttClient.debug(False)
                self.tasmotaHandler = Handler(
                    Parameters["Mode4"].strip().split('|'),
                    Parameters["Mode1"].strip(), Parameters["Mode2"].strip(),
                    Parameters["Mode3"].strip(), self.mqttClient, Devices)
                self.tasmotaHandler.debug(True)
            except Exception as e:
                Domoticz.Error("Plugin::onStart: {}".format(str(e)))
                self.mqttClient = None
        else:
            Domoticz.Error(
                "Plugin::onStart: Domoticz Python env error {}".format(errmsg))
            self.mqttClient = None

    def debug(self, flag):
        global pluginDebug
        pluginDebug = flag

    def checkDevices(self):
        Debug("Plugin::checkDevices")

    # Let tasmotaHandler react to commands from Domoticz

    def onCommand(self, Unit, Command, Level, Color):
        if self.mqttClient is None:
            return False
        return self.tasmotaHandler.onDomoticzCommand(Unit, Command, Level,
                                                     Color)

    def onConnect(self, Connection, Status, Description):
        Debug("Plugin::onConnect")
        if self.mqttClient is not None:
            self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        if self.mqttClient is not None:
            self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        if self.mqttClient is not None:
            self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        Debug("Plugin::onHeartbeat")
        if self.mqttClient is not None:
            try:
                # Reconnect if connection has dropped
                if (self.mqttClient._connection is
                        None) or (not self.mqttClient.isConnected):
                    Debug("Plugin::onHeartbeat: Reconnecting")
                    self.mqttClient._open()
                else:
                    self.mqttClient.ping()
            except Exception as e:
                Domoticz.Error("Plugin::onHeartbeat error {}".format(str(e)))

    # Let tasmotaHandler subscribe its topics

    def onMQTTConnected(self):
        if self.mqttClient is not None:
            self.tasmotaHandler.onMQTTConnected()

    def onMQTTDisconnected(self):
        Debug("Plugin::onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Debug("Plugin::onMQTTSubscribed")

    # Let tasmotaHandler process incoming MQTT messages

    def onMQTTPublish(self, topic, message):
        return self.tasmotaHandler.onMQTTPublish(topic, message)
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]
        
        if self.debugging == "Verbose":
            Domoticz.Debugging(2+4+8+16+64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        Domoticz.Debug("onStart called")
        self.install()
        self.base_topic = Parameters["Mode1"].strip()

        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()
        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port, mqtt_client_id, self.onMQTTConnected, self.onMQTTDisconnected, self.onMQTTPublish, self.onMQTTSubscribed)

        self.api = API(Devices, self.onApiCommand)
        self.devices_manager = DevicesManager()
        self.groups_manager = GroupsManager()

    def checkDevices(self):
        Domoticz.Debug("checkDevices called")

    def onStop(self):
        Domoticz.Debug("onStop called")
        self.uninstall()


    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("onCommand: " + Command + ", level (" + str(Level) + ") Color:" + Color)

        message = None
        device = Devices[Unit] #Devices is Domoticz collection of devices for this hardware
        device_params = device.DeviceID.split('_')
        entity_id = device_params[0]

        if (self.devices_manager.get_device_by_id(entity_id) != None):
            message = self.devices_manager.handle_command(device, Command, Level, Color)
        elif(self.groups_manager.get_group_by_deviceid(device.DeviceID) != None):
            message = self.groups_manager.handle_command(device, Command, Level, Color)
        else:
            Domoticz.Log('Can\'t process command from device "' + device.Name + '"')

        if (message != None):
            self.publishToMqtt(message['topic'], message['payload'])

    def onApiCommand(self, command, data):
        if command == 'publish_mqtt':
            return self.publishToMqtt(data['topic'], data['payload'])
        elif command == 'remove_device':
            return self.devices_manager.remove(data)
        else:
            Domoticz.Error('Internal API command "' + command +'" is not supported by plugin')

    def publishToMqtt(self, topic, payload):
        self.mqttClient.publish(self.base_topic + '/' + topic, payload)

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onDeviceModified(self, unit):
        if (unit == 255):
            self.api.handle_request(Devices[unit].sValue)
            return

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    def onMQTTConnected(self):
        self.mqttClient.subscribe([self.base_topic + '/#'])

    def onMQTTDisconnected(self):
        Domoticz.Debug('Disconnected from MQTT server')

    def onMQTTSubscribed(self):
        Domoticz.Debug('Subscribed to "' + self.base_topic + '/#" topic')

    def onMQTTPublish(self, topic, message):
        # Domoticz.Debug("MQTT message: " + topic + " " + str(message))
        topic = topic.replace(self.base_topic + '/', '')

        self.api.handle_mqtt_message(topic, message)

        if (topic == 'bridge/config/permit_join'):
            return

        if (topic == 'bridge/config/logging'):
            # TODO: Add log feature
            return

        if (topic == 'bridge/devices'):
            Domoticz.Log('Received available devices list from bridge')
            self.devices_manager.set_devices(message)
            return

        if (topic == 'bridge/config'):
            permit_join = 'enabled' if message['permit_join'] else 'disabled'
            Domoticz.Debug('Zigbee2mqtt log level is ' + message['log_level'])
            Domoticz.Log('Joining new devices is ' + permit_join + ' on the zigbee bridge')
            return

        if (topic == 'bridge/state'):
            Domoticz.Log('Zigbee2mqtt bridge is ' + message)

            if message == 'online':
                # self.publishToMqtt('bridge/config/devices', '')
                self.publishToMqtt('bridge/config/groups', '')

            return

        if (topic == 'bridge/log'):
            is_connected = message['type'] == 'device_connected'
            is_removed = message['type'] == 'device_removed'
            is_paired = message['type'] == 'pairing' and message['message'] == 'interview_successful'

            if message['type'] == 'groups':
                Domoticz.Log('Received groups list from bridge')
                self.groups_manager.register_groups(message['message'])

            if is_connected or is_removed or is_paired:
                self.publishToMqtt('bridge/config/devices/get', '')

            if message['type'] == 'ota_update':
                Domoticz.Log(message['message'])

            if message['type'] == 'zigbee_publish_error':
                #an error occured on publish to the zigbee network
                deviceMeta = message['meta']
                Domoticz.Error("A Zigbee publish error occured for device '" + deviceMeta['friendly_name'] + "' with error message: " + message['message'])

            return

        if (self.devices_manager.get_device_by_name(topic) != None):
            self.devices_manager.handle_mqtt_message(topic, message)
        elif (self.groups_manager.get_group_by_name(topic) != None):
            self.groups_manager.handle_mqtt_message(topic, message)

    def install(self):
        Domoticz.Log('Installing plugin custom page...')

        try:
            source_path = Parameters['HomeFolder'] + 'frontend'
            templates_path = Parameters['StartupFolder'] + 'www/templates'
            dst_plugin_path = templates_path + '/zigbee2mqtt'

            Domoticz.Debug('Copying files from ' + source_path + ' to ' + templates_path)

            if not (os.path.isdir(dst_plugin_path)):
                os.makedirs(dst_plugin_path)

            copy2(source_path + '/zigbee2mqtt.html', templates_path)
            copy2(source_path + '/zigbee2mqtt.js', templates_path)
            copy2(source_path + '/zigbee_devices.js', dst_plugin_path)
            copy2(source_path + '/zigbee_groups.js', dst_plugin_path)
            copy2(source_path + '/libs/leaflet.js', dst_plugin_path)
            copy2(source_path + '/libs/leaflet.css', dst_plugin_path)
            copy2(source_path + '/libs/viz.js', dst_plugin_path)
            copy2(source_path + '/libs/viz.full.render.js', dst_plugin_path)
            
            Domoticz.Log('Installing plugin custom page completed.')
        except Exception as e:
            Domoticz.Error('Error during installing plugin custom page')
            Domoticz.Error(repr(e))

    def uninstall(self):
        Domoticz.Log('Uninstalling plugin custom page...')

        try:
            templates_path = Parameters['StartupFolder'] + 'www/templates'
            dst_plugin_path = templates_path + '/zigbee2mqtt'

            Domoticz.Debug('Removing files from ' + templates_path)

            if (os.path.isdir(dst_plugin_path)):
                rmtree(dst_plugin_path)

            if os.path.exists(templates_path + "/zigbee2mqtt.html"):
                os.remove(templates_path + "/zigbee2mqtt.html")

            if os.path.exists(templates_path + "/zigbee2mqtt.js"):
                os.remove(templates_path + "/zigbee2mqtt.js")

            Domoticz.Log('Uninstalling plugin custom page completed.')
        except Exception as e:
            Domoticz.Error('Error during uninstalling plugin custom page')
            Domoticz.Error(repr(e))
Example #7
0
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]
        
        if self.debugging == "Verbose+":
            Domoticz.Debugging(2+4+8+16+64)
        if self.debugging == "Verbose":
            Domoticz.Debugging(2+4+8+16+64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2+4+8)

        self.devices_manager = DevicesManager()
        self.devices_manager.set_devices(Devices)
        device_topic = Parameters["Mode1"].strip()

        if (Parameters["Mode2"] == "Dimmer"):
            self.controller = Dimmer(self.devices_manager, device_topic)
        elif (Parameters["Mode2"] == "PwmDimmer"):
            self.controller = PwmDimmer(self.devices_manager, device_topic)
        elif (Parameters["Mode2"] == "RGB"):
            self.controller = ColorDimmer(self.devices_manager, device_topic, 3)
        elif (Parameters["Mode2"] == "RGBW"):
            self.controller = ColorDimmer(self.devices_manager, device_topic, 4)
        elif (Parameters["Mode2"] == "RGBWW"):
            self.controller = ColorDimmer(self.devices_manager, device_topic, 5)

        self.controller.checkDevices()

        self.topics = list(["stat/" + device_topic + "/RESULT", "tele/" + device_topic + "/STATE"])
        
        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()
        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port, mqtt_client_id, self.onMQTTConnected, self.onMQTTDisconnected, self.onMQTTPublish, self.onMQTTSubscribed)

    def checkDevices(self):
        Domoticz.Log("checkDevices called")

    def onStop(self):
        Domoticz.Log("onStop called")

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("Command: " + Command + " (" + str(Level) + ") Color:" + Color)
        self.controller.onCommand(self.mqttClient, Unit, Command, Level, Color)

    def onConnect(self, Connection, Status, Description):
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    def onMQTTConnected(self):
        Domoticz.Debug("onMQTTConnected")
        self.mqttClient.subscribe(self.topics)

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT message: " + topic + " " + str(message))

        if (topic in self.topics):
            self.controller.onMqttMessage(topic, message)
Example #8
0
class EwpeSmartOverMqttPlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        self.devices = {}
        self.base_topic = Parameters["Mode1"].strip()

        mqttserver_address = Parameters["Address"].strip()
        mqttserver_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()

        self.mqttClient = MqttClient(mqttserver_address, mqttserver_port,
                                     mqtt_client_id, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

        Domoticz.Debug("onStart called")

    def onMQTTConnected(self):
        self.mqttClient.subscribe([self.base_topic + '/#'])
        self.mqttClient.publish(self.base_topic + '/devices/list', '')

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT message: " + topic + " " + str(message))

        if topic == self.base_topic + '/devices':
            Domoticz.Log('Received available devices list')
            self.devices = {}

            for item in message:
                mac = item['mac']
                Domoticz.Log('Device ' + item['name'].strip() + ' (MAC: ' +
                             mac + ')')
                self.devices[mac] = Device(Devices, self.base_topic, item)
        else:
            for mac, device in self.devices.items():
                if topic.startswith(self.base_topic + '/' + mac):
                    Domoticz.Debug(self.base_topic + '/' + mac)
                    device.handle_message(topic, message)

    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("onCommand: " + Command + ", level (" + str(Level) +
                       ") Color:" + Color)
        [mac, alias] = Devices[Unit].DeviceID.split('_')

        if mac in self.devices:
            state = self.devices[mac].handle_command(alias, Command, Level,
                                                     Color)
            topic = '/'.join([self.base_topic, mac, 'set'])
            self.mqttClient.publish(topic, json.dumps(state))

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()
Example #9
0
class DysonPureLinkPlugin:
    #define class variables
    enabled = False
    mqttClient = None
    #unit numbers for devices to create
    #for Pure Cool models
    fanModeUnit = 1
    nightModeUnit = 2
    fanSpeedUnit = 3
    fanOscillationUnit = 4
    standbyMonitoringUnit = 5
    filterLifeUnit = 6
    qualityTargetUnit = 7
    tempHumUnit = 8
    volatileUnit = 9
    particlesUnit = 10
    sleepTimeUnit = 11
    fanStateUnit = 12
    fanFocusUnit = 13
    fanModeAutoUnit = 14
    particles2_5Unit = 15
    particles10Unit = 16
    nitrogenDioxideDensityUnit = 17
    runCounter = 6

    def __init__(self):
        self.myDevice = None
        self.password = None
        self.ip_address = None
        self.port_number = None
        self.sensor_data = None
        self.state_data = None
        self.mqttClient = None

    def onStart(self):
        Domoticz.Log("onStart called")
        if Parameters['Mode4'] == 'Debug':
            Domoticz.Debugging(2)
            DumpConfigToLog()
        if Parameters['Mode4'] == 'Verbose':
            Domoticz.Debugging(1 + 2 + 4 + 8 + 16 + 64)
            DumpConfigToLog()

        #PureLink needs polling, get from config
        Domoticz.Heartbeat(10)

        #check, per device, if it is created. If not,create it
        Options = {
            "LevelActions": "|||",
            "LevelNames": "|OFF|ON|AUTO",
            "LevelOffHidden": "true",
            "SelectorStyle": "1"
        }
        if self.fanModeUnit not in Devices:
            Domoticz.Device(Name='Fan mode',
                            Unit=self.fanModeUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()
        Options = {
            "LevelActions": "||",
            "LevelNames": "|OFF|ON",
            "LevelOffHidden": "true",
            "SelectorStyle": "1"
        }
        if self.fanStateUnit not in Devices:
            Domoticz.Device(Name='Fan state',
                            Unit=self.fanStateUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()
        if self.nightModeUnit not in Devices:
            Domoticz.Device(Name='Night mode',
                            Unit=self.nightModeUnit,
                            Type=244,
                            Subtype=62,
                            Switchtype=0,
                            Image=9).Create()

        Options = {
            "LevelActions": "|||||||||||",
            "LevelNames": "|1|2|3|4|5|6|7|8|9|10|Auto",
            "LevelOffHidden": "false",
            "SelectorStyle": "1"
        }
        if self.fanSpeedUnit not in Devices:
            Domoticz.Device(Name='Fan speed',
                            Unit=self.fanSpeedUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()

        if self.fanOscillationUnit not in Devices:
            Domoticz.Device(Name='Oscilation mode',
                            Unit=self.fanOscillationUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.standbyMonitoringUnit not in Devices:
            Domoticz.Device(Name='Standby monitor',
                            Unit=self.standbyMonitoringUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.filterLifeUnit not in Devices:
            Domoticz.Device(Name='Remaining filter life',
                            Unit=self.filterLifeUnit,
                            TypeName="Custom").Create()
        if self.tempHumUnit not in Devices:
            Domoticz.Device(Name='Temperature and Humidity',
                            Unit=self.tempHumUnit,
                            TypeName="Temp+Hum").Create()
        if self.volatileUnit not in Devices:
            Domoticz.Device(Name='Volatile organic',
                            Unit=self.volatileUnit,
                            TypeName="Air Quality").Create()
        if self.sleepTimeUnit not in Devices:
            Domoticz.Device(Name='Sleep timer',
                            Unit=self.sleepTimeUnit,
                            TypeName="Custom").Create()

        if self.particlesUnit not in Devices:
            Domoticz.Device(Name='Dust',
                            Unit=self.particlesUnit,
                            TypeName="Air Quality").Create()
        if self.qualityTargetUnit not in Devices:
            Options = {
                "LevelActions": "||",
                "LevelNames":
                "|Normal|Sensitive (Medium)|Very Sensitive (High)",
                "LevelOffHidden": "true",
                "SelectorStyle": "1"
            }
            Domoticz.Device(Name='Air quality setpoint',
                            Unit=self.qualityTargetUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()

        if self.particles2_5Unit not in Devices:
            Domoticz.Device(Name='Dust (PM 2,5)',
                            Unit=self.particles2_5Unit,
                            TypeName="Air Quality").Create()
        if self.particles10Unit not in Devices:
            Domoticz.Device(Name='Dust (PM 10)',
                            Unit=self.particles10Unit,
                            TypeName="Air Quality").Create()
        Options = {
            "LevelActions": "|||",
            "LevelNames": "|OFF|ON",
            "LevelOffHidden": "true",
            "SelectorStyle": "1"
        }
        if self.fanModeAutoUnit not in Devices:
            Domoticz.Device(Name='Fan mode auto',
                            Unit=self.fanModeAutoUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()
        Options = {
            "LevelActions": "|||",
            "LevelNames": "|OFF|ON",
            "LevelOffHidden": "true",
            "SelectorStyle": "1"
        }
        if self.fanFocusUnit not in Devices:
            Domoticz.Device(Name='Fan focus mode',
                            Unit=self.fanFocusUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.nitrogenDioxideDensityUnit not in Devices:
            Domoticz.Device(Name='Nitrogen Dioxide Density (NOx)',
                            Unit=self.nitrogenDioxideDensityUnit,
                            TypeName="Air Quality").Create()

        #read out parameters for local connection
        self.ip_address = Parameters["Address"].strip()
        self.port_number = Parameters["Port"].strip()
        self.password = self._hashed_password(Parameters['Password'])
        mqtt_client_id = ""
        self.runCounter = int(Parameters['Mode2'])

        #create a Dyson account
        Domoticz.Debug("=== start making connection to Dyson account ===")
        dysonAccount = DysonAccount(Parameters['Mode5'], Parameters['Mode3'],
                                    "NL")
        dysonAccount.login()
        #deviceList = ()
        deviceList = []
        deviceList = dysonAccount.devices()

        if deviceList == None or len(deviceList) < 1:
            Domoticz.Log("No devices found in Dyson cloud account")
        else:
            Domoticz.Debug("Number of devices from cloud: '" +
                           str(len(deviceList)) + "'")

        if deviceList != None and len(deviceList) > 0:
            if len(Parameters['Mode6']) > 0:
                if Parameters['Mode6'] in deviceList:
                    self.myDevice = deviceList[Parameters['Mode6']]
                else:
                    Domoticz.Error(
                        "The configured device name '" + Parameters['Mode6'] +
                        "' was not found in the cloud account. Available options: "
                        + str(list(deviceList)))
                    return
            elif len(deviceList) == 1:
                self.myDevice = deviceList[list(deviceList)[0]]
                Domoticz.Log(
                    "1 device found in cloud, none configured, assuming we need this one: '"
                    + self.myDevice.name + "'")
            else:
                Domoticz.Error(
                    "More than 1 device found in cloud account but no device name given to select"
                )
                return
            Domoticz.Debug("local device pwd:      '" + self.password + "'")
            Domoticz.Debug("cloud device pwd:      '" +
                           self.myDevice.password + "'")
            Parameters[
                'Username'] = self.myDevice.serial  #take username from account
            Parameters[
                'Password'] = self.myDevice.password  #override the default password with the one returned from the cloud
        elif len(Parameters['Username']) > 0:
            Domoticz.Log(
                "No cloud devices found, the local credentials will be used")
            #create a Dyson device object using plugin parameters
            Domoticz.Debug("local device pwd:      '" + self.password + "'")
            Domoticz.Debug("local device usn:      '" +
                           Parameters['Username'] + "'")
            Parameters['Password'] = self.password
            self.myDevice = DysonPureLinkDevice(Parameters['Password'],
                                                Parameters['Username'],
                                                Parameters['Mode1'])
        else:
            Domoticz.Error("No usable credentials found")

        Domoticz.Log("Device instance created: " + str(self.myDevice))
        self.base_topic = self.myDevice.device_base_topic
        Domoticz.Debug("base topic defined: '" + self.base_topic + "'")

        #create the connection
        if self.myDevice != None:
            self.mqttClient = MqttClient(self.ip_address, self.port_number,
                                         mqtt_client_id, self.onMQTTConnected,
                                         self.onMQTTDisconnected,
                                         self.onMQTTPublish,
                                         self.onMQTTSubscribed)

    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level, Hue):
        Domoticz.Debug("DysonPureLink plugin: onCommand called for Unit " +
                       str(Unit) + ": Parameter '" + str(Command) +
                       "', Level: " + str(Level))
        topic = ''
        payload = ''

        if Unit == self.qualityTargetUnit and Level <= 100:
            topic, payload = self.myDevice.set_quality_target(Level)
        if Unit == self.fanSpeedUnit and Level <= 100:
            arg = "0000" + str(Level // 10)
            topic, payload = self.myDevice.set_fan_speed(
                arg[-4:])  #use last 4 characters as speed level or AUTO
        if Unit == self.fanModeUnit or (Unit == self.fanSpeedUnit
                                        and Level > 100):
            if Level == 10: arg = "OFF"
            if Level == 20: arg = "FAN"
            if Level >= 30: arg = "AUTO"
            topic, payload = self.myDevice.set_fan_mode(arg)
        if Unit == self.fanStateUnit:
            if Level == 10: arg = "OFF"
            if Level == 20: arg = "ON"
            topic, payload = self.myDevice.set_fan_state(arg)
        if Unit == self.fanOscillationUnit:
            topic, payload = self.myDevice.set_oscilation(str(Command).upper())
        if Unit == self.fanFocusUnit:
            topic, payload = self.myDevice.set_focus(str(Command).upper())
        if Unit == self.fanModeAutoUnit:
            topic, payload = self.myDevice.set_fan_mode_auto(
                str(Command).upper())
        if Unit == self.standbyMonitoringUnit:
            topic, payload = self.myDevice.set_standby_monitoring(
                str(Command).upper())
        if Unit == self.nightModeUnit:
            topic, payload = self.myDevice.set_night_mode(str(Command).upper())

        self.mqttClient.Publish(topic, payload)

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called: Connection '" + str(Connection) +
                       "', Status: '" + str(Status) + "', Description: '" +
                       Description + "'")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onNotification(self, Name, Subject, Text, Status, Priority, Sound,
                       ImageFile):
        Domoticz.Log("DysonPureLink plugin: onNotification: " + Name + "," +
                     Subject + "," + Text + "," + Status + "," +
                     str(Priority) + "," + Sound + "," + ImageFile)

    def onHeartbeat(self):
        if self.myDevice != None:
            self.mqttClient.onHeartbeat()
            self.runCounter = self.runCounter - 1
            if self.runCounter <= 0:
                Domoticz.Debug("DysonPureLink plugin: Poll unit")
                self.runCounter = int(Parameters['Mode2'])
                topic, payload = self.myDevice.request_state()
                self.mqttClient.Publish(
                    topic, payload)  #ask for update of current status

            else:
                Domoticz.Debug("Polling unit in " + str(self.runCounter) +
                               " heartbeats.")

    def onDeviceRemoved(self, unit):
        Domoticz.Log(
            "DysonPureLink plugin: onDeviceRemoved called for unit '" +
            str(unit) + "'")

    def updateDevices(self):
        """Update the defined devices from incoming mesage info"""
        #update the devices
        UpdateDevice(self.fanOscillationUnit,
                     self.state_data.oscillation.state,
                     str(self.state_data.oscillation))
        UpdateDevice(self.nightModeUnit, self.state_data.night_mode.state,
                     str(self.state_data.night_mode))

        # Fan speed
        f_rate = self.state_data.fan_speed
        if (f_rate == "AUTO"):
            sValueNew = "110"  # Auto
        else:
            sValueNew = str(int(f_rate) * 10)

        UpdateDevice(self.fanSpeedUnit, 1, sValueNew)
        UpdateDevice(self.fanModeUnit, self.state_data.fan_mode.state,
                     str((self.state_data.fan_mode.state + 1) * 10))
        UpdateDevice(self.fanStateUnit, self.state_data.fan_state.state,
                     str((self.state_data.fan_state.state + 1) * 10))
        UpdateDevice(self.filterLifeUnit, self.state_data.filter_life,
                     str(self.state_data.filter_life))
        UpdateDevice(self.qualityTargetUnit,
                     self.state_data.quality_target.state,
                     str((self.state_data.quality_target.state + 1) * 10))
        UpdateDevice(self.standbyMonitoringUnit,
                     self.state_data.standby_monitoring.state,
                     str((self.state_data.standby_monitoring.state + 1) * 10))
        if self.state_data.fan_mode_auto is not None:
            UpdateDevice(self.fanModeAutoUnit,
                         self.state_data.fan_mode_auto.state,
                         str((self.state_data.fan_mode_auto.state + 1) * 10))
        if self.state_data.focus is not None:
            UpdateDevice(self.fanFocusUnit, self.state_data.focus.state,
                         str(self.state_data.focus))

    def updateSensors(self):
        """Update the defined devices from incoming mesage info"""
        #update the devices
        if self.sensor_data.temperature is not None:
            tempNum = int(self.sensor_data.temperature)
            humNum = int(self.sensor_data.humidity)
            UpdateDevice(
                self.tempHumUnit, 1,
                str(self.sensor_data.temperature)[:4] + ';' +
                str(self.sensor_data.humidity) + ";1")
        UpdateDevice(self.volatileUnit, self.sensor_data.volatile_compounds,
                     str(self.sensor_data.volatile_compounds))
        if self.sensor_data.particles is not None:
            UpdateDevice(self.particlesUnit, self.sensor_data.particles,
                         str(self.sensor_data.particles))
        if self.sensor_data.particles2_5 is not None:
            UpdateDevice(self.particles2_5Unit, self.sensor_data.particles2_5,
                         str(self.sensor_data.particles2_5))
        if self.sensor_data.particles10 is not None:
            UpdateDevice(self.particles10Unit, self.sensor_data.particles10,
                         str(self.sensor_data.particles10))
        if self.sensor_data.nitrogenDioxideDensity is not None:
            UpdateDevice(self.nitrogenDioxideDensityUnit,
                         self.sensor_data.nitrogenDioxideDensity,
                         str(self.sensor_data.nitrogenDioxideDensity))
        UpdateDevice(self.sleepTimeUnit, self.sensor_data.sleep_timer,
                     str(self.sensor_data.sleep_timer))
        Domoticz.Debug("update SensorData: " + str(self.sensor_data))
        Domoticz.Debug("update StateData: " + str(self.state_data))

    def onMQTTConnected(self):
        """connection to device established"""
        Domoticz.Debug("onMQTTConnected called")
        Domoticz.Log("MQTT connection established")
        self.mqttClient.Subscribe([self.base_topic + '/#'
                                   ])  #subscribe to all topics on the machine
        topic, payload = self.myDevice.request_state()
        self.mqttClient.Publish(topic,
                                payload)  #ask for update of current status

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT Publish: MQTT message incoming: " + topic + " " +
                       str(message))

        if (topic == self.base_topic + '/status/current'):
            #update of the machine's status
            if StateData.is_state_data(message):
                Domoticz.Debug("machine state or state change recieved")
                self.state_data = StateData(message)
                self.updateDevices()
            if SensorsData.is_sensors_data(message):
                Domoticz.Debug("sensor state recieved")
                self.sensor_data = SensorsData(message)
                self.updateSensors()

        if (topic == self.base_topic + '/status/connection'):
            #connection status received
            Domoticz.Debug("connection state recieved")

        if (topic == self.base_topic + '/status/software'):
            #connection status received
            Domoticz.Debug("software state recieved")

        if (topic == self.base_topic + '/status/summary'):
            #connection status received
            Domoticz.Debug("summary state recieved")

    def _hashed_password(self, pwd):
        """Hash password (found in manual) to a base64 encoded of its sha512 value"""
        hash = hashlib.sha512()
        hash.update(pwd.encode('utf-8'))
        return base64.b64encode(hash.digest()).decode('utf-8')
Example #10
0
class BasePlugin:

    HEARTBEAT_SEC = 10

    def __init__(self):
        self.mqttClient = None
        self.myroomba = None
        self.state = None
        self.batpct = None
        self.execute = None
        self.MqttUpdatereceived = False
        self.lastMqttUpdate = datetime.now()
        return

    def onStart(self):

        # Debugging On/Off
        self.debug = _DEBUG_ON if Parameters["Mode6"] == "Debug" else _DEBUG_OFF
        Domoticz.Debugging(self.debug)

        # Create images if necessary
        if _IMAGE_ROOMBA not in Images:
            Domoticz.Image("Roomba.zip").Create()

        # Create devices (USED BY DEFAULT)
        CreateDevicesUsed()
        TimeoutDevice(All=True)

        # Create devices (NOT USED BY DEFAULT)
        CreateDevicesNotUsed()

        # Global settings
        DumpConfigToLog()

        # Start MQTT
        try:
            self.mqttClient = MqttClient('localhost', '1883',
                                         self.onMQTTConnected,
                                         self.onMQTTDisconnected,
                                         self.onMQTTPublish,
                                         self.onMQTTSubscribed)
        except Exception as e:
            Domoticz.Error("MQTT client start error: " + str(e))
            self.mqttClient = None

    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level,
                  Color):  # react to commands arrived from Domoticz
        Domoticz.Debug("Command: " + Command + " (" + str(Level) + ") Color:" +
                       Color)
        if Unit == _UNIT_RUNNING:
            if Command == 'On':
                if self.state == 'Charging':
                    self.mqttClient.Publish(_COMMANDS, 'start')
                else:
                    self.mqttClient.Publish(_COMMANDS, 'dock')
            else:
                self.mqttClient.Publish(_COMMANDS, 'stop')
                self.execute = _DOCK

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("Connection: " + str(Status))
        if self.mqttClient is not None:
            self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        if self.mqttClient is not None:
            self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        if self.mqttClient is not None:
            self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        Domoticz.Debug("Heartbeating...")

        if self.mqttClient is not None:
            try:
                # Reconnect if connection has dropped
                if self.mqttClient.mqttConn is None or (
                        not self.mqttClient.mqttConn.Connecting()
                        and not self.mqttClient.mqttConn.Connected()
                        or not self.mqttClient.isConnected):
                    Domoticz.Debug("Reconnecting")
                    self.mqttClient.Open()
                else:
                    self.mqttClient.Ping()

                    # Commands to Roomba
                    if self.execute == _DOCK:
                        if self.state == 'Stopped':
                            self.mqttClient.Publish(_COMMANDS, 'dock')
                        if self.state == 'Charging':
                            self.execute = None

            except Exception as e:
                Domoticz.Error("onHeartbeat: " + str(e))

        # Update devices
        if self.MqttUpdatereceived:
            if self.state:
                UpdateDevice(_UNIT_STATE, 0, self.state,
                             Images[_IMAGE_ROOMBA].ID)
                if self.state == 'Running':
                    UpdateDevice(_UNIT_RUNNING, 1, 1, Images[_IMAGE_ROOMBA].ID)
                else:
                    UpdateDevice(_UNIT_RUNNING, 0, 0, Images[_IMAGE_ROOMBA].ID)
            if self.batpct:
                UpdateDeviceBatSig(_UNIT_RUNNING, self.batpct)
                UpdateDevice(_UNIT_BAT, self.batpct, self.batpct,
                             Images[_IMAGE_ROOMBA].ID)
            self.MqttUpdatereceived = False

        # Check if getting information from MQTT Broker
        if (datetime.now() - self.lastMqttUpdate).total_seconds() > int(
                Parameters["Mode5"]) * 60:
            TimeoutDevice(All=True)

    def onMQTTConnected(self):
        Domoticz.Debug("onMQTTConnected")
        if self.mqttClient is not None:
            self.mqttClient.Subscribe([_STATE, _BATPCT])

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):  # process incoming MQTT statuses
        message = message.decode('utf-8')
        Domoticz.Debug("MQTT message: " + topic + " " + message)
        if topic == _STATE:
            self.state = message
            self.lastMqttUpdate = datetime.now()
            self.MqttUpdatereceived = True
        if topic == _BATPCT:
            self.batpct = int(message)
            self.lastMqttUpdate = datetime.now()
            self.MqttUpdatereceived = True
Example #11
0
class BasePlugin:
    mqttClient = None

    def __init__(self):
        return

    def onStart(self):
        global errmsg
        self.devnames = []
        self.devtimes = []
        if errmsg == "":
            try:
                Domoticz.Heartbeat(10)
                self.debugging = Parameters["Mode6"]
                if self.debugging == "Verbose":
                    Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
                if self.debugging == "Debug":
                    Domoticz.Debugging(2)
                self.base_topic = Parameters["Mode1"]
                self.learnmode = Parameters["Mode2"]
                self.mqttserveraddress = Parameters["Address"].strip()
                self.mqttserverport = Parameters["Port"].strip()
                self.mqttClient = MqttClient(self.mqttserveraddress,
                                             self.mqttserverport, "",
                                             self.onMQTTConnected,
                                             self.onMQTTDisconnected,
                                             self.onMQTTPublish,
                                             self.onMQTTSubscribed)
            except Exception as e:
                Domoticz.Error("MQTT client start error: " + str(e))
                self.mqttClient = None
        else:
            Domoticz.Error(
                "Your Domoticz Python environment is not functional! " +
                errmsg)
            self.mqttClient = None

    def checkDevices(self):
        Domoticz.Debug("checkDevices called")

    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level,
                  Color):  # react to commands arrived from Domoticz
        if self.mqttClient is None:
            return False
        Domoticz.Debug("Command: " + Command + " (" + str(Level) + ") Color:" +
                       Color)
        if Command in ["On", "Off"]:
            if Command == "On":
                Devices[Unit].Update(nValue=1, sValue=str(Command))
            else:
                Devices[Unit].Update(nValue=0, sValue=str(Command))

    def onConnect(self, Connection, Status, Description):
        if self.mqttClient is not None:
            self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        if self.mqttClient is not None:
            self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        if self.mqttClient is not None:
            try:
                self.mqttClient.onMessage(Connection, Data)
            except:
                pass

    def onHeartbeat(self):
        Domoticz.Debug("Heartbeating...")
        if self.mqttClient is not None:
            try:
                # Reconnect if connection has dropped
                if (self.mqttClient._connection is
                        None) or (not self.mqttClient.isConnected):
                    Domoticz.Debug("Reconnecting")
                    self.mqttClient._open()
                else:
                    self.mqttClient.ping()
            except Exception as e:
                Domoticz.Error(str(e))

    def onMQTTConnected(self):
        if self.mqttClient is not None:
            if self.base_topic and self.base_topic != "#":
                topic = self.base_topic + '/#'
            else:
                topic = "rtl_433/#"
#        if "events" not in topic:
#         topic += "/events"
            self.mqttClient.subscribe([topic])

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):  # process incoming MQTT statuses
        try:
            topic = str(topic)
            message2 = str(message)
        except:
            Domoticz.Debug("MQTT message is not a valid string!"
                           )  #if message is not a real string, drop it
            return False
        mqttpath = topic.split('/')
        if "/events" in topic:
            Domoticz.Debug("MQTT message: " + topic + " " + str(message2))
            data = ""
            if 'rows' in message:  # raw data in events - check for Flex encoder output!
                th = []
                for row in message['rows']:
                    try:
                        if int(row['len']) > 4 and str(
                                row['data']).strip() != "":
                            th.append(str(row['data']))
                    except:
                        pass
                mf = 0
                for i in th:
                    cf = th.count(i)
                    if cf > mf:
                        data = i
                        mf = cf
            model = ""
            ch = ""
            st = ""
            if 'model' in message:  # model name in events
                model = str(message['model']).replace("-", "_")
            if 'channel' in message:
                ch = str(message['channel'])
            if 'subtype' in message:
                st = str(message['subtype'])
            if st == "":
                if 'unit' in message:
                    st = str(message['unit'])
            if ch == "" and st == "":
                if 'id' in message:
                    st = str(
                        message['id']
                    )  # use id only for last resort, as cheap weather stations generates new id on every restart
            devname = self.createdevname(model, st, ch, data, "")
            Domoticz.Debug(devname)
            if 'time' in message:
                devtime = str(message['time'])
            else:
                devtime = str(time.time())
            if devname not in self.devnames:
                self.devnames.append(devname)
                self.devtimes.append(devtime)
            else:
                didx = self.devnames.index(devname)
                if didx > 0 and didx < len(self.devtimes):
                    if self.devtimes[
                            didx] == devtime:  # filter duplicated messages
                        return False


#         Domoticz.Debug(devtime)
            signal = 12
            if "rssi" in message:
                try:
                    rssi = float(message["rssi"])
                except:
                    rssi = 0
                if rssi > -0.134:
                    signal = 10
                elif rssi <= -7:
                    signal = 0
                else:
                    signal = 10 - (rssi * -1.34)
            try:
                Domoticz.Debug(
                    str(message["rssi"]) + " " + str(message["snr"]) + " " +
                    str(message["noise"]))
            except:
                pass

            battery = 255
            if "battery" in message:
                if (str(message["battery"]).lower() == "low") or (str(
                        message["battery"]) == "0"):
                    battery = 10
                elif (str(message["battery"]).lower() == "ok") or (str(
                        message["battery"]) == "100"):
                    battery = 100
            if "battery_ok" in message:
                if (str(message["battery_ok"]) == "0"):
                    battery = 10
                elif (str(message["battery_ok"]) == "1"):
                    battery = 100

            state = None  # check if is it a binary sensor
            if "state" in message or "command" in message:
                state = False
                if "state" in message:
                    state = (message["state"] == "ON")
                if "command" in message:
                    state = (message["command"] == "On")
            elif data != "":
                if data[:16] == "ffffffffffffffff":  # false positive
                    return False
                state = True

            if state is not None:
                self.SendSwitch(devname, state, battery, signal)
                return True

            tempc = None
            if "temperature_C" in message:
                tempc = message['temperature_C']
            elif "temperature_F" in message:
                tempc = str((float(message['temperature_F']) - 32) * 5.0 / 9.0)
            try:
                tempc = float(tempc)
            except:
                tempc = None
            if tempc < -40 or tempc > 80:
                tempc = None
                return False  # out of range - false positive

            humi = None
            if "humidity" in message:
                if message["humidity"] == "HH":
                    humi = 90
                elif message["humidity"] == "LL":
                    humi = 10
                else:
                    try:
                        humi = float(message['humidity'])
                    except:
                        humi = None
                    if humi < 0 or humi > 100:
                        humi = None

            pressure = None
            if "pressure_hPa" in message:
                pressure = message['pressure']

            if (tempc is not None) and (humi is not None) and (pressure
                                                               is not None):
                self.SendTempHumBaroSensor(devname, tempc, humi, pressure,
                                           battery, signal)
            elif (tempc is not None) and (humi is not None):
                self.SendTempHumSensor(devname, tempc, humi, battery, signal)
            elif (tempc is not None):
                self.SendTempSensor(devname, tempc, battery, signal)
            elif (humi is not None):
                self.SendHumSensor(devname, humi, battery, signal)

            rain = None
            if "rain" in message:
                rain = message['rain']
            elif "rain_mm" in message:
                rain = message['rain_mm']
            elif "rainfall_mm" in message:
                rain = message['rainfall_mm']
            raintotal = None
            if "rain_total" in message:
                raintotal = message['rain_total']

            if rain is not None:
                self.SendRainSensor(devname, rain, raintotal, battery, signal)

            depth = None
            if "depth_cm" in message:
                depth = message['depth_cm']
            elif "depth" in message:
                depth = message['depth']

            if depth is not None:
                self.SendDistanceSensor(devname, depth, battery, signal)

            windstr = None
            if "windstrength" in message:
                windstr = message['windstrength']
            elif "wind_speed" in message:
                windstr = message['wind_speed']
            elif "average" in message:
                windstr = message['average']
            elif "wind_avg_km_h" in message:
                windstr = message['wind_avg_km_h']
            elif "wind_avg_m_s" in message:
                windstr = message['wind_avg_m_s']

            winddir = None
            if "winddirection" in message:
                winddir = message['winddirection']
            elif "wind_direction" in message:
                winddir = message['wind_direction']
            elif "direction" in message:
                winddir = message['direction']

            winddirdeg = 0
            if "wind_dir_deg" in message:
                winddirdeg = message['wind_dir_deg']

            windgust = None
            if "wind_gust" in message:
                windgust = message['wind_gust']
            elif "gust" in message:
                windgust = message['gust']

            if winddir is not None:
                self.SendWind(devname, winddirdeg, winddir, windstr, windgust,
                              tempc, battery, signal)

            moisture = None
            if "moisture" in message:
                moisture = message['moisture']

            if (moisture is not None):
                self.SendMoisture(devname, moisture, battery, signal)

            power = None
            if "power_W" in message:
                power = message['power_W']

            if (power is not None):
                self.SendWattMeter(devname, watt, battery, signal)

    def getdevID(self, unitname):
        iUnit = -1
        for Device in Devices:
            try:
                if (Devices[Device].DeviceID.strip() == unitname):
                    iUnit = Device
                    break
            except:
                pass
        return iUnit

    def SendSwitch(self, unitname, state, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Switch",
                                Used=0,
                                DeviceID=unitname).Create()
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            try:
                if state == False or (str(state).strip().lower()
                                      in ["0", "off"]):
                    scmd = "Off"
                    ncmd = 0
                else:
                    scmd = "On"
                    ncmd = 1
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=ncmd,
                                      sValue=scmd,
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except Exception as e:
                Domoticz.Debug(str(e))
                return False

    def SendTempHumBaroSensor(self, unitname, temp, hum, press, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                Type=84,
                                Subtype=16,
                                Used=0,
                                DeviceID=unitname).Create()  # Temp+Hum+Baro
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            sval = str(temp) + ";" + str(hum) + ";" + str(
                self.gethumstatus(hum)) + ";" + str(press) + ";0"
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendTempHumSensor(self, unitname, temp, hum, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Temp+Hum",
                                Used=0,
                                DeviceID=unitname).Create()  # Temp+Hum
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            sval = str(temp) + ";" + str(hum) + ";" + str(
                self.gethumstatus(hum))
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendTempSensor(self, unitname, temp, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Temperature",
                                Used=0,
                                DeviceID=unitname).Create()  # Temp
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            sval = str(temp)
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendHumSensor(self, unitname, hum, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Humidity",
                                Used=0,
                                DeviceID=unitname).Create()  # Hum
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            sval = str(hum)
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendRainSensor(self, unitname, rain, raintotal, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Rain",
                                Used=0,
                                DeviceID=unitname).Create()  # Rain
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            if raintotal is None:
                try:
                    curval = Devices[iUnit].sValue
                    prevdata = curval.split(";")
                except:
                    prevdata = []
                if len(prevdata) < 2:
                    prevdata.append(0)
                    prevdata.append(0)
                raintotal = prevdata[1] + rain
            sval = str(int(float(rain) * 100)) + ";" + str(raintotal)
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendDistanceSensor(self, unitname, dist, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Distance",
                                Used=0,
                                DeviceID=unitname).Create()  # Distance
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            sval = str(dist)
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendWind(self, unitname, winddirdeg, winddir, windstr, windgust, tempc,
                 battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Wind",
                                Used=0,
                                DeviceID=unitname).Create()  # Wind
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            sval = str(winddirdeg)
            if winddir is not None:
                sval += ";" + str(winddir)
            else:
                sval += ";"
            ts = ";"
            try:
                ts = ";" + str(float(windstr) * 10)
            except:
                ts = ";"
            sval += ts

            ts = ";"
            try:
                ts = ";" + str(float(windgust) * 10)
            except:
                ts = ";"
            sval += ts

            if tempc is not None:
                sval += ";" + str(tempc)
            else:
                sval += ";;"
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendMoisture(self, unitname, moist, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                TypeName="Soil Moisture",
                                Used=0,
                                DeviceID=unitname).Create()  # Moisture
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            sval = str(moist)
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def SendWattMeter(self, unitname, watt, battery, rssi):
        iUnit = self.getdevID(unitname)
        if iUnit < 0 and self.learnmode != False:  # if device does not exists in Domoticz, than create it
            try:
                iUnit = 0
                for x in range(1, 256):
                    if x not in Devices:
                        iUnit = x
                        break
                if iUnit == 0:
                    iUnit = len(Devices) + 1
                Domoticz.Device(Name=unitname,
                                Unit=iUnit,
                                Type=243,
                                Subtype=29,
                                Used=0,
                                DeviceID=unitname).Create()  # kWh
            except Exception as e:
                Domoticz.Debug(str(e))
                return False
        if iUnit > 0:
            try:
                sval = str(float(watt) / 1000) + ";0"
            except:
                sval = "0;0"
            try:
                if battery is None:
                    battery = 255
                Devices[iUnit].Update(nValue=0,
                                      sValue=str(sval),
                                      BatteryLevel=int(battery),
                                      SignalLevel=int(rssi))
            except:
                Domoticz.Debug(str(e))

    def gethumstatus(self, mval):
        hstat = 0
        if int(mval) >= 50 and int(mval) <= 70:
            hstat = 1
        elif int(mval) < 40:
            hstat = 2
        elif int(mval) > 70:
            hstat = 3
        return hstat

    def createdevname(self, m, s, c, d, t):
        tn = ""
        if s:
            tn += s
        if c:
            tn += c
        if d:
            tn += "-" + d[:16]
        if t:
            tn += "-" + t[:16]
        lplen = 25 - len(tn)
        if lplen == 0:
            return tn
        elif lplen < 0:
            return tn[:25]
        else:
            if tn[0] != "-":
                tn = "-" + tn
            tn = m[:(25 - len(tn) - 1)] + tn
            return tn
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        self.install()
        self.base_topic = Parameters["Mode1"].strip()

        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()
        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port,
                                     mqtt_client_id, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

        self.api = API(self.onApiCommand)
        self.devices_manager = DevicesManager()
        self.groups_manager = GroupsManager()

    def checkDevices(self):
        domoticz.debug("checkDevices called")

    def onStop(self):
        domoticz.debug("onStop called")
        self.uninstall()

    def onCommand(self, device_id, unit, command, Level, Color):
        domoticz.debug("[Command] Device " + device_id + '(' + str(unit) +
                       '): ' + command + "(level = " + str(Level) +
                       ", color = " + Color + ')')

        message = None
        domoticz_device = domoticz.get_device(device_id, unit)
        zigbee_device_alias = configuration.get_zigbee_feature_data(
            device_id, unit)

        if zigbee_device_alias == None:
            domoticz.log('Can\'t process command from device "' +
                         domoticz_device.Name + '"')

        if self.groups_manager.get_group_by_id(
                zigbee_device_alias['zigbee']['address']) != None:
            message = self.groups_manager.handle_command(
                device_id, unit, command, Level, Color)
        else:
            message = self.devices_manager.handle_command(
                device_id, unit, command, Level, Color)

        if (message != None):
            self.publishToMqtt(message['topic'], message['payload'])

    def onApiCommand(self, command, data):
        if command == 'publish_mqtt':
            return self.publishToMqtt(data['topic'], data['payload'])
        elif command == 'remove_device':
            return self.devices_manager.remove(data)
        else:
            domoticz.error('Internal API command "' + command +
                           '" is not supported by plugin')

    def publishToMqtt(self, topic, payload):
        self.mqttClient.publish(self.base_topic + '/' + topic, payload)

    def onConnect(self, Connection, Status, Description):
        domoticz.debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onDeviceModified(self, device_id, unit):
        if device_id == 'api_transport' and unit == 255:
            device = domoticz.get_device(device_id, unit)
            self.api.handle_request(device.sValue)
            return

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    def onMQTTConnected(self):
        self.mqttClient.subscribe([self.base_topic + '/#'])

    def onMQTTDisconnected(self):
        domoticz.debug('Disconnected from MQTT server')

    def onMQTTSubscribed(self):
        domoticz.debug('Subscribed to "' + self.base_topic + '/#" topic')

    def onMQTTPublish(self, topic, message):
        # domoticz.debug("MQTT message: " + topic + " " + str(message))
        topic = topic.replace(self.base_topic + '/', '')

        self.api.handle_mqtt_message(topic, message)
        bridge.handle_mqtt_message(topic, message)

        if (topic == 'bridge/config/permit_join'):
            return

        if (topic == 'bridge/config/logging') or (topic == 'bridge/logging'):
            # TODO: Add log feature
            return

        if (topic == 'bridge/devices'):
            self.devices_manager.set_devices(message)
            return

        if (topic == 'bridge/config'):
            permit_join = 'enabled' if message['permit_join'] else 'disabled'
            domoticz.debug('Zigbee2mqtt log level is ' + message['log_level'])
            domoticz.log('Joining new devices is ' + permit_join +
                         ' on the zigbee bridge')
            return

        if (topic == 'bridge/state'):
            domoticz.log('Zigbee2mqtt bridge is ' + message)
            return

        if (topic == 'bridge/log'):
            is_connected = message['type'] == 'device_connected'
            is_removed = message['type'] == 'device_removed'
            is_paired = message['type'] == 'pairing' and message[
                'message'] == 'interview_successful'

            if message['type'] == 'groups':
                domoticz.log('Received groups list from bridge')
                self.groups_manager.register_groups(message['message'])

            if is_connected or is_removed or is_paired:
                self.publishToMqtt('bridge/config/devices/get', '')

            if message['type'] == 'ota_update':
                domoticz.log(message['message'])

            if (message['type'] == 'device_renamed'):
                domoticz.debug("Device renamed from '{0}' to '{1}'".format(
                    message['message']['from'], message['message']['to']))
                if (self.devices_manager.get_device_by_name(
                        message['message']['from']) != None):
                    domoticz.debug("attempt to rename on bridge/log")
                    toRename = self.devices_manager.get_device_by_name(
                        message['message']['from'])
                    toRename.zigbee_device['friendly_name'] = message[
                        'message']['to']
                    self.devices_manager.devices[
                        toRename.zigbee_device['ieee_address']] = toRename
                else:
                    domoticz.debug("attempt to rename failed on bridge/log")

            if message['type'] == 'zigbee_publish_error':
                #an error occured on publish to the zigbee network
                deviceMeta = message['meta']
                domoticz.error("A Zigbee publish error occured for device '" +
                               deviceMeta['friendly_name'] +
                               "' with error message: " + message['message'])

            return

        if (self.groups_manager.get_group_by_name(topic) != None):
            self.groups_manager.handle_mqtt_message(topic, message)
        elif (self.devices_manager.get_device_by_name(topic) != None):
            self.devices_manager.handle_mqtt_message(topic, message)

    def install(self):
        domoticz.log('Installing plugin custom page...')

        try:
            source_path = Parameters['HomeFolder'] + 'frontend'
            templates_path = Parameters['StartupFolder'] + 'www/templates'
            dst_plugin_path = templates_path + '/zigbee2mqtt'

            domoticz.debug('Copying files from ' + source_path + ' to ' +
                           templates_path)

            if not (os.path.isdir(dst_plugin_path)):
                os.makedirs(dst_plugin_path)

            copy2(source_path + '/zigbee2mqtt.html', templates_path)
            copy2(source_path + '/zigbee2mqtt.js', templates_path)
            copy2(source_path + '/plugin_config.js', dst_plugin_path)
            copy2(source_path + '/zigbee_devices.js', dst_plugin_path)
            copy2(source_path + '/zigbee_groups.js', dst_plugin_path)
            copy2(source_path + '/libs/leaflet.js', dst_plugin_path)
            copy2(source_path + '/libs/leaflet.css', dst_plugin_path)
            copy2(source_path + '/libs/viz.js', dst_plugin_path)
            copy2(source_path + '/libs/viz.full.render.js', dst_plugin_path)
            copy2(source_path + '/libs/ace_json_mode.js', dst_plugin_path)
            copy2(source_path + '/libs/ace_worker_json.js', dst_plugin_path)

            domoticz.log('Installing plugin custom page completed.')
        except Exception as e:
            domoticz.error('Error during installing plugin custom page')
            domoticz.error(repr(e))

    def uninstall(self):
        domoticz.log('Uninstalling plugin custom page...')

        try:
            templates_path = Parameters['StartupFolder'] + 'www/templates'
            dst_plugin_path = templates_path + '/zigbee2mqtt'

            domoticz.debug('Removing files from ' + templates_path)

            if (os.path.isdir(dst_plugin_path)):
                rmtree(dst_plugin_path)

            if os.path.exists(templates_path + "/zigbee2mqtt.html"):
                os.remove(templates_path + "/zigbee2mqtt.html")

            if os.path.exists(templates_path + "/zigbee2mqtt.js"):
                os.remove(templates_path + "/zigbee2mqtt.js")

            domoticz.log('Uninstalling plugin custom page completed.')
        except Exception as e:
            domoticz.error('Error during uninstalling plugin custom page')
            domoticz.error(repr(e))
Example #13
0
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        self.base_topic = "shellies"  # hardwired

        self.mqttserveraddress = Parameters["Address"].strip()
        self.mqttserverport = Parameters["Port"].strip()
        self.mqttClient = MqttClient(self.mqttserveraddress,
                                     self.mqttserverport, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

    def checkDevices(self):
        Domoticz.Debug("checkDevices called")

    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level,
                  Color):  # react to commands arrived from Domoticz
        Domoticz.Debug("Command: " + Command + " (" + str(Level) + ") Color:" +
                       Color)
        device = Devices[Unit]
        device_id = device.DeviceID.split('-')
        relnum = -1
        try:
            relnum = int(device_id[2].strip())
        except:
            relnum = -1
        if relnum in range(
                0, 4) and len(device_id) == 3:  # check if is it a normal relay
            mqttpath = self.base_topic + "/" + device_id[0] + "-" + device_id[
                1] + "/relay/" + device_id[2] + "/command"
            cmd = Command.strip().lower()
            Domoticz.Debug(mqttpath + " " + cmd)
            if cmd in ["on", "off"]:  # commands are simply on or off
                self.mqttClient.Publish(mqttpath, cmd)
                if cmd == "off":
                    device.Update(
                        Level, Command)  # force device update if it is offline
        # otherwise check if it is a roller shutter
        elif relnum in range(
                0, 4) and len(device_id) == 4 and device_id[len(device_id) -
                                                            1] == "roller":
            cmd = Command.strip().lower()
            scmd = ""  # Translate Domoticz command to Shelly command
            if cmd == "stop":
                scmd = "stop"
            elif cmd == "on":
                scmd = "open"
            elif cmd == "off":
                scmd = "close"
            if scmd != "":
                mqttpath = self.base_topic + "/" + device_id[
                    0] + "-" + device_id[1] + "/roller/" + device_id[
                        2] + "/command"
                self.mqttClient.Publish(mqttpath, scmd)
        # experimental support for v1.4 Percentage poisitioning
        elif relnum in range(
                0, 4) and len(device_id) == 4 and device_id[len(device_id) -
                                                            1] == "pos":
            cmnd = str(Command).strip().lower()
            pos = str(Level).strip().lower()
            if ((cmnd == "set level")
                    or (cmnd == "off" and pos != "50"
                        and pos != "100")):  # percentage requested
                mqttpath = self.base_topic + "/" + device_id[
                    0] + "-" + device_id[1] + "/roller/" + device_id[
                        2] + "/command/pos"
                Domoticz.Debug(mqttpath + " " + str(Command) + " " +
                               str(Level))
                self.mqttClient.Publish(mqttpath, pos)
            else:  # command arrived
                scmd = ""  # Translate Domoticz command to Shelly command
                if cmnd == "on":
                    scmd = "open"
                elif cmnd == "off":
                    scmd = "close"
                if scmd != "":
                    mqttpath = self.base_topic + "/" + device_id[
                        0] + "-" + device_id[1] + "/roller/" + device_id[
                            2] + "/command"
                    self.mqttClient.Publish(mqttpath, scmd)

    def onConnect(self, Connection, Status, Description):
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        Domoticz.Debug("Heartbeating...")

        # Reconnect if connection has dropped
        if self.mqttClient.mqttConn is None or (
                not self.mqttClient.mqttConn.Connecting()
                and not self.mqttClient.mqttConn.Connected()
                or not self.mqttClient.isConnected):
            Domoticz.Debug("Reconnecting")
            self.mqttClient.Open()
        else:
            self.mqttClient.Ping()

    def onMQTTConnected(self):
        self.mqttClient.Subscribe([self.base_topic + '/#'])

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):  # process incoming MQTT statuses
        if "/announce" in topic:  # announce did not contain any information for us
            return False
        try:
            topic = str(topic)
            message = str(message)
        except:
            Domoticz.Debug("MQTT message is not a valid string!"
                           )  #if message is not a real string, drop it
            return False
        Domoticz.Debug("MQTT message: " + topic + " " + str(message))
        mqttpath = topic.split('/')
        if (mqttpath[0] == self.base_topic):
            # RELAY type, not command->process
            if (len(mqttpath) > 3) and (mqttpath[2]
                                        == "relay") and ("/command"
                                                         not in topic):
                unitname = mqttpath[1] + "-" + mqttpath[3]
                unitname = unitname.strip()
                devtype = 1
                try:
                    funcid = int(mqttpath[3].strip())
                    devtype = 0
                except:
                    devtype = 1
                if len(mqttpath) == 5 and devtype == 0:
                    devtype = 2
                subval = ""
                if devtype == 1:
                    subval = mqttpath[3].strip()
                elif devtype == 2:
                    subval = mqttpath[4].strip()
                if subval == "power" or subval == "energy":
                    unitname = mqttpath[1] + "-energy"
                iUnit = -1
                for Device in Devices:
                    try:
                        if (Devices[Device].DeviceID.strip() == unitname):
                            iUnit = Device
                            break
                    except:
                        pass
                if iUnit < 0:  # if device does not exists in Domoticz, than create it
                    iUnit = 0
                    for x in range(1, 256):
                        if x not in Devices:
                            iUnit = x
                            break
                    if iUnit == 0:
                        iUnit = len(Devices) + 1
                    if devtype == 0:
                        Domoticz.Device(Name=unitname,
                                        Unit=iUnit,
                                        TypeName="Switch",
                                        Used=1,
                                        DeviceID=unitname).Create()
                    else:
                        if subval == "energy" or subval == "power":
                            Domoticz.Device(Name=unitname,
                                            Unit=iUnit,
                                            Type=243,
                                            Subtype=29,
                                            Used=1,
                                            DeviceID=unitname).Create()
                if devtype == 0:
                    scmd = str(message).strip().lower()
                    if (str(Devices[iUnit].sValue).lower() != scmd):
                        if (scmd == "on"):  # set device status if needed
                            Devices[iUnit].Update(1, "On")
                        else:
                            Devices[iUnit].Update(0, "Off")
                else:
                    curval = Devices[iUnit].sValue
                    try:
                        prevdata = curval.split(";")
                    except:
                        prevdata = []
                    if len(prevdata) < 2:
                        prevdata.append(0)
                        prevdata.append(0)
                    try:
                        mval = float(str(message).strip())
                    except:
                        mval = str(message).strip()
                    sval = ""
                    if subval == "power":
                        sval = str(mval) + ";" + str(prevdata[1])
                    elif subval == "energy":
                        try:
                            mval2 = (mval / 100)  # 10*Wh?
                        except:
                            mval2 = str(mval)
                        sval = str(prevdata[0]) + ";" + str(mval2)
                    Devices[iUnit].Update(0, sval)
                return True
            # ROLLER type, not command->process
            elif (len(mqttpath) > 3) and (mqttpath[2]
                                          == "roller") and ("/command"
                                                            not in topic):
                if mqttpath[len(mqttpath) - 1] == "pos":
                    unitname = mqttpath[1] + "-" + mqttpath[3] + "-pos"
                else:
                    unitname = mqttpath[1] + "-" + mqttpath[3] + "-roller"
                unitname = unitname.strip()
                iUnit = -1
                for Device in Devices:
                    try:
                        if (Devices[Device].DeviceID.strip() == unitname):
                            iUnit = Device
                            break
                    except:
                        pass
                if iUnit < 0:  # if device does not exists in Domoticz, than create it
                    iUnit = 0
                    for x in range(1, 256):
                        if x not in Devices:
                            iUnit = x
                            break
                    if iUnit == 0:
                        iUnit = len(Devices) + 1
                    if "-pos" in unitname:
                        Domoticz.Device(Name=unitname,
                                        Unit=iUnit,
                                        Type=244,
                                        Subtype=62,
                                        Switchtype=13,
                                        Used=1,
                                        DeviceID=unitname).Create(
                                        )  # create Blinds Percentage
                    else:
                        Domoticz.Device(Name=unitname,
                                        Unit=iUnit,
                                        Type=244,
                                        Subtype=62,
                                        Switchtype=15,
                                        Used=1,
                                        DeviceID=unitname).Create(
                                        )  # create Venetian Blinds EU type
                if "-pos" in unitname:
                    try:
                        pval = str(message).strip()
                        nval = 0
                        if int(pval) > 0 and int(pval) < 100:
                            nval = 2
                        if int(pval) > 99:
                            nval = 1
                        Devices[iUnit].Update(nval, pval)
                    except:
                        Domoticz.Debug("MQTT message error " + str(topic) +
                                       ":" + str(message))
                else:
                    bcmd = str(message).strip().lower()
                    if bcmd == "stop" and str(
                            Devices[iUnit].sValue).lower() != "stop":
                        Devices[iUnit].Update(17, "Stop")  # stop
                        return True
                    elif bcmd == "open" and str(
                            Devices[iUnit].sValue).lower() != "off":
                        Devices[iUnit].Update(0, "Off")  # open
                        return True
                    elif bcmd == "close" and str(
                            Devices[iUnit].sValue).lower() != "on":
                        Devices[iUnit].Update(1, "On")  # close
                        return True
            # INPUT type, not command->process
            elif (len(mqttpath) > 3) and (mqttpath[2] == "input") and (
                    mqttpath[len(mqttpath) - 1] != "command"):
                unitname = mqttpath[1] + "-" + mqttpath[3] + "-input"
                unitname = unitname.strip()
                iUnit = -1
                for Device in Devices:
                    try:
                        if (Devices[Device].DeviceID.strip() == unitname):
                            iUnit = Device
                            break
                    except:
                        pass
                if iUnit < 0:  # if device does not exists in Domoticz, than create it
                    iUnit = 0
                    for x in range(1, 256):
                        if x not in Devices:
                            iUnit = x
                            break
                    if iUnit == 0:
                        iUnit = len(Devices) + 1
                    Domoticz.Device(Name=unitname,
                                    Unit=iUnit,
                                    TypeName="Switch",
                                    Used=1,
                                    DeviceID=unitname).Create()
                if str(message).lower == "on" or str(message) == "1":
                    scmd = "on"
                else:
                    scmd = "off"
                if (str(Devices[iUnit].sValue).lower() != scmd):
                    if (scmd == "on"):  # set device status if needed
                        Devices[iUnit].Update(1, "On")
                    else:
                        Devices[iUnit].Update(0, "Off")
            # SENSOR type, not command->process
            elif (len(mqttpath) > 3) and (mqttpath[2] == "sensor") and (
                    mqttpath[3] in ['temperature', 'humidity', 'battery'
                                    ]) and (("shellysense" in mqttpath[1]) or
                                            ("shellyht" in mqttpath[1])):
                unitname = mqttpath[1] + "-sensor"
                unitname = unitname.strip()
                iUnit = -1
                for Device in Devices:
                    try:
                        if (Devices[Device].DeviceID.strip() == unitname):
                            iUnit = Device
                            break
                    except:
                        pass
                if iUnit < 0:  # if device does not exists in Domoticz, than create it
                    iUnit = 0
                    for x in range(1, 256):
                        if x not in Devices:
                            iUnit = x
                            break
                    if iUnit == 0:
                        iUnit = len(Devices) + 1
                    Domoticz.Device(
                        Name=unitname,
                        Unit=iUnit,
                        TypeName="Temp+Hum",
                        Used=1,
                        DeviceID=unitname).Create()  # create Temp+Hum Type=82
                stype = mqttpath[3].strip().lower()
                curval = Devices[iUnit].sValue
                try:
                    mval = float(message)
                except:
                    mval = str(message).strip()
                if stype == "battery":
                    Devices[iUnit].Update(0, curval, BatteryLevel=int(mval))
                elif stype == "temperature":
                    try:
                        env = curval.split(";")
                    except:
                        env = [0, 0]
                    if len(env) < 3:
                        env.append(0)
                        env.append(0)
                        env.append(0)
                    sval = str(mval) + ";" + str(env[1]) + ";" + str(env[2])
                    Devices[iUnit].Update(0, sval)
                elif stype == "humidity":
                    hstat = 0
                    try:
                        env = curval.split(";")
                    except:
                        env = [0, 0]
                    if len(env) < 1:
                        env.append(0)
                    if int(mval) >= 50 and int(mval) <= 70:
                        hstat = 1
                    elif int(mval) < 40:
                        hstat = 2
                    elif int(mval) > 70:
                        hstat = 3
                    sval = str(env[0]) + ";" + str(mval) + ";" + str(hstat)
                    Devices[iUnit].Update(0, sval)
Example #14
0
class DysonPureLinkPlugin:
    #define class variables
    #plugin version
    version = "4.0.3"
    enabled = False
    mqttClient = None
    #unit numbers for devices to create
    #for Pure Cool models
    fanModeUnit = 1
    nightModeUnit = 2
    fanSpeedUnit = 3
    fanOscillationUnit = 4
    standbyMonitoringUnit = 5
    filterLifeUnit = 6
    qualityTargetUnit = 7
    tempHumUnit = 8
    volatileUnit = 9
    particlesUnit = 10
    sleepTimeUnit = 11
    fanStateUnit = 12
    fanFocusUnit = 13
    fanModeAutoUnit = 14
    particles2_5Unit = 15
    particles10Unit = 16
    nitrogenDioxideDensityUnit = 17
    heatModeUnit = 18
    heatTargetUnit = 19
    heatStateUnit = 20
    particlesMatter25Unit = 21
    particlesMatter10Unit = 22
    resetFilterLifeUnit = 23
    deviceStatusUnit = 24

    runCounter = 6

    def __init__(self):
        self.myDevice = None
        self.password = None
        self.ip_address = None
        self.port_number = None
        self.sensor_data = None
        self.state_data = None
        self.mqttClient = None
        self.log_level = None

    def onStart(self):
        Domoticz.Debug("onStart called")
        #read out parameters for local connection
        self.ip_address = Parameters["Address"].strip()
        self.port_number = Parameters["Port"].strip()
        self.otp_code = Parameters['Mode1']
        self.runCounter = int(Parameters['Mode2'])
        self.log_level = Parameters['Mode4']
        self.account_password = Parameters['Mode3']
        self.account_email = Parameters['Mode5']
        self.machine_name = Parameters['Mode6']

        if self.log_level == 'Debug':
            Domoticz.Debugging(2)
            DumpConfigToLog()
        if self.log_level == 'Verbose':
            Domoticz.Debugging(1 + 2 + 4 + 8 + 16 + 64)
            DumpConfigToLog()
        if self.log_level == 'Reset':
            Domoticz.Log(
                "Plugin config will be erased to retreive new cloud account data"
            )
            Config = {}
            Config = Domoticz.Configuration(Config)

        #PureLink needs polling, get from config
        Domoticz.Heartbeat(10)

        self.checkVersion(self.version)

        mqtt_client_id = ""

        #create a Dyson account
        deviceList = self.get_device_names()

        if deviceList != None and len(deviceList) > 0:
            Domoticz.Debug(
                "Number of devices found in plugin configuration: '" +
                str(len(deviceList)) + "'")
        else:
            Domoticz.Log(
                "No devices found in plugin configuration, request from Dyson cloud account"
            )

            #new authentication
            Domoticz.Debug(
                "=== start making connection to Dyson account, new method as of 2021 ==="
            )
            dysonAccount2 = DysonAccount()
            challenge_id = getConfigItem(Key="challenge_id", Default="")
            setConfigItem(Key="challenge_id", Value="")  #clear after use
            if challenge_id == "":
                #request otp code via email when no code entered
                challenge_id = dysonAccount2.login_email_otp(
                    self.account_email, "NL")
                setConfigItem(Key="challenge_id", Value=challenge_id)
                Domoticz.Log(
                    '==== An OTP verification code had been requested, please check email and paste code into plugin====='
                )
                return
            else:
                #verify the received code
                if len(self.otp_code) < 6:
                    Domoticz.Error("invalid verification code supplied")
                    return
                dysonAccount2.verify(self.otp_code, self.account_email,
                                     self.account_password, challenge_id)
                setConfigItem(
                    Key="challenge_id",
                    Value="")  #reset challenge id as it is no longer valid
                Parameters['Mode1'] = "0"  #reset the stored otp code
                #get list of devices info's
                deviceList = dysonAccount2.devices()
                deviceNames = list(deviceList.keys())
                Domoticz.Log("Received new devices: " + str(deviceNames) +
                             ", they will be stored in plugin configuration")
                i = 0
                for device in deviceList:
                    setConfigItem(
                        Key="{0}.name".format(i),
                        Value=deviceNames[i])  #store the name of the machine
                    Domoticz.Debug('Key="{0}.name", Value = {1}'.format(
                        i, deviceNames[i]))  #store the name of the machine
                    setConfigItem(
                        Key="{0}.credential".format(
                            deviceList[deviceNames[i]].name),
                        Value=deviceList[
                            deviceNames[i]].credential)  #store the credential
                    Domoticz.Debug('Key="{0}.credential", Value = {1}'.format(
                        deviceList[deviceNames[i]].name, deviceList[
                            deviceNames[i]].credential))  #store the credential
                    setConfigItem(
                        Key="{0}.serial".format(
                            deviceList[deviceNames[i]].name),
                        Value=deviceList[
                            deviceNames[i]].serial)  #store the serial
                    Domoticz.Debug('Key="{0}.serial", Value =  {1}'.format(
                        deviceList[deviceNames[i]].name,
                        deviceList[deviceNames[i]].serial))  #store the serial
                    setConfigItem(Key="{0}.product_type".format(
                        deviceList[deviceNames[i]].name),
                                  Value=deviceList[deviceNames[i]].product_type
                                  )  #store the product_type
                    Domoticz.Debug(
                        'Key="{0}.product_type" , Value = {1}'.format(
                            deviceList[deviceNames[i]].name,
                            deviceList[deviceNames[i]].product_type)
                    )  #store the product_type
                    i = i + 1

        if deviceList == None or len(deviceList) < 1:
            Domoticz.Error(
                "No devices found in plugin configuration or Dyson cloud account"
            )
            return
        else:
            Domoticz.Debug("Number of devices in plugin: '" +
                           str(len(deviceList)) + "'")

        if deviceList != None and len(deviceList) > 0:
            if len(self.machine_name) > 0:
                if self.machine_name in deviceList:
                    password, serialNumber, deviceType = self.get_device_config(
                        self.machine_name)
                    Domoticz.Debug(
                        "password: {0}, serialNumber: {1}, deviceType: {2}".
                        format(password, serialNumber, deviceType))
                    self.myDevice = DysonPureLinkDevice(
                        password, serialNumber, deviceType, self.machine_name)
                else:
                    Domoticz.Error(
                        "The configured device name '" + self.machine_name +
                        "' was not found in the cloud account. Available options: "
                        + str(list(deviceList)))
                    return
            elif len(deviceList) == 1:
                self.myDevice = deviceList[list(deviceList)[0]]
                Domoticz.Log(
                    "1 device found in plugin, none configured, assuming we need this one: '"
                    + self.myDevice.name + "'")
            else:
                #more than 1 device returned in cloud and no name configured, which the the plugin can't handle
                Domoticz.Error(
                    "More than 1 device found in cloud account but no device name given to select. Select and filter one from available options: "
                    + str(list(deviceList)))
                return
            #the Domoticz connection object takes username and pwd from the Parameters so write them back
            Parameters[
                'Username'] = self.myDevice.serial  #take username from account
            Parameters[
                'Password'] = self.myDevice.password  #override the default password with the one returned from the cloud
        else:
            Domoticz.Error("No usable credentials found")
            return

        #check, per device, if it is created. If not,create it
        Options = {
            "LevelActions": "|||",
            "LevelNames": "|OFF|ON|AUTO",
            "LevelOffHidden": "true",
            "SelectorStyle": "1"
        }
        if self.fanModeUnit not in Devices:
            Domoticz.Device(Name='Fan mode',
                            Unit=self.fanModeUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()
        if self.fanStateUnit not in Devices:
            Domoticz.Device(Name='Fan state',
                            Unit=self.fanStateUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.heatStateUnit not in Devices:
            Domoticz.Device(Name='Heating state',
                            Unit=self.heatStateUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.nightModeUnit not in Devices:
            Domoticz.Device(Name='Night mode',
                            Unit=self.nightModeUnit,
                            Type=244,
                            Subtype=62,
                            Switchtype=0,
                            Image=9).Create()

        Options = {
            "LevelActions": "|||||||||||",
            "LevelNames": "OFF|1|2|3|4|5|6|7|8|9|10|AUTO",
            "LevelOffHidden": "false",
            "SelectorStyle": "1"
        }
        if self.fanSpeedUnit not in Devices:
            Domoticz.Device(Name='Fan speed',
                            Unit=self.fanSpeedUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()

        if self.fanOscillationUnit not in Devices:
            Domoticz.Device(Name='Oscilation mode',
                            Unit=self.fanOscillationUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.standbyMonitoringUnit not in Devices:
            Domoticz.Device(Name='Standby monitor',
                            Unit=self.standbyMonitoringUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.filterLifeUnit not in Devices:
            Options: {'Custom': '1;hrs'}
            Domoticz.Device(Name='Remaining filter life',
                            Unit=self.filterLifeUnit,
                            TypeName="Custom",
                            Options=Options).Create()
        if self.resetFilterLifeUnit not in Devices:
            Domoticz.Device(Name='Reset filter: 0 hrs',
                            Unit=self.resetFilterLifeUnit,
                            TypeName="Switch",
                            Image=9).Create()
        if self.tempHumUnit not in Devices:
            Domoticz.Device(Name='Temperature and Humidity',
                            Unit=self.tempHumUnit,
                            TypeName="Temp+Hum").Create()
        if self.volatileUnit not in Devices:
            Domoticz.Device(Name='Volatile organic',
                            Unit=self.volatileUnit,
                            TypeName="Air Quality").Create()
        if self.sleepTimeUnit not in Devices:
            Domoticz.Device(Name='Sleep timer',
                            Unit=self.sleepTimeUnit,
                            TypeName="Custom").Create()

        if self.particlesUnit not in Devices:
            Domoticz.Device(Name='Dust',
                            Unit=self.particlesUnit,
                            TypeName="Air Quality").Create()
        if self.qualityTargetUnit not in Devices:
            Options = {
                "LevelActions": "|||",
                "LevelNames":
                "|Normal|Sensitive (Medium)|Very Sensitive (High)|Off",
                "LevelOffHidden": "true",
                "SelectorStyle": "1"
            }
            Domoticz.Device(Name='Air quality setpoint',
                            Unit=self.qualityTargetUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()

        if self.particles2_5Unit not in Devices:
            Domoticz.Device(Name='Dust (PM 2,5)',
                            Unit=self.particles2_5Unit,
                            TypeName="Air Quality").Create()
        if self.particles10Unit not in Devices:
            Domoticz.Device(Name='Dust (PM 10)',
                            Unit=self.particles10Unit,
                            TypeName="Air Quality").Create()
        if self.particlesMatter25Unit not in Devices:
            Domoticz.Device(Name='Particles (PM 25)',
                            Unit=self.particlesMatter25Unit,
                            TypeName="Air Quality").Create()
        if self.particlesMatter10Unit not in Devices:
            Domoticz.Device(Name='Particles (PM 10)',
                            Unit=self.particlesMatter10Unit,
                            TypeName="Air Quality").Create()
        if self.fanModeAutoUnit not in Devices:
            Domoticz.Device(Name='Fan mode auto',
                            Unit=self.fanModeAutoUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.fanFocusUnit not in Devices:
            Domoticz.Device(Name='Fan focus mode',
                            Unit=self.fanFocusUnit,
                            Type=244,
                            Subtype=62,
                            Image=7,
                            Switchtype=0).Create()
        if self.nitrogenDioxideDensityUnit not in Devices:
            Domoticz.Device(Name='Nitrogen Dioxide Density (NOx)',
                            Unit=self.nitrogenDioxideDensityUnit,
                            TypeName="Air Quality").Create()
        if self.heatModeUnit not in Devices:
            Options = {
                "LevelActions": "||",
                "LevelNames": "|Off|Heating",
                "LevelOffHidden": "true",
                "SelectorStyle": "1"
            }
            Domoticz.Device(Name='Heat mode',
                            Unit=self.heatModeUnit,
                            TypeName="Selector Switch",
                            Image=7,
                            Options=Options).Create()
        if self.heatTargetUnit not in Devices:
            Domoticz.Device(Name='Heat target',
                            Unit=self.heatTargetUnit,
                            Type=242,
                            Subtype=1).Create()
        if self.deviceStatusUnit not in Devices:
            Domoticz.Device(Name='Machine status',
                            Unit=self.deviceStatusUnit,
                            TypeName="Text",
                            Image=7).Create()

        Domoticz.Log("Device instance created: " + str(self.myDevice))
        self.base_topic = self.myDevice.device_base_topic
        Domoticz.Debug("base topic defined: '" + self.base_topic + "'")

        #create the connection
        if self.myDevice != None:
            self.mqttClient = MqttClient(self.ip_address, self.port_number,
                                         mqtt_client_id, self.onMQTTConnected,
                                         self.onMQTTDisconnected,
                                         self.onMQTTPublish,
                                         self.onMQTTSubscribed)

    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level, Hue):
        Domoticz.Debug("DysonPureLink plugin: onCommand called for Unit " +
                       str(Unit) + ": Parameter '" + str(Command) +
                       "', Level: " + str(Level))
        topic = ''
        payload = ''
        arg = ''
        fan_pwr_list = ['438', '520', '527']

        if Unit == self.qualityTargetUnit and Level <= 100:
            topic, payload = self.myDevice.set_quality_target(Level)
        if Unit == self.fanSpeedUnit and Level <= 100:
            arg = "0000" + str(Level // 10)
            topic, payload = self.myDevice.set_fan_speed(
                arg[-4:])  #use last 4 characters as speed level or AUTO
            self.mqttClient.Publish(topic, payload)
            if Level > 0:
                #when setting a speed value, make sure that the fan is actually on
                if self.myDevice.product_type in fan_pwr_list:
                    topic, payload = self.myDevice.set_fan_power("ON")
                else:
                    topic, payload = self.myDevice.set_fan_mode("FAN")
            else:
                if self.myDevice.product_type in fan_pwr_list:
                    topic, payload = self.myDevice.set_fan_power("OFF")
                else:
                    topic, payload = self.myDevice.set_fan_mode(
                        "OFF")  #use last 4 characters as speed level or AUTO
        if Unit == self.fanModeUnit or (Unit == self.fanSpeedUnit
                                        and Level > 100):
            if self.myDevice.product_type in fan_pwr_list:
                if Level >= 30:
                    arg = "ON"
                    #Switch to Auto
                    topic, payload = self.myDevice.set_fan_power(arg)
                    self.mqttClient.Publish(topic, payload)
                    topic, payload = self.myDevice.set_fan_mode_auto(arg)
                elif Level == 20:
                    arg = "ON"
                    #Switch on, auto depends on previous setting
                    topic, payload = self.myDevice.set_fan_power(arg)
                else:
                    #Switch Off
                    arg = 'OFF'
                    topic, payload = self.myDevice.set_fan_power(arg)
            else:
                if Level == 10: arg = "OFF"
                if Level == 20: arg = "FAN"
                if Level >= 30: arg = "AUTO"
                topic, payload = self.myDevice.set_fan_mode(arg)
        if Unit == self.fanStateUnit:
            Domoticz.Log("Unit Fans State is read only, no command sent")
        if Unit == self.fanOscillationUnit:
            topic, payload = self.myDevice.set_oscilation(str(Command).upper())
        if Unit == self.fanFocusUnit:
            topic, payload = self.myDevice.set_focus(str(Command).upper())
        if Unit == self.fanModeAutoUnit:
            topic, payload = self.myDevice.set_fan_mode_auto(
                str(Command).upper())
        if Unit == self.standbyMonitoringUnit:
            topic, payload = self.myDevice.set_standby_monitoring(
                str(Command).upper())
        if Unit == self.nightModeUnit:
            topic, payload = self.myDevice.set_night_mode(str(Command).upper())
        if Unit == self.heatModeUnit:
            if Level == 10: arg = "OFF"
            if Level == 20: arg = "HEAT"
            topic, payload = self.myDevice.set_heat_mode(arg)
        if Unit == self.heatTargetUnit:
            topic, payload = self.myDevice.set_heat_target(Level)
        if Unit == self.resetFilterLifeUnit:
            UpdateDevice(self.resetFilterLifeUnit, 1,
                         "On")  #acknowlegde switching on
            topic, payload = self.myDevice.reset_filter()

        self.mqttClient.Publish(topic, payload)

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called: Connection '" + str(Connection) +
                       "', Status: '" + str(Status) + "', Description: '" +
                       Description + "'")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onNotification(self, Name, Subject, Text, Status, Priority, Sound,
                       ImageFile):
        Domoticz.Log("DysonPureLink plugin: onNotification: " + Name + "," +
                     Subject + "," + Text + "," + Status + "," +
                     str(Priority) + "," + Sound + "," + ImageFile)

    def onHeartbeat(self):
        if self.myDevice != None:
            self.runCounter = self.runCounter - 1
            if self.runCounter <= 0:
                Domoticz.Debug("DysonPureLink plugin: Poll unit")
                self.runCounter = int(Parameters['Mode2'])
                topic, payload = self.myDevice.request_state()
                self.mqttClient.Publish(
                    topic, payload)  #ask for update of current status

            else:
                Domoticz.Debug("Polling unit in " + str(self.runCounter) +
                               " heartbeats.")
                self.mqttClient.onHeartbeat()

    def onDeviceRemoved(self, unit):
        Domoticz.Log(
            "DysonPureLink plugin: onDeviceRemoved called for unit '" +
            str(unit) + "'")

    def updateDevices(self):
        """Update the defined devices from incoming mesage info"""
        #update the devices
        if self.state_data.oscillation is not None:
            UpdateDevice(self.fanOscillationUnit,
                         self.state_data.oscillation.state,
                         str(self.state_data.oscillation))
        if self.state_data.night_mode is not None:
            UpdateDevice(self.nightModeUnit, self.state_data.night_mode.state,
                         str(self.state_data.night_mode))

        # Fan speed
        if self.state_data.fan_speed is not None:
            f_rate = self.state_data.fan_speed

            if (f_rate == "AUTO"):
                nValueNew = 110
                sValueNew = "110"  # Auto
            else:
                nValueNew = (int(f_rate)) * 10
                sValueNew = str((int(f_rate)) * 10)
            if self.state_data.fan_mode is not None:
                Domoticz.Debug("update fanspeed, state of FanMode: " +
                               str(self.state_data.fan_mode))
                if self.state_data.fan_mode.state == 0:
                    nValueNew = 0
                    sValueNew = "0"

            UpdateDevice(self.fanSpeedUnit, nValueNew, sValueNew)

        if self.state_data.fan_mode is not None:
            UpdateDevice(self.fanModeUnit, self.state_data.fan_mode.state,
                         str((self.state_data.fan_mode.state + 1) * 10))
        if self.state_data.fan_state is not None:
            UpdateDevice(self.fanStateUnit, self.state_data.fan_state.state,
                         str((self.state_data.fan_state.state + 1) * 10))
        if self.state_data.filter_life is not None:
            UpdateDevice(self.filterLifeUnit, self.state_data.filter_life,
                         str(self.state_data.filter_life))
        if self.state_data.filter_life is not None:
            nameBase, curValue = Devices[self.resetFilterLifeUnit].Name.split(
                ":")
            UpdateDevice(self.resetFilterLifeUnit,
                         0,
                         "Off",
                         Name=nameBase + ": " +
                         str(self.state_data.filter_life) + " hrs")
        if self.state_data.quality_target is not None:
            UpdateDevice(self.qualityTargetUnit,
                         self.state_data.quality_target.state,
                         str((self.state_data.quality_target.state + 1) * 10))
        if self.state_data.standby_monitoring is not None:
            UpdateDevice(
                self.standbyMonitoringUnit,
                self.state_data.standby_monitoring.state,
                str((self.state_data.standby_monitoring.state + 1) * 10))
        if self.state_data.fan_mode_auto is not None:
            UpdateDevice(self.fanModeAutoUnit,
                         self.state_data.fan_mode_auto.state,
                         str((self.state_data.fan_mode_auto.state + 1) * 10))
        if self.state_data.focus is not None:
            UpdateDevice(self.fanFocusUnit, self.state_data.focus.state,
                         str(self.state_data.focus))
        if self.state_data.heat_mode is not None:
            UpdateDevice(self.heatModeUnit, self.state_data.heat_mode.state,
                         str((self.state_data.heat_mode.state + 1) * 10))
        if self.state_data.heat_target is not None:
            UpdateDevice(self.heatTargetUnit, 0,
                         str(self.state_data.heat_target))
        if self.state_data.heat_state is not None:
            UpdateDevice(self.heatStateUnit, self.state_data.heat_state.state,
                         str((self.state_data.heat_state.state + 1) * 10))
        if self.state_data.error is not None and self.state_data.warning is not None:
            status_string = "Error: {}<br> Warning: {}".format(
                self.state_data.error, self.state_data.warning)
            UpdateDevice(self.deviceStatusUnit, 0, status_string)
        Domoticz.Debug("update StateData: " + str(self.state_data))

    def updateSensors(self):
        """Update the defined devices from incoming mesage info"""
        #update the devices
        if self.sensor_data.temperature is not None and self.sensor_data.humidity is not None:
            tempNum = int(self.sensor_data.temperature)
            humNum = int(self.sensor_data.humidity)
            UpdateDevice(
                self.tempHumUnit, 1,
                str(self.sensor_data.temperature)[:4] + ';' +
                str(self.sensor_data.humidity) + ";1")
        if self.sensor_data.volatile_compounds is not None:
            UpdateDevice(self.volatileUnit,
                         self.sensor_data.volatile_compounds,
                         str(self.sensor_data.volatile_compounds))
        if self.sensor_data.particles is not None:
            UpdateDevice(self.particlesUnit, self.sensor_data.particles,
                         str(self.sensor_data.particles))
        if self.sensor_data.particles2_5 is not None:
            UpdateDevice(self.particles2_5Unit, self.sensor_data.particles2_5,
                         str(self.sensor_data.particles2_5))
        if self.sensor_data.particles10 is not None:
            UpdateDevice(self.particles10Unit, self.sensor_data.particles10,
                         str(self.sensor_data.particles10))
        if self.sensor_data.particulate_matter_25 is not None:
            UpdateDevice(self.particlesMatter25Unit,
                         self.sensor_data.particulate_matter_25,
                         str(self.sensor_data.particulate_matter_25))
        if self.sensor_data.particulate_matter_10 is not None:
            UpdateDevice(self.particlesMatter10Unit,
                         self.sensor_data.particulate_matter_10,
                         str(self.sensor_data.particulate_matter_10))
        if self.sensor_data.nitrogenDioxideDensity is not None:
            UpdateDevice(self.nitrogenDioxideDensityUnit,
                         self.sensor_data.nitrogenDioxideDensity,
                         str(self.sensor_data.nitrogenDioxideDensity))
        if self.sensor_data.heat_target is not None:
            UpdateDevice(self.heatTargetUnit, self.sensor_data.heat_target,
                         str(self.sensor_data.heat_target))
        UpdateDevice(self.sleepTimeUnit, self.sensor_data.sleep_timer,
                     str(self.sensor_data.sleep_timer))
        if self.sensor_data.has_data:
            Domoticz.Debug("update SensorData: " + str(self.sensor_data))
        else:
            Domoticz.Debug("partial SensorData to update")

    def onMQTTConnected(self):
        """connection to device established"""
        Domoticz.Debug("onMQTTConnected called")
        Domoticz.Log("MQTT connection established")
        self.mqttClient.Subscribe([
            self.base_topic + '/status/current',
            self.base_topic + '/status/connection',
            self.base_topic + '/status/faults'
        ])  #subscribe to all topics on the machine
        topic, payload = self.myDevice.request_state()
        self.mqttClient.Publish(topic,
                                payload)  #ask for update of current status

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT Publish: MQTT message incoming: " + topic + " " +
                       str(message))

        if (topic == self.base_topic + '/status/current'):
            #update of the machine's status
            if StateData.is_state_data(message):
                Domoticz.Debug("machine state or state change recieved")
                self.state_data = StateData(message)
                self.updateDevices()
            if SensorsData.is_sensors_data(message):
                Domoticz.Debug("sensor state recieved")
                self.sensor_data = SensorsData(message)
                self.updateSensors()

        if (topic == self.base_topic + '/status/connection'):
            #connection status received
            Domoticz.Debug("connection state recieved")

        if (topic == self.base_topic + '/status/software'):
            #connection status received
            Domoticz.Debug("software state recieved")

        if (topic == self.base_topic + '/status/summary'):
            #connection status received
            Domoticz.Debug("summary state recieved")

    def checkVersion(self, version):
        """checks actual version against stored version as 'Ma.Mi.Pa' and checks if updates needed"""
        #read version from stored configuration
        ConfVersion = getConfigItem("plugin version", "0.0.0")
        Domoticz.Log("Starting version: " + version)
        MaCurrent, MiCurrent, PaCurrent = version.split('.')
        MaConf, MiConf, PaConf = ConfVersion.split('.')
        Domoticz.Debug("checking versions: current '{0}', config '{1}'".format(
            version, ConfVersion))
        if int(MaConf) < int(MaCurrent):
            Domoticz.Log("Major version upgrade: {0} -> {1}".format(
                MaConf, MaCurrent))
            #add code to perform MAJOR upgrades
        elif int(MiConf) < int(MiCurrent):
            Domoticz.Log("Minor version upgrade: {0} -> {1}".format(
                MiConf, MiCurrent))
            #add code to perform MINOR upgrades
        elif int(PaConf) < int(PaCurrent):
            Domoticz.Log("Patch version upgrade: {0} -> {1}".format(
                PaConf, PaCurrent))
            #add code to perform PATCH upgrades, if any
        if ConfVersion != version:
            #store new version info
            self._setVersion(MaCurrent, MiCurrent, PaCurrent)

    def get_device_names(self):
        """find the amount of stored devices"""
        Configurations = getConfigItem()
        devices = {}
        for x in Configurations:
            if x.find(".") > -1 and x.split(".")[1] == "name":
                devices[str(Configurations[x])] = str(Configurations[x])
        return devices

    def get_device_config(self, name):
        """fetch all relevant config items from Domoticz.Configuration for device with name"""
        Configurations = getConfigItem()
        for x in Configurations:
            if x.split(".")[1] == "name":
                Domoticz.Debug("Found a machine name: " + x + " value: '" +
                               str(Configurations[x]) + "'")
                if Configurations[x] == name:
                    password = getConfigItem(
                        Key="{0}.{1}".format(name, "credential"))
                    serialNumber = getConfigItem(
                        Key="{0}.{1}".format(name, "serial"))
                    deviceType = getConfigItem(
                        Key="{0}.{1}".format(name, "product_type"))
                    return password, serialNumber, deviceType
        return

    def _setVersion(self, major, minor, patch):
        #set configs
        Domoticz.Debug("Setting version to {0}.{1}.{2}".format(
            major, minor, patch))
        setConfigItem(Key="MajorVersion", Value=major)
        setConfigItem(Key="MinorVersion", Value=minor)
        setConfigItem(Key="patchVersion", Value=patch)
        setConfigItem(Key="plugin version",
                      Value="{0}.{1}.{2}".format(major, minor, patch))

    def _storeCredentials(self, creds, auths):
        #store credentials as config item
        Domoticz.Debug("Storing credentials: " + str(creds) +
                       " and auth object: " + str(auths))
        currentCreds = getConfigItem(Key="credentials", Default=None)
        if currentCreds is None or currentCreds != creds:
            Domoticz.Log(
                "Credentials from user authentication do not match those stored in config, updating config"
            )
            setConfigItem(Key="credentials", Value=creds)
        return True
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        Domoticz.Debug("onStart called")
        self.base_topic = Parameters["Mode1"].strip()
        self.pairing_enabled = True if Parameters["Mode2"] == 'true' else False
        self.subscribed_for_devices = False

        mqtt_server_address = Parameters["Address"].strip()
        mqtt_server_port = Parameters["Port"].strip()
        mqtt_client_id = Parameters["Mode3"].strip()
        self.mqttClient = MqttClient(mqtt_server_address, mqtt_server_port,
                                     mqtt_client_id, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

        self.devices_manager = DevicesManager()
        self.groups_manager = GroupsManager()

    def checkDevices(self):
        Domoticz.Debug("checkDevices called")

    def onStop(self):
        Domoticz.Debug("onStop called")

    def handlePairingMode(self):
        permit_join = 'true' if self.pairing_enabled else 'false'
        self.mqttClient.publish(self.base_topic + '/bridge/config/permit_join',
                                permit_join)

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("onCommand: " + Command + ", level (" + str(Level) +
                       ") Color:" + Color)

        device = Devices[
            Unit]  #Devices is Domoticz collection of devices for this hardware
        device_params = device.DeviceID.split('_')
        entity_id = device_params[0]

        if (self.devices_manager.get_device_by_id(entity_id) != None):
            message = self.devices_manager.handle_command(
                Devices, device, Command, Level, Color)
        elif (self.groups_manager.get_group_by_deviceid(device.DeviceID) !=
              None):
            message = self.groups_manager.handle_command(
                device, Command, Level, Color)
        else:
            Domoticz.Log('Can\'t process command from device "' + device.Name +
                         '"')

        if (message != None):
            self.mqttClient.publish(self.base_topic + '/' + message['topic'],
                                    message['payload'])

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        self.mqttClient.onHeartbeat()

    def onMQTTConnected(self):
        self.mqttClient.subscribe([self.base_topic + '/bridge/#'])

    def onMQTTDisconnected(self):
        self.subscribed_for_devices = False

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT message: " + topic + " " + str(message))

        if (topic == self.base_topic + '/bridge/config/permit_join'
                or topic == self.base_topic + '/bridge/config/devices'):
            return

        if (topic == self.base_topic + '/bridge/config'):
            permit_join = 'enabled' if message['permit_join'] else 'disabled'
            Domoticz.Debug('Zigbee2mqtt log level is ' + message['log_level'])
            Domoticz.Log('Joining new devices is ' + permit_join +
                         ' on the zigbee bridge')
            return

        if (topic == self.base_topic + '/bridge/state'):
            Domoticz.Log('Zigbee2mqtt bridge is ' + message)

            if message == 'online':
                self.mqttClient.publish(
                    self.base_topic + '/bridge/config/devices', '')
                self.mqttClient.publish(
                    self.base_topic + '/bridge/config/groups', '')
                self.handlePairingMode()

            return

        if (topic == self.base_topic + '/bridge/log'):
            if message['type'] == 'devices':
                Domoticz.Log('Received available devices list from bridge')

                self.devices_manager.clear()
                self.devices_manager.update(Devices, message['message'])

                if self.subscribed_for_devices == False:
                    self.mqttClient.subscribe([self.base_topic + '/+'])
                    self.subscribed_for_devices = True

            if message['type'] == 'groups':
                Domoticz.Log('Received groups list from bridge')
                self.groups_manager.register_groups(Devices,
                                                    message['message'])

            if message['type'] == 'device_connected' or message[
                    'type'] == 'device_removed':
                self.mqttClient.publish(
                    self.base_topic + '/bridge/config/devices', '')

            return

        entity_name = topic.replace(self.base_topic + "/", "")

        if (self.devices_manager.get_device_by_name(entity_name) != None):
            self.devices_manager.handle_mqtt_message(Devices, entity_name,
                                                     message)
        elif (self.groups_manager.get_group_by_name(entity_name) != None):
            self.groups_manager.handle_mqtt_message(entity_name, message)
        else:
            Domoticz.Debug('Unhandled message from zigbee2mqtt: ' + topic +
                           ' ' + str(message))
class BasePlugin:
    mqttClient = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose+":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2 + 4 + 8)

        self.controller = EmsDevices()

        self.controller.checkDevices()

        self.topics = list([
            "home/ems-esp/thermostat_data", "home/ems-esp/boiler_data",
            "home/ems-esp/STATE"
        ])
        self.mqttserveraddress = Parameters["Address"].replace(" ", "")
        self.mqttserverport = Parameters["Port"].replace(" ", "")
        self.mqttClient = MqttClient(self.mqttserveraddress,
                                     self.mqttserverport, self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish, self.onMQTTSubscribed)

    def checkDevices(self):
        Domoticz.Log("checkDevices called")

    def onStop(self):
        Domoticz.Log("onStop called")

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("Command: " + Command + " (" + str(Level))
        self.controller.onCommand(self.mqttClient, Unit, Command, Level, Color)

    def onConnect(self, Connection, Status, Description):
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        Domoticz.Debug("Heartbeating...")

        # Reconnect if connection has dropped
        if self.mqttClient.mqttConn is None or (
                not self.mqttClient.mqttConn.Connecting()
                and not self.mqttClient.mqttConn.Connected()
                or not self.mqttClient.isConnected):
            Domoticz.Debug("Reconnecting")
            self.mqttClient.Open()
        else:
            self.mqttClient.Ping()

    def onMQTTConnected(self):
        Domoticz.Debug("onMQTTConnected")
        self.mqttClient.Subscribe(self.topics)

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, rawmessage):
        Domoticz.Debug("MQTT message: " + topic + " " + str(rawmessage))

        message = ""
        try:
            message = json.loads(rawmessage.decode('utf8'))
        except ValueError:
            message = rawmessage.decode('utf8')

        if (topic in self.topics):
            self.controller.onMqttMessage(topic, message)
Example #17
0
class BasePlugin:
    mqttClient = None
    domoticzDevicesByName = None
    domoticzDevicesById = None
    linkedDevices = None

    def onStart(self):
        self.debugging = Parameters["Mode6"]

        if self.debugging == "Verbose":
            Domoticz.Debugging(2 + 4 + 8 + 16 + 64)
        if self.debugging == "Debug":
            Domoticz.Debugging(2)

        Domoticz.Debug("onStart called")

        self.base_topic = Parameters["Mode1"].strip()
        self.domoticz_port = int(Parameters["Port"].strip())
        self.delete_removed_devices = Parameters["Mode5"].strip()
        if Parameters["Address"].find('mqtt.gbridge.io') >= 0:
            self.domoticz_mqtt_used = False
        else:
            self.domoticz_mqtt_used = True

        self.mqttClient = MqttClient(Parameters["Address"].strip().split(":")[0],
                                     Parameters["Address"].strip().split(":")[1],
                                     self.onMQTTConnected,
                                     self.onMQTTDisconnected,
                                     self.onMQTTPublish,
                                     self.onMQTTSubscribed)
        self.gBridgeClient = gBridgeClient(Parameters["Mode2"].strip(),
                                           Parameters["Mode3"].strip(),
                                           Parameters["Mode4"].strip())
        self.domoticz_client = DomoticzClient(self.domoticz_port)

        self.syncDevices()

        Domoticz.Debug("Done")

    def syncDevices(self):
        Domoticz.Debug('Starting sync')
        bridge_devices = self.gBridgeClient.fetchDevicesFromBridge()
        domoticz_devices = self.domoticz_client.fetchDevicesFromDomoticz()
        self.linkedDevices = self.domoticz_client.getLinkedDevices(domoticz_devices)
        Domoticz.Debug('Linked devices: ' + str(self.linkedDevices))
        self.domoticzDevicesByName = self.domoticz_client.getDevicesByName(domoticz_devices)
        self.domoticzDevicesById = {x['idx']: x for x in list(self.domoticzDevicesByName.values())}
        Domoticz.Debug('Domoticz devices available for gBridge: ' + str(self.domoticzDevicesByName.keys()))
        self.gBridgeClient.syncDevices(self.domoticzDevicesByName, bridge_devices,
                                       self.delete_removed_devices == 'True')
    def onStop(self):
        Domoticz.Debug("onStop called")

    def onCommand(self, Unit, Command, Level, Color):
        Domoticz.Debug("onCommand: " + Command + ", level (" + str(Level) + ") Color:" + Color)

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")
        self.mqttClient.onConnect(Connection, Status, Description)

    def onDisconnect(self, Connection):
        self.mqttClient.onDisconnect(Connection)

    def onMessage(self, Connection, Data):
        Domoticz.Debug('Incoming message!' + str(Data))
        self.mqttClient.onMessage(Connection, Data)

    def onHeartbeat(self):
        Domoticz.Debug("Heartbeating...")

        # Reconnect if connection has dropped
        if self.mqttClient.mqttConn is None or (
                not self.mqttClient.mqttConn.Connecting() and not self.mqttClient.mqttConn.Connected() or not self.mqttClient.isConnected):
            Domoticz.Debug("Reconnecting")
            self.mqttClient.Open()
        else:
            self.mqttClient.Ping()

    def onMQTTConnected(self):
        self.mqttClient.Subscribe([self.base_topic + '/#'])
        if self.domoticz_mqtt_used:
            self.mqttClient.Subscribe(['domoticz/out'])

    def onMQTTDisconnected(self):
        Domoticz.Debug("onMQTTDisconnected")

    def onMQTTSubscribed(self):
        Domoticz.Debug("onMQTTSubscribed")

    def onMQTTPublish(self, topic, message):
        Domoticz.Debug("MQTT message: " + topic + " " + str(message))
        if str(topic) == 'domoticz/out':
            if message.get('idx') is not None:
                domoticz_id = str(message['idx'])
                if message.get('Type') is not None and (message['Type'] == 'Scene' or message['Type'] == 'Group'):
                    domoticz_id = 'group_' + domoticz_id
            else:
                return
            device = None
            if domoticz_id in self.domoticzDevicesById:
                device = self.domoticzDevicesById[domoticz_id]
            elif domoticz_id in self.linkedDevices and self.linkedDevices[domoticz_id] in self.domoticzDevicesById:
                # Get the device to which this device message is linked to, for example, device id 13 -> update device 12
                device = self.domoticzDevicesById[self.linkedDevices[domoticz_id]]
            if device is not None:
                adapter = getAdapter(device)
                if adapter is not None:
                    adapter.publishStateFromDomoticzTopic(self.mqttClient, device, self.base_topic, message)
                else:
                    Domoticz.Error('No adapter registered to publish state for device: %s' % str(device))

        else:
            if message == 'SYNC':
                self.syncDevices()
            elif topic.endswith('/set'):
                Domoticz.Debug('Published new state for device, topic: %s, state: %s' % (topic, message))
            else:
                match = re.search(self.base_topic + '/(.*)/(.*)', topic)

                if match:
                    device_id = match.group(1)
                    # Backwards compatibility, previously the device name was used as topic name
                    if device_id in self.domoticzDevicesByName:
                        device = self.domoticzDevicesByName[device_id]
                    elif device_id in self.domoticzDevicesById:
                        device = self.domoticzDevicesById[device_id]
                    else:
                        Domoticz.Log('Received message for device which is not in Domoticz: %s, skipping' % device_id)
                        return
                    action = match.group(2)
                    adapter = getAdapter(device)
                    if adapter is not None:
                        adapter.handleMqttMessage(device, str(message), action, self.domoticz_port)
                        if not self.domoticz_mqtt_used:
                            adapter.publishState(self.mqttClient, device, self.base_topic, message) # answer directly
                    else:
                        Domoticz.Error('No adapter registered for action: %s for device: %s' % (action, str(device)))