diff --git a/interface/src/AuthenticatedRouting.tsx b/interface/src/AuthenticatedRouting.tsx index 43f03d00a..7fd3701b1 100644 --- a/interface/src/AuthenticatedRouting.tsx +++ b/interface/src/AuthenticatedRouting.tsx @@ -58,7 +58,7 @@ const AuthenticatedRouting = () => { } /> } /> } /> - } /> + } /> } /> } /> diff --git a/interface/src/app/main/Customizations.tsx b/interface/src/app/main/Customizations.tsx index b8f3aaef0..3fcb102b0 100644 --- a/interface/src/app/main/Customizations.tsx +++ b/interface/src/app/main/Customizations.tsx @@ -38,7 +38,7 @@ import { import { useTheme } from '@table-library/react-table-library/theme'; import { dialogStyle } from 'CustomTheme'; import { useRequest } from 'alova/client'; -import RestartMonitor from 'app/status/RestartMonitor'; +import SystemMonitor from 'app/status/SystemMonitor'; import { BlockNavigation, ButtonRow, @@ -737,7 +737,7 @@ const Customizations = () => { return ( {blocker ? : null} - {restarting ? : renderContent()} + {restarting ? : renderContent()} {selectedDeviceEntity && ( { return ( {blocker ? : null} - {restarting ? : content()} + {restarting ? : content()} ); }; diff --git a/interface/src/app/settings/DownloadUpload.tsx b/interface/src/app/settings/DownloadUpload.tsx index 7bb1d8dca..b32850e1d 100644 --- a/interface/src/app/settings/DownloadUpload.tsx +++ b/interface/src/app/settings/DownloadUpload.tsx @@ -9,7 +9,7 @@ import { API, callAction } from 'api/app'; import { useRequest } from 'alova/client'; import type { APIcall } from 'app/main/types'; -import RestartMonitor from 'app/status/RestartMonitor'; +import SystemMonitor from 'app/status/SystemMonitor'; import { FormLoader, SectionContent, @@ -123,7 +123,7 @@ const DownloadUpload = () => { }; return ( - {restarting ? : content()} + {restarting ? : content()} ); }; diff --git a/interface/src/app/settings/Settings.tsx b/interface/src/app/settings/Settings.tsx index 7aba45df7..96d25853b 100644 --- a/interface/src/app/settings/Settings.tsx +++ b/interface/src/app/settings/Settings.tsx @@ -151,7 +151,7 @@ const Settings = () => { bgcolor="#5d89f7" label={LL.DOWNLOAD_UPLOAD()} text={LL.DOWNLOAD_UPLOAD_1()} - to="upload" + to="downloadUpload" /> diff --git a/interface/src/app/settings/Version.tsx b/interface/src/app/settings/Version.tsx index c17804e50..c3c954b5f 100644 --- a/interface/src/app/settings/Version.tsx +++ b/interface/src/app/settings/Version.tsx @@ -24,7 +24,7 @@ import { getDevVersion, getStableVersion } from 'api/system'; import { dialogStyle } from 'CustomTheme'; import { useRequest } from 'alova/client'; -import RestartMonitor from 'app/status/RestartMonitor'; +import SystemMonitor from 'app/status/SystemMonitor'; import { FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; @@ -224,7 +224,7 @@ const Version = () => { <> - Firmware Version + Firmware {LL.VERSION()} { }; return ( - {restarting ? : content()} + {restarting ? : content()} ); }; diff --git a/interface/src/app/settings/network/NetworkSettings.tsx b/interface/src/app/settings/network/NetworkSettings.tsx index c521b116e..65283817d 100644 --- a/interface/src/app/settings/network/NetworkSettings.tsx +++ b/interface/src/app/settings/network/NetworkSettings.tsx @@ -43,7 +43,7 @@ import { updateValueDirty, useRest } from 'utils'; import { validate } from 'validators'; import { createNetworkSettingsValidator } from 'validators/network'; -import RestartMonitor from '../../status/RestartMonitor'; +import SystemMonitor from '../../status/SystemMonitor'; import { WiFiConnectionContext } from './WiFiConnectionContext'; import { isNetworkOpen, networkSecurityMode } from './WiFiNetworkSelector'; @@ -400,7 +400,7 @@ const NetworkSettings = () => { return ( {blocker ? : null} - {restarting ? : content()} + {restarting ? : content()} ); }; diff --git a/interface/src/app/status/RestartMonitor.tsx b/interface/src/app/status/RestartMonitor.tsx deleted file mode 100644 index 7006776b0..000000000 --- a/interface/src/app/status/RestartMonitor.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { useState } from 'react'; - -import { - Box, - CircularProgress, - Dialog, - DialogContent, - Typography -} from '@mui/material'; - -import { readSystemStatus } from 'api/system'; - -import { dialogStyle } from 'CustomTheme'; -import { useRequest } from 'alova/client'; -import MessageBox from 'components/MessageBox'; -import { useI18nContext } from 'i18n/i18n-react'; -import { useInterval } from 'utils'; - -const RestartMonitor = () => { - const [errorMessage, setErrorMessage] = useState(); - - const { LL } = useI18nContext(); - - let count = 0; - - const { data, send } = useRequest(readSystemStatus, { - force: true, - initialData: { status: 'Getting ready...' }, - async middleware(_, next) { - if (count++ >= 1) { - // skip first request (1 second) to allow AsyncWS to send its response - await next(); - } - } - }) - .onSuccess((event) => { - if (event.data.status === 'ready' || event.data.status === undefined) { - document.location.href = '/'; - } - }) - .onError((error) => { - setErrorMessage(error.message); - }); - - useInterval(() => { - void send(); - }, 1000); // check every second - - return ( - - - - - {data?.status === 'uploading' - ? LL.WAIT_FIRMWARE() - : data?.status === 'restarting' - ? LL.APPLICATION_RESTARTING() - : data?.status === 'ready' - ? LL.RESTARTING_PRE() - : LL.RESTARTING_POST()} - … - - - {LL.PLEASE_WAIT()} - - - {errorMessage ? ( - - ) : ( - - - - )} - - - - ); -}; - -export default RestartMonitor; diff --git a/interface/src/app/status/Status.tsx b/interface/src/app/status/Status.tsx index 97613c837..cfc7c93f2 100644 --- a/interface/src/app/status/Status.tsx +++ b/interface/src/app/status/Status.tsx @@ -40,7 +40,7 @@ import { NTPSyncStatus, NetworkConnectionStatus } from 'types'; import { useInterval } from 'utils'; import { formatDateTime } from 'utils/time'; -import RestartMonitor from './RestartMonitor'; +import SystemMonitor from './SystemMonitor'; const SystemStatus = () => { const { LL } = useI18nContext(); @@ -349,7 +349,7 @@ const SystemStatus = () => { }; return ( - {restarting ? : content()} + {restarting ? : content()} ); }; diff --git a/interface/src/app/status/SystemMonitor.tsx b/interface/src/app/status/SystemMonitor.tsx new file mode 100644 index 000000000..3fc607bb1 --- /dev/null +++ b/interface/src/app/status/SystemMonitor.tsx @@ -0,0 +1,122 @@ +import { useState } from 'react'; + +import CancelIcon from '@mui/icons-material/Cancel'; +import { + Box, + Button, + CircularProgress, + Dialog, + DialogContent, + Typography +} from '@mui/material'; + +import { callAction } from 'api/app'; +import { readSystemStatus } from 'api/system'; + +import { dialogStyle } from 'CustomTheme'; +import { useRequest } from 'alova/client'; +import MessageBox from 'components/MessageBox'; +import { useI18nContext } from 'i18n/i18n-react'; +import { SystemStatusCodes } from 'types'; +import { useInterval } from 'utils'; + +const SystemMonitor = () => { + const [errorMessage, setErrorMessage] = useState(); + + const { LL } = useI18nContext(); + + let count = 0; + + const { send: setSystemStatus } = useRequest( + (status: string) => callAction({ action: 'systemStatus', param: status }), + { + immediate: false + } + ); + + const { data, send } = useRequest(readSystemStatus, { + force: true, + async middleware(_, next) { + if (count++ >= 1) { + // skip first request (1 second) to allow AsyncWS to send its response + await next(); + } + } + }) + .onSuccess((event) => { + if ( + event.data.status === SystemStatusCodes.SYSTEM_STATUS_NORMAL || + event.data.status === undefined + ) { + document.location.href = '/'; + } else if ( + event.data.status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD + ) { + setErrorMessage('Please check system logs for possible causes'); + } + }) + .onError((error) => { + setErrorMessage(error.message); + }); + + useInterval(() => { + void send(); + }, 1000); // check every second + + const onCancel = () => { + setErrorMessage(undefined); + setSystemStatus(SystemStatusCodes.SYSTEM_STATUS_NORMAL as unknown as string); + document.location.href = '/'; + }; + + return ( + + + + + {data?.status === SystemStatusCodes.SYSTEM_STATUS_UPLOADING + ? LL.WAIT_FIRMWARE() + : data?.status === SystemStatusCodes.SYSTEM_STATUS_RESTARTING + ? LL.APPLICATION_RESTARTING() + : data?.status === SystemStatusCodes.SYSTEM_STATUS_NORMAL + ? LL.RESTARTING_PRE() + : data?.status === SystemStatusCodes.SYSTEM_STATUS_ERROR_UPLOAD + ? 'Upload Failed' + : LL.RESTARTING_POST()} + + + {errorMessage ? ( + + + + ) : ( + <> + + {LL.PLEASE_WAIT()}… + + + + + + )} + + + + ); +}; + +export default SystemMonitor; diff --git a/interface/src/types/system.ts b/interface/src/types/system.ts index 8b0023881..9b1f5dcf1 100644 --- a/interface/src/types/system.ts +++ b/interface/src/types/system.ts @@ -2,6 +2,17 @@ import type { busConnectionStatus } from 'app/main/types'; import type { NetworkConnectionStatus } from './network'; +export enum SystemStatusCodes { + SYSTEM_STATUS_NORMAL = 0, + SYSTEM_STATUS_PENDING_UPLOAD = 1, + SYSTEM_STATUS_UPLOADING = 2, + SYSTEM_STATUS_ERROR_UPLOAD = 3, + SYSTEM_STATUS_RESTARTING = 4, + SYSTEM_STATUS_ERROR = 5, + SYSTEM_STATUS_PENDING_RESTART = 6, + SYSTEM_STATUS_RESTART_REQUESTED = 7 +} + export interface SystemStatus { emsesp_version: string; bus_status: busConnectionStatus; @@ -41,7 +52,7 @@ export interface SystemStatus { model: string; has_loader: boolean; has_partition: boolean; - status: string; + status: number; // SystemStatusCodes which matches SYSTEM_STATUS in System.h temperature?: number; } diff --git a/mock-api/rest_server.ts b/mock-api/rest_server.ts index d7b3e7596..d7221e81e 100644 --- a/mock-api/rest_server.ts +++ b/mock-api/rest_server.ts @@ -70,7 +70,6 @@ let settings = { let system_status = { emsesp_version: 'XX.XX.XX', // defined later bus_status: 0, - // status: 2, uptime: 77186, bus_uptime: 77121, num_devices: 2, @@ -108,7 +107,8 @@ let system_status = { has_loader: true, model: '', // model: 'BBQKees Electronics EMS Gateway E32 V2 (E32 V2.0 P3/2024011)', - status: 'downloading' + // status: 0, + status: 3 }; let VERSION_IS_UPGRADEABLE: boolean; diff --git a/src/ESP32React/UploadFileService.cpp b/src/ESP32React/UploadFileService.cpp index 14d0ae020..3d82b458c 100644 --- a/src/ESP32React/UploadFileService.cpp +++ b/src/ESP32React/UploadFileService.cpp @@ -118,7 +118,8 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) { request->_tempFile.close(); // close the file handle as the upload is now done AsyncWebServerResponse * response = request->beginResponse(200); request->send(response); - emsesp::EMSESP::system_.restart_pending(true); // will be handled by the main loop. We use pending for the Web's RestartMonitor + emsesp::EMSESP::system_.systemStatus( + emsesp::SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); // will be handled by the main loop. We use pending for the Web's SystemMonitor return; } @@ -127,7 +128,8 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) { if (_is_firmware && !request->_tempObject) { AsyncWebServerResponse * response = request->beginResponse(200); request->send(response); - emsesp::EMSESP::system_.restart_pending(true); // will be handled by the main loop. We use pending for the Web's RestartMonitor + emsesp::EMSESP::system_.systemStatus( + emsesp::SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); // will be handled by the main loop. We use pending for the Web's SystemMonitor return; } diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index af90e7bd1..0f7dd6ccd 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -1734,29 +1734,34 @@ void EMSESP::loop() { esp32React.loop(); // web services system_.loop(); // does LED and checks system health, and syslog service - // if we're doing an OTA upload, skip everything except from console refresh - static bool upload_status = true; // ready for any OTA uploads - if (!system_.upload_isrunning()) { + // run the loop, unless we're in the middle of an OTA upload + if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_NORMAL) { // service loops webLogService.loop(); // log in Web UI rxservice_.loop(); // process any incoming Rx telegrams shower_.loop(); // check for shower on/off temperaturesensor_.loop(); // read sensor temperatures analogsensor_.loop(); // read analog sensor values - publish_all_loop(); // with HA messages in parts to avoid flooding the mqtt queue + publish_all_loop(); // with HA messages in parts to avoid flooding the MQTT queue mqtt_.loop(); // sends out anything in the MQTT queue webModulesService.loop(); // loop through the external library modules if (system_.PSram() == 0) { // run non-async if there is no PSRAM available webSchedulerService.loop(); } - scheduled_fetch_values(); // force a query on the EMS devices to fetch latest data at a set interval (1 min) + } - } else if (upload_status) { - // start an upload from a URL, if it exists. This is blocking. - if (!system_.uploadFirmwareURL()) { - upload_status = false; // abort all other attempts, until reset (after a restart normally) - system_.upload_isrunning(false); + if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD) { + // start an upload from a URL, assuming if the URL exists from a previous pass. + // Note this function is synchronous and blocking. + if (system_.uploadFirmwareURL()) { + // firmware has been uploaded, set status to uploading + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_UPLOADING); + } else { + // if it fails to pass, reset + Shell::loop_all(); // flush log buffers so latest error message are shown in console + system_.uploadFirmwareURL("reset"); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); } } diff --git a/src/core/system.cpp b/src/core/system.cpp index 18d00f245..ba9e3f646 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -83,8 +83,6 @@ uuid::log::Logger System::logger_{F_(system), uuid::log::Facility::KERN}; // init statics PButton System::myPButton_; -bool System::restart_requested_ = false; -bool System::restart_pending_ = false; bool System::test_set_all_active_ = false; uint32_t System::max_alloc_mem_; uint32_t System::heap_mem_; @@ -298,8 +296,7 @@ void System::system_restart(const char * partitionname) { } // make sure it's only executed once - restart_requested(false); - restart_pending(false); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL); store_nvs_values(); // save any NVS values Shell::loop_all(); // flush log to output @@ -565,27 +562,10 @@ void System::led_init(bool refresh) { } } -// returns true if OTA is uploading -bool System::upload_isrunning() { -#if defined(EMSESP_STANDALONE) - return false; -#else - return upload_isrunning_ || Update.isRunning(); -#endif -} - -void System::upload_isrunning(bool in_progress) { - // if we've just started an upload - if (!upload_isrunning_ && in_progress) { - EMSuart::stop(); - } - upload_isrunning_ = in_progress; -} - // checks system health and handles LED flashing wizardry void System::loop() { // check if we're supposed to do a reset/restart - if (restart_requested()) { + if (systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED) { system_restart(); } @@ -706,7 +686,7 @@ void System::heartbeat_json(JsonObject output) { #ifndef EMSESP_STANDALONE output["freemem"] = getHeapMem(); output["max_alloc"] = getMaxAllocMem(); -#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 output["temperature"] = temperature_; #endif #endif @@ -790,8 +770,8 @@ void System::system_check() { #ifndef EMSESP_STANDALONE #if defined(CONFIG_IDF_TARGET_ESP32) - uint8_t raw = temprature_sens_read(); - temperature_ = (raw - 32) / 1.8f; // convert to Celsius + uint8_t raw = temprature_sens_read(); + temperature_ = (raw - 32) / 1.8f; // convert to Celsius #elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 #if ESP_IDF_VERSION_MAJOR < 5 temp_sensor_read_celsius(&temperature_); @@ -1901,7 +1881,7 @@ bool System::load_board_profile(std::vector & data, const std::string & return true; } -// format command - factory reset, removing all config files +// format command - factory reset, removing all config fi`les bool System::command_format(const char * value, const int8_t id) { LOG_INFO("Removing all config files"); #ifndef EMSESP_STANDALONE @@ -1915,8 +1895,8 @@ bool System::command_format(const char * value, const int8_t id) { } #endif - EMSESP::system_.restart_requested(true); // will be handled by the main loop - + // restart will be handled by the main loop + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED); return true; } @@ -1926,11 +1906,13 @@ bool System::command_restart(const char * value, const int8_t id) { // if it has an id then it's a web call and we need to queue the restart // default id is -1 when calling /api/system/restart directly for example LOG_INFO("Preparing to restart system"); - EMSESP::system_.restart_pending(true); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); return true; } + LOG_INFO("Restarting system immediately"); - EMSESP::system_.restart_requested(true); // will be handled by the main loop + // restart will be handled by the main loop + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED); return true; } @@ -2022,25 +2004,38 @@ String System::getBBQKeesGatewayDetails() { // Stream from an URL and send straight to OTA uploader service. // -// This function needs to be called twice, once with a url to persist it, and second with no arguments to start the upload +// This function needs to be called twice, 1st pass once with a url to persist it, 2nd pass with no arguments to start the upload // This is to avoid timeouts in callback functions, like calling from a web hook. bool System::uploadFirmwareURL(const char * url) { #ifndef EMSESP_STANDALONE static String saved_url; - // if the URL is not empty, store the URL for the 2nd pass + // if the URL is not empty, store the URL for the 2nd pass and exit if (url && strlen(url) > 0) { + // if the passed URL is "reset" abort the current upload. This is called when an error happens during OTA + if (strncmp(url, "reset", 5) == 0) { + LOG_DEBUG("Firmware upload - resetting"); + saved_url.clear(); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_NORMAL); + return true; + } + + // given a URL to download from, save it saved_url = url; - EMSESP::system_.upload_isrunning(true); // tell EMS-ESP we're ready to start the uploading process + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_PENDING_UPLOAD); // we're ready to start the upload return true; } - // make sure we have a valid URL + // assumed we have a valid URL from the 1st pass if (saved_url.isEmpty()) { + LOG_ERROR("Firmware upload failed - invalid URL"); return false; // error } + LOG_INFO("Firmware downloading from %s", saved_url.c_str()); + Shell::loop_all(); // flush log buffers so latest messages are shown in console + // Configure temporary client HTTPClient http; http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // important for GitHub 302's @@ -2051,42 +2046,46 @@ bool System::uploadFirmwareURL(const char * url) { // start a connection, returns -1 if fails int httpCode = http.GET(); if (httpCode != HTTP_CODE_OK) { - LOG_ERROR("Firmware upload failed. URL %s, HTTP code %d", saved_url.c_str(), httpCode); + LOG_ERROR("Firmware upload failed - HTTP code %d", httpCode); + http.end(); return false; // error } // check we have enough space for the upload in the ota partition int firmware_size = http.getSize(); - LOG_INFO("Firmware uploading (file: %s, size: %d bytes). Please wait...", saved_url.c_str(), firmware_size); + LOG_INFO("Firmware uploading (size: %d bytes). Please wait...", firmware_size); if (!Update.begin(firmware_size)) { LOG_ERROR("Firmware upload failed - no space"); + http.end(); return false; // error } - // flush log buffers so latest messages are shown - Shell::loop_all(); + + Shell::loop_all(); // flush log buffers so latest messages are shown in console + + // TODO do we need to stop the UART with EMSuart::stop() ? // get tcp stream and send it to Updater WiFiClient * stream = http.getStreamPtr(); if (Update.writeStream(*stream) != firmware_size) { LOG_ERROR("Firmware upload failed - size differences"); + http.end(); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); return false; // error } if (!Update.end(true)) { LOG_ERROR("Firmware upload failed - general error"); + http.end(); + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_ERROR_UPLOAD); return false; // error } + // finished with upload http.end(); - - EMSESP::system_.upload_isrunning(false); saved_url.clear(); // prevent from downloading again - LOG_INFO("Firmware uploaded successfully. Restarting..."); - - restart_pending(true); - + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED); #endif return true; // OK @@ -2148,4 +2147,14 @@ bool System::command_read(const char * value, const int8_t id) { return readCommand(value); } +// set the system status code - SYSTEM_STATUS in system.h +void System::systemStatus(uint8_t status_code) { + systemStatus_ = status_code; + LOG_DEBUG("Setting System status code %d", status_code); +} + +uint8_t System::systemStatus() { + return systemStatus_; +} + } // namespace emsesp diff --git a/src/core/system.h b/src/core/system.h index 2e5eca7da..761655fae 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -59,6 +59,17 @@ namespace emsesp { enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 }; +enum SYSTEM_STATUS : uint8_t { + SYSTEM_STATUS_NORMAL = 0, + SYSTEM_STATUS_PENDING_UPLOAD = 1, + SYSTEM_STATUS_UPLOADING = 2, + SYSTEM_STATUS_ERROR_UPLOAD = 3, + SYSTEM_STATUS_RESTARTING = 4, + SYSTEM_STATUS_ERROR = 5, + SYSTEM_STATUS_PENDING_RESTART = 6, + SYSTEM_STATUS_RESTART_REQUESTED = 7 +}; + class System { public: void start(); @@ -88,8 +99,7 @@ class System { void store_nvs_values(); void system_restart(const char * partition = nullptr); - void upload_isrunning(bool in_progress); - bool upload_isrunning(); + void show_mem(const char * note); void reload_settings(); void syslog_init(); @@ -122,6 +132,9 @@ class System { void button_init(bool refresh); void commands_init(); + void systemStatus(uint8_t status_code); + uint8_t systemStatus(); + static void extractSettings(const char * filename, const char * section, JsonObject output); static bool saveSettings(const char * filename, const char * section, JsonObject input); @@ -130,20 +143,6 @@ class System { static bool readCommand(const char * data); - static void restart_requested(bool restart_requested) { - restart_requested_ = restart_requested; - } - static bool restart_requested() { - return restart_requested_; - } - - static void restart_pending(bool restart_pending) { - restart_pending_ = restart_pending; - } - static bool restart_pending() { - return restart_pending_; - } - bool telnet_enabled() { return telnet_enabled_; } @@ -341,11 +340,12 @@ class System { private: static uuid::log::Logger logger_; - static bool restart_requested_; - static bool restart_pending_; // used in 2-stage process to call restart from Web API - static bool test_set_all_active_; // force all entities in a device to have a value - static uint32_t max_alloc_mem_; - static uint32_t heap_mem_; + + static bool test_set_all_active_; // force all entities in a device to have a value + static uint32_t max_alloc_mem_; + static uint32_t heap_mem_; + + uint8_t systemStatus_; // uses SYSTEM_STATUS enum // button static PButton myPButton_; // PButton instance diff --git a/src/version.h b/src/version.h index 7fe9140fd..c643d8b34 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.7.2-dev.11" +#define EMSESP_APP_VERSION "3.7.2-dev.12" diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp index 08a6a74dc..3d1476bb7 100644 --- a/src/web/WebCustomizationService.cpp +++ b/src/web/WebCustomizationService.cpp @@ -160,7 +160,8 @@ void WebCustomizationService::reset_customization(AsyncWebServerRequest * reques if (LittleFS.remove(EMSESP_CUSTOMIZATION_FILE)) { AsyncWebServerResponse * response = request->beginResponse(205); // restart needed request->send(response); - EMSESP::system_.restart_pending(true); + emsesp::EMSESP::system_.systemStatus( + emsesp::SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART); // will be handled by the main loop. We use pending for the Web's SystemMonitor return; } diff --git a/src/web/WebSchedulerService.cpp b/src/web/WebSchedulerService.cpp index 931eb26eb..5459173e7 100644 --- a/src/web/WebSchedulerService.cpp +++ b/src/web/WebSchedulerService.cpp @@ -523,7 +523,7 @@ void WebSchedulerService::loop() { void WebSchedulerService::scheduler_task(void * pvParameters) { while (1) { delay(10); // no need to hurry - if (!EMSESP::system_.upload_isrunning()) { + if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_NORMAL) { EMSESP::webSchedulerService.loop(); } } diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 4014b9edc..1a0d52df0 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -42,7 +42,7 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se // /rest/systemStatus // This contains both system & hardware Status to avoid having multiple costly endpoints -// This is also used for polling during the RestartMonitor to see if EMS-ESP is alive +// This is also used for polling during the SystemMonitor to see if EMS-ESP is alive void WebStatusService::systemStatus(AsyncWebServerRequest * request) { EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap @@ -146,12 +146,11 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) { root["has_partition"] = false; } - // Matches status codes in RestartMonitor.tsx - if (EMSESP::system_.restart_pending()) { - root["status"] = "restarting"; - EMSESP::system_.restart_requested(true); // tell emsesp loop to start restart - } else { - root["status"] = EMSESP::system_.upload_isrunning() ? "uploading" : "ready"; + // Also used in SystemMonitor.tsx + root["status"] = EMSESP::system_.systemStatus(); // send the status. See System.h for status codes + if (EMSESP::system_.systemStatus() == SYSTEM_STATUS::SYSTEM_STATUS_PENDING_RESTART) { + // we're ready to do the actual restart ASAP + EMSESP::system_.systemStatus(SYSTEM_STATUS::SYSTEM_STATUS_RESTART_REQUESTED); } #endif @@ -194,6 +193,8 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json) ok = getCustomSupport(root); } else if (action == "uploadURL" && is_admin) { ok = uploadURL(param.c_str()); + } else if (action == "systemStatus" && is_admin) { + ok = setSystemStatus(param.c_str()); } #if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY) @@ -359,4 +360,11 @@ bool WebStatusService::uploadURL(const char * url) { return true; } +// action = systemStatus +// sets the system status +bool WebStatusService::setSystemStatus(const char * status) { + emsesp::EMSESP::system_.systemStatus(Helpers::atoint(status)); + return true; +} + } // namespace emsesp diff --git a/src/web/WebStatusService.h b/src/web/WebStatusService.h index 6c3c0c6c2..beefd2611 100644 --- a/src/web/WebStatusService.h +++ b/src/web/WebStatusService.h @@ -27,7 +27,7 @@ class WebStatusService { bool exportData(JsonObject root, std::string & type); bool getCustomSupport(JsonObject root); bool uploadURL(const char * url); - + bool setSystemStatus(const char * status); void allvalues(JsonObject output); };