From c0aa263f2b73e5a92772d36024d7b8ab60b53de2 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 8 May 2022 11:19:12 +0200 Subject: [PATCH 1/6] parse_command checks prefix-numbers and prefix `id` --- src/command.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index e54cc1ced..ee79a2d76 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -193,17 +193,22 @@ const char * Command::parse_command_string(const char * command, int8_t & id) { return nullptr; } - if (!strncmp(command, "hc", 2) && strlen(command) >= 3) { + // check prefix and valid number range, also check 'id' + if (!strncmp(command, "hc", 2) && command[2] >= '1' && command[2] <= '8') { + id = command[2] - '0'; + command += 3; + } else if (!strncmp(command, "wwc", 3) && command[3] == '1' && command[4] == '0') { + id = 19; + command += 5; + } else if (!strncmp(command, "wwc", 3) && command[3] >= '1' && command[3] <= '9') { + id = command[3] - '0' + 8; + command += 4; + } else if (!strncmp(command, "id", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '9') { + id = command[3] - '0' + 10; + command += 4; + } else if (!strncmp(command, "id", 2) && command[2] >= '1' && command[2] <= '9') { id = command[2] - '0'; command += 3; - } else if (!strncmp(command, "wwc", 3) && strlen(command) >= 4) { - if (command[3] == '1' && command[4] == '0') { - id = 19; // wwc10 - command += 5; - } else { - id = command[3] - '0' + 8; // wwc1 has id 9 - command += 4; - } } // remove separator if (command[0] == '/' || command[0] == '.' || command[0] == '_') { From 1f933fb26aee315412034f8a6509475aa82401f8 Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 8 May 2022 14:58:41 +0200 Subject: [PATCH 2/6] updates for #256 --- CHANGELOG_LATEST.md | 1 + interface/src/project/HelpInformation.tsx | 37 ++-- src/system.cpp | 198 +++++++++++++++------- 3 files changed, 158 insertions(+), 78 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index b128f1fab..0ec10fa91 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -40,6 +40,7 @@ - API fetch individual attributes from an entity [#462](https://github.com/emsesp/EMS-ESP32/issues/462) - Option to disable mDNS - Option for rendering booleans on dashboard [#456](https://github.com/emsesp/EMS-ESP32/issues/456) +- Upload customization settings from a file [#256](https://github.com/emsesp/EMS-ESP32/issues/256) ### Fixed diff --git a/interface/src/project/HelpInformation.tsx b/interface/src/project/HelpInformation.tsx index e081facca..16a27dbe4 100644 --- a/interface/src/project/HelpInformation.tsx +++ b/interface/src/project/HelpInformation.tsx @@ -2,7 +2,7 @@ import { FC, useContext } from 'react'; import { Typography, Button, Box, List, ListItem, ListItemText, Link, ListItemAvatar } from '@mui/material'; -import { SectionContent, ButtonRow } from '../components'; +import { SectionContent, ButtonRow, MessageBox } from '../components'; import { AuthenticatedContext } from '../contexts/authentication'; @@ -102,9 +102,13 @@ const HelpInformation: FC = () => { - To report an issue or request a feature, please do via  + To report an issue or request a feature, please  + onDownload('info')}> + download + +  the debug information and include in a new  - {'GitHub'} + GitHub issue @@ -112,28 +116,15 @@ const HelpInformation: FC = () => { {me.admin && ( <> - - Export Data + + Download Settings - - Download the current system information to show EMS statistics and connected devices - - - - - + Export the application settings and any customizations to a JSON file. These files can later be uploaded - via the System menu. - - - Be careful when sharing the settings as the file contains passwords and other sensitive system - information. + via System→Upload. - + )} diff --git a/src/system.cpp b/src/system.cpp index 5e6cb16e4..05c6cf911 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -689,7 +689,7 @@ void System::commands_init() { // these commands will return data in JSON format Command::add(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("show system status")); - Command::add(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("fetch system settings")); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("fetch system settings"), CommandFlag::ADMIN_ONLY); Command::add(EMSdevice::DeviceType::SYSTEM, F_(customizations), System::command_customizations, F("fetch system customizations")); Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("fetch system commands")); @@ -915,10 +915,7 @@ bool System::check_upgrade() { reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, "Settings", input); } else if (settings_type == "customizations") { // it's a customization file, just replace it and there's no need to reboot - LOG_INFO(F("Applying new customizations")); - new_file.close(); - LITTLEFS.remove(EMSESP_CUSTOMIZATION_FILE); - LITTLEFS.rename(TEMP_FILENAME_PATH, EMSESP_CUSTOMIZATION_FILE); + saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input); return false; // no reboot required } else { LOG_ERROR(F("Unrecognized file uploaded")); @@ -927,7 +924,7 @@ bool System::check_upgrade() { LOG_ERROR(F("Unrecognized file uploaded, not json")); } - // close (just in case) and remove the file + // close (just in case) and remove the temp file new_file.close(); LITTLEFS.remove(TEMP_FILENAME_PATH); } @@ -1011,36 +1008,26 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp JsonObject node; // System - node = output.createNestedObject("System"); - - node["version"] = EMSESP_APP_VERSION; - node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); - node["uptime (seconds)"] = uuid::get_uptime_sec(); - node["network time"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected"; - + node = output.createNestedObject("System Status"); + node["version"] = EMSESP_APP_VERSION; + node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); + // node["uptime (seconds)"] = uuid::get_uptime_sec(); #ifndef EMSESP_STANDALONE node["freemem"] = ESP.getFreeHeap() / 1000L; // kilobytes #endif node["reset reason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1); - if (EMSESP::dallas_enabled()) { - node["temperature sensors"] = EMSESP::dallassensor_.no_sensors(); - } - - if (EMSESP::analog_enabled()) { - node["analog sensors"] = EMSESP::analogsensor_.no_sensors(); - } #ifndef EMSESP_STANDALONE - // Network - node = output.createNestedObject("Network"); + // Network Status + node = output.createNestedObject("Network Status"); if (WiFi.status() == WL_CONNECTED) { - node["connection"] = F("WiFi"); - node["hostname"] = WiFi.getHostname(); - node["SSID"] = WiFi.SSID(); - node["BSSID"] = WiFi.BSSIDstr(); - node["RSSI"] = WiFi.RSSI(); - node["MAC"] = WiFi.macAddress(); + node["connection"] = F("WiFi"); + node["hostname"] = WiFi.getHostname(); + // node["SSID"] = WiFi.SSID(); + // node["BSSID"] = WiFi.BSSIDstr(); + node["RSSI"] = WiFi.RSSI(); + // node["MAC"] = WiFi.macAddress(); node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask()); node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP()); node["IPv4 nameserver"] = uuid::printable_to_string(WiFi.dnsIP()); @@ -1057,18 +1044,111 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") { node["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6()); } + EMSESP::webSettingsService.read([&](WebSettings & settings) { + node["phy type"] = settings.phy_type; + node["eth power"] = settings.eth_power; + node["eth phy addr"] = settings.eth_phy_addr; + node["eth clock mode"] = settings.eth_clock_mode; + }); + } +#endif + EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { + node["static ip config"] = settings.staticIPConfig; + node["enable IPv6"] = settings.enableIPv6; + node["low bandwidth"] = settings.bandwidth20; + node["disable sleep"] = settings.nosleep; + }); + EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) { + const char * pM[] = {"always", "disconnected", "never"}; + node["AP provision mode"] = pM[settings.provisionMode]; + node["AP security"] = settings.password.length() ? "wpa2" : "open"; + node["AP ssid"] = settings.ssid; + }); + + // NTP status + node = output.createNestedObject("NTP Status"); + node["network time"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected"; + EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) { + node["enabled"] = settings.enabled; + node["server"] = settings.server; + node["tz label"] = settings.tzLabel; + // node["tz format"] = settings.tzFormat; + }); + + // OTA status + node = output.createNestedObject("OTA Status"); + EMSESP::esp8266React.getOTASettingsService()->read([&](OTASettings & settings) { + node["enabled"] = settings.enabled; + node["port"] = settings.port; + }); + + // MQTT Status + node = output.createNestedObject("MQTT Status"); + node["MQTT status"] = Mqtt::connected() ? F_(connected) : F_(disconnected); + if (Mqtt::enabled()) { + node["MQTT publishes"] = Mqtt::publish_count(); + node["MQTT publish fails"] = Mqtt::publish_fails(); + } + EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) { + node["enabled"] = settings.enabled; + node["client_id"] = settings.clientId; + node["keep alive"] = settings.keepAlive; + node["clean session"] = settings.cleanSession; + node["base"] = settings.base; + node["discovery prefix"] = settings.discovery_prefix; + node["nested format"] = settings.nested_format; + node["ha enabled"] = settings.ha_enabled; + node["mqtt qos"] = settings.mqtt_qos; + node["mqtt retain"] = settings.mqtt_retain; + node["publish time boiler"] = settings.publish_time_boiler; + node["publish time thermostat"] = settings.publish_time_thermostat; + node["publish time solar"] = settings.publish_time_solar; + node["publish time mixer"] = settings.publish_time_mixer; + node["publish time other"] = settings.publish_time_other; + node["publish time sensor"] = settings.publish_time_sensor; + node["publish single"] = settings.publish_single; + node["publish2command"] = settings.publish_single2cmd; + node["send response"] = settings.send_response; + }); + + // Syslog Status + node = output.createNestedObject("Syslog Status"); + node["enabled"] = EMSESP::system_.syslog_enabled_; +#ifndef EMSESP_STANDALONE + if (EMSESP::system_.syslog_enabled_) { + node["syslog started"] = syslog_.started(); + node["syslog level"] = FL_(enum_syslog_level)[syslog_.log_level() + 1]; + node["syslog ip"] = syslog_.ip(); + node["syslog queue"] = syslog_.queued(); } #endif - // Status - node = output.createNestedObject("Status"); + // Sensor Status + node = output.createNestedObject("Sensor Status"); + if (EMSESP::dallas_enabled()) { + node["temperature sensors"] = EMSESP::dallassensor_.no_sensors(); + node["temperature sensor reads"] = EMSESP::dallassensor_.reads(); + node["temperature sensor fails"] = EMSESP::dallassensor_.fails(); + } + if (EMSESP::analog_enabled()) { + node["analog sensors"] = EMSESP::analogsensor_.no_sensors(); + node["analog sensor reads"] = EMSESP::analogsensor_.reads(); + node["analog sensor fails"] = EMSESP::analogsensor_.fails(); + } + // API Status + node = output.createNestedObject("API Status"); + node["API calls"] = WebAPIService::api_count(); + node["API fails"] = WebAPIService::api_fails(); + + // EMS Bus Status + node = output.createNestedObject("Bus Status"); switch (EMSESP::bus_status()) { case EMSESP::BUS_STATUS_OFFLINE: node["bus status"] = (F("disconnected")); break; case EMSESP::BUS_STATUS_TX_ERRORS: - node["bus status"] = (F("connected, tx issues - try a different tx-mode")); + node["bus status"] = (F("connected, tx issues - try a different Tx Mode")); break; case EMSESP::BUS_STATUS_CONNECTED: node["bus status"] = (F("connected")); @@ -1077,7 +1157,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["bus status"] = (F("unknown")); break; } - if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) { node["bus protocol"] = EMSbus::is_ht3() ? F("HT3") : F("Buderus"); node["bus telegrams received (rx)"] = EMSESP::rxservice_.telegram_count(); @@ -1088,34 +1167,37 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["bus writes failed"] = EMSESP::txservice_.telegram_write_fail_count(); node["bus rx line quality"] = EMSESP::rxservice_.quality(); node["bus tx line quality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2; - if (Mqtt::enabled()) { - node["MQTT status"] = Mqtt::connected() ? F_(connected) : F_(disconnected); - node["MQTT publishes"] = Mqtt::publish_count(); - node["MQTT publish fails"] = Mqtt::publish_fails(); - } - node["temperature sensors"] = EMSESP::dallassensor_.no_sensors(); - if (EMSESP::dallas_enabled()) { - node["temperature sensor reads"] = EMSESP::dallassensor_.reads(); - node["temperature sensor fails"] = EMSESP::dallassensor_.fails(); - } - node["analog sensors"] = EMSESP::analogsensor_.no_sensors(); - if (EMSESP::analog_enabled()) { - node["analog sensor reads"] = EMSESP::analogsensor_.reads(); - node["analog sensor fails"] = EMSESP::analogsensor_.fails(); - } - node["API calls"] = WebAPIService::api_count(); - node["API fails"] = WebAPIService::api_fails(); - -#ifndef EMSESP_STANDALONE - if (EMSESP::system_.syslog_enabled_) { - node["syslog started"] = syslog_.started(); - node["syslog level"] = FL_(enum_syslog_level)[syslog_.log_level() + 1]; - node["syslog ip"] = syslog_.ip(); - node["syslog queue"] = syslog_.queued(); - } -#endif } + // Settings + node = output.createNestedObject("Settings"); + EMSESP::webSettingsService.read([&](WebSettings & settings) { + node["board profile"] = settings.board_profile; + node["tx mode"] = settings.tx_mode; + node["ems bus id"] = settings.ems_bus_id; + node["shower timer"] = settings.shower_timer; + node["shower alert"] = settings.shower_alert; + if (settings.shower_alert) { + node["shower alert coldshot"] = settings.shower_alert_coldshot; // seconds + node["shower alert trigger"] = settings.shower_alert_trigger; // minutes + } + node["rx gpio"] = settings.rx_gpio; + node["tx gpio"] = settings.tx_gpio; + node["dallas gpio"] = settings.dallas_gpio; + node["pbutton gpio"] = settings.pbutton_gpio; + node["led gpio"] = settings.led_gpio; + node["hide led"] = settings.hide_led; + node["notoken api"] = settings.notoken_api; + node["readonly mode"] = settings.readonly_mode; + node["fahrenheit"] = settings.fahrenheit; + node["dallas parasite"] = settings.dallas_parasite; + node["bool format"] = settings.bool_format; + node["bool dashboard"] = settings.bool_dashboard; + node["enum format"] = settings.enum_format; + node["analog enabled"] = settings.analog_enabled; + node["telnet enabled"] = settings.telnet_enabled; + }); + // Devices - show EMS devices JsonArray devices = output.createNestedArray("Devices"); for (const auto & device_class : EMSFactory::device_handlers()) { From 8b521aa572930d57ba530311953d1ef3c4da9561 Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 8 May 2022 15:01:04 +0200 Subject: [PATCH 3/6] fix standalone compiling --- src/system.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/system.cpp b/src/system.cpp index 05c6cf911..48ab46c97 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -1017,7 +1017,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp #endif node["reset reason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1); - #ifndef EMSESP_STANDALONE // Network Status node = output.createNestedObject("Network Status"); @@ -1058,15 +1057,18 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["low bandwidth"] = settings.bandwidth20; node["disable sleep"] = settings.nosleep; }); +#ifndef EMSESP_STANDALONE EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) { const char * pM[] = {"always", "disconnected", "never"}; node["AP provision mode"] = pM[settings.provisionMode]; node["AP security"] = settings.password.length() ? "wpa2" : "open"; node["AP ssid"] = settings.ssid; }); +#endif // NTP status - node = output.createNestedObject("NTP Status"); + node = output.createNestedObject("NTP Status"); +#ifndef EMSESP_STANDALONE node["network time"] = EMSESP::system_.ntp_connected() ? "connected" : "disconnected"; EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) { node["enabled"] = settings.enabled; @@ -1081,6 +1083,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp node["enabled"] = settings.enabled; node["port"] = settings.port; }); +#endif // MQTT Status node = output.createNestedObject("MQTT Status"); From 0e9202ae4d5600c26bfe89ce4f8bca737ea6ea5b Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 8 May 2022 17:01:38 +0200 Subject: [PATCH 4/6] change to saving customization file after upload --- src/system.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index 48ab46c97..44eb94b31 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -916,7 +916,6 @@ bool System::check_upgrade() { } else if (settings_type == "customizations") { // it's a customization file, just replace it and there's no need to reboot saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input); - return false; // no reboot required } else { LOG_ERROR(F("Unrecognized file uploaded")); } From db8c30ddbb9db0f13109412ccea5582368bea76d Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 8 May 2022 17:02:38 +0200 Subject: [PATCH 5/6] fix for customizations after upload new file --- src/system.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index 48ab46c97..44eb94b31 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -916,7 +916,6 @@ bool System::check_upgrade() { } else if (settings_type == "customizations") { // it's a customization file, just replace it and there's no need to reboot saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input); - return false; // no reboot required } else { LOG_ERROR(F("Unrecognized file uploaded")); } From 226a557d5ccc2f972ad8b796ad561f5c7b64dd7d Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 8 May 2022 20:13:48 +0200 Subject: [PATCH 6/6] prevent build failing --- interface/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/package.json b/interface/package.json index 413709d0b..f278f7a4d 100644 --- a/interface/package.json +++ b/interface/package.json @@ -78,7 +78,7 @@ "max-len": [ 1, { - "code": 150 + "code": 200 } ], "arrow-parens": 1