Merge pull request #9701 from s-hadinger/zigbee_nov_1

Zigbee refactoring
This commit is contained in:
s-hadinger 2020-11-01 19:45:45 +01:00 committed by GitHub
commit 1d5b74e3ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 196 additions and 220 deletions

View File

@ -543,6 +543,9 @@ public:
inline bool getReachable(void) const { return 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 void setLQI(uint8_t _lqi) { lqi = _lqi; }
inline void setBatteryPercent(uint8_t bp) { batterypercent = bp; }
// Add an endpoint to a device // Add an endpoint to a device
bool addEndpoint(uint8_t endpoint); bool addEndpoint(uint8_t endpoint);
void clearEndpoints(void); void clearEndpoints(void);
@ -552,6 +555,8 @@ public:
void setModelId(const char * str); void setModelId(const char * str);
void setFriendlyName(const char * str); void setFriendlyName(const char * str);
void setLastSeenNow(void);
// dump device attributes to ZbData // dump device attributes to ZbData
void toAttributes(Z_attribute_list & attr_list) const; void toAttributes(Z_attribute_list & attr_list) const;
@ -568,30 +573,8 @@ public:
} }
} }
// returns: dirty flag, did we change the value of the object void setLightChannels(int8_t channels);
bool setLightChannels(int8_t channels) {
bool dirty = false;
if (channels >= 0) {
// retrieve of create light object
Z_Data_Light & light = data.get<Z_Data_Light>(0);
if (channels != light.getConfig()) {
light.setConfig(channels);
dirty = true;
}
Z_Data_OnOff & onoff = data.get<Z_Data_OnOff>(0);
} 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);
dirty = true;
}
}
}
return dirty;
}
protected: protected:
static void setStringAttribute(char*& attr, const char * str); static void setStringAttribute(char*& attr, const char * str);
@ -655,9 +638,9 @@ public:
// - 0x0000 = not found // - 0x0000 = not found
// - BAD_SHORTADDR = bad parameter // - BAD_SHORTADDR = bad parameter
// - 0x<shortaddr> = the device's short address // - 0x<shortaddr> = the device's short address
uint16_t isKnownLongAddr(uint64_t longaddr) const; Z_Device & isKnownLongAddrDevice(uint64_t longaddr) const;
uint16_t isKnownIndex(uint32_t index) const; Z_Device & isKnownIndexDevice(uint32_t index) const;
uint16_t isKnownFriendlyName(const char * name) const; Z_Device & isKnownFriendlyNameDevice(const char * name) const;
Z_Device & findShortAddr(uint16_t shortaddr); Z_Device & findShortAddr(uint16_t shortaddr);
const Z_Device & findShortAddr(uint16_t shortaddr) const; const Z_Device & findShortAddr(uint16_t shortaddr) const;
@ -666,9 +649,7 @@ public:
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
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
// check if a device was found or if it's the fallback device // check if a device was found or if it's the fallback device
inline bool foundDevice(const Z_Device & device) const { inline bool foundDevice(const Z_Device & device) const { return device.valid(); }
return (&device != &device_unk);
}
int32_t findFriendlyName(const char * name) const; int32_t findFriendlyName(const char * name) const;
uint64_t getDeviceLongAddr(uint16_t shortaddr) const; uint64_t getDeviceLongAddr(uint16_t shortaddr) const;
@ -689,26 +670,15 @@ public:
return findShortAddr(shortaddr).manufacturerId; return findShortAddr(shortaddr).manufacturerId;
} }
void setReachable(uint16_t shortaddr, bool reachable);
void setLQI(uint16_t shortaddr, uint8_t lqi);
void setLastSeenNow(uint16_t shortaddr);
// uint8_t getLQI(uint16_t shortaddr) const;
void setBatteryPercent(uint16_t shortaddr, uint8_t bp);
uint8_t getBatteryPercent(uint16_t shortaddr) const;
// get next sequence number for (increment at each all) // get next sequence number for (increment at each all)
uint8_t getNextSeqNumber(uint16_t shortaddr); uint8_t getNextSeqNumber(uint16_t shortaddr);
// Dump json // Dump json
static void addLightState(Z_attribute_list & attr_list, const Z_Data_Light & light);
String dumpLightState(uint16_t shortaddr) const; String dumpLightState(uint16_t shortaddr) const;
String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; String dumpDevice(uint32_t dump_mode, const Z_Device & device) const;
static String dumpSingleDevice(uint32_t dump_mode, const Z_Device & device);
int32_t deviceRestore(JsonParserObject json); int32_t deviceRestore(JsonParserObject json);
// General Zigbee device profile support
void setLightProfile(uint16_t shortaddr, uint8_t light_profile);
uint8_t getLightProfile(uint16_t shortaddr) const ;
// Hue support // Hue support
int8_t getHueBulbtype(uint16_t shortaddr) const ; int8_t getHueBulbtype(uint16_t shortaddr) const ;
void hideHueBulb(uint16_t shortaddr, bool hidden); void hideHueBulb(uint16_t shortaddr, bool hidden);
@ -731,14 +701,7 @@ public:
size_t devicesSize(void) const { size_t devicesSize(void) const {
return _devices.length(); return _devices.length();
} }
const Z_Device & devicesAt(size_t i) const { Z_Device & devicesAt(size_t i) const;
const Z_Device * devp = _devices.at(i);
if (devp) {
return *devp;
} else {
return device_unk;
}
}
// Remove device from list // Remove device from list
bool removeDevice(uint16_t shortaddr); bool removeDevice(uint16_t shortaddr);
@ -746,10 +709,10 @@ public:
// Mark data as 'dirty' and requiring to save in Flash // Mark data as 'dirty' and requiring to save in Flash
void dirty(void); void dirty(void);
void clean(void); // avoid writing to flash the last changes void clean(void); // avoid writing to flash the last changes
void shrinkToFit(uint16_t shortaddr);
// Find device by name, can be short_addr, long_addr, number_in_array or name // Find device by name, can be short_addr, long_addr, number_in_array or name
uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const; Z_Device & parseDeviceFromName(const char * param, bool short_must_be_known = false);
private: private:
LList<Z_Device> _devices; // list of devices LList<Z_Device> _devices; // list of devices
@ -757,10 +720,6 @@ private:
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
// Following device is used represent the unknown device, with all defaults
// Any find() function will not return Null, instead it will return this instance
const Z_Device device_unk = Z_Device(BAD_SHORTADDR);
//int32_t findShortAddrIdx(uint16_t shortaddr) const; //int32_t findShortAddrIdx(uint16_t shortaddr) const;
// Create a new entry in the devices list - must be called if it is sure it does not already exist // Create a new entry in the devices list - must be called if it is sure it does not already exist
Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0);
@ -772,6 +731,10 @@ private:
\*********************************************************************************************/ \*********************************************************************************************/
Z_Devices zigbee_devices = Z_Devices(); Z_Devices zigbee_devices = Z_Devices();
// Following device is used represent the unknown device, with all defaults
// Any find() function will not return Null, instead it will return this instance
Z_Device device_unk = Z_Device(BAD_SHORTADDR);
// Local coordinator information // Local coordinator information
uint64_t localIEEEAddr = 0; uint64_t localIEEEAddr = 0;
uint16_t localShortAddr = 0; uint16_t localShortAddr = 0;

View File

@ -23,12 +23,21 @@
* Implementation * Implementation
\*********************************************************************************************/ \*********************************************************************************************/
Z_Device & Z_Devices::devicesAt(size_t i) const {
Z_Device * devp = (Z_Device*) _devices.at(i);
if (devp) {
return *devp;
} else {
return device_unk;
}
}
// //
// 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.
// //
Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
if ((BAD_SHORTADDR == shortaddr) && !longaddr) { return (Z_Device&) device_unk; } // it is not legal to create this entry if ((BAD_SHORTADDR == shortaddr) && !longaddr) { return device_unk; } // it is not legal to create this entry
Z_Device & device = _devices.addToLast(); Z_Device & device = _devices.addToLast();
device.shortaddr = shortaddr; device.shortaddr = shortaddr;
device.longaddr = longaddr; device.longaddr = longaddr;
@ -56,7 +65,7 @@ Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) {
for (auto & elem : _devices) { for (auto & elem : _devices) {
if (elem.shortaddr == shortaddr) { return elem; } if (elem.shortaddr == shortaddr) { return elem; }
} }
return (Z_Device&) device_unk; return device_unk;
} }
const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const { const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const {
for (const auto & elem : _devices) { for (const auto & elem : _devices) {
@ -73,11 +82,11 @@ const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const {
// index in _devices of entry, -1 if not found // index in _devices of entry, -1 if not found
// //
Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) { Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) {
if (!longaddr) { return (Z_Device&) device_unk; } if (!longaddr) { return device_unk; }
for (auto &elem : _devices) { for (auto &elem : _devices) {
if (elem.longaddr == longaddr) { return elem; } if (elem.longaddr == longaddr) { return elem; }
} }
return (Z_Device&) device_unk; return device_unk;
} }
const Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) const { const Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) const {
if (!longaddr) { return device_unk; } if (!longaddr) { return device_unk; }
@ -109,32 +118,25 @@ int32_t Z_Devices::findFriendlyName(const char * name) const {
return -1; return -1;
} }
uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const { Z_Device & Z_Devices::isKnownLongAddrDevice(uint64_t longaddr) const {
const Z_Device & device = findLongAddr(longaddr); return (Z_Device &) findLongAddr(longaddr);
if (foundDevice(device)) {
return device.shortaddr; // can be zero, if not yet registered
} else {
return BAD_SHORTADDR;
}
} }
uint16_t Z_Devices::isKnownIndex(uint32_t index) const { Z_Device & Z_Devices::isKnownIndexDevice(uint32_t index) const {
if (index < devicesSize()) { if (index < devicesSize()) {
const Z_Device & device = devicesAt(index); return devicesAt(index);
return device.shortaddr;
} else { } else {
return BAD_SHORTADDR; return device_unk;
} }
} }
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const { Z_Device & Z_Devices::isKnownFriendlyNameDevice(const char * name) const {
if ((!name) || (0 == strlen(name))) { return BAD_SHORTADDR; } // Error if ((!name) || (0 == strlen(name))) { return device_unk; } // Error
int32_t found = findFriendlyName(name); int32_t found = findFriendlyName(name);
if (found >= 0) { if (found >= 0) {
const Z_Device & device = devicesAt(found); return devicesAt(found);
return device.shortaddr; // can be zero, if not yet registered
} else { } else {
return BAD_SHORTADDR; return device_unk;
} }
} }
@ -146,7 +148,7 @@ uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
// We have a seen a shortaddr on the network, get the corresponding device object // We have a seen a shortaddr on the network, get the corresponding device object
// //
Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) { Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
if (BAD_SHORTADDR == shortaddr) { return (Z_Device&) device_unk; } // this is not legal if (BAD_SHORTADDR == shortaddr) { return device_unk; } // this is not legal
Z_Device & device = findShortAddr(shortaddr); Z_Device & device = findShortAddr(shortaddr);
if (foundDevice(device)) { if (foundDevice(device)) {
return device; return device;
@ -156,7 +158,7 @@ Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
// find the Device object by its longaddr (unique key if not null) // find the Device object by its longaddr (unique key if not null)
Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) { Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {
if (!longaddr) { return (Z_Device&) device_unk; } if (!longaddr) { return device_unk; }
Z_Device & device = findLongAddr(longaddr); Z_Device & device = findLongAddr(longaddr);
if (foundDevice(device)) { if (foundDevice(device)) {
return device; return device;
@ -211,7 +213,7 @@ Z_Device & Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
if ((BAD_SHORTADDR != shortaddr) || longaddr) { if ((BAD_SHORTADDR != shortaddr) || longaddr) {
return createDeviceEntry(shortaddr, longaddr); return createDeviceEntry(shortaddr, longaddr);
} }
return (Z_Device&) device_unk; return device_unk;
} }
} }
@ -308,29 +310,13 @@ void Z_Device::setFriendlyName(const char * str) {
setStringAttribute(friendlyName, str); setStringAttribute(friendlyName, str);
} }
void Z_Device::setLastSeenNow(void) {
void Z_Devices::setReachable(uint16_t shortaddr, bool reachable) {
getShortAddr(shortaddr).setReachable(reachable);
}
void Z_Devices::setLQI(uint16_t shortaddr, uint8_t lqi) {
if (shortaddr == localShortAddr) { return; }
getShortAddr(shortaddr).lqi = lqi;
}
void Z_Devices::setLastSeenNow(uint16_t shortaddr) {
if (shortaddr == localShortAddr) { return; }
// Only update time if after 2020-01-01 0000. // Only update time if after 2020-01-01 0000.
// Fixes issue where zigbee device pings before WiFi/NTP has set utc_time // Fixes issue where zigbee device pings before WiFi/NTP has set utc_time
// to the correct time, and "last seen" calculations are based on the // to the correct time, and "last seen" calculations are based on the
// pre-corrected last_seen time and the since-corrected utc_time. // pre-corrected last_seen time and the since-corrected utc_time.
if (Rtc.utc_time < 1577836800) { return; } if (Rtc.utc_time < 1577836800) { return; }
getShortAddr(shortaddr).last_seen = Rtc.utc_time; last_seen = Rtc.utc_time;
}
void Z_Devices::setBatteryPercent(uint16_t shortaddr, uint8_t bp) {
getShortAddr(shortaddr).batterypercent = bp;
} }
// get the next sequance number for the device, or use the global seq number if device is unknown // get the next sequance number for the device, or use the global seq number if device is unknown
@ -345,22 +331,32 @@ uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
} }
} }
// General Zigbee device profile support // returns: dirty flag, did we change the value of the object
void Z_Devices::setLightProfile(uint16_t shortaddr, uint8_t light_profile) { void Z_Device::setLightChannels(int8_t channels) {
Z_Device &device = getShortAddr(shortaddr); if (channels >= 0) {
if (device.setLightChannels(light_profile)) { // retrieve of create light object
dirty(); Z_Data_Light & light = data.get<Z_Data_Light>(0);
if (channels != light.getConfig()) {
light.setConfig(channels);
zigbee_devices.dirty();
}
Z_Data_OnOff & onoff = data.get<Z_Data_OnOff>(0);
} 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();
}
}
} }
} }
// Returns the device profile or 0xFF if the device or profile is unknown
uint8_t Z_Devices::getLightProfile(uint16_t shortaddr) const {
const Z_Device &device = findShortAddr(shortaddr);
return device.getLightChannels();
}
int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const { int8_t Z_Devices::getHueBulbtype(uint16_t shortaddr) const {
int8_t light_profile = getLightProfile(shortaddr); const Z_Device &device = findShortAddr(shortaddr);
int8_t light_profile = device.getLightChannels();
if (0x00 == (light_profile & 0xF0)) { if (0x00 == (light_profile & 0xF0)) {
return (light_profile & 0x07); return (light_profile & 0x07);
} else { } else {
@ -575,39 +571,39 @@ void Z_Devices::clean(void) {
// - a long address starting with "0x", example: 0x7CB03EBB0A0292DD // - a long address starting with "0x", example: 0x7CB03EBB0A0292DD
// - a number 0..99, the index number in ZigbeeStatus // - a number 0..99, the index number in ZigbeeStatus
// - a friendly name, between quotes, example: "Room_Temp" // - a friendly name, between quotes, example: "Room_Temp"
uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { Z_Device & Z_Devices::parseDeviceFromName(const char * param, bool short_must_be_known) {
if (nullptr == param) { return BAD_SHORTADDR; } if (nullptr == param) { return device_unk; }
size_t param_len = strlen(param); size_t param_len = strlen(param);
char dataBuf[param_len + 1]; char dataBuf[param_len + 1];
strcpy(dataBuf, param); strcpy(dataBuf, param);
RemoveSpace(dataBuf); RemoveSpace(dataBuf);
uint16_t shortaddr = BAD_SHORTADDR; // start with unknown
if (strlen(dataBuf) < 4) { if ((dataBuf[0] >= '0') && (dataBuf[0] <= '9') && (strlen(dataBuf) < 4)) {
// simple number 0..99 // simple number 0..99
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1); return isKnownIndexDevice(XdrvMailbox.payload - 1);
} else {
return device_unk;
} }
} else if ((dataBuf[0] == '0') && ((dataBuf[1] == 'x') || (dataBuf[1] == 'X'))) { } else if ((dataBuf[0] == '0') && ((dataBuf[1] == 'x') || (dataBuf[1] == 'X'))) {
// starts with 0x // starts with 0x
if (strlen(dataBuf) < 18) { if (strlen(dataBuf) < 18) {
// expect a short address // expect a short address
shortaddr = strtoull(dataBuf, nullptr, 0); uint16_t shortaddr = strtoull(dataBuf, nullptr, 0);
if (short_must_be_known) { if (short_must_be_known) {
shortaddr = zigbee_devices.findShortAddr(shortaddr).shortaddr; // if not found, it reverts to the unknown_device with address BAD_SHORTADDR return (Z_Device&) findShortAddr(shortaddr); // if not found, it reverts to the unknown_device with address BAD_SHORTADDR
} else {
return getShortAddr(shortaddr); // create it if not registered
} }
// else we don't check if it's already registered to force unregistered devices
} else { } else {
// expect a long address // expect a long address
uint64_t longaddr = strtoull(dataBuf, nullptr, 0); uint64_t longaddr = strtoull(dataBuf, nullptr, 0);
shortaddr = zigbee_devices.isKnownLongAddr(longaddr); return isKnownLongAddrDevice(longaddr);
} }
} else { } else {
// expect a Friendly Name // expect a Friendly Name
shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf); return isKnownFriendlyNameDevice(dataBuf);
} }
return shortaddr;
} }
// Display the tracked status for a light // Display the tracked status for a light
@ -654,60 +650,70 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const {
// Dump the internal memory of Zigbee devices // Dump the internal memory of Zigbee devices
// Mode = 1: simple dump of devices addresses // Mode = 1: simple dump of devices addresses
// Mode = 2: simple dump of devices addresses and names, endpoints, light // Mode = 2: simple dump of devices addresses and names, endpoints, light
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { String Z_Devices::dumpSingleDevice(uint32_t dump_mode, const class Z_Device & device) {
uint16_t shortaddr = device.shortaddr;
char hex[22];
Z_attribute_list attr_list;
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (device.friendlyName > 0) {
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName);
}
if (2 <= dump_mode) {
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64);
attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
if (device.modelId) {
attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId);
}
if (device.manufacturerId) {
attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
}
JsonGeneratorArray arr_ep;
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t endpoint = device.endpoints[i];
if (0x00 == endpoint) { break; }
arr_ep.add(endpoint);
}
attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
JsonGeneratorArray arr_data;
for (auto & data_elt : device.data) {
char key[8];
if (data_elt.validConfig()) {
snprintf_P(key, sizeof(key), "?%02X.%1X", data_elt.getEndpoint(), data_elt.getConfig());
} else {
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
}
key[0] = Z_Data::DataTypeToChar(data_elt.getType());
arr_data.addStr(key);
}
attr_list.addAttribute(F("Config")).setStrRaw(arr_data.toString().c_str());
}
return attr_list.toString(true);
}
// If &device == nullptr, then dump all
String Z_Devices::dumpDevice(uint32_t dump_mode, const Z_Device & device) const {
JsonGeneratorArray json_arr; JsonGeneratorArray json_arr;
for (const auto & device : _devices) { if (&device == nullptr) {
uint16_t shortaddr = device.shortaddr; if (dump_mode < 2) {
char hex[22]; // dump light mode for all devices
for (const auto & device2 : _devices) {
// ignore non-current device, if device specified json_arr.addStrRaw(dumpSingleDevice(dump_mode, device2).c_str());
if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } }
Z_attribute_list attr_list;
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (device.friendlyName > 0) {
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName);
} }
} else {
if (2 <= dump_mode) { json_arr.addStrRaw(dumpSingleDevice(dump_mode, device).c_str());
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64);
attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
if (device.modelId) {
attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId);
}
if (device.manufacturerId) {
attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
}
JsonGeneratorArray arr_ep;
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t endpoint = device.endpoints[i];
if (0x00 == endpoint) { break; }
arr_ep.add(endpoint);
}
attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
JsonGeneratorArray arr_data;
for (auto & data_elt : device.data) {
char key[8];
if (data_elt.validConfig()) {
snprintf_P(key, sizeof(key), "?%02X.%1X", data_elt.getEndpoint(), data_elt.getConfig());
} else {
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
}
key[0] = Z_Data::DataTypeToChar(data_elt.getType());
arr_data.addStr(key);
}
attr_list.addAttribute(F("Config")).setStrRaw(arr_data.toString().c_str());
}
json_arr.addStrRaw(attr_list.toString(true).c_str());
} }
return json_arr.toString(); return json_arr.toString();
} }

