diff --git a/.hidden/VERSION b/.hidden/VERSION index fd17756f6..239fd1ddb 100644 --- a/.hidden/VERSION +++ b/.hidden/VERSION @@ -1 +1 @@ -{"branch": "beta7", "version": "7.2.001"} +{"branch": "beta7", "version": "7.2.002"} diff --git a/Classes/AdminWidgets.py b/Classes/AdminWidgets.py index 8672184df..3c4527b6a 100644 --- a/Classes/AdminWidgets.py +++ b/Classes/AdminWidgets.py @@ -170,7 +170,6 @@ def updateStatusWidget(self, Devices, statusType): return def updateNotificationWidget(self, Devices, notification): - deviceid_txt_widget = DEVICEID_TXT_WIDGET + "%02s" % self.HardwareID unit = 0 for x in Devices: @@ -186,5 +185,4 @@ def updateNotificationWidget(self, Devices, notification): Devices[unit].Update(nValue=nValue, sValue=sValue) def handleCommand(self, Command): - return diff --git a/Classes/GroupMgtv2/GrpDatabase.py b/Classes/GroupMgtv2/GrpDatabase.py index 0a9217f5c..d29f24218 100644 --- a/Classes/GroupMgtv2/GrpDatabase.py +++ b/Classes/GroupMgtv2/GrpDatabase.py @@ -16,13 +16,13 @@ "Device Addr"] """ -import os import json +import os import time import Domoticz - -from Modules.tools import setConfigItem, getConfigItem, is_domoticz_db_available +from Modules.domoticzAPI import getConfigItem, setConfigItem +from Modules.tools import is_domoticz_db_available def write_groups_list(self): diff --git a/Classes/GroupMgtv2/GrpDomoticz.py b/Classes/GroupMgtv2/GrpDomoticz.py index 949795675..04592479b 100644 --- a/Classes/GroupMgtv2/GrpDomoticz.py +++ b/Classes/GroupMgtv2/GrpDomoticz.py @@ -207,6 +207,11 @@ def best_group_widget(self, GroupId): WidgetType = self.ListOfDevices[NwkId]["Ep"][devEp]["ClusterType"][DomoDeviceUnit] self.logging("Debug", "------------ GroupWidget: %s WidgetType: %s" % (GroupWidgetType, WidgetType)) + if WidgetType in ("VenetianInverted", "VanneInverted", "CurtainInverted"): + # Those widgets are commanded via cluster Level Control + GroupWidgetType = "LvlControl" + continue + if GroupWidgetType is None and WidgetType in WIDGET_STYLE: GroupWidgetType = WidgetType continue @@ -240,7 +245,8 @@ def best_group_widget(self, GroupId): GroupWidgetType = WidgetType continue - if WidgetType in ("Venetian", "VenetianInverted", "WindowCovering", "BlindPercentInverted"): + + if WidgetType in ("Venetian", "WindowCovering", "BlindPercentInverted"): GroupWidgetType = WidgetType if GroupWidgetType is None: diff --git a/Classes/GroupMgtv2/GrpResponses.py b/Classes/GroupMgtv2/GrpResponses.py index 93afad2c2..d304d526d 100644 --- a/Classes/GroupMgtv2/GrpResponses.py +++ b/Classes/GroupMgtv2/GrpResponses.py @@ -36,8 +36,8 @@ def add_group_member_ship_response(self, MsgData): Status: 0x00: Ok - 0x8a: The device does not have storage space to support the requested operation. - 0x8b: The device is not in the proper state to support the requested operation. + 0x8a: Duplicate entry ---> Already exists in the group + 0x8b: Entry not found ---> Not in the group """ self.logging("Debug", "add_group_member_ship_response - MsgData: %s (%s)" % (MsgData, len(MsgData))) @@ -79,7 +79,8 @@ def add_group_member_ship_response(self, MsgData): ) return - if MsgStatus == "00": + # Status 8a is duplicate ---> Seen as success for update of data (if not exits that should not happen) + if MsgStatus == "00" or MsgStatus == "8a": # Success if "GroupMemberShip" not in self.ListOfDevices[MsgSrcAddr]: self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"] = {} @@ -140,8 +141,18 @@ def check_group_member_ship_response(self, MsgData): self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"][MsgEP][MsgGroupID]["Status"] = "OK" checkToCreateOrUpdateGroup(self, MsgSrcAddr, MsgEP, MsgGroupID) - # If we have receive a MsgStatus error, we cannot conclude, so we consider the membership to that group, not existing - + # Status 8b NetId not in GrpId ---> Must delete group data (should not happen) + elif MsgStatus == "8b" and "GroupMemberShip" in self.ListOfDevices[MsgSrcAddr]: + if MsgGroupID in self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"][MsgEP]: + del self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"][MsgEP][MsgGroupID] + + if len(self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"][MsgEP]) == 0: + del self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"][MsgEP] + + if len(self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"]) == 0: + del self.ListOfDevices[MsgSrcAddr]["GroupMemberShip"] + + checkToRemoveGroup(self,MsgSrcAddr,MsgEP,MsgGroupID) def look_for_group_member_ship_response(self, MsgData): """ diff --git a/Classes/IAS.py b/Classes/IAS.py index 7f7f354de..189c9cd25 100644 --- a/Classes/IAS.py +++ b/Classes/IAS.py @@ -9,12 +9,13 @@ """ -import time import struct +import time -import Domoticz +from Modules.basicOutputs import write_attribute from Modules.bindings import bindDevice -from Modules.tools import getEpForCluster +from Modules.sendZigateCommand import raw_APS_request +from Modules.tools import get_and_inc_ZCL_SQN, getEpForCluster from Modules.zigateConsts import ZIGATE_EP from Zigbee.zclCommands import (zcl_ias_wd_command_squawk, zcl_ias_wd_command_start_warning, @@ -22,11 +23,6 @@ zcl_read_attribute, zcl_write_attribute) from Zigbee.zdpCommands import zdp_simple_descriptor_request -from Modules.basicOutputs import write_attribute -from Modules.sendZigateCommand import raw_APS_request -from Modules.tools import get_and_inc_ZCL_SQN - - # Synopsys # @@ -294,7 +290,7 @@ def write_IAS_WD_Squawk(self, NwkId, ep, SquawkMode): SQUAWKMODE = {"disarmed": 0b00000000, "armed": 0b00000001} if SquawkMode not in SQUAWKMODE: - Domoticz.Error("_write_IAS_WD_Squawk - %s/%s Unknown Squawk Mode: %s" % (NwkId, ep, SquawkMode)) + self.logging("Error", "_write_IAS_WD_Squawk - %s/%s Unknown Squawk Mode: %s" % (NwkId, ep, SquawkMode)) self.logging( "Debug", @@ -485,7 +481,7 @@ def retreive_attributes(self, MsgData): idx += size elif len(MsgData[idx:]) == 6: # crap, lets finish it - # Domoticz.Log("Crap Data: %s len: %s" %(MsgData[idx:], len(MsgData[idx:]))) + self.logging("Debug", "Crap Data: %s len: %s" %(MsgData[idx:], len(MsgData[idx:]))) idx += 6 return attributes diff --git a/Classes/LoggingManagement.py b/Classes/LoggingManagement.py index 21846ad84..f54145be1 100644 --- a/Classes/LoggingManagement.py +++ b/Classes/LoggingManagement.py @@ -14,7 +14,6 @@ import logging import os import os.path - import threading import time from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler diff --git a/Classes/NetworkEnergy.py b/Classes/NetworkEnergy.py index ab06d25f6..16b6ab71b 100644 --- a/Classes/NetworkEnergy.py +++ b/Classes/NetworkEnergy.py @@ -26,7 +26,6 @@ from pathlib import Path from time import time -import Domoticz from Modules.basicOutputs import maskChannel from Zigbee.zdpCommands import zdp_management_network_update_request @@ -88,7 +87,7 @@ def isRouter(nwkid): if nwkid == r: continue if "Health" in self.ListOfDevices[nwkid]: - Domoticz.Log("_initNwkEnrgy %s - >%s<" % (nwkid, self.ListOfDevices[nwkid]["Health"])) + self.logging("Debug", "_initNwkEnrgy %s - >%s<" % (nwkid, self.ListOfDevices[nwkid]["Health"])) if self.ListOfDevices[nwkid]["Health"] == "Not Reachable": self.logging("Log", "_initNwkEnrgy - skiping device %s which is Not Reachable" % nwkid) continue @@ -128,13 +127,13 @@ def prettyPrintNwkEnrgy(self): for r in self.EnergyLevel: for i in self.EnergyLevel[r]: - Domoticz.Log("%s <-> %s : %s" % (r, i, self.EnergyLevel[r][i]["Status"])) + self.logging("Log", "%s <-> %s : %s" % (r, i, self.EnergyLevel[r][i]["Status"])) if self.EnergyLevel[i]["Status"] == "Completed": - Domoticz.Log("---> Tx: %s" % (self.EnergyLevel[r][i]["Tx"])) - Domoticz.Log("---> Failure: %s" % (self.EnergyLevel[r][i]["Failure"])) + self.logging("Log","---> Tx: %s" % (self.EnergyLevel[r][i]["Tx"])) + self.logging("Log","---> Failure: %s" % (self.EnergyLevel[r][i]["Failure"])) for c in self.EnergyLevel[r][i]["Channels"]: - Domoticz.Log("---> %s: %s" % (c, self.EnergyLevel[r][i]["Channels"][c])) - self.logging("Debug", "") + self.logging("Log", "---> %s: %s" % (c, self.EnergyLevel[r][i]["Channels"][c])) + self.logging("Log", "") def NwkScanReq(self, root, target, channels): @@ -162,7 +161,7 @@ def start_scan(self, root=None, target=None, channels=None): self.logging("Debug", "start_scan") if self.ScanInProgress: - Domoticz.Log("a Scan is already in progress") + self.logging("Log", "a Scan is already in progress") return self.ScanInProgress = True @@ -213,7 +212,7 @@ def _next_scan(self): if len(self.nwkidInQueue) > 0: root, entry = self.nwkidInQueue.pop() if r != root and i != entry: - Domoticz.Error("Mismatch %s versus %s" % (i, entry)) + self.logging("Error", "Mismatch %s versus %s" % (i, entry)) continue elif self.EnergyLevel[r][i]["Status"] == "ScanRequired": _channels = [] @@ -241,12 +240,10 @@ def finish_scan(self): storeEnergy = {} storeEnergy[stamp] = [] for r in self.EnergyLevel: - Domoticz.Status("Network Energy Level Report: %s" % r) - Domoticz.Status("-----------------------------------------------") - Domoticz.Status( - "%6s <- %5s %6s %8s %4s %4s %4s %4s %4s %4s" - % ("router", "nwkid", "Tx", "Failure", "11", "15", "19", "20", "25", "26") - ) + self.logging("Status", "Network Energy Level Report: %s" % r) + self.logging("Status", "-----------------------------------------------") + self.logging("Status", "%6s <- %5s %6s %8s %4s %4s %4s %4s %4s %4s" % ( + "router", "nwkid", "Tx", "Failure", "11", "15", "19", "20", "25", "26")) router = {} router["_NwkId"] = r router["MeshRouters"] = [] @@ -296,7 +293,7 @@ def finish_scan(self): entry["Channels"].append(channels) toprint += " %4s" % self.EnergyLevel[r][nwkid]["Channels"][c] router["MeshRouters"].append(entry) - Domoticz.Status(toprint) + self.logging("Status", toprint) storeEnergy[stamp].append(router) self.logging("Debug", "Network Energly Level Report: %s" % storeEnergy) @@ -324,10 +321,8 @@ def finish_scan(self): fout.write("\n") json.dump(storeEnergy, fout) else: - Domoticz.Error( - "Unable to get access to directory %s, please check PluginConf.txt" - % (self.pluginconf.pluginConf["pluginReports"]) - ) + self.logging("Error", "Unable to get access to directory %s, please check PluginConf.txt" % ( + self.pluginconf.pluginConf["pluginReports"]) ) def NwkScanResponse(self, MsgData): @@ -374,7 +369,7 @@ def NwkScanResponse(self, MsgData): } if MsgDataStatus != "00": - Domoticz.Error("NwkScanResponse - Status: %s with Data: %s" % (MsgDataStatus, MsgData)) + self.logging("Error", "NwkScanResponse - Status: %s with Data: %s" % (MsgDataStatus, MsgData)) return if len(self.nwkidInQueue) == 0 and MsgSrc: @@ -389,9 +384,8 @@ def NwkScanResponse(self, MsgData): root, entry = self.nwkidInQueue.pop() self.logging("Debug", "NwkScanResponse - Root: %s, Entry: %s, MsgSrc: %s" % (root, entry, MsgSrc)) if entry != MsgSrc: - Domoticz.Log( - "NwkScanResponse - Unexpected message >%s< from %s, expecting %s" % (MsgData, MsgSrc, entry) - ) + self.logging("Log", "NwkScanResponse - Unexpected message >%s< from %s, expecting %s" % ( + MsgData, MsgSrc, entry) ) elif len(self.nwkidInQueue) > 0: root, entry = self.nwkidInQueue.pop() diff --git a/Classes/OTA.py b/Classes/OTA.py index b70c574bc..16c2a9393 100644 --- a/Classes/OTA.py +++ b/Classes/OTA.py @@ -32,10 +32,11 @@ import time from datetime import datetime from os import listdir -from os.path import isfile, join, exists +from os.path import exists, isfile, join from pathlib import Path -import Domoticz +from Classes.AdminWidgets import AdminWidgets +from Classes.LoggingManagement import LoggingManagement from Modules.sendZigateCommand import sendZigateCmd from Modules.tools import get_device_nickname from Modules.zigateConsts import ADDRESS_MODE, ZIGATE_EP @@ -44,9 +45,6 @@ zcl_raw_ota_query_next_image_response, zcl_raw_ota_upgrade_end_response) -from Classes.AdminWidgets import AdminWidgets -from Classes.LoggingManagement import LoggingManagement - # This file is hosted on @koenkk repository. # This file is maintained from the community, so make sure what you do. @@ -509,11 +507,11 @@ def ota_send_block(self, dest_addr, dest_ep, image_type, msg_image_version, bloc logging(self, "Debug", "ota_send_block - Addr: %s/%s Type: 0x%X" % (dest_addr, dest_ep, image_type)) if image_type not in self.ListOfImages["ImageType"]: - Domoticz.Error("ota_send_block - unknown image_type %s" % image_type) + logging(self, "Error", "ota_send_block - unknown image_type %s" % image_type) return False if image_type != int(self.ListInUpdate["ImageType"], 16): - Domoticz.Error("ota_send_block - inconsistent ImageType Received: %s Expecting: %s" % (image_type, self.ListInUpdate["ImageType"])) + logging(self, "Error", "ota_send_block - inconsistent ImageType Received: %s Expecting: %s" % (image_type, self.ListInUpdate["ImageType"])) return False _status = 0x00 @@ -742,19 +740,19 @@ def firmware_update(self, brand, file_name, target_nwkid, target_ep, force_updat return False if brand not in self.ListOfImages["Brands"]: - Domoticz.Error("restapi_firmware_update Brands %s unknown" % brand) + logging(self, "Error", "restapi_firmware_update Brands %s unknown" % brand) return False if file_name not in self.ListOfImages["Brands"][brand]: - Domoticz.Error("restapi_firmware_update FileName %s unknown in this Brand %s" % (file_name, brand)) + logging(self, "Error", "restapi_firmware_update FileName %s unknown in this Brand %s" % (file_name, brand)) return False if target_nwkid not in self.ListOfDevices: - Domoticz.Error("restapi_firmware_update NwkId: %s unknown" % target_nwkid) + logging(self, "Error", "restapi_firmware_update NwkId: %s unknown" % target_nwkid) return False if target_ep not in self.ListOfDevices[target_nwkid]["Ep"]: - Domoticz.Error("restapi_firmware_update NwkId: %s Ep: %s unknown" % (target_nwkid, target_ep)) + logging(self, "Error", "restapi_firmware_update NwkId: %s Ep: %s unknown" % (target_nwkid, target_ep)) return False image_type = self.ListOfImages["Brands"][brand][file_name]["ImageType"] @@ -929,7 +927,7 @@ def ota_extract_image_headers(self, subfolder, image): # OK 13/10 logging(self, "Debug", "ota_extract_image_headers - offset:%s ..." % offset) ota_image = ota_image[offset:] - headers = unpack_headers(ota_image) + headers = unpack_headers(self, ota_image) _logging_headers(self, headers) logging( @@ -961,12 +959,12 @@ def offset_start_firmware(ota_image): # OK 13/10 return i return None -def unpack_headers(ota_image): # OK 13/10 +def unpack_headers(self, ota_image): # OK 13/10 try: header_data = list(struct.unpack("= '12': CERTIFIED_DEVICES_UPGRADE_CMD = "python3 -m pip install z4d-certified-devices --upgrade --break-system-packages" diff --git a/Conf/Local-Devices/TS0601-Solar-Siren.json b/Conf/Local-Devices/TS0601-Solar-Siren.json new file mode 100644 index 000000000..5f95e11f8 --- /dev/null +++ b/Conf/Local-Devices/TS0601-Solar-Siren.json @@ -0,0 +1,43 @@ +{ + "_source": "https://github.com/zigbeefordomoticz/Domoticz-Zigbee/issues/1581", + "_comment": "NEO - Sirène intelligente extérieure Zigbee Tuya (alimentation 5V/1A ou batterie + panneau solaire)", + "_version": "1.0", + "Identifier": [ + [ "TS0601", "_TZE200_nlrfgpny"] + ], + "Ep": { + "01": { + "0000": "", + "0004": "", + "ef00": "", + "000a": "", + "0019": "", + "Type": "SwitchAlarm/TamperSwitch/Tamper/Notification" + } + }, + "Type": "", + "ClusterToBind": [], + "ConfigureReporting": {}, + "ReadAttributes": { + "0000": [ "0004", "0000", "0001", "0005", "0007", "fffe" ], + "0009": [] + }, + "TUYA_REGISTRATION": 19, + "TS0601_DP": { + "01": { "store_tuya_attribute": "AlarmState", "sensor_type": "tamper"}, + "06": { "store_tuya_attribute": "Charging", "sensor_type": "charging_mode"}, + "07": { "store_tuya_attribute": "TuyaAlarmDuration", "action_type": "TuyaAlarmDuration"}, + "0d": { "store_tuya_attribute": "TuyaAlarmSwitch", "action_type": "TuyaAlarmSwitch"}, + "0e": { "store_tuya_attribute": "Battery", "sensor_type": "battery"}, + "14": { "store_tuya_attribute": "TamperAlarmState", "sensor_type": "tamper"}, + "15": { "store_tuya_attribute": "TuyaAlarmMelody", "action_type": "TuyaAlarmMelody"}, + "65": { "store_tuya_attribute": "TuyaTamperSwitch", "action_type": "TuyaTamperSwitch"}, + "66": { "store_tuya_attribute": "TuyaAlarmMode", "action_type": "TuyaAlarmMode"} + }, + "BatteryDevice": 1, + "Param": { + "TuyaAlarmMelody": 0, + "TuyaAlarmMode": 2, + "TuyaAlarmDuration": 1 + } +} diff --git a/Conf/ZclDefinitions/0001.json b/Conf/ZclDefinitions/0001.json index 02b62e6f5..917e0e9f8 100644 --- a/Conf/ZclDefinitions/0001.json +++ b/Conf/ZclDefinitions/0001.json @@ -84,7 +84,7 @@ "Enabled": true, "Name": "BatteryPercentageRemaining", "DataType": "20" , - "Range": [ "00", "fe" ] , + "Range": [ "00", "ff" ] , "SpecialValues": { "ff": "invalid or unknown reading"}, "Acc": "RP" , "Default": "0", diff --git a/DevicesModules/custom_zlinky.py b/DevicesModules/custom_zlinky.py index 0926ab61b..4913d8851 100644 --- a/DevicesModules/custom_zlinky.py +++ b/DevicesModules/custom_zlinky.py @@ -168,13 +168,9 @@ def zlinky_cluster_metering(self, Devices, nwkid, ep, cluster, attribut, value): store_ZLinky_infos( self, nwkid, 'EASF10', value) elif attribut == "0307": # PRM - try: - store_ZLinky_infos( self, nwkid, 'PRM', binascii.unhexlify(value).decode("utf-8")) - except Exception as e: - store_ZLinky_infos( self, nwkid, 'PRM', value) + store_ZLinky_infos( self, nwkid, 'PRM', value) elif attribut == "0308": # Serial Number - value = binascii.unhexlify(value).decode("utf-8") self.log.logging( "ZLinky", "Debug", "Cluster0702 - 0x0308 - Serial Number %s" % (value), nwkid, ) checkAndStoreAttributeValue(self, nwkid, ep, cluster, attribut, value) @@ -666,4 +662,4 @@ def zlinky_cluster_lixee_private(self, Devices, nwkid, ep, cluster, attribut, va elif attribut == "0300": # Linky Mode update_zlinky_device_model_if_needed( self, nwkid ) - \ No newline at end of file + diff --git a/Modules/actuators.py b/Modules/actuators.py index 23abf65ea..bcfbdecf5 100644 --- a/Modules/actuators.py +++ b/Modules/actuators.py @@ -12,6 +12,11 @@ import json +from Modules.basicOutputs import set_poweron_afteroffon +from Modules.readAttributes import ReadAttributeRequest_0006_400x +from Modules.thermostats import thermostat_Setpoint +from Modules.tools import Hex_Format, rgb_to_hsl, rgb_to_xy +from Modules.zigateConsts import ZIGATE_EP from Zigbee.zclCommands import (zcl_identify_send, zcl_identify_trigger_effect, zcl_level_move_to_level, zcl_move_hue_and_saturation, @@ -22,16 +27,27 @@ zcl_onoff_off_noeffect, zcl_onoff_off_witheffect, zcl_onoff_on, zcl_onoff_stop, zcl_toggle, + zcl_window_covering_level, zcl_window_covering_off, zcl_window_covering_on, - zcl_window_covering_stop, - zcl_window_covering_level, zcl_window_covering_percentage) + zcl_window_covering_percentage, + zcl_window_covering_stop) -from Modules.basicOutputs import set_poweron_afteroffon -from Modules.readAttributes import ReadAttributeRequest_0006_400x -from Modules.thermostats import thermostat_Setpoint -from Modules.tools import Hex_Format, rgb_to_hsl, rgb_to_xy -from Modules.zigateConsts import ZIGATE_EP + +def lightning_percentage_to_analog( percentage_value ): + """convert a percentage value (0-100) into an analog value ( 0-255) + + Args: + percentage_value (int): a value from 0 to 100, which represent a % + + Returns: + _type_: a value from 1 to 254 + """ + if percentage_value > 99: + return 254 + elif percentage_value < 1: + return 1 + return round((percentage_value * 255) / 100) def actuators(self, action, nwkid, epout, DeviceType, cmd=None, value=None, color=None): @@ -149,16 +165,9 @@ def actuator_setlevel(self, nwkid, EPout, value, DeviceType, transition="0010", value = "%02x" % value zcl_window_covering_percentage(self, nwkid, EPout, value) else: - if value == 100: - value = 255 - elif value == 0: - value = 0 - else: - value = round((value * 255) / 100) - if value > 0 and value == 0: - value = 1 - - value = Hex_Format(2, value) + value = Hex_Format(2, lightning_percentage_to_analog( value )) + self.log.logging("Command", "Debug", "---------- actuator_setlevel Set Level: %s" % (value), nwkid) + if withOnOff: zcl_move_to_level_with_onoff( self, nwkid, EPout, "01", value, transition) else: @@ -197,8 +206,6 @@ def actuator_setalarm(self, nwkid, EPout, value): elif value == 50: # Disarmed self.iaszonemgt.write_IAS_WD_Squawk(nwkid, EPout, "disarmed") - - def get_all_transition_mode( self, Nwkid): transitionMoveLevel = transitionRGB = transitionMoveLevel = transitionHue = transitionTemp = "0000" @@ -238,7 +245,7 @@ def actuator_setcolor(self, nwkid, EPout, value, Color): transitionMoveLevel = "%04x" % int(self.ListOfDevices[nwkid]["Param"]["moveToLevel"]) if Hue_List["m"] or Hue_List["m"] != 9998: - value = int(1 + value * 254 / 100) # To prevent off state + value = lightning_percentage_to_analog( value ) self.log.logging("Command", "Debug", "---------- Set Level: %s" % (value), nwkid) actuator_setlevel(self, nwkid, EPout, value, "Light", transitionMoveLevel) @@ -344,4 +351,4 @@ def actuator_identify(self, nwkid, ep, value=None): value = 0x00 # Flashing color = 0x03 # Blue - zcl_identify_trigger_effect( self, nwkid, ep, "%02x" % value, "%02x" % color) + zcl_identify_trigger_effect( self, nwkid, ep, "%02x" % value, "%02x" % color) \ No newline at end of file diff --git a/Modules/basicInputs.py b/Modules/basicInputs.py index 6d239f173..a292729ff 100644 --- a/Modules/basicInputs.py +++ b/Modules/basicInputs.py @@ -4,7 +4,6 @@ # Author: zaraki673 & pipiche38 # -import Domoticz import struct from Modules.basicOutputs import raw_APS_request @@ -13,26 +12,20 @@ def encode_endian_data(data, datatype): if datatype in ("10", "18", "20", "28", "30"): - value = data + return data elif datatype in ("09", "19", "21", "29", "31"): - value = "%04x" % struct.unpack(">H", struct.pack("H", int(data, 16)))[0] + return "%04x" % struct.unpack(">H", struct.pack("H", int(data, 16)))[0] elif datatype in ("22", "2a"): - value = "%06x" % struct.unpack(">I", struct.pack("I", int(data, 16)))[0] + return "%06x" % struct.unpack(">I", struct.pack("I", int(data, 16)))[0] elif datatype in ("23", "2b", "39", "e2"): - value = "%08x" % struct.unpack(">I", struct.pack("I", int(data, 16)))[0] + return "%08x" % struct.unpack(">I", struct.pack("I", int(data, 16)))[0] elif datatype in ("00", "41", "42", "4c"): - value = data - - else: - value = data - Domoticz.Log("-------> Data not decoded Type: %s Value: %s " % (datatype, value)) - - # self.log.logging( None, 'Log', "encode_endian %s -> %s" %(data, value)) - return value + return data + return data def read_attribute_response(self, nwkid, ep, sqn, cluster, status, data_type, attribute, value, manuf_code="0000"): diff --git a/Modules/batterieManagement.py b/Modules/batterieManagement.py index e5f428c1a..2b479ce0d 100644 --- a/Modules/batterieManagement.py +++ b/Modules/batterieManagement.py @@ -10,11 +10,11 @@ """ -from Modules.domoTools import Update_Battery_Device -from Modules.tools import voltage2batteryP, get_deviceconf_parameter_value - from time import time +from Modules.domoTools import Update_Battery_Device +from Modules.tools import get_deviceconf_parameter_value, voltage2batteryP + def UpdateBatteryAttribute(self, Devices, MsgSrcAddr, MsgSrcEp): diff --git a/Modules/bindings.py b/Modules/bindings.py index 9de6e74b6..8798098b4 100644 --- a/Modules/bindings.py +++ b/Modules/bindings.py @@ -6,11 +6,10 @@ from time import time -from Zigbee.zdpCommands import zdp_binding_device, zdp_unbinding_device - from Modules.pluginDbAttributes import STORE_CONFIGURE_REPORTING from Modules.tools import is_fake_ep from Modules.zigateConsts import CLUSTERS_LIST +from Zigbee.zdpCommands import zdp_binding_device, zdp_unbinding_device def bindGroup(self, ieee, ep, cluster, groupid): diff --git a/Modules/blitzwolf.py b/Modules/blitzwolf.py index b159848e1..5ea032ee8 100644 --- a/Modules/blitzwolf.py +++ b/Modules/blitzwolf.py @@ -3,7 +3,6 @@ # # Author: zaraki673 & pipiche38 # -import Domoticz from Modules.readAttributes import ReadAttributeRequest_0b04_050b_0505_0508 from Modules.zigateConsts import MAX_LOAD_ZIGATE diff --git a/Modules/casaia.py b/Modules/casaia.py index dd23685d8..297fee1af 100644 --- a/Modules/casaia.py +++ b/Modules/casaia.py @@ -12,7 +12,8 @@ from Modules.basicOutputs import write_attribute from Modules.domoMaj import MajDomoDevice from Modules.sendZigateCommand import raw_APS_request -from Modules.tools import get_and_inc_ZCL_SQN, is_ack_tobe_disabled, retreive_cmd_payload_from_8002 +from Modules.tools import (get_and_inc_ZCL_SQN, is_ack_tobe_disabled, + retreive_cmd_payload_from_8002) from Modules.zigateConsts import ZIGATE_EP CASAIA_MANUF_CODE = "113c" diff --git a/Modules/checkingUpdate.py b/Modules/checkingUpdate.py index fe2307d93..ae5eb86f7 100644 --- a/Modules/checkingUpdate.py +++ b/Modules/checkingUpdate.py @@ -11,12 +11,8 @@ # - beta # Provide response to REST API request -import time - import dns.resolver -import Domoticz - PLUGIN_TXT_RECORD = "zigate_plugin.pipiche.net" ZIGATEV1_FIRMWARE_TXT_RECORD = "zigatev1.pipiche.net" ZIGATEV1OPTIPDM_TXT_RECORD = "zigatev1optipdm.pipiche.net" @@ -29,14 +25,12 @@ } -def checkPluginVersion(zigbee_communitation, branch, zigate_model): +def checkPluginVersion(self, zigbee_communitation, branch, zigate_model): TXT_RECORD = None if zigbee_communitation == "native": TXT_RECORD = ZIGATE_DNS_RECORDS.get(zigate_model) - #Domoticz.Log("checkPluginVersion - Start request version for branche %s model: %s" %( branch, zigate_model)) - zigate_plugin = zigateVersions = None try: zigate_plugin = dns.resolver.resolve(PLUGIN_TXT_RECORD, "TXT", tcp=True, lifetime=1).response.answer[0] @@ -45,57 +39,51 @@ def checkPluginVersion(zigbee_communitation, branch, zigate_model): zigateVersions = dns.resolver.resolve(TXT_RECORD, "TXT", tcp=True, lifetime=1).response.answer[0] zigateVersions = str(zigateVersions[0]).strip('"') except Exception as e: - #Domoticz.Log("DNS error while checking Plugin and Firmware version: %s" %e) return (0, 0, 0) - #Domoticz.Log("checkPluginVersion - Plugin Version record: %s Type: %s" %(str(zigate_plugin), type(zigate_plugin))) - #Domoticz.Log("checkPluginVersion - Firmware Version record: %s Type: %s" %(str(zigateVersions), type(zigateVersions))) pluginVersion = {} if zigate_plugin and zigate_plugin != "": for branch_version in zigate_plugin.split(";"): pluginVersion[branch_version.split("=")[0]] = branch_version.split("=")[1].strip('"') - #Domoticz.Log("checkPluginVersion - Available Plugin Versions are, %s , %s" %(branch_version.split("=")[0], pluginVersion[ branch_version.split("=")[0] ])) firmwareVersion = {} if zigateVersions and zigateVersions != "": for major_minor in zigateVersions.split(";"): firmwareVersion[major_minor.split("=")[0]] = major_minor.split("=")[1].strip('"') - #Domoticz.Log("checkPluginVersion - Available Firmware Version is, %s , %s" %(major_minor.split("=")[0], firmwareVersion[ major_minor.split("=")[0] ])) if zigbee_communitation == "native" and branch in pluginVersion and "firmMajor" in firmwareVersion and "firmMinor" in firmwareVersion: return (pluginVersion[branch], firmwareVersion["firmMajor"], firmwareVersion["firmMinor"]) if zigbee_communitation == "zigpy" and branch in pluginVersion: return (pluginVersion[branch], 0, 0) - Domoticz.Error("You are running %s-%s , a NOT SUPPORTED version. Please refer to https://github.com/zigbeefordomoticz/Domoticz-Zigbee to get the latest informations" % (branch, pluginVersion )) + self.log.logging("Plugin", "Error", "You are running %s-%s , a NOT SUPPORTED version. Please refer to https://github.com/zigbeefordomoticz/Domoticz-Zigbee to get the latest informations" % (branch, pluginVersion )) return (0, 0, 0) -def checkPluginUpdate(currentVersion, availVersion): +def checkPluginUpdate(self, currentVersion, availVersion): if availVersion == 0: return False - #Domoticz.Log("checkPluginUpdate - %s %s" %(currentVersion, availVersion)) currentMaj, currentMin, currentUpd = currentVersion.split(".") availMaj, availMin, availUpd = availVersion.split(".") if availMaj > currentMaj: - Domoticz.Log("checkPluginVersion - Upgrade available: %s" %availVersion) + self.log.logging("Plugin", "Status", "checkPluginVersion - Upgrade available: %s" %availVersion) return True if availMaj == currentMaj and ( availMin == currentMin and availUpd > currentUpd or availMin > currentMin ): - Domoticz.Log("checkPluginVersion - Upgrade available: %s" %availVersion) + self.log.logging("Plugin", "Status", "checkPluginVersion - Upgrade available: %s" %availVersion) return True return False -def checkFirmwareUpdate(currentMajorVersion, currentFirmwareVersion, availfirmMajor, availfirmMinor): +def checkFirmwareUpdate(self, currentMajorVersion, currentFirmwareVersion, availfirmMajor, availfirmMinor): if not (availfirmMinor and currentFirmwareVersion): return False if int(availfirmMinor, 16) > int(currentFirmwareVersion, 16): - Domoticz.Log("checkFirmwareUpdate - Firmware update available") + self.log.logging("Plugin", "Status", "checkFirmwareUpdate - Firmware update available") return True return False diff --git a/Modules/cmdsDoorLock.py b/Modules/cmdsDoorLock.py index d8de5b32e..d86937c6b 100644 --- a/Modules/cmdsDoorLock.py +++ b/Modules/cmdsDoorLock.py @@ -10,9 +10,9 @@ """ -from Modules.zigateConsts import ZIGATE_EP from Modules.basicOutputs import raw_APS_request from Modules.tools import get_and_inc_ZCL_SQN +from Modules.zigateConsts import ZIGATE_EP def cluster0101_lock_door(self, NwkId): diff --git a/Modules/command.py b/Modules/command.py index e30d40305..08022294a 100644 --- a/Modules/command.py +++ b/Modules/command.py @@ -10,7 +10,6 @@ """ -import Domoticz from Modules.actuators import (actuator_off, actuator_on, actuator_setcolor, actuator_setlevel, actuator_stop, actuators) from Modules.casaia import (casaia_ac201_fan_control, casaia_setpoint, @@ -47,18 +46,6 @@ THERMOSTAT_LEVEL_3_MODE, ZIGATE_EP) -def debugDevices(self, Devices, Unit): - - Domoticz.Log("Device Name: %s" % Devices[Unit].Name) - Domoticz.Log(" DeviceId: %s" % Devices[Unit].DeviceID) - Domoticz.Log(" Type: %s" % Devices[Unit].Type) - Domoticz.Log(" Subtype: %s" % Devices[Unit].SubType) - Domoticz.Log(" SwitchType: %s" % Devices[Unit].SwitchType) - Domoticz.Log(" Options: %s" % Devices[Unit].Options) - Domoticz.Log(" LastLevel: %s" % Devices[Unit].LastLevel) - Domoticz.Log(" LastUpdate: %s" % Devices[Unit].LastUpdate) - - # Matrix between Domoticz Type, Subtype, SwitchType and Plugin DeviceType # Type, Subtype, Switchtype DEVICE_SWITCH_MATRIX = { @@ -142,16 +129,15 @@ def debugDevices(self, Devices, Unit): "ThermoOnOff", "ShutterCalibration", "SwitchAlarm", + "TamperSwitch" ] def mgtCommand(self, Devices, Unit, Command, Level, Color): if Devices[Unit].DeviceID not in self.IEEE2NWK: - Domoticz.Error( - "mgtCommand - something strange the Device %s DeviceID: %s Unknown" - % (Devices[Unit].Name, Devices[Unit].DeviceID) - ) + self.log.logging("Command", "Error", "mgtCommand - something strange the Device %s DeviceID: %s Unknown" % ( + Devices[Unit].Name, Devices[Unit].DeviceID) ) return NWKID = self.IEEE2NWK[Devices[Unit].DeviceID] @@ -181,7 +167,7 @@ def mgtCommand(self, Devices, Unit, Command, Level, Color): if len(ClusterTypeList) == 0: # No match with ClusterType # Should not happen. We didn't find any Widget references in the Device ClusterType! - Domoticz.Error("mgtCommand - no ClusterType found ! " + str(self.ListOfDevices[NWKID])) + self.log.logging("Command", "Error", "mgtCommand - no ClusterType found ! " + str(self.ListOfDevices[NWKID])) return self.log.logging( @@ -190,7 +176,7 @@ def mgtCommand(self, Devices, Unit, Command, Level, Color): actionable = False if len(ClusterTypeList) != 1: - Domoticz.Error("mgtCommand - Not Expected. ClusterType: %s for NwkId: %s" % (ClusterTypeList, NWKID)) + self.log.logging("Command", "Error", "mgtCommand - Not Expected. ClusterType: %s for NwkId: %s" % (ClusterTypeList, NWKID)) return if ClusterTypeList[0][0] == "00": @@ -334,18 +320,20 @@ def mgtCommand(self, Devices, Unit, Command, Level, Color): UpdateDevice_v2(self, Devices, Unit, 0, "Off", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) return - - if DeviceType == "SwitchAlarm" and _model_name == "SMSZB-120" and self.iaszonemgt: self.iaszonemgt.iaswd_develco_warning(NWKID, EPout, "00") UpdateDevice_v2(self, Devices, Unit, 0, "Off", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) return - if DeviceType == "SwitchAlarm" and _model_name == "TS0601-Solar-Siren": - if ts0601_extract_data_point_infos( self, _model_name): - ts0601_actuator(self, NWKID, "TuyaAlarmSwitch", 0) - UpdateDevice_v2(self, Devices, Unit, 0, "Off", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) - return + if DeviceType == "SwitchAlarm" and _model_name == "TS0601-Solar-Siren" and ts0601_extract_data_point_infos( self, _model_name): + ts0601_actuator(self, NWKID, "TuyaAlarmSwitch", 0) + UpdateDevice_v2(self, Devices, Unit, 0, "Off", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) + return + + if DeviceType == "TamperSwitch" and ts0601_extract_data_point_infos( self, _model_name): + ts0601_actuator(self, NWKID, "TuyaTamperSwitch", 0) + UpdateDevice_v2(self, Devices, Unit, 0, "Off", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) + return if _model_name in ("TS0601-Energy",): tuya_energy_onoff(self, NWKID, "00") @@ -596,11 +584,15 @@ def mgtCommand(self, Devices, Unit, Command, Level, Color): self.iaszonemgt.iaswd_develco_warning(NWKID, EPout, "01") return - if DeviceType == "SwitchAlarm" and _model_name == "TS0601-Solar-Siren": - if ts0601_extract_data_point_infos( self, _model_name): - ts0601_actuator(self, NWKID, "TuyaAlarmSwitch", 1) - UpdateDevice_v2(self, Devices, Unit, 1, "On", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) - return + if DeviceType == "SwitchAlarm" and _model_name == "TS0601-Solar-Siren" and ts0601_extract_data_point_infos( self, _model_name): + ts0601_actuator(self, NWKID, "TuyaAlarmSwitch", 1) + UpdateDevice_v2(self, Devices, Unit, 1, "On", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) + return + + if DeviceType == "TamperSwitch" and _model_name == "TS0601-Solar-Siren" and ts0601_extract_data_point_infos( self, _model_name): + ts0601_actuator(self, NWKID, "TuyaTamperSwitch", 1) + UpdateDevice_v2(self, Devices, Unit, 1, "On", BatteryLevel, SignalLevel, ForceUpdate_=forceUpdateDev) + return if _model_name in ("TS0601-_TZE200_nklqjk62", ): self.log.logging("Command", "Debug", "mgtCommand : On for Tuya Garage Door %s" % NWKID) @@ -823,7 +815,7 @@ def mgtCommand(self, Devices, Unit, Command, Level, Color): schneider_hact_heater_type(self, NWKID, "fip") else: - Domoticz.Error("Unknown mode %s for HACTMODE for device %s" % (Level, NWKID)) + self.log.logging("Command", "Error", "Unknown mode %s for HACTMODE for device %s" % (Level, NWKID)) # Let's force a refresh of Attribute in the next Heartbeat request_read_device_status(self, NWKID) diff --git a/Modules/danfoss.py b/Modules/danfoss.py index 23de5c5d2..609e2f3fb 100644 --- a/Modules/danfoss.py +++ b/Modules/danfoss.py @@ -6,8 +6,8 @@ from Modules.basicOutputs import (raw_APS_request, read_attribute, write_attribute) -from Modules.tools import (build_fcf, get_and_inc_ZCL_SQN, getListOfEpForCluster, - is_ack_tobe_disabled) +from Modules.tools import (build_fcf, get_and_inc_ZCL_SQN, + getListOfEpForCluster, is_ack_tobe_disabled) from Modules.zigateConsts import ZIGATE_EP diff --git a/Modules/database.py b/Modules/database.py index 365d55110..8253354bd 100644 --- a/Modules/database.py +++ b/Modules/database.py @@ -12,13 +12,13 @@ import json -from pathlib import Path import os.path import time +from pathlib import Path from typing import Dict -import Domoticz import Modules.tools +from Modules.domoticzAPI import setConfigItem, getConfigItem from Modules.manufacturer_code import check_and_update_manufcode from Modules.pluginDbAttributes import (STORE_CONFIGURE_REPORTING, STORE_CUSTOM_CONFIGURE_REPORTING, @@ -256,17 +256,17 @@ def loadTxtDatabase(self, dbName): try: dlVal = eval(val) except (SyntaxError, NameError, TypeError, ZeroDivisionError): - Domoticz.Error("LoadDeviceList failed on %s" % val) + self.log.logging("Database", "Error", "LoadDeviceList failed on %s" % val) continue self.log.logging("Database", "Debug", "LoadDeviceList - " + str(key) + " => dlVal " + str(dlVal), key) if not dlVal.get("Version"): if key == "0000": # Bug fixed in later version continue - Domoticz.Error("LoadDeviceList - entry " + key + " not loaded - not Version 3 - " + str(dlVal)) + self.log.logging("Database", "Error", "LoadDeviceList - entry " + key + " not loaded - not Version 3 - " + str(dlVal)) res = "Failed" continue if dlVal["Version"] != "3": - Domoticz.Error("LoadDeviceList - entry " + key + " not loaded - not Version 3 - " + str(dlVal)) + self.log.logging("Database", "Error", "LoadDeviceList - entry " + key + " not loaded - not Version 3 - " + str(dlVal)) res = "Failed" continue else: @@ -275,23 +275,9 @@ def loadTxtDatabase(self, dbName): return res -#def loadJsonDatabase(self, dbName): -# res = "Success" -# with open(dbName, "rt") as handle: -# _listOfDevices = {} -# try: -# _listOfDevices = json.load(handle) -# except json.decoder.JSONDecodeError as e: -# res = "Failed" -# Domoticz.Error("loadJsonDatabase poorly-formed %s, not JSON: %s" % (self.pluginConf["filename"], e)) -# for key in _listOfDevices: -# CheckDeviceList(self, key, str(_listOfDevices[key])) -# return res - - def _read_DeviceList_Domoticz(self): - ListOfDevices_from_Domoticz = Modules.tools.getConfigItem(Key="ListOfDevices", Attribute="Devices") + ListOfDevices_from_Domoticz = getConfigItem(Key="ListOfDevices", Attribute="Devices") time_stamp = 0 if "TimeStamp" in ListOfDevices_from_Domoticz: time_stamp = ListOfDevices_from_Domoticz["TimeStamp"] @@ -340,10 +326,8 @@ def WriteDeviceList(self, count): # sourcery skip: merge-nested-ifs self.log.logging("Database", "Debug", "WriteDeviceList %s %s" %(self.HBcount, count)) if self.pluginconf.pluginConf["pluginData"] is None or self.DeviceListName is None: - Domoticz.Error( - "WriteDeviceList - self.pluginconf.pluginConf['pluginData']: %s , self.DeviceListName: %s" - % (self.pluginconf.pluginConf["pluginData"], self.DeviceListName) - ) + self.log.logging("Database", "Error", "WriteDeviceList - self.pluginconf.pluginConf['pluginData']: %s , self.DeviceListName: %s" % ( + self.pluginconf.pluginConf["pluginData"], self.DeviceListName)) return if self.pluginconf.pluginConf["expJsonDatabase"]: @@ -364,7 +348,7 @@ def WriteDeviceList(self, count): # sourcery skip: merge-nested-ifs def _write_DeviceList_txt(self): # Write in classic format ( .txt ) - _pluginData = Path ( self.pluginconf.pluginConf["pluginData"] ) + _pluginData = Path( self.pluginconf.pluginConf["pluginData"] ) _DeviceListFileName = _pluginData / self.DeviceListName try: self.log.logging("Database", "Debug", "Write %s = %s" % (_DeviceListFileName, str(self.ListOfDevices))) @@ -375,12 +359,12 @@ def _write_DeviceList_txt(self): except UnicodeEncodeError: self.log.logging( "Database", "Error", "UnicodeEncodeError while while saving %s : %s on file" %( - key, self.ListOfDevices[key])) + key, self.ListOfDevices[key])) continue except ValueError: self.log.logging( "Database", "Error", "ValueError while saving %s : %s on file" %( - key, self.ListOfDevices[key])) + key, self.ListOfDevices[key])) continue except IOError: @@ -397,8 +381,10 @@ def _write_DeviceList_txt(self): def _write_DeviceList_json(self): - _pluginData = Path ( self.pluginconf.pluginConf["pluginData"] ) - _DeviceListFileName = _pluginData / self.DeviceListName[:-3] + "json" + _pluginData = Path( self.pluginconf.pluginConf["pluginData"] ) +# Incorrect error issue +# _DeviceListFileName = _pluginData / self.DeviceListName[:-3] + "json" + _DeviceListFileName = _pluginData / (self.DeviceListName[:-3] + "json") self.log.logging("Database", "Debug", "Write %s = %s" % (_DeviceListFileName, str(self.ListOfDevices))) with open(_DeviceListFileName, "wt") as file: json.dump(self.ListOfDevices, file, sort_keys=True, indent=2) @@ -408,16 +394,14 @@ def _write_DeviceList_json(self): def _write_DeviceList_Domoticz(self): ListOfDevices_for_save = self.ListOfDevices.copy() self.log.logging("Database", "Log", "WriteDeviceList - flush Plugin db to %s" % "Domoticz") - return Modules.tools.setConfigItem( - Key="ListOfDevices", Attribute="Devices", Value={"TimeStamp": time.time(), "Devices": ListOfDevices_for_save} - ) + return setConfigItem( Key="ListOfDevices", Attribute="Devices", Value={"TimeStamp": time.time(), "Devices": ListOfDevices_for_save} ) def importDeviceConf(self): # Import DeviceConf.txt tmpread = "" self.DeviceConf = {} - _pluginConfig = Path ( self.pluginconf.pluginConf["pluginConfig"] ) + _pluginConfig = Path( self.pluginconf.pluginConf["pluginConfig"] ) _DeviceConf = _pluginConfig / "DeviceConf.txt" if os.path.isfile(_DeviceConf): with open(_DeviceConf, "r") as myfile: @@ -425,10 +409,8 @@ def importDeviceConf(self): try: self.DeviceConf = eval(tmpread) except (SyntaxError, NameError, TypeError, ZeroDivisionError): - Domoticz.Error( - "Error while loading %s in line : %s" - % (self.pluginconf.pluginConf["pluginConfig"] + "DeviceConf.txt", tmpread) - ) + self.log.logging("Database", "Error", "Error while loading %s in line : %s" % ( + self.pluginconf.pluginConf["pluginConfig"] + "DeviceConf.txt", tmpread) ) return # Remove comments @@ -464,11 +446,11 @@ def import_local_device_conf(self): try: model_definition = json.load(handle) except ValueError as e: - Domoticz.Error("--> JSON ConfFile: %s load failed with error: %s" % (filename, str(e))) + self.log.logging("Database", "Error","--> JSON ConfFile: %s load failed with error: %s" % (filename, str(e))) continue except Exception as e: - Domoticz.Error("--> JSON ConfFile: %s load general error: %s" % (filename, str(e))) + self.log.logging("Database", "Error","--> JSON ConfFile: %s load general error: %s" % (filename, str(e))) continue @@ -491,7 +473,7 @@ def import_local_device_conf(self): "--> Config for %s not loaded as already defined" % (str(device_model_name)), ) except Exception: - Domoticz.Error("--> Unexpected error when loading a configuration file") + self.log.logging("Database", "Error","--> Unexpected error when loading a configuration file") self.log.logging("Database", "Debug", "--> Config loaded: %s" % self.DeviceConf.keys()) @@ -559,13 +541,7 @@ def checkListOfDevice2Devices(self, Devices): NWKID, ) else: - Domoticz.Error( - "loadListOfDevices - : " - + Devices[x].Name - + " with IEEE = " - + str(ID) - + " not found in Zigate plugin Database!" - ) + self.log.logging("Database", "Error", "loadListOfDevices - : " + Devices[x].Name + " with IEEE = " + str(ID) + " not found in Zigate plugin Database!") def saveZigateNetworkData(self, nkwdata): @@ -576,7 +552,7 @@ def saveZigateNetworkData(self, nkwdata): with open(json_filename, "wt", encoding='utf-8') as json_file: json.dump(nkwdata, json_file, indent=4, sort_keys=True) except IOError: - Domoticz.Error("Error while writing Zigate Network Details%s" % json_filename) + self.log.logging("Database", "Error", "Error while writing Zigate Network Details%s" % json_filename) def CheckDeviceList(self, key, val): @@ -650,9 +626,8 @@ def CheckDeviceList(self, key, val): OldModel = self.ListOfDevices[key][attribute] self.ListOfDevices[key][attribute] = self.ListOfDevices[key][attribute].replace("/", "") if OldModel != self.ListOfDevices[key][attribute]: - Domoticz.Status( - "Model adjustement during import from %s to %s" % (OldModel, self.ListOfDevices[key][attribute]) - ) + self.log.logging("Database", "Status", "Model adjustement during import from %s to %s" % ( + OldModel, self.ListOfDevices[key][attribute])) self.ListOfDevices[key]["Health"] = "" @@ -695,7 +670,7 @@ def check_and_update_ForceAckCommands(self): if "ForceAckCommands" not in self.DeviceConf[model]: self.ListOfDevices[x]["ForceAckCommands"] = [] continue - Domoticz.Log(" Set: %s for device %s " % (self.DeviceConf[model]["ForceAckCommands"], x)) + self.log.logging("Database", "Log"," Set: %s for device %s " % (self.DeviceConf[model]["ForceAckCommands"], x)) self.ListOfDevices[x]["ForceAckCommands"] = list(self.DeviceConf[model]["ForceAckCommands"]) @@ -714,13 +689,13 @@ def fixing_Issue566(self, key): return False if "Cluster Revision" in self.ListOfDevices[key]["Ep"]: - Domoticz.Log("++++Issue #566: Fixing Cluster Revision for NwkId: %s" % key) + self.log.logging("Database", "Log", "++++Issue #566: Fixing Cluster Revision for NwkId: %s" % key) del self.ListOfDevices[key]["Ep"]["Cluster Revision"] res = True for ep in self.ListOfDevices[key]["Ep"]: if "Cluster Revision" in self.ListOfDevices[key]["Ep"][ep]: - Domoticz.Log("++++Issue #566 Cluster Revision NwkId: %s Ep: %s" % (key, ep)) + self.log.logging("Database", "Log","++++Issue #566 Cluster Revision NwkId: %s Ep: %s" % (key, ep)) del self.ListOfDevices[key]["Ep"][ep]["Cluster Revision"] res = True @@ -732,7 +707,7 @@ def fixing_Issue566(self, key): and "ClusterType" in self.ListOfDevices[key]["Ep"]["01"] and len(self.ListOfDevices[key]["Ep"]["01"]["ClusterType"]) == 0 ): - Domoticz.Log("++++Issue #566 ClusterType mixing NwkId: %s Ep 01 and 02" % key) + self.log.logging("Database", "Log","++++Issue #566 ClusterType mixing NwkId: %s Ep 01 and 02" % key) self.ListOfDevices[key]["Ep"]["01"]["ClusterType"] = dict(self.ListOfDevices[key]["Ep"]["02"]["ClusterType"]) self.ListOfDevices[key]["Ep"]["02"]["ClusterType"] = {} res = True @@ -830,7 +805,7 @@ def load_new_param_definition(self): if plugin_generic_param is None: return False - Domoticz.Log("--->PluginConf %s <-- %s" % (param, plugin_generic_param)) + self.log.logging("Database", "Log","--->PluginConf %s <-- %s" % (param, plugin_generic_param)) self.ListOfDevices[key]["Param"][param] = self.pluginconf.pluginConf[plugin_generic_param] elif param in ("OnOffPollingFreq",): @@ -856,7 +831,7 @@ def load_new_param_definition(self): if plugin_generic_param is None: return False - Domoticz.Log("--->PluginConf %s <-- %s" % (param, plugin_generic_param)) + self.log.logging("Database", "Log","--->PluginConf %s <-- %s" % (param, plugin_generic_param)) self.ListOfDevices[key]["Param"][param] = self.pluginconf.pluginConf[plugin_generic_param] elif param in ("AC201Polling",): @@ -881,7 +856,7 @@ def load_new_param_definition(self): if plugin_generic_param is None: return False - Domoticz.Log("--->PluginConf %s <-- %s" % (param, plugin_generic_param)) + self.log.logging("Database", "Log","--->PluginConf %s <-- %s" % (param, plugin_generic_param)) self.ListOfDevices[key]["Param"][param] = self.pluginconf.pluginConf[plugin_generic_param] elif param == "netatmoLedIfOn": diff --git a/Modules/deviceAnnoucement.py b/Modules/deviceAnnoucement.py index def1fceca..1249ed50a 100755 --- a/Modules/deviceAnnoucement.py +++ b/Modules/deviceAnnoucement.py @@ -13,7 +13,6 @@ from time import time -import Domoticz from Modules.casaia import restart_plugin_reset_ModuleIRCode from Modules.domoTools import lastSeenUpdate from Modules.legrand_netatmo import legrand_refresh_battery_remote @@ -325,7 +324,7 @@ def decode004d_new_devicev2(self, Devices, NwkId, MsgIEEE, MsgMacCapa, MsgData, # I wonder if this code makes sense ? ( PP 02/05/2020 ), This should not happen! if MsgIEEE in self.IEEE2NWK: - Domoticz.Error("Decode004d - New Device %s %s already exist in IEEE2NWK" % (NwkId, MsgIEEE)) + self.log.logging("DeviceAnnoucement", "Error", "Decode004d - New Device %s %s already exist in IEEE2NWK" % (NwkId, MsgIEEE)) self.log.logging( "DeviceAnnoucement", "Debug", diff --git a/Modules/domoCreate.py b/Modules/domoCreate.py index 8c5072551..09ea64763 100644 --- a/Modules/domoCreate.py +++ b/Modules/domoCreate.py @@ -10,10 +10,12 @@ import Domoticz -from Modules.domoTools import (GetType, subtypeRGB_FromProfile_Device_IDs, - subtypeRGB_FromProfile_Device_IDs_onEp2, UpdateDevice_v2) +from Modules.domoTools import (GetType, UpdateDevice_v2, + subtypeRGB_FromProfile_Device_IDs, + subtypeRGB_FromProfile_Device_IDs_onEp2) from Modules.switchSelectorWidgets import SWITCH_SELECTORS from Modules.tools import is_domoticz_new_blind +from Modules.domoticzAbstractLayer import domo_create_api, FreeUnit def cleanup_widget_Type(widget_type_list): @@ -65,41 +67,6 @@ def deviceName(self, NWKID, DeviceType, IEEE_, EP_): return devName - -def how_many_slot_available( Devices ): - return sum(x not in Devices for x in range( 1, 255 )) - - -def FreeUnit(self, Devices, nbunit_=1): - """ - FreeUnit - Look for a Free Unit number. If nbunit > 1 then we look for nbunit consecutive slots - """ - if how_many_slot_available( Devices ) <= 5: - self.log.logging("WidgetCreation", "Status", "It seems that you can create only 5 Domoticz widgets more !!!") - elif how_many_slot_available( Devices ) <= 15: - self.log.logging("WidgetCreation", "Status", "It seems that you can create only 15 Domoticz widgets more !!") - elif how_many_slot_available( Devices ) <= 30: - self.log.logging("WidgetCreation", "Status", "It seems that you can create only 30 Domoticz widgets more !") - - for x in range(1, 255): - if x not in Devices: - if nbunit_ == 1: - return x - nb = 1 - for y in range(x + 1, 255): - if y not in Devices: - nb += 1 - else: - break - if nb == nbunit_: # We have found nbunit consecutive slots - self.log.logging("WidgetCreation", "Debug", "FreeUnit - device " + str(x) + " available") - return x - - self.log.logging("WidgetCreation", "Debug", "FreeUnit - device " + str(len(Devices) + 1)) - return len(Devices) + 1 - - def createSwitchSelector(self, nbSelector, DeviceType=None, OffHidden=False, SelectorStyle=0): """ Generate an Options attribute to handle the number of required button, if Off is hidden or notand SelectorStype @@ -110,7 +77,8 @@ def createSwitchSelector(self, nbSelector, DeviceType=None, OffHidden=False, Sel """ Options = {} - # Domoticz.Log( "createSwitchSelector - nbSelector: %s DeviceType: %s OffHidden: %s SelectorStyle %s " %(nbSelector,DeviceType,OffHidden,SelectorStyle)) + self.log.logging("WidgetCreation", "Debug", "createSwitchSelector - nbSelector: %s DeviceType: %s OffHidden: %s SelectorStyle %s " %( + nbSelector,DeviceType,OffHidden,SelectorStyle)) if nbSelector <= 1: return Options @@ -136,7 +104,7 @@ def createSwitchSelector(self, nbSelector, DeviceType=None, OffHidden=False, Sel if Options["LevelNames"] != "": count = sum(map(lambda x: 1 if "|" in x else 0, Options["LevelNames"])) - # Domoticz.Log("----> How many Levels: %s" %count) + self.log.logging("WidgetCreation", "Debug", "----> How many Levels: %s" %count) for _ in range(count): Options["LevelActions"] += "|" else: @@ -153,7 +121,7 @@ def createSwitchSelector(self, nbSelector, DeviceType=None, OffHidden=False, Sel if OffHidden: Options["LevelOffHidden"] = "true" - # Domoticz.Log(" --> Options: %s" %str(Options)) + self.log.logging("WidgetCreation", "Debug", " --> Options: %s" %str(Options)) return Options @@ -165,8 +133,8 @@ def createDomoticzWidget( self, Devices, nwkid, ieee, ep, cType, widgetType=None forceClusterType if you want to overwrite the ClusterType usally based with cType """ - unit = FreeUnit(self, Devices) - self.log.logging("WidgetCreation", "Debug", "CreateDomoDevice - unit: %s" % unit, nwkid) + unit = FreeUnit(self, Devices, ieee) + self.log.logging("WidgetCreation", "Debug", "createDomoticzWidget - unit: %s" % unit, nwkid) self.log.logging( "WidgetCreation", "Debug", "--- cType: %s widgetType: %s Type: %s Subtype: %s SwitchType: %s widgetOption: %s Image: %s ForceCluster: %s" % ( cType, widgetType, Type_, Subtype_, Switchtype_, widgetOptions, Image, ForceClusterType), nwkid, ) @@ -174,41 +142,15 @@ def createDomoticzWidget( self, Devices, nwkid, ieee, ep, cType, widgetType=None widgetName = deviceName(self, nwkid, cType, ieee, ep) # oldFashionWidgetName = cType + "-" + ieee + "-" + ep - if widgetType: - # We only base the creation on widgetType - myDev = Domoticz.Device(DeviceID=ieee, Name=widgetName, Unit=unit, TypeName=widgetType) - - elif widgetOptions: - # In case of widgetOptions, we have a Selector widget - if Type_ is None and Subtype_ is None and Switchtype_ is None: - Type_ = 244 - Subtype_ = 62 - Switchtype_ = 18 - myDev = Domoticz.Device( - DeviceID=ieee, - Name=widgetName, - Unit=unit, - Type=Type_, - Subtype=Subtype_, - Switchtype=Switchtype_, - Options=widgetOptions, - ) - elif Image: - myDev = Domoticz.Device( DeviceID=ieee, Name=widgetName, Unit=unit, Type=Type_, Subtype=Subtype_, Switchtype=Switchtype_, Image=Image ) - elif Switchtype_: - myDev = Domoticz.Device( DeviceID=ieee, Name=widgetName, Unit=unit, Type=Type_, Subtype=Subtype_, Switchtype=Switchtype_ ) - else: - myDev = Domoticz.Device(DeviceID=ieee, Name=widgetName, Unit=unit, Type=Type_, Subtype=Subtype_) - - myDev.Create() - ID = myDev.ID - if myDev.ID == -1: + myDev_ID = domo_create_api(self, Devices, ieee, unit, widgetName, widgetType=widgetType, Type_=Type_, Subtype_=Subtype_, Switchtype_=Switchtype_, widgetOptions=widgetOptions, Image=Image) + + if myDev_ID == -1: self.ListOfDevices[nwkid]["Status"] = "failDB" - Domoticz.Error("Domoticz widget creation failed. Check that Domoticz can Accept New Hardware [%s]" % myDev) + Domoticz.Error("Domoticz widget creation failed. Check that Domoticz can Accept New Hardware [%s]" % myDev_ID) return None self.ListOfDevices[nwkid]["Status"] = "inDB" - self.ListOfDevices[nwkid]["Ep"][ep]["ClusterType"][str(ID)] = ( ForceClusterType or cType ) + self.ListOfDevices[nwkid]["Ep"][ep]["ClusterType"][str(myDev_ID)] = ( ForceClusterType or cType ) return unit @@ -644,10 +586,12 @@ def set_default_value( self, Devices, unit, widget_record): "ColorControlRGB": { "Type": 241, "Subtype": 1, "Switchtype": 7, }, "LvlControl": { "Type": 244, "Subtype": 73, "Switchtype": 7 }, "SwitchAlarm": { "Type": 244, "Subtype": 73, "Switchtype": 0, "Image": 13 }, + "TamperSwitch": { "Type": 244, "Subtype": 73, "Switchtype": 0, }, "Distance": { "Type": 243, "Subtype": 27, "Switchtype": 0}, "WaterCounter": { "Type": 243, "Subtype": 28, "Switchtype": 2, "Image": 22, "sValue": "0", "nValue": 0}, "GazMeter": { "Type": 251, "Subtype": 2, "Switchtype": 0, "sValue": "0", "nValue": 0}, "Counter": { "Type": 113, "Subtype": 0, "Switchtype": 0, "sValue": "0", "nValue": 0}, + "Notification": {"Type":243, "Subtype":19, "Switchtype":0,} } BLIND_DOMOTICZ_2022 = { diff --git a/Modules/domoMaj.py b/Modules/domoMaj.py index 2b52d448b..afdf24443 100644 --- a/Modules/domoMaj.py +++ b/Modules/domoMaj.py @@ -8,17 +8,16 @@ Description: Update of Domoticz Widget """ import Domoticz -from Zigbee.zdpCommands import zdp_IEEE_address_request - from Modules.domoTools import (RetreiveSignalLvlBattery, RetreiveWidgetTypeList, TypeFromCluster, UpdateDevice_v2, remove_bad_cluster_type_entry) -from Modules.tools import zigpy_plugin_sanity_check from Modules.switchSelectorWidgets import SWITCH_SELECTORS +from Modules.tools import zigpy_plugin_sanity_check from Modules.zigateConsts import THERMOSTAT_MODE_2_LEVEL from Modules.zlinky import (ZLINK_CONF_MODEL, get_instant_power, get_tarif_color, zlinky_sum_all_indexes) - +from Zigbee.zdpCommands import zdp_IEEE_address_request +from Modules.domoticzAbstractLayer import find_widget_unit_from_WidgetID def is_PowerNegative_widget( ClusterTypeList): return any( _widget_type == "ProdMeter" for _, _, _widget_type in ClusterTypeList ) @@ -116,11 +115,8 @@ def MajDomoDevice(self, Devices, NWKID, Ep, clusterID, value, Attribute_="", Col continue DeviceUnit = 0 - for x in Devices: # Found the Device Unit - if Devices[x].ID == int(WidgetId): - DeviceUnit = x - break - if DeviceUnit == 0: + DeviceUnit = find_widget_unit_from_WidgetID(self, Devices, WidgetId ) + if DeviceUnit is None: self.log.logging( "Widget", "Error", "Device %s not found !!!" % WidgetId, NWKID) # House keeping, we need to remove this bad clusterType if remove_bad_cluster_type_entry(self, NWKID, Ep, clusterID, WidgetId ): @@ -280,6 +276,29 @@ def MajDomoDevice(self, Devices, NWKID, Ep, clusterID, value, Attribute_="", Col # it is assumed that if there is also summation provided by the device, that # such information is stored on the data structuture and here we will retreive it. # value is expected as String + + if WidgetType == "Power" and (Attribute_ in ("", "050f") or clusterID == "000c"): # kWh + if (( isinstance( value, (int, float)) and value < 0) or (float(value) < 0) ) and is_PowerNegative_widget( ClusterTypeList): + self.log.logging("Widget", "Log", "------>There is a PowerNegative widget and the value is negative. Skiping here", NWKID) + UpdateDevice_v2(self, Devices, DeviceUnit, 0, "0", BatteryLevel, SignalLevel) + continue + + nValue = round(float(value), 2) + sValue = value + self.log.logging("Widget", "Debug", "------>Power : %s" % sValue, NWKID) + UpdateDevice_v2(self, Devices, DeviceUnit, nValue, str(sValue), BatteryLevel, SignalLevel) + + if WidgetType == "ProdPower" and Attribute_ == "": + if value > 0: + self.log.logging("Widget", "Debug", "------>the value is Positive. Skiping here", NWKID) + UpdateDevice_v2(self, Devices, DeviceUnit, 0, "0", BatteryLevel, SignalLevel) + continue + + nValue = abs( round(float(value), 2) ) + sValue = abs(value) + self.log.logging("Widget", "Debug", "------>PowerNegative : %s" % sValue, NWKID) + UpdateDevice_v2(self, Devices, DeviceUnit, nValue, str(sValue), BatteryLevel, SignalLevel) + if WidgetType == "P1Meter" and Attribute_ == "0000": self.log.logging("Widget", "Debug", "------> P1Meter : %s (%s)" % (value, type(value)), NWKID) # P1Meter report Instant and Cummulative Power. @@ -392,7 +411,6 @@ def MajDomoDevice(self, Devices, NWKID, Ep, clusterID, value, Attribute_="", Col sValue = "%s" %int(value) UpdateDevice_v2(self, Devices, DeviceUnit, 0, sValue, BatteryLevel, SignalLevel) - elif WidgetType == "ConsoMeter" and Attribute_ == "0000": # Consummed Energy sValue = "%s" %int(value) @@ -435,8 +453,7 @@ def MajDomoDevice(self, Devices, NWKID, Ep, clusterID, value, Attribute_="", Col self.log.logging("Widget", "Debug", "------> : " + sValue) UpdateDevice_v2(self, Devices, DeviceUnit, 0, sValue, BatteryLevel, SignalLevel) - - if (WidgetType == "Meter" and Attribute_ == "") or (WidgetType == "Power" and clusterID == "000c"): # kWh + elif (WidgetType == "Meter" and Attribute_ == "") or (WidgetType == "Power" and clusterID == "000c"): # kWh # We receive Instant # Let's check if we have Summation in the datastructutre summation = 0 @@ -465,12 +482,6 @@ def MajDomoDevice(self, Devices, NWKID, Ep, clusterID, value, Attribute_="", Col self.log.logging("Widget", "Debug", "------> : " + sValue) UpdateDevice_v2(self, Devices, DeviceUnit, 0, sValue, BatteryLevel, SignalLevel) - elif WidgetType == "ProdMeter" and Attribute_ == "0001": - # Produced Energy injected - sValue = "%s" %int(value) - UpdateDevice_v2(self, Devices, DeviceUnit, 0, sValue, BatteryLevel, SignalLevel) - - if "WaterCounter" in ClusterType and WidgetType == "WaterCounter": # /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=INCREMENT # INCREMENT = Integer of the increment of the counter. @@ -994,7 +1005,19 @@ def MajDomoDevice(self, Devices, NWKID, Ep, clusterID, value, Attribute_="", Col sValue = "%02x" %nValue UpdateDevice_v2(self, Devices, DeviceUnit, nValue, sValue, BatteryLevel, SignalLevel) - + if ClusterType == "TamperSwitch" and WidgetType == "SwitchAlarm": + nValue = value + sValue = "%02x" %nValue + UpdateDevice_v2(self, Devices, DeviceUnit, nValue, sValue, BatteryLevel, SignalLevel) + + if "Notification" in ClusterType and WidgetType == "Notification": + # Notification + # value is a str containing all Orientation information to be updated on Text Widget + nValue = 0 + sValue = value + UpdateDevice_v2(self, Devices, DeviceUnit, nValue, sValue, BatteryLevel, SignalLevel, ForceUpdate_=True) + + if ClusterType in ( "Motion", "Door",) and WidgetType == "Motion": self.log.logging("Widget", "Debug", "------> Motion %s" % (value), NWKID) if isinstance(value, str): diff --git a/Modules/domoTools.py b/Modules/domoTools.py index 34ef560a6..4c3b66640 100644 --- a/Modules/domoTools.py +++ b/Modules/domoTools.py @@ -11,12 +11,12 @@ import time -import Domoticz from Modules.switchSelectorWidgets import SWITCH_SELECTORS from Modules.tools import (is_domoticz_touch, is_domoticz_update_SuppressTriggers, lookupForIEEE, removeDeviceInList) from Modules.zigateConsts import THERMOSTAT_MODE_2_LEVEL +from Modules.domoticzAbstractLayer import domo_update_api, device_touch_api, timeout_widget_api def RetreiveWidgetTypeList(self, Devices, NwkId, DeviceUnit=None): @@ -265,26 +265,18 @@ def resetSwitchSelectorPushButton( self, Devices, NwkId, WidgetType, unit, Signa self.log.logging( "Widget", "Debug", "Last update of the devices %s WidgetType: %s was %s ago" % (unit, WidgetType, (now - lastupdate)), NwkId, ) # Domoticz.Log(" Update nValue: %s sValue: %s" %(nValue, sValue)) - def UpdateDevice_v2(self, Devices, Unit, nValue, sValue, BatteryLvl, SignalLvl, Color_="", ForceUpdate_=False): if Unit not in Devices: - Domoticz.Error("Droping Update to Device due to Unit %s not found" % Unit) + self.log.logging("Widget", "Error", "Droping Update to Device due to Unit %s not found" % Unit) return if Devices[Unit].DeviceID not in self.IEEE2NWK: - Domoticz.Error( - "Droping Update to Device due to DeviceID %s not found in IEEE2NWK %s" - % (Devices[Unit].DeviceID, str(self.IEEE2NWK)) - ) + self.log.logging("Widget", "Error", "Droping Update to Device due to DeviceID %s not found in IEEE2NWK %s" % ( + Devices[Unit].DeviceID, str(self.IEEE2NWK)) ) return - self.log.logging( - "Widget", - "Debug", - "UpdateDevice_v2 %s:%s:%s %3s:%3s:%5s (%15s)" - % (nValue, sValue, Color_, BatteryLvl, SignalLvl, ForceUpdate_, Devices[Unit].Name), - self.IEEE2NWK[Devices[Unit].DeviceID], - ) + self.log.logging( "Widget", "Debug", "UpdateDevice_v2 %s:%s:%s %3s:%3s:%5s (%15s)" % ( + nValue, sValue, Color_, BatteryLvl, SignalLvl, ForceUpdate_, Devices[Unit].Name), self.IEEE2NWK[Devices[Unit].DeviceID], ) # Make sure that the Domoticz device still exists (they can be deleted) before updating it if Unit not in Devices: @@ -299,6 +291,7 @@ def UpdateDevice_v2(self, Devices, Unit, nValue, sValue, BatteryLvl, SignalLvl, or Devices[Unit].TimedOut ): + DeviceID_ = None # This is required when we will use The Extended Framework if ( self.pluginconf.pluginConf["forceSwitchSelectorPushButton"] and ForceUpdate_ @@ -313,36 +306,14 @@ def UpdateDevice_v2(self, Devices, Unit, nValue, sValue, BatteryLvl, SignalLvl, LevelOffHidden = Devices[Unit].Options["LevelOffHidden"] if LevelOffHidden == "false": sReset = "00" - Devices[Unit].Update(nValue=nReset, sValue=sReset) - - if Color_: - Devices[Unit].Update( - nValue=int(nValue), - sValue=str(sValue), - Color=Color_, - SignalLevel=int(SignalLvl), - BatteryLevel=int(BatteryLvl), - TimedOut=0, - ) - else: - Devices[Unit].Update( - nValue=int(nValue), - sValue=str(sValue), - SignalLevel=int(SignalLvl), - BatteryLevel=int(BatteryLvl), - TimedOut=0, - ) + domo_update_api(self, Devices, DeviceID_, Unit, nReset, sReset) - if self.pluginconf.pluginConf["logDeviceUpdate"]: - Domoticz.Log("UpdateDevice - (%15s) %s:%s" % (Devices[Unit].Name, nValue, sValue)) - self.log.logging( - "Widget", - "Debug", - "---> [Unit: %s] %s:%s:%s %s:%s %s (%15s)" - % (Unit, nValue, sValue, Color_, BatteryLvl, SignalLvl, ForceUpdate_, Devices[Unit].Name), - self.IEEE2NWK[Devices[Unit].DeviceID], - ) + domo_update_api(self, Devices, DeviceID_, Unit, nValue, sValue, SignalLevel=SignalLvl, BatteryLevel=BatteryLvl, TimedOut=0, Color=Color_,) + if self.pluginconf.pluginConf["logDeviceUpdate"]: + self.log.logging( "Widget", "Log", "UpdateDevice - (%15s) %s:%s" % (Devices[Unit].Name, nValue, sValue)) + self.log.logging( "Widget", "Debug", "---> [Unit: %s] %s:%s:%s %s:%s %s (%15s)" % ( + Unit, nValue, sValue, Color_, BatteryLvl, SignalLvl, ForceUpdate_, Devices[Unit].Name), self.IEEE2NWK[Devices[Unit].DeviceID], ) def Update_Battery_Device( self, Devices, NwkId, BatteryLvl, ): @@ -375,14 +346,18 @@ def Update_Battery_Device( self, Devices, NwkId, BatteryLvl, ): def timedOutDevice(self, Devices, Unit=None, NwkId=None, MarkTimedOut=True): + self.log.logging( "Widget", "Debug", "timedOutDevice unit %s nwkid: %s MarkTimedOut: %s" % ( + Unit, NwkId, MarkTimedOut), NwkId, ) + _Unit = _nValue = _sValue = None if Unit: + DeviceID = None if MarkTimedOut and not Devices[Unit].TimedOut: - timeout_widget(self, Devices, Unit, 1) + timeout_widget_api(self, Devices, DeviceID, Unit, 1) elif not MarkTimedOut and Devices[Unit].TimedOut: - timeout_widget(self, Devices, Unit, 0) + timeout_widget_api(self, Devices, DeviceID, Unit, 0) elif NwkId: if NwkId not in self.ListOfDevices: @@ -400,48 +375,14 @@ def timedOutDevice(self, Devices, Unit=None, NwkId=None, MarkTimedOut=True): if Devices[x].TimedOut: if MarkTimedOut: continue - timeout_widget(self, Devices, x, 0) - self.log.logging( - "Widget", - "Debug", - "reset timedOutDevice unit %s nwkid: %s " % (Devices[x].Name, NwkId), - NwkId, - ) + timeout_widget_api(self, Devices, _IEEE, x, 0) + self.log.logging( "Widget", "Debug", "reset timedOutDevice unit %s nwkid: %s " % ( + Devices[x].Name, NwkId), NwkId, ) elif MarkTimedOut: - timeout_widget(self, Devices, x, 1) - self.log.logging( - "Widget", - "Debug", - "timedOutDevice unit %s nwkid: %s " % (Devices[x].Name, NwkId), - NwkId, - ) - - -def timeout_widget(self, Devices, unit, timeout_value): - - if is_meter_widget( self, Devices, unit): - return - - _nValue = Devices[unit].nValue - _sValue = Devices[unit].sValue - self.log.logging("Widget", "Log", "timeout_widget unit %s -> %s from %s:%s" % (Devices[unit].Name, bool(timeout_value), _nValue, _sValue)) - if Devices[unit].TimedOut != timeout_value: - # Update is required - if ( - timeout_value == 1 - and self.pluginconf.pluginConf["deviceOffWhenTimeOut"] - and ( - (_nValue == 1 and _sValue == "On") - or (Devices[unit].Type == 244 and Devices[unit].SubType == 73 and Devices[unit].SwitchType == 7) - or (Devices[unit].Type == 241 and Devices[unit].SwitchType == 7) - ) - ): - # Then we will switch off as per User setting - Devices[unit].Update(nValue=0, sValue="Off", TimedOut=timeout_value) - else: - Devices[unit].Update(nValue=_nValue, sValue=_sValue, TimedOut=timeout_value) - self.log.logging("Widget", "Debug", "timeout_widget unit %s -> %s " % (Devices[unit].Name, bool(timeout_value))) + timeout_widget_api(self, Devices, _IEEE, x, 1) + self.log.logging( "Widget", "Debug", "timedOutDevice unit %s nwkid: %s " % ( + Devices[x].Name, NwkId), NwkId, ) def lastSeenUpdate(self, Devices, Unit=None, NwkId=None): @@ -460,7 +401,7 @@ def lastSeenUpdate(self, Devices, Unit=None, NwkId=None): if Devices[Unit].TimedOut: timedOutDevice(self, Devices, Unit=Unit, MarkTimedOut=0) else: - device_touch( self, Devices, Unit) + device_touch_api( self, Devices, IEEE, Unit) if NwkId is None and "IEEE" in self.IEEE2NWK: NwkId = self.IEEE2NWK[IEEE] @@ -489,28 +430,8 @@ def lastSeenUpdate(self, Devices, Unit=None, NwkId=None): if Devices[x].TimedOut: timedOutDevice(self, Devices, Unit=x, MarkTimedOut=0) else: - device_touch( self, Devices, x) - - -def is_meter_widget( self, Devices, unit): - _Type = Devices[unit].Type - _Switchtype = Devices[unit].SwitchType - _Subtype = Devices[unit].SubType + device_touch_api( self, Devices, _IEEE, x) - if _Switchtype == 0 and _Subtype == 29 and _Type == 243: - return True - return False - -def device_touch( self, Devices, 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, unit): - return - - Devices[unit].Touch() def GetType(self, Addr, Ep): Type = "" @@ -645,7 +566,9 @@ def GetType(self, Addr, Ep): "fc40": "ThermoMode", "ff66": "DEMAIN", "fc80": "Heiman", - "Distance": "Distance" + "Distance": "Distance", + "TamperSwitch": "TamperSwitch", + "Notification": "Notification" } def TypeFromCluster(self, cluster, create_=False, ProfileID_="", ZDeviceID_="", ModelName=""): @@ -722,9 +645,8 @@ def subtypeRGB_FromProfile_Device_IDs(EndPoints, Model, ProfileID, ZDeviceID, Co ZLL_Commissioning = False ColorMode = 0 - if ColorInfos: - if "ColorMode" in ColorInfos: - ColorMode = ColorInfos["ColorMode"] + if ColorInfos and "ColorMode" in ColorInfos: + ColorMode = ColorInfos["ColorMode"] for iterEp in EndPoints: if "1000" in iterEp: @@ -732,14 +654,12 @@ def subtypeRGB_FromProfile_Device_IDs(EndPoints, Model, ProfileID, ZDeviceID, Co break # Device specifics section - if Model: - if Model == "lumi.light.aqcn02": # Aqara Bulb White Dim - Subtype = ColorControlWW + if Model and Model == "lumi.light.aqcn02": + Subtype = ColorControlWW # Philipps Hue - if Subtype is None and ProfileID == "a1e0": - if ZDeviceID == "0061": - Subtype = ColorControlRGBWW + if Subtype is None and ProfileID == "a1e0" and ZDeviceID == "0061": + Subtype = ColorControlRGBWW # ZLL LightLink if Subtype is None and ProfileID == "c05e": @@ -823,5 +743,4 @@ def remove_all_widgets( self, Devices, NwkId): def update_model_name( self, nwkid, new_model ): - self.ListOfDevices[ nwkid ]["Model"] = new_model \ No newline at end of file diff --git a/Modules/domoticzAPI.py b/Modules/domoticzAPI.py new file mode 100644 index 000000000..234f326ca --- /dev/null +++ b/Modules/domoticzAPI.py @@ -0,0 +1,73 @@ + +import Domoticz + +# Configuration Helpers +def setConfigItem(Key=None, Attribute="", Value=None): + + Config = {} + if not isinstance(Value, (str, int, float, bool, bytes, bytearray, list, dict)): + Domoticz.Error("setConfigItem - A value is specified of a not allowed type: '" + str(type(Value)) + "'") + return Config + + if isinstance(Value, dict): + # There is an issue that Configuration doesn't allow None value in dictionary ! + # Replace none value to 'null' + Value = prepare_dict_for_storage(Value, Attribute) + + try: + Config = Domoticz.Configuration() + if Key is None: + Config = Value # set whole configuration if no key specified + else: + Config[Key] = Value + + Config = Domoticz.Configuration(Config) + except Exception as inst: + Domoticz.Error("setConfigItem - Domoticz.Configuration operation failed: '" + str(inst) + "'") + return None + return Config + + +def getConfigItem(Key=None, Attribute="", Default=None): + + Domoticz.Log("Loading %s - %s from Domoticz sqlite Db" %( Key, Attribute)) + + if Default is None: + Default = {} + Value = Default + try: + Config = Domoticz.Configuration() + Value = Config if Key is None else Config[Key] + except KeyError: + Value = Default + except Exception as inst: + Domoticz.Error( + "getConfigItem - Domoticz.Configuration read failed: '" + + str(inst) + + "'" + ) + + return repair_dict_after_load(Value, Attribute) + + +def prepare_dict_for_storage(dict_items, Attribute): + + from base64 import b64encode + + if Attribute in dict_items: + dict_items[Attribute] = b64encode(str(dict_items[Attribute]).encode("utf-8")) + dict_items["Version"] = 1 + return dict_items + + +def repair_dict_after_load(b64_dict, Attribute): + if b64_dict in ("", {}): + return {} + if "Version" not in b64_dict: + Domoticz.Log("repair_dict_after_load - Not supported storage") + return {} + if Attribute in b64_dict: + from base64 import b64decode + + b64_dict[Attribute] = eval(b64decode(b64_dict[Attribute])) + return b64_dict diff --git a/Modules/domoticzAbstractLayer.py b/Modules/domoticzAbstractLayer.py new file mode 100644 index 000000000..47a39499c --- /dev/null +++ b/Modules/domoticzAbstractLayer.py @@ -0,0 +1,358 @@ +#!/usr/bin/env python3 +# coding: utf-8 -*- +# +# Author: pipiche38 +# +""" + Module: domoAbstractLayer.py + Description: Set of functions which abstract Domoticz Legacy and Extended framework API +""" + +import Domoticz +DOMOTICZ_EXTENDED_API = False + +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. + + Args: + Devices (dictionary): Devices dictionary provided by the Domoticz framework + """ + + for x in list(Devices): + if DOMOTICZ_EXTENDED_API: + for y in list(Devices[x].Units): + self.log.logging( "AbstractDz", "Debug", "Loading Devices[%s].Units[%s]: %s" % ( + x, y, Devices[x].Units[y].Name) ) + self.ListOfDomoticzWidget[ x ] = { + "Name": Devices[x].Units[y].Name, + "Unit": y, + "DeviceID": Devices[x].Units[y].DeviceID, + "Switchtype": Devices[x].Units[y].SwitchType, + "Subtype": Devices[x].Units[y].SubType, + } + else: + # Legacy + self.ListOfDomoticzWidget[ Devices[x].ID ] = { + "Name": Devices[x].Name, + "Unit": x, + "DeviceID": Devices[x].DeviceID, + "Switchtype": Devices[x].SwitchType, + "Subtype": Devices[x].SubType, + } + + self.log.logging( "AbstractDz", "Debug", "Loading Devices[%s]: %s" % ( + Devices[x].ID,str(self.ListOfDomoticzWidget[ Devices[x].ID] )) ) + + +def find_widget_unit_from_WidgetID(self, Devices, WidgetID ): + """Find the Widget Unit with Legay framework, the tuple ( DeviceID, Unit ) with the Extended Framework + + Args: + Devices (dict): Devices dictionary provided by the Domoticz framework + WidgetID (str): Domoticz Widget Idx, usally store in the "ClusterType" attribute associated to each Ep + Should be used in domoMaj, when looking for the 'DeviceUnit' + + Returns: + _type_: Widget Unit with Legay framework, the tuple ( DeviceID, Unit ) with the Extended Framework + In legacy 'DeviceUnit' will be a Number, while in Extended, it will be a Tupple of DeviceID and Unit + + """ + + #self.log.logging( "AbstractDz", "Debug", "find_widget_unit - WidgetId: %s (%s)" % (WidgetID, type(WidgetID))) + WidgetID = int(WidgetID) + if WidgetID in self.ListOfDomoticzWidget: + self.log.logging( "AbstractDz", "Debug", "- Found in ListOfDomoticzWidget" ) + if DOMOTICZ_EXTENDED_API: + # TO-DO + self.log.logging( "AbstractDz", "Error", "find_widget_unit() Extended Framework Not IMPLEMENTED") + return None + + else: + #Legacy + self.log.logging( "AbstractDz", "Debug", "- returning %s (%s)" %( + self.ListOfDomoticzWidget[WidgetID]['Unit'], type(self.ListOfDomoticzWidget[WidgetID]['Unit']))) + return self.ListOfDomoticzWidget[WidgetID]['Unit'] + + self.log.logging( "AbstractDz", "Log", "- Not Found in ListOfDomoticzWidget, looking the old way" ) + # In case it is not found with the new way, let's keep the old way + # TO-DO: Remove + + for x in list(Devices): + if DOMOTICZ_EXTENDED_API: + for y in list(Devices[x].Units): + if Devices[x].Units[y].ID == WidgetID: + return ( x, y ) + + elif Devices[x].ID == WidgetID: + return x + return None + + +def how_many_slot_available( Devices, DeviceId=None): + """Return the number of unit slot available + + Args: + Devices (dictionary): Devices dictionary provided by the Domoticz framework + DeviceId (str, optional): DeviceID (ieee). Defaults to None (means Legacy framework) + + Returns: + int: number of available unit slot + """ + # If DeviceId is None, then we are in Legacy mode + if DeviceId is None: + return sum(x not in Devices for x in range( 1, 255 )) + + if DeviceId in Devices: + # Look for how many entries left for this specific DeviceID ( IEEE ) + return sum( y not in Devices[ DeviceId ].Units[ y ] for y in range(1, 255) ) + + return None + + +def FreeUnit(self, Devices, DeviceId, nbunit_=1): + """Look for a Free Unit number. If nbunit > 1 then we look for nbunit consecutive slots + + Args: + Devices (dictionary): Devices dictionary provided by the Domoticz framework + DeviceId (str): DeviceID (ieee). Defaults to None (means Legacy framework) + nbunit_ (int, optional): Number of consecutive unit required. Defaults to 1. + + Returns: + int: unit number + """ + + + def _log_message(count): + messages = { + 5: "It seems that you can create only 5 Domoticz widgets more !!!", + 15: "It seems that you can create only 15 Domoticz widgets more !!", + 30: "It seems that you can create only 30 Domoticz widgets more !", + } + message = messages.get(255 - count) + if message: + self.log.logging("AbstractDz", "Status", message) + + def _free_unit_in_device( list_of_units, nbunit_): + for x in range(1, 255): + if x not in available_units: + if nbunit_ == 1: + self.log.logging("AbstractDz", "Debug", "_free_unit_in_device - device %s unit" %str(x)) + return x + nb = 1 + for y in range(x + 1, 255): + if y not in available_units: + nb += 1 + else: + break + if nb == nbunit_: # We have found nbunit consecutive slots + self.log.logging("AbstractDz", "Debug", "_free_unit_in_device - device %s unit" %str(x)) + return x + return None + + if DOMOTICZ_EXTENDED_API: + available_units = set(Devices[DeviceId].Units.keys()) + return _free_unit_in_device( available_units, nbunit_ ) + + # Legacy framework + available_units = set(Devices.keys()) + _log_message(len(available_units) + 1) + return _free_unit_in_device( available_units, nbunit_ ) + + +def domo_create_api(self, Devices, DeviceID_, Unit_, Name_, widgetType=None, Type_=None, Subtype_=None, Switchtype_=None, widgetOptions=None, Image=None): + """abstract layer to be used for Legacy or Extended framework in order to create a Domoticz Widget + + Args: + Devices (dictionary): Devices dictionary provided by the Domoticz framework + DeviceID_ (str): DeviceID (ieee). Defaults to None (means Legacy framework) + Unit_ (_type_): Unit number found with FreeUnit() + Name_ (str): Widget name + widgetType (str, optional): _description_. Defaults to None. + Type_ (int, optional): Device Type. Defaults to None. + Subtype_ (int, optional): device subtype . Defaults to None. + Switchtype_ (int, optional): device switchtype . Defaults to None. + widgetOptions (dict, optional): Device options. Defaults to None. + Image (int, optional): image number. Defaults to None. + + Returns: + _type_: return the Domoticz Widget IDX + """ + + # Create the device + self.log.logging("AbstractDz", "Debug", "domo_create_api DeviceID: %s,Name: %s,Unit: %s,TypeName: %s,Type: %s,Subtype: %s,Switchtype: %s, widgetOptions= %s, Image: %s" %( + DeviceID_, Name_, Unit_, widgetType, Type_, Subtype_, Switchtype_, widgetOptions, Image,)) + + # Determine the correct class to use based on the API type + domoticz_device_api_class = Domoticz.Unit if DOMOTICZ_EXTENDED_API else Domoticz.Device + + # Define default values if necessary + if widgetOptions is None: + widgetOptions = {} + + if widgetType: + self.log.logging("AbstractDz", "Debug", "- based on widgetType %s" %widgetType) + myDev = domoticz_device_api_class( DeviceID=DeviceID_, Name=Name_, Unit=Unit_, TypeName=widgetType, ) + + elif widgetOptions: + # In case of widgetOptions, we have a Selector widget + self.log.logging("AbstractDz", "Debug", "- based on widgetOptions %s" %widgetOptions) + if Type_ is None and Subtype_ is None and Switchtype_ is None: + Type_ = 244 + Subtype_ = 62 + Switchtype_ = 18 + myDev = domoticz_device_api_class( + DeviceID=DeviceID_, + Name=Name_, + Unit=Unit_, + Type=Type_, + Subtype=Subtype_, + Switchtype=Switchtype_, + Options=widgetOptions,) + + elif Image: + self.log.logging("AbstractDz", "Debug", "- based on Image %s" %Image) + myDev = domoticz_device_api_class( DeviceID=DeviceID_, Name=Name_, Unit=Unit_, Type=Type_, Subtype=Subtype_, Switchtype=Switchtype_, Image=Image, ) + + elif Switchtype_: + self.log.logging("AbstractDz", "Debug", "- based on Switchtype_ %s" %Switchtype_) + myDev = domoticz_device_api_class( DeviceID=DeviceID_, Name=Name_, Unit=Unit_, Type=Type_, Subtype=Subtype_, Switchtype=Switchtype_) + + else: + self.log.logging("AbstractDz", "Debug", "- default") + myDev = domoticz_device_api_class( DeviceID=DeviceID_, Name=Name_, Unit=Unit_, Type=Type_, Subtype=Subtype_, ) + + + myDev.Create() + + if DOMOTICZ_EXTENDED_API: + self.log.logging("AbstractDz", "Debug", "domo_create_api status %s" %Devices[DeviceID_].Units[Unit_].ID) + return Devices[DeviceID_].Units[Unit_].ID + + self.log.logging("AbstractDz", "Debug", "domo_create_api status %s" %myDev.ID) + return myDev.ID + + +def domo_update_api(self, Devices, DeviceID_, Unit_, nValue, sValue, SignalLevel=None, BatteryLevel=None, TimedOut=None, Color="",): + """ + Does a widget (domoticz device) value update ( nValue,sValue, Color, Battery and Signal Level) + Calls from UpdateDevice_v2 + Args: + Devices (dictionary): Devices dictionary provided by the Domoticz framework + DeviceID_ (str): DeviceID (ieee). Defaults to None (means Legacy framework) + Unit_ (int): Unit number found with FreeUnit() + nValue (int): numeric Value + sValue (str): String Value + SignalLevel (int, optional): Signal Level. Defaults to None. + BatteryLevel (int, optional): Battery Level 255 for main powered devices . Defaults to None. + TimedOut (int, optional): Timeoud flag 0 to unset the Timeout. Defaults to None. + Color (str, optional): Color . Defaults to "". + """ + self.log.logging("AbstractDz", "Debug", "domo_update_api: %s %s %s %s %s %s %s %s" %( + DeviceID_, Unit_, nValue, sValue, SignalLevel, BatteryLevel, TimedOut, Color)) + + if DOMOTICZ_EXTENDED_API: + Devices[DeviceID_].Units[Unit_].nValue = nValue + Devices[DeviceID_].Units[Unit_].sValue = sValue + + if Color != "": + Devices[DeviceID_].Units[Unit_].Color = Color + Devices[DeviceID_].Units[Unit_].TimedOut = 0 + if BatteryLevel is not None: + Devices[DeviceID_].Units[Unit_].BatteryLevel = BatteryLevel + Devices[DeviceID_].Units[Unit_].TimedOut = 0 + if SignalLevel is not None: + Devices[DeviceID_].Units[Unit_].SignalLevel = SignalLevel + Devices[DeviceID_].Units[Unit_].TimedOut = 0 + if TimedOut is not None: + Devices[DeviceID_].Units[Unit_].TimedOut = TimedOut + + Devices[DeviceID_].Units[Unit_].Update(Log=True) + return + + # Legacy + if TimedOut: + Devices[Unit_].Update(nValue=nValue, sValue=sValue, TimedOut=TimedOut,) + + elif SignalLevel is None and BatteryLevel is None: + Devices[Unit_].Update(nValue=nValue, sValue=sValue, TimedOut=0,) + + elif Color != "": + Devices[Unit_].Update( nValue=int(nValue), sValue=str(sValue), Color=Color, SignalLevel=int(SignalLevel), BatteryLevel=int(BatteryLevel), TimedOut=0, ) + + else: + Devices[Unit_].Update( nValue=int(nValue), sValue=str(sValue), SignalLevel=int(SignalLevel), BatteryLevel=int(BatteryLevel), TimedOut=0, ) + + +def _is_meter_widget( self, Devices,DeviceID_, Unit_): + # self.log.logging("AbstractDz", "Debug", "_is_meter_widget: %s %s" %(DeviceID_, Unit_)) + if DOMOTICZ_EXTENDED_API: + return ( + Devices[DeviceID_].Units[Unit_].SwitchType == 0 + and Devices[DeviceID_].Units[Unit_].SubType == 29 + and Devices[DeviceID_].Units[Unit_].Type == 243 + ) + return ( + Devices[Unit_].SwitchType == 0 + and Devices[Unit_].SubType == 29 + and Devices[Unit_].Type == 243 + ) + + +def _is_device_tobe_switched_off(self, Devices,DeviceID_, Unit_): + self.log.logging("AbstractDz", "Debug", "is_device_tobe_switched_off: %s %s" %(DeviceID_, Unit_)) + if DOMOTICZ_EXTENDED_API: + return ( + (Devices[DeviceID_].Units[Unit_].Type == 244 and Devices[DeviceID_].Units[Unit_].SubType == 73 and Devices[DeviceID_].Units[Unit_].SwitchType == 7) + or (Devices[DeviceID_].Units[Unit_].Type == 241 and Devices[DeviceID_].Units[Unit_].SwitchType == 7) + ) + return ( + (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 + # Type, Subtype, SwitchType + # 243|29|0 + + if _is_meter_widget( self, Devices, DeviceId_, Unit_): + return + if DOMOTICZ_EXTENDED_API: + Devices[DeviceId_].Units[Unit_].Touch() + return + # Legacy + Devices[Unit_].Touch() + + +def timeout_widget_api(self, Devices, DeviceId_, Unit_, timeout_value): + if _is_meter_widget( self, Devices, DeviceId_, Unit_): + return + + if DOMOTICZ_EXTENDED_API: + _nValue = Devices[DeviceId_].Units[Unit_].nValue + _sValue = Devices[DeviceId_].Units[Unit_].sValue + _TimedOut = Devices[DeviceId_].Units[Unit_].TimedOut + else: + _nValue = Devices[Unit_].nValue + _sValue = Devices[Unit_].sValue + _TimedOut = Devices[Unit_].TimedOut + + self.log.logging("Widget", "Debug", "timeout_widget unit %s -> %s from %s:%s %s" % ( + Devices[Unit_].Name, bool(timeout_value), _nValue, _sValue, Devices[Unit_].TimedOut)) + + if _TimedOut != timeout_value: + # Update is required + if ( + timeout_value == 1 + and self.pluginconf.pluginConf["deviceOffWhenTimeOut"] + and ( (_nValue == 1 and _sValue == "On") or _is_device_tobe_switched_off(self, Devices,DeviceId_, Unit_) ) + ): + # Then we will switch off as per User setting + domo_update_api(self, Devices, DeviceId_, Unit_, 0, "Off", TimedOut=timeout_value) + else: + domo_update_api(self, Devices, DeviceId_, Unit_, _nValue, _sValue, TimedOut=timeout_value) + self.log.logging("Widget", "Debug", "timeout_widget DeviceId %s unit %s -> %s completed" % (DeviceId_, Unit_, bool(timeout_value))) diff --git a/Modules/enki.py b/Modules/enki.py index f60067b2a..7fd601cdd 100644 --- a/Modules/enki.py +++ b/Modules/enki.py @@ -3,8 +3,6 @@ # # Author: zaraki673 & pipiche38 # -import Domoticz - from Modules.basicOutputs import set_poweron_afteroffon from Modules.readAttributes import ReadAttributeRequest_0006_400x @@ -15,7 +13,7 @@ def enki_set_poweron_after_offon(self, mode): # call from WebServer if mode not in ENKI_POWERON_MODE: - Domoticz.Error("enki_set_poweron_after_offon - Unknown mode: %s" % mode) + self.log.logging("Enki", "Error", "enki_set_poweron_after_offon - Unknown mode: %s" % mode) for nwkid in self.ListOfDevices: enki_set_poweron_after_offon_device(self, mode, nwkid) @@ -32,13 +30,12 @@ def enki_set_poweron_after_offon_device(self, mode, nwkid): if "0006" not in self.ListOfDevices[nwkid]["Ep"]["01"]: return if "4003" not in self.ListOfDevices[nwkid]["Ep"]["01"]["0006"]: - Domoticz.Log("enki_set_poweron_after_offon Device: %s do not have a Set Power Attribute !" % nwkid) + self.log.logging("Enki", "Debug", "enki_set_poweron_after_offon Device: %s do not have a Set Power Attribute !" % nwkid) ReadAttributeRequest_0006_400x(self, nwkid) return # At that stage, we have a Philips device with Cluster 0006 and the right attribute - Domoticz.Log( - "enki_set_poweron_after_offon - Set PowerOn after OffOn of %s to %s" % (nwkid, ENKI_POWERON_MODE[mode]) - ) + self.log.logging("Enki", "Debug", "enki_set_poweron_after_offon - Set PowerOn after OffOn of %s to %s" % ( + nwkid, ENKI_POWERON_MODE[mode]) ) set_poweron_afteroffon(self, nwkid, OnOffMode=mode) ReadAttributeRequest_0006_400x(self, nwkid) diff --git a/Modules/errorCodes.py b/Modules/errorCodes.py index 45ecd5527..40d8f9ab5 100644 --- a/Modules/errorCodes.py +++ b/Modules/errorCodes.py @@ -115,7 +115,6 @@ def DisplayStatusCode(StatusCode): - StatusMsg = "" if StatusCode in ZIGATE_CODES: return "[%s] %s" % (StatusCode, ZIGATE_CODES[StatusCode]) diff --git a/Modules/gledopto.py b/Modules/gledopto.py index f8818db32..a9d8b9ce2 100644 --- a/Modules/gledopto.py +++ b/Modules/gledopto.py @@ -3,7 +3,6 @@ # # Author: zaraki673 & pipiche38 # -import Domoticz from Modules.readAttributes import (ReadAttributeRequest_0006_0000, ReadAttributeRequest_0008_0000) @@ -34,4 +33,5 @@ def callbackDeviceAwake_Gledopto(self, Devices, NwkId, EndPoint, cluster): The function is called after processing the readCluster part """ - Domoticz.Log("callbackDeviceAwake_Legrand - Nwkid: %s, EndPoint: %s cluster: %s" % (NwkId, EndPoint, cluster)) + self.log.logging("Gledopto", "Debug", "callbackDeviceAwake_Legrand - Nwkid: %s, EndPoint: %s cluster: %s" % ( + NwkId, EndPoint, cluster)) diff --git a/Modules/heartbeat.py b/Modules/heartbeat.py index 9c70c7d1a..df1048849 100755 --- a/Modules/heartbeat.py +++ b/Modules/heartbeat.py @@ -13,7 +13,6 @@ import datetime import time -import Domoticz from Modules.basicOutputs import getListofAttribute from Modules.casaia import pollingCasaia from Modules.danfoss import danfoss_room_sensor_polling @@ -710,10 +709,8 @@ def processKnownDevices(self, Devices, NWKID): if READ_ATTRIBUTES_REQUEST[Cluster][1] in self.pluginconf.pluginConf: timing = self.pluginconf.pluginConf[READ_ATTRIBUTES_REQUEST[Cluster][1]] else: - Domoticz.Error( - "processKnownDevices - missing timing attribute for Cluster: %s - %s" - % (Cluster, READ_ATTRIBUTES_REQUEST[Cluster][1]) - ) + self.log.logging( "Heartbeat", "Error", "processKnownDevices - missing timing attribute for Cluster: %s - %s" % ( + Cluster, READ_ATTRIBUTES_REQUEST[Cluster][1]) ) continue # Let's check the timing @@ -917,22 +914,19 @@ def processListOfDevices(self, Devices): break else: # We browse the all Devices and didn't find any IEEE. if "IEEE" in self.ListOfDevices[NWKID]: - Domoticz.Log( - "processListOfDevices - No corresponding device in Domoticz for %s/%s" - % (NWKID, str(self.ListOfDevices[NWKID]["IEEE"])) - ) + self.log.logging( "Heartbeat", "Log", "processListOfDevices - No corresponding device in Domoticz for %s/%s" % ( + NWKID, str(self.ListOfDevices[NWKID]["IEEE"])) ) else: - Domoticz.Log("processListOfDevices - No corresponding device in Domoticz for %s" % (NWKID)) + self.log.logging( "Heartbeat", "Log", "processListOfDevices - No corresponding device in Domoticz for %s" % (NWKID)) fnd = False if not fnd: # Not devices found in Domoticz, so we are safe to remove it from Plugin if self.ListOfDevices[NWKID]["IEEE"] in self.IEEE2NWK: - Domoticz.Status( - "processListOfDevices - Removing %s / %s from IEEE2NWK." % (self.ListOfDevices[NWKID]["IEEE"], NWKID) - ) + self.log.logging( "Heartbeat", "Status", "processListOfDevices - Removing %s / %s from IEEE2NWK." % ( + self.ListOfDevices[NWKID]["IEEE"], NWKID) ) del self.IEEE2NWK[self.ListOfDevices[NWKID]["IEEE"]] - Domoticz.Status("processListOfDevices - Removing the entry %s from ListOfDevice" % (NWKID)) + self.log.logging( "Heartbeat", "Status", "processListOfDevices - Removing the entry %s from ListOfDevice" % (NWKID)) removeNwkInList(self, NWKID) elif status not in ("inDB", "UNKNOW", "erasePDM"): @@ -957,11 +951,8 @@ def processListOfDevices(self, Devices): del self.ListOfDevices[iterDevToBeRemoved] if self.CommiSSionning or self.busy: - self.log.logging( - "Heartbeat", - "Debug", - "Skip LQI, ConfigureReporting and Networkscan du to Busy state: Busy: %s, Enroll: %s" % (self.busy, self.CommiSSionning), - ) + self.log.logging( "Heartbeat", "Debug", "Skip LQI, ConfigureReporting and Networkscan du to Busy state: Busy: %s, Enroll: %s" % ( + self.busy, self.CommiSSionning), ) return # We don't go further as we are Commissioning a new object and give the prioirty to it # Network Topology diff --git a/Modules/ikeaTradfri.py b/Modules/ikeaTradfri.py index ab0dc2db8..9d461a73b 100644 --- a/Modules/ikeaTradfri.py +++ b/Modules/ikeaTradfri.py @@ -10,10 +10,11 @@ """ +from Modules.basicOutputs import write_attribute from Modules.domoMaj import MajDomoDevice from Modules.domoTools import lastSeenUpdate -from Modules.tools import updSQN, extract_info_from_8085, get_cluster_attribute_value -from Modules.basicOutputs import write_attribute +from Modules.tools import (extract_info_from_8085, get_cluster_attribute_value, + updSQN) from Modules.zigateConsts import ZIGATE_EP diff --git a/Modules/input.py b/Modules/input.py index 97ff3487a..b95dbb480 100644 --- a/Modules/input.py +++ b/Modules/input.py @@ -15,26 +15,25 @@ import time from datetime import datetime -import Domoticz from Classes.ZigateTransport.sqnMgmt import (TYPE_APP_ZCL, TYPE_APP_ZDP, sqn_get_internal_sqn_from_app_sqn, sqn_get_internal_sqn_from_aps_sqn) from Modules.basicInputs import read_attribute_response -from Modules.basicOutputs import (getListofAttribute, send_default_response,handle_unknow_device, - setTimeServer) +from Modules.basicOutputs import (getListofAttribute, handle_unknow_device, + send_default_response, setTimeServer) from Modules.callback import callbackDeviceAwake from Modules.deviceAnnoucement import device_annoucementv2 from Modules.domoMaj import MajDomoDevice from Modules.domoTools import lastSeenUpdate, timedOutDevice from Modules.errorCodes import DisplayStatusCode from Modules.ikeaTradfri import (ikea_motion_sensor_8095, + ikea_remote_control_80A7, ikea_remote_control_8085, ikea_remote_control_8095, ikea_remote_switch_8085, ikea_remote_switch_8095, - ikea_wireless_dimer_8085, - ikea_remote_control_80A7, - ikea_remoteN2_control_80A7) + ikea_remoteN2_control_80A7, + ikea_wireless_dimer_8085) from Modules.inRawAps import inRawAps from Modules.legrand_netatmo import (legrand_motion_8085, legrand_motion_8095, legrand_remote_switch_8085, @@ -52,13 +51,14 @@ from Modules.timeServer import timeserver_read_attribute_request from Modules.tools import (DeviceExist, ReArrangeMacCapaBasedOnModel, checkAndStoreAttributeValue, decodeMacCapa, - extract_info_from_8085, get_isqn_datastruct, + extract_info_from_8085, + get_deviceconf_parameter_value, get_isqn_datastruct, get_list_isqn_attr_datastruct, getSaddrfromIEEE, loggingMessages, lookupForIEEE, mainPoweredDevice, retreive_cmd_payload_from_8002, set_request_phase_datastruct, set_status_datastruct, timeStamped, updLQI, updSQN, - zigpy_plugin_sanity_check, get_deviceconf_parameter_value) + zigpy_plugin_sanity_check) from Modules.zb_tables_management import (mgmt_rtg_rsp, store_NwkAddr_Associated_Devices) from Modules.zigateConsts import (ADDRESS_MODE, LEGRAND_REMOTE_MOTION, @@ -155,32 +155,22 @@ def ZigateRead(self, Devices, Data): FrameStart = Data[:2] FrameStop = Data[len(Data) - 2 :] if FrameStart != "01" and FrameStop != "03": - Domoticz.Error( - "ZigateRead received a non-zigate frame Data: " - + str(Data) - + " FS/FS = " - + str(FrameStart) - + "/" - + str(FrameStop) - ) + self.log.logging( "Input", "Error", "ZigateRead received a non-zigate frame Data: " + str(Data) + " FS/FS = " + str(FrameStart) + "/" + str(FrameStop) ) return - MsgType, MsgData, MsgLQI = extract_messge_infos( Data) + MsgType, MsgData, MsgLQI = extract_messge_infos( self, Data) self.Ping["Nb Ticks"] = 0 # We receive a valid packet - self.log.logging( - "Input", - "Debug", - "ZigateRead - MsgType: %s, Data: %s, LQI: %s" % (MsgType, MsgData, int(MsgLQI, 16)), - ) + self.log.logging( "Input", "Debug", "ZigateRead - MsgType: %s, Data: %s, LQI: %s" % ( + MsgType, MsgData, int(MsgLQI, 16)), ) if MsgType == "8002": # Let's try to see if we can decode it, and then get a new MsgType decoded_frame = decode8002_and_process( self, Data) if decoded_frame is None: return - MsgType, MsgData, MsgLQI = extract_messge_infos( decoded_frame) + MsgType, MsgData, MsgLQI = extract_messge_infos( self, decoded_frame) if MsgType in DECODERS: _decoding = DECODERS[MsgType] @@ -199,11 +189,11 @@ def ZigateRead(self, Devices, Data): "ZigateRead - Decoder not found for %s" % (MsgType), ) -def extract_messge_infos( Data): +def extract_messge_infos( self, Data): FrameStart = Data[:2] FrameStop = Data[len(Data) - 2 :] if FrameStart != "01" and FrameStop != "03": - Domoticz.Error("ZigateRead received a non-zigate frame Data: " + str(Data) + " FS/FS = " + str(FrameStart) + "/" + str(FrameStop)) + self.log.logging( "Input", "Error", "ZigateRead received a non-zigate frame Data: " + str(Data) + " FS/FS = " + str(FrameStart) + "/" + str(FrameStop)) return None, None, None MsgType = Data[2:6] MsgType = MsgType.lower() @@ -484,6 +474,10 @@ def Decode0100(self, Devices, MsgData, MsgLQI): # Read Attribute request def Decode0110(self, Devices, MsgData, MsgLQI): # Write Attribute request self.log.logging("Input", "Debug", "Decode0110 - message: %s" % MsgData) + if len(MsgData) < 24: + self.log.logging( "Input", "Error", "Decode0110 - Message too short %s" % (MsgData), ) + return + MsgSqn = MsgData[:2] MsgSrcAddr = MsgData[2:6] MsgSrcEp = MsgData[6:8] @@ -507,12 +501,8 @@ def Decode0110(self, Devices, MsgData, MsgLQI): # Write Attribute request idx += 4 DataValue = MsgData[idx : 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), - ) + 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), ) def Decode0302(self, Devices, MsgData, MsgLQI): # PDM Load @@ -632,12 +622,8 @@ def Decode8000_v2(self, Devices, MsgData, MsgLQI): # Status self.groupmgt.statusGroupRequest(MsgData) if MsgData[:2] != "00": - self.log.logging( - "Input", - "Error", - "Decode8000 - PacketType: %s TypeSqn: %s sqn_app: %s sqn_aps: %s Status: [%s] " - % (PacketType, type_sqn, sqn_app, sqn_aps, Status), - ) + self.log.logging( "Input", "Error", "Decode8000 - PacketType: %s TypeSqn: %s sqn_app: %s sqn_aps: %s Status: [%s] " % ( + PacketType, type_sqn, sqn_app, sqn_aps, Status), ) # Hack to reboot Zigate if MsgData[:2] not in ("01", "02", "03", "04", "05"): self.internalError += 1 @@ -668,14 +654,18 @@ def Decode8001(self, Decode, MsgData, MsgLQI): # Reception log Level + "\n" ) except IOError: - Domoticz.Error("Error while writing to ZiGate log file %s" % logfilename) + self.log.logging( "Input", "Error","Error while writing to ZiGate log file %s" % logfilename) except IOError: - Domoticz.Error("Error while Opening ZiGate log file %s" % logfilename) + self.log.logging( "Input", "Error", "Error while Opening ZiGate log file %s" % logfilename) - - def Decode8002(self, Devices, MsgData, MsgLQI): # Data indication + + if len(MsgData) < 22: + # Not good + self.log.logging( "Input", "Error", "Invalid frame %s too short" % MsgData) + return + # MsgLogLvl = MsgData[:2] MsgProfilID = MsgData[2:6] MsgClusterID = MsgData[6:10] @@ -684,66 +674,66 @@ def Decode8002(self, Devices, MsgData, MsgLQI): # Data indication MsgSourceAddressMode = MsgData[14:16] - # Domoticz.Log("Decode8002 - MsgLogLvl: %s , MsgProfilID: %s, MsgClusterID: %s MsgSourcePoint: %s, MsgDestPoint: %s, MsgSourceAddressMode: %s" \ - # %(MsgLogLvl, MsgProfilID, MsgClusterID, MsgSourcePoint, MsgDestPoint, MsgSourceAddressMode)) - - if int(MsgSourceAddressMode, 16) in [ - ADDRESS_MODE["short"], - ADDRESS_MODE["group"], - ]: + if int(MsgSourceAddressMode, 16) in [ ADDRESS_MODE["short"], ADDRESS_MODE["group"], ]: MsgSourceAddress = MsgData[16:20] # uint16_t MsgDestinationAddressMode = MsgData[20:22] - if int(MsgDestinationAddressMode, 16) in [ - ADDRESS_MODE["short"], - ADDRESS_MODE["group"], - ]: + if int(MsgDestinationAddressMode, 16) in [ ADDRESS_MODE["short"], ADDRESS_MODE["group"], ]: + if len(MsgData) < 26: + # Not good + self.log.logging( "Input", "Error", "Invalid frame %s too short" % MsgData) + return # Short Address MsgDestinationAddress = MsgData[22:26] # uint16_t MsgPayload = MsgData[26 :] elif int(MsgDestinationAddressMode, 16) == ADDRESS_MODE["ieee"]: # uint32_t + if len(MsgData) < 38: + # Not good + self.log.logging( "Input", "Error", "Invalid frame %s too short" % MsgData) + return # IEEE MsgDestinationAddress = MsgData[22:38] # uint32_t MsgPayload = MsgData[38 :] else: - self.log.logging( - "Input", - "Log", - "Decode8002 - Unexpected Destination ADDR_MOD: %s, drop packet %s" - % (MsgDestinationAddressMode, MsgData), - ) + self.log.logging( "Input", "Log", "Decode8002 - Unexpected Destination ADDR_MOD: %s, drop packet %s" % ( + MsgDestinationAddressMode, MsgData), ) return elif int(MsgSourceAddressMode, 16) == ADDRESS_MODE["ieee"]: + if len(MsgData) < 38: + # Not good + self.log.logging( "Input", "Error", "Invalid frame %s too short" % MsgData) + return + MsgSourceAddress = MsgData[16:32] # uint32_t MsgDestinationAddressMode = MsgData[32:34] - if int(MsgDestinationAddressMode, 16) in [ - ADDRESS_MODE["short"], - ADDRESS_MODE["group"], - ]: + if int(MsgDestinationAddressMode, 16) in [ ADDRESS_MODE["short"], ADDRESS_MODE["group"], ]: MsgDestinationAddress = MsgData[34:38] # uint16_t MsgPayload = MsgData[38 :] elif int(MsgDestinationAddressMode, 16) == ADDRESS_MODE["ieee"]: + if len(MsgData) < 40: + # Not good + self.log.logging( "Input", "Error", "Invalid frame %s too short" % MsgData) + return + # IEEE MsgDestinationAddress = MsgData[34:40] # uint32_t MsgPayload = MsgData[40 :] else: - self.log.logging( - "Input", - "Log", - "Decode8002 - Unexpected Destination ADDR_MOD: %s, drop packet %s" - % (MsgDestinationAddressMode, MsgData), - ) + self.log.logging( "Input", "Log", "Decode8002 - Unexpected Destination ADDR_MOD: %s, drop packet %s" % ( + MsgDestinationAddressMode, MsgData), ) return else: - self.log.logging( - "Input", - "Log", - "Decode8002 - Unexpected Source ADDR_MOD: %s, drop packet %s" % (MsgSourceAddressMode, MsgData), - ) + self.log.logging( "Input", "Log", "Decode8002 - Unexpected Source ADDR_MOD: %s, drop packet %s" % ( + MsgSourceAddressMode, MsgData), ) + return + + if len(MsgPayload) < 4 : + # Not good + self.log.logging( "Input", "Error", "Invalid frame %s, Payload %s too short" % (MsgData, MsgPayload)) return self.log.logging( @@ -765,7 +755,7 @@ def Decode8002(self, Devices, MsgData, MsgLQI): # Data indication # Let's check if this is an Schneider related APS. In that case let's process srcnwkid = dstnwkid = None if len(MsgDestinationAddress) != 4: - Domoticz.Error("not handling IEEE address") + self.log.logging( "Input", "Error", "not handling IEEE address") return srcnwkid = MsgSourceAddress @@ -1012,7 +1002,6 @@ def Decode8009(self, Devices, MsgData, MsgLQI): # Network State response (Firm self.currentChannel = int(Channel, 16) if self.iaszonemgt: - # Domoticz.Log("Update IAS Zone - IEEE: %s" %extaddr) self.iaszonemgt.setZigateIEEE(extaddr) if self.groupmgt: @@ -1059,7 +1048,7 @@ def Decode8010(self, Devices, MsgData, MsgLQI): # Reception Firmware Version else: # Zigpy 20/21/1217/20211217 self.log.logging("Input", "Log", "Decode8010 %s" %MsgData) - self.FirmwareMajorVersion = MsgData[0:2] + self.FirmwareMajorVersion = MsgData[:2] FirmwareMinorVersion = MsgData[4:8] self.FirmwareVersion = MsgData[8:] self.log.logging("Input", "Log", "Decode8010 Major: %s Minor: %s Full: %s" %( @@ -1310,7 +1299,7 @@ def Decode8014(self, Devices, MsgData, MsgLQI): # "Permit Join" status response if prev != "On": self.log.logging("Input", "Status", "Accepting new Hardware: Enable (On)") else: - Domoticz.Error("Decode8014 - Unexpected value " + str(MsgData)) + self.log.logging("Input", "Error","Decode8014 - Unexpected value " + str(MsgData)) self.log.logging( "Input", @@ -1414,7 +1403,7 @@ def Decode8024(self, Devices, MsgData, MsgLQI): # Network joined / formed MsgLen = len(MsgData) MsgDataStatus = MsgData[:2] - Domoticz.Log("Decode8024: Status: %s" % MsgDataStatus) + self.log.logging("Input", "Log", "Decode8024: Status: %s" % MsgDataStatus) if MsgDataStatus == "00": self.log.logging("Input", "Status", "Start Network - Success") @@ -1467,10 +1456,8 @@ def Decode8024(self, Devices, MsgData, MsgLQI): # Network joined / formed and not self.startZigateNeeded and str(int(MsgChannel, 16)) != self.pluginconf.pluginConf["channel"] ): - Domoticz.Status( - "Updating Channel in Plugin Configuration from: %s to: %s" - % (self.pluginconf.pluginConf["channel"], int(MsgChannel, 16)) - ) + self.log.logging("Input", "Status", "Updating Channel in Plugin Configuration from: %s to: %s" % ( + self.pluginconf.pluginConf["channel"], int(MsgChannel, 16)) ) self.pluginconf.pluginConf["channel"] = str(int(MsgChannel, 16)) self.pluginconf.write_Settings() @@ -1501,10 +1488,8 @@ def Decode8024(self, Devices, MsgData, MsgLQI): # Network joined / formed ), ) else: - Domoticz.Error( - "Coordinator initialisation failed IEEE: %s, Nwkid: %s, Channel: %s" - % (MsgExtendedAddress, MsgShortAddress, MsgChannel) - ) + self.log.logging("Input", "Error", "Coordinator initialisation failed IEEE: %s, Nwkid: %s, Channel: %s" % ( + MsgExtendedAddress, MsgShortAddress, MsgChannel) ) def Decode8028(self, Devices, MsgData, MsgLQI): # Authenticate response @@ -1607,7 +1592,7 @@ def Decode8030(self, Devices, MsgData, MsgLQI): # Bind response MsgSrcAddr = MsgData[6:14] self.log.logging("Input", "Debug", "Decode8030 - Bind reponse for %s" % (MsgSrcAddr)) if MsgSrcAddr not in self.IEEE2NWK: - Domoticz.Error("Decode8030 - Do no find %s in IEEE2NWK" % MsgSrcAddr) + self.log.logging("Input", "Error", "Decode8030 - Do no find %s in IEEE2NWK" % MsgSrcAddr) return nwkid = self.IEEE2NWK[MsgSrcAddr] @@ -1617,7 +1602,7 @@ def Decode8030(self, Devices, MsgData, MsgLQI): # Bind response nwkid = MsgSrcAddr else: - Domoticz.Error("Decode8030 - Unknown addr mode %s in %s" % (MsgSrcAddrMode, MsgData)) + self.log.logging("Input", "Error", "Decode8030 - Unknown addr mode %s in %s" % (MsgSrcAddrMode, MsgData)) return i_sqn = sqn_get_internal_sqn_from_app_sqn(self.ControllerLink, MsgSequenceNumber, TYPE_APP_ZDP) @@ -1640,10 +1625,8 @@ def Decode8030(self, Devices, MsgData, MsgLQI): # Bind response for Ep in list(self.ListOfDevices[nwkid]["Bind"]): if Ep not in self.ListOfDevices[nwkid]["Ep"]: # Bad hack - Root cause not identify. Suspition of back and fourth move between stable and beta branch - Domoticz.Error( - "Decode8030 --> %s Found an inconstitent Ep: %s in %s" - % (nwkid, Ep, str(self.ListOfDevices[nwkid]["Bind"])) - ) + self.log.logging("Input", "Error", "Decode8030 --> %s Found an inconstitent Ep: %s in %s" % ( + nwkid, Ep, str(self.ListOfDevices[nwkid]["Bind"])) ) del self.ListOfDevices[nwkid]["Bind"][Ep] continue @@ -1670,10 +1653,8 @@ def Decode8030(self, Devices, MsgData, MsgLQI): # Bind response for Ep in list(self.ListOfDevices[nwkid]["WebBind"]): if Ep not in self.ListOfDevices[nwkid]["Ep"]: # Bad hack - Root cause not identify. Suspition of back and fourth move between stable and beta branch - Domoticz.Error( - "Decode8030 --> %s Found an inconstitent Ep: %s in %s" - % (nwkid, Ep, str(self.ListOfDevices[nwkid]["WebBind"])) - ) + self.log.logging("Input", "Error", "Decode8030 --> %s Found an inconstitent Ep: %s in %s" % ( + nwkid, Ep, str(self.ListOfDevices[nwkid]["WebBind"])) ) del self.ListOfDevices[nwkid]["WebBind"][Ep] continue @@ -1688,7 +1669,7 @@ def Decode8030(self, Devices, MsgData, MsgLQI): # Bind response "Phase", "Status", ): # delete old mechanism - Domoticz.Error("---> delete destNwkid: %s" % (destNwkid)) + self.log.logging("Input", "Error", "---> delete destNwkid: %s" % (destNwkid)) del self.ListOfDevices[nwkid]["WebBind"][Ep][cluster][destNwkid] if ( @@ -1730,9 +1711,9 @@ def Decode8031(self, Devices, MsgData, MsgLQI): # Unbind response self.log.logging("Input", "Debug", "Decode8031 - UnBind reponse for %s" % (MsgSrcAddr)) if MsgSrcAddr in self.IEEE2NWK: nwkid = self.IEEE2NWK[MsgSrcAddr] - Domoticz.Error("Decode8031 - Do no find %s in IEEE2NWK" % MsgSrcAddr) + self.log.logging("Input", "Error", "Decode8031 - Do no find %s in IEEE2NWK" % MsgSrcAddr) else: - Domoticz.Error("Decode8031 - Unknown addr mode %s in %s" % (MsgSrcAddrMode, MsgData)) + self.log.logging("Input", "Error", "Decode8031 - Unknown addr mode %s in %s" % (MsgSrcAddrMode, MsgData)) return self.log.logging( @@ -2095,7 +2076,7 @@ def Decode8043(self, Devices, MsgData, MsgLQI): # Reception Simple descriptor r return if MsgDataShAddr not in self.ListOfDevices: - Domoticz.Error("Decode8043 - receive message for non existing device") + self.log.logging("Input", "Error", "Decode8043 - receive message for non existing device") return if int(MsgDataProfile, 16) == 0xC05E and int(MsgDataDeviceId, 16) == 0xE15E: @@ -2285,7 +2266,7 @@ def Decode8045(self, Devices, MsgData, MsgLQI): # Reception Active endpoint res if not DeviceExist(self, Devices, MsgDataShAddr): # Pas sur de moi, mais si le device n'existe pas, je vois pas pkoi on continuerait - Domoticz.Error("Decode8045 - KeyError: MsgDataShAddr = " + MsgDataShAddr) + self.log.logging("Input", "Error", "Decode8045 - KeyError: MsgDataShAddr = " + MsgDataShAddr) return if self.ListOfDevices[MsgDataShAddr]["Status"] == "inDB": @@ -2405,8 +2386,6 @@ def Decode8048(self, Devices, MsgData, MsgLQI): # Leave indication elif self.ListOfDevices[sAddr]["Status"] == "inDB": self.ListOfDevices[sAddr]["Status"] = "Leave" self.ListOfDevices[sAddr]["Heartbeat"] = 0 - # Domoticz.Status("Calling leaveMgt to request a rejoin of %s/%s " %( sAddr, MsgExtAddress)) - # leaveMgtReJoin( self, sAddr, MsgExtAddress ) elif self.ListOfDevices[sAddr]["Status"] in ( "004d", "0043", "8043", "0045", "8045", ): if MsgExtAddress in self.IEEE2NWK: @@ -2908,56 +2887,12 @@ def scan_attribute_reponse(self, Devices, MsgSQN, i_sqn, MsgSrcAddr, MsgSrcEp, M # crap, lets finish it # Domoticz.Log("Crap Data: %s len: %s" %(MsgData[idx:], len(MsgData[idx:]))) idx += 6 - self.log.logging( - "Input", - "Debug", - "scan_attribute_reponse - %s idx: %s Read Attribute Response: [%s:%s] ClusterID: %s MsgSQN: %s, i_sqn: %s, AttributeID: %s Status: %s Type: %s Size: %s ClusterData: >%s<" - % ( - msgtype, - idx, - MsgSrcAddr, - MsgSrcEp, - MsgClusterId, - MsgSQN, - i_sqn, - MsgAttrID, - MsgAttStatus, - MsgAttType, - MsgAttSize, - MsgClusterData, - ), - MsgSrcAddr, - ) - read_report_attributes( - self, - Devices, - msgtype, - MsgSQN, - MsgSrcAddr, - MsgSrcEp, - MsgClusterId, - MsgAttrID, - MsgAttStatus, - MsgAttType, - MsgAttSize, - MsgClusterData, - ) + self.log.logging( "Input", "Debug", "scan_attribute_reponse - %s idx: %s Read Attribute Response: [%s:%s] ClusterID: %s MsgSQN: %s, i_sqn: %s, AttributeID: %s Status: %s Type: %s Size: %s ClusterData: >%s<" % ( + msgtype, idx, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgSQN, i_sqn, MsgAttrID, MsgAttStatus, MsgAttType, MsgAttSize, MsgClusterData, ), MsgSrcAddr, ) + read_report_attributes( self, Devices, msgtype, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttStatus, MsgAttType, MsgAttSize, MsgClusterData, ) -def read_report_attributes( - self, - Devices, - MsgType, - MsgSQN, - MsgSrcAddr, - MsgSrcEp, - MsgClusterId, - MsgAttrID, - MsgAttStatus, - MsgAttType, - MsgAttSize, - MsgClusterData, -): +def read_report_attributes( self, Devices, MsgType, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttStatus, MsgAttType, MsgAttSize, MsgClusterData, ): if DeviceExist(self, Devices, MsgSrcAddr): debug_LQI(self, MsgSrcAddr, MsgClusterId, MsgAttrID, MsgClusterData, MsgSrcEp) @@ -3164,13 +3099,13 @@ def Decode8120(self, Devices, MsgData, MsgLQI): # Configure Reporting response self.log.logging("Input", "Debug", "Decode8120 - Configure reporting response: %s" % MsgData) if len(MsgData) < 14: - Domoticz.Error("Decode8120 - uncomplet message %s " % MsgData) + self.log.logging( "Input", "Error", "Decode8120 - uncomplet message %s " % MsgData) return MsgSQN = MsgData[:2] MsgSrcAddr = MsgData[2:6] if MsgSrcAddr not in self.ListOfDevices: - Domoticz.Error("Decode8120 - receiving Configure reporting response from unknown %s" % MsgSrcAddr) + self.log.logging( "Input", "Error", "Decode8120 - receiving Configure reporting response from unknown %s" % MsgSrcAddr) if not zigpy_plugin_sanity_check(self, MsgSrcAddr): handle_unknow_device( self, MsgSrcAddr) return @@ -3462,7 +3397,7 @@ def Decode8401(self, Devices, MsgData, MsgLQI): # Reception Zone status change lastSeenUpdate(self, Devices, NwkId=MsgSrcAddr) if MsgSrcAddr not in self.ListOfDevices: - Domoticz.Error("Decode8401 - unknown IAS device %s from plugin" % MsgSrcAddr) + self.log.logging( "Input", "Error", "Decode8401 - unknown IAS device %s from plugin" % MsgSrcAddr) if not zigpy_plugin_sanity_check(self, MsgSrcAddr): handle_unknow_device( self, MsgSrcAddr) return @@ -3764,7 +3699,7 @@ def Decode8085(self, Devices, MsgData, MsgLQI): lastSeenUpdate(self, Devices, NwkId=MsgSrcAddr) if "Model" not in self.ListOfDevices[MsgSrcAddr]: - Domoticz.Log("Decode8085 - No Model Name !") + self.log.logging( "Input", "Log", "Decode8085 - No Model Name !") return _ModelName = self.ListOfDevices[MsgSrcAddr]["Model"] @@ -3864,43 +3799,27 @@ def Decode8085(self, Devices, MsgData, MsgLQI): step_mod, up_down, step_size, transition = extract_info_from_8085(MsgData) - self.log.logging( - "Input", - "Log", - "Decode8085 - OSRAM Lightify Switch Mini %s/%s: Mod %s, UpDown %s Size %s Transition %s" - % (MsgSrcAddr, MsgEP, step_mod, up_down, step_size, transition), - ) + self.log.logging( "Input", "Log", "Decode8085 - OSRAM Lightify Switch Mini %s/%s: Mod %s, UpDown %s Size %s Transition %s" % ( + MsgSrcAddr, MsgEP, step_mod, up_down, step_size, transition), ) if MsgCmd == "04": # Appui court boutton central - self.log.logging( - "Input", - "Log", - "Decode8085 - OSRAM Lightify Switch Mini %s/%s Central button" % (MsgSrcAddr, MsgEP), - ) + self.log.logging( "Input", "Log", "Decode8085 - OSRAM Lightify Switch Mini %s/%s Central button" % ( + MsgSrcAddr, MsgEP), ) MajDomoDevice(self, Devices, MsgSrcAddr, "03", MsgClusterId, "02") elif MsgCmd == "05": # Appui Long Top button - self.log.logging( - "Input", - "Log", - "Decode8085 - OSRAM Lightify Switch Mini %s/%s Long press Up button" % (MsgSrcAddr, MsgEP), - ) + self.log.logging( "Input", "Log", "Decode8085 - OSRAM Lightify Switch Mini %s/%s Long press Up button" % ( + MsgSrcAddr, MsgEP), ) MajDomoDevice(self, Devices, MsgSrcAddr, "03", MsgClusterId, "03") elif MsgCmd == "01": # Appui Long Botton button - self.log.logging( - "Input", - "Log", - "Decode8085 - OSRAM Lightify Switch Mini %s/%s Long press Down button" % (MsgSrcAddr, MsgEP), - ) + self.log.logging( "Input", "Log", "Decode8085 - OSRAM Lightify Switch Mini %s/%s Long press Down button" % ( + MsgSrcAddr, MsgEP), ) MajDomoDevice(self, Devices, MsgSrcAddr, "03", MsgClusterId, "04") elif MsgCmd == "03": # Release - self.log.logging( - "Input", - "Log", - "Decode8085 - OSRAM Lightify Switch Mini %s/%s release" % (MsgSrcAddr, MsgEP), - ) + self.log.logging( "Input", "Log", "Decode8085 - OSRAM Lightify Switch Mini %s/%s release" % ( + MsgSrcAddr, MsgEP), ) self.ListOfDevices[MsgSrcAddr]["Ep"][MsgEP][MsgClusterId]["0000"] = "Cmd: %s, %s" % (MsgCmd, unknown_) @@ -3957,7 +3876,7 @@ def Decode8085(self, Devices, MsgData, MsgLQI): self.log.logging("Input", "Log", "step_mod: %s" % step_mod) if step_mod in TYPE_ACTIONS: - Domoticz.Error("Decode8085 - Profalux Remote, unknown Action: %s" % step_mod) + self.log.logging( "Input", "Error", "Decode8085 - Profalux Remote, unknown Action: %s" % step_mod) selector = None @@ -3966,10 +3885,8 @@ def Decode8085(self, Devices, MsgData, MsgLQI): elif TYPE_ACTIONS[step_mod] in ("stop"): selector = TYPE_ACTIONS[step_mod] else: - Domoticz.Error( - "Decode8085 - Profalux remote Unknown state for %s step_mod: %s up_down: %s" - % (MsgSrcAddr, step_mod, up_down) - ) + self.log.logging( "Input", "Error", "Decode8085 - Profalux remote Unknown state for %s step_mod: %s up_down: %s" % ( + MsgSrcAddr, step_mod, up_down) ) self.log.logging( "Input", @@ -4310,7 +4227,7 @@ def Decode8807(self, Devices, MsgData, MsgLQI): "JN516x M05": {0: 9.5, 52: -3, 40: -15, 31: -26}, } - Domoticz.Debug("Decode8807 - MsgData: %s" % MsgData) + self.log.logging( "Input", "Debug", "Decode8807 - MsgData: %s" % MsgData) TxPower = MsgData[:2] self.ControllerData["Tx-Power"] = TxPower diff --git a/Modules/legrand_netatmo.py b/Modules/legrand_netatmo.py index f97b485d4..95d06d134 100755 --- a/Modules/legrand_netatmo.py +++ b/Modules/legrand_netatmo.py @@ -13,8 +13,6 @@ import struct from time import time -import Domoticz - from Modules.basicOutputs import (read_attribute, write_attribute, write_attributeNoResponse) from Modules.bindings import bindDevice, unbindDevice @@ -55,9 +53,6 @@ def callbackDeviceAwake_Legrand(self, Devices, NwkId, EndPoint, cluster): The function is called after processing the readCluster part """ - # Domoticz.Log("callbackDeviceAwake_Legrand - Nwkid: %s, EndPoint: %s cluster: %s" \ - # %(NwkId, EndPoint, cluster)) - return @@ -242,11 +237,8 @@ def sendFC01Command(self, sqn, nwkid, ep, ClusterID, cmd, data): ackIsDisabled=is_ack_tobe_disabled(self, nwkid), highpriority=True, ) - self.log.logging( - "Legrand", - "Log", - "loggingLegrand - Nwkid: %s/%s Cluster: %s, Command: %s Payload: %s" % (nwkid, ep, ClusterID, cmd, data), - ) + self.log.logging( "Legrand", "Log", "loggingLegrand - Nwkid: %s/%s Cluster: %s, Command: %s Payload: %s" % ( + nwkid, ep, ClusterID, cmd, data), ) return @@ -263,7 +255,7 @@ def rejoin_legrand_reset(self): return # Send a Write Attributes no responses - Domoticz.Status("Detected Legrand IEEE, broadcast Write Attribute 0x0000/0xf000") + self.log.logging( "Legrand", "Status", "Detected Legrand IEEE, broadcast Write Attribute 0x0000/0xf000") write_attributeNoResponse(self, "ffff", ZIGATE_EP, "01", "0000", "1021", "01", "f000", "23", "00000000") @@ -282,7 +274,7 @@ def legrand_fc01(self, nwkid, command, OnOff): return if command not in LEGRAND_COMMAND_NAME: - Domoticz.Error("Unknown Legrand command %s" % command) + self.log.logging( "Legrand", "Error", "Unknown Legrand command %s" % command) return if "Model" not in self.ListOfDevices[nwkid]: return @@ -470,7 +462,7 @@ def legrand_fc40(self, nwkid, Mode): } if Mode not in CABLE_OUTLET_MODE: - Domoticz.Error(" Bad Mode : %s for %s" % (Mode, nwkid)) + self.log.logging( "Legrand", "Error", " Bad Mode : %s for %s" % (Mode, nwkid)) return Hattribute = "0000" @@ -823,7 +815,7 @@ def legrand_remote_switch_8085(self, Devices, MsgSrcAddr, MsgEP, MsgClusterId, M elif TYPE_ACTIONS[step_mod] == "stop": selector = TYPE_ACTIONS[step_mod] else: - Domoticz.Error("Decode8085 - Unknown state for %s step_mod: %s up_down: %s" % (MsgSrcAddr, step_mod, up_down)) + self.log.logging( "Legrand", "Error", "Decode8085 - Unknown state for %s step_mod: %s up_down: %s" % (MsgSrcAddr, step_mod, up_down)) return self.log.logging("Input", "Debug", "Decode8085 - Legrand selector: %s" % selector, MsgSrcAddr) diff --git a/Modules/livolo.py b/Modules/livolo.py index 15ff5a281..42f5043c5 100644 --- a/Modules/livolo.py +++ b/Modules/livolo.py @@ -10,13 +10,12 @@ """ -import Domoticz -from Classes.LoggingManagement import LoggingManagement -from Zigbee.zclCommands import zcl_level_move_to_level, zcl_toggle +from Classes.LoggingManagement import LoggingManagement from Modules.domoMaj import MajDomoDevice from Modules.tools import retreive_cmd_payload_from_8002 from Modules.zigateConsts import ZIGATE_EP +from Zigbee.zclCommands import zcl_level_move_to_level, zcl_toggle # Livolo commands. # @@ -80,14 +79,12 @@ def livolo_OnOff(self, nwkid, EPout, devunit, onoff): zcl_level_move_to_level( self, nwkid, EPout, "00", level_value, timing_value) #sendZigateCmd(self, "0081", "02" + nwkid + ZIGATE_EP + EPout + "00" + level_value + timing_value) else: - Domoticz.Error("livolo_OnOff - Wrong parameters sent ! onoff: %s devunit: %s" % (onoff, devunit)) + self.log.logging( "Livolo", "Error", "livolo_OnOff - Wrong parameters sent ! onoff: %s devunit: %s" % ( + onoff, devunit)) def livoloReadRawAPS(self, Devices, srcNWKID, srcEp, ClusterID, dstNWKID, dstEP, MsgPayload): - # Domoticz.Log("livoloReadRawAPS - Nwkid: %s Ep: %s, Cluster: %s, dstNwkid: %s, dstEp: %s, Payload: %s" \ - # %(srcNWKID, srcEp, ClusterID, dstNWKID, dstEP, MsgPayload)) - # At Device Annoucement 0x00 and 0x05 are sent by device default_response, GlobalCmd, SQN, ManufacturerCode, Command, Data = retreive_cmd_payload_from_8002(MsgPayload) diff --git a/Modules/lumi.py b/Modules/lumi.py index 6d39b6ee1..bf549b2bf 100644 --- a/Modules/lumi.py +++ b/Modules/lumi.py @@ -10,9 +10,8 @@ """ import struct import time -from math import atan, pi, sqrt, log10 +from math import atan, log10, pi, sqrt -import Domoticz from Modules.basicOutputs import (ZigatePermitToJoin, leaveRequest, read_attribute, write_attribute) from Modules.domoMaj import MajDomoDevice @@ -569,15 +568,15 @@ def readXiaomiClusterv2( dtype = MsgClusterData[idx + 2 : idx + 4] infos = XIAOMI_TAGS[(TagXiaomi, dtype)] - Domoticz.Log("Infos: %s Tag: %s Dtype: %s" % (infos, TagXiaomi, dtype)) + self.log.logging( "Lumi", "Log", "Infos: %s Tag: %s Dtype: %s" % (infos, TagXiaomi, dtype)) if dtype not in SIZE_DATA_TYPE: - Domoticz.Log("Unknown DType: %s for Tage: %s" % (dtype, TagXiaomi)) + self.log.logging( "Lumi", "Log", "Unknown DType: %s for Tage: %s" % (dtype, TagXiaomi)) continue nbByteToRead = 2 * SIZE_DATA_TYPE[dtype] svalue = MsgClusterData[idx + 4 : idx + 4 + nbByteToRead] - Domoticz.Log("----- svalue: %s" % (svalue)) + self.log.logging( "Lumi", "Log", "----- svalue: %s" % (svalue)) if dtype == "10": value = svalue @@ -639,11 +638,7 @@ def lumi_lock(self, Devices, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgC if lumilockData in LUMI_LOCK_KEY: lumilockData += MsgClusterData[4:6] - self.log.logging( - "Lumi", - "Debug", - "lumi_private_cluster - %s/%s LUMI LOCK %s lumilockData: %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterData, lumilockData), - ) + self.log.logging( "Lumi", "Debug", "lumi_private_cluster - %s/%s LUMI LOCK %s lumilockData: %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterData, lumilockData), ) MajDomoDevice(self, Devices, MsgSrcAddr, MsgSrcEp, "LumiLock", lumilockData) checkAndStoreAttributeValue(self, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgClusterData) diff --git a/Modules/orvibo.py b/Modules/orvibo.py index 5024460fa..ed0fa6ddf 100644 --- a/Modules/orvibo.py +++ b/Modules/orvibo.py @@ -4,9 +4,6 @@ # Author: zaraki673 & pipiche38 # - -import Domoticz - from Modules.basicOutputs import write_attribute from Modules.domoMaj import MajDomoDevice from Modules.tools import is_ack_tobe_disabled @@ -29,51 +26,23 @@ def callbackDeviceAwake_Orvibo(self, Devices, NwkId, EndPoint, cluster): The function is called after processing the readCluster part """ - # Domoticz.Log("callbackDeviceAwake_Orvibo - Nwkid: %s, EndPoint: %s cluster: %s" \ - # %(NwkId, EndPoint, cluster)) - return def orviboReadRawAPS(self, Devices, srcNWKID, srcEp, ClusterID, dstNWKID, dstEP, MsgPayload): - # Domoticz.Log("OrviboReadRawAPS - Nwkid: %s Ep: %s, Cluster: %s, dstNwkid: %s, dstEp: %s, Payload: %s" \ - # %(srcNWKID, srcEp, ClusterID, dstNWKID, dstEP, MsgPayload)) - - BUTTON_MAP = { - # d0d2422bbf3a4982b31ea843bfedb559 - "d0d2422bbf3a4982b31ea843bfedb559": { - "01": 1, # Top - "02": 2, # Middle - "03": 3, # Bottom - }, - # Interupteur Autocolalant / - "3c4e4fc81ed442efaf69353effcdfc5f": { - "03": 10, # Top Left, - "0b": 20, # Middle Left - "07": 30, # Top Right - "0f": 40, # Mddle Right - }, - } - - ACTIONS_MAP = { - "00": 1, # Click - "02": 2, # Long Click - "03": 3, # Release - } - if srcNWKID not in self.ListOfDevices: - Domoticz.Error("%s not found in Database") + self.log.logging("Orvibo", "Error", "%s not found in Database" %srcNWKID) return if "Model" not in self.ListOfDevices[srcNWKID]: return _Model = self.ListOfDevices[srcNWKID]["Model"] if ClusterID != "0017": - Domoticz.Error("orviboReadRawAPS - unexpected ClusterId %s for NwkId: %s" % (ClusterID, srcNWKID)) + self.log.logging("Orvibo", "Error", "orviboReadRawAPS - unexpected ClusterId %s for NwkId: %s" % (ClusterID, srcNWKID)) return - FrameControlFiled = MsgPayload[0:2] + FrameControlFiled = MsgPayload[:2] if FrameControlFiled == "19": sqn = MsgPayload[2:4] @@ -81,14 +50,36 @@ def orviboReadRawAPS(self, Devices, srcNWKID, srcEp, ClusterID, dstNWKID, dstEP, data = MsgPayload[6:] if cmd == "08": - button = data[0:2] + button = data[:2] action = data[4:6] - # Domoticz.Log("button: %s, action: %s" %(button, action)) + BUTTON_MAP = { + # d0d2422bbf3a4982b31ea843bfedb559 + "d0d2422bbf3a4982b31ea843bfedb559": { + "01": 1, # Top + "02": 2, # Middle + "03": 3, # Bottom + }, + # Interupteur Autocolalant / + "3c4e4fc81ed442efaf69353effcdfc5f": { + "03": 10, # Top Left, + "0b": 20, # Middle Left + "07": 30, # Top Right + "0f": 40, # Mddle Right + }, + } + + ACTIONS_MAP = { + "00": 1, # Click + "02": 2, # Long Click + "03": 3, # Release + } + + self.log.logging("Orvibo", "Debug", "button: %s, action: %s" %(button, action)) if action in ACTIONS_MAP and button in BUTTON_MAP[_Model]: selector = BUTTON_MAP[_Model][button] + ACTIONS_MAP[action] - # Domoticz.Log("---> Selector: %s" %selector) + self.log.logging("Orvibo", "Debug", "---> Selector: %s" %selector) MajDomoDevice(self, Devices, srcNWKID, "01", "0006", selector) @@ -112,7 +103,7 @@ def OrviboRegistration(self, nwkid): data_type = "20" # Bool data = "01" - Domoticz.Log("Orvibo registration for %s" % nwkid) + self.log.logging("Orvibo", "Debug", "Orvibo registration for %s" % nwkid) write_attribute( self, nwkid, diff --git a/Modules/pairingProcess.py b/Modules/pairingProcess.py index 727130f0c..6cc2fe2ed 100644 --- a/Modules/pairingProcess.py +++ b/Modules/pairingProcess.py @@ -12,7 +12,6 @@ import time -import Domoticz from Modules.basicOutputs import getListofAttribute, identifyEffect from Modules.bindings import bindDevice, reWebBind_Clusters, unbindDevice from Modules.casaia import casaia_pairing @@ -275,11 +274,11 @@ def request_next_Ep(self, Nwkid): def interview_timeout(self, Devices, NWKID, RIA, status): self.log.logging( "Pairing", "Debug", "interview_timeout - NWKID: %s, Status: %s, RIA: %s," % ( NWKID, status, RIA, ), ) - Domoticz.Error("[%s] NEW OBJECT: %s Not able to get all needed attributes on time" % (RIA, NWKID)) + self.log.logging("Pairing", "Error", "[%s] NEW OBJECT: %s Not able to get all needed attributes on time" % (RIA, NWKID)) self.ListOfDevices[NWKID]["Status"] = "UNKNOW" self.ListOfDevices[NWKID]["ConsistencyCheck"] = "Bad Pairing" - Domoticz.Error("processNotinDB - not able to find response from " + str(NWKID) + " stop process at " + str(status)) - Domoticz.Error("processNotinDB - Collected Infos are : %s" % (str(self.ListOfDevices[NWKID]))) + self.log.logging("Pairing", "Error", "processNotinDB - not able to find response from " + str(NWKID) + " stop process at " + str(status)) + self.log.logging("Pairing", "Error", "processNotinDB - Collected Infos are : %s" % (str(self.ListOfDevices[NWKID]))) self.adminWidgets.updateNotificationWidget(Devices, "Unable to collect all informations for enrollment of this devices. See Logs") self.CommiSSionning = False if "ReqEpv2" in self.ListOfDevices[NWKID]: @@ -335,12 +334,10 @@ def interview_state_createDB(self, Devices, NWKID, RIA, status): IsCreated = False # Let's check if the IEEE is not known in Domoticz for x in Devices: - if self.ListOfDevices[NWKID].get("IEEE") and Devices[ - x - ].DeviceID == str(self.ListOfDevices[NWKID]["IEEE"]): + if self.ListOfDevices[NWKID].get("IEEE") and Devices[ x ].DeviceID == str(self.ListOfDevices[NWKID]["IEEE"]): IsCreated = True - Domoticz.Error("processNotinDBDevices - Devices already exist. " + Devices[x].Name + " with " + str(self.ListOfDevices[NWKID])) - Domoticz.Error("processNotinDBDevices - Please cross check the consistency of the Domoticz and Plugin database.") + self.log.logging("Pairing", "Error", "processNotinDBDevices - Devices already exist. " + Devices[x].Name + " with " + str(self.ListOfDevices[NWKID])) + self.log.logging("Pairing", "Error", "processNotinDBDevices - Please cross check the consistency of the Domoticz and Plugin database.") break if not IsCreated: @@ -369,7 +366,7 @@ def full_provision_device(self, Devices, NWKID, RIA, status): CreateDomoDevice(self, Devices, NWKID) if self.ListOfDevices[NWKID]["Status"] not in ("inDB", "failDB"): # Something went wrong in the Widget creation - Domoticz.Error("processNotinDBDevices - Creat Domo Device Failed !!! for %s status: %s" % (NWKID, self.ListOfDevices[NWKID]["Status"])) + self.log.logging("Pairing", "Error","processNotinDBDevices - Creat Domo Device Failed !!! for %s status: %s" % (NWKID, self.ListOfDevices[NWKID]["Status"])) self.ListOfDevices[NWKID]["Status"] = "UNKNOW" self.CommiSSionning = False return @@ -377,11 +374,11 @@ def full_provision_device(self, Devices, NWKID, RIA, status): self.ListOfDevices[ NWKID ]["PairingTime"] = time.time() # Don't know why we need as this seems very weird if NWKID not in self.ListOfDevices: - Domoticz.Error("processNotinDBDevices - %s doesn't exist in Post creation widget" % NWKID) + self.log.logging("Pairing", "Error","processNotinDBDevices - %s doesn't exist in Post creation widget" % NWKID) self.CommiSSionning = False return if "Ep" not in self.ListOfDevices[NWKID]: - Domoticz.Error("processNotinDBDevices - %s doesn't have Ep in Post creation widget" % NWKID) + self.log.logging("Pairing", "Error","processNotinDBDevices - %s doesn't have Ep in Post creation widget" % NWKID) self.CommiSSionning = False return @@ -604,7 +601,7 @@ def create_group_if_required(self, NWKID): if len(groupToAdd) == 2: self.groupmgt.addGroupMemberShip(NWKID, groupToAdd[0], groupToAdd[1]) else: - Domoticz.Error("Uncorrect GroupMembership definition %s" % str(self.DeviceConf[self.ListOfDevices[NWKID]["Model"]]["GroupMembership"])) + self.log.logging("Pairing", "Error","Uncorrect GroupMembership definition %s" % str(self.DeviceConf[self.ListOfDevices[NWKID]["Model"]]["GroupMembership"])) if self.groupmgt and "Model" in self.ListOfDevices[NWKID] and self.ListOfDevices[NWKID]["Model"] == "tint-Remote-white": # Tint Remote manage 4 groups and we will create with ZiGate attached. diff --git a/Modules/paramDevice.py b/Modules/paramDevice.py index 09797a81c..d9ad3ba38 100644 --- a/Modules/paramDevice.py +++ b/Modules/paramDevice.py @@ -10,7 +10,6 @@ """ -import Domoticz from Modules.basicOutputs import (ballast_Configuration_max_level, ballast_Configuration_min_level, set_PIROccupiedToUnoccupiedDelay, @@ -46,10 +45,11 @@ from Modules.tuya import (SmartRelayStatus01, SmartRelayStatus02, SmartRelayStatus03, SmartRelayStatus04, get_tuya_attribute, ts110e_light_type, - ts110e_switch_type, tuya_backlight_command, - tuya_cmd_ts004F, tuya_curtain_mode, - tuya_energy_childLock, tuya_external_switch_mode, - tuya_garage_run_time, tuya_motion_zg204l_keeptime, + ts110e_switch01_type, ts110e_switch02_type, + tuya_backlight_command, tuya_cmd_ts004F, + tuya_curtain_mode, tuya_energy_childLock, + tuya_external_switch_mode, tuya_garage_run_time, + tuya_motion_zg204l_keeptime, tuya_motion_zg204l_sensitivity, tuya_pir_keep_time_lookup, tuya_radar_motion_radar_detection_delay, @@ -92,8 +92,6 @@ def param_Occupancy_settings_PIROccupiedToUnoccupiedDelay(self, nwkid, delay): # specifies the time delay, in seconds,before the PIR sensor changes to # its unoccupied state after the last detection of movement in the sensed area. - # Domoticz.Log("param_Occupancy_settings_PIROccupiedToUnoccupiedDelay %s -> delay: %s" %(nwkid, delay)) - if self.ListOfDevices[nwkid]["Manufacturer"] == "100b" or self.ListOfDevices[nwkid]["Manufacturer Name"] == "Philips": # Philips if "02" not in self.ListOfDevices[nwkid]["Ep"]: return @@ -130,7 +128,7 @@ def param_Occupancy_settings_PIROccupiedToUnoccupiedDelay(self, nwkid, delay): ReadAttributeRequest_0406_0010(self, nwkid) else: - Domoticz.Log("=====> Unknown Manufacturer/Name") + self.log.logging("Heartbeat", "Log", "=====> Unknown Manufacturer/Name") def param_PowerOnAfterOffOn(self, nwkid, mode): @@ -305,12 +303,11 @@ def ias_sensitivity(self, nwkid, sensitivity): "WiserShutterDuration": wiser_lift_duration, "AqaraMultiClick": enable_click_mode_aqara, "TS110ELightType": ts110e_light_type, - "TS110ESwitchType": ts110e_switch_type + "TS110ESwitch01Type": ts110e_switch01_type, + "TS110ESwitch02Type": ts110e_switch02_type } def sanity_check_of_param(self, NwkId): - # Domoticz.Log("sanity_check_of_param for %s" %NwkId) - if "Param" not in self.ListOfDevices[NwkId]: return @@ -322,7 +319,6 @@ def sanity_check_of_param(self, NwkId): continue if param in DEVICE_PARAMETERS: - # Domoticz.Log("sanity_check_of_param - calling %s" %param) func = DEVICE_PARAMETERS[param] value = self.ListOfDevices[NwkId]["Param"][param] func(self, NwkId, value) diff --git a/Modules/philips.py b/Modules/philips.py index de9275db9..03d5b8ad0 100644 --- a/Modules/philips.py +++ b/Modules/philips.py @@ -5,8 +5,6 @@ # import struct -import Domoticz - import Modules.paramDevice from Modules.basicOutputs import (raw_APS_request, set_poweron_afteroffon, write_attribute) @@ -15,7 +13,8 @@ ReadAttributeRequest_0006_400x, ReadAttributeRequest_0008_0000, ReadAttributeRequest_0406_philips_0030) -from Modules.tools import (checkAndStoreAttributeValue, is_hex, get_deviceconf_parameter_value, +from Modules.tools import (checkAndStoreAttributeValue, + get_deviceconf_parameter_value, is_hex, retreive_cmd_payload_from_8002) from Modules.zigateConsts import ZIGATE_EP @@ -43,7 +42,7 @@ def callbackDeviceAwake_Philips(self, Devices, NwkId, EndPoint, cluster): The function is called after processing the readCluster part """ - Domoticz.Log("callbackDeviceAwake_Legrand - Nwkid: %s, EndPoint: %s cluster: %s" % (NwkId, EndPoint, cluster)) + self.log.logging("Philips", "Log", "callbackDeviceAwake_Legrand - Nwkid: %s, EndPoint: %s cluster: %s" % (NwkId, EndPoint, cluster)) def default_response_for_philips_hue_reporting_attribute(self, Nwkid, srcEp, cluster, sqn): @@ -124,7 +123,7 @@ def philips_set_poweron_after_offon(self, mode): # call from WebServer if mode not in PHILIPS_POWERON_MODE: - Domoticz.Error("philips_set_poweron_after_offon - Unknown mode: %s" % mode) + self.log.logging("Philips", "Error", "philips_set_poweron_after_offon - Unknown mode: %s" % mode) for nwkid in self.ListOfDevices: philips_set_poweron_after_offon_device(self, mode, nwkid) @@ -142,7 +141,7 @@ def philips_set_poweron_after_offon_device(self, mode, nwkid): if "0006" not in self.ListOfDevices[nwkid]["Ep"]["0b"]: return if "4003" not in self.ListOfDevices[nwkid]["Ep"]["0b"]["0006"]: - Domoticz.Log("philips_set_poweron_after_offon Device: %s do not have a Set Power Attribute !" % nwkid) + self.log.logging("Philips", "Log", "philips_set_poweron_after_offon Device: %s do not have a Set Power Attribute !" % nwkid) ReadAttributeRequest_0006_400x(self, nwkid) return diff --git a/Modules/piZigate.py b/Modules/piZigate.py index 54f3243fb..585a834b2 100644 --- a/Modules/piZigate.py +++ b/Modules/piZigate.py @@ -12,15 +12,14 @@ import os import platform import sys -import distro -import Domoticz +import distro def linux_distribution(): try: return [distro.id(), distro.version(), distro.codename()] - except: + except Exception as e: return "N/A" @@ -30,49 +29,37 @@ def switchPiZigate_mode(self, mode="run"): return try: - Domoticz.Status( - """Python version: %s dist info: %s linux_distribution: %s system: %s machine: %s platform: %s uname: %s version: %s mac_ver: %s """ - % ( - sys.version.split("\n"), - str(distro.info()), - linux_distribution(), - platform.system(), - platform.machine(), - platform.platform(), - platform.uname(), - platform.version(), - platform.mac_ver(), - ) - ) + self.log.logging("PiZigate", "Status", "Python version: %s dist info: %s linux_distribution: %s system: %s machine: %s platform: %s uname: %s version: %s mac_ver: %s " % ( + sys.version.split("\n"), str(distro.info()), linux_distribution(), platform.system(), platform.machine(), platform.platform(), platform.uname(), platform.version(), platform.mac_ver(), ) ) except Exception as e: - Domoticz.Error("switchPiZigate_mode - unable to find distribution: Assuming debian, Error: %s" % e) - runmode_with_gpiomodule() + self.log.logging("PiZigate", "Error", "switchPiZigate_mode - unable to find distribution: Assuming debian, Error: %s" % e) + runmode_with_gpiomodule(self) return if distro.id() in ("fedora"): - runmode_with_gpiomodule() + runmode_with_gpiomodule(self) if distro.id() in ("debian", "raspbian"): - runmode_with_gpiocommand() + runmode_with_gpiocommand(self) -def runmode_with_gpiomodule(): +def runmode_with_gpiomodule(self): try: import RPi.GPIO as GPIO except RuntimeError: - Domoticz.Error("Error importing python3 module RPi.GPIO!, trying to recover with GPIO commands from wiringPi") + self.log.logging("PiZigate", "Error","Error importing python3 module RPi.GPIO!, trying to recover with GPIO commands from wiringPi") runmode_with_gpiocommand() return - except: - Domoticz.Error("Fail to import RPi.GPIO") - Domoticz.Error("+ make sure to set the PiZigate in run mode") + except Exception as e: + self.log.logging("PiZigate", "Error","Fail to import RPi.GPIO") + self.log.logging("PiZigate", "Error","+ make sure to set the PiZigate in run mode") runmode_with_gpiocommand() return - Domoticz.Log("Set PiZigate Channels 11 and 17") + self.log.logging("PiZigate", "Debug", "Set PiZigate Channels 11 and 17") channel_lst = [17, 27] try: GPIO.setmode(GPIO.BCM) @@ -87,37 +74,37 @@ def runmode_with_gpiomodule(): ei0 = GPIO.input(17) ei2 = GPIO.input(27) - Domoticz.Status("Switch PiZigate in RUN mode") + self.log.logging("PiZigate", "Status", "Switch PiZigate in RUN mode") if ei0: - Domoticz.Log(" + GPIO(RUN) OK") + self.log.logging("PiZigate", "Log", " + GPIO(RUN) OK") else: - Domoticz.Log(" + GPIO(RUN) KO") + self.log.logging("PiZigate", "Log"," + GPIO(RUN) KO") if ei2: - Domoticz.Log(" + GPIO(FLASH) OK") + self.log.logging("PiZigate", "Log"," + GPIO(FLASH) OK") else: - Domoticz.Log(" + GPIO(FLASH) KO") + self.log.logging("PiZigate", "Log"," + GPIO(FLASH) KO") except RuntimeError: - Domoticz.Error("Error executing GPIO API, let's try with GPIO commands!") - runmode_with_gpiocommand() + self.log.logging("PiZigate", "Error", "Error executing GPIO API, let's try with GPIO commands!") + runmode_with_gpiocommand(self) return - except: - Domoticz.Error("Unable to set GPIO") - Domoticz.Error("+ make sure to set the PiZigate in run mode") + except Exception as e: + self.log.logging("PiZigate", "Error", "Unable to set GPIO") + self.log.logging("PiZigate", "Error", "+ make sure to set the PiZigate in run mode") -def runmode_with_gpiocommand(): +def runmode_with_gpiocommand(self): try: from subprocess import run # nosec - except: - Domoticz.Error("Error while importing run from python module subprocess, fall back to os module") - runmode_with_osgpiocommand() + except Exception as e: + self.log.logging("PiZigate", "Error","Error while importing run from python module subprocess, fall back to os module") + runmode_with_osgpiocommand(self) return - Domoticz.Log("runmode_with_gpiocommand") + self.log.logging("PiZigate", "Log", "runmode_with_gpiocommand") GPIO_CMD = "/usr/bin/gpio" if os.path.isfile(GPIO_CMD): - Domoticz.Log("+ Checkint GPIO PINs") + self.log.logging("PiZigate", "Log", "+ Checkint GPIO PINs") run(GPIO_CMD + " read 0", shell=True, check=True) # nosec run(GPIO_CMD + " read 2", shell=True, check=True) # nosec @@ -127,19 +114,19 @@ def runmode_with_gpiocommand(): run(GPIO_CMD + " write 0 0", shell=True, check=True) # nosec run(GPIO_CMD + " write 0 1", shell=True, check=True) # nosec - Domoticz.Log("+ Checkint GPIO PINs") + self.log.logging("PiZigate", "Log", "+ Checkint GPIO PINs") run(GPIO_CMD + " read 0", shell=True, check=True) # nosec run(GPIO_CMD + " read 2", shell=True, check=True) # nosec else: - Domoticz.Error("%s command missing. Make sure to install wiringPi package" % GPIO_CMD) + self.log.logging("PiZigate", "Error", "%s command missing. Make sure to install wiringPi package" % GPIO_CMD) -def runmode_with_osgpiocommand(): +def runmode_with_osgpiocommand(self): - Domoticz.Log("runmode_with_osgpiocommand") + self.log.logging("PiZigate", "Log","runmode_with_osgpiocommand") GPIO_CMD = "/usr/bin/gpio" if os.path.isfile(GPIO_CMD): - Domoticz.Log("+ Checkint GPIO PINs") + self.log.logging("PiZigate", "Log","+ Checkint GPIO PINs") os.system(GPIO_CMD + " read 0") # nosec os.system(GPIO_CMD + " read 2") # nosec @@ -149,8 +136,8 @@ def runmode_with_osgpiocommand(): os.system(GPIO_CMD + " write 0 0") # nosec os.system(GPIO_CMD + " write 0 1") # nosec - Domoticz.Log("+ Checkint GPIO PINs") + self.log.logging("PiZigate", "Log", "+ Checkint GPIO PINs") os.system(GPIO_CMD + " read 0") # nosec os.system(GPIO_CMD + " read 2") # nosec else: - Domoticz.Error("%s command missing. Make sure to install wiringPi package" % GPIO_CMD) + self.log.logging("PiZigate", "Error", "%s command missing. Make sure to install wiringPi package" % GPIO_CMD) diff --git a/Modules/pluzzy.py b/Modules/pluzzy.py index 89851b088..20124b836 100644 --- a/Modules/pluzzy.py +++ b/Modules/pluzzy.py @@ -10,18 +10,12 @@ """ -import Domoticz - def pluzzyDecode004D(self, MsgSrcAddr, MsgIEEE, MsgMacCapa, decodedMacCapa, LQI): - self.log.logging( - "Input", "Debug", "Pluzzy-Decode004D - Device Annoucement %s %s %s" % (MsgSrcAddr, MsgIEEE, decodedMacCapa) - ) + self.log.logging( "Input", "Debug", "Pluzzy-Decode004D - Device Annoucement %s %s %s" % (MsgSrcAddr, MsgIEEE, decodedMacCapa) ) -def pluzzyDecode8102( - self, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttStatus, MsgAttType, MsgAttSize, MsgClusterData, MsgLQI -): +def pluzzyDecode8102( self, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttStatus, MsgAttType, MsgAttSize, MsgClusterData, MsgLQI ): """ Receiving a 8102 message and we are using the pluzzy Firmware. @@ -29,19 +23,16 @@ def pluzzyDecode8102( (2) if we are in the pairing/widget creation process, let's specify Model Name for Pluzzy devices. """ - self.log.logging( - "Input", - "Debug", - "Pluzzy-Decode8102 - Individual Attribute response : [%s:%s] ClusterID: %s AttributeID: %s Status: %s Type: %s Size: %s ClusterData: >%s<" - % (MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttStatus, MsgAttType, MsgAttSize, MsgClusterData), - ) + self.log.logging( "Input", "Debug", "Pluzzy-Decode8102 - Individual Attribute response : [%s:%s] ClusterID: %s AttributeID: %s Status: %s Type: %s Size: %s ClusterData: >%s<" % ( + MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttStatus, MsgAttType, MsgAttSize, MsgClusterData)) + if MsgSrcAddr not in self.ListOfDevices: - Domoticz.Error("Pluzzy-Decode8102 - Unknown Device %s" % MsgSrcAddr) + self.log.logging( "Pluzzy", "Error", "Pluzzy-Decode8102 - Unknown Device %s" % MsgSrcAddr) return if self.ListOfDevices[MsgSrcAddr]["Status"] == "UNKNOW": - Domoticz.Error("Pluzzy-Decode8102 - Device %s in UNKNOW state" % MsgSrcAddr) + self.log.logging( "Pluzzy", "Error","Pluzzy-Decode8102 - Device %s in UNKNOW state" % MsgSrcAddr) return if "Model" in self.ListOfDevices[MsgSrcAddr] and self.ListOfDevices[MsgSrcAddr]["Model"] != {}: diff --git a/Modules/pollControl.py b/Modules/pollControl.py index 30040a7f7..478242ea3 100644 --- a/Modules/pollControl.py +++ b/Modules/pollControl.py @@ -29,7 +29,7 @@ # 0x0020/0x0003: Fast Poll Timeout = 10 seconds = 0x28 quarterseconds # Notethat for the Check-in Interval, 0 is a special value and does not apply to this equation. -import Domoticz + import struct from Modules.basicOutputs import raw_APS_request @@ -53,7 +53,7 @@ def receive_poll_cluster(self, srcnwkid, srcep, cluster, dstnwkid, dstep, Sqn, M if "Model" in self.ListOfDevices[srcnwkid] and self.ListOfDevices[srcnwkid]["Model"]: model = self.ListOfDevices[srcnwkid]["Model"] - Domoticz.Log("receive_poll_cluster %s/%s %s %s %s" % (srcnwkid, srcep, cluster, Command, Data)) + self.log.logging( "PollControl", "Debug", "receive_poll_cluster %s/%s %s %s %s" % (srcnwkid, srcep, cluster, Command, Data)) if Command == "00": # We receive a Poll Checking Command poll_checkin_response_command(self, Sqn, srcnwkid, srcep, ContinueFastPoll=True, DurationFastPoll=0xFC0) @@ -78,10 +78,10 @@ def poll_checkin_command(self, Sqn, snwkid, ep): # If the Poll Control Server does not receive a Check-in response back from the Poll Control Client up to7.68 seconds # it is free to return to polling according to the LongPollInterval. - Domoticz.Log("poll_checkin_command %s %s" % (snwkid, ep)) + self.log.logging( "PollControl", "Debug", "poll_checkin_command %s %s" % (snwkid, ep)) cmd = "00" if snwkid not in self.ListOfDevices: - Domoticz.Error(" - nwkid: %s do not exist" % snwkid) + self.log.logging( "PollControl", "Error"," - nwkid: %s do not exist" % snwkid) return cluster_id = "0020" # Poll Control Cluster @@ -89,7 +89,7 @@ def poll_checkin_command(self, Sqn, snwkid, ep): payload = cluster_frame + Sqn + cmd raw_APS_request(self, snwkid, ep, "0020", "0104", payload) - Domoticz.Log("Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, snwkid, ep, payload)) + self.log.logging( "PollControl", "Debug", "Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, snwkid, ep, payload)) return @@ -98,11 +98,11 @@ def poll_checkin_response_command(self, Sqn, nwkid, ep, ContinueFastPoll=True, D # Poll Control: Check-in Response # 0 quarterseconds ( 0xFC0 => 1 minute of Fast Polling) - Domoticz.Log("poll_checkin_response_command %s/%s %s %s" % (nwkid, ep, ContinueFastPoll, DurationFastPoll)) + self.log.logging( "PollControl", "Debug", "poll_checkin_response_command %s/%s %s %s" % (nwkid, ep, ContinueFastPoll, DurationFastPoll)) cmd = "00" # Check-in Response if nwkid not in self.ListOfDevices: - Domoticz.Error(" - nwkid: %s do not exist" % nwkid) + self.log.logging( "PollControl", "Error", " - nwkid: %s do not exist" % nwkid) return cluster_id = "0020" # Poll Control Cluster ContinueFastPoll = "%02x" % ContinueFastPoll @@ -111,58 +111,58 @@ def poll_checkin_response_command(self, Sqn, nwkid, ep, ContinueFastPoll=True, D cluster_frame = "11" payload = cluster_frame + Sqn + cmd + ContinueFastPoll + DurationFastPoll raw_APS_request(self, nwkid, ep, "0020", "0104", payload) - Domoticz.Log("Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) + self.log.logging( "PollControl", "Debug", "Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) def poll_fast_poll_stop(self, Sqn, nwkid, ep): # Fast Poll Stop to be called for Remote Devices - Domoticz.Log("poll_fast_poll_stop %s/%s" % (nwkid, ep)) + self.log.logging( "PollControl", "Debug", "poll_fast_poll_stop %s/%s" % (nwkid, ep)) cmd = "01" # Fast Poll Stop ( no data) if nwkid not in self.ListOfDevices: - Domoticz.Error("Fast Poll Stop - nwkid: %s do not exist" % nwkid) + self.log.logging( "PollControl", "Error", "Fast Poll Stop - nwkid: %s do not exist" % nwkid) return cluster_id = "0020" # Poll Control Cluster cluster_frame = "11" payload = cluster_frame + Sqn + cmd raw_APS_request(self, nwkid, ep, "0020", "0104", payload) - Domoticz.Log("send Fast Poll Stop command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) + self.log.logging( "PollControl", "Debug","send Fast Poll Stop command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) def poll_set_long_poll_interval(self, Sqn, nwkid, ep, NewLongPollInterval=0x14): - Domoticz.Log("poll_set_long_poll_interval %s/%s %s" % (nwkid, ep, NewLongPollInterval)) + self.log.logging( "PollControl", "Debug", "poll_set_long_poll_interval %s/%s %s" % (nwkid, ep, NewLongPollInterval)) cmd = "02" NewLongPollInterval = "%08x" % (struct.unpack(">L", struct.pack("L", int("%08x" % NewLongPollInterval, 16)))[0]) if nwkid not in self.ListOfDevices: - Domoticz.Error(" - nwkid: %s do not exist" % nwkid) + self.log.logging( "PollControl", "Error", " - nwkid: %s do not exist" % nwkid) return cluster_id = "0020" # Poll Control Cluster cluster_frame = "11" payload = cluster_frame + Sqn + cmd + NewLongPollInterval raw_APS_request(self, nwkid, ep, "0020", "0104", payload) - Domoticz.Log("Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) + self.log.logging( "PollControl", "Debug","Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) return def poll_set_short_poll_interval(self, Sqn, nwkid, ep, NewShortPollInterval=0x2): - Domoticz.Log("poll_set_short_poll_interval %s/%s %s" % (nwkid, ep, NewShortPollInterval)) + self.log.logging( "PollControl", "Debug","poll_set_short_poll_interval %s/%s %s" % (nwkid, ep, NewShortPollInterval)) cmd = "03" NewShortPollInterval = "%04x" % (struct.unpack(">H", struct.pack("H", int("%04x" % NewShortPollInterval, 16)))[0]) if nwkid not in self.ListOfDevices: - Domoticz.Error(" - nwkid: %s do not exist" % nwkid) + self.log.logging( "PollControl", "Error", " - nwkid: %s do not exist" % nwkid) return cluster_id = "0020" # Poll Control Cluster cluster_frame = "11" payload = cluster_frame + Sqn + cmd + NewShortPollInterval raw_APS_request(self, nwkid, ep, "0020", "0104", payload) - Domoticz.Log("Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) + self.log.logging( "PollControl", "Debug", "Accept Fast Poll command 0x%s for %s/%s with payload: %s" % (cmd, nwkid, ep, payload)) diff --git a/Modules/readAttributes.py b/Modules/readAttributes.py index 45d8a0c5f..6e5a33195 100644 --- a/Modules/readAttributes.py +++ b/Modules/readAttributes.py @@ -13,28 +13,25 @@ import time -import Domoticz - import Modules.paramDevice from Modules.basicOutputs import (identifySend, read_attribute, send_zigatecmd_zcl_ack, send_zigatecmd_zcl_noack) -from Modules.macPrefix import DEVELCO_PREFIX, casaiaPrefix, OWON_PREFIX +from Modules.macPrefix import DEVELCO_PREFIX, OWON_PREFIX, casaiaPrefix from Modules.manufacturer_code import (PREFIX_MAC_LEN, PREFIX_MACADDR_CASAIA, PREFIX_MACADDR_IKEA_TRADFRI, PREFIX_MACADDR_OPPLE, - PREFIX_MACADDR_TUYA, TUYA_MANUF_CODE, - PREFIX_MACADDR_XIAOMI) -from Modules.tools import (check_datastruct, getListOfEpForCluster, - is_ack_tobe_disabled, is_attr_unvalid_datastruct, - is_time_to_perform_work, reset_attr_datastruct, - set_isqn_datastruct, set_status_datastruct, - set_timestamp_datastruct) -from Zigbee.zclRawCommands import rawaps_read_attribute_req + PREFIX_MACADDR_TUYA, + PREFIX_MACADDR_XIAOMI, TUYA_MANUF_CODE) +from Modules.tools import (check_datastruct, get_deviceconf_parameter_value, + getListOfEpForCluster, is_ack_tobe_disabled, + is_attr_unvalid_datastruct, is_time_to_perform_work, + reset_attr_datastruct, set_isqn_datastruct, + set_status_datastruct, set_timestamp_datastruct) from Modules.tuya import tuya_cmd_0x0000_0xf0 from Modules.zigateConsts import ZIGATE_EP from Modules.zlinky import get_OPTARIF - +from Zigbee.zclRawCommands import rawaps_read_attribute_req ATTRIBUTES = { "0000": [ 0x0004, 0x0005, 0x0000, 0x0001, 0x0002, 0x0003, 0x0006, 0x0007, 0x000A, 0x000F, 0x0010, 0x0015, 0x4000, 0xF000, ], @@ -190,13 +187,8 @@ def skipThisAttribute(self, addr, EpOut, Cluster, Attr): if Cluster not in self.DeviceConf[model]["ReadAttributes"]: return False if Attr in self.DeviceConf[model]["ReadAttributes"][Cluster]: - Domoticz.Log("Skip Attribute 6 %s/%s %s %s" % (addr, EpOut, Cluster, Attr)) - self.log.logging( - "ReadAttributes", - "Debug", - "normalizedReadAttrReq - Skip Read Attribute due to DeviceConf Nwkid: %s Cluster: %s Attribute: %s" % (addr, Cluster, Attr), - nwkid=addr, - ) + self.log.logging( "ReadAttributes", "Debug", "Skip Attribute 6 %s/%s %s %s" % (addr, EpOut, Cluster, Attr)) + self.log.logging( "ReadAttributes", "Debug", "normalizedReadAttrReq - Skip Read Attribute due to DeviceConf Nwkid: %s Cluster: %s Attribute: %s" % (addr, Cluster, Attr), nwkid=addr, ) return False return True @@ -502,7 +494,6 @@ def ReadAttributeRequest_0000_for_general(self, key): or ("Model" in self.ListOfDevices[key] and self.ListOfDevices[key]["Model"] in ("EH-ZB-VAC")) ): # We need to break the Read Attribute between Manufacturer specifcs one and teh generic one - # Domoticz.Log("Specific Manufacturer !!!!") manufacturer_code = "105e" for _attr in list(listAttributes): if _attr in (0xE000, 0xE001, 0xE002): @@ -522,9 +513,6 @@ def ReadAttributeRequest_0000_for_general(self, key): del listAttributes listAttributes = listAttrGeneric - - # Domoticz.Log("List Attributes: " + " ".join("0x{:04x}".format(num) for num in listAttributes) ) - if listAttributes: # self.log.logging( "ReadAttributes", 'Debug', "Request Basic via Read Attribute request %s/%s %s" %(key, EPout, str(listAttributes)), nwkid=key) self.log.logging( @@ -536,7 +524,6 @@ def ReadAttributeRequest_0000_for_general(self, key): ) ReadAttributeReq( self, key, ZIGATE_EP, EPout, "0000", listAttributes, ackIsDisabled=is_ack_tobe_disabled(self, key), checkTime=False, ) - # Domoticz.Log("List Attributes Manuf Spec: " + " ".join("0x{:04x}".format(num) for num in listAttrSpecific) ) if listAttrSpecific: # self.log.logging( "ReadAttributes", 'Debug', "Request Basic via Read Attribute request Manuf Specific %s/%s %s" %(key, EPout, str(listAttrSpecific)), nwkid=key) self.log.logging( @@ -600,6 +587,9 @@ def ReadAttributeRequest_0002(self, key, force_disable_ack=None): def ReadAttributeRequest_0006_0000(self, key): self.log.logging("ReadAttributes", "Debug", "ReadAttributeRequest_0006 focus on 0x0000 Key: %s " % key, nwkid=key) + if get_deviceconf_parameter_value(self, self.ListOfDevices[key]["Model"], "DisableOnOffPolling"): + return + ListOfEp = getListOfEpForCluster(self, key, "0006") for EPout in ListOfEp: listAttributes = [0] @@ -647,6 +637,9 @@ def ReadAttributeRequest_0006(self, key): for EPout in ListOfEp: listAttributes = [] for iterAttr in retreive_ListOfAttributesByCluster(self, key, EPout, "0006"): + if get_deviceconf_parameter_value(self, self.ListOfDevices[key]["Model"], "DisableOnOffPolling") and iterAttr == 0x0000: + continue + if iterAttr not in listAttributes: listAttributes.append(iterAttr) @@ -662,9 +655,11 @@ def ReadAttributeRequest_0006(self, key): def ReadAttributeRequest_0008_0000(self, key): self.log.logging("ReadAttributes", "Debug", "ReadAttributeRequest_0008 focus on 0x0008/0000 Key: %s " % key, nwkid=key) + if get_deviceconf_parameter_value(self, self.ListOfDevices[key]["Model"], "DisableLevelControlPolling"): + return + ListOfEp = getListOfEpForCluster(self, key, "0008") for EPout in ListOfEp: - listAttributes = [0] ReadAttributeReq(self, key, ZIGATE_EP, EPout, "0008", listAttributes, ackIsDisabled=is_ack_tobe_disabled(self, key)) @@ -677,6 +672,8 @@ def ReadAttributeRequest_0008(self, key): for EPout in ListOfEp: listAttributes = [] for iterAttr in retreive_ListOfAttributesByCluster(self, key, EPout, "0008"): + if get_deviceconf_parameter_value(self, self.ListOfDevices[key]["Model"], "DisableLevelControlPolling") and iterAttr == 0x0000: + continue if iterAttr not in listAttributes: listAttributes.append(iterAttr) @@ -888,8 +885,6 @@ def ReadAttributeRequest_0201(self, key): del listAttributes listAttributes = listAttrGeneric - # Domoticz.Log("List Attributes: " + " ".join("0x{:04x}".format(num) for num in listAttributes) ) - if listAttributes: # self.log.logging( "ReadAttributes", 'Debug', "Request 0201 %s/%s 0201 %s " %(key, EPout, listAttributes), nwkid=key) self.log.logging( @@ -910,7 +905,6 @@ def ReadAttributeRequest_0201(self, key): checkTime=False, ) - # Domoticz.Log("List Attributes Manuf Spec: " + " ".join("0x{:04x}".format(num) for num in listAttrSpecific) ) if listAttrSpecific: # self.log.logging( "ReadAttributes", 'Debug', "Request Thermostat info via Read Attribute request Manuf Specific %s/%s %s" %(key, EPout, str(listAttrSpecific)), nwkid=key) self.log.logging( diff --git a/Modules/readClusters.py b/Modules/readClusters.py index fbe141542..94f9f8837 100644 --- a/Modules/readClusters.py +++ b/Modules/readClusters.py @@ -14,9 +14,6 @@ # import time import struct from time import time - -import Domoticz - from Modules.batterieManagement import UpdateBatteryAttribute from Modules.domoMaj import MajDomoDevice from Modules.domoTools import timedOutDevice @@ -187,6 +184,7 @@ def ReadCluster( self, Devices, MsgType, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgCluste return if is_cluster_zcl_config_available( self, MsgSrcAddr, MsgSrcEp, MsgClusterId, attribute=MsgAttrID): + storeReadAttributeStatus(self, MsgType, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttrStatus) process_cluster_attribute_response( self, Devices, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID, MsgAttType, MsgAttSize, MsgClusterData, Source, ) elif MsgClusterId in DECODE_CLUSTER: @@ -269,7 +267,6 @@ def Cluster0006(self, Devices, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAt elif MsgClusterData == "cd": # short reset , a short click on the reset button return else: - # Domoticz.Log("Konke Multi Purpose Switch - Unknown Value: %s" %MsgClusterData) return self.log.logging( "Cluster", @@ -951,7 +948,7 @@ def Cluster0101(self, Devices, MsgSQN, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAt if len(MsgClusterData) != 12: # https://github.com/fairecasoimeme/ZiGate/issues/229 - Domoticz.Log("Needs Firmware 3.1b to decode this data") + self.log.logging( "Cluster", "Debug", "Needs Firmware 3.1b to decode this data") angleX, angleY, angleZ = decode_vibrAngle(MsgClusterData) diff --git a/Modules/restartPlugin.py b/Modules/restartPlugin.py index 181bd58de..bb29e8f94 100644 --- a/Modules/restartPlugin.py +++ b/Modules/restartPlugin.py @@ -23,11 +23,6 @@ def restartPluginViaDomoticzJsonApi(self, stop=False, erasePDM=False, url_base_a erasePDM = "True" if erasePDM else "False" enabled = "false" if stop else "true" - #if webUserName and webPassword: - # url = "http://%s:%s@127.0.0.1:%s" % (self.WebUsername, self.WebPassword, self.pluginconf.pluginConf["port"]) - #else: - # url = "http://127.0.0.1:%s" % self.pluginconf.pluginConf["port"] - url = url_base_api + "/json.htm?" url_infos = { diff --git a/Modules/schneider_wiser.py b/Modules/schneider_wiser.py index 5d9a6f67d..43e351554 100755 --- a/Modules/schneider_wiser.py +++ b/Modules/schneider_wiser.py @@ -15,9 +15,6 @@ import struct from time import time -import Domoticz -from Zigbee.zclCommands import zcl_onoff_off_noeffect, zcl_onoff_on - from Modules.basicOutputs import read_attribute, write_attribute from Modules.bindings import WebBindStatus, webBind from Modules.domoMaj import MajDomoDevice @@ -29,6 +26,7 @@ retreive_cmd_payload_from_8002) from Modules.writeAttributes import write_attribute_when_awake from Modules.zigateConsts import MAX_LOAD_ZIGATE, ZIGATE_EP +from Zigbee.zclCommands import zcl_onoff_off_noeffect, zcl_onoff_on WISER_LEGACY_MODEL_NAME_PREFIX = "EH-ZB" WISER_LEGACY_BASE_EP = "0b" @@ -133,7 +131,6 @@ def wiser_thermostat_monitoring_heating_demand(self, Devices): updated_pi_demand += 100 if cnt_actioners: - # Domoticz.Log("---- Actioners: %s Pi Demand: %s" %(cnt_actioners,updated_pi_demand )) self.ListOfDevices[NwkId]["Ep"]["01"]["0201"]["0008"] = int(round(updated_pi_demand / cnt_actioners)) MajDomoDevice( self, @@ -367,7 +364,7 @@ def wiser_set_calibration(self, key, EPout): # 0x0201/0x0010 # in two’s complement form calibration = int(hex(-calibration - pow(2, 32))[9:], 16) - Domoticz.Log("Calibration: 0x%02x" % calibration) + self.log.logging( "Schneider", "Log", "Calibration: 0x%02x" % calibration) manuf_id = "0000" manuf_spec = "00" @@ -1237,11 +1234,9 @@ def schneiderAlarmReceived(self, Devices, NWKID, srcEp, ClusterID, start, payloa else: schneider_bms_change_reporting(self, NWKID, srcEp, False) - if "Shedding" in self.ListOfDevices[NWKID]: - if not self.ListOfDevices[NWKID]["Shedding"]: - self.log.logging("Schneider", "Debug", "schneiderAlarmReceived not shedding - EXIT", NWKID) - return # we are already shedding - value = "00" + if "Shedding" in self.ListOfDevices[NWKID] and not self.ListOfDevices[NWKID]["Shedding"]: + self.log.logging("Schneider", "Debug", "schneiderAlarmReceived not shedding - EXIT", NWKID) + return self.ListOfDevices[NWKID]["Shedding"] = False self.log.logging( diff --git a/Modules/sendZigateCommand.py b/Modules/sendZigateCommand.py index bce60eeaa..4228659ed 100644 --- a/Modules/sendZigateCommand.py +++ b/Modules/sendZigateCommand.py @@ -10,11 +10,12 @@ """ -from Modules.zigateConsts import ADDRESS_MODE, ZIGATE_COMMANDS, ZIGATE_EP - import sys import time +from Modules.zigateConsts import ADDRESS_MODE, ZIGATE_COMMANDS, ZIGATE_EP + + def add_Last_Cmds(self, isqn, address_mode, nwkid, cmd, datas): if nwkid not in self.ListOfDevices: diff --git a/Modules/thermostats.py b/Modules/thermostats.py index 8a369aead..43176fcc1 100644 --- a/Modules/thermostats.py +++ b/Modules/thermostats.py @@ -4,16 +4,15 @@ # Author: zaraki673 & pipiche38 # -import Domoticz - from Modules.basicOutputs import write_attribute from Modules.casaia import casaia_check_irPairing, casaia_setpoint from Modules.danfoss import thermostat_Setpoint_Danfoss from Modules.readAttributes import ReadAttributeRequest_0201 from Modules.schneider_wiser import schneider_setpoint -from Modules.tuyaTRV import tuya_setpoint -from Modules.tuyaTS0601 import ts0601_extract_data_point_infos, ts0601_actuator, ts0601_action_calibration from Modules.tuyaConst import TUYA_eTRV_MODEL +from Modules.tuyaTRV import tuya_setpoint +from Modules.tuyaTS0601 import (ts0601_action_calibration, ts0601_actuator, + ts0601_extract_data_point_infos) def thermostat_Setpoint_SPZB(self, NwkId, setpoint): @@ -126,9 +125,9 @@ def thermostat_Setpoint(self, NwkId, setpoint): if self.zigbee_communication == "native" and self.ZiGateModel == 2 and int(self.FirmwareVersion, 16) < 0x0320: # Bug on ZiGate V2 - firmware 0x320 fix it - Domoticz.Log("---Zigate Model: %s Version: %s" % (self.ZiGateModel, self.FirmwareVersion)) + self.log.logging("Thermostats", "Debug", "---Zigate Model: %s Version: %s" % (self.ZiGateModel, self.FirmwareVersion)) Hdata = Hdata[2:4] + Hdata[:2] - Domoticz.Log("Patch Hdata %s" % Hdata) + self.log.logging("Thermostats", "Debug", "Patch Hdata %s" % Hdata) EPout = "01" self.log.logging( "Thermostats", "Deug", "thermostat_Setpoint - for %s with value 0x%s / cluster: %s, attribute: %s type: %s" % ( @@ -258,7 +257,7 @@ def thermostat_Mode(self, NwkId, mode): } if mode not in SYSTEM_MODE: - Domoticz.Error("thermostat_Mode - unknown system mode: %s" % mode) + self.log.logging("Thermostats", "Error", "thermostat_Mode - unknown system mode: %s" % mode) return if "Model" in self.ListOfDevices[NwkId] and self.ListOfDevices[NwkId]["Model"] in ("AC211", "AC221", "CAC221"): diff --git a/Modules/timeServer.py b/Modules/timeServer.py index 200697321..c37301859 100644 --- a/Modules/timeServer.py +++ b/Modules/timeServer.py @@ -5,9 +5,10 @@ # -from Modules.basicInputs import read_attribute_response from datetime import datetime +from Modules.basicInputs import read_attribute_response + def timeserver_read_attribute_request(self, sqn, nwkid, ep, cluster, manuf_spec, manuf_code, attribute): diff --git a/Modules/tools.py b/Modules/tools.py index 405dbdf84..818698dd2 100644 --- a/Modules/tools.py +++ b/Modules/tools.py @@ -11,12 +11,10 @@ """ import datetime -import time import os.path +import time from pathlib import Path -import Domoticz - from Modules.database import WriteDeviceList from Modules.pluginDbAttributes import STORE_CONFIGURE_REPORTING from Modules.zigateConsts import HEARTBEAT @@ -188,7 +186,7 @@ def DeviceExist(self, Devices, lookupNwkId, lookupIEEE=""): return found # We are in situation where we found the device in ListOfDevices but not in IEEE2NWK. # this is not expected - self.log.logging("Input", "Error", "DeviceExist - Found %s some inconsistency Inputs: %s %s instead of %s" % ( + self.log.logging("PluginTools", "Error", "DeviceExist - Found %s some inconsistency Inputs: %s %s instead of %s" % ( found, lookupNwkId, lookupIEEE, ieee_from_nwkid)) return found @@ -205,7 +203,7 @@ def DeviceExist(self, Devices, lookupNwkId, lookupIEEE=""): # in ListOfDevices !! # Let's cleanup del self.IEEE2NWK[lookupIEEE] - self.log.logging("Input", "Error", "DeviceExist - Found inconsistency ! Not Device %s not found, while looking for %s (%s)" % ( + self.log.logging("PluginTools", "Error", "DeviceExist - Found inconsistency ! Not Device %s not found, while looking for %s (%s)" % ( exitsingNwkId, lookupIEEE, lookupNwkId)) return False @@ -218,7 +216,7 @@ def DeviceExist(self, Devices, lookupNwkId, lookupIEEE=""): del self.IEEE2NWK[ lookupIEEE ] # Delete the all Data Structure del self.ListOfDevices[ exitsingNwkId ] - self.log.logging("Input", "Error", "DeviceExist - Found inconsistency ! Not 'Status' attribute for Device %s, while looking for %s (%s)" % ( + self.log.logging("PluginTools", "Error", "DeviceExist - Found inconsistency ! Not 'Status' attribute for Device %s, while looking for %s (%s)" % ( exitsingNwkId, lookupIEEE, lookupNwkId)) return False @@ -232,7 +230,7 @@ def DeviceExist(self, Devices, lookupNwkId, lookupIEEE=""): del self.IEEE2NWK[lookupIEEE] # Delete the all Data Structure del self.ListOfDevices[exitsingNwkId] - self.log.logging("Input", "Status", "DeviceExist - Device %s changed its ShortId: from %s to %s during provisioning. Restarting !" % ( + self.log.logging("PluginTools", "Status", "DeviceExist - Device %s changed its ShortId: from %s to %s during provisioning. Restarting !" % ( lookupIEEE, exitsingNwkId, lookupNwkId)) return False @@ -262,7 +260,7 @@ def reconnectNWkDevice(self, new_NwkId, IEEE, old_NwkId): return True if new_NwkId == "0000" or old_NwkId == "0000": - self.log.logging("Input", "Log", "reconnectNWkDevice - Looks like we have an IEEE matching a Coordinator nwkid , this is not possible by definition New: %s Old: %s IEEE: %s !!!" % ( + self.log.logging("PluginTools", "Log", "reconnectNWkDevice - Looks like we have an IEEE matching a Coordinator nwkid , this is not possible by definition New: %s Old: %s IEEE: %s !!!" % ( new_NwkId, old_NwkId, IEEE)) return False @@ -274,7 +272,7 @@ def reconnectNWkDevice(self, new_NwkId, IEEE, old_NwkId): # MostLikely exitsingKey(the old NetworkID) is not needed any more if removeNwkInList(self, old_NwkId) is None: - self.log.logging("Input", "Error", "reconnectNWkDevice - something went wrong in the reconnect New NwkId: %s Old NwkId: %s IEEE: %s" % ( + self.log.logging("PluginTools", "Error", "reconnectNWkDevice - something went wrong in the reconnect New NwkId: %s Old NwkId: %s IEEE: %s" % ( new_NwkId, old_NwkId, IEEE)) if self.groupmgt: @@ -285,7 +283,7 @@ def reconnectNWkDevice(self, new_NwkId, IEEE, old_NwkId): if self.ListOfDevices[new_NwkId]["Status"] in ( "Leave", ): self.ListOfDevices[new_NwkId]["Status"] = "inDB" self.ListOfDevices[new_NwkId]["Heartbeat"] = "0" - self.log.logging("Input", "Status", "reconnectNWkDevice - Update Status from %s to 'inDB' for NetworkID : %s" % ( + self.log.logging("PluginTools", "Status", "reconnectNWkDevice - Update Status from %s to 'inDB' for NetworkID : %s" % ( self.ListOfDevices[new_NwkId]["Status"], new_NwkId)) # We will also reset ReadAttributes @@ -297,7 +295,7 @@ def reconnectNWkDevice(self, new_NwkId, IEEE, old_NwkId): self.ListOfDevices[new_NwkId]["Heartbeat"] = "0" WriteDeviceList(self, 0) - self.log.logging("Input", "Status", "NetworkID: %s is replacing %s for object: %s" % (new_NwkId, old_NwkId, IEEE)) + self.log.logging("PluginTools", "Status", "NetworkID: %s is replacing %s for object: %s" % (new_NwkId, old_NwkId, IEEE)) return True @@ -329,13 +327,11 @@ def removeDeviceInList(self, Devices, IEEE, Unit): key = self.IEEE2NWK[IEEE] ID = Devices[Unit].ID - #Domoticz.Log("removeDeviceInList - request to remove Device: %s with IEEE: %s " % (key, IEEE)) - if ( "ClusterTye" in self.ListOfDevices[key] ): # We are in the old fasho V. 3.0.x Where ClusterType has been migrated from Domoticz if str(ID) in self.ListOfDevices[key]["ClusterType"]: del self.ListOfDevices[key]["ClusterType"][ID] # Let's remove that entry - Domoticz.Log("removeDeviceInList - removing : %s in %s" % (ID, str(self.ListOfDevices[key]["ClusterType"]))) + self.log.logging("PluginTools", "Log", "removeDeviceInList - removing : %s in %s" % (ID, str(self.ListOfDevices[key]["ClusterType"]))) else: for tmpEp in list(self.ListOfDevices[key]["Ep"].keys()): @@ -345,20 +341,18 @@ def removeDeviceInList(self, Devices, IEEE, Unit): and str(ID) in self.ListOfDevices[key]["Ep"][tmpEp]["ClusterType"] ): del self.ListOfDevices[key]["Ep"][tmpEp]["ClusterType"][str(ID)] - Domoticz.Log( - "removeDeviceInList - removing : %s with Ep: %s in - %s" - % (ID, tmpEp, str(self.ListOfDevices[key]["Ep"][tmpEp]["ClusterType"])) - ) + self.log.logging("PluginTools", "Log", "removeDeviceInList - removing : %s with Ep: %s in - %s" % ( + ID, tmpEp, str(self.ListOfDevices[key]["Ep"][tmpEp]["ClusterType"])) ) # Finaly let's see if there is any Devices left in this . emptyCT = True if "ClusterType" in self.ListOfDevices[key]: # Empty or Doesn't exist - Domoticz.Log("removeDeviceInList - existing Global 'ClusterTpe'") + self.log.logging("PluginTools", "Log", "removeDeviceInList - existing Global 'ClusterTpe'") if self.ListOfDevices[key]["ClusterType"] != {}: emptyCT = False for tmpEp in list(self.ListOfDevices[key]["Ep"].keys()): if "ClusterType" in self.ListOfDevices[key]["Ep"][tmpEp]: - Domoticz.Log("removeDeviceInList - existing Ep 'ClusterTpe'") + self.log.logging("PluginTools", "Log", "removeDeviceInList - existing Ep 'ClusterTpe'") if self.ListOfDevices[key]["Ep"][tmpEp]["ClusterType"] != {}: emptyCT = False @@ -370,7 +364,7 @@ def removeDeviceInList(self, Devices, IEEE, Unit): self.adminWidgets.updateNotificationWidget( Devices, "Device fully removed %s with IEEE: %s" % (Devices[Unit].Name, IEEE) ) - Domoticz.Status("Device %s with IEEE: %s fully removed from the system." % (Devices[Unit].Name, IEEE)) + self.log.logging("PluginTools", "Status", "Device %s with IEEE: %s fully removed from the system." % (Devices[Unit].Name, IEEE)) return True return False @@ -458,7 +452,6 @@ def updSQN(self, key, newSQN): if newSQN is None: return - # Domoticz.Log("-->SQN updated %s from %s to %s" %(key, self.ListOfDevices[key]['SQN'], newSQN)) self.ListOfDevices[key]["SQN"] = newSQN return @@ -549,7 +542,7 @@ def getListofClusterbyModel(self, Model, InOut): if InOut == "" or InOut is None: return listofCluster if InOut not in ["Epin", "Epout"]: - Domoticz.Error("getListofClusterbyModel - Argument error : " + Model + " " + InOut) + self.log.logging("PluginTools", "Error", "getListofClusterbyModel - Argument error : " + Model + " " + InOut) return "" if Model in self.DeviceConf and InOut in self.DeviceConf[Model]: @@ -727,7 +720,7 @@ def ReArrangeMacCapaBasedOnModel(self, nwkid, inMacCapa): Return the old or the revised MacCapa and eventually fix some Attributes """ if nwkid not in self.ListOfDevices: - Domoticz.Error("%s not known !!!" % nwkid) + self.log.logging("PluginTools", "Error", "%s not known !!!" % nwkid) return inMacCapa if "Model" not in self.ListOfDevices[nwkid]: @@ -741,7 +734,9 @@ def ReArrangeMacCapaBasedOnModel(self, nwkid, inMacCapa): # Livol Switch, must be converted to Main Powered # Patch some status as Device Annouced doesn't provide much info self.ListOfDevices[nwkid]["LogicalType"] = "Router" - self.ListOfDevices[nwkid]["DevideType"] = "FFD" + # Not DevideType but DeviceType + # self.ListOfDevices[nwkid]["DevideType"] = "FFD" + self.ListOfDevices[nwkid]["DeviceType"] = "FFD" self.ListOfDevices[nwkid]["MacCapa"] = "8e" self.ListOfDevices[nwkid]["PowerSource"] = "Main" return "8e" @@ -774,7 +769,7 @@ def mainPoweredDevice(self, nwkid): """ if nwkid not in self.ListOfDevices: - Domoticz.Log("mainPoweredDevice - Unknown Device: %s" % nwkid) + self.log.logging("PluginTools", "Debug", "mainPoweredDevice - Unknown Device: %s" % nwkid) return False model_name = "" @@ -806,7 +801,9 @@ def mainPoweredDevice(self, nwkid): ): mainPower = True self.ListOfDevices[nwkid]["LogicalType"] = "End Device" - self.ListOfDevices[nwkid]["DevideType"] = "RFD" + # Not DevideType but DeviceType + # self.ListOfDevices[nwkid]["DevideType"] = "RFD" + self.ListOfDevices[nwkid]["DeviceType"] = "RFD" if not mainPower and "PowerSource" in self.ListOfDevices[nwkid] and self.ListOfDevices[nwkid]["PowerSource"] != {}: mainPower = self.ListOfDevices[nwkid]["PowerSource"] == "Main" @@ -836,10 +833,8 @@ def loggingMessages(self, msgtype, sAddr=None, ieee=None, LQI=None, SQN=None): if sAddr in self.ListOfDevices and "ZDeviceName" in self.ListOfDevices[sAddr]: zdevname = self.ListOfDevices[sAddr]["ZDeviceName"] - Domoticz.Log( - "Device activity for | %4s | %14s | %4s | %16s | %3s | 0x%02s |" - % (msgtype, zdevname, sAddr, ieee, int(LQI, 16), SQN) - ) + self.log.logging("PluginTools", "Log", "Device activity for | %4s | %14s | %4s | %16s | %3s | 0x%02s |" % ( + msgtype, zdevname, sAddr, ieee, int(LQI, 16), SQN) ) def try_to_reconnect_via_neighbours(self, old_nwkid): @@ -870,7 +865,7 @@ def try_to_reconnect_via_neighbours(self, old_nwkid): new_nwkid = x if new_nwkid != old_nwkid: reconnectNWkDevice(self, new_nwkid, ieee, old_nwkid) - Domoticz.Log("try_to_reconnect_via_neighbours found %s as replacement of %s" % (new_nwkid, old_nwkid)) + self.log.logging("PluginTools", "Log", "try_to_reconnect_via_neighbours found %s as replacement of %s" % (new_nwkid, old_nwkid)) return new_nwkid def chk_and_update_IEEE_NWKID(self, nwkid, ieee): @@ -886,7 +881,7 @@ def chk_and_update_IEEE_NWKID(self, nwkid, ieee): return old_nwkid = self.IEEE2NWK[ ieee ] - self.log.logging("Input", "Log", "chk_and_update_IEEE_NWKID - update %s %s -> %s" %(ieee, old_nwkid, nwkid)) + self.log.logging("PluginTools", "Log", "chk_and_update_IEEE_NWKID - update %s %s -> %s" %(ieee, old_nwkid, nwkid)) reconnectNWkDevice(self, nwkid, ieee, old_nwkid) def lookupForIEEE(self, nwkid, reconnect=False): @@ -895,7 +890,6 @@ def lookupForIEEE(self, nwkid, reconnect=False): # This is used when receiving a message from an unknown device ! # """ - # Domoticz.Log("lookupForIEEE - looking for %s in Neighbourgs table" %nwkid) for key in list(self.ListOfDevices.keys()): if "Neighbours" not in self.ListOfDevices[key]: continue @@ -916,15 +910,13 @@ def lookupForIEEE(self, nwkid, reconnect=False): old_NwkId = self.IEEE2NWK[ieee] if old_NwkId not in self.ListOfDevices: del self.IEEE2NWK[ieee] - Domoticz.Error( - "lookupForIEEE found an inconsitency %s not existing but pointed by %s, cleanup" % (old_NwkId, ieee) - ) + self.log.logging("PluginTools", "Error", "lookupForIEEE found an inconsitency %s not existing but pointed by %s, cleanup" % ( + old_NwkId, ieee) ) continue if reconnect: reconnectNWkDevice(self, nwkid, ieee, old_NwkId) - Domoticz.Log( - "lookupForIEEE found a matching IEEE: %s in the Router Neighbours %s with Nwkid: %s (old Nwkid was %s)" %( + self.log.logging("PluginTools", "Status", "lookupForIEEE found a matching IEEE: %s in the Router Neighbours %s with Nwkid: %s (old Nwkid was %s)" %( ieee, key, nwkid, old_NwkId)) return ieee return None @@ -940,7 +932,7 @@ def zigpy_plugin_sanity_check(self, nwkid): if self.IEEE2NWK[ ieee ] == nwkid and nwkid in self.ListOfDevices: if "Status" in self.ListOfDevices[ nwkid ] and self.ListOfDevices[ nwkid ]["Status"] in ( 'Leave', ): # the device is alive and ieee/nwkid is correct - self.log.logging("Input", "Status", "zigpy_plugin_sanity_check - Update Status from %s to 'inDB' for NetworkID : %s" % ( + self.log.logging("PluginTools", "Status", "zigpy_plugin_sanity_check - Update Status from %s to 'inDB' for NetworkID : %s" % ( self.ListOfDevices[nwkid]["Status"], nwkid), nwkid) self.ListOfDevices[ nwkid ]["Status"] = 'inDB' self.ListOfDevices[nwkid]["Heartbeat"] = "0" @@ -1023,19 +1015,19 @@ def checkValidValue(self, MsgSrcAddr, AttType, Data ): def getAttributeValue(self, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID): if MsgSrcAddr not in self.ListOfDevices: - self.log.logging("Input", "Debug", "getAttributeValue - Unknown %s" % (MsgSrcAddr)) + self.log.logging("PluginTools", "Debug", "getAttributeValue - Unknown %s" % (MsgSrcAddr)) return None if MsgSrcEp not in self.ListOfDevices[MsgSrcAddr]["Ep"]: - self.log.logging("Input", "Debug", "getAttributeValue - Unknown %s/%s" % (MsgSrcAddr, MsgSrcEp)) + self.log.logging("PluginTools", "Debug", "getAttributeValue - Unknown %s/%s" % (MsgSrcAddr, MsgSrcEp)) return None if MsgClusterId not in self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp]: - self.log.logging("Input", "Debug", "getAttributeValue - Unknown %s/%s %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterId)) + self.log.logging("PluginTools", "Debug", "getAttributeValue - Unknown %s/%s %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterId)) return None if not isinstance(self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp][MsgClusterId], dict): - self.log.logging("Input", "Debug", "getAttributeValue - Not dict %s/%s %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterId)) + self.log.logging("PluginTools", "Debug", "getAttributeValue - Not dict %s/%s %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterId)) return None if MsgAttrID not in self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp][MsgClusterId]: - self.log.logging("Input", "Debug", "getAttributeValue - Unknown %s/%s %s %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID)) + self.log.logging("PluginTools", "Debug", "getAttributeValue - Unknown %s/%s %s %s" % (MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID)) return None return self.ListOfDevices[MsgSrcAddr]["Ep"][MsgSrcEp][MsgClusterId][MsgAttrID] @@ -1044,13 +1036,21 @@ def getAttributeValue(self, MsgSrcAddr, MsgSrcEp, MsgClusterId, MsgAttrID): def retreive_cmd_payload_from_8002(Payload): ManufacturerCode = None + if len(Payload) < 2: + return (None, None, None, None, None, None) + fcf = Payload[:2] - GlobalCommand = is_golbalcommand(fcf) - zbee_zcl_ddr = disable_default_response(fcf) - + try: + GlobalCommand = is_globalcommand(fcf) + zbee_zcl_ddr = disable_default_response(fcf) + except Exception as e: + return (None, None, None, None, None, None) + if GlobalCommand is None: - Domoticz.Error("Strange payload: %s" % Payload) + return (None, None, None, None, None, None) + + if len(Payload) < 6: return (None, None, None, None, None, None) if is_manufspecific_8002_payload(fcf): @@ -1063,7 +1063,6 @@ def retreive_cmd_payload_from_8002(Payload): Command = Payload[4:6] Data = Payload[6:] - # Domoticz.Log("retreive_cmd_payload_from_8002 ======> Payload: %s " %Data) return (zbee_zcl_ddr, GlobalCommand, Sqn, ManufacturerCode, Command, Data) def fcf_direction(fcf): @@ -1083,7 +1082,7 @@ def is_direction_to_client(fcf): def is_direction_to_server(fcf): return fcf_direction(fcf) == 0x0 -def is_golbalcommand(fcf): +def is_globalcommand(fcf): return None if not is_hex(fcf) or len(fcf) != 2 else (int(fcf, 16) & 0b00000011) == 0 def frame_type(fcf): @@ -1100,8 +1099,6 @@ def build_fcf(frame_type, manuf_spec, direction, disabled_default): fcf |= 0b1000 if int(disabled_default, 16): fcf |= 0b10000 - # Domoticz.Log("build_fcf FrameType: %s Manuf: %s Direction: %s DisabledDefault: %s ==> 0x%02x/%s" %( - # frame_type, manuf_spec, direction, disabled_default, fcf, bin(fcf))) return "%02x" % fcf def get_cluster_attribute_value( self, key, endpoint, clusterId, AttributeId): @@ -1308,9 +1305,7 @@ def is_attr_unvalid_datastruct(self, DeviceAttribute, key, endpoint, clusterId, lastStatus = get_status_datastruct(self, DeviceAttribute, key, endpoint, clusterId, AttributeId) if lastStatus is None: return False - if lastStatus in ("86", "8c"): - return True - return lastStatus != "00" + return True if lastStatus in ("86", "8c") else lastStatus != "00" def reset_attr_datastruct(self, DeviceAttribute, key, endpoint, clusterId, AttributeId): @@ -1366,108 +1361,21 @@ def is_ack_tobe_disabled(self, key): ) -def instrument_timing(module, timing, cnt_timing, cumul_timing, aver_timing, max_timing): - - cumul_timing += timing - cnt_timing += 1 - aver_timing = int(cumul_timing / cnt_timing) - if timing > max_timing: - Domoticz.Log("%s report a timing %s ms greated than the current max %s ms" % (module, timing, max_timing)) - max_timing = timing - - return cnt_timing, cumul_timing, aver_timing, max_timing - - -# Configuration Helpers -def setConfigItem(Key=None, Attribute="", Value=None): - - Domoticz.Log("Saving %s - %s into Domoticz sqlite Db" %( Key, Attribute)) - - Config = {} - if not isinstance(Value, (str, int, float, bool, bytes, bytearray, list, dict)): - Domoticz.Error("setConfigItem - A value is specified of a not allowed type: '" + str(type(Value)) + "'") - return Config - - if isinstance(Value, dict): - # There is an issue that Configuration doesn't allow None value in dictionary ! - # Replace none value to 'null' - Value = prepare_dict_for_storage(Value, Attribute) - - try: - Config = Domoticz.Configuration() - if Key is None: - Config = Value # set whole configuration if no key specified - else: - Config[Key] = Value - - Config = Domoticz.Configuration(Config) - except Exception as inst: - Domoticz.Error("setConfigItem - Domoticz.Configuration operation failed: '" + str(inst) + "'") - return None - return Config - - -def getConfigItem(Key=None, Attribute="", Default=None): - - Domoticz.Log("Loading %s - %s from Domoticz sqlite Db" %( Key, Attribute)) - - if Default is None: - Default = {} - Value = Default - try: - Config = Domoticz.Configuration() - Value = Config if Key is None else Config[Key] - except KeyError: - Value = Default - except Exception as inst: - Domoticz.Error( - "getConfigItem - Domoticz.Configuration read failed: '" - + str(inst) - + "'" - ) - - return repair_dict_after_load(Value, Attribute) - - -def prepare_dict_for_storage(dict_items, Attribute): - - from base64 import b64encode - - if Attribute in dict_items: - dict_items[Attribute] = b64encode(str(dict_items[Attribute]).encode("utf-8")) - dict_items["Version"] = 1 - return dict_items - - -def repair_dict_after_load(b64_dict, Attribute): - if b64_dict in ("", {}): - return {} - if "Version" not in b64_dict: - Domoticz.Log("repair_dict_after_load - Not supported storage") - return {} - if Attribute in b64_dict: - from base64 import b64decode - - b64_dict[Attribute] = eval(b64decode(b64_dict[Attribute])) - return b64_dict - - def is_domoticz_db_available(self): # Domoticz 2021.1 build 13495 if not self.VersionNewFashion: - #Domoticz.Log("is_domoticz_db_available: %s due to Fashion" % False) + self.log.logging("PluginTools", "Debug", "is_domoticz_db_available: %s due to Fashion" % False) return False if self.DomoticzMajor < 2021: - #Domoticz.Log("is_domoticz_db_available: %s due to Major" % False) + self.log.logging("PluginTools", "Debug", "is_domoticz_db_available: %s due to Major" % False) return False if self.DomoticzMajor == 2021 and self.DomoticzMinor < 1: - # Domoticz.Log("is_domoticz_db_available: %s due to Minor" % False) + self.log.logging("PluginTools", "Debug", "is_domoticz_db_available: %s due to Minor" % False) return False - #Domoticz.Log("is_domoticz_db_available: %s" % True) return True def get_device_nickname( self, NwkId=None, Ieee=None): @@ -1542,7 +1450,7 @@ def night_shift_jobs( self ): # Otherwise return True only if between midnight and 6am if not self.pluginconf.pluginConf["NightShift"]: - # Domoticz.Log("Always On" ) + #self.log.logging("PluginTools", "Debug", "Always On" ) return True current = datetime.datetime.now().time() @@ -1552,17 +1460,17 @@ def night_shift_jobs( self ): end = datetime.time(23,59,59) if start <= current <= end: - #Domoticz.Log("Inside of Night Shift period %s %s %s" %( start, current, end)) + self.log.logging("PluginTools", "Debug", "Inside of Night Shift period %s %s %s" %( start, current, end)) return True # Check against the second part of the night start = datetime.time(0, 0,0) end = datetime.time(6,0,0) if start <= current <= end: - #Domoticz.Log("Inside of Night Shift period %s %s %s" %( start, current, end)) + self.log.logging("PluginTools", "Debug","Inside of Night Shift period %s %s %s" %( start, current, end)) return True - #Domoticz.Log("Outside of Night Shift period %s %s %s" %( start, current, end)) + self.log.logging("PluginTools", "Debug", "Outside of Night Shift period %s %s %s" %( start, current, end)) return False @@ -1571,11 +1479,11 @@ def print_stack( self ): try: import inspect except Exception as e: - self.log.logging( "Zigpy", "Error", "Cannot import python module inspect") + self.log.logging( "PluginTools", "Error", "Cannot import python module inspect") return for x in inspect.stack(): - self.log.logging( "Zigpy", "Error", "[{:40}| {}:{}".format(x.function, x.filename, x.lineno)) + self.log.logging( "PluginTools", "Error", "[{:40}| {}:{}".format(x.function, x.filename, x.lineno)) @@ -1711,7 +1619,7 @@ def is_domoticz_above_2023(self): def is_domoticz_new_API(self): 'is_domoticz_new_API() False True 1 15356' - self.log.logging("Input", "Debug", "is_domoticz_new_API() %s %s %s %s" %( + self.log.logging("PluginTools", "Debug", "is_domoticz_new_API() %s %s %s %s" %( is_domoticz_below_2023(self), is_domoticz_2023(self), self.DomoticzMinor, self.DomoticzBuild)) if is_domoticz_below_2023(self): diff --git a/Modules/tuya.py b/Modules/tuya.py index f979193e9..90b1a585e 100644 --- a/Modules/tuya.py +++ b/Modules/tuya.py @@ -14,7 +14,6 @@ import time from datetime import datetime, timedelta -import Domoticz from Modules.basicOutputs import raw_APS_request, write_attribute from Modules.bindings import bindDevice from Modules.domoMaj import MajDomoDevice @@ -58,9 +57,11 @@ def tuya_registration(self, nwkid, device_reset=False, parkside=False, tuya_regi if parkside: write_attribute(self, nwkid, ZIGATE_EP, EPout, "0000", "0000", "00", "ffde", "20", "0d", ackIsDisabled=False) + # if tuya_registration_value: + # write_attribute(self, nwkid, ZIGATE_EP, EPout, "0000", TUYA_MANUF_CODE, "01", "ffde", "20", "%02x" %tuya_registration_value, ackIsDisabled=False) if tuya_registration_value: - write_attribute(self, nwkid, ZIGATE_EP, EPout, "0000", TUYA_MANUF_CODE, "01", "ffde", "20", "%02x" %tuya_registration_value, ackIsDisabled=False) - + write_attribute(self, nwkid, ZIGATE_EP, EPout, "0000", "0000", "00", "ffde", "20", "%02x" %tuya_registration_value, ackIsDisabled=False) + elif _ModelName == "TS0216": # Heiman like siren # Just do the Regitsration @@ -144,7 +145,7 @@ def callbackDeviceAwake_Tuya(self, Devices, NwkId, EndPoint, cluster): This is fonction is call when receiving a message from a Manufacturer battery based device. The function is called after processing the readCluster part """ - Domoticz.Log("callbackDeviceAwake_Tuya - Nwkid: %s, EndPoint: %s cluster: %s" % (NwkId, EndPoint, cluster)) + self.log.logging( "Tuya", "Log", "callbackDeviceAwake_Tuya - Nwkid: %s, EndPoint: %s cluster: %s" % (NwkId, EndPoint, cluster)) def tuyaReadRawAPS(self, Devices, NwkId, srcEp, ClusterID, dstNWKID, dstEP, MsgPayload): @@ -238,7 +239,7 @@ def tuyaReadRawAPS(self, Devices, NwkId, srcEp, ClusterID, dstNWKID, dstEP, MsgP version = MsgPayload[10:12] # int8 store_tuya_attribute(self, NwkId, "TUYA_MCU_VERSION_RSP", version) except Exception as e: - Domoticz.Error("tuyaReadRawAPS - MCU_VERSION_RSP error on Payload: %s reason %s" % (MsgPayload,e)) + self.log.logging( "Tuya", "Error", "tuyaReadRawAPS - MCU_VERSION_RSP error on Payload: %s reason %s" % (MsgPayload,e)) elif cmd == "23": # TUYA_REPORT_LOG pass @@ -1503,10 +1504,14 @@ def ts110e_light_type( self, NwkId, mode): mode = "%02x" %mode write_attribute(self, NwkId, ZIGATE_EP, EPout, "0008", "0000", "00", "fc02", "20", mode, ackIsDisabled=False) +def ts110e_switch01_type( self, NwkId, mode): + ts110e_switch_type( self, NwkId, "01", mode) -def ts110e_switch_type( self, NwkId, mode): +def ts110e_switch02_type( self, NwkId, mode): + ts110e_switch_type( self, NwkId, "02", mode) + +def ts110e_switch_type( self, NwkId, EPout, mode): # momentary: 0, toggle: 1, state: 2 self.log.logging("Tuya", "Debug", "ts110e_switch_type - mode %s" % mode, NwkId) - EPout = "01" mode = "%02x" %mode write_attribute(self, NwkId, ZIGATE_EP, EPout, "0008", "0000", "00", "fc02", "20", mode, ackIsDisabled=False) diff --git a/Modules/tuyaSiren.py b/Modules/tuyaSiren.py index c5c9bba5f..6756c6246 100644 --- a/Modules/tuyaSiren.py +++ b/Modules/tuyaSiren.py @@ -12,8 +12,6 @@ import struct -import Domoticz - from Modules.basicOutputs import raw_APS_request, write_attribute from Modules.domoMaj import MajDomoDevice from Modules.domoTools import Update_Battery_Device @@ -213,7 +211,7 @@ def get_alarm_attrbutes(self, nwkid, alarm_num): alarm = "Alarm%s" % alarm_num if alarm not in default_value: - Domoticz.Error("get_alarm_attrbutes - something wrong %s %s" % (alarm_num, alarm)) + self.log.logging("Tuya", "Error", "get_alarm_attrbutes - something wrong %s %s" % (alarm_num, alarm)) return None default_alarm = default_value[alarm] diff --git a/Modules/tuyaTRV.py b/Modules/tuyaTRV.py index 4a33ce32c..135052cdb 100644 --- a/Modules/tuyaTRV.py +++ b/Modules/tuyaTRV.py @@ -11,18 +11,17 @@ """ # https://github.com/zigpy/zha-device-handlers/issues/357 -import Domoticz import binascii -from datetime import datetime,timedelta +from datetime import datetime, timedelta from Modules.basicOutputs import raw_APS_request, write_attribute from Modules.domoMaj import MajDomoDevice from Modules.domoTools import Update_Battery_Device from Modules.tools import (checkAndStoreAttributeValue, get_and_inc_ZCL_SQN, is_ack_tobe_disabled, voltage2batteryP) +from Modules.tuyaConst import eTRV_MODELS from Modules.tuyaTools import (get_tuya_attribute, store_tuya_attribute, tuya_cmd) -from Modules.tuyaConst import eTRV_MODELS from Modules.zigateConsts import ZIGATE_EP @@ -452,23 +451,23 @@ def receive_moe_schedule(self, Devices, model_target, NwkId, srcEp, ClusterID, d # 0600-28/ 0b1e-2a/ 0d1e-2c 111e-2e # 0600-30/ 0c00-2e/ 0e1e-2c 111e-2a # 0600-26/ 0c1e-28/ 0e1e-2a 121e-28 - Domoticz.Log("receive_moe_schedule( %s )" %data) - schedule = {'Monday Friday': decode_moes_plan(data[:24])} - schedule['Saturday'] = decode_moes_plan(data[24:48]) - schedule['Sunday'] = decode_moes_plan(data[48:72]) + self.log.logging( "Tuya", "Log", "receive_moe_schedule( %s )" %data) + schedule = {'Monday Friday': decode_moes_plan(self, data[:24])} + schedule['Saturday'] = decode_moes_plan(self, data[24:48]) + schedule['Sunday'] = decode_moes_plan(self, data[48:72]) store_tuya_attribute(self, NwkId, "Schedule", schedule ) -def decode_moes_plan( data ): - Domoticz.Log("decode_moes_plan( %s )" %data) +def decode_moes_plan( self, data ): + self.log.logging( "Tuya", "Log", "decode_moes_plan( %s )" %data) return { - 'Period1': decode_moes_period( data[:6] ), - 'Period2': decode_moes_period( data[6:12]) , - 'Period3': decode_moes_period( data[12:18]) , - 'Period4': decode_moes_period( data[18:24]), + 'Period1': decode_moes_period( self, data[:6] ), + 'Period2': decode_moes_period( self, data[6:12]) , + 'Period3': decode_moes_period( self, data[12:18]) , + 'Period4': decode_moes_period( self, data[18:24]), } -def decode_moes_period( data ): - Domoticz.Log("decode_moes_period( %s )" %data) +def decode_moes_period( self, data ): + self.log.logging( "Tuya", "Log", "decode_moes_period( %s )" %data) return { 'Start': data[:4], 'Setpoint': int(data[4:6],16) diff --git a/Modules/tuyaTS0601.py b/Modules/tuyaTS0601.py index a81e665a7..bb1588660 100644 --- a/Modules/tuyaTS0601.py +++ b/Modules/tuyaTS0601.py @@ -11,12 +11,13 @@ """ import struct + from Modules.domoMaj import MajDomoDevice from Modules.domoTools import Update_Battery_Device from Modules.tools import (checkAndStoreAttributeValue, get_and_inc_ZCL_SQN, getAttributeValue) -from Modules.tuyaTools import store_tuya_attribute, tuya_cmd, get_tuya_attribute - +from Modules.tuyaTools import (get_tuya_attribute, store_tuya_attribute, + tuya_cmd) # Generic functions @@ -141,13 +142,16 @@ def evaluate_expression_with_data(self, expression, value): return eval( expression ) except NameError as e: - self.log.logging("ZclClusters", "Error", "Undefined variable, please check the formula %s" %expression) + self.log.logging("ZclClusters", "Error", "Undefined variable, please check the formula %s or value %s" %( + expression, value)) except SyntaxError as e: - self.log.logging("ZclClusters", "Error", "Syntax error, please check the formula %s" %expression) + self.log.logging("ZclClusters", "Error", "Syntax error, please check the formula %s or value %s" %( + expression, value)) except ValueError as e: - self.log.logging("ZclClusters", "Error", "Value Error, please check the formula %s %s" %(expression, e)) + self.log.logging("ZclClusters", "Error", "Value Error, please check the formula %s or value %s. Error: %s" %( + expression, value, e)) return value @@ -227,11 +231,19 @@ def ts0601_battery_state(self, Devices, nwkid, ep, value ): def ts0601_tamper(self, Devices, nwkid, ep, value): self.log.logging("Tuya0601", "Debug", "ts0601_tamper - Tamper %s %s %s" % (nwkid, ep, value), nwkid) - store_tuya_attribute(self, nwkid, "SmokeTamper", value) + store_tuya_attribute(self, nwkid, "Tamper", value) state = "01" if value != 0 else "00" MajDomoDevice(self, Devices, nwkid, ep, "0009", state) - - + +def ts0601_charging_mode(self, Devices, nwkid, ep, value): + self.log.logging("Tuya0601", "Debug", "ts0601_charging_mode - Charging %s %s %s" % (nwkid, ep, value), nwkid) + store_tuya_attribute(self, nwkid, "Tamper", value) + state = "01" if value != 0 else "00" + if state == "01": + MajDomoDevice(self, Devices, nwkid, ep, "Notification", "Charging On") + else: + MajDomoDevice(self, Devices, nwkid, ep, "Notification", "Charging Off") + def ts0601_switch(self, Devices, nwkid, ep, value): self.log.logging("Tuya0601", "Debug", "ts0601_switch - Switch%s %s %s" % (nwkid, ep, value), nwkid) store_tuya_attribute(self, nwkid, "Switch", value) @@ -317,7 +329,6 @@ def ts0601_instant_power(self, Devices, nwkid, ep, value): MajDomoDevice(self, Devices, nwkid, ep, "0702", signed_int) store_tuya_attribute(self, nwkid, "InstantPower", signed_int) # Store str - def ts0601_voltage(self, Devices, nwkid, ep, value): self.log.logging( "Tuya0601", "Debug", "ts0601_voltage - Voltage %s %s %s" % (nwkid, ep, value), nwkid, ) MajDomoDevice(self, Devices, nwkid, ep, "0001", value) @@ -363,7 +374,12 @@ def ts0601_sirene_switch(self, Devices, nwkid, ep, value): self.log.logging("Tuya0601", "Debug", "ts0601_sirene_switch - After Nwkid: %s/%s Alarm: %s" % (nwkid, ep, value)) store_tuya_attribute(self, nwkid, "Alarm", value) MajDomoDevice(self, Devices, nwkid, ep, "0006", value) - + +def ts0601_tamper_switch(self, Devices, nwkid, ep, value): + self.log.logging("Tuya0601", "Debug", "ts0601_sirene_switch - After Nwkid: %s/%s Alarm: %s" % (nwkid, ep, value)) + store_tuya_attribute(self, nwkid, "Alarm", value) + MajDomoDevice(self, Devices, nwkid, ep, "0006", value) + def ts0601_sirene_level(self, Devices, nwkid, ep, value): self.log.logging("Tuya0601", "Debug", "ts0601_sirene_level - Sound Level: %s" % value, nwkid) store_tuya_attribute(self, nwkid, "AlarmLevel", value) @@ -443,6 +459,7 @@ def ts0601_sensor_irrigation_mode(self, Devices, nwkid, ep, value): "battery": ts0601_battery, "batteryState": ts0601_battery_state, "tamper": ts0601_tamper, + "charging_mode": ts0601_charging_mode, "switch": ts0601_switch, "door": ts0601_door, "lvl_percentage": ts0601_level_percentage, @@ -466,6 +483,7 @@ def ts0601_sensor_irrigation_mode(self, Devices, nwkid, ep, value): "TuyaAlarmMelody": ts0601_sirene_melody, "TuyaAlarmLevel": ts0601_sirene_level, "TuyaAlarmSwitch": ts0601_sirene_switch, + "TuyaTamperSwitch": ts0601_tamper_switch, "smoke_state": ts0601_smoke_detection, "smoke_ppm": ts0601_smoke_concentration, "water_consumption": ts0601_water_consumption, @@ -581,6 +599,19 @@ def ts0601_action_siren_switch(self, NwkId, Ep, dp, value=None): data = "%02x" % (device_value) ts0601_tuya_cmd(self, NwkId, Ep, action, data) +def ts0601_tamper_siren_switch(self, NwkId, Ep, dp, value=None): + if value is None: + return + + self.log.logging("Tuya0601", "Debug", "ts0601_tamper_siren_switch - %s Tamper Switch Action: dp:%s value: %s" % ( + NwkId, dp, value)) + device_value = value + + action = "%02x01" % dp # Mode + data = "%02x" % (device_value) + ts0601_tuya_cmd(self, NwkId, Ep, action, data) + + def ts0601_action_switch(self, NwkId, Ep, dp, value=None): if value is None: return @@ -633,12 +664,45 @@ def ts0601_irrigation_valve_target( self, NwkId, Ep, dp, value=None): data = "%08x" % (device_value) ts0601_tuya_cmd(self, NwkId, Ep, action, data) +def ts0601_solar_siren_alarm_melody( self, NwkId, Ep, dp, melody=None): + if melody is None: + return + self.log.logging("Tuya0601", "Log", "ts0601_solar_siren_alarm_melody - %s Switch Action: dp:%s value: %s" % ( + NwkId, dp, melody)) + if melody is None: + return + action = "%02x04" % dp # I + data = "%02x" % (melody) + ts0601_tuya_cmd(self, NwkId, Ep, action, data) + +def ts0601_solar_siren_alarm_mode( self, NwkId, Ep, dp, mode=None): + if mode is None: + return + self.log.logging("Tuya0601", "Log", "ts0601_solar_siren_alarm_mode - %s Switch Action: dp:%s value: %s" % ( + NwkId, dp, mode)) + if mode is None: + return + action = "%02x04" % dp # I + data = "%02x" % (mode) + ts0601_tuya_cmd(self, NwkId, Ep, action, data) + +def ts0601_solar_siren_alarm_duration( self, NwkId, Ep, dp, duration=None): + if duration is None: + return + self.log.logging("Tuya0601", "Log", "ts0601_solar_siren_alarm_duration - %s Switch Action: dp:%s value: %s" % ( + NwkId, dp, duration)) + action = "%02x02" % dp # I + data = "%08x" % (duration) + ts0601_tuya_cmd(self, NwkId, Ep, action, data) TS0601_COMMANDS = { "TRV7WindowDetection": ts0601_window_detection_mode, "TRV7ChildLock": ts0601_child_lock_mode, "TuyaIrrigationTarget": ts0601_irrigation_valve_target, - "TuyaIrrigationMode": ts0601_irrigation_mode + "TuyaIrrigationMode": ts0601_irrigation_mode, + "TuyaAlarmMelody": ts0601_solar_siren_alarm_melody, + "TuyaAlarmMode": ts0601_solar_siren_alarm_mode, + "TuyaAlarmDuration": ts0601_solar_siren_alarm_duration } DP_ACTION_FUNCTION = { @@ -647,5 +711,6 @@ def ts0601_irrigation_valve_target( self, NwkId, Ep, dp, value=None): "calibration": ts0601_action_calibration, "TRV6SystemMode": ts0601_action_trv6_system_mode, "TRV7SystemMode": ts0601_action_trv7_system_mode, - "TuyaAlarmSwitch": ts0601_action_siren_switch + "TuyaAlarmSwitch": ts0601_action_siren_switch, + "TuyaTamperSwitch": ts0601_tamper_siren_switch } diff --git a/Modules/tuyaTools.py b/Modules/tuyaTools.py index aabf62f88..054d10439 100644 --- a/Modules/tuyaTools.py +++ b/Modules/tuyaTools.py @@ -12,8 +12,9 @@ from Modules.basicOutputs import raw_APS_request, write_attribute from Modules.tools import is_ack_tobe_disabled -from Modules.zigateConsts import ZIGATE_EP from Modules.tuyaConst import TUYA_MANUFACTURER_NAME +from Modules.zigateConsts import ZIGATE_EP + def tuya_manufacturer_device(self, NwkId): " return True if the NwkId device is a Tuya device, otherwise return False" diff --git a/Modules/txPower.py b/Modules/txPower.py index fa59c5748..3ea46ed24 100644 --- a/Modules/txPower.py +++ b/Modules/txPower.py @@ -15,7 +15,7 @@ from datetime import datetime from time import time -from Modules.zigateCommands import zigate_set_tx_power, zigate_get_tx_power +from Modules.zigateCommands import zigate_get_tx_power, zigate_set_tx_power # # (Zigate) JN5168 standard-power module has a transmission power range of -32 to 0 dBm diff --git a/Modules/zb_tables_management.py b/Modules/zb_tables_management.py index fdffac5b0..43b40c974 100644 --- a/Modules/zb_tables_management.py +++ b/Modules/zb_tables_management.py @@ -11,12 +11,10 @@ """ -from sqlite3 import Timestamp import struct import time from datetime import datetime - -import Domoticz +from sqlite3 import Timestamp from Modules.basicOutputs import mgt_binding_table_req, mgt_routing_req from Modules.tools import get_device_nickname diff --git a/Modules/zclClusterHelpers.py b/Modules/zclClusterHelpers.py index 135645b30..8f057e931 100644 --- a/Modules/zclClusterHelpers.py +++ b/Modules/zclClusterHelpers.py @@ -52,7 +52,7 @@ def decoding_attribute_data( AttType, attribute_value, handleErrors=False): if (signed_int & 0x00800000) != 0: # Check the sign bit signed_int -= 0x01000000 # If negative, adjust to two's complement return signed_int - + if int(AttType, 16) == 0x2B: # 32Bitint return struct.unpack("i", struct.pack("I", int(attribute_value[:8], 16)))[0] diff --git a/Modules/zigbeeController.py b/Modules/zigbeeController.py index 41138b7be..1bdb7e08d 100644 --- a/Modules/zigbeeController.py +++ b/Modules/zigbeeController.py @@ -3,21 +3,23 @@ # # Author: zaraki673 & pipiche38 # -import Domoticz + from Zigbee.zdpCommands import (zdp_active_endpoint_request, zdp_simple_descriptor_request) def initLODZigate(self, nwkid, ieee): - Domoticz.Status("Initialize Zigate Data Structure %s %s" % (nwkid, ieee)) + self.log.logging("Input", "Status", "Initialize Zigate Data Structure %s %s" % (nwkid, ieee)) self.IEEE2NWK[ieee] = nwkid - self.ListOfDevices[nwkid] = {'Version': '3', 'ZDeviceName': 'Zigbee Coordinator'} - self.ListOfDevices[nwkid]["IEEE"] = ieee - self.ListOfDevices[nwkid]["Ep"] = {} - self.ListOfDevices[nwkid]["PowerSource"] = "Main" - self.ListOfDevices[nwkid]["LogicalType"] = "Coordinator" - + self.ListOfDevices[nwkid] = { + 'Version': '3', + 'ZDeviceName': 'Zigbee Coordinator', + "IEEE": ieee, + "Ep": {}, + "PowerSource": "Main", + "LogicalType": "Coordinator", + } endpointZigate(self, ieee) diff --git a/Modules/zigpyBackup.py b/Modules/zigpyBackup.py index 34ff058fd..d1e9a8331 100644 --- a/Modules/zigpyBackup.py +++ b/Modules/zigpyBackup.py @@ -1,10 +1,11 @@ -import Domoticz -import os.path import json +import os.path from pathlib import Path + import Modules.tools + def handle_zigpy_backup(self, backup): if not backup: @@ -24,7 +25,7 @@ def handle_zigpy_backup(self, backup): self.log.logging("TransportZigpy", "Status", "Coordinator backup is available: %s" %_coordinator_backup) except IOError: - Domoticz.Error("Error while Writing Coordinator backup %s" % _coordinator_backup) + self.log.logging("TransportZigpy", "Error", "Error while Writing Coordinator backup %s" % _coordinator_backup) def handle_zigpy_retreive_last_backup( self ): diff --git a/ReleaseNotes.md b/ReleaseNotes.md index d1f234361..45fe1926f 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -18,6 +18,15 @@ Release Numbering - Odd numbers --> Stable/6 - Even numbers --> Beta/6 (dev branch) +## October 2023 - stable7 - 7.1.004 ( 2023.10 ) + +- [Hardware] - Ronelabs SEM101 Meter +- [Hardware] - Neo Solar Siren full integration +- [Technical] - Domoticz abstract layer to faciliate move to Extended Framework. +- [Issue] - Fix issues in Groups management ( PR #1647, PR #1648 @GMLinky ) +- [Issue] - Fix issues in ZLinky integration (PR #1646 @SylvainPer ) +- [Issue] - Limit Level control anbalog value to 254 (0xfe) as per Zigbee standard + ## Septembre 2023 - stable7 - 7.1.003 ( 2023.9) - [Technical] - Compatible Debian12 and python3.11 @@ -42,12 +51,12 @@ Release Numbering - [zigpy] - Upgrade to latest zigpy radio library level - ## June 2023 - stable6 - 6.0.014 ( 2023.6 ) - [Issue] - Zigpy, make sure we leave a minimum elapse time before 2 commands to the same device. (regression from a 6.3.008) ## May 2023 - stable6 - 6.3.013 ( 2023.5 ) + - [Issue] - for Illuminence (LUMI) make sure to use an int (and not hex) - [Issue] - Fix illuminence calculation for Aqara RTCGQ11LM - [Issue] - Fix check_requirements() diff --git a/Tools/plugin-auto-upgrade.sh b/Tools/plugin-auto-upgrade.sh index 53fea6a9d..2835c04d5 100755 --- a/Tools/plugin-auto-upgrade.sh +++ b/Tools/plugin-auto-upgrade.sh @@ -79,12 +79,12 @@ fi echo " " echo "(4) git config --global --unset safe.directory" -git config --global --unset safe.directory $(pwd)/external/bellows -git config --global --unset safe.directory $(pwd)/external/zigpy-deconz -git config --global --unset safe.directory $(pwd)/external/zigpy-zigate -git config --global --unset safe.directory $(pwd)/external/zigpy-znp -git config --global --unset safe.directory $(pwd)/external/zigpy -git config --global --unset safe.directory $(pwd) +git config --global --unset-all safe.directory $(pwd)/external/bellows +git config --global --unset-all safe.directory $(pwd)/external/zigpy-deconz +git config --global --unset-all safe.directory $(pwd)/external/zigpy-zigate +git config --global --unset-all safe.directory $(pwd)/external/zigpy-znp +git config --global --unset-all safe.directory $(pwd)/external/zigpy +git config --global --unset-all safe.directory $(pwd) echo " " echo "Plugin Upgrade process completed without errors." diff --git a/Zigbee/decode8002.py b/Zigbee/decode8002.py index fb4313086..0f50b3dc8 100644 --- a/Zigbee/decode8002.py +++ b/Zigbee/decode8002.py @@ -7,7 +7,6 @@ from Modules.tools import lookupForIEEE from Modules.zigateConsts import ADDRESS_MODE - from Zigbee.zclDecoders import zcl_decoders from Zigbee.zdpDecoders import zdp_decoders diff --git a/Zigbee/zclDecoders.py b/Zigbee/zclDecoders.py index e503b0deb..31594ea8b 100644 --- a/Zigbee/zclDecoders.py +++ b/Zigbee/zclDecoders.py @@ -5,15 +5,14 @@ # -from distutils.command.build import build import struct +from distutils.command.build import build from os import stat from Modules.tools import (is_direction_to_client, is_direction_to_server, retreive_cmd_payload_from_8002) from Modules.zigateConsts import (SIZE_DATA_TYPE, ZIGATE_EP, composite_value, discrete_value) - from Zigbee.encoder_tools import decode_endian_data, encapsulate_plugin_frame from Zigbee.zclRawCommands import zcl_raw_default_response @@ -226,7 +225,9 @@ def buildframe_read_attribute_response(self, frame, Sqn, SrcNwkId, SrcEndPoint, nbAttribute = 0 idx = 0 buildPayload = Sqn + SrcNwkId + SrcEndPoint + ClusterId - while idx < len(Data) and len(Data[idx:]) >= 8: + # Len of remaining Data is either 8 for response with Status/Type/Value or 6 for response with only Status (exemple "86" attribute doesn't exist in cluster) + # while idx < len(Data) and len(Data[idx:]) >= 8: + while idx < len(Data) and len(Data[idx:]) >= 6: nbAttribute += 1 Attribute = "%04x" % struct.unpack("H", struct.pack(">H", int(Data[idx : idx + 4], 16)))[0] idx += 4 @@ -443,18 +444,18 @@ def buildframe8062_look_for_group_member_ship_response(self, frame, Sqn, SrcNwkI def buildframe8063_remove_group_member_ship_response(self, frame, Sqn, SrcNwkId, SrcEndPoint, TargetEp, ClusterId, Data): - #MsgSequenceNumber = MsgData[0:2] - #MsgEP = MsgData[2:4] - #MsgClusterID = MsgData[4:8] - #MsgStatus = MsgData[8:10] - #MsgGroupID = MsgData[10:14] - #MsgSrcAddr = MsgData[14:18] + # MsgSequenceNumber = MsgData[0:2] + # MsgEP = MsgData[2:4] + # MsgClusterID = MsgData[4:8] + # MsgStatus = MsgData[8:10] + # MsgGroupID = MsgData[10:14] + # MsgSrcAddr = MsgData[14:18] self.log.logging("zclDecoder", "Debug", "buildframe8063_remove_group_member_ship_response - Data: %s" % Data) - - buildPayload = Sqn + SrcEndPoint + "0004" + Data[:2] + decode_endian_data( Data[ 2:6 ], "21") +# SrcNwkId is not passed ----> Causes a false Error in GrpResponses.py function remove_group_member_ship_response +# buildPayload = Sqn + SrcEndPoint + "0004" + Data[:2] + decode_endian_data( Data[ 2:6 ], "21") + buildPayload = Sqn + SrcEndPoint + "0004" + Data[:2] + decode_endian_data( Data[ 2:6 ], "21") + SrcNwkId return encapsulate_plugin_frame("8063", buildPayload, frame[len(frame) - 4 : len(frame) - 2]) - # Cluster 0x0005 - Scenes def buildframe_for_cluster_0005(self, Command, frame, Sqn, SrcNwkId, SrcEndPoint, TargetEp, ClusterId, Data): @@ -699,4 +700,4 @@ def decoding_error(self, source, sqn, nwkid, ep, cluster, attribute, DType, idx= "Idx": idx, } self.log.logging("zclDecoder", "Error", "%s - decoding_error - %s %s %s %s %s %s %s %s %s %s" % ( - source, sqn, nwkid, ep, cluster, attribute, DType, idx, buildPayload, frame, Data ), nwkid=nwkid, context=_context) \ No newline at end of file + source, sqn, nwkid, ep, cluster, attribute, DType, idx, buildPayload, frame, Data ), nwkid=nwkid, context=_context) diff --git a/Zigbee/zclRawCommands.py b/Zigbee/zclRawCommands.py index 50902d8ee..eec66c217 100644 --- a/Zigbee/zclRawCommands.py +++ b/Zigbee/zclRawCommands.py @@ -5,8 +5,10 @@ # import struct + from Modules.sendZigateCommand import raw_APS_request -from Modules.tools import get_and_inc_ZCL_SQN, fcf_direction, build_fcf, is_ack_tobe_disabled +from Modules.tools import (build_fcf, fcf_direction, get_and_inc_ZCL_SQN, + is_ack_tobe_disabled) from Zigbee.encoder_tools import decode_endian_data DEFAULT_ACK_MODE = False diff --git a/Zigbee/zdpCommands.py b/Zigbee/zdpCommands.py index c362a2b9c..b2a99ab70 100644 --- a/Zigbee/zdpCommands.py +++ b/Zigbee/zdpCommands.py @@ -11,7 +11,6 @@ """ from Modules.sendZigateCommand import raw_APS_request, send_zigatecmd_raw - from Zigbee.zdpRawCommands import (zdp_raw_active_endpoint_request, zdp_raw_binding_device, zdp_raw_IEEE_address_request, diff --git a/Zigbee/zdpDecoders.py b/Zigbee/zdpDecoders.py index aaa4ec13e..285dcf6c2 100644 --- a/Zigbee/zdpDecoders.py +++ b/Zigbee/zdpDecoders.py @@ -5,8 +5,10 @@ # import struct + from Zigbee.encoder_tools import encapsulate_plugin_frame + def is_duplicate_zdp_frame(self, Nwkid, ClusterId, Sqn): if self.zigbee_communication != "zigpy": diff --git a/Zigbee/zdpRawCommands.py b/Zigbee/zdpRawCommands.py index 7dac720c5..6b614df51 100644 --- a/Zigbee/zdpRawCommands.py +++ b/Zigbee/zdpRawCommands.py @@ -10,9 +10,10 @@ """ import struct -from Modules.zigateConsts import ZIGATE_EP + from Modules.sendZigateCommand import raw_APS_request from Modules.tools import get_and_inc_ZDP_SQN +from Modules.zigateConsts import ZIGATE_EP # ZDP Commands diff --git a/plugin.py b/plugin.py index c1327bdb9..87deaf4d3 100644 --- a/plugin.py +++ b/plugin.py @@ -121,7 +121,7 @@ from Modules.database import (LoadDeviceList, WriteDeviceList, checkDevices2LOD, checkListOfDevice2Devices, import_local_device_conf) -from Modules.domoCreate import how_many_slot_available +from Modules.domoticzAbstractLayer import how_many_slot_available, load_list_of_domoticz_widget from Modules.domoTools import ResetDevice from Modules.heartbeat import processListOfDevices from Modules.input import ZigateRead @@ -171,6 +171,7 @@ def __init__(self): self.DeviceConf = {} # Store DeviceConf.txt, all known devices configuration self.ModelManufMapping = {} self.readZclClusters = {} + self.ListOfDomoticzWidget = {} # Objects from Classe self.configureReporting = None @@ -281,7 +282,6 @@ def onStart(self): Domoticz.Status( "Z4D plugin requires python3 3.8 or above and you are running %s.%s" %( _current_python_version_major, _current_python_version_minor)) - # TODO put the check of python3.8 on hold assert sys.version_info >= (3, 8) # nosec if check_requirements( self ): @@ -469,14 +469,16 @@ def onStart(self): import_local_device_conf(self) z4d_certified_devices.z4d_import_device_configuration(self, z4d_certified_devices_pathname ) - # if type(self.DeviceConf) is not dict: if not isinstance(self.DeviceConf, dict): self.log.logging("Plugin", "Error", "DeviceConf initialisation failure!!! %s" % type(self.DeviceConf)) self.onStop() return - - + + # Import List of Domoticz Widgets + self.ListOfDomoticzWidget = {} + load_list_of_domoticz_widget(self, Devices) + # Import DeviceList.txt Filename is : DeviceListName self.log.logging("Plugin", "Status", "load ListOfDevice") if LoadDeviceList(self) == "Failed": @@ -654,7 +656,6 @@ def onStart(self): self.log.logging("Plugin", "Error", "Unknown Transport comunication protocol : %s" % str(self.transport)) self.onStop() return - if self.transport not in ("ZigpyZNP", "ZigpydeCONZ", "ZigpyEZSP", "ZigpyZiGate", "None" ): self.log.logging("Plugin", "Debug", "Establish Zigate connection") @@ -779,6 +780,8 @@ def onDeviceRemoved(self, Unit): ) self.log.logging("Plugin", "Debug", "ListOfDevices :After REMOVE " + str(self.ListOfDevices)) + self.ListOfDomoticzWidget = {} + load_list_of_domoticz_widget(self, Devices) return if self.groupmgt and Devices[Unit].DeviceID in self.groupmgt.ListOfGroups: @@ -1006,6 +1009,7 @@ def onHeartbeat(self): self.log.logging("Plugin", "Debug", "Devices size has changed , let's write ListOfDevices on disk") WriteDeviceList(self, 0) # write immediatly networksize_update(self) + load_list_of_domoticz_widget(self, Devices) _trigger_coordinator_backup( self ) @@ -1396,7 +1400,8 @@ def start_web_server(self, webserver_port, webserver_homefolder): self.transport, self.ModelManufMapping, self.DomoticzMajor, - self.DomoticzMinor + self.DomoticzMinor, + self.readZclClusters ) if self.FirmwareVersion: self.webserver.update_firmware(self.FirmwareVersion) @@ -1723,15 +1728,16 @@ def _check_plugin_version( self ): self.pluginParameters["available"], self.pluginParameters["available-firmMajor"], self.pluginParameters["available-firmMinor"], - ) = checkPluginVersion(self.zigbee_communication, self.pluginParameters["PluginBranch"], self.FirmwareMajorVersion) + ) = checkPluginVersion(self, self.zigbee_communication, self.pluginParameters["PluginBranch"], self.FirmwareMajorVersion) self.pluginParameters["FirmwareUpdate"] = False self.pluginParameters["PluginUpdate"] = False - if checkPluginUpdate(self.pluginParameters["PluginVersion"], self.pluginParameters["available"]): + if checkPluginUpdate(self, self.pluginParameters["PluginVersion"], self.pluginParameters["available"]): self.log.logging("Plugin", "Status", "*** A recent plugin version (%s) is waiting for you on gitHub. You are on (%s) ***" %( self.pluginParameters["available"], self.pluginParameters["PluginVersion"] )) self.pluginParameters["PluginUpdate"] = True if checkFirmwareUpdate( + self, self.FirmwareMajorVersion, self.FirmwareVersion, self.pluginParameters["available-firmMajor"],