mirror of https://github.com/arendst/Tasmota.git
Zigbee device plugin mechanism with commands ``ZbLoad``, ``ZbUnload`` and ``ZbLoadDump``
This commit is contained in:
parent
94c88df93b
commit
ff07d0608b
|
@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [12.1.0.1]
|
## [12.1.0.1]
|
||||||
### Added
|
### Added
|
||||||
|
- Zigbee device plugin mechanism with commands ``ZbLoad``, ``ZbUnload`` and ``ZbLoadDump``
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -672,6 +672,9 @@
|
||||||
#define D_JSON_ZIGBEE_SCAN "ZbScan"
|
#define D_JSON_ZIGBEE_SCAN "ZbScan"
|
||||||
#define D_CMND_ZIGBEE_CIE "CIE"
|
#define D_CMND_ZIGBEE_CIE "CIE"
|
||||||
#define D_CMND_ZIGBEE_ENROLL "Enroll"
|
#define D_CMND_ZIGBEE_ENROLL "Enroll"
|
||||||
|
#define D_CMND_ZIGBEE_LOAD "Load"
|
||||||
|
#define D_CMND_ZIGBEE_LOADDUMP "LoadDump"
|
||||||
|
#define D_CMND_ZIGBEE_UNLOAD "Unload"
|
||||||
|
|
||||||
// Commands xdrv_25_A4988_Stepper.ino
|
// Commands xdrv_25_A4988_Stepper.ino
|
||||||
#define D_CMND_MOTOR "MOTOR"
|
#define D_CMND_MOTOR "MOTOR"
|
||||||
|
|
|
@ -356,9 +356,6 @@ const char Z_strings[] PROGMEM =
|
||||||
"PhysicalClosedLimitLift" "\x00"
|
"PhysicalClosedLimitLift" "\x00"
|
||||||
"PhysicalClosedLimitTilt" "\x00"
|
"PhysicalClosedLimitTilt" "\x00"
|
||||||
"Power" "\x00"
|
"Power" "\x00"
|
||||||
"Power2" "\x00"
|
|
||||||
"Power3" "\x00"
|
|
||||||
"Power4" "\x00"
|
|
||||||
"PowerOffEffect" "\x00"
|
"PowerOffEffect" "\x00"
|
||||||
"PowerOnRecall" "\x00"
|
"PowerOnRecall" "\x00"
|
||||||
"PowerOnTimer" "\x00"
|
"PowerOnTimer" "\x00"
|
||||||
|
@ -440,32 +437,12 @@ const char Z_strings[] PROGMEM =
|
||||||
"TimeStatus" "\x00"
|
"TimeStatus" "\x00"
|
||||||
"TimeZone" "\x00"
|
"TimeZone" "\x00"
|
||||||
"TotalProfileNum" "\x00"
|
"TotalProfileNum" "\x00"
|
||||||
"TuyaAutoLock" "\x00"
|
|
||||||
"TuyaAwayDays" "\x00"
|
|
||||||
"TuyaAwayTemp" "\x00"
|
|
||||||
"TuyaBattery" "\x00"
|
|
||||||
"TuyaBoostTime" "\x00"
|
|
||||||
"TuyaCalibration" "\x00"
|
"TuyaCalibration" "\x00"
|
||||||
"TuyaCalibrationTime" "\x00"
|
"TuyaCalibrationTime" "\x00"
|
||||||
"TuyaChildLock" "\x00"
|
|
||||||
"TuyaComfortTemp" "\x00"
|
|
||||||
"TuyaEcoTemp" "\x00"
|
|
||||||
"TuyaFanMode" "\x00"
|
|
||||||
"TuyaForceMode" "\x00"
|
|
||||||
"TuyaMCUVersion" "\x00"
|
"TuyaMCUVersion" "\x00"
|
||||||
"TuyaMaxTemp" "\x00"
|
|
||||||
"TuyaMinTemp" "\x00"
|
|
||||||
"TuyaMotorReversal" "\x00"
|
"TuyaMotorReversal" "\x00"
|
||||||
"TuyaMovingState" "\x00"
|
"TuyaMovingState" "\x00"
|
||||||
"TuyaPreset" "\x00"
|
|
||||||
"TuyaQuery" "\x00"
|
"TuyaQuery" "\x00"
|
||||||
"TuyaScheduleHolidays" "\x00"
|
|
||||||
"TuyaScheduleWorkdays" "\x00"
|
|
||||||
"TuyaTempTarget" "\x00"
|
|
||||||
"TuyaValveDetection" "\x00"
|
|
||||||
"TuyaValvePosition" "\x00"
|
|
||||||
"TuyaWeekSelect" "\x00"
|
|
||||||
"TuyaWindowDetection" "\x00"
|
|
||||||
"UnoccupiedCoolingSetpoint" "\x00"
|
"UnoccupiedCoolingSetpoint" "\x00"
|
||||||
"UnoccupiedHeatingSetpoint" "\x00"
|
"UnoccupiedHeatingSetpoint" "\x00"
|
||||||
"UtilityName" "\x00"
|
"UtilityName" "\x00"
|
||||||
|
@ -792,158 +769,135 @@ enum Z_offsets {
|
||||||
Zo_PhysicalClosedLimitLift = 4561,
|
Zo_PhysicalClosedLimitLift = 4561,
|
||||||
Zo_PhysicalClosedLimitTilt = 4585,
|
Zo_PhysicalClosedLimitTilt = 4585,
|
||||||
Zo_Power = 4609,
|
Zo_Power = 4609,
|
||||||
Zo_Power2 = 4615,
|
Zo_PowerOffEffect = 4615,
|
||||||
Zo_Power3 = 4622,
|
Zo_PowerOnRecall = 4630,
|
||||||
Zo_Power4 = 4629,
|
Zo_PowerOnTimer = 4644,
|
||||||
Zo_PowerOffEffect = 4636,
|
Zo_PowerSource = 4657,
|
||||||
Zo_PowerOnRecall = 4651,
|
Zo_PowerThreshold = 4669,
|
||||||
Zo_PowerOnTimer = 4665,
|
Zo_Pressure = 4684,
|
||||||
Zo_PowerSource = 4678,
|
Zo_PressureMaxMeasuredValue = 4693,
|
||||||
Zo_PowerThreshold = 4690,
|
Zo_PressureMaxScaledValue = 4718,
|
||||||
Zo_Pressure = 4705,
|
Zo_PressureMinMeasuredValue = 4741,
|
||||||
Zo_PressureMaxMeasuredValue = 4714,
|
Zo_PressureMinScaledValue = 4766,
|
||||||
Zo_PressureMaxScaledValue = 4739,
|
Zo_PressureScale = 4789,
|
||||||
Zo_PressureMinMeasuredValue = 4762,
|
Zo_PressureScaledTolerance = 4803,
|
||||||
Zo_PressureMinScaledValue = 4787,
|
Zo_PressureScaledValue = 4827,
|
||||||
Zo_PressureScale = 4810,
|
Zo_PressureTolerance = 4847,
|
||||||
Zo_PressureScaledTolerance = 4824,
|
Zo_Primary1Intensity = 4865,
|
||||||
Zo_PressureScaledValue = 4848,
|
Zo_Primary1X = 4883,
|
||||||
Zo_PressureTolerance = 4868,
|
Zo_Primary1Y = 4893,
|
||||||
Zo_Primary1Intensity = 4886,
|
Zo_Primary2Intensity = 4903,
|
||||||
Zo_Primary1X = 4904,
|
Zo_Primary2X = 4921,
|
||||||
Zo_Primary1Y = 4914,
|
Zo_Primary2Y = 4931,
|
||||||
Zo_Primary2Intensity = 4924,
|
Zo_Primary3Intensity = 4941,
|
||||||
Zo_Primary2X = 4942,
|
Zo_Primary3X = 4959,
|
||||||
Zo_Primary2Y = 4952,
|
Zo_Primary3Y = 4969,
|
||||||
Zo_Primary3Intensity = 4962,
|
Zo_ProductCode = 4979,
|
||||||
Zo_Primary3X = 4980,
|
Zo_ProductRevision = 4991,
|
||||||
Zo_Primary3Y = 4990,
|
Zo_ProductURL = 5007,
|
||||||
Zo_ProductCode = 5000,
|
Zo_QualityMeasure = 5018,
|
||||||
Zo_ProductRevision = 5012,
|
Zo_RGB = 5033,
|
||||||
Zo_ProductURL = 5028,
|
Zo_RMSCurrent = 5037,
|
||||||
Zo_QualityMeasure = 5039,
|
Zo_RMSVoltage = 5048,
|
||||||
Zo_RGB = 5054,
|
Zo_ReactivePower = 5059,
|
||||||
Zo_RMSCurrent = 5058,
|
Zo_RecallScene = 5073,
|
||||||
Zo_RMSVoltage = 5069,
|
Zo_RemainingTime = 5085,
|
||||||
Zo_ReactivePower = 5080,
|
Zo_RemoteSensing = 5099,
|
||||||
Zo_RecallScene = 5094,
|
Zo_RemoveAllGroups = 5113,
|
||||||
Zo_RemainingTime = 5106,
|
Zo_RemoveAllScenes = 5129,
|
||||||
Zo_RemoteSensing = 5120,
|
Zo_RemoveGroup = 5145,
|
||||||
Zo_RemoveAllGroups = 5134,
|
Zo_RemoveScene = 5157,
|
||||||
Zo_RemoveAllScenes = 5150,
|
Zo_ResetAlarm = 5169,
|
||||||
Zo_RemoveGroup = 5166,
|
Zo_ResetAllAlarms = 5180,
|
||||||
Zo_RemoveScene = 5178,
|
Zo_SWBuildID = 5195,
|
||||||
Zo_ResetAlarm = 5190,
|
Zo_Sat = 5205,
|
||||||
Zo_ResetAllAlarms = 5201,
|
Zo_SatMove = 5209,
|
||||||
Zo_SWBuildID = 5216,
|
Zo_SatStep = 5217,
|
||||||
Zo_Sat = 5226,
|
Zo_SceneCount = 5225,
|
||||||
Zo_SatMove = 5230,
|
Zo_SceneValid = 5236,
|
||||||
Zo_SatStep = 5238,
|
Zo_ScheduleMode = 5247,
|
||||||
Zo_SceneCount = 5246,
|
Zo_SeaPressure = 5260,
|
||||||
Zo_SceneValid = 5257,
|
Zo_ShortPollInterval = 5272,
|
||||||
Zo_ScheduleMode = 5268,
|
Zo_Shutter = 5290,
|
||||||
Zo_SeaPressure = 5281,
|
Zo_ShutterClose = 5298,
|
||||||
Zo_ShortPollInterval = 5293,
|
Zo_ShutterLift = 5311,
|
||||||
Zo_Shutter = 5311,
|
Zo_ShutterOpen = 5323,
|
||||||
Zo_ShutterClose = 5319,
|
Zo_ShutterStop = 5335,
|
||||||
Zo_ShutterLift = 5332,
|
Zo_ShutterTilt = 5347,
|
||||||
Zo_ShutterOpen = 5344,
|
Zo_SoftwareRevision = 5359,
|
||||||
Zo_ShutterStop = 5356,
|
Zo_StackVersion = 5376,
|
||||||
Zo_ShutterTilt = 5368,
|
Zo_StandardTime = 5389,
|
||||||
Zo_SoftwareRevision = 5380,
|
Zo_StartUpOnOff = 5402,
|
||||||
Zo_StackVersion = 5397,
|
Zo_Status = 5415,
|
||||||
Zo_StandardTime = 5410,
|
Zo_StoreScene = 5422,
|
||||||
Zo_StartUpOnOff = 5423,
|
Zo_SwitchType = 5433,
|
||||||
Zo_Status = 5436,
|
Zo_SystemMode = 5444,
|
||||||
Zo_StoreScene = 5443,
|
Zo_TRVBoost = 5455,
|
||||||
Zo_SwitchType = 5454,
|
Zo_TRVChildProtection = 5464,
|
||||||
Zo_SystemMode = 5465,
|
Zo_TRVMirrorDisplay = 5483,
|
||||||
Zo_TRVBoost = 5476,
|
Zo_TRVMode = 5500,
|
||||||
Zo_TRVChildProtection = 5485,
|
Zo_TRVWindowOpen = 5508,
|
||||||
Zo_TRVMirrorDisplay = 5504,
|
Zo_TempTarget = 5522,
|
||||||
Zo_TRVMode = 5521,
|
Zo_Temperature = 5533,
|
||||||
Zo_TRVWindowOpen = 5529,
|
Zo_TemperatureMaxMeasuredValue = 5545,
|
||||||
Zo_TempTarget = 5543,
|
Zo_TemperatureMinMeasuredValue = 5573,
|
||||||
Zo_Temperature = 5554,
|
Zo_TemperatureTolerance = 5601,
|
||||||
Zo_TemperatureMaxMeasuredValue = 5566,
|
Zo_TerncyDuration = 5622,
|
||||||
Zo_TemperatureMinMeasuredValue = 5594,
|
Zo_TerncyRotate = 5637,
|
||||||
Zo_TemperatureTolerance = 5622,
|
Zo_ThSetpoint = 5650,
|
||||||
Zo_TerncyDuration = 5643,
|
Zo_Time = 5661,
|
||||||
Zo_TerncyRotate = 5658,
|
Zo_TimeEpoch = 5666,
|
||||||
Zo_ThSetpoint = 5671,
|
Zo_TimeStatus = 5676,
|
||||||
Zo_Time = 5682,
|
Zo_TimeZone = 5687,
|
||||||
Zo_TimeEpoch = 5687,
|
Zo_TotalProfileNum = 5696,
|
||||||
Zo_TimeStatus = 5697,
|
Zo_TuyaCalibration = 5712,
|
||||||
Zo_TimeZone = 5708,
|
Zo_TuyaCalibrationTime = 5728,
|
||||||
Zo_TotalProfileNum = 5717,
|
Zo_TuyaMCUVersion = 5748,
|
||||||
Zo_TuyaAutoLock = 5733,
|
Zo_TuyaMotorReversal = 5763,
|
||||||
Zo_TuyaAwayDays = 5746,
|
Zo_TuyaMovingState = 5781,
|
||||||
Zo_TuyaAwayTemp = 5759,
|
Zo_TuyaQuery = 5797,
|
||||||
Zo_TuyaBattery = 5772,
|
Zo_UnoccupiedCoolingSetpoint = 5807,
|
||||||
Zo_TuyaBoostTime = 5784,
|
Zo_UnoccupiedHeatingSetpoint = 5833,
|
||||||
Zo_TuyaCalibration = 5798,
|
Zo_UtilityName = 5859,
|
||||||
Zo_TuyaCalibrationTime = 5814,
|
Zo_ValidUntilTime = 5871,
|
||||||
Zo_TuyaChildLock = 5834,
|
Zo_ValvePosition = 5886,
|
||||||
Zo_TuyaComfortTemp = 5848,
|
Zo_VelocityLift = 5900,
|
||||||
Zo_TuyaEcoTemp = 5864,
|
Zo_ViewGroup = 5913,
|
||||||
Zo_TuyaFanMode = 5876,
|
Zo_ViewScene = 5923,
|
||||||
Zo_TuyaForceMode = 5888,
|
Zo_Water = 5933,
|
||||||
Zo_TuyaMCUVersion = 5902,
|
Zo_WhitePointX = 5939,
|
||||||
Zo_TuyaMaxTemp = 5917,
|
Zo_WhitePointY = 5951,
|
||||||
Zo_TuyaMinTemp = 5929,
|
Zo_WindowCoveringType = 5963,
|
||||||
Zo_TuyaMotorReversal = 5941,
|
Zo_X = 5982,
|
||||||
Zo_TuyaMovingState = 5959,
|
Zo_Y = 5984,
|
||||||
Zo_TuyaPreset = 5975,
|
Zo_ZCLVersion = 5986,
|
||||||
Zo_TuyaQuery = 5986,
|
Zo_ZoneState = 5997,
|
||||||
Zo_TuyaScheduleHolidays = 5996,
|
Zo_ZoneStatus = 6007,
|
||||||
Zo_TuyaScheduleWorkdays = 6017,
|
Zo_ZoneStatusChange = 6018,
|
||||||
Zo_TuyaTempTarget = 6038,
|
Zo_ZoneType = 6035,
|
||||||
Zo_TuyaValveDetection = 6053,
|
Zo__ = 6044,
|
||||||
Zo_TuyaValvePosition = 6072,
|
Zo_xx = 6046,
|
||||||
Zo_TuyaWeekSelect = 6090,
|
Zo_xx000A00 = 6049,
|
||||||
Zo_TuyaWindowDetection = 6105,
|
Zo_xx0A = 6058,
|
||||||
Zo_UnoccupiedCoolingSetpoint = 6125,
|
Zo_xx0A00 = 6063,
|
||||||
Zo_UnoccupiedHeatingSetpoint = 6151,
|
Zo_xx19 = 6070,
|
||||||
Zo_UtilityName = 6177,
|
Zo_xx190A = 6075,
|
||||||
Zo_ValidUntilTime = 6189,
|
Zo_xx190A00 = 6082,
|
||||||
Zo_ValvePosition = 6204,
|
Zo_xxxx = 6091,
|
||||||
Zo_VelocityLift = 6218,
|
Zo_xxxx00 = 6096,
|
||||||
Zo_ViewGroup = 6231,
|
Zo_xxxx0A00 = 6103,
|
||||||
Zo_ViewScene = 6241,
|
Zo_xxxxyy = 6112,
|
||||||
Zo_Water = 6251,
|
Zo_xxxxyyyy = 6119,
|
||||||
Zo_WhitePointX = 6257,
|
Zo_xxxxyyyy0A00 = 6128,
|
||||||
Zo_WhitePointY = 6269,
|
Zo_xxxxyyzz = 6141,
|
||||||
Zo_WindowCoveringType = 6281,
|
Zo_xxyy = 6150,
|
||||||
Zo_X = 6300,
|
Zo_xxyy0A00 = 6155,
|
||||||
Zo_Y = 6302,
|
Zo_xxyyyy = 6164,
|
||||||
Zo_ZCLVersion = 6304,
|
Zo_xxyyyy000000000000 = 6171,
|
||||||
Zo_ZoneState = 6315,
|
Zo_xxyyyy0A0000000000 = 6190,
|
||||||
Zo_ZoneStatus = 6325,
|
Zo_xxyyyyzz = 6209,
|
||||||
Zo_ZoneStatusChange = 6336,
|
Zo_xxyyyyzzzz = 6218,
|
||||||
Zo_ZoneType = 6353,
|
Zo_xxyyzzzz = 6229,
|
||||||
Zo__ = 6362,
|
|
||||||
Zo_xx = 6364,
|
|
||||||
Zo_xx000A00 = 6367,
|
|
||||||
Zo_xx0A = 6376,
|
|
||||||
Zo_xx0A00 = 6381,
|
|
||||||
Zo_xx19 = 6388,
|
|
||||||
Zo_xx190A = 6393,
|
|
||||||
Zo_xx190A00 = 6400,
|
|
||||||
Zo_xxxx = 6409,
|
|
||||||
Zo_xxxx00 = 6414,
|
|
||||||
Zo_xxxx0A00 = 6421,
|
|
||||||
Zo_xxxxyy = 6430,
|
|
||||||
Zo_xxxxyyyy = 6437,
|
|
||||||
Zo_xxxxyyyy0A00 = 6446,
|
|
||||||
Zo_xxxxyyzz = 6459,
|
|
||||||
Zo_xxyy = 6468,
|
|
||||||
Zo_xxyy0A00 = 6473,
|
|
||||||
Zo_xxyyyy = 6482,
|
|
||||||
Zo_xxyyyy000000000000 = 6489,
|
|
||||||
Zo_xxyyyy0A0000000000 = 6508,
|
|
||||||
Zo_xxyyyyzz = 6527,
|
|
||||||
Zo_xxyyyyzzzz = 6536,
|
|
||||||
Zo_xxyyzzzz = 6547,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,955 @@
|
||||||
|
/*
|
||||||
|
xdrv_23_zigbee_converters.ino - zigbee support for Tasmota
|
||||||
|
|
||||||
|
Copyright (C) 2021 Theo Arends and Stephan Hadinger
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef USE_ZIGBEE
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* ZCL
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
enum Z_DataTypes {
|
||||||
|
Znodata = 0x00,
|
||||||
|
Zdata8 = 0x08, Zdata16, Zdata24, Zdata32, Zdata40, Zdata48, Zdata56, Zdata64,
|
||||||
|
Zbool = 0x10,
|
||||||
|
Zmap8 = 0x18, Zmap16, Zmap24, Zmap32, Zmap40, Zmap48, Zmap56, Zmap64,
|
||||||
|
Zuint8 = 0x20, Zuint16, Zuint24, Zuint32, Zuint40, Zuint48, Zuint56, Zuint64,
|
||||||
|
Zint8 = 0x28, Zint16, Zint24, Zint32, Zint40, Zint48, Zint56, Zint64,
|
||||||
|
Zenum8 = 0x30, Zenum16 = 0x31,
|
||||||
|
Zsemi = 0x38, Zsingle = 0x39, Zdouble = 0x3A,
|
||||||
|
Zoctstr = 0x41, Zstring = 0x42, Zoctstr16 = 0x43, Zstring16 = 0x44,
|
||||||
|
Zarrray = 0x48,
|
||||||
|
Zstruct = 0x4C,
|
||||||
|
Zset = 0x50, Zbag = 0x51,
|
||||||
|
ZToD = 0xE0, Zdate = 0xE1, ZUTC = 0xE2,
|
||||||
|
ZclusterId = 0xE8, ZattribId = 0xE9, ZbacOID = 0xEA,
|
||||||
|
ZEUI64 = 0xF0, Zkey128 = 0xF1,
|
||||||
|
Zunk = 0xFF,
|
||||||
|
// adding fake type for Tuya specific encodings
|
||||||
|
Ztuya0 = Zoctstr,
|
||||||
|
Ztuya1 = Zbool,
|
||||||
|
Ztuya2 = Zint32,
|
||||||
|
Ztuya3 = Zstring,
|
||||||
|
Ztuya4 = Zuint8,
|
||||||
|
Ztuya5 = Zuint32
|
||||||
|
};
|
||||||
|
|
||||||
|
const char Z_DATATYPES[] PROGMEM =
|
||||||
|
"nodata|"
|
||||||
|
"data8|data16|data24|data32|data40|data48|data56|data64|"
|
||||||
|
"bool|"
|
||||||
|
"map8|map16|map24|map32|map40|map48|map56|map64|"
|
||||||
|
"uint8|uint16|uint24|uint32|uint40|uint48|uint56|uint64|"
|
||||||
|
"int8|int16|int24|int32|int40|int48|int56|int64|"
|
||||||
|
"enum8|enum16|"
|
||||||
|
"semi|single|double|"
|
||||||
|
"octstr|string|octstr16|string16|"
|
||||||
|
"array|struct|set|bag|"
|
||||||
|
"ToD|date|UTC|"
|
||||||
|
"clusterId|attribId|bacOID|"
|
||||||
|
"EUI64|key128|"
|
||||||
|
"unk"
|
||||||
|
;
|
||||||
|
|
||||||
|
const uint8_t Z_DATA_TYPES_CODE[] PROGMEM = {
|
||||||
|
Znodata,
|
||||||
|
Zdata8, Zdata16, Zdata24, Zdata32, Zdata40, Zdata48, Zdata56, Zdata64,
|
||||||
|
Zbool,
|
||||||
|
Zmap8, Zmap16, Zmap24, Zmap32, Zmap40, Zmap48, Zmap56, Zmap64,
|
||||||
|
Zuint8, Zuint16, Zuint24, Zuint32, Zuint40, Zuint48, Zuint56, Zuint64,
|
||||||
|
Zint8, Zint16, Zint24, Zint32, Zint40, Zint48, Zint56, Zint64,
|
||||||
|
Zenum8, Zenum16,
|
||||||
|
Zsemi, Zsingle, Zdouble,
|
||||||
|
Zoctstr, Zstring, Zoctstr16, Zstring16,
|
||||||
|
Zarrray, Zstruct, Zset, Zbag,
|
||||||
|
ZToD, Zdate, ZUTC,
|
||||||
|
ZclusterId, ZattribId, ZbacOID,
|
||||||
|
ZEUI64, Zkey128,
|
||||||
|
Zunk,
|
||||||
|
};
|
||||||
|
|
||||||
|
// convert a type into a name, or HEX if none found
|
||||||
|
void Z_getTypeByNumber(char *destination, size_t destination_size, uint8_t type) {
|
||||||
|
for (uint32_t i = 0; i < ARRAY_SIZE(Z_DATA_TYPES_CODE); i++) {
|
||||||
|
if (type == pgm_read_byte(&Z_DATA_TYPES_CODE[i])) {
|
||||||
|
GetTextIndexed(destination, destination_size, i, Z_DATATYPES);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snprintf(destination, destination_size, "%02X", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert a string to a type, or Zunk if no match
|
||||||
|
uint8_t Z_getTypeByName(const char *type) {
|
||||||
|
if (type == nullptr) { return Zunk; }
|
||||||
|
char type_found[16];
|
||||||
|
int32_t ret = GetCommandCode(type_found, sizeof(type_found), type, Z_DATATYPES);
|
||||||
|
if (ret < 0) {
|
||||||
|
// try to decode hex
|
||||||
|
size_t type_len = strlen_P(type);
|
||||||
|
if (type_len > 0 && type_len <= 2) {
|
||||||
|
char *type_end;
|
||||||
|
ret = strtoul(type, &type_end, 16);
|
||||||
|
if (type_end == type) { ret = Zunk; } // could not decode
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
return pgm_read_byte(&Z_DATA_TYPES_CODE[ret]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// get the lenth in bytes for a data-type
|
||||||
|
// return 0 if unknown of type specific
|
||||||
|
//
|
||||||
|
// Note: this code is smaller than a static array
|
||||||
|
uint8_t Z_getDatatypeLen(uint8_t t) {
|
||||||
|
if ( ((t >= 0x08) && (t <= 0x0F)) || // data8 - data64
|
||||||
|
((t >= 0x18) && (t <= 0x2F)) ) { // map/uint/int
|
||||||
|
return (t & 0x07) + 1;
|
||||||
|
}
|
||||||
|
switch (t) {
|
||||||
|
case Zbool:
|
||||||
|
case Zenum8:
|
||||||
|
return 1;
|
||||||
|
case Zenum16:
|
||||||
|
case Zsemi:
|
||||||
|
case ZclusterId:
|
||||||
|
case ZattribId:
|
||||||
|
return 2;
|
||||||
|
case Zsingle:
|
||||||
|
case ZToD:
|
||||||
|
case Zdate:
|
||||||
|
case ZUTC:
|
||||||
|
case ZbacOID:
|
||||||
|
return 4;
|
||||||
|
case Zdouble:
|
||||||
|
case ZEUI64:
|
||||||
|
return 8;
|
||||||
|
case Zkey128:
|
||||||
|
return 16;
|
||||||
|
case Znodata:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the type a discrete type, cf. section 2.6.2 of ZCL spec
|
||||||
|
bool Z_isDiscreteDataType(uint8_t t) {
|
||||||
|
if ( ((t >= 0x20) && (t <= 0x2F)) || // uint8 - int64
|
||||||
|
((t >= 0x38) && (t <= 0x3A)) || // semi - double
|
||||||
|
((t >= 0xE0) && (t <= 0xE2)) ) { // ToD - UTC
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct Z_AttributeConverter {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t cluster_short;
|
||||||
|
uint16_t attribute;
|
||||||
|
uint16_t name_offset;
|
||||||
|
uint8_t multiplier_idx; // multiplier index for numerical value, use CmToMultiplier(), (if > 0 multiply by x, if <0 device by x)
|
||||||
|
// the high 4 bits are used to encode flags
|
||||||
|
// currently: 0x80 = this parameter needs to be exported to ZbData
|
||||||
|
uint8_t mapping; // high 4 bits = type, low 4 bits = offset in bytes from header
|
||||||
|
// still room for a byte
|
||||||
|
} Z_AttributeConverter;
|
||||||
|
|
||||||
|
// Get offset in bytes of attributes, starting after the header (skipping first 4 bytes)
|
||||||
|
#define Z_OFFSET(c,a) (offsetof(class c, a) - sizeof(Z_Data))
|
||||||
|
#define Z_CLASS(c) c // necessary to get a valid token without concatenation (which wouldn't work)
|
||||||
|
#define Z_MAPPING(c,a) (((((uint8_t)Z_CLASS(c)::type) & 0x0F) << 4) | Z_OFFSET(c,a))
|
||||||
|
|
||||||
|
// lines with this marker, will be used to export automatically data to `ZbData`
|
||||||
|
// at the condition Z_MAPPING() is also used
|
||||||
|
const uint8_t Z_EXPORT_DATA = 0x80;
|
||||||
|
|
||||||
|
// Cluster numbers are store in 8 bits format to save space,
|
||||||
|
// the following tables allows the conversion from 8 bits index Cx...
|
||||||
|
// to the 16 bits actual cluster number
|
||||||
|
enum Cx_cluster_short {
|
||||||
|
Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007,
|
||||||
|
Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F,
|
||||||
|
Cx0010, Cx0011, Cx0012, Cx0013, Cx0014, Cx001A, Cx0020, Cx0100,
|
||||||
|
Cx0101, Cx0102, Cx0201, Cx0300, Cx0400, Cx0401, Cx0402, Cx0403,
|
||||||
|
Cx0404, Cx0405, Cx0406, Cx0500, Cx0702, Cx0B01, Cx0B04, Cx0B05,
|
||||||
|
CxEF00, CxFC01, CxFC40, CxFCC0, CxFCCC,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint16_t Cx_cluster[] PROGMEM = {
|
||||||
|
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
||||||
|
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||||
|
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x001A, 0x0020, 0x0100,
|
||||||
|
0x0101, 0x0102, 0x0201, 0x0300, 0x0400, 0x0401, 0x0402, 0x0403,
|
||||||
|
0x0404, 0x0405, 0x0406, 0x0500, 0x0702, 0x0B01, 0x0B04, 0x0B05,
|
||||||
|
0xEF00, 0xFC01, 0xFC40, 0xFCC0, 0xFCCC,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t CxToCluster(uint8_t cx) {
|
||||||
|
if (cx < nitems(Cx_cluster)) {
|
||||||
|
return pgm_read_word(&Cx_cluster[cx]);
|
||||||
|
}
|
||||||
|
return 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ClusterToCx(uint16_t cluster) {
|
||||||
|
for (uint32_t i=0; i<nitems(Cx_cluster); i++) {
|
||||||
|
if (pgm_read_word(&Cx_cluster[i]) == cluster) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiplier contains only a limited set of values, so instead of storing the value
|
||||||
|
// we store an index in a table, and reduce it to 4 bits
|
||||||
|
enum Cm_multiplier_nibble {
|
||||||
|
Cm0 = 0, Cm1 = 1, Cm2, Cm5, Cm10, Cm100,
|
||||||
|
// negative numbers
|
||||||
|
Cm_2, Cm_5, Cm_10, Cm_100
|
||||||
|
};
|
||||||
|
|
||||||
|
const int8_t Cm_multiplier[] PROGMEM = {
|
||||||
|
0, 1, 2, 5, 10, 100,
|
||||||
|
-2, -5, -10, -100,
|
||||||
|
};
|
||||||
|
|
||||||
|
int8_t CmToMultiplier(uint8_t cm) {
|
||||||
|
cm = cm & 0x0F; // get only low nibble
|
||||||
|
if (cm < nitems(Cm_multiplier)) {
|
||||||
|
return pgm_read_byte(&Cm_multiplier[cm]);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Plug-in mechanism for device specific attributes and commands
|
||||||
|
//
|
||||||
|
|
||||||
|
/* Example:
|
||||||
|
|
||||||
|
# GiEX garden watering https://www.aliexpress.com/item/1005004222098040.html
|
||||||
|
:TS0601,_TZE200_sh1btabb
|
||||||
|
EF00_0101,water_mode
|
||||||
|
EF00_0102,water_state
|
||||||
|
EF00_0365,irrigation_start_time
|
||||||
|
EF00_0366,irrigation_target
|
||||||
|
EF00_0267,cycle_irrigation_num_times
|
||||||
|
EF00_0268,irrigation_target
|
||||||
|
EF00_0269,cycle_irrigation_interval
|
||||||
|
EF00_026A,current_temperature
|
||||||
|
EF00_026C,battery
|
||||||
|
EF00_026F,water_consumed
|
||||||
|
EF00_0372,last_irrigation_duration
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Class for a single attribute from a plugin
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class Z_plugin_attribute {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Z_plugin_attribute(void) :
|
||||||
|
type(Zunk), multiplier(1), cluster(0xFFFF), attribute(0xFFFF)
|
||||||
|
{};
|
||||||
|
|
||||||
|
void set(uint16_t cluster, uint16_t attribute, const char *name, uint8_t type = Zunk) {
|
||||||
|
this->cluster = cluster;
|
||||||
|
this->attribute = attribute;
|
||||||
|
this->name = name;
|
||||||
|
this->type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t type; // zigbee type, Zunk by default
|
||||||
|
int8_t multiplier; // multiplier, values 0, 1, 2, 5, 10, 100, -2, -5, -10, -100,
|
||||||
|
uint16_t cluster; // cluster number
|
||||||
|
uint16_t attribute; // attribute number
|
||||||
|
String name; // name of attribute once converted
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Class for an attribute synonym
|
||||||
|
//
|
||||||
|
class Z_attribute_synonym {
|
||||||
|
public:
|
||||||
|
Z_attribute_synonym(void) :
|
||||||
|
cluster(0xFFFF), attribute(0xFFFF), new_cluster(0xFFFF), new_attribute(0xFFFF),
|
||||||
|
multiplier(1)
|
||||||
|
{};
|
||||||
|
|
||||||
|
void set(uint16_t cluster, uint16_t attribute, uint16_t new_cluster, uint16_t new_attribute, int8_t multiplier = 1) {
|
||||||
|
this->cluster = cluster;
|
||||||
|
this->attribute = attribute;
|
||||||
|
this->new_cluster = new_cluster;
|
||||||
|
this->new_attribute = new_attribute;
|
||||||
|
this->multiplier = multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool found(void) const { return cluster != 0xFFFF && attribute != 0xFFFF; }
|
||||||
|
|
||||||
|
uint16_t cluster; // cluster to match
|
||||||
|
uint16_t attribute; // attribute to match
|
||||||
|
uint16_t new_cluster; // replace with this cluster
|
||||||
|
uint16_t new_attribute; // replace with this attribute
|
||||||
|
int8_t multiplier; // mutliplier if different than 1 (otherwise don't change value)
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// matcher for a device, based on ModelID and Manufacturer
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class Z_plugin_matcher {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Z_plugin_matcher(void) {};
|
||||||
|
|
||||||
|
inline void setModelManuf(const char *_model, const char *_manuf) {
|
||||||
|
model = (const char*)_model;
|
||||||
|
manufacturer = (const char*)_manuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match(const char *match_model, const char *match_manuf) const {
|
||||||
|
bool match = true;
|
||||||
|
if (model.length() > 0) {
|
||||||
|
if (!model.equals(match_model)) {
|
||||||
|
match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (manufacturer.length() > 0) {
|
||||||
|
if (!manufacturer.equals(match_manuf)) {
|
||||||
|
match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
String model;
|
||||||
|
String manufacturer;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Z_plugin_template : template for a group of devices
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class Z_plugin_template {
|
||||||
|
public:
|
||||||
|
|
||||||
|
LList<Z_plugin_matcher> matchers;
|
||||||
|
LList<Z_attribute_synonym> synonyms;
|
||||||
|
LList<Z_plugin_attribute> attributes;
|
||||||
|
char * filename = nullptr; // the filename from which it was imported
|
||||||
|
// needs to be freed by ZbUnload only (to allow reuse between mutliple instances)
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Z_plugin_templates
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class Z_plugin_templates : public LList<Z_plugin_template> {
|
||||||
|
public:
|
||||||
|
// match an attribute from the plug-in in-memory database
|
||||||
|
// returns `nullptr` if none found
|
||||||
|
// or a pointer to `Z_plugin_attribute`
|
||||||
|
const Z_plugin_attribute * matchAttributeById(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute);
|
||||||
|
const Z_plugin_attribute * matchAttributeByName(const char *model, const char *manufacturer, const char *name);
|
||||||
|
const Z_attribute_synonym * matchAttributeSynonym(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Z_attribute_synonym * Z_plugin_templates::matchAttributeSynonym(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute) {
|
||||||
|
// scan through all templates
|
||||||
|
for (const Z_plugin_template & tmpl : *this) {
|
||||||
|
const LList<Z_plugin_matcher> & matchers = tmpl.matchers; // get synonyms
|
||||||
|
const LList<Z_attribute_synonym> & synonyms = tmpl.synonyms; // get synonyms
|
||||||
|
|
||||||
|
for (const Z_plugin_matcher & mtch : matchers) {
|
||||||
|
if (mtch.match(model, manufacturer)) {
|
||||||
|
// got a match, apply template
|
||||||
|
for (const Z_attribute_synonym & syn : synonyms) {
|
||||||
|
if (syn.cluster == cluster && syn.attribute == attribute) {
|
||||||
|
return &syn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no match
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Z_plugin_attribute * Z_plugin_templates::matchAttributeById(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute) {
|
||||||
|
// scan through all templates
|
||||||
|
for (const Z_plugin_template & tmpl : *this) {
|
||||||
|
const LList<Z_plugin_matcher> & matchers = tmpl.matchers; // get synonyms
|
||||||
|
const LList<Z_plugin_attribute> & attributes = tmpl.attributes; // get synonyms
|
||||||
|
|
||||||
|
for (const Z_plugin_matcher & mtch : matchers) {
|
||||||
|
if (mtch.match(model, manufacturer)) {
|
||||||
|
// got a match, apply template
|
||||||
|
for (const Z_plugin_attribute & attr : attributes) {
|
||||||
|
if (attr.cluster == cluster && attr.attribute == attribute) {
|
||||||
|
return &attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no match
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Z_plugin_attribute * Z_plugin_templates::matchAttributeByName(const char *model, const char *manufacturer, const char * name) {
|
||||||
|
// scan through all templates
|
||||||
|
for (const Z_plugin_template & tmpl : *this) {
|
||||||
|
const LList<Z_plugin_matcher> & matchers = tmpl.matchers; // get synonyms
|
||||||
|
const LList<Z_plugin_attribute> & attributes = tmpl.attributes; // get synonyms
|
||||||
|
|
||||||
|
for (const Z_plugin_matcher & mtch : matchers) {
|
||||||
|
if (mtch.match(model, manufacturer)) {
|
||||||
|
// got a match, apply template
|
||||||
|
for (const Z_plugin_attribute & attr : attributes) {
|
||||||
|
if (attr.name.equals(name)) {
|
||||||
|
return &attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no match
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Attribute match result
|
||||||
|
//
|
||||||
|
class Z_attribute_match {
|
||||||
|
public:
|
||||||
|
inline bool found(void) const { return (cluster != 0xFFFF && attribute != 0xFFFF); }
|
||||||
|
|
||||||
|
uint16_t cluster = 0xFFFF;
|
||||||
|
uint16_t attribute = 0xFFFF;
|
||||||
|
const char * name = nullptr;
|
||||||
|
uint8_t zigbee_type = Znodata;
|
||||||
|
int8_t multiplier = 1;
|
||||||
|
uint8_t map_offset = 0;
|
||||||
|
Z_Data_Type map_type = Z_Data_Type::Z_Unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
Z_attribute_match Z_plugin_matchAttributeById(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute);
|
||||||
|
Z_attribute_match Z_plugin_matchAttributeByName(const char *model, const char *manufacturer, const char *name);
|
||||||
|
|
||||||
|
|
||||||
|
// list of post-processing directives
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof" // avoid warnings since we're using offsetof() in a risky way
|
||||||
|
const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
||||||
|
{ Zuint8, Cx0000, 0x0000, Z_(ZCLVersion), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0000, 0x0001, Z_(AppVersion), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0000, 0x0002, Z_(StackVersion), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0000, 0x0003, Z_(HWVersion), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0000, 0x0004, Z_(Manufacturer), Cm1, 0 }, // record Manufacturer
|
||||||
|
{ Zstring, Cx0000, 0x0005, Z_(ModelId), Cm1, 0 }, // record Model
|
||||||
|
// { Zstring, Cx0000, 0x0004, Z_(Manufacturer), Cm1, Z_ManufKeep, 0 }, // record Manufacturer
|
||||||
|
// { Zstring, Cx0000, 0x0005, Z_(ModelId), Cm1, Z_ModelKeep, 0 }, // record Model
|
||||||
|
{ Zstring, Cx0000, 0x0006, Z_(DateCode), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0000, 0x0007, Z_(PowerSource), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0000, 0x0008, Z_(GenericDeviceClass), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0000, 0x0009, Z_(GenericDeviceType), Cm1, 0 },
|
||||||
|
{ Zoctstr, Cx0000, 0x000A, Z_(ProductCode), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0000, 0x000B, Z_(ProductURL), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0000, 0x4000, Z_(SWBuildID), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0000, 0x4005, Z_(MullerLightMode), Cm1, 0 },
|
||||||
|
// { Zunk, Cx0000, 0xFFFF, nullptr, Cm0, 0 }, // Remove all other values
|
||||||
|
// Cmd 0x0A - Cluster 0x0000, attribute 0xFF01 - proprietary
|
||||||
|
{ Zmap8, Cx0000, 0xFF01, Z_(), Cm0, 0 },
|
||||||
|
{ Zmap8, Cx0000, 0xFF02, Z_(), Cm0, 0 },
|
||||||
|
// { Zmap8, Cx0000, 0xFF01, Z_(), Cm0, Z_AqaraSensor, 0 },
|
||||||
|
// { Zmap8, Cx0000, 0xFF02, Z_(), Cm0, Z_AqaraSensor2, 0 },
|
||||||
|
|
||||||
|
// Power Configuration cluster
|
||||||
|
{ Zuint16, Cx0001, 0x0000, Z_(MainsVoltage), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0001, 0x0001, Z_(MainsFrequency), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0001, 0x0020, Z_(BatteryVoltage), Cm_10, 0 }, // divide by 10
|
||||||
|
{ Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), Cm_2, 0 }, // divide by 2
|
||||||
|
// { Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), Cm_2, Z_BatteryPercentage, 0 }, // divide by 2
|
||||||
|
|
||||||
|
// Device Temperature Configuration cluster
|
||||||
|
{ Zint16, Cx0002, 0x0000, Z_(CurrentTemperature), Cm1, 0 },
|
||||||
|
{ Zint16, Cx0002, 0x0001, Z_(MinTempExperienced), Cm1, 0 },
|
||||||
|
{ Zint16, Cx0002, 0x0002, Z_(MaxTempExperienced), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0002, 0x0003, Z_(OverTempTotalDwell), Cm1, 0 },
|
||||||
|
|
||||||
|
// Identify cluster
|
||||||
|
{ Zuint16, Cx0003, 0x0000, Z_(IdentifyTime), Cm1, 0 },
|
||||||
|
|
||||||
|
// Groups cluster
|
||||||
|
{ Zmap8, Cx0004, 0x0000, Z_(GroupNameSupport), Cm1, 0 },
|
||||||
|
|
||||||
|
// Scenes cluster
|
||||||
|
{ Zuint8, Cx0005, 0x0000, Z_(SceneCount), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0005, 0x0001, Z_(CurrentScene), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0005, 0x0002, Z_(CurrentGroup), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0005, 0x0003, Z_(SceneValid), Cm1, 0 },
|
||||||
|
//{ Zmap8, Cx0005, 0x0004, (NameSupport), Cm1, 0 },
|
||||||
|
|
||||||
|
// On/off cluster
|
||||||
|
{ Zbool, Cx0006, 0x0000, Z_(Power), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_OnOff, power) },
|
||||||
|
{ Zenum8, Cx0006, 0x4003, Z_(StartUpOnOff), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0006, 0x8000, Z_(Power), Cm1, 0 }, // See 7280
|
||||||
|
|
||||||
|
// On/Off Switch Configuration cluster
|
||||||
|
{ Zenum8, Cx0007, 0x0000, Z_(SwitchType), Cm1, 0 },
|
||||||
|
|
||||||
|
// Level Control cluster
|
||||||
|
{ Zuint8, Cx0008, 0x0000, Z_(Dimmer), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, dimmer) },
|
||||||
|
{ Zmap8, Cx0008, 0x000F, Z_(DimmerOptions), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0008, 0x0001, Z_(DimmerRemainingTime), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0008, 0x0010, Z_(OnOffTransitionTime), Cm1, 0 },
|
||||||
|
// { Zuint8, Cx0008, 0x0011, (OnLevel), Cm1, 0 },
|
||||||
|
// { Zuint16, Cx0008, 0x0012, (OnTransitionTime), Cm1, 0 },
|
||||||
|
// { Zuint16, Cx0008, 0x0013, (OffTransitionTime), Cm1, 0 },
|
||||||
|
// { Zuint16, Cx0008, 0x0014, (DefaultMoveRate), Cm1, 0 },
|
||||||
|
|
||||||
|
// Alarms cluster
|
||||||
|
{ Zuint16, Cx0009, 0x0000, Z_(AlarmCount), Cm1, 0 },
|
||||||
|
|
||||||
|
// Time cluster
|
||||||
|
{ ZUTC, Cx000A, 0x0000, Z_(Time), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx000A, 0x0001, Z_(TimeStatus), Cm1, 0 },
|
||||||
|
{ Zint32, Cx000A, 0x0002, Z_(TimeZone), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000A, 0x0003, Z_(DstStart), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000A, 0x0004, Z_(DstEnd), Cm1, 0 },
|
||||||
|
{ Zint32, Cx000A, 0x0005, Z_(DstShift), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000A, 0x0006, Z_(StandardTime), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000A, 0x0007, Z_(LocalTime), Cm1, 0 },
|
||||||
|
{ ZUTC, Cx000A, 0x0008, Z_(LastSetTime), Cm1, 0 },
|
||||||
|
{ ZUTC, Cx000A, 0x0009, Z_(ValidUntilTime), Cm1, 0 },
|
||||||
|
{ ZUTC, Cx000A, 0xFF00, Z_(TimeEpoch), Cm1, 0 }, // Tasmota specific, epoch
|
||||||
|
|
||||||
|
// RSSI Location cluster
|
||||||
|
{ Zdata8, Cx000B, 0x0000, Z_(LocationType), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx000B, 0x0001, Z_(LocationMethod), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx000B, 0x0002, Z_(LocationAge), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx000B, 0x0003, Z_(QualityMeasure), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx000B, 0x0004, Z_(NumberOfDevices), Cm1, 0 },
|
||||||
|
|
||||||
|
// Analog Input cluster
|
||||||
|
// { 0xFF, Cx000C, 0x0004, (AnalogInActiveText), Cm1, 0 },
|
||||||
|
{ Zstring, Cx000C, 0x001C, Z_(AnalogInDescription), Cm1, 0 },
|
||||||
|
// { 0xFF, Cx000C, 0x002E, (AnalogInInactiveText), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000C, 0x0041, Z_(AnalogInMaxValue), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000C, 0x0045, Z_(AnalogInMinValue), Cm1, 0 },
|
||||||
|
{ Zbool, Cx000C, 0x0051, Z_(AnalogInOutOfService), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000C, 0x0055, Z_(AnalogValue), Cm1, 0 },
|
||||||
|
// { 0xFF, Cx000C, 0x0057, (AnalogInPriorityArray),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx000C, 0x0067, Z_(AnalogInReliability), Cm1, 0 },
|
||||||
|
// { 0xFF, Cx000C, 0x0068, (AnalogInRelinquishDefault),Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000C, 0x006A, Z_(AnalogInResolution), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx000C, 0x006F, Z_(AnalogInStatusFlags), Cm1, 0 },
|
||||||
|
{ Zenum16, Cx000C, 0x0075, Z_(AnalogInEngineeringUnits),Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000C, 0x0100, Z_(AnalogInApplicationType),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx000C, 0xFF55, Z_(AqaraRotate), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx000C, 0xFF05, Z_(Aqara_FF05), Cm1, 0 },
|
||||||
|
|
||||||
|
// Analog Output cluster
|
||||||
|
{ Zstring, Cx000D, 0x001C, Z_(AnalogOutDescription), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000D, 0x0041, Z_(AnalogOutMaxValue), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000D, 0x0045, Z_(AnalogOutMinValue), Cm1, 0 },
|
||||||
|
{ Zbool, Cx000D, 0x0051, Z_(AnalogOutOutOfService),Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000D, 0x0055, Z_(AnalogOutValue), Cm1, 0 },
|
||||||
|
// { Zunk, Cx000D, 0x0057, (AnalogOutPriorityArray),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx000D, 0x0067, Z_(AnalogOutReliability), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000D, 0x0068, Z_(AnalogOutRelinquishDefault), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000D, 0x006A, Z_(AnalogOutResolution), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx000D, 0x006F, Z_(AnalogOutStatusFlags), Cm1, 0 },
|
||||||
|
{ Zenum16, Cx000D, 0x0075, Z_(AnalogOutEngineeringUnits), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000D, 0x0100, Z_(AnalogOutApplicationType), Cm1, 0 },
|
||||||
|
|
||||||
|
// Analog Value cluster
|
||||||
|
{ Zstring, Cx000E, 0x001C, Z_(AnalogDescription), Cm1, 0 },
|
||||||
|
{ Zbool, Cx000E, 0x0051, Z_(AnalogOutOfService), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000E, 0x0055, Z_(AnalogValue), Cm1, 0 },
|
||||||
|
{ Zunk, Cx000E, 0x0057, Z_(AnalogPriorityArray), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx000E, 0x0067, Z_(AnalogReliability), Cm1, 0 },
|
||||||
|
{ Zsingle, Cx000E, 0x0068, Z_(AnalogRelinquishDefault),Cm1, 0 },
|
||||||
|
{ Zmap8, Cx000E, 0x006F, Z_(AnalogStatusFlags), Cm1, 0 },
|
||||||
|
{ Zenum16, Cx000E, 0x0075, Z_(AnalogEngineeringUnits),Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000E, 0x0100, Z_(AnalogApplicationType),Cm1, 0 },
|
||||||
|
|
||||||
|
// Binary Input cluster
|
||||||
|
{ Zstring, Cx000F, 0x0004, Z_(BinaryInActiveText), Cm1, 0 },
|
||||||
|
{ Zstring, Cx000F, 0x001C, Z_(BinaryInDescription), Cm1, 0 },
|
||||||
|
{ Zstring, Cx000F, 0x002E, Z_(BinaryInInactiveText),Cm1, 0 },
|
||||||
|
{ Zbool, Cx000F, 0x0051, Z_(BinaryInOutOfService),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx000F, 0x0054, Z_(BinaryInPolarity), Cm1, 0 },
|
||||||
|
{ Zstring, Cx000F, 0x0055, Z_(BinaryInValue), Cm1, 0 },
|
||||||
|
// { 0xFF, Cx000F, 0x0057, (BinaryInPriorityArray),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx000F, 0x0067, Z_(BinaryInReliability), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx000F, 0x006F, Z_(BinaryInStatusFlags), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx000F, 0x0100, Z_(BinaryInApplicationType),Cm1, 0 },
|
||||||
|
|
||||||
|
// Binary Output cluster
|
||||||
|
{ Zstring, Cx0010, 0x0004, Z_(BinaryOutActiveText), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0010, 0x001C, Z_(BinaryOutDescription), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0010, 0x002E, Z_(BinaryOutInactiveText),Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0010, 0x0042, Z_(BinaryOutMinimumOffTime),Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0010, 0x0043, Z_(BinaryOutMinimumOnTime),Cm1, 0 },
|
||||||
|
{ Zbool, Cx0010, 0x0051, Z_(BinaryOutOutOfService),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0010, 0x0054, Z_(BinaryOutPolarity), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0010, 0x0055, Z_(BinaryOutValue), Cm1, 0 },
|
||||||
|
// { Zunk, Cx0010, 0x0057, (BinaryOutPriorityArray),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0010, 0x0067, Z_(BinaryOutReliability), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0010, 0x0068, Z_(BinaryOutRelinquishDefault),Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0010, 0x006F, Z_(BinaryOutStatusFlags), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0010, 0x0100, Z_(BinaryOutApplicationType),Cm1, 0 },
|
||||||
|
|
||||||
|
// Binary Value cluster
|
||||||
|
{ Zstring, Cx0011, 0x0004, Z_(BinaryActiveText), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0011, 0x001C, Z_(BinaryDescription), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0011, 0x002E, Z_(BinaryInactiveText), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0011, 0x0042, Z_(BinaryMinimumOffTime), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0011, 0x0043, Z_(BinaryMinimumOnTime), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0011, 0x0051, Z_(BinaryOutOfService), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0011, 0x0055, Z_(BinaryValue), Cm1, 0 },
|
||||||
|
// { Zunk, Cx0011, 0x0057, (BinaryPriorityArray), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0011, 0x0067, Z_(BinaryReliability), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0011, 0x0068, Z_(BinaryRelinquishDefault),Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0011, 0x006F, Z_(BinaryStatusFlags), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0011, 0x0100, Z_(BinaryApplicationType),Cm1, 0 },
|
||||||
|
|
||||||
|
// Multistate Input cluster
|
||||||
|
// { Zunk, Cx0012, 0x000E, (MultiInStateText), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0012, 0x001C, Z_(MultiInDescription), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0012, 0x004A, Z_(MultiInNumberOfStates),Cm1, 0 },
|
||||||
|
{ Zbool, Cx0012, 0x0051, Z_(MultiInOutOfService), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0012, 0x0055, Z_(MultiInValue), Cm1, 0 },
|
||||||
|
// { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), Cm0, Z_AqaraCube, 0 },
|
||||||
|
// { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), Cm0, Z_AqaraButton, 0 },
|
||||||
|
{ Zenum8, Cx0012, 0x0067, Z_(MultiInReliability), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0012, 0x006F, Z_(MultiInStatusFlags), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0012, 0x0100, Z_(MultiInApplicationType),Cm1, 0 },
|
||||||
|
|
||||||
|
// Multistate output
|
||||||
|
// { Zunk, Cx0013, 0x000E, (MultiOutStateText), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0013, 0x001C, Z_(MultiOutDescription), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0013, 0x004A, Z_(MultiOutNumberOfStates),Cm1, 0 },
|
||||||
|
{ Zbool, Cx0013, 0x0051, Z_(MultiOutOutOfService), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0013, 0x0055, Z_(MultiOutValue), Cm1, 0 },
|
||||||
|
// { Zunk, Cx0013, 0x0057, (MultiOutPriorityArray),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0013, 0x0067, Z_(MultiOutReliability), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0013, 0x0068, Z_(MultiOutRelinquishDefault),Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0013, 0x006F, Z_(MultiOutStatusFlags), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0013, 0x0100, Z_(MultiOutApplicationType),Cm1, 0 },
|
||||||
|
|
||||||
|
// Multistate Value cluster
|
||||||
|
// { Zunk, Cx0014, 0x000E, (MultiStateText), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0014, 0x001C, Z_(MultiDescription), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0014, 0x004A, Z_(MultiNumberOfStates), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0014, 0x0051, Z_(MultiOutOfService), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0014, 0x0055, Z_(MultiValue), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0014, 0x0067, Z_(MultiReliability), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0014, 0x0068, Z_(MultiRelinquishDefault),Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0014, 0x006F, Z_(MultiStatusFlags), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0014, 0x0100, Z_(MultiApplicationType), Cm1, 0 },
|
||||||
|
|
||||||
|
// Power Profile cluster
|
||||||
|
{ Zuint8, Cx001A, 0x0000, Z_(TotalProfileNum), Cm1, 0 },
|
||||||
|
{ Zbool, Cx001A, 0x0001, Z_(MultipleScheduling), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx001A, 0x0002, Z_(EnergyFormatting), Cm1, 0 },
|
||||||
|
{ Zbool, Cx001A, 0x0003, Z_(EnergyRemote), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx001A, 0x0004, Z_(ScheduleMode), Cm1, 0 },
|
||||||
|
|
||||||
|
// Poll Control cluster
|
||||||
|
{ Zuint32, Cx0020, 0x0000, Z_(CheckinInterval), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0020, 0x0001, Z_(LongPollInterval), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0020, 0x0002, Z_(ShortPollInterval), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0020, 0x0003, Z_(FastPollTimeout), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0020, 0x0004, Z_(CheckinIntervalMin), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0020, 0x0005, Z_(LongPollIntervalMin), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0020, 0x0006, Z_(FastPollTimeoutMax), Cm1, 0 },
|
||||||
|
|
||||||
|
// Shade Configuration cluster
|
||||||
|
{ Zuint16, Cx0100, 0x0000, Z_(PhysicalClosedLimit), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0100, 0x0001, Z_(MotorStepSize), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0100, 0x0002, Z_(Status), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0100, 0x0010, Z_(ClosedLimit), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0100, 0x0011, Z_(Mode), Cm1, 0 },
|
||||||
|
|
||||||
|
// Door Lock cluster
|
||||||
|
{ Zenum8, Cx0101, 0x0000, Z_(LockState), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0101, 0x0001, Z_(LockType), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0101, 0x0002, Z_(ActuatorEnabled), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0101, 0x0003, Z_(DoorState), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0101, 0x0004, Z_(DoorOpenEvents), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0101, 0x0005, Z_(DoorClosedEvents), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0101, 0x0006, Z_(OpenPeriod), Cm1, 0 },
|
||||||
|
|
||||||
|
// Aqara Lumi Vibration Sensor
|
||||||
|
{ Zuint16, Cx0101, 0x0055, Z_(AqaraVibrationMode), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0101, 0x0503, Z_(AqaraVibrationsOrAngle), Cm1, 0 },
|
||||||
|
{ Zuint32, Cx0101, 0x0505, Z_(AqaraVibration505), Cm1, 0 },
|
||||||
|
{ Zuint48, Cx0101, 0x0508, Z_(AqaraAccelerometer), Cm1, 0 },
|
||||||
|
|
||||||
|
// Window Covering cluster
|
||||||
|
{ Zenum8, Cx0102, 0x0000, Z_(WindowCoveringType), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0001, Z_(PhysicalClosedLimitLift),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0002, Z_(PhysicalClosedLimitTilt),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0003, Z_(CurrentPositionLift), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0004, Z_(CurrentPositionTilt), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0005, Z_(NumberofActuationsLift),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0006, Z_(NumberofActuationsTilt),Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0102, 0x0007, Z_(ConfigStatus), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0102, 0x0008, Z_(CurrentPositionLiftPercentage),Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0102, 0x0009, Z_(CurrentPositionTiltPercentage),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0010, Z_(InstalledOpenLimitLift),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0011, Z_(InstalledClosedLimitLift),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0012, Z_(InstalledOpenLimitTilt),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0013, Z_(InstalledClosedLimitTilt),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0014, Z_(VelocityLift), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0015, Z_(AccelerationTimeLift),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0x0016, Z_(DecelerationTimeLift), Cm1, 0 },
|
||||||
|
{ Zmap8, Cx0102, 0x0017, Z_(Mode), Cm1, 0 },
|
||||||
|
{ Zoctstr, Cx0102, 0x0018, Z_(IntermediateSetpointsLift),Cm1, 0 },
|
||||||
|
{ Zoctstr, Cx0102, 0x0019, Z_(IntermediateSetpointsTilt),Cm1, 0 },
|
||||||
|
// Tuya specific, from Zigbee2MQTT https://github.com/Koenkk/zigbee-herdsman/blob/4fed7310d1e1e9d81e5148cf5b4d8ec98d03c687/src/zcl/definition/cluster.ts#L1772
|
||||||
|
{ Zenum8, Cx0102, 0xF000, Z_(TuyaMovingState),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0102, 0xF001, Z_(TuyaCalibration),Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0102, 0xF002, Z_(TuyaMotorReversal),Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0102, 0xF003, Z_(TuyaCalibrationTime),Cm1, 0 },
|
||||||
|
|
||||||
|
// Thermostat
|
||||||
|
{ Zint16, Cx0201, 0x0000, Z_(LocalTemperature), Cm_100, Z_MAPPING(Z_Data_Thermo, temperature) },
|
||||||
|
{ Zint16, Cx0201, 0x0001, Z_(OutdoorTemperature),Cm_100, 0 },
|
||||||
|
{ Zuint8, Cx0201, 0x0007, Z_(PICoolingDemand), Cm1, Z_MAPPING(Z_Data_Thermo, th_setpoint) },
|
||||||
|
{ Zuint8, Cx0201, 0x0008, Z_(PIHeatingDemand), Cm1, Z_MAPPING(Z_Data_Thermo, th_setpoint) },
|
||||||
|
{ Zint8, Cx0201, 0x0010, Z_(LocalTemperatureCalibration), Cm_10, 0 },
|
||||||
|
{ Zint16, Cx0201, 0x0011, Z_(OccupiedCoolingSetpoint), Cm_100, Z_MAPPING(Z_Data_Thermo, temperature_target) },
|
||||||
|
{ Zint16, Cx0201, 0x0012, Z_(OccupiedHeatingSetpoint), Cm_100, Z_MAPPING(Z_Data_Thermo, temperature_target) },
|
||||||
|
{ Zint16, Cx0201, 0x0013, Z_(UnoccupiedCoolingSetpoint), Cm_100, 0 },
|
||||||
|
{ Zint16, Cx0201, 0x0014, Z_(UnoccupiedHeatingSetpoint), Cm_100, 0 },
|
||||||
|
{ Zmap8, Cx0201, 0x001A, Z_(RemoteSensing), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0201, 0x001B, Z_(ControlSequenceOfOperation), Cm1, 0 },
|
||||||
|
{ Zenum8, Cx0201, 0x001C, Z_(SystemMode), Cm1, 0 },
|
||||||
|
// below is Eurotronic specific
|
||||||
|
{ Zenum8, Cx0201, 0x4000, Z_(TRVMode), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0201, 0x4001, Z_(ValvePosition), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0201, 0x4002, Z_(EurotronicErrors), Cm1, 0 },
|
||||||
|
{ Zint16, Cx0201, 0x4003, Z_(CurrentTemperatureSetPoint), Cm_100, 0 },
|
||||||
|
{ Zuint24, Cx0201, 0x4008, Z_(EurotronicHostFlags), Cm1, 0 },
|
||||||
|
// below are synthetic virtual attributes used to decode EurotronicHostFlags
|
||||||
|
// Last byte acts as a field mask for the lowest byte value
|
||||||
|
{ Zbool, Cx0201, 0xF002, Z_(TRVMirrorDisplay), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0201, 0xF004, Z_(TRVBoost), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0201, 0xF010, Z_(TRVWindowOpen), Cm1, 0 },
|
||||||
|
{ Zbool, Cx0201, 0xF080, Z_(TRVChildProtection), Cm1, 0 },
|
||||||
|
// below are virtual attributes to simplify ZbData import/export
|
||||||
|
{ Zuint8, Cx0201, 0xFFF0, Z_(ThSetpoint), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Thermo, th_setpoint) },
|
||||||
|
{ Zint16, Cx0201, 0xFFF1, Z_(TempTarget), Cm_100 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Thermo, temperature_target) },
|
||||||
|
|
||||||
|
// Color Control cluster
|
||||||
|
{ Zuint8, Cx0300, 0x0000, Z_(Hue), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, hue) },
|
||||||
|
{ Zuint8, Cx0300, 0x0001, Z_(Sat), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, sat) },
|
||||||
|
{ Zuint16, Cx0300, 0x0002, Z_(RemainingTime), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0003, Z_(X), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, x) },
|
||||||
|
{ Zuint16, Cx0300, 0x0004, Z_(Y), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, y) },
|
||||||
|
{ Zenum8, Cx0300, 0x0005, Z_(DriftCompensation), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0300, 0x0006, Z_(CompensationText), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0007, Z_(CT), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, ct) },
|
||||||
|
{ Zenum8, Cx0300, 0x0008, Z_(ColorMode), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, colormode) },
|
||||||
|
{ Zuint8, Cx0300, 0x0010, Z_(NumberOfPrimaries), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0011, Z_(Primary1X), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0012, Z_(Primary1Y), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0300, 0x0013, Z_(Primary1Intensity), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0015, Z_(Primary2X), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0016, Z_(Primary2Y), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0300, 0x0017, Z_(Primary2Intensity), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0019, Z_(Primary3X), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x001A, Z_(Primary3Y), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0300, 0x001B, Z_(Primary3Intensity), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0030, Z_(WhitePointX), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0031, Z_(WhitePointY), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0032, Z_(ColorPointRX), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0033, Z_(ColorPointRY), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0300, 0x0034, Z_(ColorPointRIntensity), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0036, Z_(ColorPointGX), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x0037, Z_(ColorPointGY), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0300, 0x0038, Z_(ColorPointGIntensity), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x003A, Z_(ColorPointBX), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0300, 0x003B, Z_(ColorPointBY), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0300, 0x003C, Z_(ColorPointBIntensity), Cm1, 0 },
|
||||||
|
|
||||||
|
// Illuminance Measurement cluster
|
||||||
|
{ Zuint16, Cx0400, 0x0000, Z_(Illuminance), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_PIR, illuminance) }, // Illuminance (in Lux)
|
||||||
|
{ Zuint16, Cx0400, 0x0001, Z_(IlluminanceMinMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0400, 0x0002, Z_(IlluminanceMaxMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0400, 0x0003, Z_(IlluminanceTolerance), Cm1, 0 }, //
|
||||||
|
{ Zenum8, Cx0400, 0x0004, Z_(IlluminanceLightSensorType), Cm1, 0 }, //
|
||||||
|
{ Zunk, Cx0400, 0xFFFF, Z_(), Cm0, 0 }, // Remove all other values
|
||||||
|
|
||||||
|
// Illuminance Level Sensing cluster
|
||||||
|
{ Zenum8, Cx0401, 0x0000, Z_(IlluminanceLevelStatus), Cm1, 0 }, // Illuminance (in Lux)
|
||||||
|
{ Zenum8, Cx0401, 0x0001, Z_(IlluminanceLightSensorType), Cm1, 0 }, // LightSensorType
|
||||||
|
{ Zuint16, Cx0401, 0x0010, Z_(IlluminanceTargetLevel), Cm1, 0 }, //
|
||||||
|
{ Zunk, Cx0401, 0xFFFF, Z_(), Cm0, 0 }, // Remove all other values
|
||||||
|
|
||||||
|
// Temperature Measurement cluster
|
||||||
|
{ Zint16, Cx0402, 0x0000, Z_(Temperature), Cm_100 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Thermo, temperature) },
|
||||||
|
{ Zint16, Cx0402, 0x0001, Z_(TemperatureMinMeasuredValue), Cm_100, 0 }, //
|
||||||
|
{ Zint16, Cx0402, 0x0002, Z_(TemperatureMaxMeasuredValue), Cm_100, 0 }, //
|
||||||
|
{ Zuint16, Cx0402, 0x0003, Z_(TemperatureTolerance), Cm_100, 0 }, //
|
||||||
|
{ Zunk, Cx0402, 0xFFFF, Z_(), Cm0, 0 }, // Remove all other values
|
||||||
|
|
||||||
|
// Pressure Measurement cluster
|
||||||
|
{ Zint16, Cx0403, 0x0000, Z_(Pressure), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Thermo, pressure) }, // Pressure
|
||||||
|
{ Zint16, Cx0403, 0x0001, Z_(PressureMinMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zint16, Cx0403, 0x0002, Z_(PressureMaxMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0403, 0x0003, Z_(PressureTolerance), Cm1, 0 }, //
|
||||||
|
{ Zint16, Cx0403, 0x0010, Z_(PressureScaledValue), Cm1, 0 }, //
|
||||||
|
{ Zint16, Cx0403, 0x0011, Z_(PressureMinScaledValue), Cm1, 0 }, //
|
||||||
|
{ Zint16, Cx0403, 0x0012, Z_(PressureMaxScaledValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0403, 0x0013, Z_(PressureScaledTolerance), Cm1, 0 }, //
|
||||||
|
{ Zint8, Cx0403, 0x0014, Z_(PressureScale), Cm1, 0 }, //
|
||||||
|
{ Zint16, Cx0403, 0xFFF0, Z_(SeaPressure), Cm1, Z_MAPPING(Z_Data_Thermo, pressure) }, // Pressure at Sea Level, Tasmota specific
|
||||||
|
{ Zunk, Cx0403, 0xFFFF, Z_(), Cm0, 0 }, // Remove all other Pressure values
|
||||||
|
|
||||||
|
// Flow Measurement cluster
|
||||||
|
{ Zuint16, Cx0404, 0x0000, Z_(FlowRate), Cm_10, 0 }, // Flow (in m3/h)
|
||||||
|
{ Zuint16, Cx0404, 0x0001, Z_(FlowMinMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0404, 0x0002, Z_(FlowMaxMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0404, 0x0003, Z_(FlowTolerance), Cm1, 0 }, //
|
||||||
|
{ Zunk, Cx0404, 0xFFFF, Z_(), Cm0, 0 }, // Remove all other values
|
||||||
|
|
||||||
|
// Relative Humidity Measurement cluster
|
||||||
|
{ Zuint16, Cx0405, 0x0000, Z_(Humidity), Cm_100 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Thermo, humidity) }, // Humidity
|
||||||
|
{ Zuint16, Cx0405, 0x0001, Z_(HumidityMinMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0405, 0x0002, Z_(HumidityMaxMeasuredValue), Cm1, 0 }, //
|
||||||
|
{ Zuint16, Cx0405, 0x0003, Z_(HumidityTolerance), Cm1, 0 }, //
|
||||||
|
{ Zunk, Cx0405, 0xFFFF, Z_(), Cm0, 0 }, // Remove all other values
|
||||||
|
|
||||||
|
// Occupancy Sensing cluster
|
||||||
|
{ Zmap8, Cx0406, 0x0000, Z_(Occupancy), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_PIR, occupancy) }, // Occupancy (map8)
|
||||||
|
{ Zenum8, Cx0406, 0x0001, Z_(OccupancySensorType), Cm1, 0 }, // OccupancySensorType
|
||||||
|
{ Zunk, Cx0406, 0xFFFF, Z_(), Cm0, 0 }, // Remove all other values
|
||||||
|
|
||||||
|
// IAS Cluster (Intruder Alarm System)
|
||||||
|
{ Zenum8, Cx0500, 0x0000, Z_(ZoneState), Cm1, 0 }, // Occupancy (map8)
|
||||||
|
{ Zenum16, Cx0500, 0x0001, Z_(ZoneType), Cm1 + Z_EXPORT_DATA, 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_status) }, // Zone status for sensor
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_CIE, Z_(CIE), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_PIR, Z_(Occupancy), Cm1, 0 }, // normally converted to the actual Occupancy 0406/0000
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_Contact, Z_(Contact), Cm1, Z_MAPPING(Z_Data_Alarm, zone_status) }, // We fit the first bit in the LSB
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_Fire, Z_(Fire), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_Water, Z_(Water), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_CO, Z_(CO), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_Personal, Z_(PersonalAlarm),Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_Movement, Z_(Movement), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_Panic, Z_(Panic), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0500, 0xFFF0 + ZA_GlassBreak, Z_(GlassBreak),Cm1, 0 },
|
||||||
|
|
||||||
|
// Metering (Smart Energy) cluster
|
||||||
|
{ Zuint48, Cx0702, 0x0000, Z_(EnergyTotal), Cm1, 0 },
|
||||||
|
|
||||||
|
// Meter Identification cluster
|
||||||
|
{ Zstring, Cx0B01, 0x0000, Z_(CompanyName), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0B01, 0x0001, Z_(MeterTypeID), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0B01, 0x0004, Z_(DataQualityID), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0B01, 0x0005, Z_(CustomerName), Cm1, 0 },
|
||||||
|
{ Zoctstr, Cx0B01, 0x0006, Z_(Model), Cm1, 0 },
|
||||||
|
{ Zoctstr, Cx0B01, 0x0007, Z_(PartNumber), Cm1, 0 },
|
||||||
|
{ Zoctstr, Cx0B01, 0x0008, Z_(ProductRevision), Cm1, 0 },
|
||||||
|
{ Zoctstr, Cx0B01, 0x000A, Z_(SoftwareRevision), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0B01, 0x000B, Z_(UtilityName), Cm1, 0 },
|
||||||
|
{ Zstring, Cx0B01, 0x000C, Z_(POD), Cm1, 0 },
|
||||||
|
{ Zint24, Cx0B01, 0x000D, Z_(AvailablePower), Cm1, 0 },
|
||||||
|
{ Zint24, Cx0B01, 0x000E, Z_(PowerThreshold), Cm1, 0 },
|
||||||
|
|
||||||
|
// Electrical Measurement cluster
|
||||||
|
{ Zuint16, Cx0B04, 0x0505, Z_(RMSVoltage), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Plug, mains_voltage) },
|
||||||
|
{ Zuint16, Cx0B04, 0x0508, Z_(RMSCurrent), Cm1, 0 },
|
||||||
|
{ Zint16, Cx0B04, 0x050B, Z_(ActivePower), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Plug, mains_power) },
|
||||||
|
{ Zint16, Cx0B04, 0x050E, Z_(ReactivePower), Cm1, 0 },
|
||||||
|
{ Zint16, Cx0B04, 0x050F, Z_(ApparentPower), Cm1, 0 },
|
||||||
|
|
||||||
|
// Diagnostics cluster
|
||||||
|
{ Zuint16, Cx0B05, 0x0000, Z_(NumberOfResets), Cm1, 0 },
|
||||||
|
{ Zuint16, Cx0B05, 0x0001, Z_(PersistentMemoryWrites),Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0B05, 0x011C, Z_(LastMessageLQI), Cm1, 0 },
|
||||||
|
{ Zuint8, Cx0B05, 0x011D, Z_(LastMessageRSSI), Cm1, 0 },
|
||||||
|
|
||||||
|
// Tuya Moes specific - 0xEF00
|
||||||
|
// Mapping of Tuya type with internal mapping
|
||||||
|
// 0x00 - Zoctstr (len N)
|
||||||
|
// 0x01 - Ztuya1 (len 1) - equivalent to Zuint8 without invalid value handling
|
||||||
|
// 0x02 - Ztuya4 (len 4) - equivalent to Zint32 in big endian and without invalid value handling
|
||||||
|
// 0x03 - Zstr (len N)
|
||||||
|
// 0x04 - Ztuya1 (len 1)
|
||||||
|
// 0x05 - Ztuya4u (len 1/2/4) - equivalent to Zuint32
|
||||||
|
// { Ztuya0, CxEF00, 0x0070, Z_(TuyaScheduleWorkdays), Cm1, 0 },
|
||||||
|
// { Ztuya0, CxEF00, 0x0071, Z_(TuyaScheduleHolidays), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0101, Z_(Power), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0102, Z_(Power2), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0103, Z_(Power3), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0104, Z_(Power4), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0107, Z_(TuyaChildLock), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0112, Z_(TuyaWindowDetection), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0114, Z_(TuyaValveDetection), Cm1, 0 },
|
||||||
|
// { Ztuya1, CxEF00, 0x0174, Z_(TuyaAutoLock), Cm1, 0 },
|
||||||
|
// { Zint16, CxEF00, 0x0202, Z_(TuyaTempTarget), Cm_10, Z_MAPPING(Z_Data_Thermo, temperature_target) },
|
||||||
|
// { Zint16, CxEF00, 0x0203, Z_(LocalTemperature), Cm_10, Z_MAPPING(Z_Data_Thermo, temperature) }, // will be overwritten by actual LocalTemperature
|
||||||
|
// { Zuint8, CxEF00, 0x0203, Z_(Dimmer), Cm1, Z_MAPPING(Z_Data_Light, dimmer) }, // will be overwritten by actual LocalTemperature
|
||||||
|
// { Zmap8, CxEF00, 0x0203, Z_(Occupancy), Cm1, Z_MAPPING(Z_Data_PIR, occupancy) }, // will be overwritten by actual LocalTemperature
|
||||||
|
// { Ztuya2, CxEF00, 0x0215, Z_(TuyaBattery), Cm1, 0 }, // TODO check equivalent?
|
||||||
|
// { Ztuya2, CxEF00, 0x0266, Z_(TuyaMinTemp), Cm1, 0 },
|
||||||
|
// { Ztuya2, CxEF00, 0x0267, Z_(TuyaMaxTemp), Cm1, 0 },
|
||||||
|
// { Ztuya2, CxEF00, 0x0269, Z_(TuyaBoostTime), Cm1, 0 },
|
||||||
|
// { Ztuya2, CxEF00, 0x026B, Z_(TuyaComfortTemp), Cm1, 0 },
|
||||||
|
// { Ztuya2, CxEF00, 0x026C, Z_(TuyaEcoTemp), Cm1, 0 },
|
||||||
|
// { Zuint8, CxEF00, 0x026D, Z_(TuyaValvePosition), Cm1, Z_MAPPING(Z_Data_Thermo, th_setpoint) },
|
||||||
|
// { Ztuya2, CxEF00, 0x0272, Z_(TuyaAwayTemp), Cm1, 0 },
|
||||||
|
// { Ztuya2, CxEF00, 0x0275, Z_(TuyaAwayDays), Cm1, 0 },
|
||||||
|
// { Ztuya4, CxEF00, 0x0404, Z_(TuyaPreset), Cm1, 0 },
|
||||||
|
// { Ztuya4, CxEF00, 0x0405, Z_(TuyaFanMode), Cm1, 0 },
|
||||||
|
// { Ztuya4, CxEF00, 0x046A, Z_(TuyaForceMode), Cm1, 0 },
|
||||||
|
// { Ztuya4, CxEF00, 0x046F, Z_(TuyaWeekSelect), Cm1, 0 },
|
||||||
|
|
||||||
|
// Legrand BTicino - Manuf code 0x1021
|
||||||
|
{ Zdata16, CxFC01, 0x0000, Z_(LegrandOpt1), Cm1, 0 },
|
||||||
|
{ Zbool, CxFC01, 0x0001, Z_(LegrandOpt2), Cm1, 0 },
|
||||||
|
{ Zbool, CxFC01, 0x0002, Z_(LegrandOpt3), Cm1, 0 },
|
||||||
|
|
||||||
|
// Legrand - Manuf code 0x1021
|
||||||
|
{ Zenum8, CxFC40, 0x0000, Z_(LegrandHeatingMode), Cm1, 0 },
|
||||||
|
|
||||||
|
// Aqara Opple spacific
|
||||||
|
{ Zuint8, CxFCC0, 0x0009, Z_(OppleMode), Cm1, 0 },
|
||||||
|
|
||||||
|
// Terncy specific - 0xFCCC
|
||||||
|
{ Zuint16, CxFCCC, 0x001A, Z_(TerncyDuration), Cm1, 0 },
|
||||||
|
{ Zint16, CxFCCC, 0x001B, Z_(TerncyRotate), Cm1, 0 },
|
||||||
|
};
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#endif // USE_ZIGBEE
|
File diff suppressed because it is too large
Load Diff
|
@ -212,7 +212,6 @@ void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin
|
||||||
zigbee_devices.getShortAddr(shortaddr).setReachable(false); // mark device as reachable
|
zigbee_devices.getShortAddr(shortaddr).setReachable(false); // mark device as reachable
|
||||||
Z_attribute_list attr_list;
|
Z_attribute_list attr_list;
|
||||||
attr_list.addAttributePMEM(PSTR("Reachable")).setBool(false); // "Reachable":false
|
attr_list.addAttributePMEM(PSTR("Reachable")).setBool(false); // "Reachable":false
|
||||||
// Z_postProcessAttributes(shortaddr, endpoint, attr_list); // make sure all is updated accordingly
|
|
||||||
zigbee_devices.jsonPublishNow(shortaddr, attr_list);
|
zigbee_devices.jsonPublishNow(shortaddr, attr_list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -529,6 +529,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||||
ZI_CALL(&Z_Load_Devices, 0)
|
ZI_CALL(&Z_Load_Devices, 0)
|
||||||
ZI_CALL(&Z_Load_Data, 0)
|
ZI_CALL(&Z_Load_Data, 0)
|
||||||
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
|
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
|
||||||
|
ZI_CALL(&Z_ZbAutoload, 0)
|
||||||
ZI_CALL(&Z_Query_Bulbs, 0)
|
ZI_CALL(&Z_Query_Bulbs, 0)
|
||||||
|
|
||||||
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
||||||
|
@ -966,6 +967,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||||
ZI_CALL(&Z_Load_Devices, 0)
|
ZI_CALL(&Z_Load_Devices, 0)
|
||||||
ZI_CALL(&Z_Load_Data, 0)
|
ZI_CALL(&Z_Load_Data, 0)
|
||||||
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
|
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
|
||||||
|
ZI_CALL(&Z_ZbAutoload, 0)
|
||||||
ZI_CALL(&Z_Query_Bulbs, 0)
|
ZI_CALL(&Z_Query_Bulbs, 0)
|
||||||
|
|
||||||
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
||||||
|
|
|
@ -0,0 +1,379 @@
|
||||||
|
/*
|
||||||
|
xdrv_23_zigbee.ino - zigbee support for Tasmota
|
||||||
|
|
||||||
|
Copyright (C) 2021 Theo Arends and Stephan Hadinger
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef USE_ZIGBEE
|
||||||
|
|
||||||
|
// global singleton
|
||||||
|
Z_plugin_templates g_plugin_templates;
|
||||||
|
|
||||||
|
class Z_attribute_synonym Z_plugin_matchAttributeSynonym(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute) {
|
||||||
|
const Z_attribute_synonym * attr_syn;
|
||||||
|
attr_syn = g_plugin_templates.matchAttributeSynonym(model == nullptr ? "" : model,
|
||||||
|
manufacturer == nullptr ? "" : manufacturer,
|
||||||
|
cluster, attribute);
|
||||||
|
|
||||||
|
Z_attribute_synonym syn;
|
||||||
|
if (attr_syn != nullptr) {
|
||||||
|
syn = *attr_syn;
|
||||||
|
}
|
||||||
|
return syn;
|
||||||
|
}
|
||||||
|
|
||||||
|
Z_attribute_match Z_plugin_matchAttributeById(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute) {
|
||||||
|
const Z_plugin_attribute * attr_tmpl;
|
||||||
|
attr_tmpl = g_plugin_templates.matchAttributeById(model == nullptr ? "" : model,
|
||||||
|
manufacturer == nullptr ? "" : manufacturer,
|
||||||
|
cluster, attribute);
|
||||||
|
|
||||||
|
Z_attribute_match attr;
|
||||||
|
if (attr_tmpl != nullptr) {
|
||||||
|
attr.cluster = attr_tmpl->cluster;
|
||||||
|
attr.attribute = attr_tmpl->attribute;
|
||||||
|
attr.name = attr_tmpl->name.c_str();
|
||||||
|
attr.zigbee_type = attr_tmpl->type;
|
||||||
|
attr.multiplier = attr_tmpl->multiplier;
|
||||||
|
}
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Z_attribute_match Z_plugin_matchAttributeByName(const char *model, const char *manufacturer, const char * name) {
|
||||||
|
const Z_plugin_attribute * attr_tmpl;
|
||||||
|
Z_attribute_match attr;
|
||||||
|
|
||||||
|
if (name != nullptr) {
|
||||||
|
attr_tmpl = g_plugin_templates.matchAttributeByName(model == nullptr ? "" : model,
|
||||||
|
manufacturer == nullptr ? "" : manufacturer,
|
||||||
|
name);
|
||||||
|
|
||||||
|
if (attr_tmpl != nullptr) {
|
||||||
|
attr.cluster = attr_tmpl->cluster;
|
||||||
|
attr.attribute = attr_tmpl->attribute;
|
||||||
|
attr.name = attr_tmpl->name.c_str();
|
||||||
|
attr.zigbee_type = attr_tmpl->type;
|
||||||
|
attr.multiplier = attr_tmpl->multiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Zb_readline(File *f, char* buf, size_t size) {
|
||||||
|
bool eof = 0;
|
||||||
|
while (1) {
|
||||||
|
// read line
|
||||||
|
bool comment = false; // did we encounter '#', if so ignore anything until '\n'
|
||||||
|
char * p = buf;
|
||||||
|
while (1) {
|
||||||
|
int c = f->read();
|
||||||
|
if (c == -1) { eof = true; break; } // EOF reached
|
||||||
|
if (c == '#') { comment = true; } // rest of line is ignored
|
||||||
|
if (c == '\n') { break; } // end of line
|
||||||
|
if (!comment) {
|
||||||
|
if (p < buf + size - 1) {
|
||||||
|
*p++ = c; // append character
|
||||||
|
} else {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad line exceeds 96 bytes, aborting");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int32_t ret = p - buf; // len in bytes
|
||||||
|
if (eof && ret == 0) { return false; } // nothing more to read
|
||||||
|
// found something, don't add the \n since we don't need it
|
||||||
|
buf[ret] = 0; // add string terminator
|
||||||
|
RemoveSpace(buf); // remove anything that looks like a space, tab, crlf...
|
||||||
|
// AddLog(LOG_LEVEL_INFO, "ZIG: ZbRead>'%s'", buf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_UFILESYS
|
||||||
|
extern FS *ffsp;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// load a file from filesystem
|
||||||
|
// returns `true` if success
|
||||||
|
bool ZbLoad(const char *filename_raw) {
|
||||||
|
|
||||||
|
#ifdef USE_UFILESYS
|
||||||
|
if (ffsp) {
|
||||||
|
// first unload previsou definitions
|
||||||
|
ZbUnload(filename_raw);
|
||||||
|
|
||||||
|
String filename = filename_raw;
|
||||||
|
if (filename_raw[0] != '/') {
|
||||||
|
filename = "/";
|
||||||
|
filename += filename_raw;
|
||||||
|
}
|
||||||
|
File fp;
|
||||||
|
fp = ffsp->open(filename.c_str(), "r");
|
||||||
|
|
||||||
|
if (fp <= 0) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: unable to load file '%s'", filename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * filename_imported = nullptr;
|
||||||
|
Z_plugin_template * tmpl = nullptr; // current template with matchers and attributes
|
||||||
|
bool new_matchers = false; // indicates that we have finished the current matchers
|
||||||
|
char buf_line[96]; // max line is 96 bytes (comments don't count)
|
||||||
|
|
||||||
|
// read the first 6 chars
|
||||||
|
bool invalid_header = false;
|
||||||
|
static const char Z2T_HEADER_V1[] PROGMEM = "#Z2Tv1";
|
||||||
|
for (uint32_t i = 0; i < 6; i++) {
|
||||||
|
int c = fp.read();
|
||||||
|
if (c < 0) {
|
||||||
|
invalid_header = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf_line[i] = c;
|
||||||
|
buf_line[i+1] = 0;
|
||||||
|
}
|
||||||
|
if (!invalid_header) {
|
||||||
|
if (strcmp_P(buf_line, Z2T_HEADER_V1) != 0) {
|
||||||
|
invalid_header = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalid_header) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad '%s' invalid header", filename_raw);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse line by line
|
||||||
|
while (1) {
|
||||||
|
if (!Zb_readline(&fp, buf_line, sizeof(buf_line))) {
|
||||||
|
// EOF
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// at first valid line, we instanciate a new plug-in instance and assign a filemane
|
||||||
|
if (filename_imported == nullptr) {
|
||||||
|
// allocate only once the filename for multiple entries
|
||||||
|
// freed only by `ZbUnload`
|
||||||
|
filename_imported = (char*) malloc(strlen(filename.c_str())+1);
|
||||||
|
strcpy(filename_imported, filename.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is a non-empty line, containing no space/tab/crlf
|
||||||
|
// depending on the first char, parse either device name or cluster/attribute+name
|
||||||
|
if (buf_line[0] == ':') {
|
||||||
|
if (tmpl == nullptr || new_matchers) {
|
||||||
|
tmpl = &g_plugin_templates.addToLast();
|
||||||
|
tmpl->filename = filename_imported;
|
||||||
|
new_matchers = false;
|
||||||
|
}
|
||||||
|
// parse device name
|
||||||
|
char *rest = buf_line + 1; // skip first char ':'
|
||||||
|
char *token = strtok_r(rest, ",", &rest);
|
||||||
|
Z_plugin_matcher & matcher = tmpl->matchers.addToLast();
|
||||||
|
if (token != nullptr) {
|
||||||
|
matcher.model = token;
|
||||||
|
}
|
||||||
|
token = strtok_r(rest, ",", &rest);
|
||||||
|
if (token != nullptr) {
|
||||||
|
matcher.manufacturer = token;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tmpl == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
new_matchers = true;
|
||||||
|
char *rest = buf_line;
|
||||||
|
char *token = strtok_r(rest, ",", &rest);
|
||||||
|
if (token == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// detect if token contains '=', if yes it is a synonym
|
||||||
|
char * delimiter_equal = strchr(token, '=');
|
||||||
|
|
||||||
|
if (delimiter_equal == nullptr) {
|
||||||
|
// token is of from '0000/0000' or '0000/0000%00'
|
||||||
|
char * delimiter_slash = strchr(token, '/');
|
||||||
|
char * delimiter_percent = strchr(token, '%');
|
||||||
|
if (delimiter_slash == nullptr || (delimiter_percent != nullptr && delimiter_slash > delimiter_percent)) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad '%s' wrong delimiter '%s'", filename_raw, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t attr_id = 0xFFFF;
|
||||||
|
uint16_t cluster_id = 0xFFFF;
|
||||||
|
uint8_t type_id = Zunk;
|
||||||
|
|
||||||
|
cluster_id = strtoul(token, &delimiter_slash, 16);
|
||||||
|
if (!delimiter_percent) {
|
||||||
|
attr_id = strtoul(delimiter_slash+1, nullptr, 16);
|
||||||
|
} else {
|
||||||
|
attr_id = strtoul(delimiter_slash+1, &delimiter_percent, 16);
|
||||||
|
type_id = Z_getTypeByName(delimiter_percent+1);
|
||||||
|
}
|
||||||
|
// name of the attribute
|
||||||
|
token = strtok_r(rest, ",", &rest);
|
||||||
|
if (token == nullptr) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad '%s' ignore missing name '%s'", filename_raw, buf_line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// token contains the name of the attribute
|
||||||
|
Z_plugin_attribute & plugin_attr = tmpl->attributes.addToLast();
|
||||||
|
plugin_attr.cluster = cluster_id;
|
||||||
|
plugin_attr.attribute = attr_id;
|
||||||
|
plugin_attr.type = type_id;
|
||||||
|
plugin_attr.name = token;
|
||||||
|
} else {
|
||||||
|
// token is of from '0000/0000=0000/0000,1'
|
||||||
|
char * rest2 = token;
|
||||||
|
char * tok2 = strtok_r(rest2, "=", &rest2);
|
||||||
|
char * delimiter_slash = strchr(tok2, '/');
|
||||||
|
uint16_t cluster_id = strtoul(tok2, &delimiter_slash, 16);
|
||||||
|
uint16_t attr_id = strtoul(delimiter_slash+1, nullptr, 16);
|
||||||
|
tok2 = strtok_r(rest2, "=", &rest2);
|
||||||
|
char * delimiter_slash2 = strchr(tok2, '/');
|
||||||
|
uint16_t new_cluster_id = strtoul(tok2, &delimiter_slash2, 16);
|
||||||
|
uint16_t new_attr_id = strtoul(delimiter_slash2+1, nullptr, 16);
|
||||||
|
// multiplier
|
||||||
|
token = strtok_r(rest, ",", &rest);
|
||||||
|
int8_t multiplier = 1;
|
||||||
|
if (token != nullptr) {
|
||||||
|
multiplier = strtol(token, nullptr, 10);
|
||||||
|
}
|
||||||
|
// create the synonym
|
||||||
|
Z_attribute_synonym & syn = tmpl->synonyms.addToLast();
|
||||||
|
syn.cluster = cluster_id;
|
||||||
|
syn.attribute = attr_id;
|
||||||
|
syn.new_cluster = new_cluster_id;
|
||||||
|
syn.new_attribute = new_attr_id;
|
||||||
|
syn.multiplier = multiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AddLog(LOG_LEVEL_ERROR, "ZIG: filesystem not enabled");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad requires file system");
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlaod previously loaded definitions
|
||||||
|
bool ZbUnload(const char *filename_raw) {
|
||||||
|
String filename = filename_raw;
|
||||||
|
if (filename_raw[0] != '/') {
|
||||||
|
filename = "/";
|
||||||
|
filename += filename_raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * filename_registered = nullptr; // internal allocation for filename
|
||||||
|
for (const Z_plugin_template & tmpl : g_plugin_templates) {
|
||||||
|
bool to_be_freed = false;
|
||||||
|
if (filename_registered) {
|
||||||
|
// if filename_registered is not NULL, compare pointers
|
||||||
|
if (tmpl.filename == filename_registered) { to_be_freed = true; }
|
||||||
|
} else {
|
||||||
|
if (strcmp(tmpl.filename, filename.c_str()) == 0) {
|
||||||
|
filename_registered = tmpl.filename;
|
||||||
|
to_be_freed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check if we remove this node
|
||||||
|
if (to_be_freed) {
|
||||||
|
g_plugin_templates.remove(&tmpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// free memory for filename
|
||||||
|
if (filename_registered) {
|
||||||
|
free(filename_registered);
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbUnload '%s' sucessful", filename_raw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump the ZbLoad structure in a format compatible with ZbLoad
|
||||||
|
void ZbLoadDump(void) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad dump all current information");
|
||||||
|
AddLog(LOG_LEVEL_INFO, "====> START");
|
||||||
|
|
||||||
|
for (const Z_plugin_template & tmpl : g_plugin_templates) {
|
||||||
|
if (tmpl.filename != nullptr) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "# imported from '%s'", tmpl.filename);
|
||||||
|
}
|
||||||
|
// marchers
|
||||||
|
if (tmpl.matchers.length() == 0) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, ": # no matcher");
|
||||||
|
} else {
|
||||||
|
for (const Z_plugin_matcher & matcher : tmpl.matchers) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, ":%s,%s", matcher.model.c_str(), matcher.manufacturer.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// attributes
|
||||||
|
if (tmpl.attributes.length() == 0 && tmpl.synonyms.length() == 0) {
|
||||||
|
// no content, output an empty line
|
||||||
|
AddLog(LOG_LEVEL_INFO, "");
|
||||||
|
} else {
|
||||||
|
for (const Z_plugin_attribute & attr : tmpl.attributes) {
|
||||||
|
if (attr.type == Zunk) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "%04X/%04X,%s", attr.cluster, attr.attribute, attr.name.c_str());
|
||||||
|
} else {
|
||||||
|
char type[16];
|
||||||
|
Z_getTypeByNumber(type, sizeof(type), attr.type);
|
||||||
|
AddLog(LOG_LEVEL_INFO, "%04X/%04X%%%s,%s", attr.cluster, attr.attribute, type, attr.name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const Z_attribute_synonym & syn : tmpl.synonyms) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "%04X/%04X=%04X/%04X,%i", syn.cluster, syn.attribute, syn.new_cluster, syn.new_attribute, syn.multiplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddLog(LOG_LEVEL_INFO, "<==== END");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-load all files ending with '.zb'
|
||||||
|
void ZbAutoload(void) {
|
||||||
|
#ifdef USE_UFILESYS
|
||||||
|
if (ffsp) {
|
||||||
|
File dir = ffsp->open("/", "r");
|
||||||
|
if (dir) {
|
||||||
|
dir.rewindDirectory();
|
||||||
|
while (1) {
|
||||||
|
File entry = dir.openNextFile();
|
||||||
|
if (!entry) { break; }
|
||||||
|
const char * fn = entry.name();
|
||||||
|
if (strcmp(fn, ".") && strcmp(fn, "..")) {
|
||||||
|
// check suffix
|
||||||
|
size_t l = strlen(fn);
|
||||||
|
if (l > 3) {
|
||||||
|
if (fn[l-3] == '.' && fn[l-2] == 'z' && fn[l-1] == 'b') {
|
||||||
|
bool ret = ZbLoad(fn);
|
||||||
|
if (ret) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad '%s' loaded successfully", fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_UFILESYS
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_ZIGBEE
|
|
@ -1553,25 +1553,24 @@ void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uin
|
||||||
uint16_t max_interval = pgm_read_word(&(Z_autoAttributeReporting[i].max_interval));
|
uint16_t max_interval = pgm_read_word(&(Z_autoAttributeReporting[i].max_interval));
|
||||||
float report_change_raw = Z_autoAttributeReporting[i].report_change;
|
float report_change_raw = Z_autoAttributeReporting[i].report_change;
|
||||||
double report_change = report_change_raw;
|
double report_change = report_change_raw;
|
||||||
uint8_t attr_type;
|
// uint8_t attr_type;
|
||||||
int8_t multiplier;
|
// int8_t multiplier;
|
||||||
|
|
||||||
const __FlashStringHelper* attr_name = zigbeeFindAttributeById(cluster, attr_id, &attr_type, &multiplier);
|
Z_attribute_match attr_matched = Z_findAttributeMatcherById(shortaddr, cluster, attr_id, false);
|
||||||
|
if (attr_matched.found()) {
|
||||||
if (attr_name) {
|
|
||||||
if (comma) { ResponseAppend_P(PSTR(",")); }
|
if (comma) { ResponseAppend_P(PSTR(",")); }
|
||||||
comma = true;
|
comma = true;
|
||||||
ResponseAppend_P(PSTR("\"%s\":{\"MinInterval\":%d,\"MaxInterval\":%d"), attr_name, min_interval, max_interval);
|
ResponseAppend_P(PSTR("\"%s\":{\"MinInterval\":%d,\"MaxInterval\":%d"), attr_matched.name, min_interval, max_interval);
|
||||||
|
|
||||||
buf.add8(0); // direction, always 0
|
buf.add8(0); // direction, always 0
|
||||||
buf.add16(attr_id);
|
buf.add16(attr_id);
|
||||||
buf.add8(attr_type);
|
buf.add8(attr_matched.zigbee_type);
|
||||||
buf.add16(min_interval);
|
buf.add16(min_interval);
|
||||||
buf.add16(max_interval);
|
buf.add16(max_interval);
|
||||||
if (!Z_isDiscreteDataType(attr_type)) { // report_change is only valid for non-discrete data types (numbers)
|
if (!Z_isDiscreteDataType(attr_matched.zigbee_type)) { // report_change is only valid for non-discrete data types (numbers)
|
||||||
ZbApplyMultiplier(report_change, multiplier);
|
ZbApplyMultiplier(report_change, attr_matched.multiplier);
|
||||||
// encode value
|
// encode value
|
||||||
int32_t res = encodeSingleAttribute(buf, report_change, "", attr_type);
|
int32_t res = encodeSingleAttribute(buf, report_change, "", attr_matched.zigbee_type);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "internal error, unsupported attribute type"));
|
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "internal error, unsupported attribute type"));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1690,12 +1689,12 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
|
||||||
zcl_received.parseReadAttributesResponse(attr_list);
|
zcl_received.parseReadAttributesResponse(attr_list);
|
||||||
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
if (clusterid) { defer_attributes = true; } // don't defer system Cluster=0 messages
|
||||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES == zcl_received.getCmdId())) {
|
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseReadAttributes(attr_list);
|
zcl_received.parseReadAttributes(srcaddr, attr_list);
|
||||||
// never defer read_attributes, so the auto-responder can send response back on a per cluster basis
|
// never defer read_attributes, so the auto-responder can send response back on a per cluster basis
|
||||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_REPORTING_CONFIGURATION_RESPONSE == zcl_received.getCmdId())) {
|
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_REPORTING_CONFIGURATION_RESPONSE == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseReadConfigAttributes(attr_list);
|
zcl_received.parseReadConfigAttributes(srcaddr, attr_list);
|
||||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_CONFIGURE_REPORTING_RESPONSE == zcl_received.getCmdId())) {
|
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_CONFIGURE_REPORTING_RESPONSE == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseConfigAttributes(attr_list);
|
zcl_received.parseConfigAttributes(srcaddr, attr_list);
|
||||||
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_WRITE_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
} else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_WRITE_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseWriteAttributesResponse(attr_list);
|
zcl_received.parseWriteAttributesResponse(attr_list);
|
||||||
} else if (zcl_received.isClusterSpecificCommand()) {
|
} else if (zcl_received.isClusterSpecificCommand()) {
|
||||||
|
@ -2123,6 +2122,15 @@ int32_t Z_Query_Bulbs(uint8_t value) {
|
||||||
return 0; // continue
|
return 0; // continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Z_ZbAutoload - autoload all definitions from filesystem
|
||||||
|
// files with ending '.zb' suffix
|
||||||
|
//
|
||||||
|
int32_t Z_ZbAutoload(uint8_t value) {
|
||||||
|
ZbAutoload();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Zigbee initialization is complete, let the party begin
|
// Zigbee initialization is complete, let the party begin
|
||||||
//
|
//
|
||||||
|
@ -2193,7 +2201,7 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!attr.isNone()) {
|
if (!attr.isNone()) {
|
||||||
Z_parseAttributeKey(attr, cluster);
|
Z_parseAttributeKey(shortaddr, attr, cluster);
|
||||||
attr_list.addAttribute(cluster, attr_id) = attr;
|
attr_list.addAttribute(cluster, attr_id) = attr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,8 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
||||||
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
|
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
|
||||||
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_OCCUPANCY "|"
|
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_OCCUPANCY "|"
|
||||||
D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|" D_CMND_ZIGBEE_MAP "|" D_CMND_ZIGBEE_LEAVE "|"
|
D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|" D_CMND_ZIGBEE_MAP "|" D_CMND_ZIGBEE_LEAVE "|"
|
||||||
D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA "|" D_CMND_ZIGBEE_SCAN "|" D_CMND_ZIGBEE_ENROLL "|" D_CMND_ZIGBEE_CIE
|
D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA "|" D_CMND_ZIGBEE_SCAN "|" D_CMND_ZIGBEE_ENROLL "|" D_CMND_ZIGBEE_CIE "|"
|
||||||
|
D_CMND_ZIGBEE_LOAD "|" D_CMND_ZIGBEE_UNLOAD "|" D_CMND_ZIGBEE_LOADDUMP
|
||||||
;
|
;
|
||||||
|
|
||||||
SO_SYNONYMS(kZbSynonyms,
|
SO_SYNONYMS(kZbSynonyms,
|
||||||
|
@ -62,6 +63,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||||
&CmndZbRestore, &CmndZbBindState, &CmndZbMap, &CmndZbLeave,
|
&CmndZbRestore, &CmndZbBindState, &CmndZbMap, &CmndZbLeave,
|
||||||
&CmndZbConfig, &CmndZbData, &CmndZbScan,
|
&CmndZbConfig, &CmndZbData, &CmndZbScan,
|
||||||
&CmndZbenroll, &CmndZbcie,
|
&CmndZbenroll, &CmndZbcie,
|
||||||
|
&CmndZbLoad, &CmndZbUnload, &CmndZbLoadDump,
|
||||||
};
|
};
|
||||||
|
|
||||||
/********************************************************************************************/
|
/********************************************************************************************/
|
||||||
|
@ -339,7 +341,7 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLFrame & zcl)
|
||||||
|
|
||||||
Z_attribute attr;
|
Z_attribute attr;
|
||||||
attr.setKeyName(key.getStr());
|
attr.setKeyName(key.getStr());
|
||||||
if (Z_parseAttributeKey(attr, tuya_protocol ? 0xEF00 : 0xFFFF)) { // favor tuya protocol if needed
|
if (Z_parseAttributeKey(zcl.shortaddr, attr, tuya_protocol ? 0xEF00 : 0xFFFF)) { // favor tuya protocol if needed
|
||||||
// Buffer ready, do some sanity checks
|
// Buffer ready, do some sanity checks
|
||||||
|
|
||||||
// all attributes must use the same cluster
|
// all attributes must use the same cluster
|
||||||
|
@ -352,11 +354,11 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLFrame & zcl)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (attr.key_is_str) {
|
if (attr.key_is_str) {
|
||||||
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR(D_ZIGBEE_UNKNOWN_ATTRIBUTE " "), key);
|
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR(D_ZIGBEE_UNKNOWN_ATTRIBUTE " "), key.getStr());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Zunk == attr.attr_type) {
|
if (Zunk == attr.attr_type) {
|
||||||
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR(D_ZIGBEE_UNSUPPORTED_ATTRIBUTE_TYPE " "), key);
|
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR(D_ZIGBEE_UNSUPPORTED_ATTRIBUTE_TYPE " "), key.getStr());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,34 +625,26 @@ void ZbSendRead(JsonParserToken val_attr, ZCLFrame & zcl) {
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
// scan attributes to find by name, and retrieve type
|
// scan attributes to find by name, and retrieve type
|
||||||
for (uint32_t i = 0; i < nitems(Z_PostProcess); i++) {
|
Z_attribute_match matched_attr = Z_findAttributeMatcherByName(zcl.shortaddr, key.getStr());
|
||||||
const Z_AttributeConverter *converter = &Z_PostProcess[i];
|
if (matched_attr.found()) {
|
||||||
uint16_t local_attr_id = pgm_read_word(&converter->attribute);
|
// match name
|
||||||
uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short));
|
// check if there is a conflict with cluster
|
||||||
// uint8_t local_type_id = pgm_read_byte(&converter->type);
|
if (!(value.getBool()) && attr_item_offset) {
|
||||||
|
// If value is false (non-default) then set direction to 1 (for ReadConfig)
|
||||||
if ((pgm_read_word(&converter->name_offset)) && (0 == strcasecmp_P(key.getStr(), Z_strings + pgm_read_word(&converter->name_offset)))) {
|
attrs[actual_attr_len] = 0x01;
|
||||||
// match name
|
}
|
||||||
// check if there is a conflict with cluster
|
actual_attr_len += attr_item_offset;
|
||||||
// TODO
|
attrs[actual_attr_len++] = matched_attr.attribute & 0xFF;
|
||||||
if (!(value.getBool()) && attr_item_offset) {
|
attrs[actual_attr_len++] = matched_attr.attribute >> 8;
|
||||||
// If value is false (non-default) then set direction to 1 (for ReadConfig)
|
actual_attr_len += attr_item_len - 2 - attr_item_offset; // normally 0
|
||||||
attrs[actual_attr_len] = 0x01;
|
found = true;
|
||||||
}
|
// check cluster
|
||||||
actual_attr_len += attr_item_offset;
|
if (!zcl.validCluster()) {
|
||||||
attrs[actual_attr_len++] = local_attr_id & 0xFF;
|
zcl.cluster = matched_attr.cluster;
|
||||||
attrs[actual_attr_len++] = local_attr_id >> 8;
|
} else if (zcl.cluster != matched_attr.cluster) {
|
||||||
actual_attr_len += attr_item_len - 2 - attr_item_offset; // normally 0
|
ResponseCmndChar_P(PSTR(D_ZIGBEE_TOO_MANY_CLUSTERS));
|
||||||
found = true;
|
if (attrs) { free(attrs); }
|
||||||
// check cluster
|
return;
|
||||||
if (!zcl.validCluster()) {
|
|
||||||
zcl.cluster = local_cluster_id;
|
|
||||||
} else if (zcl.cluster != local_cluster_id) {
|
|
||||||
ResponseCmndChar_P(PSTR(D_ZIGBEE_TOO_MANY_CLUSTERS));
|
|
||||||
if (attrs) { free(attrs); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break; // found, exit loop
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
@ -863,7 +857,10 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||||
if (val_cluster) {
|
if (val_cluster) {
|
||||||
cluster = val_cluster.getUInt(cluster); // first convert as number
|
cluster = val_cluster.getUInt(cluster); // first convert as number
|
||||||
if (0 == cluster) {
|
if (0 == cluster) {
|
||||||
zigbeeFindAttributeByName(val_cluster.getStr(), &cluster, nullptr, nullptr);
|
Z_attribute_match attr_matched = Z_findAttributeMatcherByName(BAD_SHORTADDR, val_cluster.getStr());
|
||||||
|
if (attr_matched.found()) {
|
||||||
|
cluster = attr_matched.cluster;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1306,6 +1303,53 @@ void CmndZbSave(void) {
|
||||||
ResponseCmndDone();
|
ResponseCmndDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Command `ZbLoad`
|
||||||
|
// Load a device specific zigbee template
|
||||||
|
//
|
||||||
|
void CmndZbLoad(void) {
|
||||||
|
// can be called before Zigbee is initialized
|
||||||
|
RemoveSpace(XdrvMailbox.data);
|
||||||
|
|
||||||
|
bool ret = true;;
|
||||||
|
if (strcmp(XdrvMailbox.data, "*") == 0) {
|
||||||
|
ZbAutoload();
|
||||||
|
} else {
|
||||||
|
ret = ZbLoad(XdrvMailbox.data);
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
ResponseCmndDone();
|
||||||
|
} else {
|
||||||
|
ResponseCmndError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Command `ZbUnload`
|
||||||
|
// Unload a template previously loaded
|
||||||
|
//
|
||||||
|
void CmndZbUnload(void) {
|
||||||
|
// can be called before Zigbee is initialized
|
||||||
|
RemoveSpace(XdrvMailbox.data);
|
||||||
|
|
||||||
|
bool ret = ZbUnload(XdrvMailbox.data);
|
||||||
|
if (ret) {
|
||||||
|
ResponseCmndDone();
|
||||||
|
} else {
|
||||||
|
ResponseCmndError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Command `ZbLoadDump`
|
||||||
|
// Load a device specific zigbee template
|
||||||
|
//
|
||||||
|
void CmndZbLoadDump(void) {
|
||||||
|
// can be called before Zigbee is initialized
|
||||||
|
ZbLoadDump();
|
||||||
|
ResponseCmndDone();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Command `ZbScan`
|
// Command `ZbScan`
|
||||||
// Run an energy scan
|
// Run an energy scan
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#Z2Tv1
|
||||||
|
# GiEX garden watering https://www.aliexpress.com/item/1005004222098040.html
|
||||||
|
:TS0601,_TZE200_sh1btabb
|
||||||
|
EF00/0101,WaterMode # duration=0 / capacity=1
|
||||||
|
EF00/0102,WaterState # off=0 / on=1
|
||||||
|
EF00/0365,IrrigationStartTime # (string) ex: "08:12:26"
|
||||||
|
EF00/0366,IrrigationTarget # (string) ex: "08:13:36"
|
||||||
|
EF00/0267,CycleIrrigationNumTimes # number of cycle irrigation times, set to 0 for single cycle
|
||||||
|
EF00/0268,IrrigationTarget # duration in minutes or capacity in Liters (depending on mode)
|
||||||
|
EF00/0269,CycleIrrigationInterval # cycle irrigation interval (minutes, max 1440)
|
||||||
|
EF00/026A,CurrentTemperature # (value ignored because isn't a valid tempurature reading. Misdocumented and usage unclear)
|
||||||
|
EF00/026C=0001/0021,2 # match to BatteryPercentage
|
||||||
|
EF00/026F,WaterConsumed # water consumed (Litres)
|
||||||
|
EF00/0372,LastIrrigationDuration # (string) Ex: "00:01:10,0"
|
Loading…
Reference in New Issue