Zigbee support for Mi Door and Contact

This commit is contained in:
Stephan Hadinger 2020-11-06 22:24:45 +01:00
parent bf2788a6fd
commit 0840b7447d
4 changed files with 181 additions and 162 deletions

View File

@ -364,11 +364,14 @@ public:
inline bool validZoneType(void) const { return 0xFFFF != zone_type; } inline bool validZoneType(void) const { return 0xFFFF != zone_type; }
inline uint16_t getZoneType(void) const { return 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; } inline void setZoneType(uint16_t _zone_type) { zone_type = _zone_type; }
// 4 bytes // 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 // 0x0000 Standard CIE
// 0x000d Motion sensor // 0x000d Motion sensor
// 0x0015 Contact switch // 0x0015 Contact switch

View File

@ -341,6 +341,7 @@ const char Z_strings[] PROGMEM =
"ZoneState" "\x00" "ZoneState" "\x00"
"ZoneType" "\x00" "ZoneType" "\x00"
"ZoneStatus" "\x00" "ZoneStatus" "\x00"
"Contact" "\x00"
"CurrentSummDelivered" "\x00" "CurrentSummDelivered" "\x00"
"CompanyName" "\x00" "CompanyName" "\x00"
"MeterTypeID" "\x00" "MeterTypeID" "\x00"
@ -381,8 +382,8 @@ const char Z_strings[] PROGMEM =
"TuyaFanMode" "\x00" "TuyaFanMode" "\x00"
"TuyaForceMode" "\x00" "TuyaForceMode" "\x00"
"TuyaWeekSelect" "\x00" "TuyaWeekSelect" "\x00"
"TerncyRotate" "\x00"
"TerncyDuration" "\x00" "TerncyDuration" "\x00"
"TerncyRotate" "\x00"
"Identify" "\x00" "Identify" "\x00"
"xxxx" "\x00" "xxxx" "\x00"
"IdentifyQuery" "\x00" "IdentifyQuery" "\x00"
@ -742,137 +743,138 @@ enum Z_offsets {
Zo_ZoneState = 4540, Zo_ZoneState = 4540,
Zo_ZoneType = 4550, Zo_ZoneType = 4550,
Zo_ZoneStatus = 4559, Zo_ZoneStatus = 4559,
Zo_CurrentSummDelivered = 4570, Zo_Contact = 4570,
Zo_CompanyName = 4591, Zo_CurrentSummDelivered = 4578,
Zo_MeterTypeID = 4603, Zo_CompanyName = 4599,
Zo_DataQualityID = 4615, Zo_MeterTypeID = 4611,
Zo_CustomerName = 4629, Zo_DataQualityID = 4623,
Zo_Model = 4642, Zo_CustomerName = 4637,
Zo_PartNumber = 4648, Zo_Model = 4650,
Zo_ProductRevision = 4659, Zo_PartNumber = 4656,
Zo_SoftwareRevision = 4675, Zo_ProductRevision = 4667,
Zo_UtilityName = 4692, Zo_SoftwareRevision = 4683,
Zo_POD = 4704, Zo_UtilityName = 4700,
Zo_AvailablePower = 4708, Zo_POD = 4712,
Zo_PowerThreshold = 4723, Zo_AvailablePower = 4716,
Zo_RMSVoltage = 4738, Zo_PowerThreshold = 4731,
Zo_RMSCurrent = 4749, Zo_RMSVoltage = 4746,
Zo_ActivePower = 4760, Zo_RMSCurrent = 4757,
Zo_NumberOfResets = 4772, Zo_ActivePower = 4768,
Zo_PersistentMemoryWrites = 4787, Zo_NumberOfResets = 4780,
Zo_LastMessageLQI = 4810, Zo_PersistentMemoryWrites = 4795,
Zo_LastMessageRSSI = 4825, Zo_LastMessageLQI = 4818,
Zo_TuyaScheduleWorkdays = 4841, Zo_LastMessageRSSI = 4833,
Zo_TuyaScheduleHolidays = 4862, Zo_TuyaScheduleWorkdays = 4849,
Zo_TuyaChildLock = 4883, Zo_TuyaScheduleHolidays = 4870,
Zo_TuyaWindowDetection = 4897, Zo_TuyaChildLock = 4891,
Zo_TuyaValveDetection = 4917, Zo_TuyaWindowDetection = 4905,
Zo_TuyaAutoLock = 4936, Zo_TuyaValveDetection = 4925,
Zo_TuyaTempTarget = 4949, Zo_TuyaAutoLock = 4944,
Zo_TuyaBattery = 4964, Zo_TuyaTempTarget = 4957,
Zo_TuyaMinTemp = 4976, Zo_TuyaBattery = 4972,
Zo_TuyaMaxTemp = 4988, Zo_TuyaMinTemp = 4984,
Zo_TuyaBoostTime = 5000, Zo_TuyaMaxTemp = 4996,
Zo_TuyaComfortTemp = 5014, Zo_TuyaBoostTime = 5008,
Zo_TuyaEcoTemp = 5030, Zo_TuyaComfortTemp = 5022,
Zo_TuyaValvePosition = 5042, Zo_TuyaEcoTemp = 5038,
Zo_TuyaAwayTemp = 5060, Zo_TuyaValvePosition = 5050,
Zo_TuyaAwayDays = 5073, Zo_TuyaAwayTemp = 5068,
Zo_TuyaPreset = 5086, Zo_TuyaAwayDays = 5081,
Zo_TuyaFanMode = 5097, Zo_TuyaPreset = 5094,
Zo_TuyaForceMode = 5109, Zo_TuyaFanMode = 5105,
Zo_TuyaWeekSelect = 5123, Zo_TuyaForceMode = 5117,
Zo_TerncyRotate = 5138, Zo_TuyaWeekSelect = 5131,
Zo_TerncyDuration = 5151, Zo_TerncyDuration = 5146,
Zo_Identify = 5166, Zo_TerncyRotate = 5161,
Zo_xxxx = 5175, Zo_Identify = 5174,
Zo_IdentifyQuery = 5180, Zo_xxxx = 5183,
Zo_AddGroup = 5194, Zo_IdentifyQuery = 5188,
Zo_xxxx00 = 5203, Zo_AddGroup = 5202,
Zo_ViewGroup = 5210, Zo_xxxx00 = 5211,
Zo_GetGroup = 5220, Zo_ViewGroup = 5218,
Zo_01xxxx = 5229, Zo_GetGroup = 5228,
Zo_GetAllGroups = 5236, Zo_01xxxx = 5237,
Zo_00 = 5249, Zo_GetAllGroups = 5244,
Zo_RemoveGroup = 5252, Zo_00 = 5257,
Zo_RemoveAllGroups = 5264, Zo_RemoveGroup = 5260,
Zo_ViewScene = 5280, Zo_RemoveAllGroups = 5272,
Zo_xxxxyy = 5290, Zo_ViewScene = 5288,
Zo_RemoveScene = 5297, Zo_xxxxyy = 5298,
Zo_RemoveAllScenes = 5309, Zo_RemoveScene = 5305,
Zo_RecallScene = 5325, Zo_RemoveAllScenes = 5317,
Zo_GetSceneMembership = 5337, Zo_RecallScene = 5333,
Zo_PowerOffEffect = 5356, Zo_GetSceneMembership = 5345,
Zo_xxyy = 5371, Zo_PowerOffEffect = 5364,
Zo_PowerOnRecall = 5376, Zo_xxyy = 5379,
Zo_PowerOnTimer = 5390, Zo_PowerOnRecall = 5384,
Zo_xxyyyyzzzz = 5403, Zo_PowerOnTimer = 5398,
Zo_xx0A00 = 5414, Zo_xxyyyyzzzz = 5411,
Zo_DimmerUp = 5421, Zo_xx0A00 = 5422,
Zo_00190200 = 5430, Zo_DimmerUp = 5429,
Zo_DimmerDown = 5439, Zo_00190200 = 5438,
Zo_01190200 = 5450, Zo_DimmerDown = 5447,
Zo_DimmerStop = 5459, Zo_01190200 = 5458,
Zo_ResetAlarm = 5470, Zo_DimmerStop = 5467,
Zo_xxyyyy = 5481, Zo_ResetAlarm = 5478,
Zo_ResetAllAlarms = 5488, Zo_xxyyyy = 5489,
Zo_xx000A00 = 5503, Zo_ResetAllAlarms = 5496,
Zo_HueSat = 5512, Zo_xx000A00 = 5511,
Zo_xxyy0A00 = 5519, Zo_HueSat = 5520,
Zo_Color = 5528, Zo_xxyy0A00 = 5527,
Zo_xxxxyyyy0A00 = 5534, Zo_Color = 5536,
Zo_xxxx0A00 = 5547, Zo_xxxxyyyy0A00 = 5542,
Zo_ShutterOpen = 5556, Zo_xxxx0A00 = 5555,
Zo_ShutterClose = 5568, Zo_ShutterOpen = 5564,
Zo_ShutterStop = 5581, Zo_ShutterClose = 5576,
Zo_ShutterLift = 5593, Zo_ShutterStop = 5589,
Zo_xx = 5605, Zo_ShutterLift = 5601,
Zo_ShutterTilt = 5608, Zo_xx = 5613,
Zo_Shutter = 5620, Zo_ShutterTilt = 5616,
Zo_DimmerMove = 5628, Zo_Shutter = 5628,
Zo_xx0A = 5639, Zo_DimmerMove = 5636,
Zo_DimmerStepUp = 5644, Zo_xx0A = 5647,
Zo_00xx0A00 = 5657, Zo_DimmerStepUp = 5652,
Zo_DimmerStepDown = 5666, Zo_00xx0A00 = 5665,
Zo_01xx0A00 = 5681, Zo_DimmerStepDown = 5674,
Zo_DimmerStep = 5690, Zo_01xx0A00 = 5689,
Zo_xx190A00 = 5701, Zo_DimmerStep = 5698,
Zo_01 = 5710, Zo_xx190A00 = 5709,
Zo_HueMove = 5713, Zo_01 = 5718,
Zo_xx19 = 5721, Zo_HueMove = 5721,
Zo_HueStepUp = 5726, Zo_xx19 = 5729,
Zo_HueStepDown = 5736, Zo_HueStepUp = 5734,
Zo_03xx0A00 = 5748, Zo_HueStepDown = 5744,
Zo_HueStep = 5757, Zo_03xx0A00 = 5756,
Zo_SatMove = 5765, Zo_HueStep = 5765,
Zo_SatStep = 5773, Zo_SatMove = 5773,
Zo_xx190A = 5781, Zo_SatStep = 5781,
Zo_ColorMove = 5788, Zo_xx190A = 5789,
Zo_xxxxyyyy = 5798, Zo_ColorMove = 5796,
Zo_ColorStep = 5807, Zo_xxxxyyyy = 5806,
Zo_ColorTempMoveUp = 5817, Zo_ColorStep = 5815,
Zo_01xxxx000000000000 = 5833, Zo_ColorTempMoveUp = 5825,
Zo_ColorTempMoveDown = 5852, Zo_01xxxx000000000000 = 5841,
Zo_03xxxx000000000000 = 5870, Zo_ColorTempMoveDown = 5860,
Zo_ColorTempMoveStop = 5889, Zo_03xxxx000000000000 = 5878,
Zo_00xxxx000000000000 = 5907, Zo_ColorTempMoveStop = 5897,
Zo_ColorTempMove = 5926, Zo_00xxxx000000000000 = 5915,
Zo_xxyyyy000000000000 = 5940, Zo_ColorTempMove = 5934,
Zo_ColorTempStepUp = 5959, Zo_xxyyyy000000000000 = 5948,
Zo_01xxxx0A0000000000 = 5975, Zo_ColorTempStepUp = 5967,
Zo_ColorTempStepDown = 5994, Zo_01xxxx0A0000000000 = 5983,
Zo_03xxxx0A0000000000 = 6012, Zo_ColorTempStepDown = 6002,
Zo_ColorTempStep = 6031, Zo_03xxxx0A0000000000 = 6020,
Zo_xxyyyy0A0000000000 = 6045, Zo_ColorTempStep = 6039,
Zo_ArrowClick = 6064, Zo_xxyyyy0A0000000000 = 6053,
Zo_ArrowHold = 6075, Zo_ArrowClick = 6072,
Zo_ArrowRelease = 6085, Zo_ArrowHold = 6083,
Zo_ZoneStatusChange = 6098, Zo_ArrowRelease = 6093,
Zo_xxxxyyzz = 6115, Zo_ZoneStatusChange = 6106,
Zo_xxyyzzzz = 6124, Zo_xxxxyyzz = 6123,
Zo_AddScene = 6133, Zo_xxyyzzzz = 6132,
Zo_xxyyyyzz = 6142, Zo_AddScene = 6141,
Zo_StoreScene = 6151, Zo_xxyyyyzz = 6150,
Zo_StoreScene = 6159,
}; };

