mirror of https://github.com/arendst/Tasmota.git
Add Zigbee persistence and friendly names
This commit is contained in:
parent
50a25aeb7c
commit
18ce64f813
|
@ -5,6 +5,7 @@
|
||||||
- Fix ``PowerDelta`` zero power detection (#7515)
|
- Fix ``PowerDelta`` zero power detection (#7515)
|
||||||
- Fix OTA minimal gzipped detection regression from 8.1.0.3
|
- Fix OTA minimal gzipped detection regression from 8.1.0.3
|
||||||
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
|
- Add web page sliders when ``SetOption37 128`` is active allowing control of white(s)
|
||||||
|
- Add Zigbee persistence and friendly names
|
||||||
|
|
||||||
### 8.1.0.3 20200106
|
### 8.1.0.3 20200106
|
||||||
|
|
||||||
|
|
|
@ -480,7 +480,10 @@
|
||||||
#define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZigbeeZCLRawReceived"
|
#define D_JSON_ZIGBEEZCL_RAW_RECEIVED "ZigbeeZCLRawReceived"
|
||||||
#define D_JSON_ZIGBEE_DEVICE "Device"
|
#define D_JSON_ZIGBEE_DEVICE "Device"
|
||||||
#define D_JSON_ZIGBEE_NAME "Name"
|
#define D_JSON_ZIGBEE_NAME "Name"
|
||||||
|
#define D_CMND_ZIGBEE_NAME "ZigbeeName"
|
||||||
#define D_CMND_ZIGBEE_PROBE "ZigbeeProbe"
|
#define D_CMND_ZIGBEE_PROBE "ZigbeeProbe"
|
||||||
|
#define D_CMND_ZIGBEE_FORGET "ZigbeeForget"
|
||||||
|
#define D_CMND_ZIGBEE_SAVE "ZigbeeSave"
|
||||||
#define D_CMND_ZIGBEE_RECEIVED "ZigbeeReceived"
|
#define D_CMND_ZIGBEE_RECEIVED "ZigbeeReceived"
|
||||||
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
#define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality"
|
||||||
#define D_CMND_ZIGBEE_READ "ZigbeeRead"
|
#define D_CMND_ZIGBEE_READ "ZigbeeRead"
|
||||||
|
|
|
@ -101,8 +101,8 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
||||||
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
|
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
|
||||||
uint32_t data; // Allow bit manipulation using SetOption
|
uint32_t data; // Allow bit manipulation using SetOption
|
||||||
struct { // SetOption82 .. SetOption113
|
struct { // SetOption82 .. SetOption113
|
||||||
uint32_t alexa_ct_range : 1; // bit 0 (v8.1.0.2) - SetOption82 - Reduced CT range for Alexa
|
uint32_t alexa_ct_range : 1; // bit 0 (v8.1.0.2) - SetOption82 - Reduced CT range for Alexa
|
||||||
uint32_t spare01 : 1;
|
uint32_t zigbee_use_names : 1; // bit 1 (V8.1.0.4) - SetOption83 - Use FriendlyNames instead of ShortAddresses when possible
|
||||||
uint32_t spare02 : 1;
|
uint32_t spare02 : 1;
|
||||||
uint32_t spare03 : 1;
|
uint32_t spare03 : 1;
|
||||||
uint32_t spare04 : 1;
|
uint32_t spare04 : 1;
|
||||||
|
|
|
@ -80,7 +80,7 @@ public:
|
||||||
return _buf->len;
|
return _buf->len;
|
||||||
}
|
}
|
||||||
size_t add32(const uint32_t data) { // append 32 bits value
|
size_t add32(const uint32_t data) { // append 32 bits value
|
||||||
if (_buf->len < _buf->size - 3) { // do we have room for 2 bytes
|
if (_buf->len < _buf->size - 3) { // do we have room for 4 bytes
|
||||||
_buf->buf[_buf->len++] = data;
|
_buf->buf[_buf->len++] = data;
|
||||||
_buf->buf[_buf->len++] = data >> 8;
|
_buf->buf[_buf->len++] = data >> 8;
|
||||||
_buf->buf[_buf->len++] = data >> 16;
|
_buf->buf[_buf->len++] = data >> 16;
|
||||||
|
@ -88,6 +88,19 @@ public:
|
||||||
}
|
}
|
||||||
return _buf->len;
|
return _buf->len;
|
||||||
}
|
}
|
||||||
|
size_t add64(const uint64_t data) { // append 64 bits value
|
||||||
|
if (_buf->len < _buf->size - 7) { // do we have room for 8 bytes
|
||||||
|
_buf->buf[_buf->len++] = data;
|
||||||
|
_buf->buf[_buf->len++] = data >> 8;
|
||||||
|
_buf->buf[_buf->len++] = data >> 16;
|
||||||
|
_buf->buf[_buf->len++] = data >> 24;
|
||||||
|
_buf->buf[_buf->len++] = data >> 32;
|
||||||
|
_buf->buf[_buf->len++] = data >> 40;
|
||||||
|
_buf->buf[_buf->len++] = data >> 48;
|
||||||
|
_buf->buf[_buf->len++] = data >> 56;
|
||||||
|
}
|
||||||
|
return _buf->len;
|
||||||
|
}
|
||||||
|
|
||||||
size_t addBuffer(const SBuffer &buf2) {
|
size_t addBuffer(const SBuffer &buf2) {
|
||||||
if (len() + buf2.len() <= size()) {
|
if (len() + buf2.len() <= size()) {
|
||||||
|
@ -152,6 +165,20 @@ public:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if no NULL is found, returns length until the end of the buffer
|
||||||
|
inline size_t strlen(const size_t offset) const {
|
||||||
|
return strnlen((const char*) &_buf->buf[offset], len() - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlen_s(const size_t offset) const {
|
||||||
|
size_t slen = this->strlen(offset);
|
||||||
|
if (slen == len() - offset) {
|
||||||
|
return 0; // we didn't find a NULL char
|
||||||
|
} else {
|
||||||
|
return slen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SBuffer subBuffer(const size_t start, size_t len) const {
|
SBuffer subBuffer(const size_t start, size_t len) const {
|
||||||
if (start >= _buf->len) {
|
if (start >= _buf->len) {
|
||||||
len = 0;
|
len = 0;
|
||||||
|
|
|
@ -986,8 +986,10 @@ void CmndSensorRetain(void)
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||||
|
|
||||||
const static uint16_t tls_spi_start_sector = SPIFFS_END + 4; // 0xXXFF
|
// const static uint16_t tls_spi_start_sector = SPIFFS_END + 4; // 0xXXFF
|
||||||
const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000
|
// const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000
|
||||||
|
const static uint16_t tls_spi_start_sector = 0xFF; // Force last bank of first MB
|
||||||
|
const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000
|
||||||
const static size_t tls_spi_len = 0x1000; // 4kb blocs
|
const static size_t tls_spi_len = 0x1000; // 4kb blocs
|
||||||
const static size_t tls_block_offset = 0x0400;
|
const static size_t tls_block_offset = 0x0400;
|
||||||
const static size_t tls_block_len = 0x0400; // 1kb
|
const static size_t tls_block_len = 0x0400; // 1kb
|
||||||
|
|
|
@ -22,6 +22,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#ifndef ZIGBEE_SAVE_DELAY_SECONDS
|
||||||
|
#define ZIGBEE_SAVE_DELAY_SECONDS 10; // wait for 10s before saving Zigbee info
|
||||||
|
#endif
|
||||||
|
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
|
||||||
|
|
||||||
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value);
|
typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value);
|
||||||
|
|
||||||
|
@ -57,6 +61,16 @@ class Z_Devices {
|
||||||
public:
|
public:
|
||||||
Z_Devices() {};
|
Z_Devices() {};
|
||||||
|
|
||||||
|
// Probe the existence of device keys
|
||||||
|
// Results:
|
||||||
|
// - 0x0000 = not found
|
||||||
|
// - 0xFFFF = bad parameter
|
||||||
|
// - 0x<shortaddr> = the device's short address
|
||||||
|
uint16_t isKnownShortAddr(uint16_t shortaddr) const;
|
||||||
|
uint16_t isKnownLongAddr(uint64_t longaddr) const;
|
||||||
|
uint16_t isKnownIndex(uint32_t index) const;
|
||||||
|
uint16_t isKnownFriendlyName(const char * name) 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);
|
||||||
|
@ -74,13 +88,14 @@ public:
|
||||||
|
|
||||||
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);
|
||||||
void setFriendlyNameId(uint16_t shortaddr, const char * str);
|
void setFriendlyName(uint16_t shortaddr, const char * str);
|
||||||
|
const String * getFriendlyName(uint16_t) const;
|
||||||
|
|
||||||
// device just seen on the network, update the lastSeen field
|
// device just seen on the network, update the lastSeen field
|
||||||
void updateLastSeen(uint16_t shortaddr);
|
void updateLastSeen(uint16_t shortaddr);
|
||||||
|
|
||||||
// Dump json
|
// Dump json
|
||||||
String dump(uint32_t dump_mode, int32_t device_num = 0) const;
|
String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const;
|
||||||
|
|
||||||
// Timers
|
// Timers
|
||||||
void resetTimer(uint32_t shortaddr);
|
void resetTimer(uint32_t shortaddr);
|
||||||
|
@ -89,13 +104,32 @@ public:
|
||||||
|
|
||||||
// Append or clear attributes Json structure
|
// Append or clear attributes Json structure
|
||||||
void jsonClear(uint16_t shortaddr);
|
void jsonClear(uint16_t shortaddr);
|
||||||
void jsonAppend(uint16_t shortaddr, JsonObject &values);
|
void jsonAppend(uint16_t shortaddr, const JsonObject &values);
|
||||||
const JsonObject *jsonGet(uint16_t shortaddr);
|
const JsonObject *jsonGet(uint16_t shortaddr);
|
||||||
const void jsonPublish(uint16_t shortaddr); // publish the json message and clear buffer
|
void jsonPublishFlush(uint16_t shortaddr); // publish the json message and clear buffer
|
||||||
bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values);
|
bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values);
|
||||||
|
void jsonPublishNow(uint16_t shortaddr, JsonObject &values);
|
||||||
|
|
||||||
|
// Iterator
|
||||||
|
size_t devicesSize(void) const {
|
||||||
|
return _devices.size();
|
||||||
|
}
|
||||||
|
const Z_Device &devicesAt(size_t i) const {
|
||||||
|
return _devices.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove device from list
|
||||||
|
bool removeDevice(uint16_t shortaddr);
|
||||||
|
|
||||||
|
// Mark data as 'dirty' and requiring to save in Flash
|
||||||
|
void dirty(void);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Z_Device> _devices = {};
|
std::vector<Z_Device> _devices = {};
|
||||||
|
uint32_t _saveTimer = 0;
|
||||||
|
|
||||||
template < typename T>
|
template < typename T>
|
||||||
static bool findInVector(const std::vector<T> & vecOfElements, const T & element);
|
static bool findInVector(const std::vector<T> & vecOfElements, const T & element);
|
||||||
|
@ -109,8 +143,9 @@ private:
|
||||||
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
|
||||||
|
|
||||||
int32_t findShortAddr(uint16_t shortaddr);
|
int32_t findShortAddr(uint16_t shortaddr) const;
|
||||||
int32_t findLongAddr(uint64_t longaddr);
|
int32_t findLongAddr(uint64_t longaddr) const;
|
||||||
|
int32_t findFriendlyName(const char * name) const;
|
||||||
|
|
||||||
void _updateLastSeen(Z_Device &device) {
|
void _updateLastSeen(Z_Device &device) {
|
||||||
if (&device != nullptr) {
|
if (&device != nullptr) {
|
||||||
|
@ -187,6 +222,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
||||||
nullptr, nullptr };
|
nullptr, nullptr };
|
||||||
device.json_buffer = new DynamicJsonBuffer();
|
device.json_buffer = new DynamicJsonBuffer();
|
||||||
_devices.push_back(device);
|
_devices.push_back(device);
|
||||||
|
dirty();
|
||||||
return _devices.back();
|
return _devices.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +234,7 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) {
|
||||||
// Out:
|
// Out:
|
||||||
// index in _devices of entry, -1 if not found
|
// index in _devices of entry, -1 if not found
|
||||||
//
|
//
|
||||||
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) {
|
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const {
|
||||||
if (!shortaddr) { return -1; } // does not make sense to look for 0x0000 shortaddr (localhost)
|
if (!shortaddr) { return -1; } // does not make sense to look for 0x0000 shortaddr (localhost)
|
||||||
int32_t found = 0;
|
int32_t found = 0;
|
||||||
if (shortaddr) {
|
if (shortaddr) {
|
||||||
|
@ -217,7 +253,7 @@ int32_t Z_Devices::findShortAddr(uint16_t shortaddr) {
|
||||||
// Out:
|
// Out:
|
||||||
// index in _devices of entry, -1 if not found
|
// index in _devices of entry, -1 if not found
|
||||||
//
|
//
|
||||||
int32_t Z_Devices::findLongAddr(uint64_t longaddr) {
|
int32_t Z_Devices::findLongAddr(uint64_t longaddr) const {
|
||||||
if (!longaddr) { return -1; }
|
if (!longaddr) { return -1; }
|
||||||
int32_t found = 0;
|
int32_t found = 0;
|
||||||
if (longaddr) {
|
if (longaddr) {
|
||||||
|
@ -228,6 +264,66 @@ int32_t Z_Devices::findLongAddr(uint64_t longaddr) {
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// Scan all devices to find a corresponding friendlyNme
|
||||||
|
// Looks info device.friendlyName entry
|
||||||
|
// In:
|
||||||
|
// friendlyName (null terminated, should not be empty)
|
||||||
|
// Out:
|
||||||
|
// index in _devices of entry, -1 if not found
|
||||||
|
//
|
||||||
|
int32_t Z_Devices::findFriendlyName(const char * name) const {
|
||||||
|
if (!name) { return -1; } // if pointer is null
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
int32_t found = 0;
|
||||||
|
if (name_len) {
|
||||||
|
for (auto &elem : _devices) {
|
||||||
|
if (elem.friendlyName == name) { return found; }
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probe if device is already known but don't create any entry
|
||||||
|
uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const {
|
||||||
|
int32_t found = findShortAddr(shortaddr);
|
||||||
|
if (found >= 0) {
|
||||||
|
return shortaddr;
|
||||||
|
} else {
|
||||||
|
return 0; // unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const {
|
||||||
|
int32_t found = findLongAddr(longaddr);
|
||||||
|
if (found >= 0) {
|
||||||
|
const Z_Device & device = devicesAt(found);
|
||||||
|
return device.shortaddr; // can be zero, if not yet registered
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Z_Devices::isKnownIndex(uint32_t index) const {
|
||||||
|
if (index < devicesSize()) {
|
||||||
|
const Z_Device & device = devicesAt(index);
|
||||||
|
return device.shortaddr;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
|
||||||
|
if ((!name) || (0 == strlen(name))) { return 0xFFFF; } // 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
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// We have a seen a shortaddr on the network, get the corresponding
|
// We have a seen a shortaddr on the network, get the corresponding
|
||||||
|
@ -252,6 +348,17 @@ Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) {
|
||||||
return createDeviceEntry(0, longaddr);
|
return createDeviceEntry(0, longaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove device from list, return true if it was known, false if it was not recorded
|
||||||
|
bool Z_Devices::removeDevice(uint16_t shortaddr) {
|
||||||
|
int32_t found = findShortAddr(shortaddr);
|
||||||
|
if (found >= 0) {
|
||||||
|
_devices.erase(_devices.begin() + found);
|
||||||
|
dirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// We have just seen a device on the network, update the info based on short/long addr
|
// We have just seen a device on the network, update the info based on short/long addr
|
||||||
// In:
|
// In:
|
||||||
|
@ -270,15 +377,18 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
||||||
// erase the previous shortaddr
|
// erase the previous shortaddr
|
||||||
_devices.erase(_devices.begin() + s_found);
|
_devices.erase(_devices.begin() + s_found);
|
||||||
updateLastSeen(shortaddr);
|
updateLastSeen(shortaddr);
|
||||||
|
dirty();
|
||||||
}
|
}
|
||||||
} else if (s_found >= 0) {
|
} else if (s_found >= 0) {
|
||||||
// shortaddr already exists but longaddr not
|
// shortaddr already exists but longaddr not
|
||||||
// add the longaddr to the entry
|
// add the longaddr to the entry
|
||||||
_devices[s_found].longaddr = longaddr;
|
_devices[s_found].longaddr = longaddr;
|
||||||
updateLastSeen(shortaddr);
|
updateLastSeen(shortaddr);
|
||||||
|
dirty();
|
||||||
} else if (l_found >= 0) {
|
} else if (l_found >= 0) {
|
||||||
// longaddr entry exists, update shortaddr
|
// longaddr entry exists, update shortaddr
|
||||||
_devices[l_found].shortaddr = shortaddr;
|
_devices[l_found].shortaddr = shortaddr;
|
||||||
|
dirty();
|
||||||
} else {
|
} else {
|
||||||
// neither short/lonf addr are found.
|
// neither short/lonf addr are found.
|
||||||
if (shortaddr || longaddr) {
|
if (shortaddr || longaddr) {
|
||||||
|
@ -298,6 +408,7 @@ void Z_Devices::addEndoint(uint16_t shortaddr, uint8_t endpoint) {
|
||||||
_updateLastSeen(device);
|
_updateLastSeen(device);
|
||||||
if (findEndpointInVector(device.endpoints, ep_profile) < 0) {
|
if (findEndpointInVector(device.endpoints, ep_profile) < 0) {
|
||||||
device.endpoints.push_back(ep_profile);
|
device.endpoints.push_back(ep_profile);
|
||||||
|
dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,8 +421,12 @@ void Z_Devices::addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t
|
||||||
int32_t found = findEndpointInVector(device.endpoints, ep_profile);
|
int32_t found = findEndpointInVector(device.endpoints, ep_profile);
|
||||||
if (found < 0) {
|
if (found < 0) {
|
||||||
device.endpoints.push_back(ep_profile);
|
device.endpoints.push_back(ep_profile);
|
||||||
|
dirty();
|
||||||
} else {
|
} else {
|
||||||
device.endpoints[found] = ep_profile;
|
if (device.endpoints[found] != ep_profile) {
|
||||||
|
device.endpoints[found] = ep_profile;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,10 +439,12 @@ void Z_Devices::addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluste
|
||||||
if (!out) {
|
if (!out) {
|
||||||
if (!findInVector(device.clusters_in, ep_cluster)) {
|
if (!findInVector(device.clusters_in, ep_cluster)) {
|
||||||
device.clusters_in.push_back(ep_cluster);
|
device.clusters_in.push_back(ep_cluster);
|
||||||
|
dirty();
|
||||||
}
|
}
|
||||||
} else { // out
|
} else { // out
|
||||||
if (!findInVector(device.clusters_out, ep_cluster)) {
|
if (!findInVector(device.clusters_out, ep_cluster)) {
|
||||||
device.clusters_out.push_back(ep_cluster);
|
device.clusters_out.push_back(ep_cluster);
|
||||||
|
dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -353,18 +470,32 @@ void Z_Devices::setManufId(uint16_t shortaddr, const char * str) {
|
||||||
if (&device == nullptr) { return; } // don't crash if not found
|
if (&device == nullptr) { return; } // don't crash if not found
|
||||||
_updateLastSeen(device);
|
_updateLastSeen(device);
|
||||||
device.manufacturerId = str;
|
device.manufacturerId = str;
|
||||||
|
dirty();
|
||||||
}
|
}
|
||||||
void Z_Devices::setModelId(uint16_t shortaddr, const char * str) {
|
void Z_Devices::setModelId(uint16_t shortaddr, const char * str) {
|
||||||
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
|
||||||
_updateLastSeen(device);
|
_updateLastSeen(device);
|
||||||
device.modelId = str;
|
device.modelId = str;
|
||||||
|
dirty();
|
||||||
}
|
}
|
||||||
void Z_Devices::setFriendlyNameId(uint16_t shortaddr, const char * str) {
|
void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) {
|
||||||
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
|
||||||
_updateLastSeen(device);
|
_updateLastSeen(device);
|
||||||
device.friendlyName = str;
|
device.friendlyName = str;
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const String * Z_Devices::getFriendlyName(uint16_t shortaddr) const {
|
||||||
|
int32_t found = findShortAddr(shortaddr);
|
||||||
|
if (found >= 0) {
|
||||||
|
const Z_Device & device = devicesAt(found);
|
||||||
|
if (device.friendlyName.length() > 0) {
|
||||||
|
return &device.friendlyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// device just seen on the network, update the lastSeen field
|
// device just seen on the network, update the lastSeen field
|
||||||
|
@ -398,19 +529,22 @@ void Z_Devices::setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster,
|
||||||
|
|
||||||
// Run timer at each tick
|
// Run timer at each tick
|
||||||
void Z_Devices::runTimer(void) {
|
void Z_Devices::runTimer(void) {
|
||||||
uint32_t now = millis();
|
|
||||||
|
|
||||||
for (std::vector<Z_Device>::iterator it = _devices.begin(); it != _devices.end(); ++it) {
|
for (std::vector<Z_Device>::iterator it = _devices.begin(); it != _devices.end(); ++it) {
|
||||||
Z_Device &device = *it;
|
Z_Device &device = *it;
|
||||||
uint16_t shortaddr = device.shortaddr;
|
uint16_t shortaddr = device.shortaddr;
|
||||||
|
|
||||||
uint32_t timer = device.timer;
|
uint32_t timer = device.timer;
|
||||||
if ((timer) && (timer <= now)) {
|
if ((timer) && TimeReached(timer)) {
|
||||||
device.timer = 0; // cancel the timer before calling, so the callback can set another timer
|
device.timer = 0; // cancel the timer before calling, so the callback can set another timer
|
||||||
// trigger the timer
|
// trigger the timer
|
||||||
(*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value);
|
(*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// save timer
|
||||||
|
if ((_saveTimer) && TimeReached(_saveTimer)) {
|
||||||
|
saveZigbeeDevices();
|
||||||
|
_saveTimer = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z_Devices::jsonClear(uint16_t shortaddr) {
|
void Z_Devices::jsonClear(uint16_t shortaddr) {
|
||||||
|
@ -482,7 +616,7 @@ bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Z_Devices::jsonAppend(uint16_t shortaddr, JsonObject &values) {
|
void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) {
|
||||||
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 (&values == nullptr) { return; }
|
if (&values == nullptr) { return; }
|
||||||
|
@ -500,40 +634,103 @@ const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) {
|
||||||
return device.json;
|
return device.json;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void Z_Devices::jsonPublish(uint16_t shortaddr) {
|
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
||||||
const JsonObject *json = zigbee_devices.jsonGet(shortaddr);
|
Z_Device & device = getShortAddr(shortaddr);
|
||||||
if (json == nullptr) { return; } // don't crash if not found
|
if (&device == nullptr) { return; } // don't crash if not found
|
||||||
|
JsonObject * json = device.json;
|
||||||
|
if (json == nullptr) { return; } // abort if nothing in buffer
|
||||||
|
|
||||||
|
const String * fname = zigbee_devices.getFriendlyName(shortaddr);
|
||||||
|
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
||||||
|
|
||||||
|
if (use_fname) {
|
||||||
|
// we need to add the Device short_addr inside the JSON
|
||||||
|
char sa[8];
|
||||||
|
snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr);
|
||||||
|
json->set(F(D_JSON_ZIGBEE_DEVICE), sa);
|
||||||
|
} else if (fname) {
|
||||||
|
json->set(F(D_JSON_NAME), (char*) fname);
|
||||||
|
}
|
||||||
|
|
||||||
String msg = "";
|
String msg = "";
|
||||||
json->printTo(msg);
|
json->printTo(msg);
|
||||||
zigbee_devices.jsonClear(shortaddr);
|
zigbee_devices.jsonClear(shortaddr);
|
||||||
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
|
|
||||||
|
if (use_fname) {
|
||||||
|
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str());
|
||||||
|
} else {
|
||||||
|
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str());
|
||||||
|
}
|
||||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
||||||
XdrvRulesProcess();
|
XdrvRulesProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) {
|
||||||
|
jsonPublishFlush(shortaddr); // flush any previous buffer
|
||||||
|
jsonAppend(shortaddr, values);
|
||||||
|
jsonPublishFlush(shortaddr); // publish now
|
||||||
|
}
|
||||||
|
|
||||||
|
void Z_Devices::dirty(void) {
|
||||||
|
_saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the command parameters for either:
|
||||||
|
// - a short address starting with "0x", example: 0x1234
|
||||||
|
// - 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 0; }
|
||||||
|
size_t param_len = strlen(param);
|
||||||
|
char dataBuf[param_len + 1];
|
||||||
|
strcpy(dataBuf, param);
|
||||||
|
RemoveSpace(dataBuf);
|
||||||
|
uint16_t shortaddr = 0;
|
||||||
|
|
||||||
|
if (strlen(dataBuf) < 4) {
|
||||||
|
// simple number 0..99
|
||||||
|
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) {
|
||||||
|
shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1);
|
||||||
|
}
|
||||||
|
} else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) {
|
||||||
|
// starts with 0x
|
||||||
|
if (strlen(dataBuf) < 18) {
|
||||||
|
// expect a short address
|
||||||
|
shortaddr = strtoull(dataBuf, nullptr, 0);
|
||||||
|
if (short_must_be_known) {
|
||||||
|
shortaddr = zigbee_devices.isKnownShortAddr(shortaddr);
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// expect a Friendly Name
|
||||||
|
shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shortaddr;
|
||||||
|
}
|
||||||
|
|
||||||
// Dump the internal memory of Zigbee devices
|
// Dump the internal memory of Zigbee devices
|
||||||
// Mode = 1: simple dump of devices addresses and names
|
// Mode = 1: simple dump of devices addresses
|
||||||
// Mode = 2: Mode 1 + also dump the endpoints, profiles and clusters
|
// Mode = 2: simple dump of devices addresses and names
|
||||||
String Z_Devices::dump(uint32_t dump_mode, int32_t device_num) const {
|
// Mode = 3: Mode 2 + also dump the endpoints, profiles and clusters
|
||||||
|
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
|
||||||
DynamicJsonBuffer jsonBuffer;
|
DynamicJsonBuffer jsonBuffer;
|
||||||
JsonArray& json = jsonBuffer.createArray();
|
JsonArray& json = jsonBuffer.createArray();
|
||||||
JsonArray& devices = json;
|
JsonArray& devices = json;
|
||||||
//JsonArray& devices = json.createNestedArray(F("ZigbeeDevices"));
|
|
||||||
|
|
||||||
// if device_num == 0, then we show all devices.
|
|
||||||
// When no payload, the default is -99. In this case change it to 0.
|
|
||||||
if (device_num < 0) { device_num = 0; }
|
|
||||||
|
|
||||||
uint32_t device_current = 1;
|
|
||||||
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it, ++device_current) {
|
|
||||||
// ignore non-current device, if specified device is non-zero
|
|
||||||
if ((device_num > 0) && (device_num != device_current)) { continue; }
|
|
||||||
|
|
||||||
|
for (std::vector<Z_Device>::const_iterator it = _devices.begin(); it != _devices.end(); ++it) {
|
||||||
const Z_Device& device = *it;
|
const Z_Device& device = *it;
|
||||||
uint16_t shortaddr = device.shortaddr;
|
uint16_t shortaddr = device.shortaddr;
|
||||||
char hex[20];
|
char hex[22];
|
||||||
|
|
||||||
|
// ignore non-current device, if specified device is non-zero
|
||||||
|
if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; }
|
||||||
|
|
||||||
JsonObject& dev = devices.createNestedObject();
|
JsonObject& dev = devices.createNestedObject();
|
||||||
|
|
||||||
|
@ -545,7 +742,9 @@ String Z_Devices::dump(uint32_t dump_mode, int32_t device_num) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (2 <= dump_mode) {
|
if (2 <= dump_mode) {
|
||||||
Uint64toHex(device.longaddr, hex, 64);
|
hex[0] = '0'; // prefix with '0x'
|
||||||
|
hex[1] = 'x';
|
||||||
|
Uint64toHex(device.longaddr, &hex[2], 64);
|
||||||
dev[F("IEEEAddr")] = hex;
|
dev[F("IEEEAddr")] = hex;
|
||||||
if (device.modelId.length() > 0) {
|
if (device.modelId.length() > 0) {
|
||||||
dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId;
|
dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId;
|
||||||
|
|
|
@ -0,0 +1,332 @@
|
||||||
|
/*
|
||||||
|
xdrv_23_zigbee.ino - zigbee support for Tasmota
|
||||||
|
|
||||||
|
Copyright (C) 2020 Theo Arends and Stephan Hadinger
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef USE_ZIGBEE
|
||||||
|
|
||||||
|
// Ensure persistence of devices into Flash
|
||||||
|
//
|
||||||
|
// Structure:
|
||||||
|
// (from file info):
|
||||||
|
// uint16 - start address in Flash (offset)
|
||||||
|
// uint16 - length in bytes (makes sure parsing stops)
|
||||||
|
//
|
||||||
|
// File structure:
|
||||||
|
// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased)
|
||||||
|
//
|
||||||
|
// [Array of devices]
|
||||||
|
// [Offset = 2]
|
||||||
|
// uint8 - length of revice record
|
||||||
|
// uint16 - short address
|
||||||
|
// uint64 - long IEEE address
|
||||||
|
// uint8 - number of endpoints
|
||||||
|
// [Array of endpoints]
|
||||||
|
// uint8 - endpoint number
|
||||||
|
// uint16 - profileID of the endpoint
|
||||||
|
// Array of uint8 - clusters In codes, 0xFF end marker
|
||||||
|
// Array of uint8 - clusters Out codes, 0xFF end marker
|
||||||
|
//
|
||||||
|
// str - ModelID (null terminated C string, 32 chars max)
|
||||||
|
// str - Manuf (null terminated C string, 32 chars max)
|
||||||
|
// reserved for extensions
|
||||||
|
|
||||||
|
// Memory footprint
|
||||||
|
const static uint16_t z_spi_start_sector = 0xFF; // Force last bank of first MB
|
||||||
|
const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000
|
||||||
|
const static uint8_t* z_dev_start = z_spi_start + 0x0800; // 0x402FF800 - 2KB
|
||||||
|
const static size_t z_spi_len = 0x1000; // 4kb blocs
|
||||||
|
const static size_t z_block_offset = 0x0800;
|
||||||
|
const static size_t z_block_len = 0x0800; // 2kb
|
||||||
|
|
||||||
|
class z_flashdata_t {
|
||||||
|
public:
|
||||||
|
uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2'
|
||||||
|
uint16_t len; // len of object
|
||||||
|
uint16_t reserved; // align on 4 bytes boundary
|
||||||
|
};
|
||||||
|
|
||||||
|
const static uint32_t ZIGB_NAME = 0x3167697A; // 'zig1' little endian
|
||||||
|
const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); // 2040
|
||||||
|
|
||||||
|
// encoding for the most commonly 32 clusters, used for binary encoding
|
||||||
|
const uint16_t Z_ClusterNumber[] PROGMEM = {
|
||||||
|
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
||||||
|
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
||||||
|
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
||||||
|
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
|
||||||
|
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||||
|
0x0100, 0x0101, 0x0102,
|
||||||
|
0x0201, 0x0202, 0x0203, 0x0204,
|
||||||
|
0x0300, 0x0301,
|
||||||
|
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406,
|
||||||
|
0x0500, 0x0501, 0x0502,
|
||||||
|
0x0700, 0x0701, 0x0702,
|
||||||
|
0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05,
|
||||||
|
0x1000,
|
||||||
|
0xFC0F,
|
||||||
|
};
|
||||||
|
|
||||||
|
// convert a 1 byte cluster code to the actual cluster number
|
||||||
|
uint16_t fromClusterCode(uint8_t c) {
|
||||||
|
if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) {
|
||||||
|
return 0xFFFF; // invalid
|
||||||
|
}
|
||||||
|
return pgm_read_word(&Z_ClusterNumber[c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert a cluster number to 1 byte, or 0xFF if not in table
|
||||||
|
uint8_t toClusterCode(uint16_t c) {
|
||||||
|
for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) {
|
||||||
|
if (c == pgm_read_word(&Z_ClusterNumber[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0xFF; // not found
|
||||||
|
}
|
||||||
|
|
||||||
|
class SBuffer hibernateDevice(const struct Z_Device &device) {
|
||||||
|
SBuffer buf(128);
|
||||||
|
|
||||||
|
buf.add8(0x00); // overall length, will be updated later
|
||||||
|
buf.add16(device.shortaddr);
|
||||||
|
buf.add64(device.longaddr);
|
||||||
|
uint32_t endpoints = device.endpoints.size();
|
||||||
|
if (endpoints > 254) { endpoints = 254; }
|
||||||
|
buf.add8(endpoints);
|
||||||
|
// iterate on 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;
|
||||||
|
|
||||||
|
buf.add8(endpoint);
|
||||||
|
buf.add16(profileId);
|
||||||
|
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) {
|
||||||
|
uint8_t clusterCode = toClusterCode(cluster);
|
||||||
|
if (0xFF != clusterCode) { buf.add8(clusterCode); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModelID
|
||||||
|
size_t model_len = device.modelId.length();
|
||||||
|
if (model_len > 32) { model_len = 32; } // max 32 chars
|
||||||
|
buf.addBuffer(device.modelId.c_str(), model_len);
|
||||||
|
buf.add8(0x00); // end of string marker
|
||||||
|
|
||||||
|
// ManufID
|
||||||
|
size_t manuf_len = device.manufacturerId.length();
|
||||||
|
if (manuf_len > 32) {manuf_len = 32; } // max 32 chars
|
||||||
|
buf.addBuffer(device.manufacturerId.c_str(), manuf_len);
|
||||||
|
buf.add8(0x00); // end of string marker
|
||||||
|
|
||||||
|
// FriendlyName
|
||||||
|
size_t frname_len = device.friendlyName.length();
|
||||||
|
if (frname_len > 32) {frname_len = 32; } // max 32 chars
|
||||||
|
buf.addBuffer(device.friendlyName.c_str(), frname_len);
|
||||||
|
buf.add8(0x00); // end of string marker
|
||||||
|
|
||||||
|
// update overall length
|
||||||
|
buf.set8(0, buf.len());
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SBuffer hibernateDevices(void) {
|
||||||
|
SBuffer buf(2048);
|
||||||
|
|
||||||
|
size_t devices_size = zigbee_devices.devicesSize();
|
||||||
|
if (devices_size > 32) { devices_size = 32; } // arbitrarily limit to 32 devices, for now
|
||||||
|
buf.add8(devices_size); // number of devices
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < devices_size; i++) {
|
||||||
|
const Z_Device & device = zigbee_devices.devicesAt(i);
|
||||||
|
const SBuffer buf_device = hibernateDevice(device);
|
||||||
|
buf.addBuffer(buf_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t buf_len = buf.len();
|
||||||
|
if (buf_len > 2040) {
|
||||||
|
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log
|
||||||
|
char *hex_char = (char*) malloc((buf_len * 2) + 2);
|
||||||
|
if (hex_char) {
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZigbeeFlashStore %s"),
|
||||||
|
ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2));
|
||||||
|
free(hex_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hidrateDevices(const SBuffer &buf) {
|
||||||
|
uint32_t buf_len = buf.len();
|
||||||
|
if (buf_len <= 10) { return; }
|
||||||
|
|
||||||
|
uint32_t k = 0;
|
||||||
|
uint32_t num_devices = buf.get8(k++);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
|
||||||
|
uint32_t dev_record_len = buf.get8(k);
|
||||||
|
|
||||||
|
SBuffer buf_d = buf.subBuffer(k, dev_record_len);
|
||||||
|
|
||||||
|
uint32_t d = 1; // index in device buffer
|
||||||
|
uint16_t shortaddr = buf_d.get16(d); d += 2;
|
||||||
|
uint64_t longaddr = buf_d.get64(d); d += 8;
|
||||||
|
zigbee_devices.updateDevice(shortaddr, longaddr); // update device's addresses
|
||||||
|
|
||||||
|
uint32_t endpoints = buf_d.get8(d++);
|
||||||
|
for (uint32_t j = 0; j < endpoints; j++) {
|
||||||
|
uint8_t ep = buf_d.get8(d++);
|
||||||
|
uint16_t ep_profile = buf_d.get16(d); d += 2;
|
||||||
|
zigbee_devices.addEndointProfile(shortaddr, ep, ep_profile);
|
||||||
|
|
||||||
|
// in clusters
|
||||||
|
while (d < dev_record_len) { // safe guard against overflow
|
||||||
|
uint8_t ep_cluster = buf_d.get8(d++);
|
||||||
|
if (0xFF == ep_cluster) { break; } // end of block
|
||||||
|
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), false);
|
||||||
|
}
|
||||||
|
// out clusters
|
||||||
|
while (d < dev_record_len) { // safe guard against overflow
|
||||||
|
uint8_t ep_cluster = buf_d.get8(d++);
|
||||||
|
if (0xFF == ep_cluster) { break; } // end of block
|
||||||
|
zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse 3 strings
|
||||||
|
char empty[] = "";
|
||||||
|
|
||||||
|
// ManufID
|
||||||
|
uint32_t s_len = buf_d.strlen_s(d);
|
||||||
|
char *ptr = s_len ? buf_d.charptr(d) : empty;
|
||||||
|
zigbee_devices.setModelId(shortaddr, ptr);
|
||||||
|
d += s_len + 1;
|
||||||
|
|
||||||
|
// ManufID
|
||||||
|
s_len = buf_d.strlen_s(d);
|
||||||
|
ptr = s_len ? buf_d.charptr(d) : empty;
|
||||||
|
zigbee_devices.setManufId(shortaddr, ptr);
|
||||||
|
d += s_len + 1;
|
||||||
|
|
||||||
|
// FriendlyName
|
||||||
|
s_len = buf_d.strlen_s(d);
|
||||||
|
ptr = s_len ? buf_d.charptr(d) : empty;
|
||||||
|
zigbee_devices.setFriendlyName(shortaddr, ptr);
|
||||||
|
d += s_len + 1;
|
||||||
|
|
||||||
|
// next iteration
|
||||||
|
k += dev_record_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadZigbeeDevices(void) {
|
||||||
|
z_flashdata_t flashdata;
|
||||||
|
memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t));
|
||||||
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len);
|
||||||
|
|
||||||
|
// Check the signature
|
||||||
|
if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) {
|
||||||
|
uint16_t buf_len = flashdata.len;
|
||||||
|
// parse what seems to be a valid entry
|
||||||
|
SBuffer buf(buf_len);
|
||||||
|
buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len);
|
||||||
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len);
|
||||||
|
hidrateDevices(buf);
|
||||||
|
} else {
|
||||||
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveZigbeeDevices(void) {
|
||||||
|
SBuffer buf = hibernateDevices();
|
||||||
|
size_t buf_len = buf.len();
|
||||||
|
if (buf_len > Z_MAX_FLASH) {
|
||||||
|
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// first copy SPI buffer into ram
|
||||||
|
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
|
||||||
|
if (!spi_buffer) {
|
||||||
|
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// copy the flash into RAM to make local change, and write back the whole buffer
|
||||||
|
ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||||
|
|
||||||
|
z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset);
|
||||||
|
flashdata->name = ZIGB_NAME;
|
||||||
|
flashdata->len = buf_len;
|
||||||
|
flashdata->reserved = 0;
|
||||||
|
|
||||||
|
memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len);
|
||||||
|
|
||||||
|
// buffer is now ready, write it back
|
||||||
|
if (ESP.flashEraseSector(z_spi_start_sector)) {
|
||||||
|
ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(spi_buffer);
|
||||||
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase the flash area containing the ZigbeeData
|
||||||
|
void eraseZigbeeDevices(void) {
|
||||||
|
// first copy SPI buffer into ram
|
||||||
|
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
|
||||||
|
if (!spi_buffer) {
|
||||||
|
AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// copy the flash into RAM to make local change, and write back the whole buffer
|
||||||
|
ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||||
|
|
||||||
|
// Fill the Zigbee area with 0xFF
|
||||||
|
memset(spi_buffer + z_block_offset, 0xFF, z_block_len);
|
||||||
|
|
||||||
|
// buffer is now ready, write it back
|
||||||
|
if (ESP.flashEraseSector(z_spi_start_sector)) {
|
||||||
|
ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(spi_buffer);
|
||||||
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_ZIGBEE
|
|
@ -830,10 +830,10 @@ int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject&
|
||||||
|
|
||||||
// Publish a message for `"Occupancy":0` when the timer expired
|
// Publish a message for `"Occupancy":0` when the timer expired
|
||||||
int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
|
int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) {
|
||||||
// send Occupancy:false message
|
DynamicJsonBuffer jsonBuffer;
|
||||||
Response_P(PSTR("{\"" D_CMND_ZIGBEE_RECEIVED "\":{\"0x%04X\":{\"" OCCUPANCY "\":0}}}"), shortaddr);
|
JsonObject& json = jsonBuffer.createObject();
|
||||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR));
|
json[F(OCCUPANCY)] = 0;
|
||||||
XdrvRulesProcess();
|
zigbee_devices.jsonPublishNow(shortaddr, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aqara Cube
|
// Aqara Cube
|
||||||
|
|
|
@ -354,7 +354,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||||
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "starting zigbee coordinator")
|
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "starting zigbee coordinator")
|
||||||
ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
|
ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
|
||||||
ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
|
ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
|
||||||
ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
|
ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started
|
||||||
ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
|
ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
|
||||||
ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
|
ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
|
||||||
//ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // memorize info
|
//ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // memorize info
|
||||||
|
@ -386,6 +386,7 @@ ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
|
||||||
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, "Started")
|
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, "Started")
|
||||||
ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Zigbee started")
|
ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Zigbee started")
|
||||||
ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
|
ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
|
||||||
|
ZI_CALL(&Z_Load_Devices, 0)
|
||||||
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP)
|
||||||
ZI_WAIT_FOREVER()
|
ZI_WAIT_FOREVER()
|
||||||
ZI_GOTO(ZIGBEE_LABEL_READY)
|
ZI_GOTO(ZIGBEE_LABEL_READY)
|
||||||
|
|
|
@ -403,7 +403,7 @@ int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpo
|
||||||
// Post-provess for Aqara Presence Senson
|
// Post-provess for Aqara Presence Senson
|
||||||
Z_AqaraOccupancy(shortaddr, cluster, endpoint, json);
|
Z_AqaraOccupancy(shortaddr, cluster, endpoint, json);
|
||||||
|
|
||||||
zigbee_devices.jsonPublish(shortaddr);
|
zigbee_devices.jsonPublishFlush(shortaddr);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,8 +433,15 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
||||||
|
|
||||||
DynamicJsonBuffer jsonBuffer;
|
DynamicJsonBuffer jsonBuffer;
|
||||||
JsonObject& json_root = jsonBuffer.createObject();
|
JsonObject& json_root = jsonBuffer.createObject();
|
||||||
|
|
||||||
JsonObject& json1 = json_root.createNestedObject(F(D_CMND_ZIGBEE_RECEIVED));
|
JsonObject& json1 = json_root.createNestedObject(F(D_CMND_ZIGBEE_RECEIVED));
|
||||||
JsonObject& json = json1.createNestedObject(shortaddr);
|
|
||||||
|
const String * fname = zigbee_devices.getFriendlyName(srcaddr);
|
||||||
|
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
||||||
|
JsonObject& json = json1.createNestedObject(use_fname ? fname->c_str() : shortaddr);
|
||||||
|
if (use_fname) {
|
||||||
|
json[F(D_JSON_ZIGBEE_DEVICE)] = shortaddr;
|
||||||
|
}
|
||||||
|
|
||||||
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) {
|
||||||
zcl_received.parseRawAttributes(json);
|
zcl_received.parseRawAttributes(json);
|
||||||
|
@ -449,6 +456,11 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
||||||
json_root.printTo(msg);
|
json_root.printTo(msg);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str());
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLRawReceived: %s"), msg.c_str());
|
||||||
|
|
||||||
|
// Add friendly name
|
||||||
|
if ((!use_fname) && (fname)) {
|
||||||
|
json[F(D_JSON_ZIGBEE_NAME)] = (char*)fname->c_str(); // (char*) will force a copy of the string
|
||||||
|
}
|
||||||
|
|
||||||
zcl_received.postProcessAttributes(srcaddr, json);
|
zcl_received.postProcessAttributes(srcaddr, json);
|
||||||
// Add linkquality
|
// Add linkquality
|
||||||
json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality;
|
json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality;
|
||||||
|
@ -457,7 +469,7 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
||||||
// Prepare for publish
|
// Prepare for publish
|
||||||
if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
|
if (zigbee_devices.jsonIsConflict(srcaddr, json)) {
|
||||||
// there is conflicting values, force a publish of the previous message now and don't coalesce
|
// there is conflicting values, force a publish of the previous message now and don't coalesce
|
||||||
zigbee_devices.jsonPublish(srcaddr);
|
zigbee_devices.jsonPublishFlush(srcaddr);
|
||||||
} else {
|
} else {
|
||||||
zigbee_devices.jsonAppend(srcaddr, json);
|
zigbee_devices.jsonAppend(srcaddr, json);
|
||||||
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes);
|
zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes);
|
||||||
|
@ -511,6 +523,12 @@ int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t Z_Load_Devices(uint8_t value) {
|
||||||
|
// try to hidrate from known devices
|
||||||
|
loadZigbeeDevices();
|
||||||
|
return 0; // continue
|
||||||
|
}
|
||||||
|
|
||||||
int32_t Z_State_Ready(uint8_t value) {
|
int32_t Z_State_Ready(uint8_t value) {
|
||||||
zigbee.init_phase = false; // initialization phase complete
|
zigbee.init_phase = false; // initialization phase complete
|
||||||
return 0; // continue
|
return 0; // continue
|
||||||
|
|
|
@ -31,13 +31,15 @@ TasmotaSerial *ZigbeeSerial = nullptr;
|
||||||
const char kZigbeeCommands[] PROGMEM = "|"
|
const char kZigbeeCommands[] PROGMEM = "|"
|
||||||
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
|
||||||
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|"
|
||||||
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE
|
D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|"
|
||||||
|
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME
|
||||||
;
|
;
|
||||||
|
|
||||||
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||||
&CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
|
&CmndZigbeeZNPSend, &CmndZigbeePermitJoin,
|
||||||
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
|
&CmndZigbeeStatus, &CmndZigbeeReset, &CmndZigbeeSend,
|
||||||
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive
|
&CmndZigbeeProbe, &CmndZigbeeRead, &CmndZigbeeZNPReceive,
|
||||||
|
&CmndZigbeeForget, &CmndZigbeeSave, &CmndZigbeeName
|
||||||
};
|
};
|
||||||
|
|
||||||
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
int32_t ZigbeeProcessInput(class SBuffer &buf) {
|
||||||
|
@ -254,6 +256,7 @@ void CmndZigbeeReset(void) {
|
||||||
switch (XdrvMailbox.payload) {
|
switch (XdrvMailbox.payload) {
|
||||||
case 1:
|
case 1:
|
||||||
ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET));
|
ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET));
|
||||||
|
eraseZigbeeDevices();
|
||||||
restart_flag = 2;
|
restart_flag = 2;
|
||||||
ResponseCmndChar(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING);
|
ResponseCmndChar(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING);
|
||||||
break;
|
break;
|
||||||
|
@ -263,13 +266,6 @@ void CmndZigbeeReset(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmndZigbeeStatus(void) {
|
|
||||||
if (ZigbeeSerial) {
|
|
||||||
String dump = zigbee_devices.dump(XdrvMailbox.index, XdrvMailbox.payload);
|
|
||||||
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CmndZigbeeZNPSendOrReceive(bool send)
|
void CmndZigbeeZNPSendOrReceive(bool send)
|
||||||
{
|
{
|
||||||
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
|
if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) {
|
||||||
|
@ -548,20 +544,68 @@ void CmndZigbeeSend(void) {
|
||||||
// Probe a specific device to get its endpoints and supported clusters
|
// Probe a specific device to get its endpoints and supported clusters
|
||||||
void CmndZigbeeProbe(void) {
|
void CmndZigbeeProbe(void) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
char dataBufUc[XdrvMailbox.data_len + 1];
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
UpperCase(dataBufUc, XdrvMailbox.data);
|
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||||
RemoveSpace(dataBufUc);
|
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||||
if (strlen(dataBufUc) < 3) { ResponseCmndChar("Invalid destination"); return; }
|
|
||||||
|
|
||||||
// TODO, for now ignore friendly names
|
|
||||||
uint16_t shortaddr = strtoull(dataBufUc, nullptr, 0);
|
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CmndZigbeeScan: short addr 0x%04X"), shortaddr);
|
|
||||||
|
|
||||||
// everything is good, we can send the command
|
// everything is good, we can send the command
|
||||||
Z_SendActiveEpReq(shortaddr);
|
Z_SendActiveEpReq(shortaddr);
|
||||||
ResponseCmndDone();
|
ResponseCmndDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specify, read or erase a Friendly Name
|
||||||
|
void CmndZigbeeName(void) {
|
||||||
|
// Syntax is:
|
||||||
|
// ZigbeeName <device_id>,<friendlyname> - assign a friendly name
|
||||||
|
// ZigbeeName <device_id> - display the current friendly name
|
||||||
|
// ZigbeeName <device_id>, - remove friendly name
|
||||||
|
//
|
||||||
|
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name
|
||||||
|
|
||||||
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
|
|
||||||
|
// check if parameters contain a comma ','
|
||||||
|
char *p;
|
||||||
|
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
|
||||||
|
|
||||||
|
// parse first part, <device_id>
|
||||||
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
||||||
|
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||||
|
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||||
|
|
||||||
|
if (p == nullptr) {
|
||||||
|
const String * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
|
||||||
|
Response_P(PSTR("{\"0x%04X\":{\"name\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName->c_str() : "");
|
||||||
|
} else {
|
||||||
|
zigbee_devices.setFriendlyName(shortaddr, p);
|
||||||
|
Response_P(PSTR("{\"0x%04X\":{\"name\":\"%s\"}}"), shortaddr, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
|
||||||
|
void CmndZigbeeForget(void) {
|
||||||
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
|
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||||
|
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||||
|
|
||||||
|
// everything is good, we can send the command
|
||||||
|
if (zigbee_devices.removeDevice(shortaddr)) {
|
||||||
|
ResponseCmndDone();
|
||||||
|
} else {
|
||||||
|
ResponseCmndChar("Unknown device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save Zigbee information to flash
|
||||||
|
void CmndZigbeeSave(void) {
|
||||||
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
|
|
||||||
|
saveZigbeeDevices();
|
||||||
|
|
||||||
|
ResponseCmndDone();
|
||||||
|
}
|
||||||
|
|
||||||
// Send an attribute read command to a device, specifying cluster and list of attributes
|
// Send an attribute read command to a device, specifying cluster and list of attributes
|
||||||
void CmndZigbeeRead(void) {
|
void CmndZigbeeRead(void) {
|
||||||
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
|
// ZigbeeRead {"Device":"0xF289","Cluster":0,"Endpoint":3,"Attr":5}
|
||||||
|
@ -633,6 +677,20 @@ void CmndZigbeePermitJoin(void)
|
||||||
ResponseCmndDone();
|
ResponseCmndDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CmndZigbeeStatus(void) {
|
||||||
|
if (ZigbeeSerial) {
|
||||||
|
if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; }
|
||||||
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
|
if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; }
|
||||||
|
if (XdrvMailbox.payload > 0) {
|
||||||
|
if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; }
|
||||||
|
}
|
||||||
|
|
||||||
|
String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr);
|
||||||
|
Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
* Interface
|
* Interface
|
||||||
\*********************************************************************************************/
|
\*********************************************************************************************/
|
||||||
|
|
Loading…
Reference in New Issue