View File

@ -257,7 +257,7 @@ void hydrateSingleDevice(const SBuffer & buf_d, uint32_t version) {
// Hue bulbtype - if present // Hue bulbtype - if present
if (1 == version) { if (1 == version) {
zigbee_devices.setLightProfile(shortaddr, buf_d.get8(d)); device.setLightChannels(buf_d.get8(d));
d++; d++;
} else if (2 == version) { } else if (2 == version) {
// v2 parser // v2 parser

View File

@ -1853,7 +1853,7 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib
switch (ccccaaaa) { switch (ccccaaaa) {
case 0x00000004: device.setManufId(attr.getStr()); break; case 0x00000004: device.setManufId(attr.getStr()); break;
case 0x00000005: device.setModelId(attr.getStr()); break; case 0x00000005: device.setModelId(attr.getStr()); break;
case 0x00010021: zigbee_devices.setBatteryPercent(shortaddr, uval16 / 2); break; case 0x00010021: device.setBatteryPercent(uval16 / 2); break;
case 0x00060000: case 0x00060000:
case 0x00068000: device.setPower(attr.getBool(), src_ep); break; case 0x00068000: device.setPower(attr.getBool(), src_ep); break;
} }

View File

@ -199,7 +199,7 @@ void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster
// This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms // This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms
void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
if (BAD_SHORTADDR != shortaddr) { if (BAD_SHORTADDR != shortaddr) {
zigbee_devices.setReachable(shortaddr, false); // mark device as reachable zigbee_devices.getShortAddr(shortaddr).setReachable(false); // mark device as reachable
} }
} }

View File

@ -1470,8 +1470,11 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
// log the packet details // log the packet details
zcl_received.log(); zcl_received.log();
zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI Z_Device & device = zigbee_devices.getShortAddr(srcaddr);
zigbee_devices.setLastSeenNow(srcaddr); if (srcaddr != localShortAddr) {
device.setLQI(linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI
device.setLastSeenNow();
}
char shortaddr[8]; char shortaddr[8];
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
@ -1519,7 +1522,7 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
// since we just receveived data from the device, it is reachable // since we just receveived data from the device, it is reachable
zigbee_devices.resetTimersForDevice(srcaddr, 0 /* groupaddr */, Z_CAT_REACHABILITY); // remove any reachability timer already there zigbee_devices.resetTimersForDevice(srcaddr, 0 /* groupaddr */, Z_CAT_REACHABILITY); // remove any reachability timer already there
zigbee_devices.setReachable(srcaddr, true); // mark device as reachable device.setReachable(true); // mark device as reachable
if (defer_attributes) { if (defer_attributes) {
// Prepare for publish // Prepare for publish
@ -1615,8 +1618,11 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) {
if ((0x0000 == profileid) && (0x00 == srcendpoint)) { if ((0x0000 == profileid) && (0x00 == srcendpoint)) {
// ZDO request // ZDO request
// Report LQI // Report LQI
zigbee_devices.setLQI(srcaddr, linkquality); Z_Device & device = zigbee_devices.getShortAddr(srcaddr);
zigbee_devices.setLastSeenNow(srcaddr); if (srcaddr != localShortAddr) {
device.setLQI(linkquality);
device.setLastSeenNow();
}
// Since ZDO messages start with a sequence number, we skip it // Since ZDO messages start with a sequence number, we skip it
// but we add the source address in the last 2 bytes // but we add the source address in the last 2 bytes
SBuffer zdo_buf(buf.get8(20) - 1 + 2); SBuffer zdo_buf(buf.get8(20) - 1 + 2);

View File

@ -683,7 +683,7 @@ void CmndZbSend(void) {
// parse "Device" and "Group" // parse "Device" and "Group"
JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)]; JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_DEVICE)];
if (val_device) { if (val_device) {
device = zigbee_devices.parseDeviceParam(val_device.getStr()); device = zigbee_devices.parseDeviceFromName(val_device.getStr()).shortaddr;
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
} }
if (BAD_SHORTADDR == device) { // if not found, check if we have a group if (BAD_SHORTADDR == device) { // if not found, check if we have a group
@ -828,7 +828,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
// Information about source device: "Device", "Endpoint", "Cluster" // Information about source device: "Device", "Endpoint", "Cluster"
// - the source endpoint must have a known IEEE address // - the source endpoint must have a known IEEE address
srcDevice = zigbee_devices.parseDeviceParam(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr)); srcDevice = zigbee_devices.parseDeviceFromName(root.getStr(PSTR(D_CMND_ZIGBEE_DEVICE), nullptr)).shortaddr;
if (BAD_SHORTADDR == srcDevice) { ResponseCmndChar_P(PSTR("Unknown source device")); return; } if (BAD_SHORTADDR == srcDevice) { ResponseCmndChar_P(PSTR("Unknown source device")); return; }
// check if IEEE address is known // check if IEEE address is known
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice); uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
@ -862,7 +862,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
if ((dst_device) || (BAD_SHORTADDR != dstDevice)) { if ((dst_device) || (BAD_SHORTADDR != dstDevice)) {
if (BAD_SHORTADDR == dstDevice) { if (BAD_SHORTADDR == dstDevice) {
dstDevice = zigbee_devices.parseDeviceParam(dst_device.getStr(nullptr)); dstDevice = zigbee_devices.parseDeviceFromName(dst_device.getStr(nullptr)).shortaddr;
if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; } if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
} }
if (0x0000 == dstDevice) { if (0x0000 == dstDevice) {
@ -941,7 +941,7 @@ void CmndZbUnbind(void) {
void CmndZbBindState_or_Map(uint16_t zdo_cmd) { void CmndZbBindState_or_Map(uint16_t zdo_cmd) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
uint8_t index = XdrvMailbox.index - 1; // change default 1 to 0 uint8_t index = XdrvMailbox.index - 1; // change default 1 to 0
@ -1002,7 +1002,7 @@ void CmndZbProbe(void) {
// //
void CmndZbProbeOrPing(boolean probe) { void CmndZbProbeOrPing(boolean probe) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); uint16_t shortaddr = zigbee_devices.parseDeviceFromName(XdrvMailbox.data).shortaddr;
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
// everything is good, we can send the command // everything is good, we can send the command
@ -1034,19 +1034,19 @@ void CmndZbName(void) {
// check if parameters contain a comma ',' // check if parameters contain a comma ','
char *p; char *p;
char *str = strtok_r(XdrvMailbox.data, ", ", &p); char *str = strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (p == nullptr) { if (p == nullptr) {
const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); const char * friendlyName = device.friendlyName;
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName : ""); Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), device.shortaddr, friendlyName ? friendlyName : "");
} else { } else {
if (strlen(p) > 32) { p[32] = 0x00; } // truncate to 32 chars max if (strlen(p) > 32) { p[32] = 0x00; } // truncate to 32 chars max
zigbee_devices.getShortAddr(shortaddr).setFriendlyName(p); device.setFriendlyName(p);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p); Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), device.shortaddr, p);
} }
} }
@ -1066,18 +1066,18 @@ void CmndZbModelId(void) {
// check if parameters contain a comma ',' // check if parameters contain a comma ','
char *p; char *p;
char *str = strtok_r(XdrvMailbox.data, ", ", &p); char *str = strtok_r(XdrvMailbox.data, ",", &p);
// parse first part, <device_id> // parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (p == nullptr) { if (p == nullptr) {
const char * modelId = zigbee_devices.getModelId(shortaddr); const char * modelId = device.modelId;
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId : ""); Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, modelId ? modelId : "");
} else { } else {
zigbee_devices.getShortAddr(shortaddr).setModelId(p); device.setModelId(p);
Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p); Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, p);
} }
} }
@ -1098,16 +1098,16 @@ void CmndZbLight(void) {
char *str = strtok_r(XdrvMailbox.data, ", ", &p); char *str = strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id> // parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
if (p) { if (p) {
int8_t bulbtype = strtol(p, nullptr, 10); int8_t bulbtype = strtol(p, nullptr, 10);
if (bulbtype > 5) { bulbtype = 5; } if (bulbtype > 5) { bulbtype = 5; }
if (bulbtype < -1) { bulbtype = -1; } if (bulbtype < -1) { bulbtype = -1; }
zigbee_devices.setLightProfile(shortaddr, bulbtype); device.setLightChannels(bulbtype);
} }
String dump = zigbee_devices.dumpLightState(shortaddr); String dump = zigbee_devices.dumpLightState(device.shortaddr);
Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str()); Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str());
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT)); MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_LIGHT));
@ -1141,10 +1141,8 @@ void CmndZbOccupancy(void) {
char *str = strtok_r(XdrvMailbox.data, ", ", &p); char *str = strtok_r(XdrvMailbox.data, ", ", &p);
// parse first part, <device_id> // parse first part, <device_id>
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
int8_t occupancy_time = -1; int8_t occupancy_time = -1;
if (p) { if (p) {
@ -1170,11 +1168,11 @@ void CmndZbOccupancy(void) {
// //
void CmndZbForget(void) { void CmndZbForget(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
// everything is good, we can send the command // everything is good, we can send the command
if (zigbee_devices.removeDevice(shortaddr)) { if (zigbee_devices.removeDevice(device.shortaddr)) {
ResponseCmndDone(); ResponseCmndDone();
} else { } else {
ResponseCmndChar_P(PSTR("Unknown device")); ResponseCmndChar_P(PSTR("Unknown device"));
@ -1369,12 +1367,17 @@ void ZigbeeGlowPermitJoinLight(void) {
void CmndZbStatus(void) { void CmndZbStatus(void) {
if (ZigbeeSerial) { if (ZigbeeSerial) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); String dump;
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data);
if (XdrvMailbox.data_len > 0) { if (XdrvMailbox.data_len > 0) {
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device);
} else {
if (XdrvMailbox.index >= 2) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, *(Z_Device*)nullptr);
} }
String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr);
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
} }
} }
@ -1502,9 +1505,8 @@ void CmndZbData(void) {
} }
for (auto device_name : root) { for (auto device_name : root) {
uint16_t shortaddr = zigbee_devices.parseDeviceParam(device_name.getStr()); Z_Device & device = zigbee_devices.parseDeviceFromName(device_name.getStr());
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
JsonParserObject inner_data = device_name.getValue().getObject(); JsonParserObject inner_data = device_name.getValue().getObject();
if (inner_data) { if (inner_data) {
if (!parseDeviceInnerData(device, inner_data)) { if (!parseDeviceInnerData(device, inner_data)) {
@ -1518,9 +1520,8 @@ void CmndZbData(void) {
// non-JSON, export current data // non-JSON, export current data
// ZbData 0x1234 // ZbData 0x1234
// ZbData Device_Name // ZbData Device_Name
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data);
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
Z_attribute_list attr_data; Z_attribute_list attr_data;
@ -1568,7 +1569,7 @@ void CmndZbData(void) {
} }
char hex[8]; char hex[8];
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), device.shortaddr);
Response_P(PSTR("{\"%s\":{\"%s\":%s}}"), XdrvMailbox.command, hex, attr_data.toString(true).c_str()); Response_P(PSTR("{\"%s\":{\"%s\":%s}}"), XdrvMailbox.command, hex, attr_data.toString(true).c_str());
} }
} }