def tuya_cmd(self, nwkid, EPout, cluster_frame, sqn, cmd, action, data, action2=None, data2=None): if nwkid not in self.ListOfDevices: return transid = "%02x" % get_next_tuya_transactionId(self, nwkid) len_data = (len(data)) // 2 payload = cluster_frame + sqn + cmd + "00" + transid + action + "00" + "%02x" % len_data + data if action2 and data2: len_data2 = (len(data2)) // 2 payload += action2 + "00" + "%02x" % len_data2 + data2 raw_APS_request( self, nwkid, EPout, "ef00", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, nwkid), ) self.log.logging( "Tuya", "Debug", "tuya_cmd - %s/%s cmd: %s payload: %s" % (nwkid, EPout, cmd, payload))
def poll_checkin_response_command(self, Sqn, nwkid, ep, ContinueFastPoll=True, DurationFastPoll=0xFC0): # 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)) cmd = "00" # Check-in Response if nwkid not in self.ListOfDevices: Domoticz.Error(" - nwkid: %s do not exist" % nwkid) return cluster_id = "0020" # Poll Control Cluster ContinueFastPoll = "%02x" % ContinueFastPoll DurationFastPoll = "%04x" % (struct.unpack( ">H", struct.pack("H", int("%04x" % DurationFastPoll, 16)))[0]) 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))
def cluster0101_toggle_door(self, NwkId): cmd = "02" # determine which Endpoint EPout = "01" sqn = get_and_inc_SQN(self, NwkId) # Cluster Frame: # 0b xxxx xxxx # |- Frame Type: Cluster Specific (0x01) # |-- Manufacturer Specific False # |--- Command Direction: Client to Server (0) # | ---- Disable default response: True # |||- ---- Reserved : 0x000 # ClusterFrame: 0b0001 0001 cluster_frame = "11" payload = cluster_frame + sqn + cmd raw_APS_request(self, NwkId, "01", "0101", "0104", payload, zigate_ep=ZIGATE_EP)
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)) cmd = "01" # Fast Poll Stop ( no data) if nwkid not in self.ListOfDevices: Domoticz.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))
def tuya_cmd_0x0000_0xf0(self, NwkId): # Seen at pairing of a WGH-JLCZ02 / TS011F and TS0201 and TS0601 (MOES BRT-100) payload = "11" + get_and_inc_SQN(self, NwkId) + "fe" raw_APS_request( self, NwkId, '01', "0000", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, NwkId), ) self.log.logging( "Tuya", "Debug", "tuya_cmd_0x0000_0xf0 - Nwkid: %s reset device Cmd: fe" % NwkId)
def default_response_for_philips_hue_reporting_attribute( self, Nwkid, srcEp, cluster, sqn): fcf = "10" cmd = "0b" cmd_reporting_attribute = "0a" status = "00" payload = fcf + sqn + cmd + cmd_reporting_attribute + "00" raw_APS_request(self, Nwkid, srcEp, cluster, "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=False) self.log.logging( "Philips", "Log", "default_response_for_philips_hue_reporting_attribute - %s/%s " % (Nwkid, srcEp))
def send_timesynchronisation(self, NwkId, srcEp, ClusterID, dstNWKID, dstEP, serial_number): # Request: cmd: 0x24 Data: 0x0008 # 0008 600d8029 600d8e39 # Request: cmd: 0x24 Data: 0x0053 # 0053 60e9ba1f 60e9d63f if NwkId not in self.ListOfDevices: return sqn = get_and_inc_SQN(self, NwkId) field1 = "0d" field2 = "80" field3 = "29" EPOCTime = datetime(1970, 1, 1) now = datetime.utcnow() UTCTime_in_sec = int((now - EPOCTime).total_seconds()) LOCALtime_in_sec = int((utc_to_local(now) - EPOCTime).total_seconds()) utctime = "%08x" % UTCTime_in_sec localtime = "%08x" % LOCALtime_in_sec self.log.logging( "Tuya", "Debug", "send_timesynchronisation - %s/%s UTC: %s Local: %s" % (NwkId, srcEp, UTCTime_in_sec, LOCALtime_in_sec), ) payload = "11" + sqn + "24" + serial_number + utctime + localtime raw_APS_request(self, NwkId, srcEp, "ef00", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=False) self.log.logging("Tuya", "Debug", "send_timesynchronisation - %s/%s " % (NwkId, srcEp))
def get_group_identifiers_request(self, nwkid): cluster_frame = "19" sqn = get_and_inc_SQN(self, nwkid) command = "41" start_index = "00" cluster = "1000" ListOfEp = getListOfEpForCluster(self, nwkid, cluster) if len(ListOfEp) != 1: return payload = cluster_frame + sqn + command + start_index ep = ListOfEp[0] raw_APS_request( self, nwkid, ep, cluster, "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, nwkid), highpriority=True, )
def read_attribute_response(self, nwkid, ep, sqn, cluster, status, data_type, attribute, value, manuf_code="0000"): # self.log.logging( None, 'Log', "Nwkid: %s Ep: %s Sqn: %s Cluster: %s Status: %s Data_Type: %s Attribute: %s Value: %s" \ # %( nwkid, ep, sqn, cluster, status, data_type, attribute, value)) cmd = "01" # Attribute Response if manuf_code == "0000": manuf_specific = "00" attribute = "%04x" % struct.unpack( "H", struct.pack(">H", int(attribute, 16)))[0] if manuf_code == "0000": cluster_frame = "18" # Profile-wide, Server to Client, Disable default Response payload = cluster_frame + sqn + cmd else: cluster_frame = "28" # Manufacturer Specific , Server to Client, Disable default Response payload = cluster_frame + manuf_code + sqn + cmd payload += attribute + status if status == "00": payload += data_type + encode_endian_data(value, data_type) # self.log.logging( None, 'Log', "read_attribute_response - %s/%s Cluster: %s Payload: %s" %(nwkid, ep, cluster, payload)) raw_APS_request(self, nwkid, ep, cluster, "0104", payload, zigate_ep=ZIGATE_EP)
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)) 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) 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))
def poll_checkin_command(self, Sqn, snwkid, ep): # Server -> End Device # The Poll Control Cluster server sends out a Check-in command to the devices to which it is paired based on the server’s Check-inIntervalattribute. # It does this to find out if any of the Poll Control Cluster Clients with which it is paired are interested in having it enter fastpoll mode # so that it can be managed. This request is sent out based on either the Check-inInterval, # or the next Check-in value in the Fast Poll Stop Request generated by the Poll Control Cluster Client. # The Check-in command expects a Check-in Response command to be sent back from the Poll Control Client. # 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)) cmd = "00" if snwkid not in self.ListOfDevices: Domoticz.Error(" - nwkid: %s do not exist" % snwkid) return cluster_id = "0020" # Poll Control Cluster cluster_frame = "11" 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)) return
def tuya_send_default_response(self, Nwkid, srcEp, sqn, cmd, orig_fcf): if Nwkid not in self.ListOfDevices: return orig_fcf = int(orig_fcf, 16) frame_type = "%02x" % (0b00000011 & orig_fcf) manuf_spec = "%02x" % ((0b00000100 & orig_fcf) >> 2) direction = "%02x" % (not ((0b00001000 & orig_fcf) >> 3)) disabled_default = "%02x" % ((0b00010000 & orig_fcf) >> 4) if disabled_default == "01": return fcf = build_fcf("00", manuf_spec, direction, disabled_default) payload = fcf + sqn + "0b" if manuf_spec == "01": payload += TUYA_MANUF_CODE[2:4] + TUYA_MANUF_CODE[0:2] payload += cmd + "00" raw_APS_request( self, Nwkid, srcEp, "ef00", "0104", payload, zigate_ep=ZIGATE_EP, highpriority=True, ackIsDisabled=is_ack_tobe_disabled(self, Nwkid), ) self.log.logging( "Tuya", "Debug", "tuya_send_default_response - %s/%s fcf: 0x%s ManufSpec: 0x%s Direction: 0x%s DisableDefault: 0x%s" % (Nwkid, srcEp, fcf, manuf_spec, direction, disabled_default), )
def tuya_sirene_registration(self, nwkid): self.log.logging("Tuya", "Debug", "tuya_sirene_registration - Nwkid: %s" % nwkid) EPout = "01" payload = "11" + get_and_inc_SQN(self, nwkid) + "10" + "002a" raw_APS_request( self, nwkid, EPout, "ef00", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, nwkid), ) # (1) 3 x Write Attribute Cluster 0x0000 - Attribute 0xffde - DT 0x20 - Value: 0x13 EPout = "01" write_attribute(self, nwkid, ZIGATE_EP, EPout, "0000", "0000", "00", "ffde", "20", "13", ackIsDisabled=False) # (2) Cmd 0xf0 send on Cluster 0x0000 - no data payload = "11" + get_and_inc_SQN(self, nwkid) + "f0" raw_APS_request( self, nwkid, EPout, "0000", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, nwkid), ) # (3) Cmd 0x03 on Cluster 0xef00 (Cluster Specific) payload = "11" + get_and_inc_SQN(self, nwkid) + "03" raw_APS_request( self, nwkid, EPout, "ef00", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, nwkid), ) # Set the Siren to °C tuya_siren_temp_unit(self, nwkid, unit="C")
def thermostat_Setpoint_Danfoss(self, NwkId, setpoint): # Command Manufactuer Specific # Setpoint command sends: setpointType (enum8) + HeatingSetpoint (16bit) # if setpointType = 1 the actuator will make a large movement to minimize reaction time to UI. # If setpointType = 0 the behavior will be the same as setting the attribute "Occupied Heating Setpoint" to the same value. # if setpointType = 2 displayed setpoint is not effected but regulated setpoint will change. can be used for Forecast functionality self.log.logging( "Danfoss", "Debug", "thermostat_Setpoint_Danfoss - for %s with value %s " % (NwkId, setpoint), nwkid=NwkId, ) if "Param" not in self.ListOfDevices[NwkId]: return if "DanfossSetPointType" not in self.ListOfDevices[NwkId]["Param"]: return if not int(self.ListOfDevices[NwkId]["Param"]["DanfossSetPointType"]): return if int(self.ListOfDevices[NwkId]["Param"]["DanfossSetPointType"]) not in ( 1, 2): return self.log.logging( "Danfoss", "Debug", "thermostat_Setpoint_Danfoss - for %s with value %s and SetPointType: %s" % (NwkId, setpoint, int(self.ListOfDevices[NwkId]["Param"]["DanfossSetPointType"])), nwkid=NwkId, ) danfoss_setpoint_command = "40" danfoss_setpoint_type = "%02x" % int( self.ListOfDevices[NwkId]["Param"]["DanfossSetPointType"]) danfoss_setpoint_value = "%04x" % int( (setpoint * 2) / 2) # Round to 0.5 degrees EPout = getListOfEpForCluster(self, NwkId, "0201") # cluster_id = "%04x" % 0x0201 manuf_id = "1246" sqn = get_and_inc_SQN(self, NwkId) cluster_frame = build_fcf("1", "1", "0", "0") payload = cluster_frame + manuf_id[2:4] + manuf_id[ 0: 2] + sqn + danfoss_setpoint_command + danfoss_setpoint_type + danfoss_setpoint_value[ 2:4] + danfoss_setpoint_value[0:2] self.log.logging( "Danfoss", "Debug", "thermostat_Setpoint_Danfoss - for %s with cluster_frame: %s payload: %s " % (NwkId, cluster_frame, payload), nwkid=NwkId, ) for ep in EPout: raw_APS_request( self, NwkId, ep, cluster_id, "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, NwkId), )
def tuya_registration(self, nwkid, device_reset=False, parkside=False): if "Model" not in self.ListOfDevices[nwkid]: return _ModelName = self.ListOfDevices[nwkid]["Model"] self.log.logging( "Tuya", "Debug", "tuya_registration - Nwkid: %s Model: %s" % (nwkid, _ModelName)) # (1) 3 x Write Attribute Cluster 0x0000 - Attribute 0xffde - DT 0x20 - Value: 0x13 ( 19 Decimal) # It looks like for Lidl Watering switch the Value is 0x0d ( 13 in decimal ) EPout = "01" self.log.logging( "Tuya", "Debug", "tuya_registration - Nwkid: %s ----- 0x13 in 0x0000/0xffde" % nwkid) if parkside: write_attribute(self, nwkid, ZIGATE_EP, EPout, "0000", "0000", "00", "ffde", "20", "0d", ackIsDisabled=True) else: write_attribute(self, nwkid, ZIGATE_EP, EPout, "0000", "0000", "00", "ffde", "20", "13", ackIsDisabled=True) # (3) Cmd 0x03 on Cluster 0xef00 (Cluster Specific) / Zigbee Device Reset if device_reset: payload = "11" + get_and_inc_SQN(self, nwkid) + "03" raw_APS_request( self, nwkid, EPout, "ef00", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, nwkid), ) self.log.logging( "Tuya", "Debug", "tuya_registration - Nwkid: %s reset device Cmd: 03" % nwkid) # Gw->Zigbee gateway query MCU version self.log.logging( "Tuya", "Debug", "tuya_registration - Nwkid: %s Request MCU Version Cmd: 10" % nwkid) if _ModelName in ("TS0601-_TZE200_nklqjk62", ): payload = "11" + get_and_inc_SQN(self, nwkid) + "10" + "000e" else: payload = "11" + get_and_inc_SQN(self, nwkid) + "10" + "0002" raw_APS_request( self, nwkid, EPout, "ef00", "0104", payload, zigate_ep=ZIGATE_EP, ackIsDisabled=is_ack_tobe_disabled(self, nwkid), )