Merge pull request #16718 from s-hadinger/zigbee_alexa_multi_ep

Zigbee Alexa/Hue emulation, support multiple switches on separate endpoints
This commit is contained in:
s-hadinger 2022-10-04 13:28:58 +02:00 committed by GitHub
commit 9e2f18c0a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 109 deletions

View File

@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file.
## [12.1.1.4]
### Added
- Support for Shelly Plus 2PM using template ``{"NAME":"Shelly Plus 2PM PCB v0.1.9","GPIO":[320,0,0,0,32,192,0,0,225,224,0,0,0,0,193,0,0,0,0,0,0,608,640,3458,0,0,0,0,0,9472,0,4736,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,10000,10000,3350"}``
- Zigbee Alexa/Hue emulation, support multiple switches on separate endpoints
### Changed
### Fixed

View File

@ -415,12 +415,14 @@ const char HueConfigResponse_JSON[] PROGMEM = "\x3D\xA7\xB3\xAC\x6B\x3D\x87\x99\
/********************************************************************************************/
String GetHueDeviceId(uint16_t id)
String GetHueDeviceId(uint16_t id, uint8_t ep = 0)
{
char s[32];
String deviceid = WiFi.macAddress();
deviceid.toLowerCase();
snprintf(s, sizeof(s), "%s:%02x:11-%02x", deviceid.c_str(), (id >> 8) & 0xFF, id & 0xFF);
if (0x11 == ep) { ep = 0xFE; } // avoid collision with 0x11 which is used as default for `0`
if (0 == ep) { ep = 0x11; } // if ep is zero, revert to original value
snprintf(s, sizeof(s), "%s:%02x:%02X-%02x", deviceid.c_str(), (id >> 8) & 0xFF, ep, id & 0xFF);
return String(s); // 5c:cf:7f:13:9f:3d:00:11-01
}
@ -989,7 +991,7 @@ void HueLights(String *path_req)
uint16_t shortaddr;
device = DecodeLightIdZigbee(device_id, &shortaddr); // device is endpoint when in Zigbee mode
if (shortaddr) {
code = ZigbeeHandleHue(shortaddr, device_id, device, response);
code = ZigbeeHandleHue(shortaddr, device, device_id, device, response);
goto exit;
}
#endif // USE_ZIGBEE
@ -1022,7 +1024,7 @@ void HueLights(String *path_req)
uint16_t shortaddr;
device = DecodeLightIdZigbee(device_id, &shortaddr);
if (shortaddr) {
code = ZigbeeHueStatus(&response, shortaddr);
code = ZigbeeHueStatus(&response, shortaddr, device);
goto exit;
}
#endif // USE_ZIGBEE

View File

@ -1010,7 +1010,7 @@ public:
inline void setReachable(bool _reachable) { reachable = _reachable; }
inline bool getReachable(void) const { return reachable; }
inline bool getPower(uint8_t ep =0) const;
inline bool getPower(uint8_t ep = 0) const;
inline bool isRouter(void) const { return is_router; }
inline bool isCoordinator(void) const { return 0x0000 == shortaddr; }
@ -1066,8 +1066,8 @@ public:
void setPower(bool power_on, uint8_t ep = 0);
// If light, returns the number of channels, or 0xFF if unknown
int8_t getLightChannels(void) const {
const Z_Data_Light & light = data.find<Z_Data_Light>(0);
int8_t getLightChannels(uint8_t ep = 0) const {
const Z_Data_Light & light = data.find<Z_Data_Light>(ep);
if (&light != &z_data_unk) {
return light.getConfig();
} else {
@ -1075,7 +1075,16 @@ public:
}
}
void setLightChannels(int8_t channels);
int8_t getHueBulbtype(uint8_t ep = 0) const {
int8_t light_profile = getLightChannels(ep);
if (0x00 == (light_profile & 0xF0)) {
return (light_profile & 0x07);
} else {
// not a bulb
return -1;
}
}
void setLightChannels(int8_t channels, uint8_t ep);
static void setStringAttribute(char*& attr, const char * str);
};
@ -1097,9 +1106,10 @@ typedef enum Z_Def_Category {
Z_CAT_PERMIT_JOIN, // timer to signal the end of the PermitJoin period
// Below will clear based on device + cluster pair.
Z_CLEAR_DEVICE_CLUSTER,
Z_CAT_READ_CLUSTER,
// none for now
// Below will clear based on device + cluster + endpoint
Z_CLEAR_DEVICE_CLUSTER_ENDPOINT,
Z_CAT_READ_CLUSTER,
Z_CAT_EP_DESC, // read endpoint descriptor to gather clusters
Z_CAT_BIND, // send auto-binding to coordinator
Z_CAT_CONFIG_ATTR, // send a config attribute reporting request
@ -1183,10 +1193,10 @@ public:
int32_t deviceRestore(JsonParserObject json);
// Hue support
int8_t getHueBulbtype(uint16_t shortaddr) const ;
int8_t getHueBulbtype(uint16_t shortaddr, uint8_t ep = 0) const ;
void hideHueBulb(uint16_t shortaddr, bool hidden);
bool isHueBulbHidden(uint16_t shortaddr) const ;
Z_Data_Light & getLight(uint16_t shortaddr);
Z_Data_Light & getLight(uint16_t shortaddr, uint8_t ep = 0);
// device is reachable
void deviceWasReached(uint16_t shortaddr);

View File

@ -413,38 +413,47 @@ uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
}
// returns: dirty flag, did we change the value of the object
void Z_Device::setLightChannels(int8_t channels) {
if (channels >= 0) {
void Z_Device::setLightChannels(int8_t channels, uint8_t ep) {
if (channels >= 0) {
if (ep) { // if ep is not zero, the endpoint must exist
bool found = false;
for (uint32_t i = 0; i < endpoints_max; i++) {
if (ep == endpoints[i]) { found = true; break; }
}
if (!found) {
AddLog(LOG_LEVEL_INFO, D_LOG_ZIGBEE "cannot set light type to unknown ep=%i", ep);
return;
}
} else {
// if ep == 0, use first endpoint, or zero if no endpoint is known
ep = endpoints[0];
}
// retrieve of create light object
Z_Data_Light & light = data.get<Z_Data_Light>(0);
Z_Data_Light & light = data.get<Z_Data_Light>(ep);
if (channels != light.getConfig()) {
light.setConfig(channels);
zigbee_devices.dirty();
}
Z_Data_OnOff & onoff = data.get<Z_Data_OnOff>(0);
Z_Data_OnOff & onoff = data.get<Z_Data_OnOff>(ep);
(void)onoff;
} else {
// remove light / onoff object if any
for (auto & data_elt : data) {
if ((data_elt.getType() == Z_Data_Type::Z_Light) ||
(data_elt.getType() == Z_Data_Type::Z_OnOff)) {
// remove light object
data.remove(&data_elt);
zigbee_devices.dirty();
if (ep == 0 || data_elt.getEndpoint() == ep) { // if remove ep==0 then remove all definitions
// remove light object
data.remove(&data_elt);
zigbee_devices.dirty();
}
}
}
}
}
int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const {
int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr, uint8_t ep) const {
const Z_Device &device = findShortAddr(shortaddr);
int8_t light_profile = device.getLightChannels();
if (0x00 == (light_profile & 0xF0)) {
return (light_profile & 0x07);
} else {
// not a bulb
return -1;
}
return device.getHueBulbtype(ep);
}
void Z_Devices::hideHueBulb(uint16_t shortaddr, bool hidden) {
@ -1001,8 +1010,8 @@ int32_t Z_Devices::deviceRestore(JsonParserObject json) {
return 0;
}
Z_Data_Light & Z_Devices::getLight(uint16_t shortaddr) {
return getShortAddr(shortaddr).data.get<Z_Data_Light>();
Z_Data_Light & Z_Devices::getLight(uint16_t shortaddr, uint8_t ep) {
return getShortAddr(shortaddr).data.get<Z_Data_Light>(ep);
}
bool Z_Devices::isTuyaProtocol(uint16_t shortaddr, uint8_t ep) const {

View File

@ -23,7 +23,7 @@
// Add global functions for Hue Emulation
// idx: index in the list of zigbee_devices
void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, String *response) {
void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t ep, uint8_t local_light_subtype, String *response) {
static const char HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE[] PROGMEM =
"%s\"alert\":\"none\","
"\"effect\":\"none\","
@ -41,7 +41,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
uint32_t echo_gen = findEchoGeneration(); // 1 for 1st gen =+ Echo Dot 2nd gen, 2 for 2nd gen and above
const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
const Z_Data_Light & light = device.data.find<Z_Data_Light>();
const Z_Data_Light & light = device.data.find<Z_Data_Light>(ep);
if (&light != &z_data_unk) {
bri = light.getDimmer();
colormode = light.getColorMode();
@ -51,7 +51,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
x = light.getX();
y = light.getY();
}
power = device.getPower();
power = device.getPower(ep);
reachable = device.getReachable();
if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254
@ -89,29 +89,45 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
free(buf);
}
void HueLightStatus2Zigbee(uint16_t shortaddr, String *response)
void HueLightStatus2Zigbee(uint16_t shortaddr, uint8_t ep, String *response)
{
const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
const char * friendlyName = device.friendlyName;
const char * modelId = device.modelId;
const char * manufacturerId = device.manufacturerId;
char shortaddrname[8];
snprintf_P(shortaddrname, sizeof(shortaddrname), PSTR("0x%04X"), shortaddr);
char* buf = HueLightStatus2Generic((friendlyName) ? friendlyName : shortaddrname,
char name[32+4];
const char * local_friendfly_name = device.ep_names.getEPName(ep != 0 ? ep : device.endpoints[0]); // try endpoint, if `0` then found nothing
if (local_friendfly_name != nullptr) {
snprintf_P(name, sizeof(name), PSTR("%s"), local_friendfly_name);
} else if (friendlyName != nullptr) {
if (ep == 0 || ep == device.endpoints[0]) { // default endpoint, no suffix
snprintf_P(name, sizeof(name), PSTR("%s"), friendlyName);
} else { // no endpoint specific name so add suffix
snprintf_P(name, sizeof(name), PSTR("%s-%i"), friendlyName, ep);
}
} else {
if (ep == 0 || ep == device.endpoints[0]) { // default endpoint, no suffix
snprintf_P(name, sizeof(name), PSTR("0x%04X"), shortaddr);
} else {
snprintf_P(name, sizeof(name), PSTR("0x%04X-%i"), shortaddr, ep);
}
}
char* buf = HueLightStatus2Generic(name,
(modelId) ? modelId : PSTR("Unknown"),
(manufacturerId) ? manufacturerId : PSTR("Tasmota"),
GetHueDeviceId(shortaddr).c_str());
GetHueDeviceId(shortaddr, ep).c_str());
*response += buf;
free(buf);
}
int32_t ZigbeeHueStatus(String * response, uint16_t shortaddr) {
int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr);
int32_t ZigbeeHueStatus(String * response, uint16_t shortaddr, uint8_t ep) {
int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr, ep);
if (bulbtype >= 0) { // respond only if eligible
*response += F("{\"state\":");
HueLightStatus1Zigbee(shortaddr, zigbee_devices.getHueBulbtype(shortaddr), response);
HueLightStatus2Zigbee(shortaddr, response);
HueLightStatus1Zigbee(shortaddr, ep, zigbee_devices.getHueBulbtype(shortaddr, ep), response);
HueLightStatus2Zigbee(shortaddr, ep, response);
return 200;
} else {
return -3;
@ -120,40 +136,51 @@ int32_t ZigbeeHueStatus(String * response, uint16_t shortaddr) {
void ZigbeeCheckHue(String & response, bool * appending) {
uint32_t zigbee_num = zigbee_devices.devicesSize();
for (uint32_t i = 0; i < zigbee_num; i++) {
uint16_t shortaddr = zigbee_devices.devicesAt(i).shortaddr;
int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr);
if (bulbtype >= 0) {
// this bulb is advertized
if (*appending) { response += ","; }
response += "\"";
response += EncodeLightIdZigbee(0, shortaddr);
response += F("\":{\"state\":");
HueLightStatus1Zigbee(shortaddr, bulbtype, &response); // TODO
HueLightStatus2Zigbee(shortaddr, &response);
*appending = true;
for (uint32_t idx = 0; idx < zigbee_num; idx++) {
const Z_Device & device = zigbee_devices.devicesAt(idx);
uint16_t shortaddr = device.shortaddr;
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t ep = device.endpoints[i];
if (i > 0 && ep == 0) { break; }
int8_t bulbtype = device.getHueBulbtype(ep);
if (bulbtype >= 0) {
// this bulb is advertized
if (*appending) { response += ","; }
response += "\"";
response += EncodeLightIdZigbee((i == 0) ? 0 : ep, shortaddr);
response += F("\":{\"state\":");
HueLightStatus1Zigbee(shortaddr, ep, bulbtype, &response);
HueLightStatus2Zigbee(shortaddr, (i == 0) ? 0 : ep, &response); // if first endpoint ,announce as `0`
*appending = true;
}
}
}
}
void ZigbeeHueGroups(String * lights) {
uint32_t zigbee_num = zigbee_devices.devicesSize();
for (uint32_t i = 0; i < zigbee_num; i++) {
uint16_t shortaddr = zigbee_devices.devicesAt(i).shortaddr;
int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr);
for (uint32_t idx = 0; idx < zigbee_num; idx++) {
const Z_Device & device = zigbee_devices.devicesAt(idx);
uint16_t shortaddr = device.shortaddr;
if (bulbtype >= 0) {
*lights += ",\"";
*lights += EncodeLightIdZigbee(0, shortaddr);
*lights += "\"";
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t ep = device.endpoints[i];
if (i > 0 && ep == 0) { break; } // no more endpoints
int8_t bulbtype = device.getHueBulbtype(ep);
if (bulbtype >= 0) {
*lights += ",\"";
*lights += EncodeLightIdZigbee((i == 0) ? 0 : ep, shortaddr); // if first endpont, announce as `0`
*lights += "\"";
}
}
}
}
void ZigbeeSendHue(uint16_t shortaddr, uint16_t cluster, uint8_t cmd, const SBuffer & s) {
void ZigbeeSendHue(uint16_t shortaddr, uint8_t ep, uint16_t cluster, uint8_t cmd, const SBuffer & s) {
ZCLFrame zcl(s.len());
zcl.shortaddr = shortaddr;
zcl.dstendpoint = ep; // if set to `0`, we will use the first endpoint
zcl.cluster = cluster;
zcl.cmd = cmd;
zcl.clusterSpecific = true;
@ -165,69 +192,69 @@ void ZigbeeSendHue(uint16_t shortaddr, uint16_t cluster, uint8_t cmd, const SBuf
// Send commands
// Power On/Off
void ZigbeeHuePower(uint16_t shortaddr, bool power) {
void ZigbeeHuePower(uint16_t shortaddr, uint8_t ep, bool power) {
SBuffer s(0);
ZigbeeSendHue(shortaddr, 0x0006, power ? 1 : 0, s);
zigbee_devices.getShortAddr(shortaddr).setPower(power, 0);
ZigbeeSendHue(shortaddr, ep, 0x0006, power ? 1 : 0, s);
zigbee_devices.getShortAddr(shortaddr).setPower(power, ep);
}
// Dimmer
void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) {
void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t ep, uint8_t dimmer) {
if (dimmer > 0xFE) { dimmer = 0xFE; }
SBuffer s(4);
s.add8(dimmer);
s.add16(0x000A); // transition time = 1s
ZigbeeSendHue(shortaddr, 0x0008, 0x04, s);
zigbee_devices.getLight(shortaddr).setDimmer(dimmer);
ZigbeeSendHue(shortaddr, ep, 0x0008, 0x04, s);
zigbee_devices.getLight(shortaddr, ep).setDimmer(dimmer);
}
// CT
void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) {
void ZigbeeHueCT(uint16_t shortaddr, uint8_t ep, uint16_t ct) {
if (ct > 0xFEFF) { ct = 0xFEFF; }
SBuffer s(4);
s.add16(ct);
s.add16(0x000A); // transition time = 1s
ZigbeeSendHue(shortaddr, 0x0300, 0x0A, s);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
ZigbeeSendHue(shortaddr, ep, 0x0300, 0x0A, s);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr, ep);
light.setColorMode(2); // "ct"
light.setCT(ct);
}
// XY
void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) {
void ZigbeeHueXY(uint16_t shortaddr, uint8_t ep, uint16_t x, uint16_t y) {
if (x > 0xFEFF) { x = 0xFEFF; }
if (y > 0xFEFF) { y = 0xFEFF; }
SBuffer s(8);
s.add16(x);
s.add16(y);
s.add16(0x000A); // transition time = 1s
ZigbeeSendHue(shortaddr, 0x0300, 0x07, s);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
ZigbeeSendHue(shortaddr, ep, 0x0300, 0x07, s);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr, ep);
light.setColorMode(1); // "xy"
light.setX(x);
light.setY(y);
}
// HueSat
void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) {
void ZigbeeHueHS(uint16_t shortaddr, uint8_t ep, uint16_t hue, uint8_t sat) {
uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254);
if (sat > 0xFE) { sat = 0xFE; }
SBuffer s(4);
s.add8(hue8);
s.add8(sat);
s.add16(0);
ZigbeeSendHue(shortaddr, 0x0300, 0x06, s);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
ZigbeeSendHue(shortaddr, ep, 0x0300, 0x06, s);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr, ep);
light.setColorMode(0); // "hs"
light.setSat(sat);
light.setHue(hue);
}
int32_t ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, uint8_t endpoint, String &response) {
int32_t ZigbeeHandleHue(uint16_t shortaddr, uint8_t ep, uint32_t device_id, uint8_t endpoint, String &response) {
uint8_t bri, sat;
uint16_t ct, hue;
int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr);
int8_t bulbtype = zigbee_devices.getHueBulbtype(shortaddr, ep);
if (bulbtype < 0) { // respond only if eligible
response = F("{}");
return 200;
@ -259,9 +286,9 @@ int32_t ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, uint8_t endpoint
device_id, on ? PSTR("true") : PSTR("false"));
if (on) {
ZigbeeHuePower(shortaddr, 0x01);
ZigbeeHuePower(shortaddr, ep, 0x01);
} else {
ZigbeeHuePower(shortaddr, 0x00);
ZigbeeHuePower(shortaddr, ep, 0x00);
}
response += buf;
resp = true;
@ -280,7 +307,7 @@ int32_t ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, uint8_t endpoint
if (LST_SINGLE <= bulbtype) {
// extend bri value if set to max
if (254 <= bri) { bri = 255; }
ZigbeeHueDimmer(shortaddr, bri);
ZigbeeHueDimmer(shortaddr, ep, bri);
}
resp = true;
}
@ -304,7 +331,7 @@ int32_t ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, uint8_t endpoint
resp = true;
uint16_t xi = x * 65536.0f;
uint16_t yi = y * 65536.0f;
ZigbeeHueXY(shortaddr, xi, yi);
ZigbeeHueXY(shortaddr, ep, xi, yi);
}
bool huesat_changed = false;
@ -342,7 +369,7 @@ int32_t ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, uint8_t endpoint
huesat_changed = true;
}
if (huesat_changed) {
ZigbeeHueHS(shortaddr, hue, sat);
ZigbeeHueHS(shortaddr, ep, hue, sat);
}
resp = true;
}
@ -358,7 +385,7 @@ int32_t ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, uint8_t endpoint
device_id, PSTR("ct"), ct);
response += buf;
if ((LST_COLDWARM == bulbtype) || (LST_RGBW <= bulbtype)) {
ZigbeeHueCT(shortaddr, ct);
ZigbeeHueCT(shortaddr, ep, ct);
}
resp = true;
}

View File

@ -972,7 +972,7 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const SBuffer &buf) {
);
// query the state of the bulb (for Alexa)
uint32_t wait_ms = 2000; // wait for 2s
Z_Query_Bulb(nwkAddr, wait_ms);
Z_Query_Bulb(nwkAddr, 0xFF, wait_ms); // 0xFF means iterate on all endpoints
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
// Continue the discovery process and auto-binding only if the device was unknown or if PermitJoin is ongoing
@ -2118,24 +2118,39 @@ int32_t Z_Load_Data(uint8_t value) {
return 0; // continue
}
static void Z_Query_Bulb_inner(uint16_t shortaddr, uint8_t ep, uint32_t &wait_ms) {
const uint32_t inter_message_ms = 100; // wait 100ms between messages
if (ep == 0) { ep = zigbee_devices.findFirstEndpoint(shortaddr); }
if (ep) {
if (0 <= zigbee_devices.getHueBulbtype(shortaddr, ep)) {
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0006, ep, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0008, ep, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, ep, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, ep, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
wait_ms += 1000; // wait 1 second between devices
}
}
}
//
// Query the state of a bulb (light) if its type allows it
//
void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) {
const uint32_t inter_message_ms = 100; // wait 100ms between messages
if (0 <= zigbee_devices.getHueBulbtype(shortaddr)) {
uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
if (endpoint) { // send only if we know the endpoint
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0006, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0008, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
wait_ms += 1000; // wait 1 second between devices
// ep==0 is default endpoint
// ep==0xFF iterates on all endpoints
void Z_Query_Bulb(uint16_t shortaddr, uint8_t ep, uint32_t &wait_ms) {
if (ep != 0xFF) {
Z_Query_Bulb_inner(shortaddr, ep, wait_ms); // try a single endpoint
} else {
// iterate on all endpoints
const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
if (!device.valid()) { return; }
for (uint32_t i = 0; i < endpoints_max; i++) {
ep = device.endpoints[i];
if (ep == 0) { break; }
Z_Query_Bulb_inner(shortaddr, ep, wait_ms); // try a single endpoint
}
}
}
@ -2173,7 +2188,7 @@ int32_t Z_Query_Bulbs(uint8_t value) {
uint32_t wait_ms = 1000; // start with 1.0 s delay
for (uint32_t i = 0; i < zigbee_devices.devicesSize(); i++) {
const Z_Device &device = zigbee_devices.devicesAt(i);
Z_Query_Bulb(device.shortaddr, wait_ms);
Z_Query_Bulb(device.shortaddr, 0xFF, wait_ms); // 0xFF means all endpoints
}
}
return 0; // continue

View File

@ -1218,7 +1218,7 @@ void CmndZbModelId(void) {
// Specify, read or erase a Light type for Hue/Alexa integration
void CmndZbLight(void) {
// Syntax is:
// ZbLight <device_id>,<x> - assign a bulb type 0-5
// ZbLight <device_id>,<x> - assign a bulb type 0-5, or -1 to remove
// ZbLight <device_id> - display the current bulb type and status
//
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name
@ -1226,18 +1226,20 @@ void CmndZbLight(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
// check if parameters contain a comma ','
char *p;
strtok_r(XdrvMailbox.data, ", ", &p);
char *p = XdrvMailbox.data;
char *device_id = strsep(&p, ","); // zigbee identifier
char *bulbtype_str = strsep(&p, ","); // friendly name
int32_t ep = (p != nullptr) ? strtol(p, nullptr, 10) : 0; // get endpoint number, or `0` if none
// parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
Z_Device & device = zigbee_devices.parseDeviceFromName(device_id, nullptr, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
if (p) {
int8_t bulbtype = strtol(p, nullptr, 10);
if (bulbtype_str != nullptr) {
int8_t bulbtype = strtol(bulbtype_str, nullptr, 10);
if (bulbtype > 5) { bulbtype = 5; }
if (bulbtype < -1) { bulbtype = -1; }
device.setLightChannels(bulbtype);
device.setLightChannels(bulbtype, ep); // assign by default to first endpoint, or 0 if no endpoint known
}
Z_attribute_list attr_list;
device.jsonLightState(attr_list);
@ -1271,7 +1273,7 @@ void CmndZbOccupancy(void) {
// check if parameters contain a comma ','
char *p;
strtok_r(XdrvMailbox.data, ", ", &p);
strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id>
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered