Merge pull request #7944 from s-hadinger/zigbee_simpl

Change Zigbee simplification of devices probing, saving Flash and memory
This commit is contained in:
Theo Arends 2020-03-18 09:02:54 +01:00 committed by GitHub
commit 35f533994f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 92 additions and 269 deletions

View File

@ -8,6 +8,7 @@
- Add command ``HumOffset -10.0 .. 10.0`` to set global humidity sensor offset (#7934) - Add command ``HumOffset -10.0 .. 10.0`` to set global humidity sensor offset (#7934)
- Add Zigbee support for Hue emulation by Stefan Hadinger - Add Zigbee support for Hue emulation by Stefan Hadinger
- Add Dew Point to Temperature and Humidity sensors - Add Dew Point to Temperature and Humidity sensors
- Change Zigbee simplification of devices probing, saving Flash and memory
### 8.1.0.10 20200227 ### 8.1.0.10 20200227

View File

@ -388,9 +388,6 @@ enum ZCL_Global_Commands {
#define ZF(s) static const char ZS_ ## s[] PROGMEM = #s; #define ZF(s) static const char ZS_ ## s[] PROGMEM = #s;
#define Z(s) ZS_ ## s #define Z(s) ZS_ ## s
const uint16_t Z_ProfileIds[] PROGMEM = { 0x0104, 0x0109, 0xA10E, 0xC05E };
const char Z_ProfileNames[] PROGMEM = "ZigBee Home Automation|ZigBee Smart Energy|ZigBee Green Power|ZigBee Light Link";
typedef struct Z_StatusLine { typedef struct Z_StatusLine {
uint32_t status; // no need to use uint8_t since it uses 32 bits anyways uint32_t status; // no need to use uint8_t since it uses 32 bits anyways
const char * status_msg; const char * status_msg;

View File

@ -28,14 +28,14 @@ const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait f
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value); typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value);
const size_t endpoints_max = 8; // we limit to 8 endpoints
typedef struct Z_Device { typedef struct Z_Device {
uint64_t longaddr; // 0x00 means unspecified uint64_t longaddr; // 0x00 means unspecified
char * manufacturerId; char * manufacturerId;
char * modelId; char * modelId;
char * friendlyName; char * friendlyName;
std::vector<uint32_t> endpoints; // encoded as high 16 bits is endpoint, low 16 bits is ProfileId uint8_t endpoints[endpoints_max]; // static array to limit memory consumption, list of endpoints until 0x00 or end of array
std::vector<uint32_t> clusters_in; // encoded as high 16 bits is endpoint, low 16 bits is cluster number
std::vector<uint32_t> clusters_out; // encoded as high 16 bits is endpoint, low 16 bits is cluster number
// json buffer used for attribute reporting // json buffer used for attribute reporting
DynamicJsonBuffer *json_buffer; DynamicJsonBuffer *json_buffer;
JsonObject *json; JsonObject *json;
@ -81,7 +81,6 @@ typedef struct Z_Deferred {
// - shortaddr is unique if not null // - shortaddr is unique if not null
// - longaddr is unique if not null // - longaddr is unique if not null
// - shortaddr and longaddr cannot be both null // - shortaddr and longaddr cannot be both null
// - clusters_in and clusters_out containt only endpoints listed in endpoints
class Z_Devices { class Z_Devices {
public: public:
Z_Devices() {}; Z_Devices() {};
@ -98,20 +97,17 @@ public:
uint64_t getDeviceLongAddr(uint16_t shortaddr) const; uint64_t getDeviceLongAddr(uint16_t shortaddr) const;
uint8_t findFirstEndpoint(uint16_t shortaddr) const;
// Add new device, provide ShortAddr and optional longAddr // Add new device, provide ShortAddr and optional longAddr
// If it is already registered, update information, otherwise create the entry // If it is already registered, update information, otherwise create the entry
void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0); void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0);
// Add an endpoint to a device // Add an endpoint to a device
void addEndoint(uint16_t shortaddr, uint8_t endpoint); void addEndpoint(uint16_t shortaddr, uint8_t endpoint);
// Add endpoint profile
void addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId);
// Add cluster // Add cluster
void addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out); void addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster);
uint8_t findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster);
void setManufId(uint16_t shortaddr, const char * str); void setManufId(uint16_t shortaddr, const char * str);
void setModelId(uint16_t shortaddr, const char * str); void setModelId(uint16_t shortaddr, const char * str);
@ -175,8 +171,6 @@ public:
private: private:
std::vector<Z_Device*> _devices = {}; std::vector<Z_Device*> _devices = {};
std::vector<Z_Deferred> _deferred = {}; // list of deferred calls std::vector<Z_Deferred> _deferred = {}; // list of deferred calls
// std::vector<Z_Device> _devices = std::vector<Z_Device>(4);
// std::vector<Z_Deferred> _deferred = std::vector<Z_Deferred>(4); // list of deferred calls
uint32_t _saveTimer = 0; uint32_t _saveTimer = 0;
uint8_t _seqNumber = 0; // global seqNumber if device is unknown uint8_t _seqNumber = 0; // global seqNumber if device is unknown
@ -186,9 +180,6 @@ private:
template < typename T> template < typename T>
static int32_t findEndpointInVector(const std::vector<T> & vecOfElements, uint8_t element); static int32_t findEndpointInVector(const std::vector<T> & vecOfElements, uint8_t element);
// find the first endpoint match for a cluster
static int32_t findClusterEndpoint(const std::vector<uint32_t> & vecOfElements, uint16_t element);
Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist Z_Device & getShortAddr(uint16_t shortaddr); // find Device from shortAddr, creates it if does not exist
const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; // find Device from shortAddr, creates it if does not exist const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; // find Device from shortAddr, creates it if does not exist
Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist Z_Device & getLongAddr(uint64_t longaddr); // find Device from shortAddr, creates it if does not exist
@ -226,31 +217,13 @@ int32_t Z_Devices::findEndpointInVector(const std::vector<T> & vecOfElements, u
int32_t found = 0; int32_t found = 0;
for (auto &elem : vecOfElements) { for (auto &elem : vecOfElements) {
if ( ((elem >> 16) & 0xFF) == element) { return found; } if (elem == element) { return found; }
found++; found++;
} }
return -1; return -1;
} }
//
// Find the first endpoint match for a cluster, whether in or out
// Clusters are stored in the format 0x00EECCCC (EE=endpoint, CCCC=cluster number)
// In:
// _devices.clusters_in or _devices.clusters_out
// cluster number looked for
// Out:
// Index of found Endpoint_Cluster number, or -1 if not found
//
int32_t Z_Devices::findClusterEndpoint(const std::vector<uint32_t> & vecOfElements, uint16_t cluster) {
int32_t found = 0;
for (auto &elem : vecOfElements) {
if ((elem & 0xFFFF) == cluster) { return found; }
found++;
}
return -1;
}
// //
// Create a new Z_Device entry in _devices. Only to be called if you are sure that no // Create a new Z_Device entry in _devices. Only to be called if you are sure that no
// entry with same shortaddr or longaddr exists. // entry with same shortaddr or longaddr exists.
@ -263,9 +236,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
nullptr, // ManufId nullptr, // ManufId
nullptr, // DeviceId nullptr, // DeviceId
nullptr, // FriendlyName nullptr, // FriendlyName
std::vector<uint32_t>(), // at least one endpoint { 0, 0, 0, 0, 0, 0, 0, 0 }, // endpoints
std::vector<uint32_t>(), // try not to allocate if not needed
std::vector<uint32_t>(), // try not to allocate if not needed
nullptr, nullptr, nullptr, nullptr,
shortaddr, shortaddr,
0, // seqNumber 0, // seqNumber
@ -293,13 +264,6 @@ void Z_Devices::freeDeviceEntry(Z_Device *device) {
free(device); free(device);
} }
void Z_Devices::shrinkToFit(uint16_t shortaddr) {
Z_Device & device = getShortAddr(shortaddr);
device.endpoints.shrink_to_fit();
device.clusters_in.shrink_to_fit();
device.clusters_out.shrink_to_fit();
}
// //
// Scan all devices to find a corresponding shortaddr // Scan all devices to find a corresponding shortaddr
// Looks info device.shortaddr entry // Looks info device.shortaddr entry
@ -489,67 +453,31 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
// //
// Add an endpoint to a shortaddr // Add an endpoint to a shortaddr
// //
void Z_Devices::addEndoint(uint16_t shortaddr, uint8_t endpoint) { void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) {
if (!shortaddr) { return; } if (!shortaddr) { return; }
if (0x00 == endpoint) { return; } if (0x00 == endpoint) { return; }
uint32_t ep_profile = (endpoint << 16);
Z_Device &device = getShortAddr(shortaddr); Z_Device &device = getShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found if (&device == nullptr) { return; } // don't crash if not found
if (findEndpointInVector(device.endpoints, endpoint) < 0) {
device.endpoints.push_back(ep_profile);
dirty();
}
}
void Z_Devices::addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId) { for (uint32_t i = 0; i < endpoints_max; i++) {
if (!shortaddr) { return; } if (endpoint == device.endpoints[i]) {
if (0x00 == endpoint) { return; } return; // endpoint already there
uint32_t ep_profile = (endpoint << 16) | profileId; }
Z_Device &device = getShortAddr(shortaddr); if (0 == device.endpoints[i]) {
if (&device == nullptr) { return; } // don't crash if not found device.endpoints[i] = endpoint;
int32_t found = findEndpointInVector(device.endpoints, endpoint);
if (found < 0) {
device.endpoints.push_back(ep_profile);
dirty();
} else {
if (device.endpoints[found] != ep_profile) {
device.endpoints[found] = ep_profile;
dirty(); dirty();
return;
} }
} }
} }
void Z_Devices::addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out) { // Find the first endpoint of the device
if (!shortaddr) { return; } uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const {
Z_Device & device = getShortAddr(shortaddr); int32_t found = findShortAddr(shortaddr);
if (&device == nullptr) { return; } // don't crash if not found if (found < 0) return 0; // avoid creating an entry if the device was never seen
uint32_t ep_cluster = (endpoint << 16) | cluster; const Z_Device &device = devicesAt(found);
if (!out) {
if (!findInVector(device.clusters_in, ep_cluster)) {
device.clusters_in.push_back(ep_cluster);
dirty();
}
} else { // out
if (!findInVector(device.clusters_out, ep_cluster)) {
device.clusters_out.push_back(ep_cluster);
dirty();
}
}
}
// Look for the best endpoint match to send a command for a specific Cluster ID return device.endpoints[0]; // returns 0x00 if no endpoint
// return 0x00 if none found
uint8_t Z_Devices::findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster){
int32_t short_found = findShortAddr(shortaddr);
if (short_found < 0) return 0; // avoid creating an entry if the device was never seen
Z_Device &device = getShortAddr(shortaddr);
if (&device == nullptr) { return 0; } // don't crash if not found
int32_t found = findClusterEndpoint(device.clusters_in, cluster);
if (found >= 0) {
return (device.clusters_in[found] >> 16) & 0xFF;
} else {
return 0;
}
} }
void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { void Z_Devices::setManufId(uint16_t shortaddr, const char * str) {
@ -1069,58 +997,13 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
if (device.manufacturerId) { if (device.manufacturerId) {
dev[F("Manufacturer")] = device.manufacturerId; dev[F("Manufacturer")] = device.manufacturerId;
} }
} JsonArray& dev_endpoints = dev.createNestedArray(F("Endpoints"));
for (uint32_t i = 0; i < endpoints_max; i++) {
// If dump_mode == 2, dump a lot more details uint8_t endpoint = device.endpoints[i];
if (3 <= dump_mode) { if (0x00 == endpoint) { break; }
JsonObject& dev_endpoints = dev.createNestedObject(F("Endpoints"));
for (std::vector<uint32_t>::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) {
uint32_t ep_profile = *ite;
uint8_t endpoint = (ep_profile >> 16) & 0xFF;
uint16_t profileId = ep_profile & 0xFFFF;
snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint);
JsonObject& ep = dev_endpoints.createNestedObject(hex); dev_endpoints.add(hex);
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), profileId);
ep[F("ProfileId")] = hex;
int32_t found = -1;
for (uint32_t i = 0; i < sizeof(Z_ProfileIds) / sizeof(Z_ProfileIds[0]); i++) {
if (pgm_read_word(&Z_ProfileIds[i]) == profileId) {
found = i;
break;
}
}
if (found > 0) {
GetTextIndexed(hex, sizeof(hex), found, Z_ProfileNames);
ep[F("ProfileIdName")] = hex;
}
ep.createNestedArray(F("ClustersIn"));
ep.createNestedArray(F("ClustersOut"));
}
for (std::vector<uint32_t>::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) {
uint16_t cluster = *itc & 0xFFFF;
uint8_t endpoint = (*itc >> 16) & 0xFF;
snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint);
JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersIn")];
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster);
cluster_arr.add(hex);
}
for (std::vector<uint32_t>::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) {
uint16_t cluster = *itc & 0xFFFF;
uint8_t endpoint = (*itc >> 16) & 0xFF;
snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint);
JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersOut")];
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster);
cluster_arr.add(hex);
} }
} }
} }

View File

@ -108,37 +108,25 @@ class SBuffer hibernateDevice(const struct Z_Device &device) {
buf.add8(0x00); // overall length, will be updated later buf.add8(0x00); // overall length, will be updated later
buf.add16(device.shortaddr); buf.add16(device.shortaddr);
buf.add64(device.longaddr); buf.add64(device.longaddr);
uint32_t endpoints = device.endpoints.size();
if (endpoints > 254) { endpoints = 254; } uint32_t endpoints_count = 0;
buf.add8(endpoints); for (endpoints_count = 0; endpoints_count < endpoints_max; endpoints_count++) {
if (0x00 == device.endpoints[endpoints_count]) { break; }
}
buf.add8(endpoints_count);
// iterate on endpoints // iterate on endpoints
for (std::vector<uint32_t>::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) { for (uint32_t i = 0; i < endpoints_max; i++) {
uint32_t ep_profile = *ite; uint8_t endpoint = device.endpoints[i];
uint8_t endpoint = (ep_profile >> 16) & 0xFF; if (0x00 == endpoint) { break; } // stop
uint16_t profileId = ep_profile & 0xFFFF;
buf.add8(endpoint); buf.add8(endpoint);
buf.add16(profileId); buf.add16(0x0000); // profile_id, not used anymore
for (std::vector<uint32_t>::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) {
uint16_t cluster = *itc & 0xFFFF;
uint8_t c_endpoint = (*itc >> 16) & 0xFF;
if (endpoint == c_endpoint) { // removed clusters_in
uint8_t clusterCode = toClusterCode(cluster);
if (0xFF != clusterCode) { buf.add8(clusterCode); }
}
}
buf.add8(0xFF); // end of endpoint marker buf.add8(0xFF); // end of endpoint marker
for (std::vector<uint32_t>::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) { // no more storage of clusters_out
uint16_t cluster = *itc & 0xFFFF;
uint8_t c_endpoint = (*itc >> 16) & 0xFF;
if (endpoint == c_endpoint) {
uint8_t clusterCode = toClusterCode(cluster);
if (0xFF != clusterCode) { buf.add8(clusterCode); }
}
}
buf.add8(0xFF); // end of endpoint marker buf.add8(0xFF); // end of endpoint marker
} }
@ -235,22 +223,21 @@ void hydrateDevices(const SBuffer &buf) {
for (uint32_t j = 0; j < endpoints; j++) { for (uint32_t j = 0; j < endpoints; j++) {
uint8_t ep = buf_d.get8(d++); uint8_t ep = buf_d.get8(d++);
uint16_t ep_profile = buf_d.get16(d); d += 2; uint16_t ep_profile = buf_d.get16(d); d += 2;
zigbee_devices.addEndointProfile(shortaddr, ep, ep_profile); zigbee_devices.addEndpoint(shortaddr, ep);
// in clusters // in clusters
while (d < dev_record_len) { // safe guard against overflow while (d < dev_record_len) { // safe guard against overflow
uint8_t ep_cluster = buf_d.get8(d++); uint8_t ep_cluster = buf_d.get8(d++);
if (0xFF == ep_cluster) { break; } // end of block if (0xFF == ep_cluster) { break; } // end of block
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), false); // ignore
} }
// out clusters // out clusters
while (d < dev_record_len) { // safe guard against overflow while (d < dev_record_len) { // safe guard against overflow
uint8_t ep_cluster = buf_d.get8(d++); uint8_t ep_cluster = buf_d.get8(d++);
if (0xFF == ep_cluster) { break; } // end of block if (0xFF == ep_cluster) { break; } // end of block
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), true); // ignore
} }
} }
zigbee_devices.shrinkToFit(shortaddr);
//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device 0x%04X Memory3.shrink = %d"), shortaddr, ESP.getFreeHeap()); //AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device 0x%04X Memory3.shrink = %d"), shortaddr, ESP.getFreeHeap());
// parse 3 strings // parse 3 strings

View File

@ -549,6 +549,7 @@ ZF(ZCLVersion) ZF(AppVersion) ZF(StackVersion) ZF(HWVersion) ZF(Manufacturer) ZF
ZF(DateCode) ZF(PowerSource) ZF(SWBuildID) ZF(Power) ZF(SwitchType) ZF(Dimmer) ZF(DateCode) ZF(PowerSource) ZF(SWBuildID) ZF(Power) ZF(SwitchType) ZF(Dimmer)
ZF(MainsVoltage) ZF(MainsFrequency) ZF(BatteryVoltage) ZF(BatteryPercentage) ZF(MainsVoltage) ZF(MainsFrequency) ZF(BatteryVoltage) ZF(BatteryPercentage)
ZF(CurrentTemperature) ZF(MinTempExperienced) ZF(MaxTempExperienced) ZF(OverTempTotalDwell) ZF(CurrentTemperature) ZF(MinTempExperienced) ZF(MaxTempExperienced) ZF(OverTempTotalDwell)
ZF(SceneCount) ZF(CurrentScene) ZF(CurrentGroup) ZF(SceneValid)
ZF(AlarmCount) ZF(Time) ZF(TimeStatus) ZF(TimeZone) ZF(DstStart) ZF(DstEnd) ZF(AlarmCount) ZF(Time) ZF(TimeStatus) ZF(TimeZone) ZF(DstStart) ZF(DstEnd)
ZF(DstShift) ZF(StandardTime) ZF(LocalTime) ZF(LastSetTime) ZF(ValidUntilTime) ZF(DstShift) ZF(StandardTime) ZF(LocalTime) ZF(LastSetTime) ZF(ValidUntilTime)
@ -660,6 +661,13 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ 0x0002, 0x0002, Z(MaxTempExperienced), &Z_Copy }, { 0x0002, 0x0002, Z(MaxTempExperienced), &Z_Copy },
{ 0x0002, 0x0003, Z(OverTempTotalDwell), &Z_Copy }, { 0x0002, 0x0003, Z(OverTempTotalDwell), &Z_Copy },
// Scenes cluster
{ 0x0005, 0x0000, Z(SceneCount), &Z_Copy },
{ 0x0005, 0x0001, Z(CurrentScene), &Z_Copy },
{ 0x0005, 0x0002, Z(CurrentGroup), &Z_Copy },
{ 0x0005, 0x0003, Z(SceneValid), &Z_Copy },
//{ 0x0005, 0x0004, Z(NameSupport), &Z_Copy },
// On/off cluster // On/off cluster
{ 0x0006, 0x0000, Z(Power), &Z_Copy }, { 0x0006, 0x0000, Z(Power), &Z_Copy },
{ 0x0006, 0x8000, Z(Power), &Z_Copy }, // See 7280 { 0x0006, 0x8000, Z(Power), &Z_Copy }, // See 7280
@ -933,7 +941,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
{ 0x0405, 0x0000, Z(Humidity), &Z_FloatDiv100 }, // Humidity { 0x0405, 0x0000, Z(Humidity), &Z_FloatDiv100 }, // Humidity
{ 0x0405, 0x0001, Z(HumidityMinMeasuredValue), &Z_Copy }, // { 0x0405, 0x0001, Z(HumidityMinMeasuredValue), &Z_Copy }, //
{ 0x0405, 0x0002, Z(HumidityMaxMeasuredValue), &Z_Copy }, // { 0x0405, 0x0002, Z(HumidityMaxMeasuredValue), &Z_Copy }, //
{ 0x0405, 0x0003, Z(HumidityTolerance), &Z_Copy }, // { 0x0405, 0x0003, "HumidityTolerance", &Z_Copy }, //
{ 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values { 0x0405, 0xFFFF, nullptr, &Z_Remove }, // Remove all other values
// Occupancy Sensing cluster // Occupancy Sensing cluster

View File

@ -127,7 +127,7 @@ const Z_CommandConverter Z_Commands[] PROGMEM = {
{ Z(RemoveScene), 0x0005, 0x02, 0x82, Z(xxyyyyzz) }, // xx = status, yyyy = group id, zz = scene id { Z(RemoveScene), 0x0005, 0x02, 0x82, Z(xxyyyyzz) }, // xx = status, yyyy = group id, zz = scene id
{ Z(RemoveAllScenes),0x0005, 0x03, 0x82, Z(xxyyyy) }, // xx = status, yyyy = group id { Z(RemoveAllScenes),0x0005, 0x03, 0x82, Z(xxyyyy) }, // xx = status, yyyy = group id
{ Z(StoreScene), 0x0005, 0x04, 0x82, Z(xxyyyyzz) }, // xx = status, yyyy = group id, zz = scene id { Z(StoreScene), 0x0005, 0x04, 0x82, Z(xxyyyyzz) }, // xx = status, yyyy = group id, zz = scene id
{ Z(GetSceneMembership),0x0005, 0x06, 0x82, Z() }, // specific { Z(GetSceneMembership),0x0005, 0x06, 0x82,Z(xxyyzzzz) }, // specific
}; };
#define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian #define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian
@ -289,7 +289,7 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin
if (z_cat >= 0) { if (z_cat >= 0) {
uint8_t endpoint = 0; uint8_t endpoint = 0;
if (!groupaddr) { if (!groupaddr) {
endpoint = zigbee_devices.findClusterEndpointIn(shortaddr, cluster); endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
} }
if ((endpoint) || (groupaddr)) { // send only if we know the endpoint if ((endpoint) || (groupaddr)) { // send only if we know the endpoint
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback); zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback);
@ -395,7 +395,33 @@ void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, boo
for (uint32_t i = 0; i < xyz.y; i++) { for (uint32_t i = 0; i < xyz.y; i++) {
arr.add(payload.get16(2 + 2*i)); arr.add(payload.get16(2 + 2*i));
} }
//arr.add(xyz.z); } else if ((cluster == 0x0005) && ((cmd == 0x00) || (cmd == 0x02) || (cmd == 0x03))) {
// AddScene or RemoveScene or StoreScene
json[command_name2 + F("Status")] = xyz.x;
json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
json[F("GroupId")] = xyz.y;
json[F("SceneId")] = xyz.z;
} else if ((cluster == 0x0005) && (cmd == 0x01)) {
// ViewScene
json[command_name2 + F("Status")] = xyz.x;
json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
json[F("GroupId")] = xyz.y;
json[F("SceneId")] = xyz.z;
String scene_payload = json[attrid_str];
json[F("ScenePayload")] = scene_payload.substring(8); // remove first 8 characters
} else if ((cluster == 0x0005) && (cmd == 0x03)) {
// RemoveAllScenes
json[command_name2 + F("Status")] = xyz.x;
json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
json[F("GroupId")] = xyz.y;
} else if ((cluster == 0x0005) && (cmd == 0x06)) {
// GetSceneMembership
json[command_name2 + F("Status")] = xyz.x;
json[command_name2 + F("StatusMsg")] = getZigbeeStatusMessage(xyz.x);
json[F("Capacity")] = xyz.y;
json[F("GroupId")] = xyz.z;
String scene_payload = json[attrid_str];
json[F("ScenePayload")] = scene_payload.substring(8); // remove first 8 characters
} }
} else { } else {
if (0 == xyz.x_type) { if (0 == xyz.x_type) {

View File

@ -190,20 +190,6 @@ void Z_SendActiveEpReq(uint16_t shortaddr) {
Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) };
ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq));
// uint8_t NodeDescReq[] = { Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ,
// Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) };
//ZigbeeZNPSend(NodeDescReq, sizeof(NodeDescReq)); Not sure this is useful
}
// Send ZDO_SIMPLE_DESC_REQ to get full list of supported Clusters for a specific endpoint
void Z_SendSimpleDescReq(uint16_t shortaddr, uint8_t endpoint) {
uint8_t SimpleDescReq[] = { Z_SREQ | Z_ZDO, ZDO_SIMPLE_DESC_REQ, // 2504
Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr),
endpoint };
ZigbeeZNPSend(SimpleDescReq, sizeof(SimpleDescReq));
} }
const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" }; const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" };
@ -248,13 +234,8 @@ int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) {
uint8_t activeEpCount = buf.get8(7); uint8_t activeEpCount = buf.get8(7);
uint8_t* activeEpList = (uint8_t*) buf.charptr(8); uint8_t* activeEpList = (uint8_t*) buf.charptr(8);
for (uint32_t i = 0; i < activeEpCount; i++) { for (uint32_t i = 0; i < activeEpCount; i++) {
zigbee_devices.addEndoint(nwkAddr, activeEpList[i]); zigbee_devices.addEndpoint(nwkAddr, activeEpList[i]);
}
for (uint32_t i = 0; i < activeEpCount; i++) {
Z_SendSimpleDescReq(nwkAddr, activeEpList[i]);
} }
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
@ -292,56 +273,6 @@ void Z_SendAFInfoRequest(uint16_t shortaddr, uint8_t endpoint, uint16_t clusteri
ZigbeeZNPSend(buf.getBuffer(), buf.len()); ZigbeeZNPSend(buf.getBuffer(), buf.len());
} }
int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) {
// Received ZDO_SIMPLE_DESC_RSP
Z_ShortAddress srcAddr = buf.get16(2);
uint8_t status = buf.get8(4);
Z_ShortAddress nwkAddr = buf.get16(5);
uint8_t lenDescriptor = buf.get8(7);
uint8_t endpoint = buf.get8(8);
uint16_t profileId = buf.get16(9); // The profile Id for this endpoint.
uint16_t deviceId = buf.get16(11); // The Device Description Id for this endpoint.
uint8_t deviceVersion = buf.get8(13); // 0 Version 1.00
uint8_t numInCluster = buf.get8(14);
uint8_t numOutCluster = buf.get8(15 + numInCluster*2);
if (0 == status) {
zigbee_devices.addEndointProfile(nwkAddr, endpoint, profileId);
for (uint32_t i = 0; i < numInCluster; i++) {
zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(15 + i*2), false);
}
for (uint32_t i = 0; i < numOutCluster; i++) {
zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(16 + numInCluster*2 + i*2), true);
}
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
"\"Status\":%d,\"Endpoint\":\"0x%02X\""
",\"ProfileId\":\"0x%04X\",\"DeviceId\":\"0x%04X\",\"DeviceVersion\":%d"
"\"InClusters\":["),
ZIGBEE_STATUS_SIMPLE_DESC, endpoint,
profileId, deviceId, deviceVersion);
for (uint32_t i = 0; i < numInCluster; i++) {
if (i > 0) { ResponseAppend_P(PSTR(",")); }
ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(15 + i*2));
}
ResponseAppend_P(PSTR("],\"OutClusters\":["));
for (uint32_t i = 0; i < numOutCluster; i++) {
if (i > 0) { ResponseAppend_P(PSTR(",")); }
ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(16 + numInCluster*2 + i*2));
}
ResponseAppend_P(PSTR("]}}"));
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
XdrvRulesProcess();
uint8_t cluster = zigbee_devices.findClusterEndpointIn(nwkAddr, 0x0000);
if (cluster) {
Z_SendAFInfoRequest(nwkAddr, cluster, 0x0000, 0x01); // TODO, do we need tarnsacId counter?
}
}
return -1;
}
int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
uint8_t status = buf.get8(2); uint8_t status = buf.get8(2);
Z_IEEEAddress ieeeAddr = buf.get64(3); Z_IEEEAddress ieeeAddr = buf.get64(3);
@ -353,14 +284,6 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
zigbee_devices.updateDevice(nwkAddr, ieeeAddr); zigbee_devices.updateDevice(nwkAddr, ieeeAddr);
char hex[20]; char hex[20];
Uint64toHex(ieeeAddr, hex, 64); Uint64toHex(ieeeAddr, hex, 64);
// Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
// "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\""
// "}}"),
// ZIGBEE_STATUS_DEVICE_IEEE, hex, nwkAddr
// );
// MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
// XdrvRulesProcess();
// Ping response // Ping response
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
if (friendlyName) { if (friendlyName) {
@ -417,7 +340,9 @@ int32_t Z_DataConfirm(int32_t res, const class SBuffer &buf) {
char status_message[32]; char status_message[32];
if (status) { // only report errors if (status) { // only report errors
strncpy_P(status_message, (const char*) getZigbeeStatusMessage(status), sizeof(status_message)); const char * statm = (const char*) getZigbeeStatusMessage(status);
if (nullptr == statm) { statm = PSTR(""); }
strncpy_P(status_message, statm, sizeof(status_message));
status_message[sizeof(status_message)-1] = 0; // truncate if needed, strlcpy is safer but strlcpy_P does not exist status_message[sizeof(status_message)-1] = 0; // truncate if needed, strlcpy is safer but strlcpy_P does not exist
Response_P(PSTR("{\"" D_JSON_ZIGBEE_CONFIRM "\":{\"" D_CMND_ZIGBEE_ENDPOINT "\":%d" Response_P(PSTR("{\"" D_JSON_ZIGBEE_CONFIRM "\":{\"" D_CMND_ZIGBEE_ENDPOINT "\":%d"
@ -442,7 +367,7 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
char hex[20]; char hex[20];
Uint64toHex(ieeeAddr, hex, 64); Uint64toHex(ieeeAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
"\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"),
ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr, ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr,
(capabilities & 0x04) ? "true" : "false", (capabilities & 0x04) ? "true" : "false",
@ -467,7 +392,7 @@ int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
char hex[20]; char hex[20];
Uint64toHex(ieeeAddr, hex, 64); Uint64toHex(ieeeAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
"\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
",\"ParentNetwork\":\"0x%04X\"}}"), ",\"ParentNetwork\":\"0x%04X\"}}"),
ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw
); );
@ -600,7 +525,6 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus }, { AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus },
{ AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc },
{ AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp },
{ AREQ_ZDO_SIMPLEDESCRSP, &Z_ReceiveSimpleDesc },
{ AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr }, { AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr },
{ AREQ_ZDO_BIND_RSP, &Z_BindRsp }, { AREQ_ZDO_BIND_RSP, &Z_BindRsp },
}; };
@ -635,24 +559,21 @@ int32_t Z_Query_Bulbs(uint8_t value) {
if (0 <= device.bulbtype) { if (0 <= device.bulbtype) {
uint16_t cluster; uint16_t cluster;
uint8_t endpoint; uint8_t endpoint = zigbee_devices.findFirstEndpoint(device.shortaddr);
cluster = 0x0006; cluster = 0x0006;
endpoint = zigbee_devices.findClusterEndpointIn(device.shortaddr, cluster);
if (endpoint) { // send only if we know the endpoint if (endpoint) { // send only if we know the endpoint
zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms; wait_ms += inter_message_ms;
} }
cluster = 0x0008; cluster = 0x0008;
endpoint = zigbee_devices.findClusterEndpointIn(device.shortaddr, cluster);
if (endpoint) { // send only if we know the endpoint if (endpoint) { // send only if we know the endpoint
zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms; wait_ms += inter_message_ms;
} }
cluster = 0x0300; cluster = 0x0300;
endpoint = zigbee_devices.findClusterEndpointIn(device.shortaddr, cluster);
if (endpoint) { // send only if we know the endpoint if (endpoint) { // send only if we know the endpoint
zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback); zigbee_devices.setTimer(device.shortaddr, 0 /* groupaddr */, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms; wait_ms += inter_message_ms;

View File

@ -394,7 +394,7 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
if ((0 == endpoint) && (shortaddr)) { if ((0 == endpoint) && (shortaddr)) {
// endpoint is not specified, let's try to find it from shortAddr, unless it's a group address // endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
endpoint = zigbee_devices.findClusterEndpointIn(shortaddr, cluster); endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
} }
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
@ -847,7 +847,7 @@ void CmndZbRead(void) {
} }
if ((0 == endpoint) && (device)) { // try to compute the endpoint if ((0 == endpoint) && (device)) { // try to compute the endpoint
endpoint = zigbee_devices.findClusterEndpointIn(device, cluster); endpoint = zigbee_devices.findFirstEndpoint(device);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
} }
if (groupaddr) { if (groupaddr) {