From 0840b7447d0ffd9d817149d7bc399ab2461ec2b6 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 6 Nov 2020 22:24:45 +0100 Subject: [PATCH] Zigbee support for Mi Door and Contact --- tasmota/xdrv_23_zigbee_2_devices.ino | 5 +- tasmota/xdrv_23_zigbee_5__constants.ino | 266 ++++++++++++------------ tasmota/xdrv_23_zigbee_5_converters.ino | 45 ++-- tasmota/xdrv_23_zigbee_6_commands.ino | 27 ++- 4 files changed, 181 insertions(+), 162 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 097b608eb..fd64a5695 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -364,11 +364,14 @@ public: inline bool validZoneType(void) const { return 0xFFFF != zone_type; } inline uint16_t getZoneType(void) const { return zone_type; } + inline bool isPIR(void) const { return 0x000d == zone_type; } + inline bool isContact(void) const { return 0x0015 == zone_type; } inline void setZoneType(uint16_t _zone_type) { zone_type = _zone_type; } // 4 bytes - uint16_t zone_type; // mapped to the Zigbee standard + uint16_t zone_status; // last known state for sensor 1 & 2 + uint16_t zone_type; // mapped to the Zigbee standard // 0x0000 Standard CIE // 0x000d Motion sensor // 0x0015 Contact switch diff --git a/tasmota/xdrv_23_zigbee_5__constants.ino b/tasmota/xdrv_23_zigbee_5__constants.ino index 4786cc944..405f51068 100644 --- a/tasmota/xdrv_23_zigbee_5__constants.ino +++ b/tasmota/xdrv_23_zigbee_5__constants.ino @@ -341,6 +341,7 @@ const char Z_strings[] PROGMEM = "ZoneState" "\x00" "ZoneType" "\x00" "ZoneStatus" "\x00" + "Contact" "\x00" "CurrentSummDelivered" "\x00" "CompanyName" "\x00" "MeterTypeID" "\x00" @@ -381,8 +382,8 @@ const char Z_strings[] PROGMEM = "TuyaFanMode" "\x00" "TuyaForceMode" "\x00" "TuyaWeekSelect" "\x00" - "TerncyRotate" "\x00" "TerncyDuration" "\x00" + "TerncyRotate" "\x00" "Identify" "\x00" "xxxx" "\x00" "IdentifyQuery" "\x00" @@ -742,137 +743,138 @@ enum Z_offsets { Zo_ZoneState = 4540, Zo_ZoneType = 4550, Zo_ZoneStatus = 4559, - Zo_CurrentSummDelivered = 4570, - Zo_CompanyName = 4591, - Zo_MeterTypeID = 4603, - Zo_DataQualityID = 4615, - Zo_CustomerName = 4629, - Zo_Model = 4642, - Zo_PartNumber = 4648, - Zo_ProductRevision = 4659, - Zo_SoftwareRevision = 4675, - Zo_UtilityName = 4692, - Zo_POD = 4704, - Zo_AvailablePower = 4708, - Zo_PowerThreshold = 4723, - Zo_RMSVoltage = 4738, - Zo_RMSCurrent = 4749, - Zo_ActivePower = 4760, - Zo_NumberOfResets = 4772, - Zo_PersistentMemoryWrites = 4787, - Zo_LastMessageLQI = 4810, - Zo_LastMessageRSSI = 4825, - Zo_TuyaScheduleWorkdays = 4841, - Zo_TuyaScheduleHolidays = 4862, - Zo_TuyaChildLock = 4883, - Zo_TuyaWindowDetection = 4897, - Zo_TuyaValveDetection = 4917, - Zo_TuyaAutoLock = 4936, - Zo_TuyaTempTarget = 4949, - Zo_TuyaBattery = 4964, - Zo_TuyaMinTemp = 4976, - Zo_TuyaMaxTemp = 4988, - Zo_TuyaBoostTime = 5000, - Zo_TuyaComfortTemp = 5014, - Zo_TuyaEcoTemp = 5030, - Zo_TuyaValvePosition = 5042, - Zo_TuyaAwayTemp = 5060, - Zo_TuyaAwayDays = 5073, - Zo_TuyaPreset = 5086, - Zo_TuyaFanMode = 5097, - Zo_TuyaForceMode = 5109, - Zo_TuyaWeekSelect = 5123, - Zo_TerncyRotate = 5138, - Zo_TerncyDuration = 5151, - Zo_Identify = 5166, - Zo_xxxx = 5175, - Zo_IdentifyQuery = 5180, - Zo_AddGroup = 5194, - Zo_xxxx00 = 5203, - Zo_ViewGroup = 5210, - Zo_GetGroup = 5220, - Zo_01xxxx = 5229, - Zo_GetAllGroups = 5236, - Zo_00 = 5249, - Zo_RemoveGroup = 5252, - Zo_RemoveAllGroups = 5264, - Zo_ViewScene = 5280, - Zo_xxxxyy = 5290, - Zo_RemoveScene = 5297, - Zo_RemoveAllScenes = 5309, - Zo_RecallScene = 5325, - Zo_GetSceneMembership = 5337, - Zo_PowerOffEffect = 5356, - Zo_xxyy = 5371, - Zo_PowerOnRecall = 5376, - Zo_PowerOnTimer = 5390, - Zo_xxyyyyzzzz = 5403, - Zo_xx0A00 = 5414, - Zo_DimmerUp = 5421, - Zo_00190200 = 5430, - Zo_DimmerDown = 5439, - Zo_01190200 = 5450, - Zo_DimmerStop = 5459, - Zo_ResetAlarm = 5470, - Zo_xxyyyy = 5481, - Zo_ResetAllAlarms = 5488, - Zo_xx000A00 = 5503, - Zo_HueSat = 5512, - Zo_xxyy0A00 = 5519, - Zo_Color = 5528, - Zo_xxxxyyyy0A00 = 5534, - Zo_xxxx0A00 = 5547, - Zo_ShutterOpen = 5556, - Zo_ShutterClose = 5568, - Zo_ShutterStop = 5581, - Zo_ShutterLift = 5593, - Zo_xx = 5605, - Zo_ShutterTilt = 5608, - Zo_Shutter = 5620, - Zo_DimmerMove = 5628, - Zo_xx0A = 5639, - Zo_DimmerStepUp = 5644, - Zo_00xx0A00 = 5657, - Zo_DimmerStepDown = 5666, - Zo_01xx0A00 = 5681, - Zo_DimmerStep = 5690, - Zo_xx190A00 = 5701, - Zo_01 = 5710, - Zo_HueMove = 5713, - Zo_xx19 = 5721, - Zo_HueStepUp = 5726, - Zo_HueStepDown = 5736, - Zo_03xx0A00 = 5748, - Zo_HueStep = 5757, - Zo_SatMove = 5765, - Zo_SatStep = 5773, - Zo_xx190A = 5781, - Zo_ColorMove = 5788, - Zo_xxxxyyyy = 5798, - Zo_ColorStep = 5807, - Zo_ColorTempMoveUp = 5817, - Zo_01xxxx000000000000 = 5833, - Zo_ColorTempMoveDown = 5852, - Zo_03xxxx000000000000 = 5870, - Zo_ColorTempMoveStop = 5889, - Zo_00xxxx000000000000 = 5907, - Zo_ColorTempMove = 5926, - Zo_xxyyyy000000000000 = 5940, - Zo_ColorTempStepUp = 5959, - Zo_01xxxx0A0000000000 = 5975, - Zo_ColorTempStepDown = 5994, - Zo_03xxxx0A0000000000 = 6012, - Zo_ColorTempStep = 6031, - Zo_xxyyyy0A0000000000 = 6045, - Zo_ArrowClick = 6064, - Zo_ArrowHold = 6075, - Zo_ArrowRelease = 6085, - Zo_ZoneStatusChange = 6098, - Zo_xxxxyyzz = 6115, - Zo_xxyyzzzz = 6124, - Zo_AddScene = 6133, - Zo_xxyyyyzz = 6142, - Zo_StoreScene = 6151, + Zo_Contact = 4570, + Zo_CurrentSummDelivered = 4578, + Zo_CompanyName = 4599, + Zo_MeterTypeID = 4611, + Zo_DataQualityID = 4623, + Zo_CustomerName = 4637, + Zo_Model = 4650, + Zo_PartNumber = 4656, + Zo_ProductRevision = 4667, + Zo_SoftwareRevision = 4683, + Zo_UtilityName = 4700, + Zo_POD = 4712, + Zo_AvailablePower = 4716, + Zo_PowerThreshold = 4731, + Zo_RMSVoltage = 4746, + Zo_RMSCurrent = 4757, + Zo_ActivePower = 4768, + Zo_NumberOfResets = 4780, + Zo_PersistentMemoryWrites = 4795, + Zo_LastMessageLQI = 4818, + Zo_LastMessageRSSI = 4833, + Zo_TuyaScheduleWorkdays = 4849, + Zo_TuyaScheduleHolidays = 4870, + Zo_TuyaChildLock = 4891, + Zo_TuyaWindowDetection = 4905, + Zo_TuyaValveDetection = 4925, + Zo_TuyaAutoLock = 4944, + Zo_TuyaTempTarget = 4957, + Zo_TuyaBattery = 4972, + Zo_TuyaMinTemp = 4984, + Zo_TuyaMaxTemp = 4996, + Zo_TuyaBoostTime = 5008, + Zo_TuyaComfortTemp = 5022, + Zo_TuyaEcoTemp = 5038, + Zo_TuyaValvePosition = 5050, + Zo_TuyaAwayTemp = 5068, + Zo_TuyaAwayDays = 5081, + Zo_TuyaPreset = 5094, + Zo_TuyaFanMode = 5105, + Zo_TuyaForceMode = 5117, + Zo_TuyaWeekSelect = 5131, + Zo_TerncyDuration = 5146, + Zo_TerncyRotate = 5161, + Zo_Identify = 5174, + Zo_xxxx = 5183, + Zo_IdentifyQuery = 5188, + Zo_AddGroup = 5202, + Zo_xxxx00 = 5211, + Zo_ViewGroup = 5218, + Zo_GetGroup = 5228, + Zo_01xxxx = 5237, + Zo_GetAllGroups = 5244, + Zo_00 = 5257, + Zo_RemoveGroup = 5260, + Zo_RemoveAllGroups = 5272, + Zo_ViewScene = 5288, + Zo_xxxxyy = 5298, + Zo_RemoveScene = 5305, + Zo_RemoveAllScenes = 5317, + Zo_RecallScene = 5333, + Zo_GetSceneMembership = 5345, + Zo_PowerOffEffect = 5364, + Zo_xxyy = 5379, + Zo_PowerOnRecall = 5384, + Zo_PowerOnTimer = 5398, + Zo_xxyyyyzzzz = 5411, + Zo_xx0A00 = 5422, + Zo_DimmerUp = 5429, + Zo_00190200 = 5438, + Zo_DimmerDown = 5447, + Zo_01190200 = 5458, + Zo_DimmerStop = 5467, + Zo_ResetAlarm = 5478, + Zo_xxyyyy = 5489, + Zo_ResetAllAlarms = 5496, + Zo_xx000A00 = 5511, + Zo_HueSat = 5520, + Zo_xxyy0A00 = 5527, + Zo_Color = 5536, + Zo_xxxxyyyy0A00 = 5542, + Zo_xxxx0A00 = 5555, + Zo_ShutterOpen = 5564, + Zo_ShutterClose = 5576, + Zo_ShutterStop = 5589, + Zo_ShutterLift = 5601, + Zo_xx = 5613, + Zo_ShutterTilt = 5616, + Zo_Shutter = 5628, + Zo_DimmerMove = 5636, + Zo_xx0A = 5647, + Zo_DimmerStepUp = 5652, + Zo_00xx0A00 = 5665, + Zo_DimmerStepDown = 5674, + Zo_01xx0A00 = 5689, + Zo_DimmerStep = 5698, + Zo_xx190A00 = 5709, + Zo_01 = 5718, + Zo_HueMove = 5721, + Zo_xx19 = 5729, + Zo_HueStepUp = 5734, + Zo_HueStepDown = 5744, + Zo_03xx0A00 = 5756, + Zo_HueStep = 5765, + Zo_SatMove = 5773, + Zo_SatStep = 5781, + Zo_xx190A = 5789, + Zo_ColorMove = 5796, + Zo_xxxxyyyy = 5806, + Zo_ColorStep = 5815, + Zo_ColorTempMoveUp = 5825, + Zo_01xxxx000000000000 = 5841, + Zo_ColorTempMoveDown = 5860, + Zo_03xxxx000000000000 = 5878, + Zo_ColorTempMoveStop = 5897, + Zo_00xxxx000000000000 = 5915, + Zo_ColorTempMove = 5934, + Zo_xxyyyy000000000000 = 5948, + Zo_ColorTempStepUp = 5967, + Zo_01xxxx0A0000000000 = 5983, + Zo_ColorTempStepDown = 6002, + Zo_03xxxx0A0000000000 = 6020, + Zo_ColorTempStep = 6039, + Zo_xxyyyy0A0000000000 = 6053, + Zo_ArrowClick = 6072, + Zo_ArrowHold = 6083, + Zo_ArrowRelease = 6093, + Zo_ZoneStatusChange = 6106, + Zo_xxxxyyzz = 6123, + Zo_xxyyzzzz = 6132, + Zo_AddScene = 6141, + Zo_xxyyyyzz = 6150, + Zo_StoreScene = 6159, }; diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index cb2317084..642901f13 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -558,8 +558,9 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = { // IAS Cluster (Intruder Alarm System) { Zenum8, Cx0500, 0x0000, Z_(ZoneState), Cm1, 0 }, // Occupancy (map8) - { Zenum16, Cx0500, 0x0001, Z_(ZoneType), Cm1, 0 }, // Occupancy (map8) - { Zmap16, Cx0500, 0x0002, Z_(ZoneStatus), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Alarm, zone_type) }, // Occupancy (map8) + { Zenum16, Cx0500, 0x0001, Z_(ZoneType), Cm1, Z_MAPPING(Z_Data_Alarm, zone_type) }, // Zone type for sensor + { Zmap16, Cx0500, 0x0002, Z_(ZoneStatus), Cm1, Z_MAPPING(Z_Data_Alarm, zone_status) }, // Zone status for sensor + { Zbool, Cx0500, 0xFFF0, Z_(Contact), Cm1, Z_MAPPING(Z_Data_Alarm, zone_status) }, // We fit the first bit in the LSB // Metering (Smart Energy) cluster { Zuint48, Cx0702, 0x0000, Z_(CurrentSummDelivered), Cm1, 0 }, @@ -1226,6 +1227,8 @@ void ZCLFrame::generateSyntheticAttributes(Z_attribute_list& attr_list) { // Note: both function are now split to compute on extracted attributes // void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { + const char * model_c = zigbee_devices.getModelId(_srcaddr); // null if unknown + String modelId((char*) model_c); // scan through attributes and apply specific converters for (auto &attr : attr_list) { if (attr.key_is_str) { continue; } // pass if key is a name @@ -1239,12 +1242,13 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { } break; case 0x00010021: // BatteryPercentage - { - const char * model_c = zigbee_devices.getModelId(_srcaddr); // null if unknown - String modelId((char*) model_c); - if (modelId.startsWith(F("TRADFRI"))) { - attr.setUInt(attr.getUInt() * 2); // bug in TRADFRI battery, need to double the value - } + if (modelId.startsWith(F("TRADFRI"))) { + attr.setUInt(attr.getUInt() * 2); // bug in TRADFRI battery, need to double the value + } + break; + case 0x00060000: // "Power" for lumi Door/Window is converted to "Contact" + if (modelId.startsWith(F("lumi.sensor_magnet"))) { + attr.setKeyId(0x0500, 0xFFF0); // change cluster and attribute to 0500/FFF0 } break; case 0x02010008: // Pi Heating Demand - solve Eutotronic bug @@ -1558,9 +1562,21 @@ void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribu attr_list.addAttribute(0x0001, 0x0020).setFloat(batteryvoltage); uint8_t batterypercentage = toPercentageCR2032(uval32); attr_list.addAttribute(0x0001, 0x0021).setUInt(batterypercentage * 2); - } else if ((nullptr != modelId) && (0 == getManufCode())) { + } else if ((nullptr != modelId) && ((0 == getManufCode()) || (0x115F == getManufCode()))) { translated = true; - if (modelId.startsWith(F("lumi.sensor_ht")) || + if (modelId.startsWith(F("lumi.sensor_magnet"))) { // door / window sensor + if (0x64 == attrid) { + attr_list.addAttribute(0x0500, 0xFFF0).copyVal(attr); // Contact + } + } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { // gas leak + if (0x64 == attrid) { + attr_list.addAttribute(F("SmokeDensity")).copyVal(attr); + } + } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { // gas leak + if (0x64 == attrid) { + attr_list.addAttribute(F("GasDensity")).copyVal(attr); + } + } else if (modelId.startsWith(F("lumi.sensor_ht")) || modelId.equals(F("lumi.sens")) || modelId.startsWith(F("lumi.weather"))) { // Temp sensor // Filter according to prefix of model name @@ -1572,18 +1588,9 @@ void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribu } else if (0x66 == attrid) { attr_list.addAttribute(0x0403, 0x0000).setUInt((ival32 + 50) / 100); // Pressure } - } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { // gas leak - if (0x64 == attrid) { - attr_list.addAttribute(F("SmokeDensity")).copyVal(attr); - } - } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { // gas leak - if (0x64 == attrid) { - attr_list.addAttribute(F("GasDensity")).copyVal(attr); - } } else { translated = false; // we didn't find a match } - // } else if (0x115F == zcl->getManufCode()) { // Aqara Motion Sensor, still unknown field } if (!translated) { if (attrid >= 100) { // payload is always above 0x64 or 100 diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index f5854d175..425fcad0b 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -346,17 +346,24 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, // IAS switch (cccc00mm) { case 0x05000000: // "ZoneStatusChange" - attr_list.addAttribute(command_name, true).setUInt(xyz.x); - if (0 != xyz.y) { - attr_list.addAttribute(command_name, PSTR("Ext")).setUInt(xyz.y); + { + attr_list.addAttribute(command_name, true).setUInt(xyz.x); + if (0 != xyz.y) { + attr_list.addAttribute(command_name, PSTR("Ext")).setUInt(xyz.y); + } + if ((0 != xyz.z) && (0xFF != xyz.z)) { + attr_list.addAttribute(command_name, PSTR("Zone")).setUInt(xyz.z); + } + // Convert to "Occupancy" or to "Contact" if the device is PIR or Contact sensor + const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(shortaddr).data.find(Z_Data_Type::Z_Alarm, srcendpoint); + if (&alarm != nullptr) { + if (alarm.isPIR()) { // set Occupancy + attr_list.addAttribute(0x0406, 0x0000).setUInt((xyz.x) & 0x01 ? 1 : 0); + } else if (alarm.isContact()) { // set Contact + attr_list.addAttribute(0x0500, 0xFFF0).setUInt((xyz.x) & 0x01 ? 1 : 0); + } + } } - if ((0 != xyz.z) && (0xFF != xyz.z)) { - attr_list.addAttribute(command_name, PSTR("Zone")).setUInt(xyz.z); - } - // for now convert alamrs 1 and 2 to Occupancy - // TODO we may only do this conversion to ZoneType == 0x000D 'Motion Sensor' - // Occupancy is 0406/0000 of type Zmap8 - attr_list.addAttribute(0x0406, 0x0000).setUInt((xyz.x) & 0x01 ? 1 : 0); break; case 0x00040000: case 0x00040001: