Merge pull request #9759 from s-hadinger/zigbee_mi_door

Zigbee support for Mi Door and Contact
This commit is contained in:
s-hadinger 2020-11-06 22:38:28 +01:00 committed by GitHub
commit 37d74810ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 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

View File

@ -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,
};

View File

@ -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

View File

@ -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: