diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino index c1704a67b..a9d6ef244 100644 --- a/tasmota/xdrv_23_zigbee_1z_libs.ino +++ b/tasmota/xdrv_23_zigbee_1z_libs.ino @@ -257,6 +257,8 @@ public: inline Z_attribute & addAttribute(const __FlashStringHelper * name, uint8_t suffix = 0) { return addAttribute((const char*) name, true, suffix); } + // smaller version called often to reduce code size + Z_attribute & addAttributePMEM(const char * name); // Remove from list by reference, if null or not found, then do nothing inline void removeAttribute(const Z_attribute * attr) { remove(attr); } @@ -297,6 +299,11 @@ public: bool mergeList(const Z_attribute_list &list2); }; + +Z_attribute & Z_attribute_list::addAttributePMEM(const char * name) { + return addAttribute(name, true, 0); +} + /*********************************************************************************************\ * * Implementation for Z_attribute diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino index 59ad6b6f5..d9a437db8 100644 --- a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino @@ -637,28 +637,30 @@ void Z_Device::jsonAddDeviceNamme(Z_attribute_list & attr_list) const { 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); + attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_DEVICE)).setStr(hex); if (fname) { - attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(fname); + attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_NAME)).setStr(fname); } } + // Add "IEEEAddr":"0x1234567812345678" void Z_Device::jsonAddIEEE(Z_attribute_list & attr_list) const { char hex[22]; hex[0] = '0'; // prefix with '0x' hex[1] = 'x'; Uint64toHex(longaddr, &hex[2], 64); - attr_list.addAttribute(F("IEEEAddr")).setStr(hex); + attr_list.addAttributePMEM(PSTR("IEEEAddr")).setStr(hex); } // Add "ModelId":"","Manufacturer":"" void Z_Device::jsonAddModelManuf(Z_attribute_list & attr_list) const { if (modelId) { - attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(modelId); + attr_list.addAttributePMEM(PSTR(D_JSON_MODEL D_JSON_ID)).setStr(modelId); } if (manufacturerId) { - attr_list.addAttribute(F("Manufacturer")).setStr(manufacturerId); + attr_list.addAttributePMEM(PSTR("Manufacturer")).setStr(manufacturerId); } } + // Add "Endpoints":[...] void Z_Device::jsonAddEndpoints(Z_attribute_list & attr_list) const { JsonGeneratorArray arr_ep; @@ -667,7 +669,7 @@ void Z_Device::jsonAddEndpoints(Z_attribute_list & attr_list) const { if (0x00 == endpoint) { break; } arr_ep.add(endpoint); } - attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str()); + attr_list.addAttributePMEM(PSTR("Endpoints")).setStrRaw(arr_ep.toString().c_str()); } // Add "Config":["",""...] void Z_Device::jsonAddConfig(Z_attribute_list & attr_list) const { @@ -682,7 +684,7 @@ void Z_Device::jsonAddConfig(Z_attribute_list & attr_list) const { key[0] = Z_Data::DataTypeToChar(data_elt.getType()); arr_data.addStr(key); } - attr_list.addAttribute(F("Config")).setStrRaw(arr_data.toString().c_str()); + attr_list.addAttributePMEM(PSTR("Config")).setStrRaw(arr_data.toString().c_str()); } // Add All data attributes void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const { @@ -693,15 +695,15 @@ void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const { } // Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality" void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const { - attr_list.addAttribute(F("Reachable")).setBool(getReachable()); - if (validBatteryPercent()) { attr_list.addAttribute(PSTR("BatteryPercentage")).setUInt(batterypercent); } + attr_list.addAttributePMEM(PSTR("Reachable")).setBool(getReachable()); + if (validBatteryPercent()) { attr_list.addAttributePMEM(PSTR("BatteryPercentage")).setUInt(batterypercent); } if (validLastSeen()) { if (Rtc.utc_time >= last_seen) { - attr_list.addAttribute(PSTR("LastSeen")).setUInt(Rtc.utc_time - last_seen); + attr_list.addAttributePMEM(PSTR("LastSeen")).setUInt(Rtc.utc_time - last_seen); } - attr_list.addAttribute(PSTR("LastSeenEpoch")).setUInt(last_seen); + attr_list.addAttributePMEM(PSTR("LastSeenEpoch")).setUInt(last_seen); } - if (validLqi()) { attr_list.addAttribute(PSTR(D_CMND_ZIGBEE_LINKQUALITY)).setUInt(lqi); } + if (validLqi()) { attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_LINKQUALITY)).setUInt(lqi); } } @@ -709,8 +711,8 @@ void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const { void Z_Device::jsonLightState(Z_attribute_list & attr_list) const { if (valid()) { // dump all known values - attr_list.addAttribute(F("Reachable")).setBool(getReachable()); - if (validPower()) { attr_list.addAttribute(F("Power")).setUInt(getPower()); } + attr_list.addAttributePMEM(PSTR("Reachable")).setBool(getReachable()); + if (validPower()) { attr_list.addAttributePMEM(PSTR("Power")).setUInt(getPower()); } int32_t light_mode = -1; const Z_Data_Light & light = data.find(0); if (&light != nullptr) { @@ -723,7 +725,7 @@ void Z_Device::jsonLightState(Z_attribute_list & attr_list) const { attr_list.findOrCreateAttribute(PSTR("Hue")).setUInt(light.getHue()); } } - attr_list.addAttribute(F("Light")).setInt(light_mode); + attr_list.addAttributePMEM(PSTR("Light")).setInt(light_mode); } } diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index d3156f85e..cdf64fbf5 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1389,7 +1389,7 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) { uint16_t read_attr_ids[len/2]; - attr_list.addAttribute(F(D_CMND_ZIGBEE_CLUSTER)).setUInt(_cluster_id); + attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_CLUSTER)).setUInt(_cluster_id); JsonGeneratorArray attr_numbers; Z_attribute_list attr_names; @@ -1411,8 +1411,8 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) { } i += 2; } - attr_list.addAttribute(F("Read")).setStrRaw(attr_numbers.toString().c_str()); - attr_list.addAttribute(F("ReadNames")).setStrRaw(attr_names.toString(true).c_str()); + attr_list.addAttributePMEM(PSTR("Read")).setStrRaw(attr_numbers.toString().c_str()); + attr_list.addAttributePMEM(PSTR("ReadNames")).setStrRaw(attr_names.toString(true).c_str()); // call auto-responder autoResponder(read_attr_ids, len/2); @@ -1428,8 +1428,8 @@ void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) { uint16_t attr_id = _payload.get8(i+2); Z_attribute_list attr_config_response; - attr_config_response.addAttribute(F("Status")).setUInt(status); - attr_config_response.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); + attr_config_response.addAttributePMEM(PSTR("Status")).setUInt(status); + attr_config_response.addAttributePMEM(PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); const __FlashStringHelper* attr_name = zigbeeFindAttributeById(_cluster_id, attr_id, nullptr, nullptr); if (attr_name) { @@ -1439,7 +1439,7 @@ void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) { } } - Z_attribute &attr_1 = attr_list.addAttribute(F("ConfigResponse")); + Z_attribute &attr_1 = attr_list.addAttributePMEM(PSTR("ConfigResponse")); attr_1.setStrRaw(attr_config_list.toString(true).c_str()); } @@ -1448,7 +1448,7 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) { uint32_t i = 0; uint32_t len = _payload.len(); - Z_attribute &attr_root = attr_list.addAttribute(F("ReadConfig")); + Z_attribute &attr_root = attr_list.addAttributePMEM(PSTR("ReadConfig")); Z_attribute_list attr_1; while (len >= i + 4) { @@ -1458,7 +1458,7 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) { Z_attribute_list attr_2; if (direction) { - attr_2.addAttribute(F("DirectionReceived")).setBool(true); + attr_2.addAttributePMEM(PSTR("DirectionReceived")).setBool(true); } // find the attribute name @@ -1477,15 +1477,15 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) { } i += 4; if (0 != status) { - attr_2.addAttribute(F("Status")).setUInt(status); - attr_2.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); + attr_2.addAttributePMEM(PSTR("Status")).setUInt(status); + attr_2.addAttributePMEM(PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str()); } else { // no error, decode data if (direction) { // only Timeout period is present uint16_t attr_timeout = _payload.get16(i); i += 2; - attr_2.addAttribute(F("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout); + attr_2.addAttributePMEM(PSTR("TimeoutPeriod")).setUInt((0xFFFF == attr_timeout) ? -1 : attr_timeout); } else { // direction == 0, we have a data type uint8_t attr_type = _payload.get8(i); @@ -1493,11 +1493,11 @@ void ZCLFrame::parseReadConfigAttributes(Z_attribute_list& attr_list) { uint16_t attr_min_interval = _payload.get16(i+1); uint16_t attr_max_interval = _payload.get16(i+3); i += 5; - attr_2.addAttribute(F("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval); - attr_2.addAttribute(F("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval); + attr_2.addAttributePMEM(PSTR("MinInterval")).setUInt((0xFFFF == attr_min_interval) ? -1 : attr_min_interval); + attr_2.addAttributePMEM(PSTR("MaxInterval")).setUInt((0xFFFF == attr_max_interval) ? -1 : attr_max_interval); if (!attr_discrete) { // decode Reportable Change - Z_attribute &attr_change = attr_2.addAttribute(F("ReportableChange")); + Z_attribute &attr_change = attr_2.addAttributePMEM(PSTR("ReportableChange")); i += parseSingleAttribute(attr_change, _payload, i, attr_type); if ((1 != multiplier) && (0 != multiplier)) { float fval = attr_change.getFloat(); @@ -1541,21 +1541,21 @@ void ZCLFrame::parseResponse(void) { // "Device" char s[12]; snprintf_P(s, sizeof(s), PSTR("0x%04X"), _srcaddr); - attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(s); + attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_DEVICE)).setStr(s); // "Name" const char * friendlyName = zigbee_devices.getFriendlyName(_srcaddr); if (friendlyName) { - attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(friendlyName); + attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_NAME)).setStr(friendlyName); } // "Command" snprintf_P(s, sizeof(s), PSTR("%04X!%02X"), _cluster_id, cmd); - attr_list.addAttribute(F(D_JSON_ZIGBEE_CMD)).setStr(s); + attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_CMD)).setStr(s); // "Status" - attr_list.addAttribute(F(D_JSON_ZIGBEE_STATUS)).setUInt(status); + attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_STATUS)).setUInt(status); // "StatusMessage" - attr_list.addAttribute(F(D_JSON_ZIGBEE_STATUS_MSG)).setStr(getZigbeeStatusMessage(status).c_str()); + attr_list.addAttributePMEM(PSTR(D_JSON_ZIGBEE_STATUS_MSG)).setStr(getZigbeeStatusMessage(status).c_str()); // Add Endpoint - attr_list.addAttribute(F(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint); + attr_list.addAttributePMEM(PSTR(D_CMND_ZIGBEE_ENDPOINT)).setUInt(_srcendpoint); // Add Group if non-zero if (_groupaddr) { // TODO what about group zero attr_list.group_id = _groupaddr; @@ -1628,11 +1628,11 @@ void ZCLFrame::syntheticAqaraSensor(Z_attribute_list &attr_list, class Z_attribu } } else if (modelId.startsWith(F("lumi.sensor_smoke"))) { // gas leak if (0x64 == attrid) { - attr_list.addAttribute(F("SmokeDensity")).copyVal(attr); + attr_list.addAttributePMEM(PSTR("SmokeDensity")).copyVal(attr); } } else if (modelId.startsWith(F("lumi.sensor_natgas"))) { // gas leak if (0x64 == attrid) { - attr_list.addAttribute(F("GasDensity")).copyVal(attr); + attr_list.addAttributePMEM(PSTR("GasDensity")).copyVal(attr); } } else if (modelId.startsWith(F("lumi.sensor_ht")) || modelId.equals(F("lumi.sens")) || @@ -1831,7 +1831,7 @@ void ZCLFrame::syntheticAqaraVibration(class Z_attribute_list &attr_list, class int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; snprintf_P(temp, sizeof(temp), "[%i,%i,%i]", Angle_X, Angle_Y, Angle_Z); - attr_list.addAttribute(F("AqaraAngles")).setStrRaw(temp); + attr_list.addAttributePMEM(PSTR("AqaraAngles")).setStrRaw(temp); } } break; diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 7f16a9997..3b37323ad 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -201,7 +201,7 @@ void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin if (BAD_SHORTADDR != shortaddr) { zigbee_devices.getShortAddr(shortaddr).setReachable(false); // mark device as reachable Z_attribute_list attr_list; - attr_list.addAttribute(F("Reachable")).setBool(false); // "Reachable":false + attr_list.addAttributePMEM(PSTR("Reachable")).setBool(false); // "Reachable":false // Z_postProcessAttributes(shortaddr, endpoint, attr_list); // make sure all is updated accordingly zigbee_devices.jsonPublishNow(shortaddr, attr_list); } @@ -394,38 +394,38 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, case 0x00050004: // AddScene or RemoveScene or StoreScene attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x); attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str()); - attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.y); - attr_list.addAttribute(PSTR("SceneId"), true).setUInt(xyz.z); + attr_list.addAttributePMEM(PSTR("GroupId")).setUInt(xyz.y); + attr_list.addAttributePMEM(PSTR("SceneId")).setUInt(xyz.z); if (0x00050001 == cccc00mm) { // ViewScene specific - attr_list.addAttribute(PSTR("ScenePayload"), true).setBuf(payload, 4, payload.len()-4); // remove first 4 bytes + attr_list.addAttributePMEM(PSTR("ScenePayload")).setBuf(payload, 4, payload.len()-4); // remove first 4 bytes } break; case 0x00050003: // RemoveAllScenes attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x); attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str()); - attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.y); + attr_list.addAttributePMEM(PSTR("GroupId")).setUInt(xyz.y); break; case 0x00050006: // GetSceneMembership attr_list.addAttribute(command_name, PSTR("Status")).setUInt(xyz.x); attr_list.addAttribute(command_name, PSTR("StatusMsg")).setStr(getZigbeeStatusMessage(xyz.x).c_str()); - attr_list.addAttribute(PSTR("Capacity"), true).setUInt(xyz.y); - attr_list.addAttribute(PSTR("GroupId"), true).setUInt(xyz.z); - attr_list.addAttribute(PSTR("ScenePayload"), true).setBuf(payload, 4, payload.len()-4); // remove first 4 bytes + attr_list.addAttributePMEM(PSTR("Capacity")).setUInt(xyz.y); + attr_list.addAttributePMEM(PSTR("GroupId")).setUInt(xyz.z); + attr_list.addAttributePMEM(PSTR("ScenePayload")).setBuf(payload, 4, payload.len()-4); // remove first 4 bytes break; case 0x00060040: // Power Off With Effect - attr_list.addAttribute(PSTR("Power"), true).setUInt(0); - attr_list.addAttribute(PSTR("PowerEffect"), true).setUInt(xyz.x); - attr_list.addAttribute(PSTR("PowerEffectVariant"), true).setUInt(xyz.y); + attr_list.addAttributePMEM(PSTR("Power")).setUInt(0); + attr_list.addAttributePMEM(PSTR("PowerEffect")).setUInt(xyz.x); + attr_list.addAttributePMEM(PSTR("PowerEffectVariant")).setUInt(xyz.y); break; case 0x00060041: // Power On With Recall Global Scene - attr_list.addAttribute(PSTR("Power"), true).setUInt(1); - attr_list.addAttribute(PSTR("PowerRecallGlobalScene"), true).setBool(true); + attr_list.addAttributePMEM(PSTR("Power")).setUInt(1); + attr_list.addAttributePMEM(PSTR("PowerRecallGlobalScene")).setBool(true); break; case 0x00060042: // Power On With Timed Off Command - attr_list.addAttribute(PSTR("Power"), true).setUInt(1); - attr_list.addAttribute(PSTR("PowerOnlyWhenOn"), true).setUInt(xyz.x); - attr_list.addAttribute(PSTR("PowerOnTime"), true).setFloat(xyz.y / 10.0f); - attr_list.addAttribute(PSTR("PowerOffWait"), true).setFloat(xyz.z / 10.0f); + attr_list.addAttributePMEM(PSTR("Power")).setUInt(1); + attr_list.addAttributePMEM(PSTR("PowerOnlyWhenOn")).setUInt(xyz.x); + attr_list.addAttributePMEM(PSTR("PowerOnTime")).setFloat(xyz.y / 10.0f); + attr_list.addAttributePMEM(PSTR("PowerOffWait")).setFloat(xyz.z / 10.0f); break; case 0xEF000000 ... 0xEF0000FF: // any Tuya - Moes command if (convertTuyaSpecificCluster(attr_list, cluster, cmd, direction, shortaddr, srcendpoint, payload)) { @@ -433,8 +433,8 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster, } break; case 0xFCCC0000: // Terncy button (multi-)press - attr_list.addAttribute(PSTR("TerncyPress"), true).setUInt(xyz.y); - attr_list.addAttribute(PSTR("TerncyCount"), true).setUInt(xyz.x); + attr_list.addAttributePMEM(PSTR("TerncyPress")).setUInt(xyz.y); + attr_list.addAttributePMEM(PSTR("TerncyCount")).setUInt(xyz.x); break; } } else { // general case diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 5b8fc1960..9fdc9c107 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -1116,13 +1116,11 @@ void CmndZbModelId(void) { Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, true); // in case of short_addr, it must be already registered if (!device.valid()) { ResponseCmndChar_P(PSTR("Unknown device")); return; } - if (p == nullptr) { - const char * modelId = device.modelId; - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, modelId ? modelId : ""); - } else { + if (p != nullptr) { device.setModelId(p); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, p); } + const char * modelId = device.modelId; + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_MODELID "\":\"%s\"}}"), device.shortaddr, modelId ? modelId : ""); } //