diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino index 2832e98e1..34dfd2e57 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino @@ -110,7 +110,8 @@ public: // The high 8 bits are `0` command sent to device or `1` command received from device uint8_t key_suffix; // append a suffix to key (default is 1, explicitly output if >1) uint8_t attr_type; // [opt] type of the attribute, default to Zunk (0xFF) - uint8_t attr_multiplier; // [opt] multiplier for attribute, defaults to 0x01 (no change) + int8_t attr_multiplier; // [opt] multiplier for attribute, defaults to 0x01 (no change) + int8_t attr_divider; // [opt] divider uint16_t manuf; // manufacturer id (0 if none) // Constructor with all defaults @@ -127,6 +128,7 @@ public: key_suffix(1), attr_type(0xFF), attr_multiplier(1), + attr_divider(1), manuf(0) {}; @@ -784,6 +786,7 @@ void Z_attribute::deepCopy(const Z_attribute & rhs) { key_suffix = rhs.key_suffix; attr_type = rhs.attr_type; attr_multiplier = rhs.attr_multiplier; + attr_divider = rhs.attr_divider; // copy value copyVal(rhs); // don't touch next pointer diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_1_attributes.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_1_attributes.ino index a0a9bb6a8..bae1ad62e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_1_attributes.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_1_attributes.ino @@ -273,7 +273,9 @@ class Z_plugin_attribute { public: Z_plugin_attribute(void) : - type(Zunk), multiplier(1), cluster(0xFFFF), attribute(0xFFFF), manuf(0) + type(Zunk), + multiplier(1), divider(1), base(0), + cluster(0xFFFF), attribute(0xFFFF), manuf(0) {}; void set(uint16_t cluster, uint16_t attribute, const char *name, uint8_t type = Zunk) { @@ -284,7 +286,9 @@ public: } uint8_t type; // zigbee type, Zunk by default - int8_t multiplier; // multiplier, values 0, 1, 2, 5, 10, 100, -2, -5, -10, -100, + int8_t multiplier; // multiply by x (ignore if 0 or 1) + int8_t divider; // divide by x (ignore if 0 or 1) + int16_t base; // add x (ignore if 0) uint16_t cluster; // cluster number uint16_t attribute; // attribute number uint16_t manuf; // manufacturer code, 0 if none @@ -298,15 +302,18 @@ class Z_attribute_synonym { public: Z_attribute_synonym(void) : cluster(0xFFFF), attribute(0xFFFF), new_cluster(0xFFFF), new_attribute(0xFFFF), - multiplier(1) + multiplier(1), divider(1), base(0) {}; - void set(uint16_t cluster, uint16_t attribute, uint16_t new_cluster, uint16_t new_attribute, int8_t multiplier = 1) { + void set(uint16_t cluster, uint16_t attribute, uint16_t new_cluster, uint16_t new_attribute, + int8_t multiplier = 1, int8_t divider = 1, int16_t base = 0) { this->cluster = cluster; this->attribute = attribute; this->new_cluster = new_cluster; this->new_attribute = new_attribute; this->multiplier = multiplier; + this->divider = divider; + this->base = base; } inline bool found(void) const { return cluster != 0xFFFF && attribute != 0xFFFF; } @@ -315,7 +322,9 @@ public: uint16_t attribute; // attribute to match uint16_t new_cluster; // replace with this cluster uint16_t new_attribute; // replace with this attribute - int8_t multiplier; // mutliplier if different than 1 (otherwise don't change value) + int8_t multiplier; // multiply by x (ignore if 0 or 1) + int8_t divider; // divide by x (ignore if 0 or 1) + int16_t base; // add x (ignore if 0) }; // @@ -457,6 +466,8 @@ public: const char * name = nullptr; uint8_t zigbee_type = Znodata; int8_t multiplier = 1; + int8_t divider = 1; + int8_t base = 0; uint8_t map_offset = 0; Z_Data_Type map_type = Z_Data_Type::Z_Unknown; uint16_t manuf = 0x0000; // manuf code (if any) 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 76462970d..065c847fd 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 @@ -46,7 +46,9 @@ class Z_attribute_match Z_findAttributeMatcherByName(uint16_t shortaddr, const c matched_attr.cluster = CxToCluster(pgm_read_byte(&converter->cluster_short)); matched_attr.attribute = pgm_read_word(&converter->attribute); matched_attr.name = (Z_strings + pgm_read_word(&converter->name_offset)); - matched_attr.multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + int8_t multiplier8 = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + if (multiplier8 > 1) { matched_attr.multiplier = multiplier8; } + if (multiplier8 < 0) { matched_attr.divider = -multiplier8; } matched_attr.zigbee_type = pgm_read_byte(&converter->type); uint8_t conv_mapping = pgm_read_byte(&converter->mapping); matched_attr.map_type = (Z_Data_Type) ((conv_mapping & 0xF0)>>4); @@ -77,7 +79,9 @@ class Z_attribute_match Z_findAttributeMatcherById(uint16_t shortaddr, uint16_t matched_attr.cluster = cluster; matched_attr.attribute = attr_id; matched_attr.name = (Z_strings + pgm_read_word(&converter->name_offset)); - matched_attr.multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + int8_t multiplier8 = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + if (multiplier8 > 1) { matched_attr.multiplier = multiplier8; } + if (multiplier8 < 0) { matched_attr.divider = -multiplier8; } matched_attr.zigbee_type = pgm_read_byte(&converter->type); uint8_t conv_mapping = pgm_read_byte(&converter->mapping); matched_attr.map_type = (Z_Data_Type) ((conv_mapping & 0xF0)>>4); @@ -697,11 +701,18 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) { attr.cluster, attr.attr_id); if (syn.found()) { attr.setKeyId(syn.new_cluster, syn.new_attribute); - if (syn.multiplier != 1 && syn.multiplier != 0) { + if ((syn.multiplier != 1 && syn.multiplier != 0) || (syn.divider != 1 && syn.divider != 0) || (syn.base != 0)) { // we need to change the value float fval = attr.getFloat(); - if (syn.multiplier > 0) { fval = fval * syn.multiplier; } - else { fval = fval / (-syn.multiplier); } + if (syn.multiplier != 1 && syn.multiplier != 0) { + fval = fval * syn.multiplier; + } + if (syn.divider != 1 && syn.divider != 0) { + fval = fval / syn.divider; + } + if (syn.base != 0) { + fval = fval + syn.base; + } attr.setFloat(fval); } } @@ -945,10 +956,14 @@ void ZCLFrame::parseReadConfigAttributes(uint16_t shortaddr, Z_attribute_list& a // find the multiplier int8_t multiplier = 1; + int8_t divider = 1; + int16_t base = 0; Z_attribute_match matched_attr = Z_findAttributeMatcherById(shortaddr, cluster, attrid, false); if (matched_attr.found()) { attr_2.addAttribute(matched_attr.name, true).setBool(true); multiplier = matched_attr.multiplier; + divider = matched_attr.divider; + base = matched_attr.base; } i += 4; if (0 != status) { @@ -974,10 +989,18 @@ void ZCLFrame::parseReadConfigAttributes(uint16_t shortaddr, Z_attribute_list& a // decode Reportable Change Z_attribute &attr_change = attr_2.addAttributePMEM(PSTR("ReportableChange")); i += parseSingleAttribute(attr_change, payload, i, attr_type); - if ((1 != multiplier) && (0 != multiplier)) { + + if ((multiplier != 1 && multiplier != 0) || (divider != 1 && divider != 0) || (base != 0)) { float fval = attr_change.getFloat(); - if (multiplier > 0) { fval = fval * multiplier; } - else { fval = fval / (-multiplier); } + if (multiplier != 1 && multiplier != 0) { + fval = fval * multiplier; + } + if (divider != 1 && divider != 0) { + fval = fval / divider; + } + if (base != 0) { + fval = fval + base; + } attr_change.setFloat(fval); } } @@ -1480,9 +1503,16 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib // now apply the multiplier to make it human readable if (found) { if (0 == matched_attr.multiplier) { attr_list.removeAttribute(&attr); continue; } // remove attribute if multiplier is zero - if (1 != matched_attr.multiplier) { - if (matched_attr.multiplier > 0) { fval = fval * matched_attr.multiplier; } - else { fval = fval / (-matched_attr.multiplier); } + if ((matched_attr.multiplier != 1 && matched_attr.multiplier != 0) || (matched_attr.divider != 1 && matched_attr.divider != 0) || (matched_attr.base != 0)) { + if (matched_attr.multiplier != 1 && matched_attr.multiplier != 0) { + fval = fval * matched_attr.multiplier; + } + if (matched_attr.divider != 1 && matched_attr.divider != 0) { + fval = fval / matched_attr.divider; + } + if (matched_attr.base != 0) { + fval = fval + matched_attr.base; + } attr.setFloat(fval); } } @@ -1509,6 +1539,7 @@ void Z_parseAttributeKey_inner(uint16_t shortaddr, class Z_attribute & attr, uin attr.setKeyId(matched_attr.cluster, matched_attr.attribute); attr.attr_type = matched_attr.zigbee_type; attr.attr_multiplier = matched_attr.multiplier; + attr.attr_divider = matched_attr.divider; attr.manuf = matched_attr.manuf; } } @@ -1592,7 +1623,11 @@ void Z_Data::toAttributes(Z_attribute_list & attr_list) const { const Z_AttributeConverter *converter = &Z_PostProcess[i]; uint8_t conv_export = pgm_read_byte(&converter->multiplier_idx) & Z_EXPORT_DATA; uint8_t conv_mapping = pgm_read_byte(&converter->mapping); - int8_t multiplier = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + int8_t multiplier = 1; + int8_t divider = 1; + int8_t multiplier8 = CmToMultiplier(pgm_read_byte(&converter->multiplier_idx)); + if (multiplier8 > 1) { multiplier = multiplier8; } + if (multiplier8 < 0) { divider = -multiplier8; } Z_Data_Type map_type = (Z_Data_Type) ((conv_mapping & 0xF0)>>4); uint8_t map_offset = (conv_mapping & 0x0F); @@ -1624,9 +1659,13 @@ void Z_Data::toAttributes(Z_attribute_list & attr_list) const { float fval; if (data_size > 0) { fval = uval32; } else { fval = ival32; } - if ((1 != multiplier) && (0 != multiplier)) { - if (multiplier > 0) { fval = fval * multiplier; } - else { fval = fval / (-multiplier); } + if ((multiplier != 1 && multiplier != 0) || (divider != 1 && divider != 0)) { + if (multiplier != 1 && multiplier != 0) { + fval = fval * multiplier; + } + if (divider != 1 && divider != 0) { + fval = fval / divider; + } } attr.setFloat(fval); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_plugin.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_plugin.ino index 094ec3248..13a9f99ef 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_plugin.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_6_plugin.ino @@ -22,6 +22,7 @@ const char Z_MUL[] PROGMEM = "mul:"; const char Z_DIV[] PROGMEM = "div:"; const char Z_MANUF[] PROGMEM = "manuf:"; +const char Z_OFFSET[] PROGMEM = "offset:"; char * Z_subtoken(char * token, const char * prefix) { size_t prefix_len = strlen_P(prefix); @@ -60,6 +61,8 @@ Z_attribute_match Z_plugin_matchAttributeById(const char *model, const char *man attr.name = attr_tmpl->name.c_str(); attr.zigbee_type = attr_tmpl->type; attr.multiplier = attr_tmpl->multiplier; + attr.divider = attr_tmpl->divider; + attr.base = attr_tmpl->base; attr.manuf = attr_tmpl->manuf; } return attr; @@ -80,6 +83,8 @@ Z_attribute_match Z_plugin_matchAttributeByName(const char *model, const char *m attr.name = attr_tmpl->name.c_str(); attr.zigbee_type = attr_tmpl->type; attr.multiplier = attr_tmpl->multiplier; + attr.divider = attr_tmpl->divider; + attr.base = attr_tmpl->base; attr.manuf = attr_tmpl->manuf; } } @@ -231,6 +236,8 @@ bool ZbLoad(const char *filename_raw) { uint16_t cluster_id = 0xFFFF; uint8_t type_id = Zunk; int8_t multiplier = 1; + int8_t divider = 1; + int16_t base = 0; char * name = nullptr; uint16_t manuf = 0; @@ -254,11 +261,15 @@ bool ZbLoad(const char *filename_raw) { char * sub_token; // look for multiplier if (sub_token = Z_subtoken(token, Z_MUL)) { - multiplier = strtoul(sub_token, nullptr, 10); + multiplier = strtol(sub_token, nullptr, 10); } // look for divider else if (sub_token = Z_subtoken(token, Z_DIV)) { - multiplier = - strtoul(sub_token, nullptr, 10); // negative to indicate divider + divider = strtol(sub_token, nullptr, 10); // negative to indicate divider + } + // look for offset (base) + else if (sub_token = Z_subtoken(token, Z_OFFSET)) { + base = strtol(sub_token, nullptr, 10); // negative to indicate divider } // look for `manuf:HHHH` else if (sub_token = Z_subtoken(token, Z_MANUF)) { @@ -276,6 +287,8 @@ bool ZbLoad(const char *filename_raw) { plugin_attr.type = type_id; plugin_attr.name = name; plugin_attr.multiplier = multiplier; + plugin_attr.divider = divider; + plugin_attr.base = base; plugin_attr.manuf = manuf; } else { // ATTRIBUTE SYNONYM @@ -290,17 +303,23 @@ bool ZbLoad(const char *filename_raw) { uint16_t new_cluster_id = strtoul(tok2, &delimiter_slash2, 16); uint16_t new_attr_id = strtoul(delimiter_slash2+1, nullptr, 16); int8_t multiplier = 1; + int8_t divider = 1; + int16_t base = 0; // ADDITIONAL ELEMENTS? while (token = strtok_r(rest, ",", &rest)) { char * sub_token; // look for multiplier if (sub_token = Z_subtoken(token, Z_MUL)) { - multiplier = strtoul(sub_token, nullptr, 10); + multiplier = strtol(sub_token, nullptr, 10); } // look for divider else if (sub_token = Z_subtoken(token, Z_DIV)) { - multiplier = - strtoul(sub_token, nullptr, 10); // negative to indicate divider + divider = strtol(sub_token, nullptr, 10); // negative to indicate divider + } + // look for offset (base) + else if (sub_token = Z_subtoken(token, Z_OFFSET)) { + base = strtol(sub_token, nullptr, 10); // negative to indicate divider } else { AddLog(LOG_LEVEL_DEBUG, "ZIG: ZbLoad unrecognized modifier '%s'", token); @@ -313,6 +332,8 @@ bool ZbLoad(const char *filename_raw) { syn.new_cluster = new_cluster_id; syn.new_attribute = new_attr_id; syn.multiplier = multiplier; + syn.divider = divider; + syn.base = base; } } } @@ -360,9 +381,15 @@ bool ZbUnload(const char *filename_raw) { } // append modifiers like mul/div/manuf -void Z_AppendModifiers(char * buf, size_t buf_len, int8_t multiplier, uint16_t manuf) { +void Z_AppendModifiers(char * buf, size_t buf_len, int8_t multiplier, int8_t divider, int16_t base, uint16_t manuf) { if (multiplier != 0 && multiplier != 1) { - ext_snprintf_P(buf, buf_len, "%s,%s%i", buf, multiplier > 0 ? Z_MUL : Z_DIV, multiplier > 0 ? multiplier : -multiplier); + ext_snprintf_P(buf, buf_len, "%s,%s%i", buf, Z_MUL, multiplier); + } + if (divider != 0 && divider != 1) { + ext_snprintf_P(buf, buf_len, "%s,%s%i", buf, Z_DIV, divider); + } + if (base != 0) { + ext_snprintf_P(buf, buf_len, "%s,%s%i", buf, Z_OFFSET, base); } if (manuf) { ext_snprintf_P(buf, buf_len, "%s,%s%04X", buf, Z_MANUF, manuf); @@ -403,12 +430,12 @@ void ZbLoadDump(void) { Z_getTypeByNumber(type_str, sizeof(type_str), attr.type); ext_snprintf_P(buf, sizeof(buf), "%s%%%s", buf, type_str); } - Z_AppendModifiers(buf, sizeof(buf), attr.multiplier, attr.manuf); + Z_AppendModifiers(buf, sizeof(buf), attr.multiplier, attr.divider, attr.base, attr.manuf); AddLog(LOG_LEVEL_INFO, PSTR("%s"), buf); } for (const Z_attribute_synonym & syn : tmpl.synonyms) { ext_snprintf_P(buf, sizeof(buf), "%04X/%04X=%04X/%04X", syn.cluster, syn.attribute, syn.new_cluster, syn.new_attribute); - Z_AppendModifiers(buf, sizeof(buf), syn.multiplier, 0); + Z_AppendModifiers(buf, sizeof(buf), syn.multiplier, syn.divider, syn.base, 0); AddLog(LOG_LEVEL_INFO, PSTR("%s"), buf); } } 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 43090cda9..4edf32a09 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino @@ -1568,7 +1568,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); + ZbApplyMultiplier(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 42d7ee74f..1318d11a3 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino @@ -222,15 +222,15 @@ void zigbeeZCLSendCmd(class ZCLFrame &zcl) { // Special encoding for multiplier: // multiplier == 0: ignore // multiplier == 1: ignore -// multiplier > 0: divide by the multiplier -// multiplier < 0: multiply by the -multiplier (positive) -void ZbApplyMultiplier(double &val_d, int8_t multiplier) { +void ZbApplyMultiplier(double &val_d, int8_t multiplier, int8_t divider, int8_t base) { if ((0 != multiplier) && (1 != multiplier)) { - if (multiplier > 0) { // inverse of decoding - val_d = val_d / multiplier; - } else { - 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; } } @@ -242,8 +242,8 @@ bool ZbTuyaWrite(SBuffer & buf, const Z_attribute & attr) { const char * val_str = attr.getStr(); if (attr.key_is_str || attr.key_is_cmd) { return false; } // couldn't find attr if so skip - if (attr.isNum() && (1 != attr.attr_multiplier)) { - ZbApplyMultiplier(val_d, attr.attr_multiplier); + if (attr.isNum()) { + ZbApplyMultiplier(val_d, attr.attr_multiplier, attr.attr_divider, 0); } uint32_t u32 = val_d; int32_t i32 = val_d; @@ -301,8 +301,8 @@ bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_stat const char * val_str = attr.getStr(); if (attr.key_is_str && attr.key_is_cmd) { return false; } // couldn't find attr if so skip - if (attr.isNum() && (1 != attr.attr_multiplier)) { - ZbApplyMultiplier(val_d, attr.attr_multiplier); + if (attr.isNum()) { + ZbApplyMultiplier(val_d, attr.attr_multiplier, attr.attr_divider, 0); } // push the value in the buffer @@ -421,7 +421,7 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLFrame & zcl) if (val_attr_rc) { val_d = val_attr_rc.getFloat(); val_str = val_attr_rc.getStr(); - ZbApplyMultiplier(val_d, attr.attr_multiplier); + ZbApplyMultiplier(val_d, attr.attr_multiplier, attr.attr_divider, 0); } // read TimeoutPeriod diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino index 03a288781..2642ac280 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino @@ -254,15 +254,16 @@ extern "C" { extern const be_ctypes_structure_t be_zigbee_zcl_attribute_struct = { sizeof(Z_attribute), /* size in bytes */ - 8, /* number of elements */ + 9, /* number of elements */ nullptr, - (const be_ctypes_structure_item_t[8]) { + (const be_ctypes_structure_item_t[9]) { { "_attr_id", offsetof(Z_attribute, attr_id), 0, 0, ctypes_u16, 0 }, { "_cluster", offsetof(Z_attribute, cluster), 0, 0, ctypes_u16, 0 }, { "_cmd", offsetof(Z_attribute, attr_id), 0, 0, ctypes_u8, 0 }, // low 8 bits of attr_id { "_direction", offsetof(Z_attribute, attr_id) + 1, 0, 0, ctypes_u8, 0 }, // high 8 bits of attr_id { "_iscmd", offsetof(Z_attribute, key_is_cmd), 0, 0, ctypes_u8, 0 }, { "attr_multiplier", offsetof(Z_attribute, attr_multiplier), 0, 0, ctypes_i8, 0 }, + { "attr_divider", offsetof(Z_attribute, attr_divider), 0, 0, ctypes_i8, 0 }, { "attr_type", offsetof(Z_attribute, attr_type), 0, 0, ctypes_u8, 0 }, // { "key", offsetof(Z_attribute, key), 0, 0, ctypes_ptr32, 0 }, // { "key_is_pmem", offsetof(Z_attribute, key_is_pmem), 0, 0, ctypes_u8, 0 },