mirror of https://github.com/arendst/Tasmota.git
Zigbee improve plugin format
This commit is contained in:
parent
156adad97c
commit
9d7cbcae62
|
@ -113,6 +113,7 @@ public:
|
|||
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)
|
||||
uint16_t manuf; // manufacturer id (0 if none)
|
||||
|
||||
// Constructor with all defaults
|
||||
Z_attribute():
|
||||
|
@ -124,7 +125,8 @@ public:
|
|||
val_str_raw(false),
|
||||
key_suffix(1),
|
||||
attr_type(0xFF),
|
||||
attr_multiplier(1)
|
||||
attr_multiplier(1),
|
||||
manuf(0)
|
||||
{};
|
||||
|
||||
Z_attribute(const Z_attribute & rhs) {
|
||||
|
|
|
@ -271,7 +271,7 @@ class Z_plugin_attribute {
|
|||
public:
|
||||
|
||||
Z_plugin_attribute(void) :
|
||||
type(Zunk), multiplier(1), cluster(0xFFFF), attribute(0xFFFF)
|
||||
type(Zunk), multiplier(1), cluster(0xFFFF), attribute(0xFFFF), manuf(0)
|
||||
{};
|
||||
|
||||
void set(uint16_t cluster, uint16_t attribute, const char *name, uint8_t type = Zunk) {
|
||||
|
@ -285,6 +285,7 @@ public:
|
|||
int8_t multiplier; // multiplier, values 0, 1, 2, 5, 10, 100, -2, -5, -10, -100,
|
||||
uint16_t cluster; // cluster number
|
||||
uint16_t attribute; // attribute number
|
||||
uint16_t manuf; // manufacturer code, 0 if none
|
||||
String name; // name of attribute once converted
|
||||
};
|
||||
|
||||
|
@ -456,6 +457,7 @@ public:
|
|||
int8_t multiplier = 1;
|
||||
uint8_t map_offset = 0;
|
||||
Z_Data_Type map_type = Z_Data_Type::Z_Unknown;
|
||||
uint16_t manuf = 0x0000; // manuf code (if any)
|
||||
};
|
||||
|
||||
Z_attribute_match Z_plugin_matchAttributeById(const char *model, const char *manufacturer, uint16_t cluster, uint16_t attribute);
|
||||
|
@ -472,8 +474,6 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
{ Zuint8, Cx0000, 0x0003, Z_(HWVersion), Cm1, 0 },
|
||||
{ Zstring, Cx0000, 0x0004, Z_(Manufacturer), Cm1, 0 }, // record Manufacturer
|
||||
{ Zstring, Cx0000, 0x0005, Z_(ModelId), Cm1, 0 }, // record Model
|
||||
// { Zstring, Cx0000, 0x0004, Z_(Manufacturer), Cm1, Z_ManufKeep, 0 }, // record Manufacturer
|
||||
// { Zstring, Cx0000, 0x0005, Z_(ModelId), Cm1, Z_ModelKeep, 0 }, // record Model
|
||||
{ Zstring, Cx0000, 0x0006, Z_(DateCode), Cm1, 0 },
|
||||
{ Zenum8, Cx0000, 0x0007, Z_(PowerSource), Cm1, 0 },
|
||||
{ Zenum8, Cx0000, 0x0008, Z_(GenericDeviceClass), Cm1, 0 },
|
||||
|
@ -524,8 +524,8 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
|||
|
||||
// Level Control cluster
|
||||
{ Zuint8, Cx0008, 0x0000, Z_(Dimmer), Cm1 + Z_EXPORT_DATA, Z_MAPPING(Z_Data_Light, dimmer) },
|
||||
{ Zmap8, Cx0008, 0x000F, Z_(DimmerOptions), Cm1, 0 },
|
||||
{ Zuint16, Cx0008, 0x0001, Z_(DimmerRemainingTime), Cm1, 0 },
|
||||
{ Zmap8, Cx0008, 0x000F, Z_(DimmerOptions), Cm1, 0 },
|
||||
{ Zuint16, Cx0008, 0x0010, Z_(OnOffTransitionTime), Cm1, 0 },
|
||||
// { Zuint8, Cx0008, 0x0011, (OnLevel), Cm1, 0 },
|
||||
// { Zuint16, Cx0008, 0x0012, (OnTransitionTime), Cm1, 0 },
|
||||
|
|
|
@ -1509,6 +1509,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.manuf = matched_attr.manuf;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1516,6 +1517,7 @@ void Z_parseAttributeKey_inner(uint16_t shortaddr, class Z_attribute & attr, uin
|
|||
Z_attribute_match matched_attr = Z_findAttributeMatcherById(shortaddr, attr.key.id.cluster, attr.key.id.attr_id, false);
|
||||
if (matched_attr.found()) {
|
||||
attr.attr_type = matched_attr.zigbee_type;
|
||||
attr.manuf = matched_attr.manuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,18 @@
|
|||
|
||||
#ifdef USE_ZIGBEE
|
||||
|
||||
const char Z_MUL[] PROGMEM = "mul:";
|
||||
const char Z_DIV[] PROGMEM = "div:";
|
||||
const char Z_MANUF[] PROGMEM = "manuf:";
|
||||
|
||||
char * Z_subtoken(char * token, const char * prefix) {
|
||||
size_t prefix_len = strlen_P(prefix);
|
||||
if (!strncmp_P(token, prefix, prefix_len)) {
|
||||
return token + prefix_len;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// global singleton
|
||||
Z_plugin_templates g_plugin_templates;
|
||||
|
||||
|
@ -48,6 +60,7 @@ 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.manuf = attr_tmpl->manuf;
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
|
@ -67,6 +80,7 @@ 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.manuf = attr_tmpl->manuf;
|
||||
}
|
||||
}
|
||||
return attr;
|
||||
|
@ -205,6 +219,7 @@ bool ZbLoad(const char *filename_raw) {
|
|||
char * delimiter_equal = strchr(token, '=');
|
||||
|
||||
if (delimiter_equal == nullptr) {
|
||||
// NORMAL ATTRIBUTE
|
||||
// token is of from '0000/0000' or '0000/0000%00'
|
||||
char * delimiter_slash = strchr(token, '/');
|
||||
char * delimiter_percent = strchr(token, '%');
|
||||
|
@ -215,6 +230,9 @@ bool ZbLoad(const char *filename_raw) {
|
|||
uint16_t attr_id = 0xFFFF;
|
||||
uint16_t cluster_id = 0xFFFF;
|
||||
uint8_t type_id = Zunk;
|
||||
int8_t multiplier = 1;
|
||||
char * name = nullptr;
|
||||
uint16_t manuf = 0;
|
||||
|
||||
cluster_id = strtoul(token, &delimiter_slash, 16);
|
||||
if (!delimiter_percent) {
|
||||
|
@ -223,19 +241,44 @@ bool ZbLoad(const char *filename_raw) {
|
|||
attr_id = strtoul(delimiter_slash+1, &delimiter_percent, 16);
|
||||
type_id = Z_getTypeByName(delimiter_percent+1);
|
||||
}
|
||||
// name of the attribute
|
||||
// NAME of the attribute
|
||||
token = strtok_r(rest, ",", &rest);
|
||||
if (token == nullptr) {
|
||||
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad '%s' ignore missing name '%s'", filename_raw, buf_line);
|
||||
continue;
|
||||
}
|
||||
name = token;
|
||||
// ADDITIONAL ELEMENTS?
|
||||
// Ex: `manuf:1037`
|
||||
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);
|
||||
}
|
||||
// look for divider
|
||||
else if (sub_token = Z_subtoken(token, Z_DIV)) {
|
||||
multiplier = - strtoul(sub_token, nullptr, 10); // negative to indicate divider
|
||||
}
|
||||
// look for `manuf:HHHH`
|
||||
else if (sub_token = Z_subtoken(token, Z_MANUF)) {
|
||||
manuf = strtoul(sub_token, nullptr, 16);
|
||||
}
|
||||
else {
|
||||
AddLog(LOG_LEVEL_DEBUG, "ZIG: ZbLoad unrecognized modifier '%s'", token);
|
||||
}
|
||||
}
|
||||
|
||||
// token contains the name of the attribute
|
||||
Z_plugin_attribute & plugin_attr = tmpl->attributes.addToLast();
|
||||
plugin_attr.cluster = cluster_id;
|
||||
plugin_attr.attribute = attr_id;
|
||||
plugin_attr.type = type_id;
|
||||
plugin_attr.name = token;
|
||||
plugin_attr.name = name;
|
||||
plugin_attr.multiplier = multiplier;
|
||||
plugin_attr.manuf = manuf;
|
||||
} else {
|
||||
// ATTRIBUTE SYNONYM
|
||||
// token is of from '0000/0000=0000/0000,1'
|
||||
char * rest2 = token;
|
||||
char * tok2 = strtok_r(rest2, "=", &rest2);
|
||||
|
@ -246,11 +289,22 @@ bool ZbLoad(const char *filename_raw) {
|
|||
char * delimiter_slash2 = strchr(tok2, '/');
|
||||
uint16_t new_cluster_id = strtoul(tok2, &delimiter_slash2, 16);
|
||||
uint16_t new_attr_id = strtoul(delimiter_slash2+1, nullptr, 16);
|
||||
// multiplier
|
||||
token = strtok_r(rest, ",", &rest);
|
||||
int8_t multiplier = 1;
|
||||
if (token != nullptr) {
|
||||
multiplier = strtol(token, nullptr, 10);
|
||||
|
||||
// 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);
|
||||
}
|
||||
// look for divider
|
||||
else if (sub_token = Z_subtoken(token, Z_DIV)) {
|
||||
multiplier = - strtoul(sub_token, nullptr, 10); // negative to indicate divider
|
||||
}
|
||||
else {
|
||||
AddLog(LOG_LEVEL_DEBUG, "ZIG: ZbLoad unrecognized modifier '%s'", token);
|
||||
}
|
||||
}
|
||||
// create the synonym
|
||||
Z_attribute_synonym & syn = tmpl->synonyms.addToLast();
|
||||
|
@ -305,21 +359,35 @@ bool ZbUnload(const char *filename_raw) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// append modifiers like mul/div/manuf
|
||||
void Z_AppendModifiers(char * buf, size_t buf_len, int8_t multiplier, 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);
|
||||
}
|
||||
if (manuf) {
|
||||
ext_snprintf_P(buf, buf_len, "%s,%s%04X", buf, Z_MANUF, manuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Dump the ZbLoad structure in a format compatible with ZbLoad
|
||||
void ZbLoadDump(void) {
|
||||
char buf[96];
|
||||
AddLog(LOG_LEVEL_INFO, "ZIG: ZbLoad dump all current information");
|
||||
AddLog(LOG_LEVEL_INFO, "====> START");
|
||||
|
||||
for (const Z_plugin_template & tmpl : g_plugin_templates) {
|
||||
if (tmpl.filename != nullptr) {
|
||||
AddLog(LOG_LEVEL_INFO, "# imported from '%s'", tmpl.filename);
|
||||
ext_snprintf_P(buf, sizeof(buf), "# imported from '%s'", tmpl.filename);
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("%s"), buf);
|
||||
}
|
||||
// marchers
|
||||
if (tmpl.matchers.length() == 0) {
|
||||
AddLog(LOG_LEVEL_INFO, ": # no matcher");
|
||||
ext_snprintf_P(buf, sizeof(buf), ": # no matcher");
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("%s"), buf);
|
||||
} else {
|
||||
for (const Z_plugin_matcher & matcher : tmpl.matchers) {
|
||||
AddLog(LOG_LEVEL_INFO, ":%s,%s", matcher.model.c_str(), matcher.manufacturer.c_str());
|
||||
ext_snprintf_P(buf, sizeof(buf), ":%s,%s", matcher.model.c_str(), matcher.manufacturer.c_str());
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("%s"), buf);
|
||||
}
|
||||
}
|
||||
// attributes
|
||||
|
@ -328,16 +396,20 @@ void ZbLoadDump(void) {
|
|||
AddLog(LOG_LEVEL_INFO, "");
|
||||
} else {
|
||||
for (const Z_plugin_attribute & attr : tmpl.attributes) {
|
||||
if (attr.type == Zunk) {
|
||||
AddLog(LOG_LEVEL_INFO, "%04X/%04X,%s", attr.cluster, attr.attribute, attr.name.c_str());
|
||||
} else {
|
||||
char type[16];
|
||||
Z_getTypeByNumber(type, sizeof(type), attr.type);
|
||||
AddLog(LOG_LEVEL_INFO, "%04X/%04X%%%s,%s", attr.cluster, attr.attribute, type, attr.name.c_str());
|
||||
ext_snprintf_P(buf, sizeof(buf), "%04X/%04X", attr.cluster, attr.attribute);
|
||||
// add type if known
|
||||
if (attr.type != Zunk) {
|
||||
char type_str[16];
|
||||
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);
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("%s"), buf);
|
||||
}
|
||||
for (const Z_attribute_synonym & syn : tmpl.synonyms) {
|
||||
AddLog(LOG_LEVEL_INFO, "%04X/%04X=%04X/%04X,%i", syn.cluster, syn.attribute, syn.new_cluster, syn.new_attribute, syn.multiplier);
|
||||
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);
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("%s"), buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -352,6 +352,15 @@ void ZbSendReportWrite(class JsonParserToken val_pubwrite, class ZCLFrame & zcl)
|
|||
return;
|
||||
}
|
||||
|
||||
// check for manuf code
|
||||
if (attr.manuf) {
|
||||
if (zcl.manuf != 0 && zcl.manuf != attr.manuf) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "conflicting manuf code 0x%04X (was 0x%04X)"), attr.manuf, zcl.manuf);
|
||||
} else {
|
||||
zcl.manuf = attr.manuf;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (attr.key_is_str) {
|
||||
Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR(D_ZIGBEE_UNKNOWN_ATTRIBUTE " "), key.getStr());
|
||||
|
@ -646,6 +655,14 @@ void ZbSendRead(JsonParserToken val_attr, ZCLFrame & zcl) {
|
|||
if (attrs) { free(attrs); }
|
||||
return;
|
||||
}
|
||||
// check for manuf code
|
||||
if (matched_attr.manuf) {
|
||||
if (zcl.manuf != 0 && zcl.manuf != matched_attr.manuf) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "conflicting manuf code 0x%04X (was 0x%04X)"), matched_attr.manuf, zcl.manuf);
|
||||
} else {
|
||||
zcl.manuf = matched_attr.manuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE D_ZIGBEE_UNKNWON_ATTRIBUTE), key.getStr());
|
||||
|
|
|
@ -9,6 +9,6 @@ EF00/0267,CycleIrrigationNumTimes # number of cycle irrigation times, set to 0
|
|||
EF00/0268,IrrigationTarget # duration in minutes or capacity in Liters (depending on mode)
|
||||
EF00/0269,CycleIrrigationInterval # cycle irrigation interval (minutes, max 1440)
|
||||
EF00/026A,CurrentTemperature # (value ignored because isn't a valid tempurature reading. Misdocumented and usage unclear)
|
||||
EF00/026C=0001/0021,2 # match to BatteryPercentage
|
||||
EF00/026C=0001/0021,mul:2 # match to BatteryPercentage
|
||||
EF00/026F,WaterConsumed # water consumed (Litres)
|
||||
EF00/0372,LastIrrigationDuration # (string) Ex: "00:01:10,0"
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#Z2Tv1
|
||||
# DEPRECATED
|
||||
# Legacy Tuya attributes from before Zigbee Device plugins
|
||||
# Use only if you need to legacy behavior, prefer device specific
|
||||
: # apply to all devices
|
||||
EF00/0070,TuyaScheduleWorkdays
|
||||
EF00/0071,TuyaScheduleHolidays
|
||||
EF00/0101,Power
|
||||
EF00/0102,Power2
|
||||
EF00/0103,Power3
|
||||
EF00/0104,Power4
|
||||
EF00/0107,TuyaChildLock
|
||||
EF00/0112,TuyaWindowDetection
|
||||
EF00/0114,TuyaValveDetection
|
||||
EF00/0174,TuyaAutoLock
|
||||
EF00/0202,TuyaTempTarget
|
||||
EF00/0202=0201/FFF1,mul:10 # TempTarget
|
||||
EF00/0203=0402/0000,mul:10 # Temperature
|
||||
EF00/0215,TuyaBattery
|
||||
EF00/0266,TuyaMinTemp
|
||||
EF00/0267,TuyaMaxTemp
|
||||
EF00/0269,TuyaBoostTime
|
||||
EF00/026B,TuyaComfortTemp
|
||||
EF00/026C,TuyaEcoTemp
|
||||
EF00/026D=0201/FFF0
|
||||
EF00/0272,TuyaAwayTemp
|
||||
EF00/0275,TuyaAwayDays
|
||||
EF00/0404,TuyaPreset
|
||||
EF00/0405,TuyaFanMode
|
||||
EF00/046A,TuyaForceMode
|
||||
EF00/046F,TuyaWeekSelect
|
Loading…
Reference in New Issue