View File

@ -558,8 +558,9 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
// IAS Cluster (Intruder Alarm System) // IAS Cluster (Intruder Alarm System)
{ Zenum8, Cx0500, 0x0000, Z_(ZoneState), Cm1, 0 }, // Occupancy (map8) { Zenum8, Cx0500, 0x0000, Z_(ZoneState), Cm1, 0 }, // Occupancy (map8)
{ Zenum16, Cx0500, 0x0001, Z_(ZoneType), Cm1, 0 }, // 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_EXPORT_DATA, Z_MAPPING(Z_Data_Alarm, zone_type) }, // Occupancy (map8) { 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 // Metering (Smart Energy) cluster
{ Zuint48, Cx0702, 0x0000, Z_(CurrentSummDelivered), Cm1, 0 }, { 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 // Note: both function are now split to compute on extracted attributes
// //
void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { 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 // scan through attributes and apply specific converters
for (auto &attr : attr_list) { for (auto &attr : attr_list) {
if (attr.key_is_str) { continue; } // pass if key is a name 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; break;
case 0x00010021: // BatteryPercentage case 0x00010021: // BatteryPercentage
{ if (modelId.startsWith(F("TRADFRI"))) {
const char * model_c = zigbee_devices.getModelId(_srcaddr); // null if unknown attr.setUInt(attr.getUInt() * 2); // bug in TRADFRI battery, need to double the value
String modelId((char*) model_c); }
if (modelId.startsWith(F("TRADFRI"))) { break;
attr.setUInt(attr.getUInt() * 2); // bug in TRADFRI battery, need to double the value 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; break;
case 0x02010008: // Pi Heating Demand - solve Eutotronic bug 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); attr_list.addAttribute(0x0001, 0x0020).setFloat(batteryvoltage);
uint8_t batterypercentage = toPercentageCR2032(uval32); uint8_t batterypercentage = toPercentageCR2032(uval32);
attr_list.addAttribute(0x0001, 0x0021).setUInt(batterypercentage * 2); attr_list.addAttribute(0x0001, 0x0021).setUInt(batterypercentage * 2);
} else if ((nullptr != modelId) && (0 == getManufCode())) { } else if ((nullptr != modelId) && ((0 == getManufCode()) || (0x115F == getManufCode()))) {
translated = true; 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.equals(F("lumi.sens")) ||
modelId.startsWith(F("lumi.weather"))) { // Temp sensor modelId.startsWith(F("lumi.weather"))) { // Temp sensor
// Filter according to prefix of model name // 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) { } else if (0x66 == attrid) {
attr_list.addAttribute(0x0403, 0x0000).setUInt((ival32 + 50) / 100); // Pressure 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 { } else {
translated = false; // we didn't find a match translated = false; // we didn't find a match
} }
// } else if (0x115F == zcl->getManufCode()) { // Aqara Motion Sensor, still unknown field
} }
if (!translated) { if (!translated) {
if (attrid >= 100) { // payload is always above 0x64 or 100 if (attrid >= 100) { // payload is always above 0x64 or 100

View File

@ -346,17 +346,24 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster,
// IAS // IAS
switch (cccc00mm) { switch (cccc00mm) {
case 0x05000000: // "ZoneStatusChange" case 0x05000000: // "ZoneStatusChange"
attr_list.addAttribute(command_name, true).setUInt(xyz.x); {
if (0 != xyz.y) { attr_list.addAttribute(command_name, true).setUInt(xyz.x);
attr_list.addAttribute(command_name, PSTR("Ext")).setUInt(xyz.y); 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; break;
case 0x00040000: case 0x00040000:
case 0x00040001: case 0x00040001: