mirror of https://github.com/arendst/Tasmota.git
Merge pull request #9491 from s-hadinger/zigee_zbdata
Add Zigbee ``ZbData`` command for better support of device specific data
This commit is contained in:
commit
6927e1d305
|
@ -106,6 +106,13 @@ void JsonGeneratorObject::add(const char* key, const String & str) {
|
|||
post();
|
||||
}
|
||||
|
||||
// Add up to 32 bits hex value
|
||||
void JsonGeneratorObject::addHex32(const char* key, uint32_t uval32) {
|
||||
char hex[16];
|
||||
snprintf_P(hex, sizeof(hex), PSTR("\"0x%8X\""), uval32);
|
||||
addStrRaw(key, hex);
|
||||
}
|
||||
|
||||
// Add a raw string, that will not be escaped.
|
||||
// Can be used to add a sub-array, sub-object or 'null', 'true', 'false' values
|
||||
void JsonGeneratorObject::addStrRaw(const char* key, const char * sval) {
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
void add(const char* key, uint32_t uval32);
|
||||
void add(const char* key, int32_t uval32);
|
||||
void add(const char* key, const String & str);
|
||||
void addHex32(const char* key, uint32_t uval32);
|
||||
void addStrRaw(const char* key, const char * sval);
|
||||
void addStr(const char* key, const char * sval);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
- Add support for inverted NeoPixelBus data line by enabling ``#define USE_WS2812_INVERTED`` (#8988)
|
||||
- Add PWM dimmer color/trigger on tap, SO88 led, DGR WITH_LOCAL flag by Paul Diem (#9474)
|
||||
- Add support for stateful ACs using ``StateMode`` in tasmota-ir.bin by Arik Yavilevich (#9472)
|
||||
- Add Zigbee ``ZbData`` command for better support of device specific data
|
||||
|
||||
## Released
|
||||
|
||||
|
|
|
@ -572,6 +572,7 @@
|
|||
#define D_CMND_ZIGBEE_RESTORE "Restore"
|
||||
#define D_CMND_ZIGBEE_CONFIG "Config"
|
||||
#define D_JSON_ZIGBEE_CONFIG "Config"
|
||||
#define D_CMND_ZIGBEE_DATA "Data"
|
||||
|
||||
// Commands xdrv_25_A4988_Stepper.ino
|
||||
#define D_CMND_MOTOR "MOTOR"
|
||||
|
|
|
@ -72,6 +72,10 @@ public:
|
|||
T & addHead(void);
|
||||
T & addHead(const T &val);
|
||||
T & addToLast(void);
|
||||
// add an element allocated externally
|
||||
// memory is free by us now -- don't free it!
|
||||
T & addHead(LList_elt<T> * elt);
|
||||
T & addToLast(LList_elt<T> * elt);
|
||||
|
||||
// iterator
|
||||
// see https://stackoverflow.com/questions/8164567/how-to-make-my-custom-type-to-work-with-range-based-for-loops
|
||||
|
@ -174,6 +178,13 @@ T & LList<T>::addHead(const T &val) {
|
|||
return elt->_val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T & LList<T>::addHead(LList_elt<T> * elt) {
|
||||
elt->next(_head); // insert at the head
|
||||
_head = elt;
|
||||
return elt->_val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T & LList<T>::addToLast(void) {
|
||||
LList_elt<T> **curr_ptr = &_head;
|
||||
|
@ -183,4 +194,15 @@ T & LList<T>::addToLast(void) {
|
|||
LList_elt<T> * elt = new LList_elt<T>(); // create element
|
||||
*curr_ptr = elt;
|
||||
return elt->_val;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T & LList<T>::addToLast(LList_elt<T> * elt) {
|
||||
LList_elt<T> **curr_ptr = &_head;
|
||||
while (*curr_ptr) {
|
||||
curr_ptr = &((*curr_ptr)->_next);
|
||||
}
|
||||
*curr_ptr = elt;
|
||||
elt->_next = nullptr;
|
||||
return elt->_val;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,779 @@
|
|||
/*
|
||||
xdrv_23_zigbee_2a_devices_impl.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
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Implementation
|
||||
\*********************************************************************************************/
|
||||
|
||||
//
|
||||
// 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
|
||||
Z_Device device(shortaddr, longaddr);
|
||||
|
||||
dirty();
|
||||
return _devices.addHead(device);
|
||||
}
|
||||
|
||||
void Z_Devices::freeDeviceEntry(Z_Device *device) {
|
||||
if (device->manufacturerId) { free(device->manufacturerId); }
|
||||
if (device->modelId) { free(device->modelId); }
|
||||
if (device->friendlyName) { free(device->friendlyName); }
|
||||
free(device);
|
||||
}
|
||||
|
||||
//
|
||||
// Scan all devices to find a corresponding shortaddr
|
||||
// Looks info device.shortaddr entry
|
||||
// In:
|
||||
// shortaddr (not BAD_SHORTADDR)
|
||||
// Out:
|
||||
// reference to device, or to device_unk if not found
|
||||
// (use foundDevice() to check if found)
|
||||
Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) {
|
||||
for (auto & elem : _devices) {
|
||||
if (elem.shortaddr == shortaddr) { return elem; }
|
||||
}
|
||||
return (Z_Device&) device_unk;
|
||||
}
|
||||
const Z_Device & Z_Devices::findShortAddr(uint16_t shortaddr) const {
|
||||
for (const auto & elem : _devices) {
|
||||
if (elem.shortaddr == shortaddr) { return elem; }
|
||||
}
|
||||
return device_unk;
|
||||
}
|
||||
//
|
||||
// Scan all devices to find a corresponding longaddr
|
||||
// Looks info device.longaddr entry
|
||||
// In:
|
||||
// longaddr (non null)
|
||||
// Out:
|
||||
// index in _devices of entry, -1 if not found
|
||||
//
|
||||
Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) {
|
||||
if (!longaddr) { return (Z_Device&) device_unk; }
|
||||
for (auto &elem : _devices) {
|
||||
if (elem.longaddr == longaddr) { return elem; }
|
||||
}
|
||||
return (Z_Device&) device_unk;
|
||||
}
|
||||
const Z_Device & Z_Devices::findLongAddr(uint64_t longaddr) const {
|
||||
if (!longaddr) { return device_unk; }
|
||||
for (const auto &elem : _devices) {
|
||||
if (elem.longaddr == longaddr) { return elem; }
|
||||
}
|
||||
return device_unk;
|
||||
}
|
||||
//
|
||||
// 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) {
|
||||
if (strcasecmp(elem.friendlyName, name) == 0) { return found; }
|
||||
}
|
||||
found++;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Z_Devices::isKnownIndex(uint32_t index) const {
|
||||
if (index < devicesSize()) {
|
||||
const Z_Device & device = devicesAt(index);
|
||||
return device.shortaddr;
|
||||
} else {
|
||||
return BAD_SHORTADDR;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
|
||||
if ((!name) || (0 == strlen(name))) { return BAD_SHORTADDR; } // 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 BAD_SHORTADDR;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
|
||||
return findShortAddr(shortaddr).longaddr; // if unknown, it reverts to the Unknown device and longaddr is 0x00
|
||||
}
|
||||
|
||||
//
|
||||
// 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
|
||||
Z_Device & device = findShortAddr(shortaddr);
|
||||
if (foundDevice(device)) {
|
||||
return device;
|
||||
}
|
||||
return createDeviceEntry(shortaddr, 0);
|
||||
}
|
||||
|
||||
// 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; }
|
||||
Z_Device & device = findLongAddr(longaddr);
|
||||
if (foundDevice(device)) {
|
||||
return device;
|
||||
}
|
||||
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) {
|
||||
Z_Device & device = findShortAddr(shortaddr);
|
||||
if (foundDevice(device)) {
|
||||
_devices.remove(&device);
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// We have just seen a device on the network, update the info based on short/long addr
|
||||
// In:
|
||||
// shortaddr
|
||||
// longaddr (both can't be null at the same time)
|
||||
void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
||||
Z_Device * s_found = &findShortAddr(shortaddr); // is there already a shortaddr entry
|
||||
Z_Device * l_found = &findLongAddr(longaddr); // is there already a longaddr entry
|
||||
|
||||
if (foundDevice(*s_found) && foundDevice(*l_found)) { // both shortaddr and longaddr are already registered
|
||||
if (s_found == l_found) {
|
||||
} else { // they don't match
|
||||
// the device with longaddr got a new shortaddr
|
||||
l_found->shortaddr = shortaddr; // update the shortaddr corresponding to the longaddr
|
||||
// erase the previous shortaddr
|
||||
freeDeviceEntry(s_found);
|
||||
_devices.remove(s_found);
|
||||
dirty();
|
||||
}
|
||||
} else if (foundDevice(*s_found)) {
|
||||
// shortaddr already exists but longaddr not
|
||||
// add the longaddr to the entry
|
||||
s_found->longaddr = longaddr;
|
||||
dirty();
|
||||
} else if (foundDevice(*l_found)) {
|
||||
// longaddr entry exists, update shortaddr
|
||||
l_found->shortaddr = shortaddr;
|
||||
dirty();
|
||||
} else {
|
||||
// neither short/lonf addr are found.
|
||||
if ((BAD_SHORTADDR != shortaddr) || longaddr) {
|
||||
createDeviceEntry(shortaddr, longaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Clear all endpoints
|
||||
//
|
||||
void Z_Devices::clearEndpoints(uint16_t shortaddr) {
|
||||
Z_Device &device = getShortAddr(shortaddr);
|
||||
for (uint32_t i = 0; i < endpoints_max; i++) {
|
||||
device.endpoints[i] = 0;
|
||||
// no dirty here because it doesn't make sense to store it, does it?
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Add an endpoint to a shortaddr
|
||||
//
|
||||
void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) {
|
||||
if (0x00 == endpoint) { return; }
|
||||
Z_Device &device = getShortAddr(shortaddr);
|
||||
|
||||
for (uint32_t i = 0; i < endpoints_max; i++) {
|
||||
if (endpoint == device.endpoints[i]) {
|
||||
return; // endpoint already there
|
||||
}
|
||||
if (0 == device.endpoints[i]) {
|
||||
device.endpoints[i] = endpoint;
|
||||
dirty();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Count the number of known endpoints
|
||||
//
|
||||
uint32_t Z_Devices::countEndpoints(uint16_t shortaddr) const {
|
||||
uint32_t count_ep = 0;
|
||||
const Z_Device & device =findShortAddr(shortaddr);
|
||||
if (!foundDevice(device)) return 0;
|
||||
|
||||
for (uint32_t i = 0; i < endpoints_max; i++) {
|
||||
if (0 != device.endpoints[i]) {
|
||||
count_ep++;
|
||||
}
|
||||
}
|
||||
return count_ep;
|
||||
}
|
||||
|
||||
// Find the first endpoint of the device
|
||||
uint8_t Z_Devices::findFirstEndpoint(uint16_t shortaddr) const {
|
||||
// When in router of end-device mode, the coordinator was not probed, in this case always talk to endpoint 1
|
||||
if (0x0000 == shortaddr) { return 1; }
|
||||
return findShortAddr(shortaddr).endpoints[0]; // returns 0x00 if no endpoint
|
||||
}
|
||||
|
||||
void Z_Devices::setStringAttribute(char*& attr, const char * str) {
|
||||
if (nullptr == str) { return; } // ignore a null parameter
|
||||
size_t str_len = strlen(str);
|
||||
|
||||
if ((nullptr == attr) && (0 == str_len)) { return; } // if both empty, don't do anything
|
||||
if (attr) {
|
||||
// we already have a value
|
||||
if (strcmp(attr, str) != 0) {
|
||||
// new value
|
||||
free(attr); // free previous value
|
||||
attr = nullptr;
|
||||
} else {
|
||||
return; // same value, don't change anything
|
||||
}
|
||||
}
|
||||
if (str_len) {
|
||||
attr = (char*) malloc(str_len + 1);
|
||||
strlcpy(attr, str, str_len + 1);
|
||||
}
|
||||
dirty();
|
||||
}
|
||||
|
||||
//
|
||||
// Sets the ManufId for a device.
|
||||
// No action taken if the device does not exist.
|
||||
// Inputs:
|
||||
// - shortaddr: 16-bits short address of the device. No action taken if the device is unknown
|
||||
// - str: string pointer, if nullptr it is considered as empty string
|
||||
// Impact:
|
||||
// - Any actual change in ManufId (i.e. setting a different value) triggers a `dirty()` and saving to Flash
|
||||
//
|
||||
void Z_Devices::setManufId(uint16_t shortaddr, const char * str) {
|
||||
setStringAttribute(getShortAddr(shortaddr).manufacturerId, str);
|
||||
}
|
||||
|
||||
void Z_Devices::setModelId(uint16_t shortaddr, const char * str) {
|
||||
setStringAttribute(getShortAddr(shortaddr).modelId, str);
|
||||
}
|
||||
|
||||
void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) {
|
||||
setStringAttribute(getShortAddr(shortaddr).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; }
|
||||
// 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;
|
||||
}
|
||||
|
||||
// get the next sequance number for the device, or use the global seq number if device is unknown
|
||||
uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) {
|
||||
Z_Device & device = findShortAddr(shortaddr);
|
||||
if (foundDevice(device)) {
|
||||
device.seqNumber += 1;
|
||||
return device.seqNumber;
|
||||
} else {
|
||||
_seqNumber += 1;
|
||||
return _seqNumber;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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);
|
||||
if (0x00 == (light_profile & 0xF0)) {
|
||||
return (light_profile & 0x07);
|
||||
} else {
|
||||
// not a bulb
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Z_Devices::hideHueBulb(uint16_t shortaddr, bool hidden) {
|
||||
Z_Device &device = getShortAddr(shortaddr);
|
||||
if (device.hidden != hidden) {
|
||||
device.hidden = hidden;
|
||||
dirty();
|
||||
}
|
||||
}
|
||||
// true if device is not knwon or not a bulb - it wouldn't make sense to publish a non-bulb
|
||||
bool Z_Devices::isHueBulbHidden(uint16_t shortaddr) const {
|
||||
const Z_Device & device = findShortAddr(shortaddr);
|
||||
if (foundDevice(device)) {
|
||||
return device.hidden;
|
||||
}
|
||||
return true; // Fallback - Device is considered as hidden
|
||||
}
|
||||
|
||||
// Deferred actions
|
||||
// Parse for a specific category, of all deferred for a device if category == 0xFF
|
||||
// Only with specific cluster number or for all clusters if cluster == 0xFFFF
|
||||
void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster, uint8_t endpoint) {
|
||||
// iterate the list of deferred, and remove any linked to the shortaddr
|
||||
for (auto & defer : _deferred) {
|
||||
if ((defer.shortaddr == shortaddr) && (defer.groupaddr == groupaddr)) {
|
||||
if ((0xFF == category) || (defer.category == category)) {
|
||||
if ((0xFFFF == cluster) || (defer.cluster == cluster)) {
|
||||
if ((0xFF == endpoint) || (defer.endpoint == endpoint)) {
|
||||
_deferred.remove(&defer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set timer for a specific device
|
||||
void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) {
|
||||
// First we remove any existing timer for same device in same category, except for category=0x00 (they need to happen anyway)
|
||||
if (category >= Z_CLEAR_DEVICE) { // if category == 0, we leave all previous timers
|
||||
resetTimersForDevice(shortaddr, groupaddr, category, category >= Z_CLEAR_DEVICE_CLUSTER ? cluster : 0xFFFF, category >= Z_CLEAR_DEVICE_CLUSTER_ENDPOINT ? endpoint : 0xFF); // remove any cluster
|
||||
}
|
||||
|
||||
// Now create the new timer
|
||||
Z_Deferred & deferred = _deferred.addHead();
|
||||
deferred = { wait_ms + millis(), // timer
|
||||
shortaddr,
|
||||
groupaddr,
|
||||
cluster,
|
||||
endpoint,
|
||||
category,
|
||||
value,
|
||||
func };
|
||||
}
|
||||
|
||||
// Set timer after the already queued events
|
||||
// I.e. the wait_ms is not counted from now, but from the last event queued, which is 'now' or in the future
|
||||
void Z_Devices::queueTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
uint32_t now_millis = millis();
|
||||
if (TimeReached(device.defer_last_message_sent)) {
|
||||
device.defer_last_message_sent = now_millis;
|
||||
}
|
||||
// defer_last_message_sent equals now or a value in the future
|
||||
device.defer_last_message_sent += wait_ms;
|
||||
|
||||
// for queueing we don't clear the backlog, so we force category to Z_CAT_ALWAYS
|
||||
setTimer(shortaddr, groupaddr, (device.defer_last_message_sent - now_millis), cluster, endpoint, Z_CAT_ALWAYS, value, func);
|
||||
}
|
||||
|
||||
// Run timer at each tick
|
||||
// WARNING: don't set a new timer within a running timer, this causes memory corruption
|
||||
void Z_Devices::runTimer(void) {
|
||||
// visit all timers
|
||||
for (auto & defer : _deferred) {
|
||||
uint32_t timer = defer.timer;
|
||||
if (TimeReached(timer)) {
|
||||
(*defer.func)(defer.shortaddr, defer.groupaddr, defer.cluster, defer.endpoint, defer.value);
|
||||
_deferred.remove(&defer);
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to save to Flash
|
||||
if ((_saveTimer) && TimeReached(_saveTimer)) {
|
||||
saveZigbeeDevices();
|
||||
_saveTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// does the new payload conflicts with the existing payload, i.e. values would be overwritten
|
||||
// true - one attribute (except LinkQuality) woudl be lost, there is conflict
|
||||
// false - new attributes can be safely added
|
||||
bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_list) const {
|
||||
const Z_Device & device = findShortAddr(shortaddr);
|
||||
|
||||
if (!foundDevice(device)) { return false; }
|
||||
if (attr_list.isEmpty()) {
|
||||
return false; // if no previous value, no conflict
|
||||
}
|
||||
|
||||
// compare groups
|
||||
if (device.attr_list.isValidGroupId() && attr_list.isValidGroupId()) {
|
||||
if (device.attr_list.group_id != attr_list.group_id) { return true; } // groups are in conflict
|
||||
}
|
||||
|
||||
// compare src_ep
|
||||
if (device.attr_list.isValidSrcEp() && attr_list.isValidSrcEp()) {
|
||||
if (device.attr_list.src_ep != attr_list.src_ep) { return true; }
|
||||
}
|
||||
|
||||
// LQI does not count as conflicting
|
||||
|
||||
// parse all other parameters
|
||||
for (const auto & attr : attr_list) {
|
||||
const Z_attribute * curr_attr = device.attr_list.findAttribute(attr);
|
||||
if (nullptr != curr_attr) {
|
||||
if (!curr_attr->equalsVal(attr)) {
|
||||
return true; // the value already exists and is different - conflict!
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Z_Devices::jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
device.attr_list.mergeList(attr_list);
|
||||
}
|
||||
|
||||
void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
|
||||
Z_Device & device = getShortAddr(shortaddr);
|
||||
if (!device.valid()) { return; } // safeguard
|
||||
Z_attribute_list &attr_list = device.attr_list;
|
||||
|
||||
if (!attr_list.isEmpty()) {
|
||||
const char * fname = zigbee_devices.getFriendlyName(shortaddr);
|
||||
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
||||
|
||||
// save parameters is global variables to be used by Rules
|
||||
gZbLastMessage.device = shortaddr; // %zbdevice%
|
||||
gZbLastMessage.groupaddr = attr_list.group_id; // %zbgroup%
|
||||
gZbLastMessage.endpoint = attr_list.src_ep; // %zbendpoint%
|
||||
|
||||
mqtt_data[0] = 0; // clear string
|
||||
// Do we prefix with `ZbReceived`?
|
||||
if (!Settings.flag4.remove_zbreceived) {
|
||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":"));
|
||||
}
|
||||
// What key do we use, shortaddr or name?
|
||||
if (use_fname) {
|
||||
Response_P(PSTR("%s{\"%s\":{"), mqtt_data, fname);
|
||||
} else {
|
||||
Response_P(PSTR("%s{\"0x%04X\":{"), mqtt_data, shortaddr);
|
||||
}
|
||||
// Add "Device":"0x...."
|
||||
Response_P(PSTR("%s\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\","), mqtt_data, shortaddr);
|
||||
// Add "Name":"xxx" if name is present
|
||||
if (fname) {
|
||||
Response_P(PSTR("%s\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), mqtt_data, EscapeJSONString(fname).c_str());
|
||||
}
|
||||
// Add all other attributes
|
||||
Response_P(PSTR("%s%s}}"), mqtt_data, attr_list.toString().c_str());
|
||||
|
||||
if (!Settings.flag4.remove_zbreceived) {
|
||||
Response_P(PSTR("%s}"), mqtt_data);
|
||||
}
|
||||
attr_list.reset(); // clear the attributes
|
||||
|
||||
if (Settings.flag4.zigbee_distinct_topics) {
|
||||
if (Settings.flag4.zb_topic_fname && fname) {
|
||||
//Clean special characters and check size of friendly name
|
||||
char stemp[TOPSZ];
|
||||
strlcpy(stemp, (!strlen(fname)) ? MQTT_TOPIC : fname, sizeof(stemp));
|
||||
MakeValidMqtt(0, stemp);
|
||||
//Create topic with Prefix3 and cleaned up friendly name
|
||||
char frtopic[TOPSZ];
|
||||
snprintf_P(frtopic, sizeof(frtopic), PSTR("%s/%s/" D_RSLT_SENSOR), SettingsText(SET_MQTTPREFIX3), stemp);
|
||||
MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
|
||||
} else {
|
||||
char subtopic[16];
|
||||
snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr);
|
||||
MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain);
|
||||
}
|
||||
} else {
|
||||
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
|
||||
}
|
||||
XdrvRulesProcess(); // apply rules
|
||||
}
|
||||
}
|
||||
|
||||
void Z_Devices::jsonPublishNow(uint16_t shortaddr, Z_attribute_list &attr_list) {
|
||||
jsonPublishFlush(shortaddr); // flush any previous buffer
|
||||
jsonAppend(shortaddr, attr_list);
|
||||
jsonPublishFlush(shortaddr); // publish now
|
||||
}
|
||||
|
||||
void Z_Devices::dirty(void) {
|
||||
_saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis();
|
||||
}
|
||||
void Z_Devices::clean(void) {
|
||||
_saveTimer = 0;
|
||||
}
|
||||
|
||||
// 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 BAD_SHORTADDR; }
|
||||
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) {
|
||||
// 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') || (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.findShortAddr(shortaddr).shortaddr; // if not found, it reverts to the unknown_device with address BAD_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;
|
||||
}
|
||||
|
||||
// Display the tracked status for a light
|
||||
String Z_Devices::dumpLightState(uint16_t shortaddr) const {
|
||||
Z_attribute_list attr_list;
|
||||
char hex[8];
|
||||
|
||||
const Z_Device & device = findShortAddr(shortaddr);
|
||||
const char * fname = getFriendlyName(shortaddr);
|
||||
bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
|
||||
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
|
||||
|
||||
attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
|
||||
if (fname) {
|
||||
attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(fname);
|
||||
}
|
||||
|
||||
if (foundDevice(device)) {
|
||||
// dump all known values
|
||||
attr_list.addAttribute(F("Reachable")).setBool(device.getReachable());
|
||||
if (device.validPower()) { attr_list.addAttribute(F("Power")).setUInt(device.getPower()); }
|
||||
Z_Data_Light::toAttributes(attr_list, device.data.find<Z_Data_Light>(0));
|
||||
}
|
||||
|
||||
Z_attribute_list attr_list_root;
|
||||
Z_attribute * attr_root;
|
||||
if (use_fname) {
|
||||
attr_root = &attr_list_root.addAttribute(fname);
|
||||
} else {
|
||||
attr_root = &attr_list_root.addAttribute(hex);
|
||||
}
|
||||
attr_root->setStrRaw(attr_list.toString(true).c_str());
|
||||
return attr_list_root.toString(true);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
Z_json_array 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 (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);
|
||||
}
|
||||
int8_t bulbtype = getHueBulbtype(shortaddr);
|
||||
if (bulbtype >= 0) {
|
||||
attr_list.addAttribute(F(D_JSON_ZIGBEE_LIGHT)).setInt(bulbtype); // sign extend, 0xFF changed as -1
|
||||
}
|
||||
if (device.manufacturerId) {
|
||||
attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
|
||||
}
|
||||
Z_json_array 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());
|
||||
}
|
||||
json_arr.addStrRaw(attr_list.toString(true).c_str());
|
||||
}
|
||||
return json_arr.toString();
|
||||
}
|
||||
|
||||
// Restore a single device configuration based on json export
|
||||
// Input: json element as expported by `ZbStatus2``
|
||||
// Mandatory attribue: `Device`
|
||||
//
|
||||
// Returns:
|
||||
// 0 : Ok
|
||||
// <0 : Error
|
||||
//
|
||||
// Ex: {"Device":"0x5ADF","Name":"IKEA_Light","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]}
|
||||
int32_t Z_Devices::deviceRestore(JsonParserObject json) {
|
||||
|
||||
// params
|
||||
uint16_t device = 0x0000; // 0x0000 is coordinator so considered invalid
|
||||
uint64_t ieeeaddr = 0x0000000000000000LL; // 0 means unknown
|
||||
const char * modelid = nullptr;
|
||||
const char * manufid = nullptr;
|
||||
const char * friendlyname = nullptr;
|
||||
int8_t bulbtype = -1;
|
||||
size_t endpoints_len = 0;
|
||||
|
||||
// read mandatory "Device"
|
||||
JsonParserToken val_device = json[PSTR("Device")];
|
||||
if (val_device) {
|
||||
device = (uint32_t) val_device.getUInt(device);
|
||||
} else {
|
||||
return -1; // missing "Device" attribute
|
||||
}
|
||||
|
||||
ieeeaddr = json.getULong(PSTR("IEEEAddr"), ieeeaddr); // read "IEEEAddr" 64 bits in format "0x0000000000000000"
|
||||
friendlyname = json.getStr(PSTR("Name"), nullptr); // read "Name"
|
||||
modelid = json.getStr(PSTR("ModelId"), nullptr);
|
||||
manufid = json.getStr(PSTR("Manufacturer"), nullptr);
|
||||
JsonParserToken tok_bulbtype = json[PSTR(D_JSON_ZIGBEE_LIGHT)];
|
||||
|
||||
// update internal device information
|
||||
updateDevice(device, ieeeaddr);
|
||||
if (modelid) { setModelId(device, modelid); }
|
||||
if (manufid) { setManufId(device, manufid); }
|
||||
if (friendlyname) { setFriendlyName(device, friendlyname); }
|
||||
if (tok_bulbtype) { setLightProfile(device, tok_bulbtype.getInt()); }
|
||||
|
||||
// read "Endpoints"
|
||||
JsonParserToken val_endpoints = json[PSTR("Endpoints")];
|
||||
if (val_endpoints.isArray()) {
|
||||
JsonParserArray arr_ep = JsonParserArray(val_endpoints);
|
||||
clearEndpoints(device); // clear even if array is empty
|
||||
for (auto ep_elt : arr_ep) {
|
||||
uint8_t ep = ep_elt.getUInt();
|
||||
if (ep) { addEndpoint(device, ep); }
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Z_Data_Light & Z_Devices::getLight(uint16_t shortaddr) {
|
||||
return getShortAddr(shortaddr).data.get<Z_Data_Light>();
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Export device specific attributes to ZbData
|
||||
\*********************************************************************************************/
|
||||
void Z_Device::toAttributes(Z_attribute_list & attr_list) const {
|
||||
if (validLqi()) { attr_list.addAttribute(PSTR(D_CMND_ZIGBEE_LINKQUALITY)).setUInt(lqi); }
|
||||
if (validBatteryPercent()) { attr_list.addAttribute(PSTR("BatteryPercentage")).setUInt(batterypercent); }
|
||||
if (validLastSeen()) { attr_list.addAttribute(PSTR("LastSeen")).setUInt(last_seen); }
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Device specific data handlers
|
||||
\*********************************************************************************************/
|
||||
void Z_Device::setPower(bool power_on, uint8_t ep) {
|
||||
data.get<Z_Data_OnOff>(ep).setPower(power_on);
|
||||
}
|
||||
|
||||
bool Z_Device::validPower(uint8_t ep) const {
|
||||
const Z_Data_OnOff & onoff = data.find<Z_Data_OnOff>(ep);
|
||||
return (&onoff != nullptr);
|
||||
}
|
||||
|
||||
bool Z_Device::getPower(uint8_t ep) const {
|
||||
const Z_Data_OnOff & onoff = data.find<Z_Data_OnOff>(ep);
|
||||
if (&onoff != nullptr) return onoff.getPower();
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // USE_ZIGBEE
|
|
@ -29,23 +29,29 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
|
|||
"\"effect\":\"none\","
|
||||
"\"reachable\":%s}";
|
||||
|
||||
bool power, reachable;
|
||||
uint8_t colormode, bri, sat;
|
||||
uint16_t ct, hue;
|
||||
uint16_t x, y;
|
||||
bool power = false;
|
||||
bool reachable = false;
|
||||
uint8_t colormode = 0xFF;
|
||||
uint8_t bri = 0xFF;
|
||||
uint8_t sat = 0xFF;
|
||||
uint16_t ct = 0xFFFF;
|
||||
uint16_t hue = 0xFFFF;
|
||||
uint16_t x = 0xFFFF, y = 0xFFFF;
|
||||
String light_status = "";
|
||||
uint32_t echo_gen = findEchoGeneration(); // 1 for 1st gen =+ Echo Dot 2nd gen, 2 for 2nd gen and above
|
||||
|
||||
const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
|
||||
// TODO TODO check also validity
|
||||
bri = device.dimmer;
|
||||
const Z_Data_Light & light = device.data.find<Z_Data_Light>();
|
||||
if (&light != nullptr) {
|
||||
bri = light.getDimmer();
|
||||
colormode = light.getColorMode();
|
||||
sat = light.getSat();
|
||||
ct = light.getCT();
|
||||
hue = light.getHue();
|
||||
x = light.getX();
|
||||
y = light.getY();
|
||||
}
|
||||
power = device.getPower();
|
||||
colormode = device.colormode;
|
||||
sat = device.sat;
|
||||
ct = device.ct;
|
||||
hue = device.hue;
|
||||
x = device.x;
|
||||
y = device.y;
|
||||
reachable = device.getReachable();
|
||||
|
||||
if (bri > 254) bri = 254; // Philips Hue bri is between 1 and 254
|
||||
|
@ -148,7 +154,7 @@ void ZigbeeHueGroups(String * lights) {
|
|||
// Power On/Off
|
||||
void ZigbeeHuePower(uint16_t shortaddr, bool power) {
|
||||
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, "");
|
||||
zigbee_devices.getShortAddr(shortaddr).setPower(power);
|
||||
zigbee_devices.getShortAddr(shortaddr).setPower(power, 0);
|
||||
}
|
||||
|
||||
// Dimmer
|
||||
|
@ -157,7 +163,7 @@ void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) {
|
|||
char param[8];
|
||||
snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer);
|
||||
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param);
|
||||
zigbee_devices.getShortAddr(shortaddr).dimmer = dimmer;
|
||||
zigbee_devices.getLight(shortaddr).setDimmer(dimmer);
|
||||
}
|
||||
|
||||
// CT
|
||||
|
@ -168,9 +174,9 @@ void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) {
|
|||
snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8);
|
||||
uint8_t colormode = 2; // "ct"
|
||||
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param);
|
||||
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
|
||||
device.colormode = colormode;
|
||||
device.ct = ct;
|
||||
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
|
||||
light.setColorMode(colormode);
|
||||
light.setCT(ct);
|
||||
}
|
||||
|
||||
// XY
|
||||
|
@ -181,10 +187,10 @@ void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) {
|
|||
snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8);
|
||||
uint8_t colormode = 1; // "xy"
|
||||
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param);
|
||||
Z_Device & device = zigbee_devices.getShortAddr(shortaddr);
|
||||
device.colormode = colormode;
|
||||
device.x = x;
|
||||
device.y = y;
|
||||
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
|
||||
light.setColorMode(colormode);
|
||||
light.setX(x);
|
||||
light.setY(y);
|
||||
}
|
||||
|
||||
// HueSat
|
||||
|
@ -195,10 +201,10 @@ void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) {
|
|||
snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat);
|
||||
uint8_t colormode = 0; // "hs"
|
||||
zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param);
|
||||
Z_Device device = zigbee_devices.getShortAddr(shortaddr);
|
||||
device.colormode = colormode;
|
||||
device.sat = sat;
|
||||
device.hue = hue;
|
||||
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
|
||||
light.setColorMode(colormode);
|
||||
light.setSat(sat);
|
||||
light.setHue(hue);
|
||||
}
|
||||
|
||||
void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
|
||||
|
|
|
@ -127,7 +127,7 @@ class SBuffer hibernateDevice(const struct Z_Device &device) {
|
|||
buf.add8(0x00); // end of string marker
|
||||
|
||||
// Zigbee Profile
|
||||
buf.add8(device.zb_profile);
|
||||
buf.add8(device.getLightChannels());
|
||||
|
||||
// update overall length
|
||||
buf.set8(0, buf.len());
|
||||
|
@ -170,7 +170,6 @@ void hydrateDevices(const SBuffer &buf) {
|
|||
|
||||
uint32_t k = 0;
|
||||
uint32_t num_devices = buf.get8(k++);
|
||||
//size_t before = 0;
|
||||
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
|
||||
uint32_t dev_record_len = buf.get8(k);
|
||||
|
||||
|
@ -200,7 +199,6 @@ void hydrateDevices(const SBuffer &buf) {
|
|||
// ignore
|
||||
}
|
||||
}
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device 0x%04X Memory3.shrink = %d"), shortaddr, ESP_getFreeHeap());
|
||||
|
||||
// parse 3 strings
|
||||
char empty[] = "";
|
||||
|
@ -225,13 +223,12 @@ void hydrateDevices(const SBuffer &buf) {
|
|||
|
||||
// Hue bulbtype - if present
|
||||
if (d < dev_record_len) {
|
||||
zigbee_devices.setZbProfile(shortaddr, buf_d.get8(d));
|
||||
zigbee_devices.setLightProfile(shortaddr, buf_d.get8(d));
|
||||
d++;
|
||||
}
|
||||
|
||||
// next iteration
|
||||
k += dev_record_len;
|
||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device %d After Memory = %d"), i, ESP_getFreeHeap());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,14 +91,20 @@ bool Z_isDiscreteDataType(uint8_t t) {
|
|||
}
|
||||
|
||||
typedef struct Z_AttributeConverter {
|
||||
uint8_t type;
|
||||
uint8_t cluster_short;
|
||||
uint16_t attribute;
|
||||
uint16_t name_offset;
|
||||
int8_t multiplier; // multiplier for numerical value, (if > 0 multiply by x, if <0 device by x)
|
||||
uint8_t type;
|
||||
uint8_t cluster_short;
|
||||
uint16_t attribute;
|
||||
uint16_t name_offset;
|
||||
int8_t multiplier; // multiplier for numerical value, (if > 0 multiply by x, if <0 device by x)
|
||||
uint8_t mapping; // high 4 bits = type, low 4 bits = offset in bytes from header
|
||||
// still room for a byte
|
||||
} Z_AttributeConverter;
|
||||
|
||||
// Get offset in bytes of attributes, starting after the header (skipping first 4 bytes)
|
||||
#define Z_OFFSET(c,a) (offsetof(class c, a) - sizeof(Z_Data))
|
||||
#define Z_CLASS(c) c // necessary to get a valid token without concatenation (which wouldn't work)
|
||||
#define Z_MAPPING(c,a) (((((uint8_t)Z_CLASS(c)::type) & 0x0F) << 4) | Z_OFFSET(c,a))
|
||||
|
||||
// Cluster numbers are store in 8 bits format to save space,
|
||||
// the following tables allows the conversion from 8 bits index Cx...
|
||||
// to the 16 bits actual cluster number
|
||||
|
@ -135,413 +141,415 @@ uint8_t ClusterToCx(uint16_t cluster) {
|
|||
}
|
||||
|
||||
// list of post-processing directives
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Winvalid-offsetof" // avoid warnings since we're using offsetof() in a risky way
|
||||
const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
||||
{ Zuint8, Cx0000, 0x0000, Z_(ZCLVersion), 1 },
|
||||
{ Zuint8, Cx0000, 0x0001, Z_(AppVersion), 1 },
|
||||
{ Zuint8, Cx0000, 0x0002, Z_(StackVersion), 1 },
|
||||
{ Zuint8, Cx0000, 0x0003, Z_(HWVersion), 1 },
|
||||
{ Zstring, Cx0000, 0x0004, Z_(Manufacturer), 1 }, // record Manufacturer
|
||||
{ Zstring, Cx0000, 0x0005, Z_(ModelId), 1 }, // record Model
|
||||
// { Zstring, Cx0000, 0x0004, Z_(Manufacturer), 1, Z_ManufKeep }, // record Manufacturer
|
||||
// { Zstring, Cx0000, 0x0005, Z_(ModelId), 1, Z_ModelKeep }, // record Model
|
||||
{ Zstring, Cx0000, 0x0006, Z_(DateCode), 1 },
|
||||
{ Zenum8, Cx0000, 0x0007, Z_(PowerSource), 1 },
|
||||
{ Zenum8, Cx0000, 0x0008, Z_(GenericDeviceClass), 1 },
|
||||
{ Zenum8, Cx0000, 0x0009, Z_(GenericDeviceType), 1 },
|
||||
{ Zoctstr, Cx0000, 0x000A, Z_(ProductCode), 1 },
|
||||
{ Zstring, Cx0000, 0x000B, Z_(ProductURL), 1 },
|
||||
{ Zstring, Cx0000, 0x4000, Z_(SWBuildID), 1 },
|
||||
// { Zunk, Cx0000, 0xFFFF, nullptr, 0 }, // Remove all other values
|
||||
{ Zuint8, Cx0000, 0x0000, Z_(ZCLVersion), 1, 0 },
|
||||
{ Zuint8, Cx0000, 0x0001, Z_(AppVersion), 1, 0 },
|
||||
{ Zuint8, Cx0000, 0x0002, Z_(StackVersion), 1, 0 },
|
||||
{ Zuint8, Cx0000, 0x0003, Z_(HWVersion), 1, 0 },
|
||||
{ Zstring, Cx0000, 0x0004, Z_(Manufacturer), 1, 0 }, // record Manufacturer
|
||||
{ Zstring, Cx0000, 0x0005, Z_(ModelId), 1, 0 }, // record Model
|
||||
// { Zstring, Cx0000, 0x0004, Z_(Manufacturer), 1, Z_ManufKeep, 0 }, // record Manufacturer
|
||||
// { Zstring, Cx0000, 0x0005, Z_(ModelId), 1, Z_ModelKeep, 0 }, // record Model
|
||||
{ Zstring, Cx0000, 0x0006, Z_(DateCode), 1, 0 },
|
||||
{ Zenum8, Cx0000, 0x0007, Z_(PowerSource), 1, 0 },
|
||||
{ Zenum8, Cx0000, 0x0008, Z_(GenericDeviceClass), 1, 0 },
|
||||
{ Zenum8, Cx0000, 0x0009, Z_(GenericDeviceType), 1, 0 },
|
||||
{ Zoctstr, Cx0000, 0x000A, Z_(ProductCode), 1, 0 },
|
||||
{ Zstring, Cx0000, 0x000B, Z_(ProductURL), 1, 0 },
|
||||
{ Zstring, Cx0000, 0x4000, Z_(SWBuildID), 1, 0 },
|
||||
// { Zunk, Cx0000, 0xFFFF, nullptr, 0, 0 }, // Remove all other values
|
||||
// Cmd 0x0A - Cluster 0x0000, attribute 0xFF01 - proprietary
|
||||
{ Zmap8, Cx0000, 0xFF01, Z_(), 0 },
|
||||
{ Zmap8, Cx0000, 0xFF02, Z_(), 0 },
|
||||
// { Zmap8, Cx0000, 0xFF01, Z_(), 0, Z_AqaraSensor },
|
||||
// { Zmap8, Cx0000, 0xFF02, Z_(), 0, Z_AqaraSensor2 },
|
||||
{ Zmap8, Cx0000, 0xFF01, Z_(), 0, 0 },
|
||||
{ Zmap8, Cx0000, 0xFF02, Z_(), 0, 0 },
|
||||
// { Zmap8, Cx0000, 0xFF01, Z_(), 0, Z_AqaraSensor, 0 },
|
||||
// { Zmap8, Cx0000, 0xFF02, Z_(), 0, Z_AqaraSensor2, 0 },
|
||||
|
||||
// Power Configuration cluster
|
||||
{ Zuint16, Cx0001, 0x0000, Z_(MainsVoltage), 1 },
|
||||
{ Zuint8, Cx0001, 0x0001, Z_(MainsFrequency), 1 },
|
||||
{ Zuint8, Cx0001, 0x0020, Z_(BatteryVoltage), -10 }, // divide by 10
|
||||
{ Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), -2 }, // divide by 2
|
||||
// { Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), -2, Z_BatteryPercentage }, // divide by 2
|
||||
{ Zuint16, Cx0001, 0x0000, Z_(MainsVoltage), 1, 0 },
|
||||
{ Zuint8, Cx0001, 0x0001, Z_(MainsFrequency), 1, 0 },
|
||||
{ Zuint8, Cx0001, 0x0020, Z_(BatteryVoltage), -10, 0 }, // divide by 10
|
||||
{ Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), -2, 0 }, // divide by 2
|
||||
// { Zuint8, Cx0001, 0x0021, Z_(BatteryPercentage), -2, Z_BatteryPercentage, 0 }, // divide by 2
|
||||
|
||||
// Device Temperature Configuration cluster
|
||||
{ Zint16, Cx0002, 0x0000, Z_(CurrentTemperature), 1 },
|
||||
{ Zint16, Cx0002, 0x0001, Z_(MinTempExperienced), 1 },
|
||||
{ Zint16, Cx0002, 0x0002, Z_(MaxTempExperienced), 1 },
|
||||
{ Zuint16, Cx0002, 0x0003, Z_(OverTempTotalDwell), 1 },
|
||||
{ Zint16, Cx0002, 0x0000, Z_(CurrentTemperature), 1, 0 },
|
||||
{ Zint16, Cx0002, 0x0001, Z_(MinTempExperienced), 1, 0 },
|
||||
{ Zint16, Cx0002, 0x0002, Z_(MaxTempExperienced), 1, 0 },
|
||||
{ Zuint16, Cx0002, 0x0003, Z_(OverTempTotalDwell), 1, 0 },
|
||||
|
||||
// Identify cluster
|
||||
{ Zuint16, Cx0003, 0x0000, Z_(IdentifyTime), 1 },
|
||||
{ Zuint16, Cx0003, 0x0000, Z_(IdentifyTime), 1, 0 },
|
||||
|
||||
// Groups cluster
|
||||
{ Zmap8, Cx0004, 0x0000, Z_(GroupNameSupport), 1 },
|
||||
{ Zmap8, Cx0004, 0x0000, Z_(GroupNameSupport), 1, 0 },
|
||||
|
||||
// Scenes cluster
|
||||
{ Zuint8, Cx0005, 0x0000, Z_(SceneCount), 1 },
|
||||
{ Zuint8, Cx0005, 0x0001, Z_(CurrentScene), 1 },
|
||||
{ Zuint16, Cx0005, 0x0002, Z_(CurrentGroup), 1 },
|
||||
{ Zbool, Cx0005, 0x0003, Z_(SceneValid), 1 },
|
||||
//{ Zmap8, Cx0005, 0x0004, (NameSupport), 1 },
|
||||
{ Zuint8, Cx0005, 0x0000, Z_(SceneCount), 1, 0 },
|
||||
{ Zuint8, Cx0005, 0x0001, Z_(CurrentScene), 1, 0 },
|
||||
{ Zuint16, Cx0005, 0x0002, Z_(CurrentGroup), 1, 0 },
|
||||
{ Zbool, Cx0005, 0x0003, Z_(SceneValid), 1, 0 },
|
||||
//{ Zmap8, Cx0005, 0x0004, (NameSupport), 1, 0 },
|
||||
|
||||
// On/off cluster
|
||||
{ Zbool, Cx0006, 0x0000, Z_(Power), 1 },
|
||||
{ Zenum8, Cx0006, 0x4003, Z_(StartUpOnOff), 1 },
|
||||
{ Zbool, Cx0006, 0x8000, Z_(Power), 1 }, // See 7280
|
||||
{ Zbool, Cx0006, 0x0000, Z_(Power), 1, 0 },
|
||||
{ Zenum8, Cx0006, 0x4003, Z_(StartUpOnOff), 1, 0 },
|
||||
{ Zbool, Cx0006, 0x8000, Z_(Power), 1, 0 }, // See 7280
|
||||
|
||||
// On/Off Switch Configuration cluster
|
||||
{ Zenum8, Cx0007, 0x0000, Z_(SwitchType), 1 },
|
||||
{ Zenum8, Cx0007, 0x0000, Z_(SwitchType), 1, 0 },
|
||||
|
||||
// Level Control cluster
|
||||
{ Zuint8, Cx0008, 0x0000, Z_(Dimmer), 1 },
|
||||
{ Zmap8, Cx0008, 0x000F, Z_(DimmerOptions), 1 },
|
||||
{ Zuint16, Cx0008, 0x0001, Z_(DimmerRemainingTime), 1 },
|
||||
{ Zuint16, Cx0008, 0x0010, Z_(OnOffTransitionTime), 1 },
|
||||
// { Zuint8, Cx0008, 0x0011, (OnLevel), 1 },
|
||||
// { Zuint16, Cx0008, 0x0012, (OnTransitionTime), 1 },
|
||||
// { Zuint16, Cx0008, 0x0013, (OffTransitionTime), 1 },
|
||||
// { Zuint16, Cx0008, 0x0014, (DefaultMoveRate), 1 },
|
||||
{ Zuint8, Cx0008, 0x0000, Z_(Dimmer), 1, Z_MAPPING(Z_Data_Light, dimmer) },
|
||||
{ Zmap8, Cx0008, 0x000F, Z_(DimmerOptions), 1, 0 },
|
||||
{ Zuint16, Cx0008, 0x0001, Z_(DimmerRemainingTime), 1, 0 },
|
||||
{ Zuint16, Cx0008, 0x0010, Z_(OnOffTransitionTime), 1, 0 },
|
||||
// { Zuint8, Cx0008, 0x0011, (OnLevel), 1, 0 },
|
||||
// { Zuint16, Cx0008, 0x0012, (OnTransitionTime), 1, 0 },
|
||||
// { Zuint16, Cx0008, 0x0013, (OffTransitionTime), 1, 0 },
|
||||
// { Zuint16, Cx0008, 0x0014, (DefaultMoveRate), 1, 0 },
|
||||
|
||||
// Alarms cluster
|
||||
{ Zuint16, Cx0009, 0x0000, Z_(AlarmCount), 1 },
|
||||
{ Zuint16, Cx0009, 0x0000, Z_(AlarmCount), 1, 0 },
|
||||
|
||||
// Time cluster
|
||||
{ ZUTC, Cx000A, 0x0000, Z_(Time), 1 },
|
||||
{ Zmap8, Cx000A, 0x0001, Z_(TimeStatus), 1 },
|
||||
{ Zint32, Cx000A, 0x0002, Z_(TimeZone), 1 },
|
||||
{ Zuint32, Cx000A, 0x0003, Z_(DstStart), 1 },
|
||||
{ Zuint32, Cx000A, 0x0004, Z_(DstEnd), 1 },
|
||||
{ Zint32, Cx000A, 0x0005, Z_(DstShift), 1 },
|
||||
{ Zuint32, Cx000A, 0x0006, Z_(StandardTime), 1 },
|
||||
{ Zuint32, Cx000A, 0x0007, Z_(LocalTime), 1 },
|
||||
{ ZUTC, Cx000A, 0x0008, Z_(LastSetTime), 1 },
|
||||
{ ZUTC, Cx000A, 0x0009, Z_(ValidUntilTime), 1 },
|
||||
{ ZUTC, Cx000A, 0xFF00, Z_(TimeEpoch), 1 }, // Tasmota specific, epoch
|
||||
{ ZUTC, Cx000A, 0x0000, Z_(Time), 1, 0 },
|
||||
{ Zmap8, Cx000A, 0x0001, Z_(TimeStatus), 1, 0 },
|
||||
{ Zint32, Cx000A, 0x0002, Z_(TimeZone), 1, 0 },
|
||||
{ Zuint32, Cx000A, 0x0003, Z_(DstStart), 1, 0 },
|
||||
{ Zuint32, Cx000A, 0x0004, Z_(DstEnd), 1, 0 },
|
||||
{ Zint32, Cx000A, 0x0005, Z_(DstShift), 1, 0 },
|
||||
{ Zuint32, Cx000A, 0x0006, Z_(StandardTime), 1, 0 },
|
||||
{ Zuint32, Cx000A, 0x0007, Z_(LocalTime), 1, 0 },
|
||||
{ ZUTC, Cx000A, 0x0008, Z_(LastSetTime), 1, 0 },
|
||||
{ ZUTC, Cx000A, 0x0009, Z_(ValidUntilTime), 1, 0 },
|
||||
{ ZUTC, Cx000A, 0xFF00, Z_(TimeEpoch), 1, 0 }, // Tasmota specific, epoch
|
||||
|
||||
// RSSI Location cluster
|
||||
{ Zdata8, Cx000B, 0x0000, Z_(LocationType), 1 },
|
||||
{ Zenum8, Cx000B, 0x0001, Z_(LocationMethod), 1 },
|
||||
{ Zuint16, Cx000B, 0x0002, Z_(LocationAge), 1 },
|
||||
{ Zuint8, Cx000B, 0x0003, Z_(QualityMeasure), 1 },
|
||||
{ Zuint8, Cx000B, 0x0004, Z_(NumberOfDevices), 1 },
|
||||
{ Zdata8, Cx000B, 0x0000, Z_(LocationType), 1, 0 },
|
||||
{ Zenum8, Cx000B, 0x0001, Z_(LocationMethod), 1, 0 },
|
||||
{ Zuint16, Cx000B, 0x0002, Z_(LocationAge), 1, 0 },
|
||||
{ Zuint8, Cx000B, 0x0003, Z_(QualityMeasure), 1, 0 },
|
||||
{ Zuint8, Cx000B, 0x0004, Z_(NumberOfDevices), 1, 0 },
|
||||
|
||||
// Analog Input cluster
|
||||
// { 0xFF, Cx000C, 0x0004, (AnalogInActiveText), 1 },
|
||||
{ Zstring, Cx000C, 0x001C, Z_(AnalogInDescription), 1 },
|
||||
// { 0xFF, Cx000C, 0x002E, (AnalogInInactiveText), 1 },
|
||||
{ Zsingle, Cx000C, 0x0041, Z_(AnalogInMaxValue), 1 },
|
||||
{ Zsingle, Cx000C, 0x0045, Z_(AnalogInMinValue), 1 },
|
||||
{ Zbool, Cx000C, 0x0051, Z_(AnalogInOutOfService), 1 },
|
||||
{ Zsingle, Cx000C, 0x0055, Z_(AqaraRotate), 1 },
|
||||
// { 0xFF, Cx000C, 0x0057, (AnalogInPriorityArray),1 },
|
||||
{ Zenum8, Cx000C, 0x0067, Z_(AnalogInReliability), 1 },
|
||||
// { 0xFF, Cx000C, 0x0068, (AnalogInRelinquishDefault),1 },
|
||||
{ Zsingle, Cx000C, 0x006A, Z_(AnalogInResolution), 1 },
|
||||
{ Zmap8, Cx000C, 0x006F, Z_(AnalogInStatusFlags), 1 },
|
||||
{ Zenum16, Cx000C, 0x0075, Z_(AnalogInEngineeringUnits),1 },
|
||||
{ Zuint32, Cx000C, 0x0100, Z_(AnalogInApplicationType),1 },
|
||||
{ Zuint16, Cx000C, 0xFF05, Z_(Aqara_FF05), 1 },
|
||||
// { 0xFF, Cx000C, 0x0004, (AnalogInActiveText), 1, 0 },
|
||||
{ Zstring, Cx000C, 0x001C, Z_(AnalogInDescription), 1, 0 },
|
||||
// { 0xFF, Cx000C, 0x002E, (AnalogInInactiveText), 1, 0 },
|
||||
{ Zsingle, Cx000C, 0x0041, Z_(AnalogInMaxValue), 1, 0 },
|
||||
{ Zsingle, Cx000C, 0x0045, Z_(AnalogInMinValue), 1, 0 },
|
||||
{ Zbool, Cx000C, 0x0051, Z_(AnalogInOutOfService), 1, 0 },
|
||||
{ Zsingle, Cx000C, 0x0055, Z_(AqaraRotate), 1, 0 },
|
||||
// { 0xFF, Cx000C, 0x0057, (AnalogInPriorityArray),1, 0 },
|
||||
{ Zenum8, Cx000C, 0x0067, Z_(AnalogInReliability), 1, 0 },
|
||||
// { 0xFF, Cx000C, 0x0068, (AnalogInRelinquishDefault),1, 0 },
|
||||
{ Zsingle, Cx000C, 0x006A, Z_(AnalogInResolution), 1, 0 },
|
||||
{ Zmap8, Cx000C, 0x006F, Z_(AnalogInStatusFlags), 1, 0 },
|
||||
{ Zenum16, Cx000C, 0x0075, Z_(AnalogInEngineeringUnits),1, 0 },
|
||||
{ Zuint32, Cx000C, 0x0100, Z_(AnalogInApplicationType),1, 0 },
|
||||
{ Zuint16, Cx000C, 0xFF05, Z_(Aqara_FF05), 1, 0 },
|
||||
|
||||
// Analog Output cluster
|
||||
{ Zstring, Cx000D, 0x001C, Z_(AnalogOutDescription), 1 },
|
||||
{ Zsingle, Cx000D, 0x0041, Z_(AnalogOutMaxValue), 1 },
|
||||
{ Zsingle, Cx000D, 0x0045, Z_(AnalogOutMinValue), 1 },
|
||||
{ Zbool, Cx000D, 0x0051, Z_(AnalogOutOutOfService),1 },
|
||||
{ Zsingle, Cx000D, 0x0055, Z_(AnalogOutValue), 1 },
|
||||
// { Zunk, Cx000D, 0x0057, (AnalogOutPriorityArray),1 },
|
||||
{ Zenum8, Cx000D, 0x0067, Z_(AnalogOutReliability), 1 },
|
||||
{ Zsingle, Cx000D, 0x0068, Z_(AnalogOutRelinquishDefault),1 },
|
||||
{ Zsingle, Cx000D, 0x006A, Z_(AnalogOutResolution), 1 },
|
||||
{ Zmap8, Cx000D, 0x006F, Z_(AnalogOutStatusFlags), 1 },
|
||||
{ Zenum16, Cx000D, 0x0075, Z_(AnalogOutEngineeringUnits),1 },
|
||||
{ Zuint32, Cx000D, 0x0100, Z_(AnalogOutApplicationType),1 },
|
||||
{ Zstring, Cx000D, 0x001C, Z_(AnalogOutDescription), 1, 0 },
|
||||
{ Zsingle, Cx000D, 0x0041, Z_(AnalogOutMaxValue), 1, 0 },
|
||||
{ Zsingle, Cx000D, 0x0045, Z_(AnalogOutMinValue), 1, 0 },
|
||||
{ Zbool, Cx000D, 0x0051, Z_(AnalogOutOutOfService),1, 0 },
|
||||
{ Zsingle, Cx000D, 0x0055, Z_(AnalogOutValue), 1, 0 },
|
||||
// { Zunk, Cx000D, 0x0057, (AnalogOutPriorityArray),1, 0 },
|
||||
{ Zenum8, Cx000D, 0x0067, Z_(AnalogOutReliability), 1, 0 },
|
||||
{ Zsingle, Cx000D, 0x0068, Z_(AnalogOutRelinquishDefault),1, 0 },
|
||||
{ Zsingle, Cx000D, 0x006A, Z_(AnalogOutResolution), 1, 0 },
|
||||
{ Zmap8, Cx000D, 0x006F, Z_(AnalogOutStatusFlags), 1, 0 },
|
||||
{ Zenum16, Cx000D, 0x0075, Z_(AnalogOutEngineeringUnits),1, 0 },
|
||||
{ Zuint32, Cx000D, 0x0100, Z_(AnalogOutApplicationType),1, 0 },
|
||||
|
||||
// Analog Value cluster
|
||||
{ Zstring, Cx000E, 0x001C, Z_(AnalogDescription), 1 },
|
||||
{ Zbool, Cx000E, 0x0051, Z_(AnalogOutOfService), 1 },
|
||||
{ Zsingle, Cx000E, 0x0055, Z_(AnalogValue), 1 },
|
||||
{ Zunk, Cx000E, 0x0057, Z_(AnalogPriorityArray), 1 },
|
||||
{ Zenum8, Cx000E, 0x0067, Z_(AnalogReliability), 1 },
|
||||
{ Zsingle, Cx000E, 0x0068, Z_(AnalogRelinquishDefault),1 },
|
||||
{ Zmap8, Cx000E, 0x006F, Z_(AnalogStatusFlags), 1 },
|
||||
{ Zenum16, Cx000E, 0x0075, Z_(AnalogEngineeringUnits),1 },
|
||||
{ Zuint32, Cx000E, 0x0100, Z_(AnalogApplicationType),1 },
|
||||
{ Zstring, Cx000E, 0x001C, Z_(AnalogDescription), 1, 0 },
|
||||
{ Zbool, Cx000E, 0x0051, Z_(AnalogOutOfService), 1, 0 },
|
||||
{ Zsingle, Cx000E, 0x0055, Z_(AnalogValue), 1, 0 },
|
||||
{ Zunk, Cx000E, 0x0057, Z_(AnalogPriorityArray), 1, 0 },
|
||||
{ Zenum8, Cx000E, 0x0067, Z_(AnalogReliability), 1, 0 },
|
||||
{ Zsingle, Cx000E, 0x0068, Z_(AnalogRelinquishDefault),1, 0 },
|
||||
{ Zmap8, Cx000E, 0x006F, Z_(AnalogStatusFlags), 1, 0 },
|
||||
{ Zenum16, Cx000E, 0x0075, Z_(AnalogEngineeringUnits),1, 0 },
|
||||
{ Zuint32, Cx000E, 0x0100, Z_(AnalogApplicationType),1, 0 },
|
||||
|
||||
// Binary Input cluster
|
||||
{ Zstring, Cx000F, 0x0004, Z_(BinaryInActiveText), 1 },
|
||||
{ Zstring, Cx000F, 0x001C, Z_(BinaryInDescription), 1 },
|
||||
{ Zstring, Cx000F, 0x002E, Z_(BinaryInInactiveText),1 },
|
||||
{ Zbool, Cx000F, 0x0051, Z_(BinaryInOutOfService),1 },
|
||||
{ Zenum8, Cx000F, 0x0054, Z_(BinaryInPolarity), 1 },
|
||||
{ Zstring, Cx000F, 0x0055, Z_(BinaryInValue), 1 },
|
||||
// { 0xFF, Cx000F, 0x0057, (BinaryInPriorityArray),1 },
|
||||
{ Zenum8, Cx000F, 0x0067, Z_(BinaryInReliability), 1 },
|
||||
{ Zmap8, Cx000F, 0x006F, Z_(BinaryInStatusFlags), 1 },
|
||||
{ Zuint32, Cx000F, 0x0100, Z_(BinaryInApplicationType),1 },
|
||||
{ Zstring, Cx000F, 0x0004, Z_(BinaryInActiveText), 1, 0 },
|
||||
{ Zstring, Cx000F, 0x001C, Z_(BinaryInDescription), 1, 0 },
|
||||
{ Zstring, Cx000F, 0x002E, Z_(BinaryInInactiveText),1, 0 },
|
||||
{ Zbool, Cx000F, 0x0051, Z_(BinaryInOutOfService),1, 0 },
|
||||
{ Zenum8, Cx000F, 0x0054, Z_(BinaryInPolarity), 1, 0 },
|
||||
{ Zstring, Cx000F, 0x0055, Z_(BinaryInValue), 1, 0 },
|
||||
// { 0xFF, Cx000F, 0x0057, (BinaryInPriorityArray),1, 0 },
|
||||
{ Zenum8, Cx000F, 0x0067, Z_(BinaryInReliability), 1, 0 },
|
||||
{ Zmap8, Cx000F, 0x006F, Z_(BinaryInStatusFlags), 1, 0 },
|
||||
{ Zuint32, Cx000F, 0x0100, Z_(BinaryInApplicationType),1, 0 },
|
||||
|
||||
// Binary Output cluster
|
||||
{ Zstring, Cx0010, 0x0004, Z_(BinaryOutActiveText), 1 },
|
||||
{ Zstring, Cx0010, 0x001C, Z_(BinaryOutDescription), 1 },
|
||||
{ Zstring, Cx0010, 0x002E, Z_(BinaryOutInactiveText),1 },
|
||||
{ Zuint32, Cx0010, 0x0042, Z_(BinaryOutMinimumOffTime),1 },
|
||||
{ Zuint32, Cx0010, 0x0043, Z_(BinaryOutMinimumOnTime),1 },
|
||||
{ Zbool, Cx0010, 0x0051, Z_(BinaryOutOutOfService),1 },
|
||||
{ Zenum8, Cx0010, 0x0054, Z_(BinaryOutPolarity), 1 },
|
||||
{ Zbool, Cx0010, 0x0055, Z_(BinaryOutValue), 1 },
|
||||
// { Zunk, Cx0010, 0x0057, (BinaryOutPriorityArray),1 },
|
||||
{ Zenum8, Cx0010, 0x0067, Z_(BinaryOutReliability), 1 },
|
||||
{ Zbool, Cx0010, 0x0068, Z_(BinaryOutRelinquishDefault),1 },
|
||||
{ Zmap8, Cx0010, 0x006F, Z_(BinaryOutStatusFlags), 1 },
|
||||
{ Zuint32, Cx0010, 0x0100, Z_(BinaryOutApplicationType),1 },
|
||||
{ Zstring, Cx0010, 0x0004, Z_(BinaryOutActiveText), 1, 0 },
|
||||
{ Zstring, Cx0010, 0x001C, Z_(BinaryOutDescription), 1, 0 },
|
||||
{ Zstring, Cx0010, 0x002E, Z_(BinaryOutInactiveText),1, 0 },
|
||||
{ Zuint32, Cx0010, 0x0042, Z_(BinaryOutMinimumOffTime),1, 0 },
|
||||
{ Zuint32, Cx0010, 0x0043, Z_(BinaryOutMinimumOnTime),1, 0 },
|
||||
{ Zbool, Cx0010, 0x0051, Z_(BinaryOutOutOfService),1, 0 },
|
||||
{ Zenum8, Cx0010, 0x0054, Z_(BinaryOutPolarity), 1, 0 },
|
||||
{ Zbool, Cx0010, 0x0055, Z_(BinaryOutValue), 1, 0 },
|
||||
// { Zunk, Cx0010, 0x0057, (BinaryOutPriorityArray),1, 0 },
|
||||
{ Zenum8, Cx0010, 0x0067, Z_(BinaryOutReliability), 1, 0 },
|
||||
{ Zbool, Cx0010, 0x0068, Z_(BinaryOutRelinquishDefault),1, 0 },
|
||||
{ Zmap8, Cx0010, 0x006F, Z_(BinaryOutStatusFlags), 1, 0 },
|
||||
{ Zuint32, Cx0010, 0x0100, Z_(BinaryOutApplicationType),1, 0 },
|
||||
|
||||
// Binary Value cluster
|
||||
{ Zstring, Cx0011, 0x0004, Z_(BinaryActiveText), 1 },
|
||||
{ Zstring, Cx0011, 0x001C, Z_(BinaryDescription), 1 },
|
||||
{ Zstring, Cx0011, 0x002E, Z_(BinaryInactiveText), 1 },
|
||||
{ Zuint32, Cx0011, 0x0042, Z_(BinaryMinimumOffTime), 1 },
|
||||
{ Zuint32, Cx0011, 0x0043, Z_(BinaryMinimumOnTime), 1 },
|
||||
{ Zbool, Cx0011, 0x0051, Z_(BinaryOutOfService), 1 },
|
||||
{ Zbool, Cx0011, 0x0055, Z_(BinaryValue), 1 },
|
||||
// { Zunk, Cx0011, 0x0057, (BinaryPriorityArray), 1 },
|
||||
{ Zenum8, Cx0011, 0x0067, Z_(BinaryReliability), 1 },
|
||||
{ Zbool, Cx0011, 0x0068, Z_(BinaryRelinquishDefault),1 },
|
||||
{ Zmap8, Cx0011, 0x006F, Z_(BinaryStatusFlags), 1 },
|
||||
{ Zuint32, Cx0011, 0x0100, Z_(BinaryApplicationType),1 },
|
||||
{ Zstring, Cx0011, 0x0004, Z_(BinaryActiveText), 1, 0 },
|
||||
{ Zstring, Cx0011, 0x001C, Z_(BinaryDescription), 1, 0 },
|
||||
{ Zstring, Cx0011, 0x002E, Z_(BinaryInactiveText), 1, 0 },
|
||||
{ Zuint32, Cx0011, 0x0042, Z_(BinaryMinimumOffTime), 1, 0 },
|
||||
{ Zuint32, Cx0011, 0x0043, Z_(BinaryMinimumOnTime), 1, 0 },
|
||||
{ Zbool, Cx0011, 0x0051, Z_(BinaryOutOfService), 1, 0 },
|
||||
{ Zbool, Cx0011, 0x0055, Z_(BinaryValue), 1, 0 },
|
||||
// { Zunk, Cx0011, 0x0057, (BinaryPriorityArray), 1, 0 },
|
||||
{ Zenum8, Cx0011, 0x0067, Z_(BinaryReliability), 1, 0 },
|
||||
{ Zbool, Cx0011, 0x0068, Z_(BinaryRelinquishDefault),1, 0 },
|
||||
{ Zmap8, Cx0011, 0x006F, Z_(BinaryStatusFlags), 1, 0 },
|
||||
{ Zuint32, Cx0011, 0x0100, Z_(BinaryApplicationType),1, 0 },
|
||||
|
||||
// Multistate Input cluster
|
||||
// { Zunk, Cx0012, 0x000E, (MultiInStateText), 1 },
|
||||
{ Zstring, Cx0012, 0x001C, Z_(MultiInDescription), 1 },
|
||||
{ Zuint16, Cx0012, 0x004A, Z_(MultiInNumberOfStates),1 },
|
||||
{ Zbool, Cx0012, 0x0051, Z_(MultiInOutOfService), 1 },
|
||||
{ Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 1 },
|
||||
// { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraCube },
|
||||
// { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraButton },
|
||||
{ Zenum8, Cx0012, 0x0067, Z_(MultiInReliability), 1 },
|
||||
{ Zmap8, Cx0012, 0x006F, Z_(MultiInStatusFlags), 1 },
|
||||
{ Zuint32, Cx0012, 0x0100, Z_(MultiInApplicationType),1 },
|
||||
// { Zunk, Cx0012, 0x000E, (MultiInStateText), 1, 0 },
|
||||
{ Zstring, Cx0012, 0x001C, Z_(MultiInDescription), 1, 0 },
|
||||
{ Zuint16, Cx0012, 0x004A, Z_(MultiInNumberOfStates),1, 0 },
|
||||
{ Zbool, Cx0012, 0x0051, Z_(MultiInOutOfService), 1, 0 },
|
||||
{ Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 1, 0 },
|
||||
// { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraCube, 0 },
|
||||
// { Zuint16, Cx0012, 0x0055, Z_(MultiInValue), 0, Z_AqaraButton, 0 },
|
||||
{ Zenum8, Cx0012, 0x0067, Z_(MultiInReliability), 1, 0 },
|
||||
{ Zmap8, Cx0012, 0x006F, Z_(MultiInStatusFlags), 1, 0 },
|
||||
{ Zuint32, Cx0012, 0x0100, Z_(MultiInApplicationType),1, 0 },
|
||||
|
||||
// Multistate output
|
||||
// { Zunk, Cx0013, 0x000E, (MultiOutStateText), 1 },
|
||||
{ Zstring, Cx0013, 0x001C, Z_(MultiOutDescription), 1 },
|
||||
{ Zuint16, Cx0013, 0x004A, Z_(MultiOutNumberOfStates),1 },
|
||||
{ Zbool, Cx0013, 0x0051, Z_(MultiOutOutOfService), 1 },
|
||||
{ Zuint16, Cx0013, 0x0055, Z_(MultiOutValue), 1 },
|
||||
// { Zunk, Cx0013, 0x0057, (MultiOutPriorityArray),1 },
|
||||
{ Zenum8, Cx0013, 0x0067, Z_(MultiOutReliability), 1 },
|
||||
{ Zuint16, Cx0013, 0x0068, Z_(MultiOutRelinquishDefault),1 },
|
||||
{ Zmap8, Cx0013, 0x006F, Z_(MultiOutStatusFlags), 1 },
|
||||
{ Zuint32, Cx0013, 0x0100, Z_(MultiOutApplicationType),1 },
|
||||
// { Zunk, Cx0013, 0x000E, (MultiOutStateText), 1, 0 },
|
||||
{ Zstring, Cx0013, 0x001C, Z_(MultiOutDescription), 1, 0 },
|
||||
{ Zuint16, Cx0013, 0x004A, Z_(MultiOutNumberOfStates),1, 0 },
|
||||
{ Zbool, Cx0013, 0x0051, Z_(MultiOutOutOfService), 1, 0 },
|
||||
{ Zuint16, Cx0013, 0x0055, Z_(MultiOutValue), 1, 0 },
|
||||
// { Zunk, Cx0013, 0x0057, (MultiOutPriorityArray),1, 0 },
|
||||
{ Zenum8, Cx0013, 0x0067, Z_(MultiOutReliability), 1, 0 },
|
||||
{ Zuint16, Cx0013, 0x0068, Z_(MultiOutRelinquishDefault),1, 0 },
|
||||
{ Zmap8, Cx0013, 0x006F, Z_(MultiOutStatusFlags), 1, 0 },
|
||||
{ Zuint32, Cx0013, 0x0100, Z_(MultiOutApplicationType),1, 0 },
|
||||
|
||||
// Multistate Value cluster
|
||||
// { Zunk, Cx0014, 0x000E, (MultiStateText), 1 },
|
||||
{ Zstring, Cx0014, 0x001C, Z_(MultiDescription), 1 },
|
||||
{ Zuint16, Cx0014, 0x004A, Z_(MultiNumberOfStates), 1 },
|
||||
{ Zbool, Cx0014, 0x0051, Z_(MultiOutOfService), 1 },
|
||||
{ Zuint16, Cx0014, 0x0055, Z_(MultiValue), 1 },
|
||||
{ Zenum8, Cx0014, 0x0067, Z_(MultiReliability), 1 },
|
||||
{ Zuint16, Cx0014, 0x0068, Z_(MultiRelinquishDefault),1 },
|
||||
{ Zmap8, Cx0014, 0x006F, Z_(MultiStatusFlags), 1 },
|
||||
{ Zuint32, Cx0014, 0x0100, Z_(MultiApplicationType), 1 },
|
||||
// { Zunk, Cx0014, 0x000E, (MultiStateText), 1, 0 },
|
||||
{ Zstring, Cx0014, 0x001C, Z_(MultiDescription), 1, 0 },
|
||||
{ Zuint16, Cx0014, 0x004A, Z_(MultiNumberOfStates), 1, 0 },
|
||||
{ Zbool, Cx0014, 0x0051, Z_(MultiOutOfService), 1, 0 },
|
||||
{ Zuint16, Cx0014, 0x0055, Z_(MultiValue), 1, 0 },
|
||||
{ Zenum8, Cx0014, 0x0067, Z_(MultiReliability), 1, 0 },
|
||||
{ Zuint16, Cx0014, 0x0068, Z_(MultiRelinquishDefault),1, 0 },
|
||||
{ Zmap8, Cx0014, 0x006F, Z_(MultiStatusFlags), 1, 0 },
|
||||
{ Zuint32, Cx0014, 0x0100, Z_(MultiApplicationType), 1, 0 },
|
||||
|
||||
// Power Profile cluster
|
||||
{ Zuint8, Cx001A, 0x0000, Z_(TotalProfileNum), 1 },
|
||||
{ Zbool, Cx001A, 0x0001, Z_(MultipleScheduling), 1 },
|
||||
{ Zmap8, Cx001A, 0x0002, Z_(EnergyFormatting), 1 },
|
||||
{ Zbool, Cx001A, 0x0003, Z_(EnergyRemote), 1 },
|
||||
{ Zmap8, Cx001A, 0x0004, Z_(ScheduleMode), 1 },
|
||||
{ Zuint8, Cx001A, 0x0000, Z_(TotalProfileNum), 1, 0 },
|
||||
{ Zbool, Cx001A, 0x0001, Z_(MultipleScheduling), 1, 0 },
|
||||
{ Zmap8, Cx001A, 0x0002, Z_(EnergyFormatting), 1, 0 },
|
||||
{ Zbool, Cx001A, 0x0003, Z_(EnergyRemote), 1, 0 },
|
||||
{ Zmap8, Cx001A, 0x0004, Z_(ScheduleMode), 1, 0 },
|
||||
|
||||
// Poll Control cluster
|
||||
{ Zuint32, Cx0020, 0x0000, Z_(CheckinInterval), 1 },
|
||||
{ Zuint32, Cx0020, 0x0001, Z_(LongPollInterval), 1 },
|
||||
{ Zuint16, Cx0020, 0x0002, Z_(ShortPollInterval), 1 },
|
||||
{ Zuint16, Cx0020, 0x0003, Z_(FastPollTimeout), 1 },
|
||||
{ Zuint32, Cx0020, 0x0004, Z_(CheckinIntervalMin), 1 },
|
||||
{ Zuint32, Cx0020, 0x0005, Z_(LongPollIntervalMin), 1 },
|
||||
{ Zuint16, Cx0020, 0x0006, Z_(FastPollTimeoutMax), 1 },
|
||||
{ Zuint32, Cx0020, 0x0000, Z_(CheckinInterval), 1, 0 },
|
||||
{ Zuint32, Cx0020, 0x0001, Z_(LongPollInterval), 1, 0 },
|
||||
{ Zuint16, Cx0020, 0x0002, Z_(ShortPollInterval), 1, 0 },
|
||||
{ Zuint16, Cx0020, 0x0003, Z_(FastPollTimeout), 1, 0 },
|
||||
{ Zuint32, Cx0020, 0x0004, Z_(CheckinIntervalMin), 1, 0 },
|
||||
{ Zuint32, Cx0020, 0x0005, Z_(LongPollIntervalMin), 1, 0 },
|
||||
{ Zuint16, Cx0020, 0x0006, Z_(FastPollTimeoutMax), 1, 0 },
|
||||
|
||||
// Shade Configuration cluster
|
||||
{ Zuint16, Cx0100, 0x0000, Z_(PhysicalClosedLimit), 1 },
|
||||
{ Zuint8, Cx0100, 0x0001, Z_(MotorStepSize), 1 },
|
||||
{ Zmap8, Cx0100, 0x0002, Z_(Status), 1 },
|
||||
{ Zuint16, Cx0100, 0x0010, Z_(ClosedLimit), 1 },
|
||||
{ Zenum8, Cx0100, 0x0011, Z_(Mode), 1 },
|
||||
{ Zuint16, Cx0100, 0x0000, Z_(PhysicalClosedLimit), 1, 0 },
|
||||
{ Zuint8, Cx0100, 0x0001, Z_(MotorStepSize), 1, 0 },
|
||||
{ Zmap8, Cx0100, 0x0002, Z_(Status), 1, 0 },
|
||||
{ Zuint16, Cx0100, 0x0010, Z_(ClosedLimit), 1, 0 },
|
||||
{ Zenum8, Cx0100, 0x0011, Z_(Mode), 1, 0 },
|
||||
|
||||
// Door Lock cluster
|
||||
{ Zenum8, Cx0101, 0x0000, Z_(LockState), 1 },
|
||||
{ Zenum8, Cx0101, 0x0001, Z_(LockType), 1 },
|
||||
{ Zbool, Cx0101, 0x0002, Z_(ActuatorEnabled), 1 },
|
||||
{ Zenum8, Cx0101, 0x0003, Z_(DoorState), 1 },
|
||||
{ Zuint32, Cx0101, 0x0004, Z_(DoorOpenEvents), 1 },
|
||||
{ Zuint32, Cx0101, 0x0005, Z_(DoorClosedEvents), 1 },
|
||||
{ Zuint16, Cx0101, 0x0006, Z_(OpenPeriod), 1 },
|
||||
{ Zenum8, Cx0101, 0x0000, Z_(LockState), 1, 0 },
|
||||
{ Zenum8, Cx0101, 0x0001, Z_(LockType), 1, 0 },
|
||||
{ Zbool, Cx0101, 0x0002, Z_(ActuatorEnabled), 1, 0 },
|
||||
{ Zenum8, Cx0101, 0x0003, Z_(DoorState), 1, 0 },
|
||||
{ Zuint32, Cx0101, 0x0004, Z_(DoorOpenEvents), 1, 0 },
|
||||
{ Zuint32, Cx0101, 0x0005, Z_(DoorClosedEvents), 1, 0 },
|
||||
{ Zuint16, Cx0101, 0x0006, Z_(OpenPeriod), 1, 0 },
|
||||
|
||||
// Aqara Lumi Vibration Sensor
|
||||
{ Zuint16, Cx0101, 0x0055, Z_(AqaraVibrationMode), 1 },
|
||||
{ Zuint16, Cx0101, 0x0503, Z_(AqaraVibrationsOrAngle), 1 },
|
||||
{ Zuint32, Cx0101, 0x0505, Z_(AqaraVibration505), 1 },
|
||||
{ Zuint48, Cx0101, 0x0508, Z_(AqaraAccelerometer), 1 },
|
||||
{ Zuint16, Cx0101, 0x0055, Z_(AqaraVibrationMode), 1, 0 },
|
||||
{ Zuint16, Cx0101, 0x0503, Z_(AqaraVibrationsOrAngle), 1, 0 },
|
||||
{ Zuint32, Cx0101, 0x0505, Z_(AqaraVibration505), 1, 0 },
|
||||
{ Zuint48, Cx0101, 0x0508, Z_(AqaraAccelerometer), 1, 0 },
|
||||
|
||||
// Window Covering cluster
|
||||
{ Zenum8, Cx0102, 0x0000, Z_(WindowCoveringType), 1 },
|
||||
{ Zuint16, Cx0102, 0x0001, Z_(PhysicalClosedLimitLift),1 },
|
||||
{ Zuint16, Cx0102, 0x0002, Z_(PhysicalClosedLimitTilt),1 },
|
||||
{ Zuint16, Cx0102, 0x0003, Z_(CurrentPositionLift), 1 },
|
||||
{ Zuint16, Cx0102, 0x0004, Z_(CurrentPositionTilt), 1 },
|
||||
{ Zuint16, Cx0102, 0x0005, Z_(NumberofActuationsLift),1 },
|
||||
{ Zuint16, Cx0102, 0x0006, Z_(NumberofActuationsTilt),1 },
|
||||
{ Zmap8, Cx0102, 0x0007, Z_(ConfigStatus), 1 },
|
||||
{ Zuint8, Cx0102, 0x0008, Z_(CurrentPositionLiftPercentage),1 },
|
||||
{ Zuint8, Cx0102, 0x0009, Z_(CurrentPositionTiltPercentage),1 },
|
||||
{ Zuint16, Cx0102, 0x0010, Z_(InstalledOpenLimitLift),1 },
|
||||
{ Zuint16, Cx0102, 0x0011, Z_(InstalledClosedLimitLift),1 },
|
||||
{ Zuint16, Cx0102, 0x0012, Z_(InstalledOpenLimitTilt),1 },
|
||||
{ Zuint16, Cx0102, 0x0013, Z_(InstalledClosedLimitTilt),1 },
|
||||
{ Zuint16, Cx0102, 0x0014, Z_(VelocityLift), 1 },
|
||||
{ Zuint16, Cx0102, 0x0015, Z_(AccelerationTimeLift),1 },
|
||||
{ Zuint16, Cx0102, 0x0016, Z_(DecelerationTimeLift), 1 },
|
||||
{ Zmap8, Cx0102, 0x0017, Z_(Mode), 1 },
|
||||
{ Zoctstr, Cx0102, 0x0018, Z_(IntermediateSetpointsLift),1 },
|
||||
{ Zoctstr, Cx0102, 0x0019, Z_(IntermediateSetpointsTilt),1 },
|
||||
{ Zenum8, Cx0102, 0x0000, Z_(WindowCoveringType), 1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0001, Z_(PhysicalClosedLimitLift),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0002, Z_(PhysicalClosedLimitTilt),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0003, Z_(CurrentPositionLift), 1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0004, Z_(CurrentPositionTilt), 1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0005, Z_(NumberofActuationsLift),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0006, Z_(NumberofActuationsTilt),1, 0 },
|
||||
{ Zmap8, Cx0102, 0x0007, Z_(ConfigStatus), 1, 0 },
|
||||
{ Zuint8, Cx0102, 0x0008, Z_(CurrentPositionLiftPercentage),1, 0 },
|
||||
{ Zuint8, Cx0102, 0x0009, Z_(CurrentPositionTiltPercentage),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0010, Z_(InstalledOpenLimitLift),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0011, Z_(InstalledClosedLimitLift),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0012, Z_(InstalledOpenLimitTilt),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0013, Z_(InstalledClosedLimitTilt),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0014, Z_(VelocityLift), 1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0015, Z_(AccelerationTimeLift),1, 0 },
|
||||
{ Zuint16, Cx0102, 0x0016, Z_(DecelerationTimeLift), 1, 0 },
|
||||
{ Zmap8, Cx0102, 0x0017, Z_(Mode), 1, 0 },
|
||||
{ Zoctstr, Cx0102, 0x0018, Z_(IntermediateSetpointsLift),1, 0 },
|
||||
{ Zoctstr, Cx0102, 0x0019, Z_(IntermediateSetpointsTilt),1, 0 },
|
||||
|
||||
// Thermostat
|
||||
{ Zint16, Cx0201, 0x0000, Z_(LocalTemperature), -100 },
|
||||
{ Zint16, Cx0201, 0x0001, Z_(OutdoorTemperature),-100 },
|
||||
{ Zuint8, Cx0201, 0x0007, Z_(PICoolingDemand), 1 },
|
||||
{ Zuint8, Cx0201, 0x0008, Z_(PIHeatingDemand), 1 },
|
||||
{ Zint8, Cx0201, 0x0010, Z_(LocalTemperatureCalibration), -10 },
|
||||
{ Zint16, Cx0201, 0x0011, Z_(OccupiedCoolingSetpoint), -100 },
|
||||
{ Zint16, Cx0201, 0x0012, Z_(OccupiedHeatingSetpoint), -100 },
|
||||
{ Zint16, Cx0201, 0x0013, Z_(UnoccupiedCoolingSetpoint), -100 },
|
||||
{ Zint16, Cx0201, 0x0014, Z_(UnoccupiedHeatingSetpoint), -100 },
|
||||
{ Zmap8, Cx0201, 0x001A, Z_(RemoteSensing), 1 },
|
||||
{ Zenum8, Cx0201, 0x001B, Z_(ControlSequenceOfOperation), 1 },
|
||||
{ Zenum8, Cx0201, 0x001C, Z_(SystemMode), 1 },
|
||||
{ Zint16, Cx0201, 0x0000, Z_(LocalTemperature), -100, Z_MAPPING(Z_Data_Thermo, temperature) },
|
||||
{ Zint16, Cx0201, 0x0001, Z_(OutdoorTemperature),-100, 0 },
|
||||
{ Zuint8, Cx0201, 0x0007, Z_(PICoolingDemand), 1, Z_MAPPING(Z_Data_Thermo, th_setpoint) },
|
||||
{ Zuint8, Cx0201, 0x0008, Z_(PIHeatingDemand), 1, Z_MAPPING(Z_Data_Thermo, th_setpoint) },
|
||||
{ Zint8, Cx0201, 0x0010, Z_(LocalTemperatureCalibration), -10, 0 },
|
||||
{ Zint16, Cx0201, 0x0011, Z_(OccupiedCoolingSetpoint), -100, Z_MAPPING(Z_Data_Thermo, temperature_target) },
|
||||
{ Zint16, Cx0201, 0x0012, Z_(OccupiedHeatingSetpoint), -100, Z_MAPPING(Z_Data_Thermo, temperature_target) },
|
||||
{ Zint16, Cx0201, 0x0013, Z_(UnoccupiedCoolingSetpoint), -100, 0 },
|
||||
{ Zint16, Cx0201, 0x0014, Z_(UnoccupiedHeatingSetpoint), -100, 0 },
|
||||
{ Zmap8, Cx0201, 0x001A, Z_(RemoteSensing), 1, 0 },
|
||||
{ Zenum8, Cx0201, 0x001B, Z_(ControlSequenceOfOperation), 1, 0 },
|
||||
{ Zenum8, Cx0201, 0x001C, Z_(SystemMode), 1, 0 },
|
||||
// below is Eurotronic specific
|
||||
{ Zenum8, Cx0201, 0x4000, Z_(TRVMode), 1 },
|
||||
{ Zuint8, Cx0201, 0x4001, Z_(SetValvePosition), 1 },
|
||||
{ Zuint8, Cx0201, 0x4002, Z_(EurotronicErrors), 1 },
|
||||
{ Zint16, Cx0201, 0x4003, Z_(CurrentTemperatureSetPoint), -100 },
|
||||
{ Zenum8, Cx0201, 0x4000, Z_(TRVMode), 1, 0 },
|
||||
{ Zuint8, Cx0201, 0x4001, Z_(SetValvePosition), 1, 0 },
|
||||
{ Zuint8, Cx0201, 0x4002, Z_(EurotronicErrors), 1, 0 },
|
||||
{ Zint16, Cx0201, 0x4003, Z_(CurrentTemperatureSetPoint), -100, 0 },
|
||||
|
||||
// Color Control cluster
|
||||
{ Zuint8, Cx0300, 0x0000, Z_(Hue), 1 },
|
||||
{ Zuint8, Cx0300, 0x0001, Z_(Sat), 1 },
|
||||
{ Zuint16, Cx0300, 0x0002, Z_(RemainingTime), 1 },
|
||||
{ Zuint16, Cx0300, 0x0003, Z_(X), 1 },
|
||||
{ Zuint16, Cx0300, 0x0004, Z_(Y), 1 },
|
||||
{ Zenum8, Cx0300, 0x0005, Z_(DriftCompensation), 1 },
|
||||
{ Zstring, Cx0300, 0x0006, Z_(CompensationText), 1 },
|
||||
{ Zuint16, Cx0300, 0x0007, Z_(CT), 1 },
|
||||
{ Zenum8, Cx0300, 0x0008, Z_(ColorMode), 1 },
|
||||
{ Zuint8, Cx0300, 0x0010, Z_(NumberOfPrimaries), 1 },
|
||||
{ Zuint16, Cx0300, 0x0011, Z_(Primary1X), 1 },
|
||||
{ Zuint16, Cx0300, 0x0012, Z_(Primary1Y), 1 },
|
||||
{ Zuint8, Cx0300, 0x0013, Z_(Primary1Intensity), 1 },
|
||||
{ Zuint16, Cx0300, 0x0015, Z_(Primary2X), 1 },
|
||||
{ Zuint16, Cx0300, 0x0016, Z_(Primary2Y), 1 },
|
||||
{ Zuint8, Cx0300, 0x0017, Z_(Primary2Intensity), 1 },
|
||||
{ Zuint16, Cx0300, 0x0019, Z_(Primary3X), 1 },
|
||||
{ Zuint16, Cx0300, 0x001A, Z_(Primary3Y), 1 },
|
||||
{ Zuint8, Cx0300, 0x001B, Z_(Primary3Intensity), 1 },
|
||||
{ Zuint16, Cx0300, 0x0030, Z_(WhitePointX), 1 },
|
||||
{ Zuint16, Cx0300, 0x0031, Z_(WhitePointY), 1 },
|
||||
{ Zuint16, Cx0300, 0x0032, Z_(ColorPointRX), 1 },
|
||||
{ Zuint16, Cx0300, 0x0033, Z_(ColorPointRY), 1 },
|
||||
{ Zuint8, Cx0300, 0x0034, Z_(ColorPointRIntensity), 1 },
|
||||
{ Zuint16, Cx0300, 0x0036, Z_(ColorPointGX), 1 },
|
||||
{ Zuint16, Cx0300, 0x0037, Z_(ColorPointGY), 1 },
|
||||
{ Zuint8, Cx0300, 0x0038, Z_(ColorPointGIntensity), 1 },
|
||||
{ Zuint16, Cx0300, 0x003A, Z_(ColorPointBX), 1 },
|
||||
{ Zuint16, Cx0300, 0x003B, Z_(ColorPointBY), 1 },
|
||||
{ Zuint8, Cx0300, 0x003C, Z_(ColorPointBIntensity), 1 },
|
||||
{ Zuint8, Cx0300, 0x0000, Z_(Hue), 1, Z_MAPPING(Z_Data_Light, hue) },
|
||||
{ Zuint8, Cx0300, 0x0001, Z_(Sat), 1, Z_MAPPING(Z_Data_Light, sat) },
|
||||
{ Zuint16, Cx0300, 0x0002, Z_(RemainingTime), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0003, Z_(X), 1, Z_MAPPING(Z_Data_Light, x) },
|
||||
{ Zuint16, Cx0300, 0x0004, Z_(Y), 1, Z_MAPPING(Z_Data_Light, y) },
|
||||
{ Zenum8, Cx0300, 0x0005, Z_(DriftCompensation), 1, 0 },
|
||||
{ Zstring, Cx0300, 0x0006, Z_(CompensationText), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0007, Z_(CT), 1, Z_MAPPING(Z_Data_Light, ct) },
|
||||
{ Zenum8, Cx0300, 0x0008, Z_(ColorMode), 1, Z_MAPPING(Z_Data_Light, colormode) },
|
||||
{ Zuint8, Cx0300, 0x0010, Z_(NumberOfPrimaries), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0011, Z_(Primary1X), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0012, Z_(Primary1Y), 1, 0 },
|
||||
{ Zuint8, Cx0300, 0x0013, Z_(Primary1Intensity), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0015, Z_(Primary2X), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0016, Z_(Primary2Y), 1, 0 },
|
||||
{ Zuint8, Cx0300, 0x0017, Z_(Primary2Intensity), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0019, Z_(Primary3X), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x001A, Z_(Primary3Y), 1, 0 },
|
||||
{ Zuint8, Cx0300, 0x001B, Z_(Primary3Intensity), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0030, Z_(WhitePointX), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0031, Z_(WhitePointY), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0032, Z_(ColorPointRX), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0033, Z_(ColorPointRY), 1, 0 },
|
||||
{ Zuint8, Cx0300, 0x0034, Z_(ColorPointRIntensity), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0036, Z_(ColorPointGX), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x0037, Z_(ColorPointGY), 1, 0 },
|
||||
{ Zuint8, Cx0300, 0x0038, Z_(ColorPointGIntensity), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x003A, Z_(ColorPointBX), 1, 0 },
|
||||
{ Zuint16, Cx0300, 0x003B, Z_(ColorPointBY), 1, 0 },
|
||||
{ Zuint8, Cx0300, 0x003C, Z_(ColorPointBIntensity), 1, 0 },
|
||||
|
||||
// Illuminance Measurement cluster
|
||||
{ Zuint16, Cx0400, 0x0000, Z_(Illuminance), 1 }, // Illuminance (in Lux)
|
||||
{ Zuint16, Cx0400, 0x0001, Z_(IlluminanceMinMeasuredValue), 1 }, //
|
||||
{ Zuint16, Cx0400, 0x0002, Z_(IlluminanceMaxMeasuredValue), 1 }, //
|
||||
{ Zuint16, Cx0400, 0x0003, Z_(IlluminanceTolerance), 1 }, //
|
||||
{ Zenum8, Cx0400, 0x0004, Z_(IlluminanceLightSensorType), 1 }, //
|
||||
{ Zunk, Cx0400, 0xFFFF, Z_(), 0 }, // Remove all other values
|
||||
{ Zuint16, Cx0400, 0x0000, Z_(Illuminance), 1, 0 }, // Illuminance (in Lux)
|
||||
{ Zuint16, Cx0400, 0x0001, Z_(IlluminanceMinMeasuredValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0400, 0x0002, Z_(IlluminanceMaxMeasuredValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0400, 0x0003, Z_(IlluminanceTolerance), 1, 0 }, //
|
||||
{ Zenum8, Cx0400, 0x0004, Z_(IlluminanceLightSensorType), 1, 0 }, //
|
||||
{ Zunk, Cx0400, 0xFFFF, Z_(), 0, 0 }, // Remove all other values
|
||||
|
||||
// Illuminance Level Sensing cluster
|
||||
{ Zenum8, Cx0401, 0x0000, Z_(IlluminanceLevelStatus), 1 }, // Illuminance (in Lux)
|
||||
{ Zenum8, Cx0401, 0x0001, Z_(IlluminanceLightSensorType), 1 }, // LightSensorType
|
||||
{ Zuint16, Cx0401, 0x0010, Z_(IlluminanceTargetLevel), 1 }, //
|
||||
{ Zunk, Cx0401, 0xFFFF, Z_(), 0 }, // Remove all other values
|
||||
{ Zenum8, Cx0401, 0x0000, Z_(IlluminanceLevelStatus), 1, 0 }, // Illuminance (in Lux)
|
||||
{ Zenum8, Cx0401, 0x0001, Z_(IlluminanceLightSensorType), 1, 0 }, // LightSensorType
|
||||
{ Zuint16, Cx0401, 0x0010, Z_(IlluminanceTargetLevel), 1, 0 }, //
|
||||
{ Zunk, Cx0401, 0xFFFF, Z_(), 0, 0 }, // Remove all other values
|
||||
|
||||
// Temperature Measurement cluster
|
||||
{ Zint16, Cx0402, 0x0000, Z_(Temperature), -100 }, // divide by 100
|
||||
{ Zint16, Cx0402, 0x0001, Z_(TemperatureMinMeasuredValue), -100 }, //
|
||||
{ Zint16, Cx0402, 0x0002, Z_(TemperatureMaxMeasuredValue), -100 }, //
|
||||
{ Zuint16, Cx0402, 0x0003, Z_(TemperatureTolerance), -100 }, //
|
||||
{ Zunk, Cx0402, 0xFFFF, Z_(), 0 }, // Remove all other values
|
||||
{ Zint16, Cx0402, 0x0000, Z_(Temperature), -100, Z_MAPPING(Z_Data_Thermo, temperature) },
|
||||
{ Zint16, Cx0402, 0x0001, Z_(TemperatureMinMeasuredValue), -100, 0 }, //
|
||||
{ Zint16, Cx0402, 0x0002, Z_(TemperatureMaxMeasuredValue), -100, 0 }, //
|
||||
{ Zuint16, Cx0402, 0x0003, Z_(TemperatureTolerance), -100, 0 }, //
|
||||
{ Zunk, Cx0402, 0xFFFF, Z_(), 0, 0 }, // Remove all other values
|
||||
|
||||
// Pressure Measurement cluster
|
||||
{ Zint16, Cx0403, 0x0000, Z_(Pressure), 1 }, // Pressure
|
||||
{ Zint16, Cx0403, 0x0001, Z_(PressureMinMeasuredValue), 1 }, //
|
||||
{ Zint16, Cx0403, 0x0002, Z_(PressureMaxMeasuredValue), 1 }, //
|
||||
{ Zuint16, Cx0403, 0x0003, Z_(PressureTolerance), 1 }, //
|
||||
{ Zint16, Cx0403, 0x0010, Z_(PressureScaledValue), 1 }, //
|
||||
{ Zint16, Cx0403, 0x0011, Z_(PressureMinScaledValue), 1 }, //
|
||||
{ Zint16, Cx0403, 0x0012, Z_(PressureMaxScaledValue), 1 }, //
|
||||
{ Zuint16, Cx0403, 0x0013, Z_(PressureScaledTolerance), 1 }, //
|
||||
{ Zint8, Cx0403, 0x0014, Z_(PressureScale), 1 }, //
|
||||
{ Zint16, Cx0403, 0xFF00, Z_(SeaPressure), 1 }, // Pressure at Sea Level, Tasmota specific
|
||||
{ Zunk, Cx0403, 0xFFFF, Z_(), 0 }, // Remove all other Pressure values
|
||||
{ Zint16, Cx0403, 0x0000, Z_(Pressure), 1, Z_MAPPING(Z_Data_Thermo, pressure) }, // Pressure
|
||||
{ Zint16, Cx0403, 0x0001, Z_(PressureMinMeasuredValue), 1, 0 }, //
|
||||
{ Zint16, Cx0403, 0x0002, Z_(PressureMaxMeasuredValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0403, 0x0003, Z_(PressureTolerance), 1, 0 }, //
|
||||
{ Zint16, Cx0403, 0x0010, Z_(PressureScaledValue), 1, 0 }, //
|
||||
{ Zint16, Cx0403, 0x0011, Z_(PressureMinScaledValue), 1, 0 }, //
|
||||
{ Zint16, Cx0403, 0x0012, Z_(PressureMaxScaledValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0403, 0x0013, Z_(PressureScaledTolerance), 1, 0 }, //
|
||||
{ Zint8, Cx0403, 0x0014, Z_(PressureScale), 1, 0 }, //
|
||||
{ Zint16, Cx0403, 0xFF00, Z_(SeaPressure), 1, Z_MAPPING(Z_Data_Thermo, pressure) }, // Pressure at Sea Level, Tasmota specific
|
||||
{ Zunk, Cx0403, 0xFFFF, Z_(), 0, 0 }, // Remove all other Pressure values
|
||||
|
||||
// Flow Measurement cluster
|
||||
{ Zuint16, Cx0404, 0x0000, Z_(FlowRate), -10 }, // Flow (in m3/h)
|
||||
{ Zuint16, Cx0404, 0x0001, Z_(FlowMinMeasuredValue), 1 }, //
|
||||
{ Zuint16, Cx0404, 0x0002, Z_(FlowMaxMeasuredValue), 1 }, //
|
||||
{ Zuint16, Cx0404, 0x0003, Z_(FlowTolerance), 1 }, //
|
||||
{ Zunk, Cx0404, 0xFFFF, Z_(), 0 }, // Remove all other values
|
||||
{ Zuint16, Cx0404, 0x0000, Z_(FlowRate), -10, 0 }, // Flow (in m3/h)
|
||||
{ Zuint16, Cx0404, 0x0001, Z_(FlowMinMeasuredValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0404, 0x0002, Z_(FlowMaxMeasuredValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0404, 0x0003, Z_(FlowTolerance), 1, 0 }, //
|
||||
{ Zunk, Cx0404, 0xFFFF, Z_(), 0, 0 }, // Remove all other values
|
||||
|
||||
// Relative Humidity Measurement cluster
|
||||
{ Zuint16, Cx0405, 0x0000, Z_(Humidity), -100 }, // Humidity
|
||||
{ Zuint16, Cx0405, 0x0001, Z_(HumidityMinMeasuredValue), 1 }, //
|
||||
{ Zuint16, Cx0405, 0x0002, Z_(HumidityMaxMeasuredValue), 1 }, //
|
||||
{ Zuint16, Cx0405, 0x0003, Z_(HumidityTolerance), 1 }, //
|
||||
{ Zunk, Cx0405, 0xFFFF, Z_(), 0 }, // Remove all other values
|
||||
{ Zuint16, Cx0405, 0x0000, Z_(Humidity), -100, Z_MAPPING(Z_Data_Thermo, humidity) }, // Humidity
|
||||
{ Zuint16, Cx0405, 0x0001, Z_(HumidityMinMeasuredValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0405, 0x0002, Z_(HumidityMaxMeasuredValue), 1, 0 }, //
|
||||
{ Zuint16, Cx0405, 0x0003, Z_(HumidityTolerance), 1, 0 }, //
|
||||
{ Zunk, Cx0405, 0xFFFF, Z_(), 0, 0 }, // Remove all other values
|
||||
|
||||
// Occupancy Sensing cluster
|
||||
{ Zmap8, Cx0406, 0x0000, Z_(Occupancy), 1 }, // Occupancy (map8)
|
||||
{ Zenum8, Cx0406, 0x0001, Z_(OccupancySensorType), 1 }, // OccupancySensorType
|
||||
{ Zunk, Cx0406, 0xFFFF, Z_(), 0 }, // Remove all other values
|
||||
{ Zmap8, Cx0406, 0x0000, Z_(Occupancy), 1, 0 }, // Occupancy (map8)
|
||||
{ Zenum8, Cx0406, 0x0001, Z_(OccupancySensorType), 1, 0 }, // OccupancySensorType
|
||||
{ Zunk, Cx0406, 0xFFFF, Z_(), 0, 0 }, // Remove all other values
|
||||
|
||||
// IAS Cluster (Intruder Alarm System)
|
||||
{ Zenum8, Cx0500, 0x0000, Z_(ZoneState), 1 }, // Occupancy (map8)
|
||||
{ Zenum16, Cx0500, 0x0001, Z_(ZoneType), 1 }, // Occupancy (map8)
|
||||
{ Zmap16, Cx0500, 0x0002, Z_(ZoneStatus), 1 }, // Occupancy (map8)
|
||||
{ Zenum8, Cx0500, 0x0000, Z_(ZoneState), 1, 0 }, // Occupancy (map8)
|
||||
{ Zenum16, Cx0500, 0x0001, Z_(ZoneType), 1, 0 }, // Occupancy (map8)
|
||||
{ Zmap16, Cx0500, 0x0002, Z_(ZoneStatus), 1, Z_MAPPING(Z_Data_Alarm, zone_type) }, // Occupancy (map8)
|
||||
|
||||
// Metering (Smart Energy) cluster
|
||||
{ Zuint48, Cx0702, 0x0000, Z_(CurrentSummDelivered), 1 },
|
||||
{ Zuint48, Cx0702, 0x0000, Z_(CurrentSummDelivered), 1, 0 },
|
||||
|
||||
// Meter Identification cluster
|
||||
{ Zstring, Cx0B01, 0x0000, Z_(CompanyName), 1 },
|
||||
{ Zuint16, Cx0B01, 0x0001, Z_(MeterTypeID), 1 },
|
||||
{ Zuint16, Cx0B01, 0x0004, Z_(DataQualityID), 1 },
|
||||
{ Zstring, Cx0B01, 0x0005, Z_(CustomerName), 1 },
|
||||
{ Zoctstr, Cx0B01, 0x0006, Z_(Model), 1 },
|
||||
{ Zoctstr, Cx0B01, 0x0007, Z_(PartNumber), 1 },
|
||||
{ Zoctstr, Cx0B01, 0x0008, Z_(ProductRevision), 1 },
|
||||
{ Zoctstr, Cx0B01, 0x000A, Z_(SoftwareRevision), 1 },
|
||||
{ Zstring, Cx0B01, 0x000B, Z_(UtilityName), 1 },
|
||||
{ Zstring, Cx0B01, 0x000C, Z_(POD), 1 },
|
||||
{ Zint24, Cx0B01, 0x000D, Z_(AvailablePower), 1 },
|
||||
{ Zint24, Cx0B01, 0x000E, Z_(PowerThreshold), 1 },
|
||||
{ Zstring, Cx0B01, 0x0000, Z_(CompanyName), 1, 0 },
|
||||
{ Zuint16, Cx0B01, 0x0001, Z_(MeterTypeID), 1, 0 },
|
||||
{ Zuint16, Cx0B01, 0x0004, Z_(DataQualityID), 1, 0 },
|
||||
{ Zstring, Cx0B01, 0x0005, Z_(CustomerName), 1, 0 },
|
||||
{ Zoctstr, Cx0B01, 0x0006, Z_(Model), 1, 0 },
|
||||
{ Zoctstr, Cx0B01, 0x0007, Z_(PartNumber), 1, 0 },
|
||||
{ Zoctstr, Cx0B01, 0x0008, Z_(ProductRevision), 1, 0 },
|
||||
{ Zoctstr, Cx0B01, 0x000A, Z_(SoftwareRevision), 1, 0 },
|
||||
{ Zstring, Cx0B01, 0x000B, Z_(UtilityName), 1, 0 },
|
||||
{ Zstring, Cx0B01, 0x000C, Z_(POD), 1, 0 },
|
||||
{ Zint24, Cx0B01, 0x000D, Z_(AvailablePower), 1, 0 },
|
||||
{ Zint24, Cx0B01, 0x000E, Z_(PowerThreshold), 1, 0 },
|
||||
|
||||
// Electrical Measurement cluster
|
||||
{ Zuint16, Cx0B04, 0x0505, Z_(RMSVoltage), 1 },
|
||||
{ Zuint16, Cx0B04, 0x0508, Z_(RMSCurrent), 1 },
|
||||
{ Zint16, Cx0B04, 0x050B, Z_(ActivePower), 1 },
|
||||
{ Zuint16, Cx0B04, 0x0505, Z_(RMSVoltage), 1, Z_MAPPING(Z_Data_Plug, mains_voltage) },
|
||||
{ Zuint16, Cx0B04, 0x0508, Z_(RMSCurrent), 1, 0 },
|
||||
{ Zint16, Cx0B04, 0x050B, Z_(ActivePower), 1, Z_MAPPING(Z_Data_Plug, mains_power) },
|
||||
|
||||
// Diagnostics cluster
|
||||
{ Zuint16, Cx0B05, 0x0000, Z_(NumberOfResets), 1 },
|
||||
{ Zuint16, Cx0B05, 0x0001, Z_(PersistentMemoryWrites),1 },
|
||||
{ Zuint8, Cx0B05, 0x011C, Z_(LastMessageLQI), 1 },
|
||||
{ Zuint8, Cx0B05, 0x011D, Z_(LastMessageRSSI), 1 },
|
||||
{ Zuint16, Cx0B05, 0x0000, Z_(NumberOfResets), 1, 0 },
|
||||
{ Zuint16, Cx0B05, 0x0001, Z_(PersistentMemoryWrites),1, 0 },
|
||||
{ Zuint8, Cx0B05, 0x011C, Z_(LastMessageLQI), 1, 0 },
|
||||
{ Zuint8, Cx0B05, 0x011D, Z_(LastMessageRSSI), 1, 0 },
|
||||
|
||||
};
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
typedef union ZCLHeaderFrameControl_t {
|
||||
struct {
|
||||
|
@ -1673,8 +1681,10 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_
|
|||
|
||||
// Look for an entry in the converter table
|
||||
bool found = false;
|
||||
int8_t conv_multiplier;
|
||||
const char * conv_name;
|
||||
Z_Data_Type map_type;
|
||||
uint8_t map_offset;
|
||||
uint8_t zigbee_type;
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
|
||||
const Z_AttributeConverter *converter = &Z_PostProcess[i];
|
||||
uint16_t conv_cluster = CxToCluster(pgm_read_byte(&converter->cluster_short));
|
||||
|
@ -1682,7 +1692,10 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_
|
|||
|
||||
if ((conv_cluster == cluster) &&
|
||||
((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) {
|
||||
conv_multiplier = pgm_read_byte(&converter->multiplier);
|
||||
zigbee_type = pgm_read_byte(&converter->type);
|
||||
uint8_t mapping = pgm_read_byte(&converter->mapping);
|
||||
map_type = (Z_Data_Type) ((mapping & 0xF0)>>4);
|
||||
map_offset = (mapping & 0x0F);
|
||||
conv_name = Z_strings + pgm_read_word(&converter->name_offset);
|
||||
found = true;
|
||||
break;
|
||||
|
@ -1691,42 +1704,39 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_
|
|||
|
||||
// apply multiplier if needed
|
||||
float fval = attr.getFloat();
|
||||
if (found) {
|
||||
if (0 == conv_multiplier) { attr_list.removeAttribute(&attr); continue; } // remove attribute if multiplier is zero
|
||||
if (1 != conv_multiplier) {
|
||||
if (conv_multiplier > 0) { fval = fval * conv_multiplier; }
|
||||
else { fval = fval / (-conv_multiplier); }
|
||||
attr.setFloat(fval);
|
||||
if (found && (map_type != Z_Data_Type::Z_Unknown)) {
|
||||
// We apply an automatic mapping to Z_Data_XXX object
|
||||
// First we find or instantiate the correct Z_Data_XXX accorfing to the endpoint
|
||||
// Then store the attribute at the attribute addres (via offset) and according to size 8/16/32 bits
|
||||
|
||||
// we don't apply the multiplier, but instead store in Z_Data_XXX object
|
||||
Z_Data & data = device.data.getByType(map_type, src_ep);
|
||||
uint8_t *attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + map_offset;
|
||||
uint32_t uval32 = attr.getUInt(); // call converter to uint only once
|
||||
int32_t ival32 = attr.getInt(); // call converter to int only once
|
||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Mapping type=%d offset=%d zigbee_type=%02X value=%d\n"), (uint8_t) map_type, map_offset, zigbee_type, ival32);
|
||||
switch (zigbee_type) {
|
||||
case Zenum8:
|
||||
case Zuint8: *(uint8_t*)attr_address = uval32; break;
|
||||
case Zenum16:
|
||||
case Zuint16: *(uint16_t*)attr_address = uval32; break;
|
||||
case Zuint32: *(uint32_t*)attr_address = uval32; break;
|
||||
case Zint8: *(int8_t*)attr_address = ival32; break;
|
||||
case Zint16: *(int16_t*)attr_address = ival32; break;
|
||||
case Zint32: *(int32_t*)attr_address = ival32; break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t uval16 = attr.getUInt(); // call converter to uint only once
|
||||
int16_t ival16 = attr.getInt(); // call converter to int only once
|
||||
Z_Data_Set & data = device.data;
|
||||
// update any internal structure
|
||||
switch (ccccaaaa) {
|
||||
case 0x00000004: zigbee_devices.setManufId(shortaddr, attr.getStr()); break;
|
||||
case 0x00000005: zigbee_devices.setModelId(shortaddr, attr.getStr()); break;
|
||||
case 0x00010021: zigbee_devices.setBatteryPercent(shortaddr, uval16); break;
|
||||
case 0x00060000:
|
||||
case 0x00068000: device.setPower(attr.getBool()); break;
|
||||
case 0x00080000: device.dimmer = uval16; break;
|
||||
case 0x02010000: device.temperature = fval * 10 + 0.5f; break;
|
||||
case 0x02010008: device.th_setpoint = uval16; break;
|
||||
case 0x02010012: device.temperature_target = fval * 10 + 0.5f; break;
|
||||
case 0x02010007: device.th_setpoint = uval16; break;
|
||||
case 0x02010011: device.temperature_target = fval * 10 + 0.5f; break;
|
||||
case 0x03000000: device.hue = changeUIntScale(uval16, 0, 254, 0, 360); break;
|
||||
case 0x03000001: device.sat = uval16; break;
|
||||
case 0x03000003: device.x = uval16; break;
|
||||
case 0x03000004: device.y = uval16; break;
|
||||
case 0x03000007: device.ct = uval16; break;
|
||||
case 0x03000008: device.colormode = uval16; break;
|
||||
case 0x04020000: device.temperature = fval * 10 + 0.5f; break;
|
||||
case 0x0403FF00: device.pressure = fval + 0.5f; break; // give priority to sea level
|
||||
case 0x04030000: device.pressure = fval + 0.5f; break;
|
||||
case 0x04050000: device.humidity = fval + 0.5f; break;
|
||||
case 0x0B040505: device.mains_voltage = uval16; break;
|
||||
case 0x0B04050B: device.mains_power = ival16; break;
|
||||
case 0x00068000: device.setPower(attr.getBool(), src_ep); break;
|
||||
}
|
||||
|
||||
// Replace cluster/attribute with name
|
||||
|
|
|
@ -33,7 +33,7 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
|
|||
D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|"
|
||||
D_CMND_ZIGBEE_BIND "|" D_CMND_ZIGBEE_UNBIND "|" D_CMND_ZIGBEE_PING "|" D_CMND_ZIGBEE_MODELID "|"
|
||||
D_CMND_ZIGBEE_LIGHT "|" D_CMND_ZIGBEE_RESTORE "|" D_CMND_ZIGBEE_BIND_STATE "|"
|
||||
D_CMND_ZIGBEE_CONFIG
|
||||
D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA
|
||||
;
|
||||
|
||||
void (* const ZigbeeCommand[])(void) PROGMEM = {
|
||||
|
@ -48,7 +48,7 @@ void (* const ZigbeeCommand[])(void) PROGMEM = {
|
|||
&CmndZbForget, &CmndZbSave, &CmndZbName,
|
||||
&CmndZbBind, &CmndZbUnbind, &CmndZbPing, &CmndZbModelId,
|
||||
&CmndZbLight, &CmndZbRestore, &CmndZbBindState,
|
||||
&CmndZbConfig,
|
||||
&CmndZbConfig, CmndZbData,
|
||||
};
|
||||
|
||||
/********************************************************************************************/
|
||||
|
@ -56,6 +56,10 @@ void (* const ZigbeeCommand[])(void) PROGMEM = {
|
|||
// Initialize internal structures
|
||||
void ZigbeeInit(void)
|
||||
{
|
||||
// #pragma GCC diagnostic push
|
||||
// #pragma GCC diagnostic ignored "-Winvalid-offsetof"
|
||||
// Serial.printf(">>> offset %d %d %d\n", Z_offset(Z_Data_Light, dimmer), Z_offset(Z_Data_Light, x), Z_offset(Z_Data_Thermo, temperature));
|
||||
// #pragma GCC diagnostic pop
|
||||
// Check if settings in Flash are set
|
||||
if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) {
|
||||
if (0 == Settings.zb_channel) {
|
||||
|
@ -1018,7 +1022,7 @@ void CmndZbLight(void) {
|
|||
int8_t bulbtype = strtol(p, nullptr, 10);
|
||||
if (bulbtype > 5) { bulbtype = 5; }
|
||||
if (bulbtype < -1) { bulbtype = -1; }
|
||||
zigbee_devices.setHueBulbtype(shortaddr, bulbtype);
|
||||
zigbee_devices.setLightProfile(shortaddr, bulbtype);
|
||||
}
|
||||
String dump = zigbee_devices.dumpLightState(shortaddr);
|
||||
Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_LIGHT "\":%s}"), dump.c_str());
|
||||
|
@ -1203,7 +1207,7 @@ 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);
|
||||
if (XdrvMailbox.payload > 0) {
|
||||
if (XdrvMailbox.data_len > 0) {
|
||||
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||
}
|
||||
|
||||
|
@ -1212,6 +1216,195 @@ void CmndZbStatus(void) {
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Innder part of ZbData parsing
|
||||
//
|
||||
// {"L-02":{"Dimmer":10,"Sat":254}}
|
||||
bool parseDeviceInnerData(class Z_Device & device, JsonParserObject root) {
|
||||
for (auto data_elt : root) {
|
||||
const char * data_type_str = data_elt.getStr();
|
||||
Z_Data_Type data_type;
|
||||
|
||||
switch (data_type_str[0]) {
|
||||
case 'P': data_type = Z_Data_Type::Z_Plug; break;
|
||||
case 'L': data_type = Z_Data_Type::Z_Light; break;
|
||||
case 'O': data_type = Z_Data_Type::Z_OnOff; break;
|
||||
case 'T': data_type = Z_Data_Type::Z_Thermo; break;
|
||||
case 'A': data_type = Z_Data_Type::Z_Alarm; break;
|
||||
case '_': data_type = Z_Data_Type::Z_Device; break;
|
||||
default: data_type = Z_Data_Type::Z_Unknown; break;
|
||||
}
|
||||
// The format should be a valid Code Lette followed by '-'
|
||||
if (data_type == Z_Data_Type::Z_Unknown) {
|
||||
Response_P(PSTR("{\"%s\":\"%s \"%s\"\"}"), XdrvMailbox.command, PSTR("Invalid Parameters"), data_type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonParserObject data_values = data_elt.getValue().getObject();
|
||||
if (!data_values) { return false; }
|
||||
|
||||
// Decode the endpoint number
|
||||
uint8_t endpoint = strtoul(&data_type_str[1], nullptr, 16); // hex base 16
|
||||
JsonParserToken val;
|
||||
|
||||
switch (data_type) {
|
||||
case Z_Data_Type::Z_Plug:
|
||||
{
|
||||
Z_Data_Plug & plug = device.data.get<Z_Data_Plug>(endpoint);
|
||||
|
||||
if (val = data_values[PSTR("RMSVoltage")]) { plug.setMainsVoltage(val.getUInt()); }
|
||||
if (val = data_values[PSTR("ActivePower")]) { plug.setMainsPower(val.getInt()); }
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_Light:
|
||||
{
|
||||
Z_Data_Light & light = device.data.get<Z_Data_Light>(endpoint);
|
||||
|
||||
if (val = data_values[PSTR("Light")]) { light.setConfig(val.getUInt()); }
|
||||
if (val = data_values[PSTR("Dimmer")]) { light.setDimmer(val.getUInt()); }
|
||||
if (val = data_values[PSTR("Colormode")]) { light.setColorMode(val.getUInt()); }
|
||||
if (val = data_values[PSTR("CT")]) { light.setCT(val.getUInt()); }
|
||||
if (val = data_values[PSTR("Sat")]) { light.setSat(val.getUInt()); }
|
||||
if (val = data_values[PSTR("Hue")]) { light.setHue(val.getUInt()); }
|
||||
if (val = data_values[PSTR("X")]) { light.setX(val.getUInt()); }
|
||||
if (val = data_values[PSTR("Y")]) { light.setY(val.getUInt()); }
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_OnOff:
|
||||
{
|
||||
Z_Data_OnOff & onoff = device.data.get<Z_Data_OnOff>(endpoint);
|
||||
|
||||
if (val = data_values[PSTR("Power")]) { onoff.setPower(val.getUInt() ? true : false); }
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_Thermo:
|
||||
{
|
||||
Z_Data_Thermo & thermo = device.data.get<Z_Data_Thermo>(endpoint);
|
||||
|
||||
if (val = data_values[PSTR("Temperature")]) { thermo.setTemperature(val.getInt()); }
|
||||
if (val = data_values[PSTR("Pressure")]) { thermo.setPressure(val.getUInt()); }
|
||||
if (val = data_values[PSTR("Humidity")]) { thermo.setHumidity(val.getUInt()); }
|
||||
if (val = data_values[PSTR("ThSetpoint")]) { thermo.setThSetpoint(val.getUInt()); }
|
||||
if (val = data_values[PSTR("TempTarget")]) { thermo.setTempTarget(val.getInt()); }
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_Alarm:
|
||||
{
|
||||
Z_Data_Alarm & alarm = device.data.get<Z_Data_Alarm>(endpoint);
|
||||
|
||||
if (val = data_values[PSTR("ZoneType")]) { alarm.setZoneType(val.getUInt()); }
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_Device:
|
||||
{
|
||||
if (val = data_values[PSTR(D_CMND_ZIGBEE_LINKQUALITY)]) { device.lqi = val.getUInt(); }
|
||||
if (val = data_values[PSTR("BatteryPercentage")]) { device.batterypercent = val.getUInt(); }
|
||||
if (val = data_values[PSTR("LastSeen")]) { device.last_seen = val.getUInt(); }
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Command `ZbData`
|
||||
//
|
||||
void CmndZbData(void) {
|
||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||
RemoveAllSpaces(XdrvMailbox.data);
|
||||
if (XdrvMailbox.data[0] == '{') {
|
||||
// JSON input, enter saved data into memory -- essentially for debugging
|
||||
JsonParser parser(XdrvMailbox.data);
|
||||
JsonParserObject root = parser.getRootObject();
|
||||
if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||
|
||||
// Skip `ZbData` if present
|
||||
JsonParserToken zbdata = root.getObject().findStartsWith(PSTR("ZbData"));
|
||||
if (zbdata) {
|
||||
root = zbdata;
|
||||
}
|
||||
|
||||
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);
|
||||
JsonParserObject inner_data = device_name.getValue().getObject();
|
||||
if (inner_data) {
|
||||
if (!parseDeviceInnerData(device, inner_data)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
ResponseCmndDone();
|
||||
} else {
|
||||
// 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_attribute_list attr_data;
|
||||
|
||||
{ // scope to force object deallocation
|
||||
Z_attribute_list device_attr;
|
||||
device.toAttributes(device_attr);
|
||||
attr_data.addAttribute(F("_00")).setStrRaw(device_attr.toString(true).c_str());
|
||||
}
|
||||
|
||||
// Iterate on data objects
|
||||
for (auto & data_elt : device.data) {
|
||||
Z_attribute_list inner_attr;
|
||||
char key[4];
|
||||
snprintf_P(key, sizeof(key), "?%02X", data_elt.getEndpoint());
|
||||
// The key is in the form "L-01", where 'L' is the type and '01' the endpoint in hex format
|
||||
// 'L' = Light
|
||||
// 'P' = Power
|
||||
//
|
||||
switch (data_elt.getType()) {
|
||||
case Z_Data_Type::Z_Plug:
|
||||
{
|
||||
key[0] = 'P';
|
||||
Z_Data_Plug::toAttributes(inner_attr, (Z_Data_Plug&) data_elt);
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_Light:
|
||||
{
|
||||
key[0] = 'L';
|
||||
Z_Data_Light::toAttributes(inner_attr, (Z_Data_Light&) data_elt);
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_OnOff:
|
||||
{
|
||||
key[0] = 'O';
|
||||
Z_Data_OnOff::toAttributes(inner_attr, (Z_Data_OnOff&) data_elt);
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_Thermo:
|
||||
{
|
||||
key[0] = 'T';
|
||||
Z_Data_Thermo::toAttributes(inner_attr, (Z_Data_Thermo&) data_elt);
|
||||
}
|
||||
break;
|
||||
case Z_Data_Type::Z_Alarm:
|
||||
{
|
||||
key[0] = 'A';
|
||||
Z_Data_Alarm::toAttributes(inner_attr, (Z_Data_Alarm&) data_elt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (key[0] != '?') {
|
||||
attr_data.addAttribute(key).setStrRaw(inner_attr.toString(true).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
char hex[8];
|
||||
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
|
||||
Response_P(PSTR("{\"%s\":{\"%s\":%s}}"), XdrvMailbox.command, hex, attr_data.toString(true).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Command `ZbConfig`
|
||||
//
|
||||
|
@ -1431,67 +1624,71 @@ void ZigbeeShow(bool json)
|
|||
), dhm );
|
||||
|
||||
// Sensors
|
||||
bool temperature_ok = device.validTemperature();
|
||||
bool tempareture_target_ok = device.validTemperatureTarget();
|
||||
bool th_setpoint_ok = device.validThSetpoint();
|
||||
bool humidity_ok = device.validHumidity();
|
||||
bool pressure_ok = device.validPressure();
|
||||
const Z_Data_Thermo & thermo = device.data.find<Z_Data_Thermo>();
|
||||
|
||||
if (temperature_ok || tempareture_target_ok || th_setpoint_ok || humidity_ok || pressure_ok) {
|
||||
if (&thermo != nullptr) {
|
||||
WSContentSend_P(PSTR("<tr class='htr'><td colspan=\"4\">┆"));
|
||||
if (temperature_ok) {
|
||||
if (thermo.validTemperature()) {
|
||||
char buf[12];
|
||||
dtostrf(device.temperature / 10.0f, 3, 1, buf);
|
||||
dtostrf(thermo.getTemperature() / 100.0f, 3, 1, buf);
|
||||
WSContentSend_PD(PSTR(" ☀️ %s°C"), buf);
|
||||
}
|
||||
if (tempareture_target_ok) {
|
||||
if (thermo.validTempTarget()) {
|
||||
char buf[12];
|
||||
dtostrf(device.temperature_target / 10.0f, 3, 1, buf);
|
||||
dtostrf(thermo.getTempTarget() / 100.0f, 3, 1, buf);
|
||||
WSContentSend_PD(PSTR(" 🎯 %s°C"), buf);
|
||||
}
|
||||
if (th_setpoint_ok) {
|
||||
WSContentSend_PD(PSTR(" ⚙️ %d%%"), device.th_setpoint);
|
||||
if (thermo.validThSetpoint()) {
|
||||
WSContentSend_PD(PSTR(" ⚙️ %d%%"), thermo.getThSetpoint());
|
||||
}
|
||||
if (humidity_ok) {
|
||||
WSContentSend_P(PSTR(" 💧 %d%%"), device.humidity);
|
||||
if (thermo.validHumidity()) {
|
||||
WSContentSend_P(PSTR(" 💧 %d%%"), (uint16_t)(thermo.getHumidity() / 100.0f + 0.5f));
|
||||
}
|
||||
if (pressure_ok) {
|
||||
WSContentSend_P(PSTR(" ⛅ %d hPa"), device.pressure);
|
||||
if (thermo.validPressure()) {
|
||||
WSContentSend_P(PSTR(" ⛅ %d hPa"), thermo.getPressure());
|
||||
}
|
||||
|
||||
WSContentSend_P(PSTR("{e}"));
|
||||
}
|
||||
|
||||
// Light, switches and plugs
|
||||
bool power_ok = device.validPower();
|
||||
if (power_ok) {
|
||||
uint8_t channels = device.getLightChannels();
|
||||
if (0xFF == channels) { channels = 5; } // if number of channel is unknown, display all known attributes
|
||||
WSContentSend_P(PSTR("<tr class='htr'><td colspan=\"4\">┆ %s"), device.getPower() ? PSTR(D_ON) : PSTR(D_OFF));
|
||||
if (device.validDimmer() && (channels >= 1)) {
|
||||
WSContentSend_P(PSTR(" 🔅 %d%%"), changeUIntScale(device.dimmer,0,254,0,100));
|
||||
const Z_Data_OnOff & onoff = device.data.find<Z_Data_OnOff>();
|
||||
const Z_Data_Light & light = device.data.find<Z_Data_Light>();
|
||||
bool light_display = (&light != nullptr) ? light.validDimmer() : false;
|
||||
const Z_Data_Plug & plug = device.data.find<Z_Data_Plug>();
|
||||
if ((&onoff != nullptr) || light_display || (&plug != nullptr)) {
|
||||
int8_t channels = device.getLightChannels();
|
||||
if (channels < 0) { channels = 5; } // if number of channel is unknown, display all known attributes
|
||||
WSContentSend_P(PSTR("<tr class='htr'><td colspan=\"4\">┆"));
|
||||
if (&onoff != nullptr) {
|
||||
WSContentSend_P(PSTR(" %s"), device.getPower() ? PSTR(D_ON) : PSTR(D_OFF));
|
||||
}
|
||||
if (device.validCT() && ((channels == 2) || (channels == 5))) {
|
||||
uint32_t ct_k = (((1000000 / device.ct) + 25) / 50) * 50;
|
||||
WSContentSend_P(PSTR(" <span title=\"CT %d\"><small>⚪ </small>%dK</span>"), device.ct, ct_k);
|
||||
}
|
||||
if (device.validHue() && device.validSat() && (channels >= 3)) {
|
||||
uint8_t r,g,b;
|
||||
uint8_t sat = changeUIntScale(device.sat, 0, 254, 0, 255); // scale to 0..255
|
||||
LightStateClass::HsToRgb(device.hue, sat, &r, &g, &b);
|
||||
WSContentSend_P(PSTR(" <i class=\"bx\" style=\"--cl:#%02X%02X%02X\"></i>#%02X%02X%02X"), r,g,b,r,g,b);
|
||||
} else if (device.validX() && device.validY() && (channels >= 3)) {
|
||||
uint8_t r,g,b;
|
||||
LightStateClass::XyToRgb(device.x / 65535.0f, device.y / 65535.0f, &r, &g, &b);
|
||||
WSContentSend_P(PSTR(" <i class=\"bx\" style=\"--cl:#%02X%02X%02X\"></i> #%02X%02X%02X"), r,g,b,r,g,b);
|
||||
}
|
||||
if (device.validMainsPower() || device.validMainsVoltage()) {
|
||||
WSContentSend_P(PSTR(" ⚡ "));
|
||||
if (device.validMainsVoltage()) {
|
||||
WSContentSend_P(PSTR(" %dV"), device.mains_voltage);
|
||||
if (&light != nullptr) {
|
||||
if (light.validDimmer() && (channels >= 1)) {
|
||||
WSContentSend_P(PSTR(" 🔅 %d%%"), changeUIntScale(light.getDimmer(),0,254,0,100));
|
||||
}
|
||||
if (device.validMainsPower()) {
|
||||
WSContentSend_P(PSTR(" %dW"), device.mains_power);
|
||||
if (light.validCT() && ((channels == 2) || (channels == 5))) {
|
||||
uint32_t ct_k = (((1000000 / light.getCT()) + 25) / 50) * 50;
|
||||
WSContentSend_P(PSTR(" <span title=\"CT %d\"><small>⚪ </small>%dK</span>"), light.getCT(), ct_k);
|
||||
}
|
||||
if (light.validHue() && light.validSat() && (channels >= 3)) {
|
||||
uint8_t r,g,b;
|
||||
uint8_t sat = changeUIntScale(light.getSat(), 0, 254, 0, 255); // scale to 0..255
|
||||
LightStateClass::HsToRgb(light.getHue(), sat, &r, &g, &b);
|
||||
WSContentSend_P(PSTR(" <i class=\"bx\" style=\"--cl:#%02X%02X%02X\"></i>#%02X%02X%02X"), r,g,b,r,g,b);
|
||||
} else if (light.validX() && light.validY() && (channels >= 3)) {
|
||||
uint8_t r,g,b;
|
||||
LightStateClass::XyToRgb(light.getX() / 65535.0f, light.getY() / 65535.0f, &r, &g, &b);
|
||||
WSContentSend_P(PSTR(" <i class=\"bx\" style=\"--cl:#%02X%02X%02X\"></i> #%02X%02X%02X"), r,g,b,r,g,b);
|
||||
}
|
||||
}
|
||||
if (&plug != nullptr) {
|
||||
WSContentSend_P(PSTR(" ⚡ "));
|
||||
if (plug.validMainsVoltage()) {
|
||||
WSContentSend_P(PSTR(" %dV"), plug.getMainsVoltage());
|
||||
}
|
||||
if (plug.validMainsPower()) {
|
||||
WSContentSend_P(PSTR(" %dW"), plug.getMainsPower());
|
||||
}
|
||||
}
|
||||
WSContentSend_P(PSTR("{e}"));
|
||||
|
|
Loading…
Reference in New Issue