From 95f76d623f17d67c55382a78bc5fb4a84ab41ba1 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 21 Jan 2024 14:29:44 +0100 Subject: [PATCH] Changed command ``TimedPower`` refactored from String to LList --- CHANGELOG.md | 3 +- RELEASENOTES.md | 1 + tasmota/include/tasmota.h | 1 - tasmota/tasmota.ino | 7 -- tasmota/tasmota_support/support_command.ino | 88 +++++++++------------ 5 files changed, 40 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d755c2682..581a84b33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ All notable changes to this project will be documented in this file. - Berry solidification of strings longer than 255 bytes (#20529) - Berry syntax coloring for Notepad++ by FransO (#20541) - Berry/Zigbee web hook per device for customized status display (#20542) -- Zigbee ``ZbEmulation`` to selectively exclude some devices from Hue/Alexa emulation +- Zigbee ``ZbEmulation`` to selectively exclude some devices from Hue/Alexa emulation (#20552) ### Breaking Changed @@ -28,6 +28,7 @@ All notable changes to this project will be documented in this file. - ESP8266 platform update from 2024.01.00 to 2024.01.01 (#20539) - ESP8266 Framework (Arduino Core) from v2.7.5 to v2.7.6 (#20539) - Refactor Pio filesystem download script (#20544) +- Command ``TimedPower`` refactored from String to LList ### Fixed - Scripter memory leak in `>w x` (#20473) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 07c154377..978956b2f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -149,6 +149,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Berry solidification of strings longer than 255 bytes [#20529](https://github.com/arendst/Tasmota/issues/20529) - Berry syntax coloring for Notepad++ by FransO [#20541](https://github.com/arendst/Tasmota/issues/20541) - Berry/Zigbee web hook per device for customized status display [#20542](https://github.com/arendst/Tasmota/issues/20542) +- Zigbee ``ZbEmulation`` to selectively exclude some devices from Hue/Alexa emulation [#20552](https://github.com/arendst/Tasmota/issues/20552) - LVGL `lv.str_arr` [#20480](https://github.com/arendst/Tasmota/issues/20480) - LVGL option to add `lv.keyboard` extra widget [#20496](https://github.com/arendst/Tasmota/issues/20496) - HASPmota `haspmota.page_show()` to change page [#20333](https://github.com/arendst/Tasmota/issues/20333) diff --git a/tasmota/include/tasmota.h b/tasmota/include/tasmota.h index 3685fbc7e..bb23f5c1e 100644 --- a/tasmota/include/tasmota.h +++ b/tasmota/include/tasmota.h @@ -221,7 +221,6 @@ const uint16_t MAX_LOGSZ = 700; // Max number of characters in log l const uint8_t SENSOR_MAX_MISS = 5; // Max number of missed sensor reads before deciding it's offline const uint32_t MIN_BACKLOG_DELAY = 200; // Minimal backlog delay in mSeconds -const uint8_t MAX_TIMED_CMND = 16; // Max number of timed commands const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 4b9643f91..16f6c3a1e 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -294,11 +294,6 @@ HardwareSerial TasConsole = Serial; // Only serial interface char EmptyStr[1] = { 0 }; // Provide a pointer destination to an empty char string -typedef struct { - uint32_t time; - String command; -} tTimedCmnd; - struct TasmotaGlobal_t { uint32_t global_update; // Timestamp of last global temperature and humidity update uint32_t baudrate; // Current Serial baudrate @@ -413,8 +408,6 @@ struct TasmotaGlobal_t { uint8_t pwm_dimmer_led_bri; // Adjusted brightness LED level #endif // USE_PWM_DIMMER - tTimedCmnd timed_cmnd[MAX_TIMED_CMND]; // Timed command buffer - #ifdef MQTT_DATA_STRING String mqtt_data; // Buffer filled by Response functions #else diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 197568e5a..d763a4130 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -655,44 +655,38 @@ void CmndPower(void) /********************************************************************************************/ +typedef struct { + uint32_t time; + char* command; +} tTimedCmnd; +LList timed_cmnd; // Timed command buffer + bool SetTimedCmnd(uint32_t time, const char *command) { - uint32_t now = millis(); - // Try to use the same slot if command is already present - for (uint32_t i = 0; i < MAX_TIMED_CMND; i++) { - if (TasmotaGlobal.timed_cmnd[i].time != 0) { - if (!strcmp(command, TasmotaGlobal.timed_cmnd[i].command.c_str())) { - // Stored command already present - TasmotaGlobal.timed_cmnd[i].time = now + time; - if (0 == TasmotaGlobal.timed_cmnd[i].time) { // Skip empty slot flag - TasmotaGlobal.timed_cmnd[i].time++; - } - return true; - } + // Remove command if present + for (auto &elem : timed_cmnd) { + if (strcmp(command, elem.command) == 0) { // Equal + free(elem.command); + timed_cmnd.remove(&elem); + break; } } - // Try to find an empty slot and add command - for (uint32_t i = 0; i < MAX_TIMED_CMND; i++) { - if (0 == TasmotaGlobal.timed_cmnd[i].time) { // Free slot - TasmotaGlobal.timed_cmnd[i].command = command; - TasmotaGlobal.timed_cmnd[i].time = now + time; - if (0 == TasmotaGlobal.timed_cmnd[i].time) { // Skip empty slot flag - TasmotaGlobal.timed_cmnd[i].time++; - } - return true; - } + // Add command + char* cmnd = (char*)malloc(strlen(command) +1); + if (cmnd) { + strcpy(cmnd, command); + tTimedCmnd &elem = timed_cmnd.addToLast(); + elem.time = millis() + time; + elem.command = cmnd; + return true; } - AddLog(LOG_LEVEL_INFO, PSTR("TIM: No more timer slots left")); return false; } void ResetTimedCmnd(const char *command) { - for (uint32_t i = 0; i < MAX_TIMED_CMND; i++) { - if (TasmotaGlobal.timed_cmnd[i].time != 0) { - if (!strncmp(command, TasmotaGlobal.timed_cmnd[i].command.c_str(), strlen(command))) { - // Stored command starts with command - TasmotaGlobal.timed_cmnd[i].time = 0; - TasmotaGlobal.timed_cmnd[i].command = (const char*) nullptr; // Force deallocation of the String internal memory - } + for (auto &elem : timed_cmnd) { + if (strncmp(command, elem.command, strlen(command)) == 0) { // StartsWith + free(elem.command); + timed_cmnd.remove(&elem); } } } @@ -701,31 +695,23 @@ void ShowTimedCmnd(const char *command) { bool found = false; uint32_t now = millis(); Response_P(PSTR("{\"%s\":"), XdrvMailbox.command); - for (uint32_t i = 0; i < MAX_TIMED_CMND; i++) { - if (TasmotaGlobal.timed_cmnd[i].time != 0) { - if (!strncmp(command, TasmotaGlobal.timed_cmnd[i].command.c_str(), strlen(command))) { - // Stored command starts with command - ResponseAppend_P(PSTR("%s"), (found) ? "," : "["); - found = true; - ResponseAppend_P(PSTR("{\"" D_JSON_REMAINING "\":%d,\"" D_JSON_COMMAND "\":\"%s\"}"), TasmotaGlobal.timed_cmnd[i].time - now, TasmotaGlobal.timed_cmnd[i].command.c_str()); - } + for (auto &elem : timed_cmnd) { + if (strncmp(command, elem.command, strlen(command)) == 0) { // StartsWith + ResponseAppend_P(PSTR("%s{\"" D_JSON_REMAINING "\":%d,\"" D_JSON_COMMAND "\":\"%s\"}"), + (found) ? "," : "[", elem.time - now, elem.command); + found = true; } } - if (found) { - ResponseAppend_P(PSTR("]}")); - } else { - ResponseAppend_P(PSTR("\"" D_JSON_EMPTY "\"}")); - } + ResponseAppend_P((found) ? PSTR("]}") : PSTR("\"" D_JSON_EMPTY "\"}")); } void LoopTimedCmnd(void) { - uint32_t now = millis(); - for (uint32_t i = 0; i < MAX_TIMED_CMND; i++) { - if ((TasmotaGlobal.timed_cmnd[i].time > 0) && (now > TasmotaGlobal.timed_cmnd[i].time)) { - TasmotaGlobal.timed_cmnd[i].time = 0; - String cmd = TasmotaGlobal.timed_cmnd[i].command; - TasmotaGlobal.timed_cmnd[i].command = (const char*) nullptr; // Force deallocation of the String internal memory - ExecuteCommand((char*)cmd.c_str(), SRC_TIMER); + for (auto &elem : timed_cmnd) { + if (TimeReached(elem.time)) { + char* command = elem.command; + timed_cmnd.remove(&elem); + ExecuteCommand(command, SRC_TIMER); + free(command); } } } @@ -763,7 +749,7 @@ void CmndTimedPower(void) { const uint8_t end_state[] = { POWER_ON, POWER_OFF, POWER_TOGGLE, POWER_BLINK_STOP }; char cmnd[CMDSZ]; snprintf_P(cmnd, sizeof(cmnd), PSTR(D_CMND_POWER "%d %d"), XdrvMailbox.index, end_state[start_state]); - if (SetTimedCmnd(time, cmnd)) { // Skip if no more timers left (MAX_TIMED_CMND) + if (SetTimedCmnd(time, cmnd)) { // Skip if no more room for timers XdrvMailbox.payload = start_state; CmndPower(); }