diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino index 0f2b271b6..4e13f6bf6 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_2_converters.ino @@ -278,10 +278,12 @@ uint8_t toPercentageCR2032(uint32_t voltage) { // Adds to buf: // - n bytes: value (typically between 1 and 4 bytes, or bigger for strings) // returns number of bytes of attribute, or <0 if error +// If the value is `NAN`, the value encoded is the "zigbee invalid value" int32_t encodeSingleAttribute(SBuffer &buf, double val_d, const char *val_str, uint8_t attrtype) { uint32_t len = Z_getDatatypeLen(attrtype); // pre-compute length, overloaded for variable length attributes - uint32_t u32 = val_d; - int32_t i32 = val_d; + bool nan = isnan(val_d); + uint32_t u32 = nan ? 0xFFFFFFFF : roundf(val_d); + int32_t i32 = roundf(val_d); float f32 = val_d; switch (attrtype) { @@ -315,13 +317,13 @@ int32_t encodeSingleAttribute(SBuffer &buf, double val_d, const char *val_str, u // signed 8 case Zint8: // int8 - buf.add8(i32); + buf.add8(nan ? 0x80 : i32); break; case Zint16: // int16 - buf.add16(i32); + buf.add16(nan ? 0x8000 : i32); break; case Zint32: // int32 - buf.add32(i32); + buf.add32(nan ? 0x80000000 : i32); break; case Zsingle: // float diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino index 16ca9f246..b9ea16e69 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino @@ -1594,7 +1594,7 @@ void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uin buf.add16(min_interval); buf.add16(max_interval); if (!Z_isDiscreteDataType(attr_matched.zigbee_type)) { // report_change is only valid for non-discrete data types (numbers) - ZbApplyMultiplier(report_change, attr_matched.multiplier, attr_matched.divider, attr_matched.base); + ZbApplyMultiplierForWrites(report_change, attr_matched.multiplier, attr_matched.divider, attr_matched.base); // encode value int32_t res = encodeSingleAttribute(buf, report_change, "", attr_matched.zigbee_type); if (res < 0) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino index 6af30f7ee..6e53e90ba 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino @@ -219,18 +219,20 @@ void zigbeeZCLSendCmd(class ZCLFrame &zcl) { // Definitive doc for Tuya protocol: // https://developer.tuya.com/en/docs/iot-device-dev/tuya-zigbee-universal-docking-access-standard?id=K9ik6zvofpzql#subtitle-6-Private%20cluster -// Special encoding for multiplier: + +// Special encoding for multiplier when sending writes or reportable attributes, +// I.e. multipliers and dividers are inversed // multiplier == 0: ignore // multiplier == 1: ignore -void ZbApplyMultiplier(double &val_d, int8_t multiplier, int8_t divider, int8_t base) { +void ZbApplyMultiplierForWrites(double &val_d, int8_t multiplier, int8_t divider, int8_t base) { + if (0 != base) { + val_d = val_d - base; + } if ((0 != multiplier) && (1 != multiplier)) { - val_d = val_d * multiplier; + val_d = val_d / multiplier; } if ((0 != divider) && (1 != divider)) { - val_d = val_d / divider; - } - if (0 != base) { - val_d = val_d + base; + val_d = val_d * divider; } } @@ -243,7 +245,7 @@ bool ZbTuyaWrite(SBuffer & buf, const Z_attribute & attr) { if (attr.key_is_str || attr.key_is_cmd) { return false; } // couldn't find attr if so skip if (attr.isNum()) { - ZbApplyMultiplier(val_d, attr.attr_multiplier, attr.attr_divider, 0); + ZbApplyMultiplierForWrites(val_d, attr.attr_multiplier, attr.attr_divider, 0); } uint32_t u32 = val_d; int32_t i32 = val_d; @@ -302,7 +304,7 @@ bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_stat if (attr.key_is_str && attr.key_is_cmd) { return false; } // couldn't find attr if so skip if (attr.isNum()) { - ZbApplyMultiplier(val_d, attr.attr_multiplier, attr.attr_divider, 0); + ZbApplyMultiplierForWrites(val_d, attr.attr_multiplier, attr.attr_divider, 0); } // push the value in the buffer @@ -419,9 +421,14 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLFrame & zcl) // read ReportableChange JsonParserToken val_attr_rc = attr_config[PSTR("ReportableChange")]; if (val_attr_rc) { - val_d = val_attr_rc.getFloat(); + // If value is `null` then we send 0xFFFF for invalid value val_str = val_attr_rc.getStr(); - ZbApplyMultiplier(val_d, attr.attr_multiplier, attr.attr_divider, 0); + if (!val_attr_rc.isNull()) { + val_d = val_attr_rc.getFloat(); + ZbApplyMultiplierForWrites(val_d, attr.attr_multiplier, attr.attr_divider, 0); + } else { + val_d = NAN; + } } // read TimeoutPeriod