mirror of https://github.com/arendst/Tasmota.git
Zigbee ``ZbEmulation`` to selectively exclude some devices from Hue/Alexa emulation (#20552)
This commit is contained in:
parent
76a7ab5131
commit
de91133414
|
@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
|
|||
- Berry solidification of strings longer than 255 bytes (#20529)
|
||||
- Berry syntax coloring for Notepad++ by FransO (#20541)
|
||||
- Berry/Zigbee web hook per device for customized status display (#20542)
|
||||
- Zigbee ``ZbEmulation`` to selectively exclude some devices from Hue/Alexa emulation
|
||||
|
||||
### Breaking Changed
|
||||
|
||||
|
|
|
@ -700,6 +700,7 @@
|
|||
#define D_CMND_ZIGBEE_SCAN "Scan"
|
||||
#define D_JSON_ZIGBEE_SCAN "ZbScan"
|
||||
#define D_CMND_ZIGBEE_CIE "CIE"
|
||||
#define D_CMND_ZIGBEE_EMULATION "Emulation"
|
||||
#define D_CMND_ZIGBEE_ENROLL "Enroll"
|
||||
#define D_CMND_ZIGBEE_LOAD "Load"
|
||||
#define D_CMND_ZIGBEE_LOADDUMP "LoadDump"
|
||||
|
|
|
@ -109,6 +109,19 @@ Output:
|
|||
00:00:00.231 >>>: v16=65535 out=63000 <> hex=0xFF
|
||||
*/
|
||||
|
||||
/*********************************************************************************************\
|
||||
*
|
||||
* Flag to no advertize for Hue (Alexa) or Matter
|
||||
*
|
||||
\*********************************************************************************************/
|
||||
|
||||
enum class Z_no_advertize : uint8_t {
|
||||
Z_no_ad_default = 0X00, // advertize on all
|
||||
Z_no_ad_hue = 0x01, // no-advertize on Hue
|
||||
Z_no_ad_matter = 0x02, // no-advertize on Matter
|
||||
Z_no_ad_all = 0xFF, // no-advertize on all
|
||||
};
|
||||
|
||||
enum class Z_Data_Type : uint8_t {
|
||||
Z_Unknown = 0x00,
|
||||
Z_Light = 1, // Lights 1-5 channels
|
||||
|
@ -961,7 +974,9 @@ public:
|
|||
uint32_t batt_last_probed; // Time when the device was last probed for batteyr values
|
||||
uint8_t lqi; // lqi from last message, 0xFF means unknown
|
||||
uint8_t batt_percent; // battery percentage (0..100), 0xFF means unknwon
|
||||
uint16_t reserved_for_alignment;
|
||||
Z_no_advertize no_advertize; // no advertize for Hue or Matter
|
||||
uint8_t reserved_for_alignment;
|
||||
|
||||
// Debounce informmation when receiving commands
|
||||
// If we receive the same ZCL transaction number from the same device and the same endpoint within 300ms
|
||||
// then discard the second packet
|
||||
|
@ -989,7 +1004,8 @@ public:
|
|||
batt_last_probed(0),
|
||||
lqi(0xFF),
|
||||
batt_percent(0xFF),
|
||||
reserved_for_alignment(0xFFFF),
|
||||
no_advertize(Z_no_advertize::Z_no_ad_default),
|
||||
reserved_for_alignment(0xFF),
|
||||
debounce_endpoint(0),
|
||||
debounce_transact(0)
|
||||
{ };
|
||||
|
@ -1008,6 +1024,28 @@ public:
|
|||
inline bool validLastSeen(void) const { return 0x0 != last_seen; }
|
||||
inline bool validBattLastSeen(void) const { return (0x0 != batt_last_seen) && (batt_last_seen < 0xFFFFFFF0); }
|
||||
|
||||
inline bool isAdvertizeNone(void) const { return (no_advertize == Z_no_advertize::Z_no_ad_all); } // never advertize in bridge mode
|
||||
inline bool isAdvertizeHue(void) const { return !((uint8_t)no_advertize & (uint8_t)Z_no_advertize::Z_no_ad_hue) ; } // advertize to Alexa/Hue (if a bulb)
|
||||
inline bool isAdvertizeMatter(void) const { return !((uint8_t)no_advertize & (uint8_t)Z_no_advertize::Z_no_ad_matter) ; } // advertize to Matter (type has to be defined in Matter)
|
||||
inline bool isAdvertizeAll(void) const { return (no_advertize == Z_no_advertize::Z_no_ad_default) ; } // advertize to all possible (default)
|
||||
|
||||
inline void setAdvertizeNone(void) { no_advertize = Z_no_advertize::Z_no_ad_all; }
|
||||
inline void setAdvertizeAll(void) { no_advertize = Z_no_advertize::Z_no_ad_default; }
|
||||
inline void setAdvertizeHue(bool adv) {
|
||||
if (adv) {
|
||||
no_advertize = (Z_no_advertize)((uint8_t)no_advertize & ~(uint8_t)Z_no_advertize::Z_no_ad_hue);
|
||||
} else {
|
||||
no_advertize = (Z_no_advertize)((uint8_t)no_advertize | (uint8_t)Z_no_advertize::Z_no_ad_hue);
|
||||
}
|
||||
}
|
||||
inline void setAdvertizeMatter(bool adv) {
|
||||
if (adv) {
|
||||
no_advertize = (Z_no_advertize)((uint8_t)no_advertize & ~(uint8_t)Z_no_advertize::Z_no_ad_matter);
|
||||
} else {
|
||||
no_advertize = (Z_no_advertize)((uint8_t)no_advertize | (uint8_t)Z_no_advertize::Z_no_ad_matter);
|
||||
}
|
||||
}
|
||||
|
||||
inline void setReachable(bool _reachable) { reachable = _reachable; }
|
||||
inline bool getReachable(void) const { return reachable; }
|
||||
inline bool getPower(uint8_t ep = 0) const;
|
||||
|
@ -1053,6 +1091,7 @@ public:
|
|||
void jsonAddModelManuf(Z_attribute_list & attr_list) const;
|
||||
void jsonAddEndpoints(Z_attribute_list & attr_list) const;
|
||||
void jsonAddConfig(Z_attribute_list & attr_list) const;
|
||||
void jsonAddEmulation(Z_attribute_list & attr_list) const;
|
||||
void jsonAddDataAttributes(Z_attribute_list & attr_list) const;
|
||||
void jsonAddDeviceAttributes(Z_attribute_list & attr_list) const;
|
||||
void jsonDumpSingleDevice(Z_attribute_list & attr_list, uint32_t dump_mode, bool add_name) const;
|
||||
|
|
|
@ -814,6 +814,11 @@ void Z_Device::jsonAddConfig(Z_attribute_list & attr_list) const {
|
|||
}
|
||||
attr_list.addAttributePMEM(PSTR("Config")).setStrRaw(arr_data.toString().c_str());
|
||||
}
|
||||
// Add "HueEmulation":false, "Matter":false
|
||||
void Z_Device::jsonAddEmulation(Z_attribute_list & attr_list) const {
|
||||
if (!isAdvertizeHue()) { attr_list.addAttributePMEM(PSTR("HueEmulation")).setBool(false); }
|
||||
if (!isAdvertizeMatter()) { attr_list.addAttributePMEM(PSTR("Matter")).setBool(false); }
|
||||
}
|
||||
// Add All data attributes
|
||||
void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const {
|
||||
// show internal data - mostly last known values
|
||||
|
@ -824,7 +829,7 @@ void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality"
|
||||
// Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality", "HueEmulation" (opt) and "Matter" (opt)
|
||||
void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const {
|
||||
attr_list.addAttributePMEM(PSTR("Reachable")).setBool(getReachable());
|
||||
if (validBatteryPercent()) { attr_list.addAttributePMEM(PSTR("BatteryPercentage")).setUInt(batt_percent); }
|
||||
|
@ -879,6 +884,7 @@ void Z_Device::jsonDumpSingleDevice(Z_attribute_list & attr_list, uint32_t dump_
|
|||
jsonAddModelManuf(attr_list);
|
||||
jsonAddEndpoints(attr_list);
|
||||
jsonAddConfig(attr_list);
|
||||
jsonAddEmulation(attr_list);
|
||||
}
|
||||
if (dump_mode >= 3) {
|
||||
jsonAddDataAttributes(attr_list);
|
||||
|
@ -928,7 +934,7 @@ String Z_Devices::dumpDevice(uint32_t dump_mode, const Z_Device & device) const
|
|||
// 0 : Ok
|
||||
// <0 : Error
|
||||
//
|
||||
// Ex: {"Device":"0x5ADF","Name":"IKEA_Light","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]}
|
||||
// Ex: {"Device":"0x5ADF","Name":"IKEA_Light","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"],"HueEmulation":false}
|
||||
int32_t Z_Devices::deviceRestore(JsonParserObject json) {
|
||||
|
||||
// params
|
||||
|
@ -1007,6 +1013,12 @@ int32_t Z_Devices::deviceRestore(JsonParserObject json) {
|
|||
}
|
||||
}
|
||||
|
||||
// read "HueEmulation" and "Matter"
|
||||
bool hue_emulation = json.getBool(PSTR("HueEmulation"), false);
|
||||
bool matter = json.getBool(PSTR("Matter"), false);
|
||||
device.setAdvertizeHue(hue_emulation);
|
||||
device.setAdvertizeMatter(matter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,9 @@ int32_t hydrateDeviceWideData(class Z_Device & device, const SBuffer & buf, size
|
|||
if (segment_len >= 10) {
|
||||
device.batt_last_seen = buf.get32(start+7);
|
||||
}
|
||||
if (segment_len >= 11) {
|
||||
device.no_advertize = (Z_no_advertize)buf.get8(start+11);
|
||||
}
|
||||
return segment_len + 1;
|
||||
}
|
||||
|
||||
|
@ -121,12 +124,14 @@ SBuffer hibernateDeviceData(const struct Z_Device & device) {
|
|||
buf.add16(device.shortaddr);
|
||||
|
||||
// device wide data
|
||||
buf.add8(10); // 10 bytes
|
||||
buf.add8(11); // 10 bytes
|
||||
buf.add32(device.last_seen);
|
||||
buf.add8(device.lqi);
|
||||
buf.add8(device.batt_percent);
|
||||
// now storing batt_last_seen
|
||||
buf.add32(device.batt_last_seen);
|
||||
// now storing no_advertize
|
||||
buf.add8((uint8_t)device.no_advertize);
|
||||
|
||||
for (const auto & data_elt : device.data) {
|
||||
size_t item_len = data_elt.DataTypeToLength(data_elt.getType());
|
||||
|
|
|
@ -144,7 +144,7 @@ void ZigbeeCheckHue(String & response, bool * appending) {
|
|||
uint8_t ep = device.endpoints[i];
|
||||
if (i > 0 && ep == 0) { break; }
|
||||
int8_t bulbtype = device.getHueBulbtype(ep);
|
||||
if (bulbtype >= 0) {
|
||||
if (bulbtype >= 0 && device.isAdvertizeHue()) {
|
||||
// this bulb is advertized
|
||||
if (*appending) { response += ","; }
|
||||
response += "\"";
|
||||
|
|
|
@ -42,6 +42,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
|||
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_CONFIG "|" D_CMND_ZIGBEE_DATA "|" D_CMND_ZIGBEE_SCAN "|" D_CMND_ZIGBEE_ENROLL "|" D_CMND_ZIGBEE_CIE "|"
|
||||
D_CMND_ZIGBEE_EMULATION "|"
|
||||
D_CMND_ZIGBEE_LOAD "|" D_CMND_ZIGBEE_UNLOAD "|" D_CMND_ZIGBEE_LOADDUMP
|
||||
#ifdef ZIGBEE_DOC
|
||||
"|" D_CMND_ZIGBEE_ATTRDUMP
|
||||
|
@ -67,7 +68,8 @@ void (* const ZigbeeCommand[])(void) PROGMEM = {
|
|||
&CmndZbLight, &CmndZbOccupancy,
|
||||
&CmndZbRestore, &CmndZbBindState, &CmndZbMap, &CmndZbLeave,
|
||||
&CmndZbConfig, &CmndZbData, &CmndZbScan,
|
||||
&CmndZbenroll, &CmndZbcie,
|
||||
&CmndZbenroll, &CmndZbCIE,
|
||||
&CmndZbEmulation,
|
||||
&CmndZbLoad, &CmndZbUnload, &CmndZbLoadDump,
|
||||
#ifdef ZIGBEE_DOC
|
||||
&CmndZbAttrDump,
|
||||
|
@ -1518,7 +1520,7 @@ void CmndZbenroll(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void CmndZbcie(void) {
|
||||
void CmndZbCIE(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||
|
||||
if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry
|
||||
|
@ -1536,6 +1538,38 @@ void CmndZbcie(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void CmndZbEmulation(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||
|
||||
if ((XdrvMailbox.data_len) && (ArgC() > 1)) { // Process parameter entry
|
||||
char argument[XdrvMailbox.data_len];
|
||||
Z_Device & device = zigbee_devices.parseDeviceFromName(ArgV(argument, 1), nullptr, nullptr, XdrvMailbox.payload);
|
||||
char * arg_v = ArgV(argument, 2);
|
||||
int emulation = atoi(arg_v);
|
||||
if (strlen(arg_v) == 0) { emulation = 255; }
|
||||
|
||||
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
|
||||
|
||||
if (emulation == 0) {
|
||||
device.setAdvertizeNone();
|
||||
} else if (emulation == 255) {
|
||||
device.setAdvertizeAll();
|
||||
} else if (emulation == 1) {
|
||||
device.setAdvertizeHue(true);
|
||||
} else if (emulation == 2) {
|
||||
device.setAdvertizeMatter(true);
|
||||
} else if (emulation == -1) {
|
||||
device.setAdvertizeHue(false);
|
||||
} else if (emulation == -2) {
|
||||
device.setAdvertizeMatter(false);
|
||||
}
|
||||
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
ResponseCmndError();
|
||||
}
|
||||
}
|
||||
|
||||
// Restore a device configuration previously exported via `ZbStatus2``
|
||||
// Format:
|
||||
// Either the entire `ZbStatus3` export, or an array or just the device configuration.
|
||||
|
@ -1755,20 +1789,27 @@ void ZigbeePermitJoinUpdate(void) {
|
|||
//
|
||||
// Command `ZbStatus`
|
||||
//
|
||||
// Options:
|
||||
// 0. `ZbStatus0` - Show information about coordinator `{"ZbStatus0":{"Device":"0x0000","IEEEAddr":"0x00124B0026B684E4","TotalDevices":19}}`
|
||||
// 1.a. `ZbStatus` - Show all devices shortaddr and names `{"ZbStatus1":[{"Device":"0x868E","Name":"Room"}...]}`
|
||||
// 1.b. `ZbStatus Room` - Show single device shortaddr and name `{"ZbStatus1":[{"Device":"0x868E","Name":"Room"}]}`
|
||||
// 2. `ZbStatus2 Room` - Show detailed information of device `{"ZbStatus2":[{"Device":"0x868E","Name":"Room","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":[1,242],"Config":["L01.2","O01"]}]}`
|
||||
void CmndZbStatus(void) {
|
||||
if (ZigbeeSerial) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||
String dump;
|
||||
|
||||
if (0 == XdrvMailbox.index) {
|
||||
if (0 == XdrvMailbox.index) { // Case 0
|
||||
dump = zigbee_devices.dumpCoordinator();
|
||||
} else {
|
||||
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, nullptr, XdrvMailbox.payload);
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
|
||||
// case 1.b and 2.
|
||||
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device);
|
||||
} else {
|
||||
if (XdrvMailbox.index >= 2) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
|
||||
// case 1.a
|
||||
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device_unk);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue