diff --git a/CHANGELOG.md b/CHANGELOG.md index 7899108dc..3b4a11279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to this project will be documented in this file. - Support for Frysk language translations by Christiaan Heerze - ESP8266 Fallback to ``*.bin.gz`` binary when OTA upload of ``*.bin`` binary fails - Berry language improved Tasmota integration -- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete`` and ``UfsRename`` +- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun`` - Basic support for filesystem ``autoexec.bat`` ### Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f68cf0a34..efb72842c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -95,7 +95,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota - Commands ``ZbNameKey``, ``ZbDeviceTopic``, ``ZbNoPrefix``, ``ZbEndpointSuffix``, ``ZbNoAutoBind`` and ``ZbNameTopic`` as synonyms for ``SetOption83, 89, 100, 101, 110`` and ``112`` - Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119`` - Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111`` -- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete`` and ``UfsRename`` +- Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun`` - Basic support for filesystem ``autoexec.bat`` - Milliseconds to console output [#10152](https://github.com/arendst/Tasmota/issues/10152) - Gpio ``Option_a1`` enabling PWM2 high impedance if powered off as used by Wyze bulbs [#10196](https://github.com/arendst/Tasmota/issues/10196) diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index 613fd019f..9ac9adeec 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -370,10 +370,10 @@ enum DevGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER, - SRC_THERMOSTAT, SRC_CHAT, SRC_TCL, SRC_BERRY, SRC_AUTOEXEC, SRC_MAX }; + SRC_THERMOSTAT, SRC_CHAT, SRC_TCL, SRC_BERRY, SRC_FILE, SRC_MAX }; const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|" "Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter|" - "Thermostat|Chat|TCL|Berry|Autoexec"; + "Thermostat|Chat|TCL|Berry|File"; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index c0b7b4669..4ebe0b647 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -426,6 +426,9 @@ void Scheduler(void) { DeviceGroupsLoop(); #endif // USE_DEVICE_GROUPS BacklogLoop(); +#ifdef USE_UFILESYS + FileRunLoop(); +#endif // USE_UFILESYS static uint32_t state_50msecond = 0; // State 50msecond timer if (TimeReached(state_50msecond)) { diff --git a/tasmota/xdrv_50_filesystem.ino b/tasmota/xdrv_50_filesystem.ino index 856150869..f79da6d17 100644 --- a/tasmota/xdrv_50_filesystem.ino +++ b/tasmota/xdrv_50_filesystem.ino @@ -85,8 +85,10 @@ uint8_t ufs_type; uint8_t ffs_type; struct { + char run_file[48]; + int run_file_pos = -1; + bool run_file_mutex = 0; bool download_busy; - bool autoexec = false; } UfsData; /*********************************************************************************************/ @@ -127,8 +129,9 @@ void UfsInitOnce(void) { dfsp = ffsp; } -// actually this inits flash file only +// Called from tasmota.ino at restart. This inits flash file only void UfsInit(void) { + UfsData.run_file_pos = -1; UfsInitOnce(); if (ufs_type) { AddLog(LOG_LEVEL_INFO, PSTR("UFS: FlashFS mounted with %d kB free"), UfsInfo(1, 0)); @@ -350,37 +353,59 @@ bool TfsRenameFile(const char *fname1, const char *fname2) { * Autoexec support \*********************************************************************************************/ -void UfsAutoexec(void) { - if (!ffs_type) { return; } - File file = ffsp->open(TASM_FILE_AUTOEXEC, "r"); - if (!file) { return; } - char cmd_line[512]; - while (file.available()) { - uint16_t index = 0; +void FileRunLoop(void) { + if (UfsData.run_file_pos < 0) { return; } + if (!ffs_type) { return; } + + if (strlen(UfsData.run_file) && !UfsData.run_file_mutex) { + File file = ffsp->open(UfsData.run_file, "r"); + if (!file) { return; } + if (!file.seek(UfsData.run_file_pos)) { return; } + + UfsData.run_file_mutex = true; + + char cmd_line[512]; + cmd_line[0] = '\0'; // Clear in case of re-entry while (file.available()) { - uint8_t buf[1]; - file.read(buf, 1); - if ((buf[0] == '\n') || (buf[0] == '\r')) { - // Line terminated with linefeed or carriage return + uint16_t index = 0; + while (file.available()) { + uint8_t buf[1]; + file.read(buf, 1); + if ((buf[0] == '\n') || (buf[0] == '\r')) { + break; // Line terminated with linefeed or carriage return + } + else if (index && (buf[0] == ';')) { + break; // End of multi command line + } + else if ((0 == index) && isspace(buf[0])) { + // Skip leading spaces (' ','\t','\n','\v','\f','\r') + } + else if (index < sizeof(cmd_line) - 2) { + cmd_line[index++] = buf[0]; + } + } + if ((index > 0) && (index < sizeof(cmd_line) - 1) && (cmd_line[0] != ';')) { + // No comment so try to execute command + cmd_line[index] = '\0'; break; } - else if ((0 == index) && isspace(buf[0])) { - // Skip leading spaces (' ','\t','\n','\v','\f','\r') - } - else if (index < sizeof(cmd_line) - 2) { - cmd_line[index++] = buf[0]; - } } - if ((index > 0) && (index < sizeof(cmd_line) - 1) && (cmd_line[0] != ';')) { - // No comment so try to execute command - cmd_line[index] = 0; - ExecuteCommand(cmd_line, SRC_AUTOEXEC); + UfsData.run_file_pos = (file.available()) ? file.position() : -1; + file.close(); + if (strlen(cmd_line)) { + ExecuteCommand(cmd_line, SRC_FILE); } - delay(0); - } - file.close(); + UfsData.run_file_mutex = false; + } +} + +void UfsAutoexec(void) { + if (TfsFileExists(TASM_FILE_AUTOEXEC)) { + snprintf(UfsData.run_file, sizeof(UfsData.run_file), TASM_FILE_AUTOEXEC); + UfsData.run_file_pos = 0; + } } /*********************************************************************************************\ @@ -388,10 +413,10 @@ void UfsAutoexec(void) { \*********************************************************************************************/ const char kUFSCommands[] PROGMEM = "Ufs|" // Prefix - "|Type|Size|Free|Delete|Rename"; + "|Type|Size|Free|Delete|Rename|Run"; void (* const kUFSCommand[])(void) PROGMEM = { - &UFSInfo, &UFSType, &UFSSize, &UFSFree, &UFSDelete, &UFSRename}; + &UFSInfo, &UFSType, &UFSSize, &UFSFree, &UFSDelete, &UFSRename, &UFSRun}; void UFSInfo(void) { Response_P(PSTR("{\"Ufs\":{\"Type\":%d,\"Size\":%d,\"Free\":%d}"), ufs_type, UfsInfo(0, 0), UfsInfo(1, 0)); @@ -465,6 +490,18 @@ void UFSRename(void) { } } +void UFSRun(void) { + if (XdrvMailbox.data_len > 0) { + if ((UfsData.run_file_pos < 0) && TfsFileExists(XdrvMailbox.data)) { + snprintf(UfsData.run_file, sizeof(UfsData.run_file), XdrvMailbox.data); + UfsData.run_file_pos = 0; + ResponseClear(); + } else { + ResponseCmndChar(PSTR(D_JSON_FAILED)); + } + } +} + /*********************************************************************************************\ * Web support \*********************************************************************************************/ @@ -824,16 +861,8 @@ bool Xdrv50(uint8_t function) { UfsCheckSDCardInit(); break; #endif // USE_SDCARD - case FUNC_EVERY_SECOND: - if (UfsData.autoexec) { - // Safe to execute autoexec commands here - UfsData.autoexec = false; - if (!TasmotaGlobal.no_autoexec) { UfsAutoexec(); } - } - break; case FUNC_MQTT_INIT: - // Do not execute autoexec commands here - UfsData.autoexec = true; + if (!TasmotaGlobal.no_autoexec) { UfsAutoexec(); } break; case FUNC_COMMAND: result = DecodeCommand(kUFSCommands, kUFSCommand);