From 96eac4a54723a86adaf876f4b475683384dac6c6 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Mon, 18 Jan 2021 16:10:17 +0100
Subject: [PATCH 01/19] Use solo1-release_v3.3-7e63061fa
---
platformio_override_sample.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index 4db6a41b3..deff8272a 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -165,7 +165,7 @@ lib_extra_dirs =
; *** EXPERIMENTAL Tasmota version for ESP32solo1 (used in some Xiaomi devices)
[env:tasmota32solo1]
extends = env:tasmota32
-platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-release_v3.3-solo1-4b325f52e.tar.gz
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-solo1-release_v3.3-7e63061fa.tar.gz
platformio/tool-mklittlefs @ ~1.203.200522
platformio/tool-esptoolpy @ ~1.30000.0
build_unflags = ${esp32_defaults.build_unflags}
From 57a4153a418d6daebc5d407f700caa611de9d05f Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 18 Jan 2021 18:31:19 +0100
Subject: [PATCH 02/19] Zigbee fix bad JSON for `ZbStatus0`
---
tasmota/xdrv_23_zigbee_2a_devices_impl.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
index ffb5a72df..7d850ef5a 100644
--- a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
+++ b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
@@ -767,7 +767,7 @@ String Z_Devices::dumpCoordinator(void) const {
attr_list.addAttributePMEM(PSTR("IEEEAddr")).setHex64(localIEEEAddr);
attr_list.addAttributePMEM(PSTR("TotalDevices")).setUInt(zigbee_devices.devicesSize());
- return attr_list.toString();
+ return attr_list.toString(true);
}
// If &device == nullptr, then dump all
From 1b1b1ed83cdba93535b01449cacbb1100d94912d Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Mon, 18 Jan 2021 19:21:51 +0100
Subject: [PATCH 03/19] build gz only for ESP8266
---
pio-tools/gzip-firmware.py | 42 ++++++++++++++++++++++----------------
1 file changed, 24 insertions(+), 18 deletions(-)
diff --git a/pio-tools/gzip-firmware.py b/pio-tools/gzip-firmware.py
index e29c05576..bb1759123 100644
--- a/pio-tools/gzip-firmware.py
+++ b/pio-tools/gzip-firmware.py
@@ -3,26 +3,32 @@ import os
import shutil
import gzip
-OUTPUT_DIR = "build_output{}".format(os.path.sep)
+platform = env.PioPlatform()
+board = env.BoardConfig()
+mcu = board.get("build.mcu", "esp32")
+# gzip only for ESP8266
+if env["PIOPLATFORM"] != "espressif32":
-def bin_gzip(source, target, env):
- variant = str(target[0]).split(os.path.sep)[2]
-
- # create string with location and file names based on variant
- bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
- gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
+ OUTPUT_DIR = "build_output{}".format(os.path.sep)
- # check if new target files exist and remove if necessary
- if os.path.isfile(gzip_file): os.remove(gzip_file)
+ def bin_gzip(source, target, env):
+ variant = str(target[0]).split(os.path.sep)[2]
- # write gzip firmware file
- with open(bin_file,"rb") as fp:
- with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
- shutil.copyfileobj(fp, f)
-
- ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
- GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
+ # create string with location and file names based on variant
+ bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
+ gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
- print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
+ # check if new target files exist and remove if necessary
+ if os.path.isfile(gzip_file): os.remove(gzip_file)
-env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])
+ # write gzip firmware file
+ with open(bin_file,"rb") as fp:
+ with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
+ shutil.copyfileobj(fp, f)
+
+ ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
+ GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
+
+ print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
+
+ env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])
From 081f097b60b037e554d33a3a90e8b48460cccd3f Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 18 Jan 2021 20:39:14 +0100
Subject: [PATCH 04/19] Zigbee increase timeouts for EZSP
---
tasmota/xdrv_23_zigbee_7_0_statemachine.ino | 64 ++++++++++-----------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
index cc23c7a92..b2a01b6eb 100644
--- a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
+++ b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
@@ -789,38 +789,38 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
// configure EFR32
ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredCoord)
- ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) // Address table size
- ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(500, ZBR_SET_OK2)
+ ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(2500, ZBR_SET_OK) // Address table size
+ ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(2500, ZBR_SET_OK2)
// read configuration
// TODO - not sure it's useful
- //ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(500, ZBR_GET_OK, &EZ_ReadAPSUnicastMessage)
+ //ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_OK, &EZ_ReadAPSUnicastMessage)
// add endpoint 0x01 and 0x0B
- ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT)
- ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT)
+ ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(2500, ZBR_ADD_ENDPOINT)
+ ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(2500, ZBR_ADD_ENDPOINT)
// set Concentrator
- ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(500, ZBR_SET_CONCENTRATOR)
+ ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(2500, ZBR_SET_CONCENTRATOR)
// setInitialSecurityState
- ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- // ZI_SEND(ZBS_SET_POLICY_04) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ // ZI_SEND(ZBS_SET_POLICY_04) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
// Decide whether we try 'networkInit()' to restore configuration, or create a new network
ZI_CALL(&EZ_GotoIfResetConfig, ZIGBEE_LABEL_CONFIGURE_EZSP) // goto ZIGBEE_LABEL_CONFIGURE_EZSP if reset_config is set
@@ -830,12 +830,12 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
// Try networkInit to restore settings, and check if network comes up
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_BAD_CONFIG) //
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_BAD_CONFIG)
- ZI_SEND(ZBS_NETWORK_INIT) ZI_WAIT_RECV(500, ZBR_NETWORK_INIT)
+ ZI_SEND(ZBS_NETWORK_INIT) ZI_WAIT_RECV(2500, ZBR_NETWORK_INIT)
ZI_WAIT_RECV(1500, ZBR_NETWORK_UP) // wait for network to start
// check if configuration is ok
- ZI_SEND(ZBS_GET_KEY_NWK) ZI_WAIT_RECV_FUNC(500, ZBR_GET_KEY_NWK, &EZ_CheckKeyNWK)
- ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64)
- ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(500, ZBR_CHECK_NETW_PARM, &EZ_NetworkParameters)
+ ZI_SEND(ZBS_GET_KEY_NWK) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_KEY_NWK, &EZ_CheckKeyNWK)
+ ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_EUI64, &EZ_GetEUI64)
+ ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(2500, ZBR_CHECK_NETW_PARM, &EZ_NetworkParameters)
// all ok, proceed to next step
ZI_GOTO(ZIGBEE_LABEL_NETWORK_CONFIGURED)
@@ -851,9 +851,9 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
// set encryption keys
- ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(500, ZBR_SET_SECURITY)
+ ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(2500, ZBR_SET_SECURITY)
// formNetwork
- ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(500, ZBR_FORM_NETWORK)
+ ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(2500, ZBR_FORM_NETWORK)
ZI_WAIT_RECV(5000, ZBR_NETWORK_UP) // wait for network to start
ZI_LABEL(ZIGBEE_LABEL_NETWORK_CONFIGURED)
@@ -861,11 +861,11 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
// Query device information
- ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64)
- ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(500, ZBR_GET_NODEID, &EZ_GetNodeId)
+ ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_EUI64, &EZ_GetEUI64)
+ ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_NODEID, &EZ_GetNodeId)
// auto-register multicast group 0x0000
ZI_LOG(LOG_LEVEL_INFO, kZigbeeGroup0)
- ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(500, ZBR_SET_MCAST_ENTRY)
+ ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(2500, ZBR_SET_MCAST_ENTRY)
// ZI_LABEL(ZIGBEE_LABEL_READY)
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted)
From cdc9d8dfc9082e39ef06ad33e0921c4913782db0 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Mon, 18 Jan 2021 21:28:12 +0100
Subject: [PATCH 05/19] Zigbee send ack to command (#10624)
* Zigbee send ack to command
* Fix cluster
Co-authored-by: Stephan Hadinger
---
tasmota/xdrv_23_zigbee_5_converters.ino | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 674f6fdd0..4af809b5c 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -1673,6 +1673,27 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
}
}
}
+ // Send Default Response to acknowledge the attribute reporting
+ if (0 == _frame_control.b.disable_def_resp) {
+ // the device expects a default response
+ SBuffer buf(2);
+ buf.add8(_cmd_id);
+ buf.add8(0x00); // Status = OK
+
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ _srcaddr,
+ 0x0000,
+ _cluster_id,
+ _srcendpoint,
+ ZCL_DEFAULT_RESPONSE,
+ _manuf_code,
+ false /* not cluster specific */,
+ false /* noresponse */,
+ true /* direct no retry */,
+ _transact_seq, /* zcl transaction id */
+ buf.getBuffer(), buf.len()
+ }));
+ }
}
// ======================================================================
From 2f139d0e47bf7e353fdf47d10b38c101cd8a1558 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 18 Jan 2021 21:48:04 +0100
Subject: [PATCH 06/19] More PROGMEM
---
.../jsmn-shadinger-1.0/src/JsonGenerator.h | 4 +-
tasmota/settings.ino | 38 ++---
tasmota/support.ino | 13 +-
tasmota/support_command.ino | 6 +-
tasmota/support_network.ino | 2 +-
tasmota/support_rtc.ino | 18 ++-
tasmota/support_tasmota.ino | 12 +-
tasmota/support_wifi.ino | 4 +-
tasmota/tasmota.ino | 4 +-
tasmota/xdrv_01_webserver.ino | 145 +++++++++---------
tasmota/xdrv_02_mqtt.ino | 50 +++---
tasmota/xdrv_04_light.ino | 2 +-
tasmota/xdrv_07_domoticz.ino | 4 +-
tasmota/xdrv_09_timers.ino | 6 +-
tasmota/xdrv_10_rules.ino | 22 +--
tasmota/xdrv_12_home_assistant.ino | 6 +-
tasmota/xdrv_16_tuyamcu.ino | 2 +-
tasmota/xdrv_20_hue.ino | 56 +++----
tasmota/xdrv_23_zigbee_1z_libs.ino | 6 +-
tasmota/xdrv_23_zigbee_2a_devices_impl.ino | 2 +-
tasmota/xdrv_23_zigbee_3_hue.ino | 16 +-
tasmota/xdrv_23_zigbee_A_impl.ino | 2 +-
tasmota/xdrv_38_ping.ino | 2 +-
tasmota/xdrv_40_telegram.ino | 4 +-
tasmota/xdrv_50_filesystem.ino | 28 ++--
tasmota/xsns_34_hx711.ino | 2 +-
26 files changed, 234 insertions(+), 222 deletions(-)
diff --git a/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h
index 2f27b846b..1d6c0f969 100644
--- a/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h
+++ b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h
@@ -32,7 +32,7 @@ extern String EscapeJSONString(const char *str);
class JsonGeneratorArray {
public:
- JsonGeneratorArray(): val("[]") {} // start with empty array
+ JsonGeneratorArray(): val(F("[]")) {} // start with empty array
void add(uint32_t uval32);
void add(int32_t uval32);
@@ -53,7 +53,7 @@ protected:
class JsonGeneratorObject {
public:
- JsonGeneratorObject(): val("{}") {} // start with empty object
+ JsonGeneratorObject(): val(F("{}")) {} // start with empty object
void add(const char* key, uint32_t uval32);
void add(const char* key, int32_t uval32);
diff --git a/tasmota/settings.ino b/tasmota/settings.ino
index b2d438df6..0ebdac4c7 100644
--- a/tasmota/settings.ino
+++ b/tasmota/settings.ino
@@ -812,10 +812,10 @@ void SettingsDefaultSet2(void)
flag3.use_wifi_rescan |= WIFI_SCAN_REGULARLY;
Settings.wifi_output_power = 170;
Settings.param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL;
- ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS);
- ParseIp(&Settings.ip_address[1], WIFI_GATEWAY);
- ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK);
- ParseIp(&Settings.ip_address[3], WIFI_DNS);
+ ParseIp(&Settings.ip_address[0], PSTR(WIFI_IP_ADDRESS));
+ ParseIp(&Settings.ip_address[1], PSTR(WIFI_GATEWAY));
+ ParseIp(&Settings.ip_address[2], PSTR(WIFI_SUBNETMASK));
+ ParseIp(&Settings.ip_address[3], PSTR(WIFI_DNS));
Settings.sta_config = WIFI_CONFIG_TOOL;
// Settings.sta_active = 0;
SettingsUpdateText(SET_STASSID1, PSTR(STA_SSID1));
@@ -865,22 +865,22 @@ void SettingsDefaultSet2(void)
flag3.grouptopic_mode |= MQTT_GROUPTOPIC_FORMAT;
SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST);
Settings.mqtt_port = MQTT_PORT;
- SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID);
- SettingsUpdateText(SET_MQTT_USER, MQTT_USER);
- SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS);
- SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC);
- SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC);
- SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC);
- SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC);
- SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC);
+ SettingsUpdateText(SET_MQTT_CLIENT, PSTR(MQTT_CLIENT_ID));
+ SettingsUpdateText(SET_MQTT_USER, PSTR(MQTT_USER));
+ SettingsUpdateText(SET_MQTT_PWD, PSTR(MQTT_PASS));
+ SettingsUpdateText(SET_MQTT_TOPIC, PSTR(MQTT_TOPIC));
+ SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, PSTR(MQTT_BUTTON_TOPIC));
+ SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, PSTR(MQTT_SWITCH_TOPIC));
+ SettingsUpdateText(SET_MQTT_GRP_TOPIC, PSTR(MQTT_GRPTOPIC));
+ SettingsUpdateText(SET_MQTT_FULLTOPIC, PSTR(MQTT_FULLTOPIC));
Settings.mqtt_retry = MQTT_RETRY_SECS;
- SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX);
- SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX);
- SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2);
- SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF);
- SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON);
- SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE);
- SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD);
+ SettingsUpdateText(SET_MQTTPREFIX1, PSTR(SUB_PREFIX));
+ SettingsUpdateText(SET_MQTTPREFIX2, PSTR(PUB_PREFIX));
+ SettingsUpdateText(SET_MQTTPREFIX3, PSTR(PUB_PREFIX2));
+ SettingsUpdateText(SET_STATE_TXT1, PSTR(MQTT_STATUS_OFF));
+ SettingsUpdateText(SET_STATE_TXT2, PSTR(MQTT_STATUS_ON));
+ SettingsUpdateText(SET_STATE_TXT3, PSTR(MQTT_CMND_TOGGLE));
+ SettingsUpdateText(SET_STATE_TXT4, PSTR(MQTT_CMND_HOLD));
memcpy_P(Settings.mqtt_fingerprint[0], default_fingerprint1, sizeof(default_fingerprint1));
memcpy_P(Settings.mqtt_fingerprint[1], default_fingerprint2, sizeof(default_fingerprint2));
Settings.tele_period = TELE_PERIOD;
diff --git a/tasmota/support.ino b/tasmota/support.ino
index 0d5ce5b94..dace03c58 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -384,7 +384,7 @@ char* Uint64toHex(uint64_t value, char *str, uint16_t bits)
char* dtostrfd(double number, unsigned char prec, char *s)
{
if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript)
- strcpy(s, "null");
+ strcpy_P(s, PSTR("null"));
return s;
} else {
return dtostrf(number, 1, prec, s);
@@ -661,10 +661,13 @@ bool ValidIpAddress(const char* str)
return (*p == '\0');
}
-bool ParseIp(uint32_t* addr, const char* str)
+bool ParseIp(uint32_t* addr, const char* str_p)
{
uint8_t *part = (uint8_t*)addr;
uint8_t i;
+ char str_r[strlen_P(str_p)+1];
+ char * str = &str_r[0];
+ strcpy_P(str, str_p);
*addr = 0;
for (i = 0; i < 4; i++) {
@@ -861,7 +864,7 @@ float ConvertPressureForSeaLevel(float pressure)
String PressureUnit(void)
{
- return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE);
+ return (Settings.flag.pressure_conversion) ? String(F(D_UNIT_MILLIMETER_MERCURY)) : String(F(D_UNIT_PRESSURE));
}
float ConvertSpeed(float s)
@@ -1024,11 +1027,11 @@ String GetSerialConfig(void) {
// b00000x00 - 1 or 2 stop bits
// b000xx000 - None, Even or Odd parity
- const char kParity[] = "NEOI";
+ const static char kParity[] PROGMEM = "NEOI";
char config[4];
config[0] = '5' + (Settings.serial_config & 0x3);
- config[1] = kParity[(Settings.serial_config >> 3) & 0x3];
+ config[1] = pgm_read_byte(&kParity[(Settings.serial_config >> 3) & 0x3]);
config[2] = '1' + ((Settings.serial_config >> 2) & 0x1);
config[3] = '\0';
return String(config);
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 1c5e32474..145d86ef2 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -110,7 +110,7 @@ void ResponseCmndStateText(uint32_t value)
void ResponseCmndDone(void)
{
- ResponseCmndChar(D_JSON_DONE);
+ ResponseCmndChar(PSTR(D_JSON_DONE));
}
void ResponseCmndIdxChar(const char* value)
@@ -352,7 +352,7 @@ void CmndBacklog(void)
#else
TasmotaGlobal.backlog_pointer = TasmotaGlobal.backlog_index;
#endif
- ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED);
+ ResponseCmndChar(blflag ? PSTR(D_JSON_EMPTY) : PSTR(D_JSON_ABORTED));
}
}
@@ -1282,7 +1282,7 @@ void CmndTemplate(void)
if (Settings.module != USER_MODULE) {
ModuleDefault(Settings.module);
}
- SettingsUpdateText(SET_TEMPLATE_NAME, "Merged");
+ SettingsUpdateText(SET_TEMPLATE_NAME, PSTR("Merged"));
uint32_t j = 0;
for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) {
if (6 == i) { j = 9; }
diff --git a/tasmota/support_network.ino b/tasmota/support_network.ino
index b2bb73bf0..e2b24d782 100644
--- a/tasmota/support_network.ino
+++ b/tasmota/support_network.ino
@@ -38,7 +38,7 @@ void StartMdns(void) {
// mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START];
MDNS.end(); // close existing or MDNS.begin will fail
Mdns.begun = (uint8_t)MDNS.begin(TasmotaGlobal.hostname);
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Mdns.begun) ? D_INITIALIZED : D_FAILED);
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Mdns.begun) ? PSTR(D_INITIALIZED) : PSTR(D_FAILED));
// }
}
}
diff --git a/tasmota/support_rtc.ino b/tasmota/support_rtc.ino
index 08f7cfc3d..2f2403e7a 100644
--- a/tasmota/support_rtc.ino
+++ b/tasmota/support_rtc.ino
@@ -33,8 +33,8 @@ const uint32_t MINS_PER_HOUR = 60UL;
Ticker TickerRtc;
-static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0
-static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+static const uint8_t kDaysInMonth[] PROGMEM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0
+static const char kMonthNamesEnglish[] PROGMEM = "JanFebMarAprMayJunJulAugSepOctNovDec";
struct RTC {
uint32_t utc_time = 0;
@@ -88,7 +88,9 @@ String GetBuildDateAndTime(void)
// "2017-03-07T11:08:02" - ISO8601:2004
char bdt[21];
char *p;
- char mdate[] = __DATE__; // "Mar 7 2017"
+ static const char mdate_P[] PROGMEM = __DATE__; // "Mar 7 2017"
+ char mdate[strlen_P(mdate_P)+1]; // copy on stack first
+ strcpy_P(mdate, mdate_P);
char *smonth = mdate;
int day = 0;
int year = 0;
@@ -107,8 +109,10 @@ String GetBuildDateAndTime(void)
year = atoi(str);
}
}
- int month = (strstr(kMonthNamesEnglish, smonth) -kMonthNamesEnglish) /3 +1;
- snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, __TIME__);
+ char MonthNamesEnglish[sizeof(kMonthNamesEnglish)];
+ strcpy_P(MonthNamesEnglish, kMonthNamesEnglish);
+ int month = (strstr(MonthNamesEnglish, smonth) -MonthNamesEnglish) /3 +1;
+ snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, PSTR(__TIME__));
return String(bdt); // 2017-03-07T11:08:02
}
@@ -290,7 +294,7 @@ void BreakTime(uint32_t time_input, TIME_T &tm)
month_length = 28;
}
} else {
- month_length = kDaysInMonth[month];
+ month_length = pgm_read_byte(&kDaysInMonth[month]);
}
if (time >= month_length) {
@@ -326,7 +330,7 @@ uint32_t MakeTime(TIME_T &tm)
if ((2 == i) && LEAP_YEAR(tm.year)) {
seconds += SECS_PER_DAY * 29;
} else {
- seconds += SECS_PER_DAY * kDaysInMonth[i-1]; // monthDay array starts from 0
+ seconds += SECS_PER_DAY * pgm_read_byte(&kDaysInMonth[i-1]); // monthDay array starts from 0
}
}
seconds+= (tm.day_of_month - 1) * SECS_PER_DAY;
diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino
index 64bc6e09d..9cac3fa37 100644
--- a/tasmota/support_tasmota.ino
+++ b/tasmota/support_tasmota.ino
@@ -20,10 +20,12 @@
const char kSleepMode[] PROGMEM = "Dynamic|Normal";
const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE;
-char* Format(char* output, const char* input, int size)
+char* Format(char* output, const char* input_p, int size)
{
char *token;
uint32_t digits = 0;
+ char input[strlen_P(input_p)+1]; // copy from PMEM to RAM
+ strcpy_P(input, input_p);
if (strchr(input, '%') != nullptr) {
strlcpy(output, input, size);
@@ -102,7 +104,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi
fulltopic += TasmotaGlobal.mqtt_client;
fulltopic += F("_fb"); // cmnd/_fb
} else {
- fulltopic += topic; // cmnd/
+ fulltopic += (const __FlashStringHelper *)topic; // cmnd/
}
} else {
fulltopic = SettingsText(SET_MQTT_FULLTOPIC);
@@ -118,7 +120,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi
}
fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix));
- fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic);
+ fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), (const __FlashStringHelper *)topic);
fulltopic.replace(F("%hostname%"), TasmotaGlobal.hostname);
String token_id = WiFi.macAddress();
token_id.replace(":", "");
@@ -486,7 +488,7 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state)
#endif // USE_DOMOTICZ
result = !Settings.flag3.button_switch_force_local; // SetOption61 - Force local operation when button/switch topic is set
} else {
- Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state);
+ Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? PSTR("Switch") : PSTR("Button"), device, state);
result = XdrvRulesProcess();
}
#ifdef USE_PWM_DIMMER
@@ -742,7 +744,7 @@ void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperat
String GetSwitchText(uint32_t i) {
String switch_text = SettingsText(SET_SWITCH_TXT1 + i);
if ('\0' == switch_text[0]) {
- switch_text = D_JSON_SWITCH + String(i +1);
+ switch_text = F(D_JSON_SWITCH) + String(i +1);
}
return switch_text;
}
diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino
index 15f5e10c2..68208b941 100644
--- a/tasmota/support_wifi.ino
+++ b/tasmota/support_wifi.ino
@@ -176,7 +176,7 @@ void WiFiSetSleepMode(void)
void WifiBegin(uint8_t flag, uint8_t channel)
{
- const char kWifiPhyMode[] = " bgnl";
+ const static char kWifiPhyMode[] PROGMEM = " bgnl";
#ifdef USE_EMULATION
UdpDisconnect();
@@ -218,7 +218,7 @@ void WifiBegin(uint8_t flag, uint8_t channel)
WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active));
}
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."),
- Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], TasmotaGlobal.hostname);
+ Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, pgm_read_byte(&kWifiPhyMode[WiFi.getPhyMode() & 0x3]), TasmotaGlobal.hostname);
#if LWIP_IPV6
for (bool configured = false; !configured;) {
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 2407e61b1..18995cd47 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -302,7 +302,7 @@ void setup(void) {
snprintf_P(TasmotaGlobal.version, sizeof(TasmotaGlobal.version), PSTR("%s.%d"), TasmotaGlobal.version, VERSION & 0xff);
}
// Thehackbox inserts "release" or "commit number" before compiling using sed -i -e 's/PSTR("(%s)")/PSTR("(85cff52-%s)")/g' tasmota.ino
- snprintf_P(TasmotaGlobal.image_name, sizeof(TasmotaGlobal.image_name), PSTR("(%s)"), CODE_IMAGE_STR); // Results in (85cff52-tasmota) or (release-tasmota)
+ snprintf_P(TasmotaGlobal.image_name, sizeof(TasmotaGlobal.image_name), PSTR("(%s)"), PSTR(CODE_IMAGE_STR)); // Results in (85cff52-tasmota) or (release-tasmota)
Format(TasmotaGlobal.mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(TasmotaGlobal.mqtt_client));
Format(TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(TasmotaGlobal.mqtt_topic));
@@ -321,7 +321,7 @@ void setup(void) {
SetPowerOnState();
AddLog_P(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE "(%s)"),
- PROJECT, SettingsText(SET_DEVICENAME), TasmotaGlobal.version, TasmotaGlobal.image_name, GetBuildDateAndTime().c_str());
+ PSTR(PROJECT), SettingsText(SET_DEVICENAME), TasmotaGlobal.version, TasmotaGlobal.image_name, GetBuildDateAndTime().c_str());
#ifdef FIRMWARE_MINIMAL
AddLog_P(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION));
#endif // FIRMWARE_MINIMAL
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index 42cbf1af4..b43bc573f 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -376,9 +376,10 @@ struct WEB {
} Web;
// Helper function to avoid code duplication (saves 4k Flash)
+// arg can be in PROGMEM
static void WebGetArg(const char* arg, char* out, size_t max)
{
- String s = Webserver->arg(arg);
+ String s = Webserver->arg((const __FlashStringHelper *)arg);
strlcpy(out, s.c_str(), max);
// out[max-1] = '\0'; // Ensure terminating NUL
}
@@ -460,7 +461,7 @@ void StartWebserver(int type, IPAddress ipweb)
WebServer_on(uri, line.handler, pgm_read_byte(&line.method));
}
Webserver->onNotFound(HandleNotFound);
- Webserver->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); // this call requires 2 functions so we keep a direct call
+ Webserver->on(F("/u2"), HTTP_POST, HandleUploadDone, HandleUploadLoop); // this call requires 2 functions so we keep a direct call
#ifndef FIRMWARE_MINIMAL
XdrvCall(FUNC_WEB_ADD_HANDLER);
XsnsCall(FUNC_WEB_ADD_HANDLER);
@@ -691,7 +692,7 @@ void WSContentStart_P(const char* title, bool auth)
WSContentBegin(200, CT_HTML);
if (title != nullptr) {
- WSContentSend_P(HTTP_HEADER1, D_HTML_LANGUAGE, SettingsText(SET_DEVICENAME), title);
+ WSContentSend_P(HTTP_HEADER1, PSTR(D_HTML_LANGUAGE), SettingsText(SET_DEVICENAME), title);
}
}
@@ -767,7 +768,7 @@ void WSContentButton(uint32_t title_index)
WSContentSend_P(PSTR("
"),
GetTextIndexed(action, sizeof(action), title_index, kButtonAction),
GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm),
- (!title_index) ? "rst" : "non",
+ (!title_index) ? PSTR("rst") : PSTR("non"),
GetTextIndexed(title, sizeof(title), title_index, kButtonTitle));
} else {
WSContentSend_P(PSTR(""),
@@ -866,8 +867,8 @@ void HandleWifiLogin(void)
void WebSliderColdWarm(void)
{
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Cold Warm
- "a", // a - Unique HTML id
- "#eff", "#f81", // 6500k in RGB (White) to 2500k in RGB (Warm Yellow)
+ PSTR("a"), // a - Unique HTML id
+ PSTR("#eff"), PSTR("#f81"), // 6500k in RGB (White) to 2500k in RGB (Warm Yellow)
1, // sl1
153, 500, // Range color temperature
LightGetColorTemp(),
@@ -879,17 +880,17 @@ void HandleRoot(void)
{
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the page.
- if (Webserver->hasArg("rst")) {
+ if (Webserver->hasArg(F("rst"))) {
WebRestart(0);
return;
}
if (WifiIsInManagerMode()) {
#ifndef FIRMWARE_MINIMAL
- if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg("USER1")) && !(Webserver->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) {
+ if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg(F("USER1"))) && !(Webserver->hasArg(F("PASS1"))) && HTTP_MANAGER_RESET_ONLY != Web.state) {
HandleWifiLogin();
} else {
- if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg("USER1") == WEB_USERNAME ) && (Webserver->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) {
+ if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg(F("USER1")) == WEB_USERNAME ) && (Webserver->arg(F("PASS1")) == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) {
HandleWifiConfiguration();
} else {
// wrong user and pass
@@ -936,8 +937,8 @@ void HandleRoot(void)
LightGetHSB(&hue, &sat, nullptr);
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Hue
- "b", // b - Unique HTML id
- "#800", PSTR("#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800"), // Hue colors
+ PSTR("b"), // b - Unique HTML id
+ PSTR("#800"), PSTR("#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800"), // Hue colors
2, // sl2 - Unique range HTML id - Used as source for Saturation end color
1, 359, // Range valid Hue
hue,
@@ -951,7 +952,7 @@ void HandleRoot(void)
snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); // Saturation end color
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Saturation
- "s", // s - Unique HTML id related to eb('s').style.background='linear-gradient(to right,rgb('+sl+'%%,'+sl+'%%,'+sl+'%%),hsl('+eb('sl2').value+',100%%,50%%))';
+ PSTR("s"), // s - Unique HTML id related to eb('s').style.background='linear-gradient(to right,rgb('+sl+'%%,'+sl+'%%,'+sl+'%%),hsl('+eb('sl2').value+',100%%,50%%))';
scolor, stemp, // Brightness to max current color
3, // sl3 - Unique range HTML id - Not used
0, 100, // Range 0 to 100%
@@ -960,8 +961,8 @@ void HandleRoot(void)
}
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Brightness - Black to White
- "c", // c - Unique HTML id
- "#000", "#fff", // Black to White
+ PSTR("c"), // c - Unique HTML id
+ PSTR("#000"), PSTR("#fff"), // Black to White
4, // sl4 - Unique range HTML id - Used as source for Saturation begin color
Settings.flag3.slider_dimmer_stay_on, 100, // Range 0/1 to 100% (SetOption77 - Do not power off if slider moved to far left)
Settings.light_dimmer,
@@ -972,8 +973,8 @@ void HandleRoot(void)
WebSliderColdWarm();
}
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // White brightness - Black to White
- "f", // f - Unique HTML id
- "#000", "#fff", // Black to White
+ PSTR("f"), // f - Unique HTML id
+ PSTR("#000"), PSTR("#fff"), // Black to White
5, // sl5 - Unique range HTML id - Not used
Settings.flag3.slider_dimmer_stay_on, 100, // Range 0/1 to 100% (SetOption77 - Do not power off if slider moved to far left)
LightGetDimmer(2),
@@ -987,7 +988,7 @@ void HandleRoot(void)
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Channel brightness - Black to White
stemp, // e1 to e5 - Unique HTML id
- "#000", "#fff", // Black to White
+ PSTR("#000"), PSTR("#fff"), // Black to White
i+1, // sl1 to sl5 - Unique range HTML id - Not used
1, 100, // Range 1 to 100%
changeUIntScale(Settings.light_color[i], 0, 255, 0, 100),
@@ -1008,7 +1009,7 @@ void HandleRoot(void)
#ifdef USE_SONOFF_IFAN
if (IsModuleIfan()) {
WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1,
- (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE,
+ (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : PSTR(D_BUTTON_TOGGLE),
"");
for (uint32_t i = 0; i < MaxFanspeed(); i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i);
@@ -1031,7 +1032,7 @@ void HandleRoot(void)
#endif // USE_SHUTTER
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx);
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / TasmotaGlobal.devices_present, idx,
- (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (TasmotaGlobal.devices_present < 5) ? D_BUTTON_TOGGLE : "",
+ (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (TasmotaGlobal.devices_present < 5) ? PSTR(D_BUTTON_TOGGLE) : "",
(set_button) ? "" : (TasmotaGlobal.devices_present > 1) ? stemp : "");
}
#ifdef USE_SONOFF_IFAN
@@ -1107,7 +1108,7 @@ bool HandleRootStatusRefresh(void)
char svalue[32]; // Command and number parameter
char webindex[5]; // WebGetArg name
- WebGetArg("o", tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed
+ WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed
if (strlen(tmp)) {
ShowWebSource(SRC_WEBGUI);
uint32_t device = atoi(tmp);
@@ -1153,12 +1154,12 @@ bool HandleRootStatusRefresh(void)
#endif // USE_TUYA_MCU
}
#ifdef USE_LIGHT
- WebGetArg("d0", tmp, sizeof(tmp)); // 0 - 100 Dimmer value
+ WebGetArg(PSTR("d0"), tmp, sizeof(tmp)); // 0 - 100 Dimmer value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("w0", tmp, sizeof(tmp)); // 0 - 100 White value
+ WebGetArg(PSTR("w0"), tmp, sizeof(tmp)); // 0 - 100 White value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
@@ -1173,17 +1174,17 @@ bool HandleRootStatusRefresh(void)
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
}
- WebGetArg("t0", tmp, sizeof(tmp)); // 153 - 500 Color temperature
+ WebGetArg(PSTR("t0"), tmp, sizeof(tmp)); // 153 - 500 Color temperature
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("h0", tmp, sizeof(tmp)); // 0 - 359 Hue value
+ WebGetArg(PSTR("h0"), tmp, sizeof(tmp)); // 0 - 359 Hue value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("n0", tmp, sizeof(tmp)); // 0 - 99 Saturation value
+ WebGetArg(PSTR("n0"), tmp, sizeof(tmp)); // 0 - 99 Saturation value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
@@ -1200,19 +1201,19 @@ bool HandleRootStatusRefresh(void)
}
#endif // USE_SHUTTER
#ifdef USE_SONOFF_RF
- WebGetArg("k", tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys
+ WebGetArg(PSTR("k"), tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
#endif // USE_SONOFF_RF
#ifdef USE_ZIGBEE
- WebGetArg("zbj", tmp, sizeof(tmp));
+ WebGetArg(PSTR("zbj"), tmp, sizeof(tmp));
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR("ZbPermitJoin"));
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("zbr", tmp, sizeof(tmp));
+ WebGetArg(PSTR("zbr"), tmp, sizeof(tmp));
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR("ZbMap"));
ExecuteWebCommand(svalue, SRC_WEBGUI);
@@ -1315,7 +1316,7 @@ void WSContentSendNiceLists(uint32_t option) {
char stemp[30]; // Template number and Sensor name
for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3...
if (option && (1 == i)) {
- WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); // }2'255'>User}3
+ WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), PSTR(D_SENSOR_USER)); // }2'255'>User}3
}
uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0;
uint32_t midx = BGPIO(ridx);
@@ -1356,7 +1357,7 @@ void WSContentSendAdcNiceList(uint32_t option) {
WSContentSend_P(PSTR("os=\""));
for (uint32_t i = 0; i < ARRAY_SIZE(kAdcNiceList); i++) { // GPIO: }2'0'>None}3}2'17'>Analog}3...
if (option && (1 == i)) {
- WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); // }2'15'>User}3
+ WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), PSTR(D_SENSOR_USER)); // }2'15'>User}3
}
uint32_t ridx = pgm_read_word(kAdcNiceList + i) & 0xFFE0;
uint32_t midx = BGPIO(ridx);
@@ -1372,7 +1373,7 @@ void HandleTemplateConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
TemplateSaveSettings();
WebRestart(1);
return;
@@ -1380,7 +1381,7 @@ void HandleTemplateConfiguration(void)
char stemp[30]; // Template number and Sensor name
- WebGetArg("t", stemp, sizeof(stemp)); // 0 - 69 Template number
+ WebGetArg(PSTR("t"), stemp, sizeof(stemp)); // 0 - 69 Template number
if (strlen(stemp)) {
uint32_t module = atoi(stemp);
uint32_t module_save = Settings.module;
@@ -1474,7 +1475,7 @@ void TemplateSaveSettings(void)
char tmp[TOPSZ]; // WebGetArg NAME and GPIO/BASE/FLAG byte value
char svalue[300]; // Template command string
- WebGetArg("s1", tmp, sizeof(tmp)); // NAME
+ WebGetArg(PSTR("s1"), tmp, sizeof(tmp)); // NAME
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp);
uint32_t j = 0;
@@ -1492,7 +1493,7 @@ void TemplateSaveSettings(void)
uint32_t state = Webserver->hasArg(webindex) << i; // FLAG
flag += state;
}
- WebGetArg("g99", tmp, sizeof(tmp)); // BASE
+ WebGetArg(PSTR("g99"), tmp, sizeof(tmp)); // BASE
uint32_t base = atoi(tmp) +1;
snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base);
@@ -1505,7 +1506,7 @@ void HandleModuleConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
ModuleSaveSettings();
WebRestart(1);
return;
@@ -1572,7 +1573,7 @@ void ModuleSaveSettings(void)
{
char tmp[8]; // WebGetArg numbers only
- WebGetArg("g99", tmp, sizeof(tmp));
+ WebGetArg(PSTR("g99"), tmp, sizeof(tmp));
uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp);
Settings.last_module = Settings.module;
Settings.module = new_module;
@@ -1624,7 +1625,7 @@ void HandleWifiConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_WIFI));
- if (Webserver->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) {
+ if (Webserver->hasArg(F("save")) && HTTP_MANAGER_RESET_ONLY != Web.state) {
WifiSaveSettings();
WebRestart(2);
return;
@@ -1639,7 +1640,7 @@ void HandleWifiConfiguration(void)
#endif // USE_ENHANCED_GUI_WIFI_SCAN
if (HTTP_MANAGER_RESET_ONLY != Web.state) {
- if (Webserver->hasArg("scan")) {
+ if (Webserver->hasArg(F("scan"))) {
#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
@@ -1776,20 +1777,20 @@ void WifiSaveSettings(void)
{
char tmp[TOPSZ]; // Max length is currently 150
- WebGetArg("h", tmp, sizeof(tmp));
+ WebGetArg(PSTR("h"), tmp, sizeof(tmp));
SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp);
if (strchr(SettingsText(SET_HOSTNAME), '%') != nullptr) {
SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME);
}
- WebGetArg("c", tmp, sizeof(tmp));
+ WebGetArg(PSTR("c"), tmp, sizeof(tmp));
SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp);
- WebGetArg("s1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("s1"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp);
- WebGetArg("s2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("s2"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp);
- WebGetArg("p1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("p1"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp);
- WebGetArg("p2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("p2"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp);
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"),
SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS));
@@ -1839,19 +1840,19 @@ void LoggingSaveSettings(void)
{
char tmp[TOPSZ]; // Max length is currently 33
- WebGetArg("l0", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l0"), tmp, sizeof(tmp));
SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp));
- WebGetArg("l1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l1"), tmp, sizeof(tmp));
Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp);
- WebGetArg("l2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l2"), tmp, sizeof(tmp));
Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp);
- WebGetArg("l3", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l3"), tmp, sizeof(tmp));
SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp));
- WebGetArg("lh", tmp, sizeof(tmp));
+ WebGetArg(PSTR("lh"), tmp, sizeof(tmp));
SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp);
- WebGetArg("lp", tmp, sizeof(tmp));
+ WebGetArg(PSTR("lp"), tmp, sizeof(tmp));
Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp);
- WebGetArg("lt", tmp, sizeof(tmp));
+ WebGetArg(PSTR("lt"), tmp, sizeof(tmp));
Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp);
if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) {
Settings.tele_period = 10; // Do not allow periods < 10 seconds
@@ -1868,7 +1869,7 @@ void HandleOtherConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_OTHER));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
OtherSaveSettings();
WebRestart(1);
return;
@@ -1932,15 +1933,15 @@ void OtherSaveSettings(void)
char friendlyname[TOPSZ];
char message[MAX_LOGSZ];
- WebGetArg("dn", tmp, sizeof(tmp));
+ WebGetArg(PSTR("dn"), tmp, sizeof(tmp));
SettingsUpdateText(SET_DEVICENAME, (!strlen(tmp)) ? "" : (!strcmp(tmp,"1")) ? SettingsText(SET_FRIENDLYNAME1) : tmp);
- WebGetArg("wp", tmp, sizeof(tmp));
+ WebGetArg(PSTR("wp"), tmp, sizeof(tmp));
SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp);
- Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); // SetOption3 - Enable MQTT
+ Settings.flag.mqtt_enabled = Webserver->hasArg(F("b1")); // SetOption3 - Enable MQTT
#ifdef USE_EMULATION
UdpDisconnect();
#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE)
- WebGetArg("b2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("b2"), tmp, sizeof(tmp));
Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp);
#endif // USE_EMULATION_WEMO || USE_EMULATION_HUE
#endif // USE_EMULATION
@@ -1951,14 +1952,14 @@ void OtherSaveSettings(void)
snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i);
WebGetArg(webindex, tmp, sizeof(tmp));
snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1);
- SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp);
+ SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : PSTR(FRIENDLY_NAME) : tmp);
snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i));
}
AddLogData(LOG_LEVEL_INFO, message);
- WebGetArg("t1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("t1"), tmp, sizeof(tmp));
if (strlen(tmp)) { // {"NAME":"12345678901234","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":255,"BASE":255}
- snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : "");
+ snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg(F("t2"))) ? PSTR("; " D_CMND_MODULE " 0") : "");
ExecuteWebCommand(message, SRC_WEBGUI);
}
}
@@ -2034,7 +2035,7 @@ void HandleRestoreConfiguration(void)
WSContentStart_P(PSTR(D_RESTORE_CONFIGURATION));
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_RST);
- WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE);
+ WSContentSend_P(HTTP_FORM_RST_UPG, PSTR(D_RESTORE));
if (WifiIsInManagerMode()) {
WSContentSpaceButton(BUTTON_MAIN);
} else {
@@ -2249,7 +2250,7 @@ void HandleUpgradeFirmware(void) {
WSContentStart_P(PSTR(D_FIRMWARE_UPGRADE));
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL));
- WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE);
+ WSContentSend_P(HTTP_FORM_RST_UPG, PSTR(D_UPGRADE));
WSContentSpaceButton(BUTTON_MAIN);
WSContentStop();
@@ -2265,7 +2266,7 @@ void HandleUpgradeFirmwareStart(void) {
WifiConfigCounter();
char otaurl[TOPSZ];
- WebGetArg("o", otaurl, sizeof(otaurl));
+ WebGetArg(PSTR("o"), otaurl, sizeof(otaurl));
if (strlen(otaurl)) {
snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl);
ExecuteWebCommand(command, SRC_WEBGUI);
@@ -2668,9 +2669,9 @@ void HandleHttpCommand(void)
if (!WebAuthenticate()) {
// Prefer authorization via HTTP header (Basic auth), if it fails, use legacy method via GET parameters
char tmp1[33];
- WebGetArg("user", tmp1, sizeof(tmp1));
+ WebGetArg(PSTR("user"), tmp1, sizeof(tmp1));
char tmp2[strlen(SettingsText(SET_WEBPWD)) + 1];
- WebGetArg("password", tmp2, sizeof(tmp2));
+ WebGetArg(PSTR("password"), tmp2, sizeof(tmp2));
if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) {
WSContentBegin(401, CT_JSON);
@@ -2681,7 +2682,7 @@ void HandleHttpCommand(void)
}
WSContentBegin(200, CT_JSON);
- String svalue = Webserver->arg("cmnd");
+ String svalue = Webserver->arg(F("cmnd"));
if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) {
uint32_t curridx = TasmotaGlobal.log_buffer_pointer;
TasmotaGlobal.templog_level = LOG_LEVEL_INFO;
@@ -2717,7 +2718,7 @@ void HandleConsole(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- if (Webserver->hasArg("c2")) { // Console refresh requested
+ if (Webserver->hasArg(F("c2"))) { // Console refresh requested
HandleConsoleRefresh();
return;
}
@@ -2734,14 +2735,14 @@ void HandleConsole(void)
void HandleConsoleRefresh(void)
{
- String svalue = Webserver->arg("c1");
+ String svalue = Webserver->arg(F("c1"));
if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) {
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str());
ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE);
}
char stmp[8];
- WebGetArg("c2", stmp, sizeof(stmp));
+ WebGetArg(PSTR("c2"), stmp, sizeof(stmp));
uint32_t index = 0; // Initial start, dump all
if (strlen(stmp)) { index = atoi(stmp); }
@@ -2758,7 +2759,7 @@ void HandleConsoleRefresh(void)
if (len > sizeof(TasmotaGlobal.mqtt_data) -2) { len = sizeof(TasmotaGlobal.mqtt_data); }
char stemp[len +1];
strlcpy(stemp, line, len);
- WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp);
+ WSContentSend_P(PSTR("%s%s"), (cflg) ? PSTR("\n") : "", stemp);
cflg = true;
}
WSContentSend_P(PSTR("}1"));
@@ -2776,14 +2777,14 @@ void HandleNotFound(void)
#ifdef USE_EMULATION
#ifdef USE_EMULATION_HUE
String path = Webserver->uri();
- if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) {
+ if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith(F("/api")))) {
HandleHueApi(&path);
} else
#endif // USE_EMULATION_HUE
#endif // USE_EMULATION
{
WSContentBegin(404, CT_PLAIN);
- WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? "GET" : "POST", Webserver->args());
+ WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? PSTR("GET") : PSTR("POST"), Webserver->args());
for (uint32_t i = 0; i < Webserver->args(); i++) {
WSContentSend_P(PSTR(" %s: %s\n"), Webserver->argName(i).c_str(), Webserver->arg(i).c_str());
}
@@ -2978,7 +2979,7 @@ void CmndWebServer(void)
}
if (Settings.webserver) {
Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"),
- (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str());
+ (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str());
} else {
ResponseCmndStateText(0);
}
diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino
index 6324a584b..bf4a9e6f9 100644
--- a/tasmota/xdrv_02_mqtt.ino
+++ b/tasmota/xdrv_02_mqtt.ino
@@ -156,7 +156,7 @@ void MqttInit(void) {
// Detect AWS IoT and set default parameters
String host = String(SettingsText(SET_MQTT_HOST));
- if (host.indexOf(".iot.") && host.endsWith(".amazonaws.com")) { // look for ".iot." and ".amazonaws.com" in the domain name
+ if (host.indexOf(F(".iot.")) && host.endsWith(F(".amazonaws.com"))) { // look for ".iot." and ".amazonaws.com" in the domain name
Settings.flag4.mqtt_no_retain = true;
}
@@ -527,10 +527,10 @@ void MqttConnected(void) {
if (Settings.webserver) {
#if LWIP_IPV6
Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"),
- (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str(), WifiGetIPv6().c_str());
+ (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str(), WifiGetIPv6().c_str());
#else
Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"),
- (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str());
+ (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str());
#endif // LWIP_IPV6 = 1
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2"));
}
@@ -787,7 +787,7 @@ void CmndMqttFingerprint(void) {
void CmndMqttUser(void) {
if (XdrvMailbox.data_len > 0) {
- SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data);
+ SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_USER) : XdrvMailbox.data);
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndChar(SettingsText(SET_MQTT_USER));
@@ -795,7 +795,7 @@ void CmndMqttUser(void) {
void CmndMqttPassword(void) {
if (XdrvMailbox.data_len > 0) {
- SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data);
+ SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_PASS) : XdrvMailbox.data);
ResponseCmndChar(SettingsText(SET_MQTT_PWD));
TasmotaGlobal.restart_flag = 2;
} else {
@@ -852,7 +852,7 @@ void CmndStateText(void) {
void CmndMqttClient(void) {
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
- SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data);
+ SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_CLIENT_ID) : XdrvMailbox.data);
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndChar(SettingsText(SET_MQTT_CLIENT));
@@ -882,7 +882,7 @@ void CmndPrefix(void) {
if (XdrvMailbox.data_len > 0) {
MakeValidMqtt(0, XdrvMailbox.data);
SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1,
- (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data);
+ (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? PSTR(SUB_PREFIX) : (2==XdrvMailbox.index) ? PSTR(PUB_PREFIX) : PSTR(PUB_PREFIX2) : XdrvMailbox.data);
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1));
@@ -918,7 +918,7 @@ void CmndGroupTopic(void) {
uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2;
MakeValidMqtt(0, XdrvMailbox.data);
if (!strcmp(XdrvMailbox.data, TasmotaGlobal.mqtt_client)) { SetShortcutDefault(); }
- SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data);
+ SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_GRPTOPIC) : XdrvMailbox.data);
// Eliminate duplicates, have at least one and fill from index 1
char stemp[MAX_GROUP_TOPICS][TOPSZ];
@@ -940,7 +940,7 @@ void CmndGroupTopic(void) {
}
}
if (0 == read_index) {
- SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC);
+ SettingsUpdateText(SET_MQTT_GRP_TOPIC, PSTR(MQTT_GRPTOPIC));
} else {
uint32_t write_index = 0;
uint32_t real_index = SET_MQTT_GRP_TOPIC;
@@ -998,7 +998,7 @@ void CmndSwitchTopic(void) {
switch (Shortcut()) {
case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break;
case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, TasmotaGlobal.mqtt_topic); break;
- case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break;
+ case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, PSTR(MQTT_SWITCH_TOPIC)); break;
default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data);
}
}
@@ -1266,7 +1266,7 @@ void HandleMqttConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_MQTT));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
MqttSaveSettings();
WebRestart(1);
return;
@@ -1282,11 +1282,11 @@ void HandleMqttConfiguration(void)
#ifdef USE_MQTT_TLS
Mqtt.mqtt_tls ? PSTR(" checked") : "", // SetOption102 - Enable MQTT TLS
#endif // USE_MQTT_TLS
- Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT));
+ Format(str, PSTR(MQTT_CLIENT_ID), sizeof(str)), PSTR(MQTT_CLIENT_ID), SettingsText(SET_MQTT_CLIENT));
WSContentSend_P(HTTP_FORM_MQTT2,
(!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER),
- Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC),
- MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC));
+ Format(str, PSTR(MQTT_TOPIC), sizeof(str)), PSTR(MQTT_TOPIC), SettingsText(SET_MQTT_TOPIC),
+ PSTR(MQTT_FULLTOPIC), PSTR(MQTT_FULLTOPIC), SettingsText(SET_MQTT_FULLTOPIC));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentStop();
@@ -1298,10 +1298,10 @@ void MqttSaveSettings(void)
char stemp[TOPSZ];
char stemp2[TOPSZ];
- WebGetArg("mt", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mt"), tmp, sizeof(tmp));
strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp));
MakeValidMqtt(0, stemp);
- WebGetArg("mf", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mf"), tmp, sizeof(tmp));
strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2));
MakeValidMqtt(1, stemp2);
if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) {
@@ -1310,18 +1310,18 @@ void MqttSaveSettings(void)
}
SettingsUpdateText(SET_MQTT_TOPIC, stemp);
SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2);
- WebGetArg("mh", tmp, sizeof(tmp));
- SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp);
- WebGetArg("ml", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mh"), tmp, sizeof(tmp));
+ SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? PSTR(MQTT_HOST) : (!strcmp(tmp,"0")) ? "" : tmp);
+ WebGetArg(PSTR("ml"), tmp, sizeof(tmp));
Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp);
#ifdef USE_MQTT_TLS
- Settings.flag4.mqtt_tls = Webserver->hasArg("b3"); // SetOption102 - Enable MQTT TLS
+ Settings.flag4.mqtt_tls = Webserver->hasArg(F("b3")); // SetOption102 - Enable MQTT TLS
#endif
- WebGetArg("mc", tmp, sizeof(tmp));
- SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp);
- WebGetArg("mu", tmp, sizeof(tmp));
- SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp);
- WebGetArg("mp", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mc"), tmp, sizeof(tmp));
+ SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? PSTR(MQTT_CLIENT_ID) : tmp);
+ WebGetArg(PSTR("mu"), tmp, sizeof(tmp));
+ SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? PSTR(MQTT_USER) : (!strcmp(tmp,"0")) ? "" : tmp);
+ WebGetArg(PSTR("mp"), tmp, sizeof(tmp));
SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp);
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"),
SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC));
diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino
index 90d94ffb7..7e0907dd1 100644
--- a/tasmota/xdrv_04_light.ino
+++ b/tasmota/xdrv_04_light.ino
@@ -2625,7 +2625,7 @@ void CmndWakeup(void)
Light.wakeup_active = 3;
Settings.light_scheme = LS_WAKEUP;
LightPowerOn();
- ResponseCmndChar(D_JSON_STARTED);
+ ResponseCmndChar(PSTR(D_JSON_STARTED));
}
void CmndColorTemperature(void)
diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino
index 82b769963..b982a58b1 100644
--- a/tasmota/xdrv_07_domoticz.ino
+++ b/tasmota/xdrv_07_domoticz.ino
@@ -546,7 +546,7 @@ void HandleDomoticzConfiguration(void) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_DOMOTICZ));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
DomoticzSaveSettings();
WebRestart(1);
return;
@@ -605,7 +605,7 @@ void DomoticzSaveSettings(void) {
Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp);
snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]);
}
- WebGetArg("ut", tmp, sizeof(tmp));
+ WebGetArg(PSTR("ut"), tmp, sizeof(tmp));
Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp);
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"),
diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino
index 0f6292e13..a33204906 100644
--- a/tasmota/xdrv_09_timers.ino
+++ b/tasmota/xdrv_09_timers.ino
@@ -841,7 +841,7 @@ void HandleTimerConfiguration(void)
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_TIMER));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
TimerSaveSettings();
HandleConfiguration();
return;
@@ -883,8 +883,8 @@ void TimerSaveSettings(void)
char message[32 + (MAX_TIMERS *11)]; // MQT: Timers 0,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000
Timer timer;
- Settings.flag3.timers_enable = Webserver->hasArg("e0"); // CMND_TIMERS
- WebGetArg("t0", tmp, sizeof(tmp));
+ Settings.flag3.timers_enable = Webserver->hasArg(F("e0")); // CMND_TIMERS
+ WebGetArg(PSTR("t0"), tmp, sizeof(tmp));
char *p = tmp;
snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); // CMND_TIMERS
for (uint32_t i = 0; i < MAX_TIMERS; i++) {
diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino
index 637a3b963..31b8e9f46 100644
--- a/tasmota/xdrv_10_rules.ino
+++ b/tasmota/xdrv_10_rules.ino
@@ -416,7 +416,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
// Step1: Analyse rule
String rule_expr = rule; // "TELE-INA219#CURRENT>0.100"
if (Rules.teleperiod) {
- int ppos = rule_expr.indexOf("TELE-"); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100"
+ int ppos = rule_expr.indexOf(F("TELE-")); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100"
if (ppos == -1) { return false; } // No pre-amble in rule
rule_expr = rule.substring(5); // "INA219#CURRENT>0.100" or "SYSTEM#BOOT"
}
@@ -494,7 +494,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
// Step2: Search rule_name
int pos;
int rule_name_idx = 0;
- if ((pos = rule_name.indexOf("[")) > 0) { // "SUBTYPE1#CURRENT[1]"
+ if ((pos = rule_name.indexOf(F("["))) > 0) { // "SUBTYPE1#CURRENT[1]"
rule_name_idx = rule_name.substring(pos +1).toInt();
if ((rule_name_idx < 1) || (rule_name_idx > 6)) { // Allow indexes 1 to 6
rule_name_idx = 1;
@@ -515,7 +515,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
}
String subtype;
uint32_t i = 0;
- while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
+ while ((pos = rule_name.indexOf(F("#"))) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
subtype = rule_name.substring(0, pos);
obj = obj[subtype.c_str()].getObject();
if (!obj) { return false; } // not found
@@ -686,14 +686,14 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
String rule = rules;
rule.toUpperCase(); // "ON INA219#CURRENT>0.100 DO BACKLOG DIMMER 10;COLOR 100000 ENDON"
- if (!rule.startsWith("ON ")) { return serviced; } // Bad syntax - Nothing to start on
+ if (!rule.startsWith(F("ON "))) { return serviced; } // Bad syntax - Nothing to start on
- int pevt = rule.indexOf(" DO ");
+ int pevt = rule.indexOf(F(" DO "));
if (pevt == -1) { return serviced; } // Bad syntax - Nothing to do
String event_trigger = rule.substring(3, pevt); // "INA219#CURRENT>0.100"
- plen = rule.indexOf(" ENDON");
- plen2 = rule.indexOf(" BREAK");
+ plen = rule.indexOf(F(" ENDON"));
+ plen2 = rule.indexOf(F(" BREAK"));
if ((plen == -1) && (plen2 == -1)) { return serviced; } // Bad syntax - No ENDON neither BREAK
if (plen == -1) { plen = 9999; }
@@ -717,10 +717,10 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
// if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception
// Use Backlog with event to prevent rule event loop exception unless IF is used which uses an implicit backlog
- if ((ucommand.indexOf("IF ") == -1) &&
- (ucommand.indexOf("EVENT ") != -1) &&
- (ucommand.indexOf("BACKLOG ") == -1)) {
- commands = "backlog " + commands;
+ if ((ucommand.indexOf(F("IF ")) == -1) &&
+ (ucommand.indexOf(F("EVENT ")) != -1) &&
+ (ucommand.indexOf(F("BACKLOG ")) == -1)) {
+ commands = F("backlog ") + commands;
}
RulesVarReplace(commands, F("%VALUE%"), Rules.event_value);
diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino
index bf4664886..0120615e0 100644
--- a/tasmota/xdrv_12_home_assistant.ino
+++ b/tasmota/xdrv_12_home_assistant.ino
@@ -294,7 +294,7 @@ void NewHAssDiscovery(void)
for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) {
char fname[TOPSZ];
snprintf_P(fname, sizeof(fname), PSTR("\"%s\""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str());
- snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : "null");
+ snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : PSTR("null"));
}
stemp3[0] = '\0';
@@ -304,7 +304,7 @@ void NewHAssDiscovery(void)
char sname[TOPSZ];
snprintf_P(sname, sizeof(sname), PSTR("\"%s\""), GetSwitchText(i).c_str());
snprintf_P(stemp3, sizeof(stemp3), PSTR("%s%s%d"), stemp3, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? Settings.switchmode[i] : -1);
- snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%s"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : "null");
+ snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%s"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : PSTR("null"));
}
stemp5[0] = '\0';
@@ -338,7 +338,7 @@ void NewHAssDiscovery(void)
if (!Settings.flag.hass_discovery) { // HassDiscoveryRelays(relays)
Response_P(HASS_DISCOVER_DEVICE, WiFi.localIP().toString().c_str(), SettingsText(SET_DEVICENAME),
stemp2, TasmotaGlobal.hostname, unique_id, ModuleName().c_str(), TuyaMod, iFanMod, GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3),
- TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), SUB_PREFIX, PUB_PREFIX, PUB_PREFIX2, Hass.RelLst, stemp3, stemp4,
+ TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), PSTR(SUB_PREFIX), PSTR(PUB_PREFIX), PSTR(PUB_PREFIX2), Hass.RelLst, stemp3, stemp4,
stemp5, Settings.flag.mqtt_response, Settings.flag.button_swap, Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked,
Settings.flag.hass_light, Settings.flag3.pwm_multi_channels, Settings.flag3.mqtt_buttons, Settings.flag4.alexa_ct_range, Settings.flag5.mqtt_switches,
Settings.flag5.fade_fixed_duration, light_controller.isCTRGBLinked(), Light.subtype, stemp6);
diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino
index a291d2e11..7535d29c5 100644
--- a/tasmota/xdrv_16_tuyamcu.ino
+++ b/tasmota/xdrv_16_tuyamcu.ino
@@ -1295,7 +1295,7 @@ void TuyaSensorsShow(bool json)
GetTextIndexed(sname, sizeof(sname), (sensor-71), kTuyaSensors);
ResponseAppend_P(PSTR("\"%s\":%s"), sname,
- (Tuya.SensorsValid[sensor-71] ? dtostrfd(Tuya.Sensors[sensor-71], res, tempval) : "null"));
+ (Tuya.SensorsValid[sensor-71] ? dtostrfd(Tuya.Sensors[sensor-71], res, tempval) : PSTR("null")));
added = true;
}
#ifdef USE_WEBSERVER
diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino
index 4da625847..013e22a98 100644
--- a/tasmota/xdrv_20_hue.ino
+++ b/tasmota/xdrv_20_hue.ino
@@ -429,9 +429,9 @@ void HandleUpnpSetupHue(void)
{
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_HUE_BRIDGE_SETUP));
String description_xml = Decompress(HUE_DESCRIPTION_XML_COMPRESSED,HUE_DESCRIPTION_XML_SIZE);
- description_xml.replace("{x1", WiFi.localIP().toString());
- description_xml.replace("{x2", HueUuid());
- description_xml.replace("{x3", HueSerialnumber());
+ description_xml.replace(F("{x1"), WiFi.localIP().toString());
+ description_xml.replace(F("{x2"), HueUuid());
+ description_xml.replace(F("{x3"), HueSerialnumber());
WSSend(200, CT_XML, description_xml);
}
@@ -439,19 +439,19 @@ void HueNotImplemented(String *path)
{
AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str());
- WSSend(200, CT_JSON, "{}");
+ WSSend(200, CT_JSON, PSTR("{}"));
}
void HueConfigResponse(String *response)
{
*response += Decompress(HueConfigResponse_JSON, HueConfigResponse_JSON_SIZE);
- response->replace("{ma", WiFi.macAddress());
- response->replace("{ip", WiFi.localIP().toString());
- response->replace("{ms", WiFi.subnetMask().toString());
- response->replace("{gw", WiFi.gatewayIP().toString());
- response->replace("{br", HueBridgeId());
- response->replace("{dt", GetDateAndTime(DT_UTC));
- response->replace("{id", GetHueUserId());
+ response->replace(F("{ma"), WiFi.macAddress());
+ response->replace(F("{ip"), WiFi.localIP().toString());
+ response->replace(F("{ms"), WiFi.subnetMask().toString());
+ response->replace(F("{gw"), WiFi.gatewayIP().toString());
+ response->replace(F("{br"), HueBridgeId());
+ response->replace(F("{dt"), GetDateAndTime(DT_UTC));
+ response->replace(F("{id"), GetHueUserId());
}
void HueConfig(String *path)
@@ -534,13 +534,13 @@ void HueLightStatus1(uint8_t device, String *response)
char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack
UnishoxStrings msg(HUE_LIGHTS);
- snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (TasmotaGlobal.power & (1 << (device-1))) ? "true" : "false");
+ snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (TasmotaGlobal.power & (1 << (device-1))) ? PSTR("true") : PSTR("false"));
// Brightness for all devices with PWM
if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo
snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri);
}
if (LST_COLDWARM <= local_light_subtype) {
- snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? "ct" : "hs");
+ snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? PSTR("ct") : PSTR("hs"));
}
if (LST_RGB <= local_light_subtype) { // colors
if (prev_x_str[0] && prev_y_str[0]) {
@@ -680,7 +680,7 @@ void HueGlobalConfig(String *path) {
#endif // USE_ZIGBEE
response += F("},\"groups\":{},\"schedules\":{},\"config\":");
HueConfigResponse(&response);
- response += "}";
+ response += F("}");
WSSend(200, CT_JSON, response);
}
@@ -700,7 +700,7 @@ void CheckHue(String * response, bool &appending) {
for (uint32_t i = 1; i <= maxhue; i++) {
if (HueActive(i)) {
if (appending) { *response += ","; }
- *response += "\"";
+ *response += F("\"");
*response += EncodeLightId(i);
*response += F("\":{\"state\":");
HueLightStatus1(i, response);
@@ -735,7 +735,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
on = hue_on.getBool();
snprintf_P(buf, buf_size,
msg[HUE_RESP_ON],
- device_id, on ? "true" : "false");
+ device_id, on ? PSTR("true") : PSTR("false"));
#ifdef USE_SHUTTER
if (ShutterState(device)) {
@@ -778,7 +778,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "bri", bri);
+ device_id, PSTR("bri"), bri);
response += buf;
if (LST_SINGLE <= Light.subtype) {
// extend bri value if set to max
@@ -824,7 +824,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "hue", hue);
+ device_id, PSTR("hue"), hue);
response += buf;
if (LST_RGB <= Light.subtype) {
// change range from 0..65535 to 0..360
@@ -843,7 +843,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "sat", sat);
+ device_id, PSTR("sat"), sat);
response += buf;
if (LST_RGB <= Light.subtype) {
// extend sat value if set to max
@@ -862,7 +862,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "ct", ct);
+ device_id, PSTR("ct"), ct);
response += buf;
if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) {
g_gotct = true;
@@ -923,7 +923,7 @@ void HueLights(String *path)
path->remove(0,path->indexOf(F("/lights"))); // Remove until /lights
if (path->endsWith(F("/lights"))) { // Got /lights
- response = "{";
+ response = F("{");
bool appending = false;
#ifdef USE_LIGHT
CheckHue(&response, appending);
@@ -934,7 +934,7 @@ void HueLights(String *path)
#ifdef USE_SCRIPT_HUE
Script_Check_Hue(&response);
#endif
- response += "}";
+ response += F("}");
}
else if (path->endsWith(F("/state"))) { // Got ID/state
path->remove(0,8); // Remove /lights/
@@ -992,7 +992,7 @@ void HueLights(String *path)
#endif // USE_LIGHT
}
else {
- response = "{}";
+ response = F("{}");
code = 406;
}
exit:
@@ -1005,24 +1005,24 @@ void HueGroups(String *path)
/*
* http://tasmota/api/username/groups?1={"name":"Woonkamer","lights":[],"type":"Room","class":"Living room"})
*/
- String response = "{}";
+ String response(F("{}"));
uint8_t maxhue = (TasmotaGlobal.devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : TasmotaGlobal.devices_present;
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups (%s)"), path->c_str());
- if (path->endsWith("/0")) {
+ if (path->endsWith(F("/0"))) {
UnishoxStrings msg(HUE_LIGHTS);
response = msg[HUE_GROUP0_STATUS_JSON];
String lights = F("\"1\"");
for (uint32_t i = 2; i <= maxhue; i++) {
- lights += ",\"";
+ lights += F(",\"");
lights += EncodeLightId(i);
- lights += "\"";
+ lights += F("\"");
}
#ifdef USE_ZIGBEE
ZigbeeHueGroups(&response);
#endif // USE_ZIGBEE
- response.replace("{l1", lights);
+ response.replace(F("{l1"), lights);
#ifdef USE_LIGHT
HueLightStatus1(1, &response);
#endif // USE_LIGHT
diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino
index 4e018a21e..a228798e2 100644
--- a/tasmota/xdrv_23_zigbee_1z_libs.ino
+++ b/tasmota/xdrv_23_zigbee_1z_libs.ino
@@ -579,7 +579,7 @@ String Z_attribute::toString(bool prefix_comma) const {
// value part
switch (type) {
case Za_type::Za_none:
- res += "null";
+ res += F("null");
break;
case Za_type::Za_bool:
res += val.uval32 ? F("true") : F("false");
@@ -638,7 +638,9 @@ String Z_attribute::toString(bool prefix_comma) const {
if (val.arrval) {
res += val.arrval->toString();
} else {
- res += "[]";
+ // res += '[';
+ // res += ']';
+ res += F("[]");
}
break;
}
diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
index 7d850ef5a..55a98a748 100644
--- a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
+++ b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
@@ -556,7 +556,7 @@ void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_l
if (Settings.flag5.zb_received_as_subtopic)
GetTopic_P(stopic, TELE, subtopic, json_prefix);
else
- GetTopic_P(stopic, TELE, subtopic, D_RSLT_SENSOR);
+ GetTopic_P(stopic, TELE, subtopic, PSTR(D_RSLT_SENSOR));
MqttPublish(stopic, Settings.flag.mqtt_sensor_retain);
} else {
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino
index a77c8b67f..d71a8c3a9 100644
--- a/tasmota/xdrv_23_zigbee_3_hue.ino
+++ b/tasmota/xdrv_23_zigbee_3_hue.ino
@@ -62,13 +62,13 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
const size_t buf_size = 256;
char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack
- snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? "true" : "false");
+ snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? PSTR("true") : PSTR("false"));
// Brightness for all devices with PWM
if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo
snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri);
}
if (LST_COLDWARM <= local_light_subtype) {
- snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? "hs" : (1 == colormode) ? "xy" : "ct");
+ snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? PSTR("hs") : (1 == colormode) ? PSTR("xy") : PSTR("ct"));
}
if (LST_RGB <= local_light_subtype) { // colors
if (prev_x_str[0] && prev_y_str[0]) {
@@ -83,7 +83,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { // white temp
snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284);
}
- snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? "true" : "false");
+ snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? PSTR("true") : PSTR("false"));
*response += buf;
free(buf);
@@ -233,7 +233,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
on = hue_on.getBool();
snprintf_P(buf, buf_size,
msg[HUE_RESP_ON],
- device_id, on ? "true" : "false");
+ device_id, on ? PSTR("true") : PSTR("false"));
if (on) {
ZigbeeHuePower(shortaddr, 0x01);
@@ -252,7 +252,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "bri", bri);
+ device_id, PSTR("bri"), bri);
response += buf;
if (LST_SINGLE <= bulbtype) {
// extend bri value if set to max
@@ -293,7 +293,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "hue", hue);
+ device_id, PSTR("hue"), hue);
response += buf;
if (LST_RGB <= bulbtype) {
// change range from 0..65535 to 0..360
@@ -311,7 +311,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "sat", sat);
+ device_id, PSTR("sat"), sat);
response += buf;
if (LST_RGB <= bulbtype) {
// extend sat value if set to max
@@ -332,7 +332,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "ct", ct);
+ device_id, PSTR("ct"), ct);
response += buf;
if ((LST_COLDWARM == bulbtype) || (LST_RGBW <= bulbtype)) {
ZigbeeHueCT(shortaddr, ct);
diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino
index ec9a454ac..673f1ff8d 100644
--- a/tasmota/xdrv_23_zigbee_A_impl.ino
+++ b/tasmota/xdrv_23_zigbee_A_impl.ino
@@ -2092,7 +2092,7 @@ void ZigbeeMapRefresh(void) {
if ((!zigbee.init_phase) && (!zigbee.mapping_in_progress)) {
ZigbeeMapAllDevices();
}
- Webserver->sendHeader("Location","/zbm"); // Add a header to respond with a new location for the browser to go to the home page again
+ Webserver->sendHeader(F("Location"),F("/zbm")); // Add a header to respond with a new location for the browser to go to the home page again
Webserver->send(302);
}
diff --git a/tasmota/xdrv_38_ping.ino b/tasmota/xdrv_38_ping.ino
index 3f87a54bb..0974697fe 100644
--- a/tasmota/xdrv_38_ping.ino
+++ b/tasmota/xdrv_38_ping.ino
@@ -303,7 +303,7 @@ void PingResponsePoll(void) {
",\"AvgTime\":%d"
"}}}"),
ping->hostname.c_str(),
- success ? "true" : "false",
+ success ? PSTR("true") : PSTR("false"),
ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24,
success,
ping->timeout_count,
diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino
index ec230e792..0ceefa3a7 100644
--- a/tasmota/xdrv_40_telegram.ino
+++ b/tasmota/xdrv_40_telegram.ino
@@ -434,14 +434,14 @@ void CmndTmChatId(void) {
void CmndTmSend(void) {
if (!Telegram.send_enable || !strlen(SettingsText(SET_TELEGRAM_CHATID))) {
- ResponseCmndChar(D_JSON_FAILED);
+ ResponseCmndChar(PSTR(D_JSON_FAILED));
return;
}
if (XdrvMailbox.data_len > 0) {
String message = XdrvMailbox.data;
String chat_id = SettingsText(SET_TELEGRAM_CHATID);
if (!TelegramSendMessage(chat_id.toInt(), message)) {
- ResponseCmndChar(D_JSON_FAILED);
+ ResponseCmndChar(PSTR(D_JSON_FAILED));
return;
}
}
diff --git a/tasmota/xdrv_50_filesystem.ino b/tasmota/xdrv_50_filesystem.ino
index fb12e3b9d..dfca83cef 100644
--- a/tasmota/xdrv_50_filesystem.ino
+++ b/tasmota/xdrv_50_filesystem.ino
@@ -391,7 +391,7 @@ void UFSDelete(void) {
result = (ufs_type && ufsp->remove(XdrvMailbox.data));
}
if (!result) {
- ResponseCmndChar(D_JSON_FAILED);
+ ResponseCmndChar(PSTR(D_JSON_FAILED));
} else {
ResponseCmndDone();
}
@@ -454,8 +454,8 @@ void UfsDirectory(void) {
strcpy(ufs_path, "/");
- if (Webserver->hasArg("download")) {
- String stmp = Webserver->arg("download");
+ if (Webserver->hasArg(F("download"))) {
+ String stmp = Webserver->arg(F("download"));
char *cp = (char*)stmp.c_str();
if (UfsDownloadFile(cp)) {
// is directory
@@ -465,8 +465,8 @@ void UfsDirectory(void) {
}
}
- if (Webserver->hasArg("dir")) {
- String stmp = Webserver->arg("dir");
+ if (Webserver->hasArg(F("dir"))) {
+ String stmp = Webserver->arg(F("dir"));
ufs_dir = atoi(stmp.c_str());
if (ufs_dir == 1) {
dfsp = ufsp;
@@ -477,8 +477,8 @@ void UfsDirectory(void) {
}
}
- if (Webserver->hasArg("delete")) {
- String stmp = Webserver->arg("delete");
+ if (Webserver->hasArg(F("delete"))) {
+ String stmp = Webserver->arg(F("delete"));
char *cp = (char*)stmp.c_str();
dfsp->remove(cp);
}
@@ -498,7 +498,7 @@ void UfsDirectory(void) {
}
WSContentSend_P(UFS_FORM_FILE_UPGc2);
- WSContentSend_P(UFS_FORM_FILE_UPG, D_SCRIPT_UPLOAD);
+ WSContentSend_P(UFS_FORM_FILE_UPG, PSTR(D_SCRIPT_UPLOAD));
WSContentSend_P(UFS_FORM_SDC_DIRa);
if (ufs_type) {
@@ -516,7 +516,7 @@ void UfsListDir(char *path, uint8_t depth) {
char name[32];
char npath[128];
char format[12];
- sprintf(format, "%%-%ds", 24 - depth);
+ sprintf(format, PSTR("%%-%ds"), 24 - depth);
File dir = dfsp->open(path, UFS_FILE_READ);
if (dir) {
@@ -533,7 +533,7 @@ void UfsListDir(char *path, uint8_t depth) {
break;
}
}
- WSContentSend_P(UFS_FORM_SDC_DIRd, npath, path, "..");
+ WSContentSend_P(UFS_FORM_SDC_DIRd, npath, path, PSTR(".."));
}
char *ep;
while (true) {
@@ -764,13 +764,13 @@ bool Xdrv50(uint8_t function) {
#ifdef USE_WEBSERVER
case FUNC_WEB_ADD_MANAGEMENT_BUTTON:
if (ufs_type) {
- WSContentSend_PD(UFS_WEB_DIR, D_MANAGE_FILE_SYSTEM);
+ WSContentSend_PD(UFS_WEB_DIR, PSTR(D_MANAGE_FILE_SYSTEM));
}
break;
case FUNC_WEB_ADD_HANDLER:
- Webserver->on("/ufsd", UfsDirectory);
- Webserver->on("/ufsu", HTTP_GET, UfsDirectory);
- Webserver->on("/ufsu", HTTP_POST,[](){Webserver->sendHeader("Location","/ufsu");Webserver->send(303);}, HandleUploadLoop);
+ Webserver->on(F("/ufsd"), UfsDirectory);
+ Webserver->on(F("/ufsu"), HTTP_GET, UfsDirectory);
+ Webserver->on(F("/ufsu"), HTTP_POST,[](){Webserver->sendHeader(F("Location"),F("/ufsu"));Webserver->send(303);}, HandleUploadLoop);
break;
#endif // USE_WEBSERVER
}
diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/xsns_34_hx711.ino
index 4f22d7cca..7dc9da3d7 100644
--- a/tasmota/xsns_34_hx711.ino
+++ b/tasmota/xsns_34_hx711.ino
@@ -241,7 +241,7 @@ bool HxCommand(void)
break;
case 7: // WeightSave
Settings.energy_frequency_calibration = Hx.weight;
- Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE);
+ Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, PSTR(D_JSON_DONE));
break;
case 8: // Json on weight change
if (strchr(XdrvMailbox.data, ',') != nullptr) {
From b55fdcef75ca397ed25ee3fe0e6b638f951628c5 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 18 Jan 2021 22:32:59 +0100
Subject: [PATCH 07/19] Fix compilation ESP32
---
tasmota/xdrv_10_rules.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino
index 31b8e9f46..4a6d3c9c1 100644
--- a/tasmota/xdrv_10_rules.ino
+++ b/tasmota/xdrv_10_rules.ino
@@ -720,7 +720,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
if ((ucommand.indexOf(F("IF ")) == -1) &&
(ucommand.indexOf(F("EVENT ")) != -1) &&
(ucommand.indexOf(F("BACKLOG ")) == -1)) {
- commands = F("backlog ") + commands;
+ commands = String(F("backlog ")) + commands;
}
RulesVarReplace(commands, F("%VALUE%"), Rules.event_value);
From 3265236fe1943a08fef3b2f36db193dfa94652be Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 18 Jan 2021 22:37:36 +0100
Subject: [PATCH 08/19] Fix compilation ESP32
---
tasmota/support_tasmota.ino | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino
index 9cac3fa37..40d5836ad 100644
--- a/tasmota/support_tasmota.ino
+++ b/tasmota/support_tasmota.ino
@@ -744,7 +744,8 @@ void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperat
String GetSwitchText(uint32_t i) {
String switch_text = SettingsText(SET_SWITCH_TXT1 + i);
if ('\0' == switch_text[0]) {
- switch_text = F(D_JSON_SWITCH) + String(i +1);
+ switch_text = F(D_JSON_SWITCH);
+ switch_text += String(i+1);
}
return switch_text;
}
From 61bf455747f08c496c5130b6b312820518dc66ae Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue, 19 Jan 2021 08:50:52 +0100
Subject: [PATCH 09/19] gz not build anymore for ESP32
---
.github/workflows/Tasmota_build.yml | 2 --
1 file changed, 2 deletions(-)
diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml
index 9e8f61219..6cf253537 100644
--- a/.github/workflows/Tasmota_build.yml
+++ b/.github/workflows/Tasmota_build.yml
@@ -1603,8 +1603,6 @@ jobs:
[ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/
[ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/
- rm ./firmware/tasmota32/*.gz
- rm ./firmware/tasmota32/languages/*.gz
[ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/
[ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/
[ ! -f ./FIRMWARE.md ] || mv -f ./FIRMWARE.md ./README.md
From 86f4e8ab74f874b24609097cd66686d512430380 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue, 19 Jan 2021 08:51:48 +0100
Subject: [PATCH 10/19] no gz build for ESP32
---
.github/workflows/Tasmota_build_master.yml | 2 --
1 file changed, 2 deletions(-)
diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml
index 8c00d25e3..b2b68c477 100644
--- a/.github/workflows/Tasmota_build_master.yml
+++ b/.github/workflows/Tasmota_build_master.yml
@@ -1603,8 +1603,6 @@ jobs:
[ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/
[ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/
- rm ./firmware/tasmota32/*.gz
- rm ./firmware/tasmota32/languages/*.gz
[ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/
[ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/
[ ! -f ./FIRMWARE.md ] || mv -f ./RELEASENOTES.md ./README.md
From ec74dc3a51f46dedda7a0c6cafd97c1c82e35390 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 19 Jan 2021 11:54:49 +0100
Subject: [PATCH 11/19] Make zbbridge transfer more verbose
Make zbbridge transfer more verbose (#10413)
---
tasmota/xdrv_23_zigbee_9a_upload.ino | 90 ++++++++++++++++++++--------
1 file changed, 64 insertions(+), 26 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_9a_upload.ino b/tasmota/xdrv_23_zigbee_9a_upload.ino
index b946848a3..d8157c127 100644
--- a/tasmota/xdrv_23_zigbee_9a_upload.ino
+++ b/tasmota/xdrv_23_zigbee_9a_upload.ino
@@ -81,6 +81,13 @@ char ZigbeeUploadFlashRead(void) {
ZbUpload.byte_counter++;
if (ZbUpload.byte_counter > ZbUpload.ota_size) {
+
+// static bool padding = true;
+// if (padding) {
+// AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Start padding from %d"), ZbUpload.byte_counter);
+// padding = false;
+// }
+
// When the source device reaches the last XModem data block, it should be padded to 128 bytes
// of data using SUB (ASCII 0x1A) characters.
data = XM_SUB;
@@ -107,8 +114,8 @@ struct XMODEM {
uint32_t delay = 0;
uint32_t flush_delay = 0xFFFFFFFF;
uint32_t filepos = 0;
+ uint32_t packet_no = 1;
int crcBuf = 0;
- uint8_t packetNo = 1;
uint8_t checksumBuf = 0;
bool oldChecksum;
} XModem;
@@ -142,6 +149,11 @@ char XModemWaitACK(void)
if (i > 200) { return -1; }
}
in_char = ZigbeeSerial->read();
+
+// if (in_char != XM_ACK) {
+// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd3 0x%02X"), in_char);
+// }
+
if (XM_CAN == in_char) { return XM_CAN; }
} while ((in_char != XM_NAK) && (in_char != XM_ACK) && (in_char != 'C'));
return in_char;
@@ -162,10 +174,12 @@ bool XModemSendPacket(uint32_t packet_no) {
XModem.checksumBuf = 0x00;
XModem.crcBuf = 0x00;
+ uint8_t packet_num = packet_no;
+
// Try to send packet, so header first
ZigbeeSerial->write(XM_SOH);
- ZigbeeSerial->write(packet_no);
- ZigbeeSerial->write(~packet_no);
+ ZigbeeSerial->write(packet_num);
+ ZigbeeSerial->write(~packet_num);
for (uint32_t i = 0; i < XMODEM_PACKET_SIZE; i++) {
in_char = ZigbeeUploadFlashRead();
XModemOutputByte(in_char);
@@ -220,6 +234,13 @@ bool ZigbeeUploadBootloaderPrompt(void) {
yield();
char bootloader_byte = ZigbeeSerial->read();
+ // [cr][lf]
+ // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf]
+ // 1. upload gbl[cr][lf]
+ // 2. run[cr][lf]
+ // 3. ebl info[cr][lf]
+ // BL >
+
if (((uint8_t)bootloader_byte >=0) && (buf_len < sizeof(serial_buffer) -2)) {
serial_buffer[buf_len++] = bootloader_byte;
}
@@ -332,17 +353,18 @@ bool ZigbeeUploadXmodem(void) {
}
} else {
// After the bootloader receives a carriage return from the target device, it displays a menu
- // Gecko Bootloader v1.A.3
- // 1. upload gbl
- // 2. run
- // 3. ebl info
+ // [cr][lf]
+ // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf]
+ // 1. upload gbl[cr][lf]
+ // 2. run[cr][lf]
+ // 3. ebl info[cr][lf]
// BL >
if (ZigbeeUploadBootloaderPrompt()) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync"));
ZigbeeSerial->flush();
ZigbeeSerial->write('1'); // upload ebl
if (TasmotaGlobal.sleep > 0) {
- TasmotaGlobal.sleep = 1; // Speed up loop used for xmodem upload
+ TasmotaGlobal.sleep = 1; // Speed up loop used for xmodem upload
}
XModem.timeout = millis() + (XMODEM_SYNC_TIMEOUT * 1000);
ZbUpload.ota_step = ZBU_SYNC;
@@ -358,11 +380,17 @@ bool ZigbeeUploadXmodem(void) {
}
// Wait for either C or NACK as a sync packet. Determines protocol details, checksum algorithm.
if (ZigbeeSerial->available()) {
+ // [cr][lf]
+ // begin upload[cr][lf]
+ // C
char xmodem_sync = ZigbeeSerial->read();
+
+// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd2 0x%02X"), xmodem_sync);
+
if (('C' == xmodem_sync) || (XM_NAK == xmodem_sync)) {
// Determine which checksum algorithm to use
XModem.oldChecksum = (xmodem_sync == XM_NAK);
- XModem.packetNo = 1;
+ XModem.packet_no = 1;
ZbUpload.byte_counter = 0;
ZbUpload.ota_step = ZBU_UPLOAD;
AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init packet send"));
@@ -372,12 +400,15 @@ bool ZigbeeUploadXmodem(void) {
}
case ZBU_UPLOAD: { // *** Handle file upload using XModem - upload
if (ZigbeeUploadAvailable()) {
- if (!XModemSendPacket(XModem.packetNo)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Packet send failed"));
+ if (ZbUpload.byte_counter && !(ZbUpload.byte_counter % 10240)) { // Show progress every 10kB
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Progress %d kB"), ZbUpload.byte_counter / 1024);
+ }
+ if (!XModemSendPacket(XModem.packet_no)) {
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Packet %d send failed"), XModem.packet_no);
ZbUpload.ota_step = ZBU_ERROR;
return true;
}
- XModem.packetNo++;
+ XModem.packet_no++;
} else {
// Once the last block is ACKed by the target, the transfer should be finalized by an
// EOT (ASCII 0x04) packet from the source. Once this packet is confirmed via XModem ACK
@@ -385,6 +416,7 @@ bool ZigbeeUploadXmodem(void) {
ZigbeeSerial->write(XM_EOT);
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EOT ACK
ZbUpload.ota_step = ZBU_EOT;
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Transferred %d bytes"), ZbUpload.ota_size);
}
break;
}
@@ -401,7 +433,12 @@ bool ZigbeeUploadXmodem(void) {
}
if (ZigbeeSerial->available()) {
char xmodem_ack = XModemWaitACK();
- if (XM_ACK == xmodem_ack) {
+ if (XM_CAN == xmodem_ack) {
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Transfer invalid"));
+ ZbUpload.ota_step = ZBU_ERROR;
+ return true;
+ }
+ else if (XM_ACK == xmodem_ack) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_SUCCESSFUL));
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EBL prompt
ZbUpload.byte_counter = 0;
@@ -418,28 +455,33 @@ bool ZigbeeUploadXmodem(void) {
} else {
// After an image successfully uploads, the XModem transaction completes and the bootloader displays
// ‘Serial upload complete’ before redisplaying the menu
- // Serial upload complete
- // Gecko Bootloader v1.A.3
- // 1. upload gbl
- // 2. run
- // 3. ebl info
+ //
+ // [cr][lf]
+ // Serial upload complete[cr][lf]
+ // [cr][lf]
+ // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf]
+ // 1. upload gbl[cr][lf]
+ // 2. run[cr][lf]
+ // 3. ebl info[cr][lf]
// BL >
if (ZigbeeUploadBootloaderPrompt()) {
ZbUpload.state = ZBU_COMPLETE;
ZbUpload.ota_step = ZBU_DONE;
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING));
}
}
break;
}
case ZBU_ERROR:
ZbUpload.state = ZBU_ERROR;
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_FAILED));
case ZBU_DONE: { // *** Clean up and restart to disable bootloader and use new firmware
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING));
ZigbeeUploadSetBootloader(1); // Disable bootloader and reset MCU - should happen at restart
if (1 == TasmotaGlobal.sleep) {
TasmotaGlobal.sleep = Settings.sleep; // Restore loop sleep
}
// TasmotaGlobal.restart_flag = 2; // Restart to disable bootloader and use new firmware
+ if (ZbUpload.buffer) { free(ZbUpload.buffer); }
ZbUpload.ota_step = ZBU_FINISH; // Never return to zero without a restart to get a sane Zigbee environment
break;
}
@@ -472,10 +514,6 @@ void ZigbeeUploadStep1Done(uint32_t data, size_t size) {
ZbUpload.state = ZBU_UPLOAD; // Signal upload done and ready for delayed upload to MCU EFR32
}
-bool ZigbeeUploadFinish(void) {
- return (ZBU_FINISH == ZbUpload.ota_step);
-}
-
#define WEB_HANDLE_ZIGBEE_XFER "zx"
const char HTTP_SCRIPT_XFER_STATE[] PROGMEM =
@@ -500,12 +538,12 @@ void HandleZigbeeXfer(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
if (Webserver->hasArg("z")) { // Status refresh requested
- WSContentBegin(200, CT_PLAIN);
- WSContentSend_P(PSTR("%d"), ZbUpload.state);
- WSContentEnd();
if (ZBU_ERROR == ZbUpload.state) {
Web.upload_error = 7; // Upload aborted (xmodem transfer failed)
}
+ WSContentBegin(200, CT_PLAIN);
+ WSContentSend_P(PSTR("%d"), ZbUpload.state);
+ WSContentEnd();
return;
}
From 865b25dff9dd4230b1d69fe3d6567c8e10a98952 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 19 Jan 2021 12:23:01 +0100
Subject: [PATCH 12/19] Remove disable messages
---
tasmota/xdrv_01_webserver.ino | 4 ----
1 file changed, 4 deletions(-)
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index b43bc573f..33d4ebce8 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2148,8 +2148,6 @@ void HandleInformation(void)
#ifdef USE_EMULATION
WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions));
-#else
- WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED));
#endif // USE_EMULATION
#ifdef USE_DISCOVERY
@@ -2161,8 +2159,6 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED));
#endif // WEBSERVER_ADVERTISE
}
-#else
- WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED));
#endif // USE_DISCOVERY
WSContentSend_P(PSTR("}1}2 ")); // Empty line
From 9a11d3613bc87c1ba9be7ed56ee3615f794348dc Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 19 Jan 2021 12:26:52 +0100
Subject: [PATCH 13/19] Remove disable messages
---
tasmota/xdrv_01_webserver.ino | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index 33d4ebce8..26a720a2d 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -2127,7 +2127,7 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port);
#ifdef USE_MQTT_TLS
WSContentSend_P(PSTR("}1" D_MQTT_TLS_ENABLE "}2%s"), Settings.flag4.mqtt_tls ? PSTR(D_ENABLED) : PSTR(D_DISABLED));
-#endif // USE_MQTT_TLS
+#endif // USE_MQTT_TLS
WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER));
WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), TasmotaGlobal.mqtt_client);
WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC));
@@ -2144,12 +2144,13 @@ void HandleInformation(void)
} else {
WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED));
}
- WSContentSend_P(PSTR("}1}2 ")); // Empty line
+#if defined(USE_EMULATION) || defined(USE_DISCOVERY)
+ WSContentSend_P(PSTR("}1}2 ")); // Empty line
+#endif // USE_EMULATION or USE_DISCOVERY
#ifdef USE_EMULATION
WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions));
-#endif // USE_EMULATION
-
+#endif // USE_EMULATION
#ifdef USE_DISCOVERY
WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); // SetOption55 - Control mDNS service
if (Settings.flag3.mdns_enabled) { // SetOption55 - Control mDNS service
@@ -2157,9 +2158,9 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER));
#else
WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED));
-#endif // WEBSERVER_ADVERTISE
+#endif // WEBSERVER_ADVERTISE
}
-#endif // USE_DISCOVERY
+#endif // USE_DISCOVERY
WSContentSend_P(PSTR("}1}2 ")); // Empty line
WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId());
From 6fe37d148a8b5a8603855e9bb555f5ba300b5095 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue, 19 Jan 2021 12:34:36 +0100
Subject: [PATCH 14/19] use esp32-1.0.5-rc6.zip for ESP32
---
platformio_tasmota32.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index 717ca03a5..7053ac3af 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -92,7 +92,7 @@ build_flags = ${esp_defaults.build_flags}
[core32]
platform = espressif32 @ 2.1.0
-platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc4/esp32-1.0.5-rc4.zip
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip
platformio/tool-mklittlefs @ ~1.203.200522
build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags}
From cba145136e58906b87e98c2510549ba6c3eb6ee1 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue, 19 Jan 2021 12:35:33 +0100
Subject: [PATCH 15/19] Update PULL_REQUEST_TEMPLATE.md
---
.github/PULL_REQUEST_TEMPLATE.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index e542181b9..0de14bbf8 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -7,7 +7,7 @@
- [ ] Only relevant files were touched
- [ ] Only one feature/fix was added per PR and the code change compiles without warnings
- [ ] The code change is tested and works on Tasmota core ESP8266 V.2.7.4.9
- - [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc4
+ - [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc6
- [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla).
_NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_
From f607152aa17cd5aa2e472b35239edc160b6474da Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Tue, 19 Jan 2021 13:37:53 +0100
Subject: [PATCH 16/19] Zigbee add EZSP 6.7.8 as Release Candidate firmware
---
.../{ => archive}/ncp-uart-sw_6.5.5_115200.ota | Bin
.../ncp-uart-sw_6.7.8_115200.ota | Bin 0 -> 190756 bytes
tools/fw_SonoffZigbeeBridge_ezsp/readme.txt | 7 +++++--
3 files changed, 5 insertions(+), 2 deletions(-)
rename tools/fw_SonoffZigbeeBridge_ezsp/{ => archive}/ncp-uart-sw_6.5.5_115200.ota (100%)
create mode 100644 tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.7.8_115200.ota
diff --git a/tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.5.5_115200.ota b/tools/fw_SonoffZigbeeBridge_ezsp/archive/ncp-uart-sw_6.5.5_115200.ota
similarity index 100%
rename from tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.5.5_115200.ota
rename to tools/fw_SonoffZigbeeBridge_ezsp/archive/ncp-uart-sw_6.5.5_115200.ota
diff --git a/tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.7.8_115200.ota b/tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.7.8_115200.ota
new file mode 100644
index 0000000000000000000000000000000000000000..9bc81b544d7c55f82768b8088df51bed51e41052
GIT binary patch
literal 190756
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%0AUWU5O5bd-u5
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%0A-Q>YP
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