diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index db74e7220..b780696f8 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -543,6 +543,9 @@ public: inline bool getReachable(void) const { return reachable; } 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 bool addEndpoint(uint8_t endpoint); void clearEndpoints(void); @@ -552,6 +555,8 @@ public: void setModelId(const char * str); void setFriendlyName(const char * str); + void setLastSeenNow(void); + // dump device attributes to ZbData void toAttributes(Z_attribute_list & attr_list) const; @@ -568,30 +573,8 @@ public: } } - // returns: dirty flag, did we change the value of the object - bool setLightChannels(int8_t channels) { - bool dirty = false; - if (channels >= 0) { - // retrieve of create light object - Z_Data_Light & light = data.get(0); - if (channels != light.getConfig()) { - light.setConfig(channels); - dirty = true; - } - Z_Data_OnOff & onoff = data.get(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; - } + void setLightChannels(int8_t channels); + protected: static void setStringAttribute(char*& attr, const char * str); @@ -655,9 +638,9 @@ public: // - 0x0000 = not found // - BAD_SHORTADDR = bad parameter // - 0x = the device's short address - uint16_t isKnownLongAddr(uint64_t longaddr) const; - uint16_t isKnownIndex(uint32_t index) const; - uint16_t isKnownFriendlyName(const char * name) const; + Z_Device & isKnownLongAddrDevice(uint64_t longaddr) const; + Z_Device & isKnownIndexDevice(uint32_t index) const; + Z_Device & isKnownFriendlyNameDevice(const char * name) const; Z_Device & findShortAddr(uint16_t shortaddr); 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 & 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 - inline bool foundDevice(const Z_Device & device) const { - return (&device != &device_unk); - } + inline bool foundDevice(const Z_Device & device) const { return device.valid(); } int32_t findFriendlyName(const char * name) const; uint64_t getDeviceLongAddr(uint16_t shortaddr) const; @@ -689,26 +670,15 @@ public: 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) uint8_t getNextSeqNumber(uint16_t shortaddr); // Dump json - static void addLightState(Z_attribute_list & attr_list, const Z_Data_Light & light); 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); - // General Zigbee device profile support - void setLightProfile(uint16_t shortaddr, uint8_t light_profile); - uint8_t getLightProfile(uint16_t shortaddr) const ; - // Hue support int8_t getHueBulbtype(uint16_t shortaddr) const ; void hideHueBulb(uint16_t shortaddr, bool hidden); @@ -731,14 +701,7 @@ public: size_t devicesSize(void) const { return _devices.length(); } - const Z_Device & devicesAt(size_t i) const { - const Z_Device * devp = _devices.at(i); - if (devp) { - return *devp; - } else { - return device_unk; - } - } + Z_Device & devicesAt(size_t i) const; // Remove device from list bool removeDevice(uint16_t shortaddr); @@ -746,10 +709,10 @@ public: // Mark data as 'dirty' and requiring to save in Flash void dirty(void); 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 - 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: LList _devices; // list of devices @@ -757,10 +720,6 @@ private: uint32_t _saveTimer = 0; 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; // 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); @@ -772,6 +731,10 @@ private: \*********************************************************************************************/ 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 uint64_t localIEEEAddr = 0; uint16_t localShortAddr = 0; diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino index 9e648f7d4..0fc289ef0 100644 --- a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino @@ -23,12 +23,21 @@ * 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 // entry with same shortaddr or longaddr exists. // 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(); device.shortaddr = shortaddr; device.longaddr = longaddr; @@ -56,7 +65,7 @@ Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) { for (auto & elem : _devices) { if (elem.shortaddr == shortaddr) { return elem; } } - return (Z_Device&) device_unk; + return device_unk; } const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const { 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 // Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) { - if (!longaddr) { return (Z_Device&) device_unk; } + if (!longaddr) { return device_unk; } for (auto &elem : _devices) { if (elem.longaddr == longaddr) { return elem; } } - return (Z_Device&) device_unk; + return device_unk; } const Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) const { if (!longaddr) { return device_unk; } @@ -109,32 +118,25 @@ int32_t Z_Devices::findFriendlyName(const char * name) const { return -1; } -uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const { - const Z_Device & device = findLongAddr(longaddr); - if (foundDevice(device)) { - return device.shortaddr; // can be zero, if not yet registered - } else { - return BAD_SHORTADDR; - } +Z_Device & Z_Devices::isKnownLongAddrDevice(uint64_t longaddr) const { + return (Z_Device &) findLongAddr(longaddr); } -uint16_t Z_Devices::isKnownIndex(uint32_t index) const { +Z_Device & Z_Devices::isKnownIndexDevice(uint32_t index) const { if (index < devicesSize()) { - const Z_Device & device = devicesAt(index); - return device.shortaddr; + return devicesAt(index); } else { - return BAD_SHORTADDR; + return device_unk; } } -uint16_t Z_Devices::isKnownFriendlyName(const char * name) const { - if ((!name) || (0 == strlen(name))) { return BAD_SHORTADDR; } // Error +Z_Device & Z_Devices::isKnownFriendlyNameDevice(const char * name) const { + if ((!name) || (0 == strlen(name))) { return device_unk; } // Error int32_t found = findFriendlyName(name); if (found >= 0) { - const Z_Device & device = devicesAt(found); - return device.shortaddr; // can be zero, if not yet registered + return devicesAt(found); } 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 // 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); if (foundDevice(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) 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); if (foundDevice(device)) { return device; @@ -211,7 +213,7 @@ Z_Device & Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { if ((BAD_SHORTADDR != 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); } - -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; } +void Z_Device::setLastSeenNow(void) { // Only update time if after 2020-01-01 0000. // 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 // pre-corrected last_seen time and the since-corrected utc_time. if (Rtc.utc_time < 1577836800) { return; } - getShortAddr(shortaddr).last_seen = Rtc.utc_time; -} - - -void Z_Devices::setBatteryPercent(uint16_t shortaddr, uint8_t bp) { - getShortAddr(shortaddr).batterypercent = bp; + last_seen = Rtc.utc_time; } // 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 -void Z_Devices::setLightProfile(uint16_t shortaddr, uint8_t light_profile) { - Z_Device &device = getShortAddr(shortaddr); - if (device.setLightChannels(light_profile)) { - dirty(); +// returns: dirty flag, did we change the value of the object +void Z_Device::setLightChannels(int8_t channels) { + if (channels >= 0) { + // retrieve of create light object + Z_Data_Light & light = data.get(0); + if (channels != light.getConfig()) { + light.setConfig(channels); + zigbee_devices.dirty(); + } + Z_Data_OnOff & onoff = data.get(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 light_profile = getLightProfile(shortaddr); + const Z_Device &device = findShortAddr(shortaddr); + int8_t light_profile = device.getLightChannels(); if (0x00 == (light_profile & 0xF0)) { return (light_profile & 0x07); } else { @@ -575,39 +571,39 @@ void Z_Devices::clean(void) { // - a long address starting with "0x", example: 0x7CB03EBB0A0292DD // - a number 0..99, the index number in ZigbeeStatus // - a friendly name, between quotes, example: "Room_Temp" -uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { - if (nullptr == param) { return BAD_SHORTADDR; } +Z_Device & Z_Devices::parseDeviceFromName(const char * param, bool short_must_be_known) { + if (nullptr == param) { return device_unk; } size_t param_len = strlen(param); char dataBuf[param_len + 1]; strcpy(dataBuf, param); 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 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'))) { // starts with 0x if (strlen(dataBuf) < 18) { // expect a short address - shortaddr = strtoull(dataBuf, nullptr, 0); + uint16_t shortaddr = strtoull(dataBuf, nullptr, 0); 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 { // expect a long address uint64_t longaddr = strtoull(dataBuf, nullptr, 0); - shortaddr = zigbee_devices.isKnownLongAddr(longaddr); + return isKnownLongAddrDevice(longaddr); } } else { // expect a Friendly Name - shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf); + return isKnownFriendlyNameDevice(dataBuf); } - - return shortaddr; } // 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 // Mode = 1: simple dump of devices addresses // 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; - for (const auto & device : _devices) { - uint16_t shortaddr = device.shortaddr; - char hex[22]; - - // ignore non-current device, if device specified - 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); + if (&device == nullptr) { + if (dump_mode < 2) { + // dump light mode for all devices + for (const auto & device2 : _devices) { + json_arr.addStrRaw(dumpSingleDevice(dump_mode, device2).c_str()); + } } - - 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()); - } - json_arr.addStrRaw(attr_list.toString(true).c_str()); + } else { + json_arr.addStrRaw(dumpSingleDevice(dump_mode, device).c_str()); } + return json_arr.toString(); } diff --git a/tasmota/xdrv_23_zigbee_4_persistence.ino b/tasmota/xdrv_23_zigbee_4_persistence.ino index f893bc004..9b24e0af8 100644 --- a/tasmota/xdrv_23_zigbee_4_persistence.ino +++ b/tasmota/xdrv_23_zigbee_4_persistence.ino @@ -257,7 +257,7 @@ void hydrateSingleDevice(const SBuffer & buf_d, uint32_t version) { // Hue bulbtype - if present if (1 == version) { - zigbee_devices.setLightProfile(shortaddr, buf_d.get8(d)); + device.setLightChannels(buf_d.get8(d)); d++; } else if (2 == version) { // v2 parser diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index c90c0f8e0..44757bf2c 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1853,7 +1853,7 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib switch (ccccaaaa) { case 0x00000004: device.setManufId(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 0x00068000: device.setPower(attr.getBool(), src_ep); break; } diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index d3bdc3253..ef3fc1f71 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -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 void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { if (BAD_SHORTADDR != shortaddr) { - zigbee_devices.setReachable(shortaddr, false); // mark device as reachable + zigbee_devices.getShortAddr(shortaddr).setReachable(false); // mark device as reachable } } diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 2ce31fa92..5fceb4533 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -1470,8 +1470,11 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) { // log the packet details zcl_received.log(); - zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI - zigbee_devices.setLastSeenNow(srcaddr); + Z_Device & device = zigbee_devices.getShortAddr(srcaddr); + if (srcaddr != localShortAddr) { + device.setLQI(linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI + device.setLastSeenNow(); + } char shortaddr[8]; 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 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) { // Prepare for publish @@ -1615,8 +1618,11 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) { if ((0x0000 == profileid) && (0x00 == srcendpoint)) { // ZDO request // Report LQI - zigbee_devices.setLQI(srcaddr, linkquality); - zigbee_devices.setLastSeenNow(srcaddr); + Z_Device & device = zigbee_devices.getShortAddr(srcaddr); + if (srcaddr != localShortAddr) { + device.setLQI(linkquality); + device.setLastSeenNow(); + } // Since ZDO messages start with a sequence number, we skip it // but we add the source address in the last 2 bytes SBuffer zdo_buf(buf.get8(20) - 1 + 2); diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 950646563..a5f849d7e 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -683,7 +683,7 @@ void CmndZbSend(void) { // parse "Device" and "Group" JsonParserToken val_device = root[PSTR(D_CMND_ZIGBEE_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) { // 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" // - 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; } // check if IEEE address is known 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 (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 (0x0000 == dstDevice) { @@ -941,7 +941,7 @@ void CmndZbUnbind(void) { void CmndZbBindState_or_Map(uint16_t zdo_cmd) { 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; } uint8_t index = XdrvMailbox.index - 1; // change default 1 to 0 @@ -1002,7 +1002,7 @@ void CmndZbProbe(void) { // void CmndZbProbeOrPing(boolean probe) { 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; } // everything is good, we can send the command @@ -1034,19 +1034,19 @@ void CmndZbName(void) { // check if parameters contain a comma ',' char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); + char *str = strtok_r(XdrvMailbox.data, ",", &p); // parse first part, - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered - if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered + if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (p == nullptr) { - const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName : ""); + const char * friendlyName = device.friendlyName; + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), device.shortaddr, friendlyName ? friendlyName : ""); } else { if (strlen(p) > 32) { p[32] = 0x00; } // truncate to 32 chars max - zigbee_devices.getShortAddr(shortaddr).setFriendlyName(p); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p); + device.setFriendlyName(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 ',' char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); + char *str = strtok_r(XdrvMailbox.data, ",", &p); // parse first part, - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered - if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered + if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (p == nullptr) { - const char * modelId = zigbee_devices.getModelId(shortaddr); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, modelId ? modelId : ""); + const char * modelId = device.modelId; + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, modelId ? modelId : ""); } else { - zigbee_devices.getShortAddr(shortaddr).setModelId(p); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), shortaddr, p); + device.setModelId(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); // parse first part, - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered - if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered + if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } if (p) { int8_t bulbtype = strtol(p, nullptr, 10); if (bulbtype > 5) { bulbtype = 5; } 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()); 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); // parse first part, - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered - if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - - Z_Device & device = zigbee_devices.getShortAddr(shortaddr); + Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered + if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } int8_t occupancy_time = -1; if (p) { @@ -1170,11 +1168,11 @@ void CmndZbOccupancy(void) { // void CmndZbForget(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } + Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered + if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } // everything is good, we can send the command - if (zigbee_devices.removeDevice(shortaddr)) { + if (zigbee_devices.removeDevice(device.shortaddr)) { ResponseCmndDone(); } else { ResponseCmndChar_P(PSTR("Unknown device")); @@ -1369,12 +1367,17 @@ void ZigbeeGlowPermitJoinLight(void) { void CmndZbStatus(void) { if (ZigbeeSerial) { 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 (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()); } } @@ -1502,9 +1505,8 @@ void CmndZbData(void) { } for (auto device_name : root) { - uint16_t shortaddr = zigbee_devices.parseDeviceParam(device_name.getStr()); - if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - Z_Device & device = zigbee_devices.getShortAddr(shortaddr); + Z_Device & device = zigbee_devices.parseDeviceFromName(device_name.getStr()); + if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } JsonParserObject inner_data = device_name.getValue().getObject(); if (inner_data) { if (!parseDeviceInnerData(device, inner_data)) { @@ -1518,9 +1520,8 @@ void CmndZbData(void) { // non-JSON, export current data // ZbData 0x1234 // ZbData Device_Name - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - const Z_Device & device = zigbee_devices.findShortAddr(shortaddr); + Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data); + if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } Z_attribute_list attr_data; @@ -1568,7 +1569,7 @@ void CmndZbData(void) { } 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()); } }