mirror of https://github.com/arendst/Tasmota.git
Zigbee support for decimal Voltage/Current/Power on power metering plugs
This commit is contained in:
parent
5eb43d21b3
commit
ead891ef0e
|
@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Berry automated solidification of code
|
- Berry automated solidification of code
|
||||||
- Support of optional file calib.dat on ADE7953 based energy monitors like Shelly EM (#16486)
|
- Support of optional file calib.dat on ADE7953 based energy monitors like Shelly EM (#16486)
|
||||||
- Command ``SetOption46 0..255`` to add 0..255 * 10 milliseconds power on delay before initializing I/O (#15438)
|
- Command ``SetOption46 0..255`` to add 0..255 * 10 milliseconds power on delay before initializing I/O (#15438)
|
||||||
|
- Zigbee support for decimal Voltage/Current/Power on power metering plugs
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- ESP32 Increase number of button GPIOs from 8 to 28 (#16518)
|
- ESP32 Increase number of button GPIOs from 8 to 28 (#16518)
|
||||||
|
|
|
@ -24,6 +24,91 @@
|
||||||
#endif
|
#endif
|
||||||
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
|
const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; // wait for x seconds
|
||||||
|
|
||||||
|
// Convert a multiplier or divisor initially on 2 bytes, to a single byte
|
||||||
|
// We use a property that values are usually powers of 10 or 2/5/25/50...
|
||||||
|
// Values can range from 0 to 31e7
|
||||||
|
typedef struct uint8log_t {
|
||||||
|
uint8_t mantissa : 6; // 0..63
|
||||||
|
uint8_t exponent10 : 2; // 0..3
|
||||||
|
} uint8log_t;
|
||||||
|
|
||||||
|
// convert uint8log to uint, lossless conversion, but can create an overflow with high exponent
|
||||||
|
uint16_t uint8log_to_uint16(uint8_t v8) {
|
||||||
|
uint8log_t * v_log = (uint8log_t*) &v8;
|
||||||
|
uint32_t val = v_log->mantissa;
|
||||||
|
for (uint32_t i = 0; i < v_log->exponent10; i++) {
|
||||||
|
val = val * 10;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert uint16_t to uint8log, ther is potential rounding happening above 63, except when a multiple of 10
|
||||||
|
uint8_t uint16_to_uint8log(uint16_t val) {
|
||||||
|
uint8log_t v_log;
|
||||||
|
uint32_t mantissa = val; // mantissa must be 0..63
|
||||||
|
uint32_t expo10 = 0; // exponent in base 10
|
||||||
|
|
||||||
|
while (mantissa > 63) {
|
||||||
|
expo10++;
|
||||||
|
mantissa = mantissa / 10;
|
||||||
|
}
|
||||||
|
// test overflow
|
||||||
|
if (expo10 > 3) {
|
||||||
|
expo10 = 3;
|
||||||
|
mantissa = 63; // max value is 63000
|
||||||
|
}
|
||||||
|
v_log.mantissa = mantissa;
|
||||||
|
v_log.exponent10 = expo10;
|
||||||
|
uint8_t * v8 = (uint8_t*) &v_log;
|
||||||
|
return *v8;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t uint8log_test_vectors[] = {
|
||||||
|
0,1,2,5,10,20,33,50,66,75,100,150,200,300,500,1000,2500,3000,5000,10000,20000,25000,30000,50000,60000,65535
|
||||||
|
};
|
||||||
|
|
||||||
|
// uint8log unit tests (normally not called)
|
||||||
|
void uint8log_tests(void) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, "ZIG: sizeof(uint8log_t)=%i", sizeof(uint8log_t));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < sizeof(uint8log_test_vectors)/2; i++) {
|
||||||
|
uint16_t v16 = uint8log_test_vectors[i];
|
||||||
|
uint8_t v = uint16_to_uint8log(v16);
|
||||||
|
uint16_t v16_out = uint8log_to_uint16(v);
|
||||||
|
AddLog(LOG_LEVEL_INFO, ">>>: v16=%5i out=%5i %s hex=0x%02X", v16, v16_out, v16 != v16_out ? "<>" : "", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Output:
|
||||||
|
00:00:00.128 ZIG: sizeof(uint8log_t)=1
|
||||||
|
00:00:00.129 >>>: v16= 0 out= 0 hex=0x00
|
||||||
|
00:00:00.129 >>>: v16= 1 out= 1 hex=0x01
|
||||||
|
00:00:00.130 >>>: v16= 2 out= 2 hex=0x02
|
||||||
|
00:00:00.140 >>>: v16= 5 out= 5 hex=0x05
|
||||||
|
00:00:00.140 >>>: v16= 10 out= 10 hex=0x0A
|
||||||
|
00:00:00.151 >>>: v16= 20 out= 20 hex=0x14
|
||||||
|
00:00:00.151 >>>: v16= 33 out= 33 hex=0x21
|
||||||
|
00:00:00.152 >>>: v16= 50 out= 50 hex=0x32
|
||||||
|
00:00:00.162 >>>: v16= 66 out= 60 <> hex=0x46
|
||||||
|
00:00:00.163 >>>: v16= 75 out= 70 <> hex=0x47
|
||||||
|
00:00:00.173 >>>: v16= 100 out= 100 hex=0x4A
|
||||||
|
00:00:00.174 >>>: v16= 150 out= 150 hex=0x4F
|
||||||
|
00:00:00.174 >>>: v16= 200 out= 200 hex=0x54
|
||||||
|
00:00:00.185 >>>: v16= 300 out= 300 hex=0x5E
|
||||||
|
00:00:00.185 >>>: v16= 500 out= 500 hex=0x72
|
||||||
|
00:00:00.185 >>>: v16= 1000 out= 1000 hex=0x8A
|
||||||
|
00:00:00.196 >>>: v16= 2500 out= 2500 hex=0x99
|
||||||
|
00:00:00.196 >>>: v16= 3000 out= 3000 hex=0x9E
|
||||||
|
00:00:00.207 >>>: v16= 5000 out= 5000 hex=0xB2
|
||||||
|
00:00:00.207 >>>: v16=10000 out=10000 hex=0xCA
|
||||||
|
00:00:00.208 >>>: v16=20000 out=20000 hex=0xD4
|
||||||
|
00:00:00.219 >>>: v16=25000 out=25000 hex=0xD9
|
||||||
|
00:00:00.219 >>>: v16=30000 out=30000 hex=0xDE
|
||||||
|
00:00:00.219 >>>: v16=50000 out=50000 hex=0xF2
|
||||||
|
00:00:00.230 >>>: v16=60000 out=60000 hex=0xFC
|
||||||
|
00:00:00.231 >>>: v16=65535 out=63000 <> hex=0xFF
|
||||||
|
*/
|
||||||
|
|
||||||
enum class Z_Data_Type : uint8_t {
|
enum class Z_Data_Type : uint8_t {
|
||||||
Z_Unknown = 0x00,
|
Z_Unknown = 0x00,
|
||||||
Z_Light = 1, // Lights 1-5 channels
|
Z_Light = 1, // Lights 1-5 channels
|
||||||
|
@ -180,22 +265,50 @@ public:
|
||||||
Z_Data_Plug(uint8_t endpoint = 0) :
|
Z_Data_Plug(uint8_t endpoint = 0) :
|
||||||
Z_Data(Z_Data_Type::Z_Plug, endpoint),
|
Z_Data(Z_Data_Type::Z_Plug, endpoint),
|
||||||
mains_voltage(0xFFFF),
|
mains_voltage(0xFFFF),
|
||||||
mains_power(-0x8000)
|
mains_power(-0x8000),
|
||||||
|
ac_voltage_div(1),
|
||||||
|
ac_voltage_mul(1),
|
||||||
|
ac_current_div(1),
|
||||||
|
ac_current_mul(1),
|
||||||
|
ac_power_div(1),
|
||||||
|
ac_power_mul(1)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
inline bool validMainsVoltage(void) const { return 0xFFFF != mains_voltage; }
|
inline bool validMainsVoltage(void) const { return 0xFFFF != mains_voltage; }
|
||||||
inline bool validMainsPower(void) const { return -0x8000 != mains_power; }
|
inline bool validMainsPower(void) const { return -0x8000 != mains_power; }
|
||||||
|
|
||||||
inline uint16_t getMainsVoltage(void) const { return mains_voltage; }
|
inline uint16_t getMainsVoltageRaw(void) const { return mains_voltage; }
|
||||||
inline int16_t getMainsPower(void) const { return mains_power; }
|
inline int16_t getMainsPowerRaw(void) const { return mains_power; }
|
||||||
|
inline float getMainsVoltage(void) const { return (float)mains_voltage * getACVoltageMul() / getACVoltageDiv(); }
|
||||||
|
inline float getMainsPower(void) const { return (float)mains_power * getACPowerMul() / getACPowerDiv(); }
|
||||||
|
|
||||||
inline void setMainsVoltage(uint16_t _mains_voltage) { mains_voltage = _mains_voltage; }
|
inline void setMainsVoltageRaw(uint16_t _mains_voltage) { mains_voltage = _mains_voltage; }
|
||||||
inline void setMainsPower(int16_t _mains_power) { mains_power = _mains_power; }
|
inline void setMainsPowerRaw(int16_t _mains_power) { mains_power = _mains_power; }
|
||||||
|
|
||||||
|
inline uint16_t getACVoltageDiv(void) const { return uint8log_to_uint16(ac_voltage_div); }
|
||||||
|
inline uint16_t getACVoltageMul(void) const { return uint8log_to_uint16(ac_voltage_mul); }
|
||||||
|
inline uint16_t getACCurrentDiv(void) const { return uint8log_to_uint16(ac_current_div); }
|
||||||
|
inline uint16_t getACCurrentMul(void) const { return uint8log_to_uint16(ac_current_mul); }
|
||||||
|
inline uint16_t getACPowerDiv(void) const { return uint8log_to_uint16(ac_power_div); }
|
||||||
|
inline uint16_t getACPowerMul(void) const { return uint8log_to_uint16(ac_power_mul); }
|
||||||
|
|
||||||
|
inline void setACVoltageDiv(uint16_t v) { ac_voltage_div = uint16_to_uint8log(v); }
|
||||||
|
inline void setACVoltageMul(uint16_t v) { ac_voltage_mul = uint16_to_uint8log(v); }
|
||||||
|
inline void setACCurrentDiv(uint16_t v) { ac_current_div = uint16_to_uint8log(v); }
|
||||||
|
inline void setACCurrentMul(uint16_t v) { ac_current_mul = uint16_to_uint8log(v); }
|
||||||
|
inline void setACPowerDiv(uint16_t v) { ac_power_div = uint16_to_uint8log(v); }
|
||||||
|
inline void setACPowerMul(uint16_t v) { ac_power_mul = uint16_to_uint8log(v); }
|
||||||
|
|
||||||
static const Z_Data_Type type = Z_Data_Type::Z_Plug;
|
static const Z_Data_Type type = Z_Data_Type::Z_Plug;
|
||||||
// 4 bytes
|
// 4 bytes
|
||||||
uint16_t mains_voltage; // AC voltage
|
uint16_t mains_voltage; // AC voltage
|
||||||
int16_t mains_power; // Active power
|
int16_t mains_power; // Active power
|
||||||
|
uint8_t ac_voltage_div;
|
||||||
|
uint8_t ac_voltage_mul;
|
||||||
|
uint8_t ac_current_div;
|
||||||
|
uint8_t ac_current_mul;
|
||||||
|
uint8_t ac_power_div;
|
||||||
|
uint8_t ac_power_mul;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
|
|
|
@ -703,7 +703,7 @@ void ZCLFrame::removeInvalidAttributes(Z_attribute_list& attr_list) {
|
||||||
// Note: both function are now split to compute on extracted attributes
|
// Note: both function are now split to compute on extracted attributes
|
||||||
//
|
//
|
||||||
void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
|
void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
|
||||||
const Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
|
Z_Device & device = zigbee_devices.findShortAddr(shortaddr);
|
||||||
|
|
||||||
String modelId((char*) device.modelId);
|
String modelId((char*) device.modelId);
|
||||||
// scan through attributes and apply specific converters
|
// scan through attributes and apply specific converters
|
||||||
|
@ -812,10 +812,30 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x05000002: // ZoneStatus
|
case 0x05000002: // ZoneStatus
|
||||||
|
{
|
||||||
const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(shortaddr).data.find(Z_Data_Type::Z_Alarm, srcendpoint);
|
const Z_Data_Alarm & alarm = (const Z_Data_Alarm&) zigbee_devices.getShortAddr(shortaddr).data.find(Z_Data_Type::Z_Alarm, srcendpoint);
|
||||||
if (&alarm != nullptr) {
|
if (&alarm != nullptr) {
|
||||||
alarm.convertZoneStatus(attr_list, attr.getUInt());
|
alarm.convertZoneStatus(attr_list, attr.getUInt());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// convert AC multipliers/dividers
|
||||||
|
case 0x0B040600 ... 0x0B040605: // cluser 0x0B04 - attr 0x0600..0x0605
|
||||||
|
{
|
||||||
|
uint16_t val = attr.getUInt();
|
||||||
|
Z_Data_Plug & plug = device.data.get<Z_Data_Plug>();
|
||||||
|
if (&plug != &z_data_unk) {
|
||||||
|
switch (ccccaaaa) {
|
||||||
|
case 0x0B040600: plug.setACVoltageMul(val); break;
|
||||||
|
case 0x0B040601: plug.setACVoltageDiv(val); break;
|
||||||
|
case 0x0B040602: plug.setACCurrentMul(val); break;
|
||||||
|
case 0x0B040603: plug.setACCurrentDiv(val); break;
|
||||||
|
case 0x0B040604: plug.setACPowerMul(val); break;
|
||||||
|
case 0x0B040605: plug.setACPowerDiv(val); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// AddLog(LOG_LEVEL_INFO, ">>>: cluster=0x%04X attr=0x%04X v=%i", attr.cluster, attr.attr_id, attr.getUInt());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1452,14 +1472,14 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib
|
||||||
float fval = attr.getFloat();
|
float fval = attr.getFloat();
|
||||||
if (found && (matched_attr.map_type != Z_Data_Type::Z_Unknown)) {
|
if (found && (matched_attr.map_type != Z_Data_Type::Z_Unknown)) {
|
||||||
// We apply an automatic mapping to Z_Data_XXX object
|
// We apply an automatic mapping to Z_Data_XXX object
|
||||||
// First we find or instantiate the correct Z_Data_XXX accorfing to the endpoint
|
// First we find or instantiate the correct Z_Data_XXX according to the endpoint
|
||||||
// Then store the attribute at the attribute addres (via offset) and according to size 8/16/32 bits
|
// Then store the attribute at the attribute addres (via offset) and according to size 8/16/32 bits
|
||||||
|
|
||||||
// add the endpoint if it was not already known
|
// add the endpoint if it was not already known
|
||||||
device.addEndpoint(src_ep);
|
device.addEndpoint(src_ep);
|
||||||
// we don't apply the multiplier, but instead store in Z_Data_XXX object
|
// we don't apply the multiplier, but instead store in Z_Data_XXX object
|
||||||
Z_Data & data = device.data.getByType(matched_attr.map_type, src_ep);
|
Z_Data & data = device.data.getByType(matched_attr.map_type, src_ep);
|
||||||
uint8_t *attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + matched_attr.map_offset;
|
uint8_t * attr_address = ((uint8_t*)&data) + sizeof(Z_Data) + matched_attr.map_offset;
|
||||||
uint32_t uval32 = attr.getUInt(); // call converter to uint only once
|
uint32_t uval32 = attr.getUInt(); // call converter to uint only once
|
||||||
int32_t ival32 = attr.getInt(); // call converter to int only once
|
int32_t ival32 = attr.getInt(); // call converter to int only once
|
||||||
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Mapping type=%d offset=%d zigbee_type=%02X value=%d\n"), (uint8_t) matched_attr.matched_attr, matched_attr.map_offset, matched_attr.zigbee_type, ival32);
|
// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Mapping type=%d offset=%d zigbee_type=%02X value=%d\n"), (uint8_t) matched_attr.matched_attr, matched_attr.map_offset, matched_attr.zigbee_type, ival32);
|
||||||
|
@ -1512,6 +1532,22 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib
|
||||||
break;
|
break;
|
||||||
case 0x00060000:
|
case 0x00060000:
|
||||||
case 0x00068000: device.setPower(attr.getBool(), src_ep); break;
|
case 0x00068000: device.setPower(attr.getBool(), src_ep); break;
|
||||||
|
// apply multiplier/divisor to AC values
|
||||||
|
case 0x0B040505: // RMSVoltage
|
||||||
|
case 0x0B040508: // RMSCurrent
|
||||||
|
case 0x0B04050B: // ActivePower
|
||||||
|
{
|
||||||
|
const Z_Data_Plug & plug = device.data.find<Z_Data_Plug>();
|
||||||
|
if (&plug != &z_data_unk) {
|
||||||
|
switch (ccccaaaa) {
|
||||||
|
case 0x0B040505: fval = fval * plug.getACVoltageMul() / plug.getACVoltageDiv(); break;
|
||||||
|
case 0x0B040508: fval = fval * plug.getACCurrentMul() / plug.getACCurrentDiv(); break;
|
||||||
|
case 0x0B04050B: fval = fval * plug.getACPowerMul() / plug.getACPowerDiv(); break;
|
||||||
|
}
|
||||||
|
attr.setFloat(fval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// now apply the multiplier to make it human readable
|
// now apply the multiplier to make it human readable
|
||||||
|
@ -1681,7 +1717,19 @@ void Z_Data::toAttributes(Z_attribute_list & attr_list) const {
|
||||||
fval = fval / divider;
|
fval = fval / divider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// special case for plugs, with parametric multiplier/divisor
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Winvalid-offsetof" // avoid warnings since we're using offsetof() in a risky way
|
||||||
|
const Z_Data_Plug * plug = (Z_Data_Plug*) this;
|
||||||
|
if (map_type == Z_Data_Type::Z_Plug) {
|
||||||
|
if (map_offset == Z_OFFSET(Z_Data_Plug, mains_voltage)) {
|
||||||
|
fval = fval * plug->getACVoltageMul() / plug->getACVoltageDiv();
|
||||||
|
} else if (map_offset == Z_OFFSET(Z_Data_Plug, mains_power)) {
|
||||||
|
fval = fval * plug->getACPowerMul() / plug->getACPowerDiv();
|
||||||
|
}
|
||||||
|
}
|
||||||
attr.setFloat(fval);
|
attr.setFloat(fval);
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -666,7 +666,7 @@ int32_t Z_ReceiveActiveEp(int32_t res, const SBuffer &buf) {
|
||||||
const uint8_t Z_bindings[] PROGMEM = {
|
const uint8_t Z_bindings[] PROGMEM = {
|
||||||
Cx0001, Cx0006, Cx0008, Cx0102, Cx0201, Cx0300,
|
Cx0001, Cx0006, Cx0008, Cx0102, Cx0201, Cx0300,
|
||||||
Cx0400, Cx0402, Cx0403, Cx0405, Cx0406,
|
Cx0400, Cx0402, Cx0403, Cx0405, Cx0406,
|
||||||
Cx0500,
|
Cx0500, Cx0B04,
|
||||||
};
|
};
|
||||||
|
|
||||||
int32_t Z_ClusterToCxBinding(uint16_t cluster) {
|
int32_t Z_ClusterToCxBinding(uint16_t cluster) {
|
||||||
|
@ -714,6 +714,11 @@ void Z_AutoBindDefer(uint16_t shortaddr, uint8_t endpoint, const SBuffer &buf,
|
||||||
zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, 0x0500, endpoint, Z_CAT_CIE_ENROLL, 1 /* zone */, &Z_SendCIEZoneEnrollResponse);
|
zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, 0x0500, endpoint, Z_CAT_CIE_ENROLL, 1 /* zone */, &Z_SendCIEZoneEnrollResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if Plug, request the multipliers and divisors for Voltage, Current and Power
|
||||||
|
if (bitRead(cluster_in_map, Z_ClusterToCxBinding(0x0B04))) {
|
||||||
|
zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, 0x0B04, endpoint, Z_CAT_READ_ATTRIBUTE, 0 /* ignore */, &Z_SendSinglePlugMulDivAttributesRead);
|
||||||
|
}
|
||||||
|
|
||||||
// enqueue bind requests
|
// enqueue bind requests
|
||||||
for (uint32_t i=0; i<nitems(Z_bindings); i++) {
|
for (uint32_t i=0; i<nitems(Z_bindings); i++) {
|
||||||
if (bitRead(cluster_map, i)) {
|
if (bitRead(cluster_map, i)) {
|
||||||
|
@ -1418,6 +1423,27 @@ void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t
|
||||||
zigbeeZCLSendCmd(zcl);
|
zigbeeZCLSendCmd(zcl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Send single attribute read request in Timer
|
||||||
|
//
|
||||||
|
void Z_SendSinglePlugMulDivAttributesRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
|
||||||
|
ZCLFrame zcl(12); // message is 12 bytes
|
||||||
|
zcl.shortaddr = shortaddr;
|
||||||
|
zcl.cluster = 0x0B04;
|
||||||
|
zcl.dstendpoint = endpoint;
|
||||||
|
zcl.cmd = ZCL_READ_ATTRIBUTES;
|
||||||
|
zcl.clusterSpecific = false;
|
||||||
|
zcl.needResponse = true;
|
||||||
|
zcl.direct = false; // discover route
|
||||||
|
zcl.payload.add16(0x0600);
|
||||||
|
zcl.payload.add16(0x0601);
|
||||||
|
zcl.payload.add16(0x0602);
|
||||||
|
zcl.payload.add16(0x0603);
|
||||||
|
zcl.payload.add16(0x0604);
|
||||||
|
zcl.payload.add16(0x0605);
|
||||||
|
zigbeeZCLSendCmd(zcl);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Write CIE address
|
// Write CIE address
|
||||||
//
|
//
|
||||||
|
|
|
@ -2260,10 +2260,12 @@ void ZigbeeShow(bool json)
|
||||||
if (plug_voltage || plug_power) {
|
if (plug_voltage || plug_power) {
|
||||||
WSContentSend_P(PSTR(" ⚡ "));
|
WSContentSend_P(PSTR(" ⚡ "));
|
||||||
if (plug_voltage) {
|
if (plug_voltage) {
|
||||||
WSContentSend_P(PSTR(" %dV"), plug.getMainsVoltage());
|
float mains_voltage = plug.getMainsVoltage();
|
||||||
|
WSContentSend_P(PSTR(" %-1_fV"), &mains_voltage);
|
||||||
}
|
}
|
||||||
if (plug_power) {
|
if (plug_power) {
|
||||||
WSContentSend_P(PSTR(" %dW"), plug.getMainsPower());
|
float mains_power = plug.getMainsPower();
|
||||||
|
WSContentSend_P(PSTR(" %-1_fW"), &mains_power);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WSContentSend_P(PSTR("{e}"));
|
WSContentSend_P(PSTR("{e}"));
|
||||||
|
|
Loading…
Reference in New Issue