diff --git a/Classes/LoggingManagement.py b/Classes/LoggingManagement.py index f54145be1..830ce1316 100644 --- a/Classes/LoggingManagement.py +++ b/Classes/LoggingManagement.py @@ -16,6 +16,7 @@ import os.path import threading import time +import traceback from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler from pathlib import Path from queue import PriorityQueue, Queue @@ -43,15 +44,19 @@ def __init__(self, pluginconf, PluginHealth, HardwareID, ListOfDevices, permitTo self._startTime = int(time.time()) self.debugzigpy = False - zigpy_loging_mode("warning") self.debugZNP = False - zigpy_logging_znp("warning") self.debugEZSP = False - zigpy_logging_ezsp("warning") self.debugZigate = False - zigpy_logging_zigate("warning") self.debugdeconz = False - zigpy_logging_deconz("warning") + + configure_zigpy_loggers("warning") + configure_zigpy_znp_loggers("warning") + configure_zigpy_ezsp_loggers("warning") + configure_zigpy_zigate_loggers("warning") + configure_zigpy_deconz_loggers("warning") + + + self.zigpy_login() @@ -73,6 +78,13 @@ def reset_new_error(self): def is_new_error(self): return bool(self._newError and bool(self.LogErrorHistory)) + def zigpy_login(self): + _configure_debug_mode(self, self.debugzigpy, "Zigpy", configure_zigpy_loggers) + _configure_debug_mode(self, self.debugZNP, "ZigpyZNP", configure_zigpy_znp_loggers) + _configure_debug_mode(self, self.debugEZSP, "ZigpyEZSP", configure_zigpy_ezsp_loggers) + _configure_debug_mode(self, self.debugZigate, "ZigpyZigate", configure_zigpy_zigate_loggers) + _configure_debug_mode(self, self.debugdeconz, "ZigpydeCONZ", configure_zigpy_deconz_loggers) + def loggingUpdatePluginVersion(self, Version): self.PluginVersion = Version if ( @@ -97,45 +109,6 @@ def loggingUpdateFirmware(self, FirmwareVersion, FirmwareMajorVersion): self.LogErrorHistory[str(self.LogErrorHistory["LastLog"])]["FirmwareVersion"] = FirmwareVersion self.LogErrorHistory[str(self.LogErrorHistory["LastLog"])]["FirmwareMajorVersion"] = FirmwareMajorVersion - def zigpy_login(self): - if not self.debugzigpy and self.pluginconf.pluginConf["Zigpy"]: - self.debugzigpy = True - zigpy_loging_mode("debug") - elif self.debugzigpy and not self.pluginconf.pluginConf["Zigpy"]: - self.debugzigpy = False - zigpy_loging_mode("warning") - - # Debug ZNP - if not self.debugZNP and self.pluginconf.pluginConf["ZigpyZNP"]: - self.debugZNP = True - zigpy_logging_znp("debug") - elif self.debugZNP and not self.pluginconf.pluginConf["ZigpyZNP"]: - self.debugZNP = False - zigpy_logging_znp("warning") - - # Debug Bellows/Ezsp - if not self.debugEZSP and self.pluginconf.pluginConf["ZigpyEZSP"]: - self.debugEZSP = True - zigpy_logging_ezsp("debug") - elif self.debugEZSP and not self.pluginconf.pluginConf["ZigpyEZSP"]: - self.debugEZSP = False - zigpy_logging_ezsp("warning") - - # Debug Zigate - if not self.debugZigate and self.pluginconf.pluginConf["ZigpyZigate"]: - self.debugZigate = True - zigpy_logging_zigate("debug") - elif self.debugZigate and not self.pluginconf.pluginConf["ZigpyZigate"]: - self.debugZigate = False - zigpy_logging_zigate("warning") - - # Debug deConz - if not self.debugdeconz and self.pluginconf.pluginConf["ZigpydeCONZ"]: - self.debugdeconz = True - zigpy_logging_deconz("debug") - elif self.debugdeconz and not self.pluginconf.pluginConf["ZigpydeCONZ"]: - self.debugdeconz = False - zigpy_logging_deconz("warning") def openLogFile(self): @@ -394,31 +367,28 @@ def loggingError(self, thread_name, module, message, nwkid, context): loggingWriteErrorHistory(self) +def loggingBuildContext(self, thread_name, module, message, nwkid, context=None): -def loggingBuildContext(self, thread_name, module, message, nwkid, context): - - if not self.PluginHealth: - _txt = "Not Started" - if "Txt" not in self.PluginHealth: - _txt = "Not Started" - else: - _txt = self.PluginHealth["Txt"] + _txt = self.PluginHealth.get("Txt", "Not Started") + _stacktrace = str(traceback.format_exc()) + _context = { "Time": int(time.time()), - "Module": module, - "nwkid": nwkid, - "PluginHealth": _txt, - "message": message, "PermitToJoin": self.permitTojoin, + "PluginHealth": _txt, "Thread": thread_name, + "nwkid": nwkid, + "Module": module, + "message": message, + "Stack Trace": _stacktrace } - if nwkid and nwkid in self.ListOfDevices: - _context["DeviceInfos"] = dict(self.ListOfDevices[nwkid]) + + if nwkid in self.ListOfDevices: + _context["DeviceInfos"] = dict(self.ListOfDevices.get(nwkid, {})) + if context is not None: - if isinstance(context, dict): - _context["context"] = context.copy() - elif isinstance(context, (str, int)): - _context["context"] = str(context) + _context["context"] = context.copy() if isinstance(context, dict) else str(context) + return _context @@ -501,56 +471,73 @@ def logging_thread(self): Domoticz.Error("logging_thread unexpected tuple %s" % (str(logging_tuple))) Domoticz.Log("logging_thread - ended") - -def zigpy_loging_mode(mode): +def configure_loggers(logger_names, mode): + #Domoticz.Log(f"configure_loggers({logger_names} with {_set_logging_level})") _set_logging_level = logging.DEBUG if mode == "debug" else logging.WARNING - Domoticz.Log( "zigpy_login_mode(%s)" %_set_logging_level) - logging.getLogger("zigpy.application").setLevel(_set_logging_level) - logging.getLogger("zigpy").setLevel(_set_logging_level) - logging.getLogger("zigpy.zdo").setLevel(_set_logging_level) - logging.getLogger("zigpy.zcl").setLevel(_set_logging_level) - logging.getLogger("zigpy.profiles").setLevel(_set_logging_level) - logging.getLogger("zigpy.quirks").setLevel(_set_logging_level) - logging.getLogger("zigpy.ota").setLevel(_set_logging_level) - logging.getLogger("zigpy.appdb_schemas").setLevel(_set_logging_level) - logging.getLogger("zigpy.backups").setLevel(_set_logging_level) - logging.getLogger("zigpy.device").setLevel(_set_logging_level) - logging.getLogger("zigpy.application").setLevel(_set_logging_level) - logging.getLogger("zigpy.appdb").setLevel(_set_logging_level) - logging.getLogger("zigpy.endpoint").setLevel(_set_logging_level) - logging.getLogger("zigpy.group").setLevel(_set_logging_level) - logging.getLogger("zigpy.neighbor").setLevel(_set_logging_level) - logging.getLogger("zigpy.topology").setLevel(_set_logging_level) + for logger_name in logger_names: + logging.getLogger(logger_name).setLevel(_set_logging_level) -def zigpy_logging_znp(mode): - _set_logging_level = logging.DEBUG if mode == "debug" else logging.WARNING - logging.getLogger("zigpy_znp").setLevel(_set_logging_level) - logging.getLogger("zigpy_znp.zigbee").setLevel(_set_logging_level) - logging.getLogger("zigpy_znp.zigbee.application").setLevel(_set_logging_level) - logging.getLogger("zigpy_znp.zigbee.device").setLevel(_set_logging_level) - logging.getLogger("Classes.ZigpyTransport.AppZnp").setLevel(_set_logging_level) - logging.getLogger("Classes.ZigpyTransport.AppGeneric").setLevel(_set_logging_level) - - -def zigpy_logging_ezsp(mode): - _set_logging_level = logging.DEBUG if mode == "debug" else logging.WARNING - logging.getLogger("bellows").setLevel(_set_logging_level) - logging.getLogger("bellows.zigbee").setLevel(_set_logging_level) - logging.getLogger("bellows.zigbee.application").setLevel(_set_logging_level) - logging.getLogger("bellows.zigbee.device").setLevel(_set_logging_level) - logging.getLogger("bellows.uart").setLevel(_set_logging_level) - logging.getLogger("Classes.ZigpyTransport.AppBellows").setLevel(_set_logging_level) - logging.getLogger("Classes.ZigpyTransport.AppGeneric").setLevel(_set_logging_level) - - -def zigpy_logging_zigate(mode): - _set_logging_level = logging.DEBUG if mode == "debug" else logging.WARNING - logging.getLogger("zigpy_zigate").setLevel(_set_logging_level) - logging.getLogger("Classes.ZigpyTransport.AppZigate").setLevel(_set_logging_level) - - -def zigpy_logging_deconz(mode): - _set_logging_level = logging.DEBUG if mode == "debug" else logging.WARNING - logging.getLogger("zigpy_deconz").setLevel(_set_logging_level) - logging.getLogger("Classes.ZigpyTransport.AppDeconz").setLevel(_set_logging_level) + +# Loggers configurations +def configure_zigpy_loggers(mode): + logger_names = [ + "zigpy.application", "zigpy", "zigpy.zdo", "zigpy.zcl", + "zigpy.profiles", "zigpy.quirks", "zigpy.ota", + "zigpy.appdb_schemas", "zigpy.backups", "zigpy.device", + "zigpy.application", "zigpy.appdb", "zigpy.endpoint", + "zigpy.group", "zigpy.neighbor", "zigpy.topology" + ] + configure_loggers(logger_names, mode) + + +def configure_zigpy_znp_loggers(mode): + logger_names = [ + "zigpy_znp", + "zigpy_znp.zigbee", + "zigpy_znp.zigbee.application", + "zigpy_znp.zigbee.device", + "Classes.ZigpyTransport.AppZnp", + "Classes.ZigpyTransport.AppGeneric" + ] + configure_loggers(logger_names, mode) + + +def configure_zigpy_ezsp_loggers(mode): + logger_names = [ + "bellows", + "bellows.zigbee", + "bellows.zigbee.application", + "bellows.zigbee.device", + "bellows.uart", + "Classes.ZigpyTransport.AppBellows", + "Classes.ZigpyTransport.AppGeneric" + ] + configure_loggers(logger_names, mode) + + +def configure_zigpy_zigate_loggers(mode): + logger_names = [ + "zigpy_zigate", + "Classes.ZigpyTransport.AppZigate" + ] + configure_loggers(logger_names, mode) + + +def configure_zigpy_deconz_loggers(mode): + logger_names = [ + "zigpy_deconz", + "Classes.ZigpyTransport.AppDeconz" + ] + configure_loggers(logger_names, mode) + + +# Main configuration function + +def _configure_debug_mode(self, debug_flag, config_name, config_function): + if not debug_flag and self.pluginconf.pluginConf[config_name]: + debug_flag = True + config_function("debug") + elif debug_flag and not self.pluginconf.pluginConf[config_name]: + debug_flag = False + config_function("warning") diff --git a/DevicesModules/custom_zlinky.py b/DevicesModules/custom_zlinky.py index daab5f734..a3abf4485 100644 --- a/DevicesModules/custom_zlinky.py +++ b/DevicesModules/custom_zlinky.py @@ -197,7 +197,7 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att elif attribut == "050b": # Active Power - self.log.logging("Cluster", "Debug", "ReadCluster %s - %s/%s Power %s" % (cluster, nwkid, ep, value)) + self.log.logging("Cluster", "Debug", "zlinky_cluster_electrical_measurement %s - %s/%s Power %s" % (cluster, nwkid, ep, value)) checkAndStoreAttributeValue(self, nwkid, ep, cluster, attribut, value) MajDomoDevice(self, Devices, nwkid, ep, cluster, str(value)) store_ZLinky_infos( self, nwkid, 'CCASN', value) @@ -206,7 +206,7 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att store_ZLinky_infos( self, nwkid, 'CCASN-1',value) elif attribut in ("0505", "0905", "0a05"): # RMS Voltage - self.log.logging("Cluster", "Debug", "ReadCluster %s - %s/%s Voltage %s" % (cluster, nwkid, ep, value)) + self.log.logging("Cluster", "Debug", "zlinky_cluster_electrical_measurement %s - %s/%s Voltage %s" % (cluster, nwkid, ep, value)) if value == 0xFFFF: return checkAndStoreAttributeValue(self, nwkid, ep, cluster, attribut, value) @@ -224,7 +224,7 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att elif attribut == "0508": # RMSCurrent if value == 0xFFFF: return - self.log.logging( "ZLinky", "Debug", "ReadCluster %s - %s/%s %s Current L1 %s" % ( + self.log.logging( "ZLinky", "Debug", "zlinky_cluster_electrical_measurement %s - %s/%s %s Current L1 %s" % ( cluster, nwkid, ep, attribut, value), nwkid, ) # from random import randrange @@ -277,7 +277,7 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att store_ZLinky_infos( self, nwkid, 'SMAXN', value) else: - self.log.logging( "ZLinky", "Error", "=====> ReadCluster %s - %s/%s Unexpected %s/%s linkyMode: %s" % ( + self.log.logging( "ZLinky", "Error", "=====> zlinky_cluster_electrical_measurement %s - %s/%s Unexpected %s/%s linkyMode: %s" % ( cluster, nwkid, ep, attribut, value, _linkyMode ), nwkid, ) return @@ -301,14 +301,14 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att elif attribut in ( "050f", "0306",) : # Apparent Power - 0x0306 is for tri-phased if value >= 0xFFFF: - self.log.logging( "ZLinky", "Error", "=====> ReadCluster %s - %s/%s Apparent Power %s out of range !!!" % (cluster, nwkid, ep, value), nwkid, ) + self.log.logging( "ZLinky", "Error", "=====> zlinky_cluster_electrical_measurement %s - %s/%s Apparent Power %s out of range !!!" % (cluster, nwkid, ep, value), nwkid, ) return checkAndStoreAttributeValue(self, nwkid, ep, cluster, attribut, value) - self.log.logging( "ZLinky", "Debug", "=====> ReadCluster %s - %s/%s Apparent Power %s" % (cluster, nwkid, ep, value), nwkid, ) + self.log.logging( "ZLinky", "Debug", "=====> zlinky_cluster_electrical_measurement %s - %s/%s Apparent Power %s" % (cluster, nwkid, ep, value), nwkid, ) # ApparentPower (Represents the single phase or Phase A, current demand of apparent (Square root of active and reactive power) power, in VA.) - self.log.logging( "ZLinky", "Debug", "=====> ReadCluster %s - %s/%s Apparent Power %s" % (cluster, nwkid, ep, value), nwkid, ) + self.log.logging( "ZLinky", "Debug", "=====> zlinky_cluster_electrical_measurement %s - %s/%s Apparent Power %s" % (cluster, nwkid, ep, value), nwkid, ) _linkyMode = linky_mode( self, nwkid, protocol=True ) @@ -330,7 +330,7 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att store_ZLinky_infos( self, nwkid, 'SINSTS', value) else: - self.log.logging( "ZLinky", "Error", "=====> ReadCluster %s - %s/%s Unexpected %s/%s linkyMode: %s" % ( + self.log.logging( "ZLinky", "Error", "=====> zlinky_cluster_electrical_measurement %s - %s/%s Unexpected %s/%s linkyMode: %s" % ( cluster, nwkid, ep, attribut, value, _linkyMode ), nwkid, ) return @@ -355,7 +355,7 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att else: MajDomoDevice(self, Devices, nwkid, "01", cluster, str(value), Attribute_=attribut) - self.log.logging( "ZLinky", "Debug", "ReadCluster %s - %s/%s Apparent Power %s" % (cluster, nwkid, ep, value), nwkid, ) + self.log.logging( "ZLinky", "Debug", "zlinky_cluster_electrical_measurement %s - %s/%s Apparent Power %s" % (cluster, nwkid, ep, value), nwkid, ) elif attribut in ( "090f", ): store_ZLinky_infos( self, nwkid, 'SINSTS2', value) @@ -372,12 +372,12 @@ def zlinky_cluster_electrical_measurement(self, Devices, nwkid, ep, cluster, att MajDomoDevice(self, Devices, nwkid, ep, cluster, str(value), Attribute_=attribut) # Check if Intensity is below subscription level if attribut == "0908": - self.log.logging("Cluster", "Debug", "ReadCluster %s - %s/%s %s Current L2 %s" % (cluster, nwkid, ep, attribut, value), nwkid) + self.log.logging("Cluster", "Debug", "zlinky_cluster_electrical_measurement %s - %s/%s %s Current L2 %s" % (cluster, nwkid, ep, attribut, value), nwkid) MajDomoDevice( self, Devices, nwkid, "f2", "0009", zlinky_check_alarm(self, Devices, nwkid, ep, value), Attribute_="0005", ) store_ZLinky_infos( self, nwkid, 'IRMS2', value) elif attribut == "0a08": - self.log.logging("Cluster", "Debug", "ReadCluster %s - %s/%s %s Current L3 %s" % (cluster, nwkid, ep, attribut, value), nwkid) + self.log.logging("Cluster", "Debug", "zlinky_cluster_electrical_measurement %s - %s/%s %s Current L3 %s" % (cluster, nwkid, ep, attribut, value), nwkid) MajDomoDevice( self, Devices, nwkid, "f3", "0009", zlinky_check_alarm(self, Devices, nwkid, ep, value), Attribute_="0005", ) store_ZLinky_infos( self, nwkid, 'IRMS3', value) @@ -605,7 +605,7 @@ def zlinky_cluster_lixee_private(self, Devices, nwkid, ep, cluster, attribut, va self.log.logging( "ZLinky", "Log", "STGE Value: %s" % ( value )) stge = value - self.log.logging( "ZLinky", "Log", "STGE decoded %s : %s" % ( stge, decode_STEG( stge ) )) + self.log.logging( "ZLinky", "Log", "STGE decoded %s : %s" % ( stge, decode_STEG( stge ) )) store_ZLinky_infos( self, nwkid, "STGE", decode_STEG( stge )) checkAndStoreAttributeValue(self, nwkid, ep, cluster, attribut, stge) diff --git a/Modules/command.py b/Modules/command.py index f2cfbd0f9..d38834ae8 100644 --- a/Modules/command.py +++ b/Modules/command.py @@ -42,6 +42,7 @@ tuya_lidl_set_mode, tuya_trv_brt100_set_mode, tuya_trv_mode, tuya_trv_onoff, tuya_trv_switch_onoff) +from Modules.danfoss import danfoss_on_off from Modules.tuyaTS0601 import ts0601_actuator, ts0601_extract_data_point_infos from Modules.zigateConsts import (THERMOSTAT_LEVEL_2_MODE, THERMOSTAT_LEVEL_3_MODE, ZIGATE_EP) @@ -504,7 +505,10 @@ def mgtCommand(self, Devices, Unit, Command, Level, Color): elif DeviceType == "ThermoOnOff": self.log.logging("Command", "Debug", "ThermoOnOff - requested Off", NWKID) - tuya_trv_onoff(self, NWKID, 0x00) + if "Model" in self.ListOfDevices[NWKID] and self.ListOfDevices[NWKID]["Model"] in ("eTRV0100"): + danfoss_on_off(self, NWKID, 0x00) + else: + tuya_trv_onoff(self, NWKID, 0x00) UpdateDevice_v2(self, Devices, Unit, 0, "Off", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) elif DeviceType == "ShutterCalibration": @@ -689,7 +693,10 @@ def mgtCommand(self, Devices, Unit, Command, Level, Color): thermostat_Mode(self, NWKID, "Heat") elif DeviceType == "ThermoOnOff": - tuya_trv_onoff(self, NWKID, 0x01) + if "Model" in self.ListOfDevices[NWKID] and self.ListOfDevices[NWKID]["Model"] in ("eTRV0100"): + danfoss_on_off(self, NWKID, 0x01) + else: + tuya_trv_onoff(self, NWKID, 0x01) UpdateDevice_v2(self, Devices, Unit, 1, "On", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) elif DeviceType == "ShutterCalibration": diff --git a/Modules/danfoss.py b/Modules/danfoss.py index 609e2f3fb..84e6d5bd1 100644 --- a/Modules/danfoss.py +++ b/Modules/danfoss.py @@ -387,3 +387,39 @@ def danfoss_viewdirection(self, NwkId, viewdirection): write_attribute(self, NwkId, ZIGATE_EP, EPout, cluster_id, manuf_id, manuf_spec, Hattribute, data_type, Hdata, ackIsDisabled=False) read_attribute(self, NwkId, ZIGATE_EP, EPout, cluster_id, "00", manuf_spec, manuf_id, 1, Hattribute, ackIsDisabled=False) + + +def danfoss_on_off(self, NwkId, on): + # 0 = window closed 1 = window opened + if on == 0: + window_opened = 1 + elif on == 1: + window_opened = 0 + else: + return + + manuf_id = "1246" + manuf_spec = "01" + cluster_id = "%04x" % 0x0201 + + EPout = "01" + for tmpEp in self.ListOfDevices[NwkId]["Ep"]: + if "0201" in self.ListOfDevices[NwkId]["Ep"][tmpEp]: + EPout = tmpEp + + Hattribute = "%04x" % 0x4003 + data_type = "10" # boolean + self.log.logging("Danfoss", "Debug", "danfoss_on_off: %s" % window_opened, nwkid=NwkId) + + Hdata = "%02x" % window_opened + + self.log.logging( + "Danfoss", + "Debug", + "danfoss_on_onff for %s with value %s / cluster: %s, attribute: %s type: %s" % (NwkId, Hdata, cluster_id, Hattribute, data_type), + nwkid=NwkId, + ) + + write_attribute(self, NwkId, ZIGATE_EP, EPout, cluster_id, manuf_id, manuf_spec, Hattribute, data_type, Hdata, ackIsDisabled=False) + read_attribute(self, NwkId, ZIGATE_EP, EPout, cluster_id, "00", manuf_spec, manuf_id, 1, Hattribute, ackIsDisabled=False) + diff --git a/Modules/domoticzAbstractLayer.py b/Modules/domoticzAbstractLayer.py index dfe7eac06..aeee57527 100644 --- a/Modules/domoticzAbstractLayer.py +++ b/Modules/domoticzAbstractLayer.py @@ -8,9 +8,14 @@ Description: Set of functions which abstract Domoticz Legacy and Extended framework API """ +import time + import Domoticz + DOMOTICZ_EXTENDED_API = False +DELAY_BETWEEN_TOUCH = 30 + def load_list_of_domoticz_widget(self, Devices): """Use at plugin start to creat an index of Domoticz Widget. It is also called after a Widget removal and when a new device has been paired. @@ -282,6 +287,7 @@ def domo_update_api(self, Devices, DeviceID_, Unit_, nValue, sValue, SignalLevel else: Devices[Unit_].Update( nValue=int(nValue), sValue=str(sValue), SignalLevel=int(SignalLevel), BatteryLevel=int(BatteryLevel), TimedOut=0, ) + def domo_read_nValue_sValue(self, Devices, DeviceID, Unit): """ Read the nValue and sValue of a device unit. @@ -302,6 +308,7 @@ def domo_read_nValue_sValue(self, Devices, DeviceID, Unit): return _unit.nValue, _unit.sValue + def domo_read_SwitchType_SubType_Type(self, Devices, DeviceID, Unit): if DOMOTICZ_EXTENDED_API: _unit = Devices[DeviceID].Units[Unit] @@ -310,6 +317,7 @@ def domo_read_SwitchType_SubType_Type(self, Devices, DeviceID, Unit): return _unit.SwitchType, _unit.SubType, _unit.Type + def _is_meter_widget( self, Devices,DeviceID_, Unit_): # self.log.logging("AbstractDz", "Debug", "_is_meter_widget: %s %s" %(DeviceID_, Unit_)) if DOMOTICZ_EXTENDED_API: @@ -336,25 +344,33 @@ def _is_device_tobe_switched_off(self, Devices,DeviceID_, Unit_): (Devices[Unit_].Type == 244 and Devices[Unit_].SubType == 73 and Devices[Unit_].SwitchType == 7) or (Devices[Unit_].Type == 241 and Devices[Unit_].SwitchType == 7) ) - - -def device_touch_api( self, Devices, DeviceId_, Unit_): - self.log.logging("AbstractDz", "Debug", "device_touch: %s %s" %(DeviceId_, Unit_)) - - # In case of Meter Device ( kWh ), we must not touch it, otherwise it will destroy the metering + +def device_touch_api(self, Devices, DeviceId_, Unit_): + self.log.logging("AbstractDz", "Debug", f"device_touch: {DeviceId_} {Unit_}") + + # In case of Meter Device (kWh), we must not touch it, otherwise it will destroy the metering # Type, Subtype, SwitchType # 243|29|0 - if _is_meter_widget( self, Devices, DeviceId_, Unit_): + if _is_meter_widget(self, Devices, DeviceId_, Unit_): return - if DOMOTICZ_EXTENDED_API: - Devices[DeviceId_].Units[Unit_].Touch() + + last_time = ( + Devices[DeviceId_].Units[Unit_].LastUpdate + if DOMOTICZ_EXTENDED_API + else Devices[Unit_].LastUpdate + ) + + last_update_time_seconds = time.mktime(time.strptime(last_time, "%Y-%m-%d %H:%M:%S")) + + if time.time() > last_update_time_seconds + DELAY_BETWEEN_TOUCH: + # Last Touch was done more than 30 seconds ago. + Devices[DeviceId_].Units[Unit_].Touch() if DOMOTICZ_EXTENDED_API else Devices[Unit_].Touch() return - # Legacy - Devices[Unit_].Touch() - + self.log.logging("AbstractDz", "Debug", f"device_touch too early: {DeviceId_} {Unit_}") + def timeout_widget_api(self, Devices, DeviceId_, Unit_, timeout_value): if _is_meter_widget( self, Devices, DeviceId_, Unit_): return diff --git a/Modules/input.py b/Modules/input.py index dfc3e3fa2..6e93b5fbc 100644 --- a/Modules/input.py +++ b/Modules/input.py @@ -492,7 +492,8 @@ def Decode0110(self, Devices, MsgData, MsgLQI): # Write Attribute request timeStamped(self, MsgSrcAddr, 0x0110) lastSeenUpdate(self, Devices, NwkId=MsgSrcAddr) - for idx in range(24, len(MsgData), 4): + idx = 24 + while idx < len(MsgData): Attribute = MsgData[idx : idx + 4] idx += 4 DataType = MsgData[idx : idx + 2] @@ -500,6 +501,7 @@ def Decode0110(self, Devices, MsgData, MsgLQI): # Write Attribute request lendata = MsgData[idx : idx + 4] idx += 4 DataValue = MsgData[idx : idx + int(lendata,16) * 2] + idx += int(lendata, 16) * 2 self.log.logging( "Input", "Debug", "Decode0110 - Sqn: %s NwkId: %s Ep: %s Cluster: %s Manuf: %s Attribute: %s Type: %s Value: %s" % ( MsgSqn, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgManufCode, Attribute, DataType, DataValue), ) @@ -4144,7 +4146,7 @@ def Decode8095(self, Devices, MsgData, MsgLQI): MajDomoDevice(self, Devices, MsgSrcAddr, "02", "0006", "01") else: - MajDomoDevice(self, Devices, MsgSrcAddr, MsgEP, "0006", str(int(MsgCmd, 16))) + MajDomoDevice(self, Devices, MsgSrcAddr, MsgEP, "0006", MsgCmd) self.ListOfDevices[MsgSrcAddr]["Ep"][MsgEP][MsgClusterId]["0000"] = "Cmd: %s, %s" % (MsgCmd, unknown_) self.log.logging( "Input", "Log", "Decode8095 - Model: %s SQN: %s, Addr: %s, Ep: %s, Cluster: %s, Cmd: %s, Unknown: %s " % ( _ModelName, MsgSQN, MsgSrcAddr, MsgEP, MsgClusterId, MsgCmd, unknown_), MsgSrcAddr, ) diff --git a/Modules/pluginModels.py b/Modules/pluginModels.py index e454dcaee..d20c92f6e 100644 --- a/Modules/pluginModels.py +++ b/Modules/pluginModels.py @@ -109,7 +109,7 @@ def check_found_plugin_model( self, model, manufacturer_name=None, manufacturer_ '_TZ3000_amdymr7l', '_TZ3000_z1pnpsdo', '_TZ3000_ksw8qtmt', '_TZ3000_nzkqcvvs', '_TZ3000_1h2x4akh', '_TZ3000_9vo5icau', '_TZ3000_cehuw1lw', '_TZ3000_ko6v90pg', '_TZ3000_f1bapcit', '_TZ3000_cjrngdr3', '_TZ3000_zloso4jk', '_TZ3000_r6buo8ba', - '_TZ3000_iksasdbv', '_TZ3000_dd8wwzcy', '_TZ3000_hdopuwv6'], + '_TZ3000_iksasdbv', '_TZ3000_dd8wwzcy', '_TZ3000_hdopuwv6', '_TZ3000_ynmowqk2'], "ManufId": [], "PluginModelName": "TS011F-plug" }, diff --git a/Modules/readClusters.py b/Modules/readClusters.py index 310a27083..d848e99a4 100644 --- a/Modules/readClusters.py +++ b/Modules/readClusters.py @@ -1379,8 +1379,15 @@ def Cluster0201(self, Devices, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAt elif MsgAttrID in ("4000", "4003", "4010", "4011", "4012", "4013", "4014", "4015", "4020", "4030", "4031") and danfoss: checkAndStoreAttributeValue(self, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgClusterData) - if MsgAttrID == "4003" and self.ListOfDevices[MsgSrcAddr]["Model"] in ("eTRV0100", "eT093WRO"): + if MsgAttrID == "4000" and self.ListOfDevices[MsgSrcAddr]["Model"] in ("eTRV0100"): # Open Window Detection for Danfoss eTRV + if value in [ 3, 4 ]: + value = "01" + else: + value = "00" + MajDomoDevice(self, Devices, MsgSrcAddr, MsgSrcEp, "0500", value) + if MsgAttrID == "4003" and self.ListOfDevices[MsgSrcAddr]["Model"] in ("eTRV0100", "eT093WRO"): + # External Open Window Detection for Danfoss eTRV MajDomoDevice(self, Devices, MsgSrcAddr, MsgSrcEp, "0500", value) elif MsgAttrID in ("e010", "e011", "e012", "e013", "e014", "e030", "e031", "e020"): diff --git a/Modules/readZclClusters.py b/Modules/readZclClusters.py index b77b01ef0..4b2a369c5 100644 --- a/Modules/readZclClusters.py +++ b/Modules/readZclClusters.py @@ -307,24 +307,27 @@ def is_cluster_specific_config(self, model, ep, cluster, attribute=None): def is_cluster_zcl_config_available( self, nwkid, ep, cluster, attribute=None): """ Is this cluster is handle via the ZclCluster , is this cluster + attribute hanlde via ZclCluster """ - - if is_manufacturer_specific_cluster( self, cluster): + + if is_manufacturer_specific_cluster( self, nwkid, ep, cluster): return True - + if is_cluster_specific_config(self, _get_model_name( self, nwkid), ep, cluster, attribute): return True - + return is_generic_zcl_cluster( self, cluster, attribute) - -def is_manufacturer_specific_cluster( self, cluster): - - if cluster not in self.readZclClusters: - return False - if "ManufSpecificCluster" in self.readZclClusters[ cluster ]: - # We have a Manufacturer Specific cluster + + + +def is_manufacturer_specific_cluster(self, nwkid, ep, cluster): + cluster_info = self.readZclClusters.get(cluster, {}).get("ManufSpecificCluster", False) + + if cluster_info: return True - - + + device_info = self.DeviceConf.get(_get_model_name(self, nwkid), {}).get('Ep', {}).get(ep, {}).get(cluster, {}) + return device_info and 'ManufSpecificCluster' in device_info + + def is_generic_zcl_cluster( self, cluster, attribute=None): if cluster not in self.readZclClusters: return False