From 26a4347155dac43cc1954204c9617991974cd785 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 26 Jun 2021 11:05:04 +0200 Subject: [PATCH 001/122] prep for 3.1.2 --- CHANGELOG.md | 40 +++++++++++++++++++++++++++++++--------- CHANGELOG_LATEST.md | 14 -------------- src/version.h | 2 +- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 710f88cd2..f4e330167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,13 +5,37 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [3.1.0] May 4 2021 +# [3.1.1] June 26 2021 + +## Changed + +- new command called `commands` which lists all available commands. `ems-esp/api/{device}/commands` +- More Home Assistant icons to match the UOMs +- new API. Using secure access tokens and OpenAPI standard. See `doc/EMS-ESP32 API.md` and [#50](https://github.com/emsesp/EMS-ESP32/issues/50) +- show log messages in Web UI [#71](https://github.com/emsesp/EMS-ESP32/issues/71) + +## Fixed + +- HA thermostat mode was not in sync with actual mode [#66](https://github.com/emsesp/EMS-ESP32/issues/66) +- Don't publish rssi if Wifi is disabled and ethernet is being used +- Booleans are shown as true/false in API GETs + +## Changed + +- `info` command always shows full names in API. For short names query the device or name directly, e.g. `http://ems-esp/api/boiler` +- free memory is shown in kilobytes +- boiler's warm water entities have ww added to the Home Assistant entity name [#67](https://github.com/emsesp/EMS-ESP32/issues/67) +- improved layout and rendering of device values in the WebUI, also the edit value screen + +# [3.1.0] May 4 2021 + +## Changed - Mock API to simulate an ESP, for testing web - Able to write values from the Web UI - check values with `"cmd":` and data empty or `?` - set hc for values and commands by id or prefix `hc`+separator, separator can be any char - + ## Fixed - Don't create Home Assistant MQTT discovery entries for device values that don't exists (#756 on EMS-ESP repo) @@ -27,9 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - lowercased Flow temp in commands - system console commands to main -## Removed - -## [3.0.1] March 30 2021 +# [3.0.1] March 30 2021 ## Added @@ -87,9 +109,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Shower Alert (disabled for now) -## [3.0.0] March 18 2021 +# [3.0.0] March 18 2021 -### Added +## Added - Power settings, disabling BLE and turning off Wifi sleep - Rx and Tx counts to Heartbeat MQTT payload @@ -106,7 +128,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - RC300 `thermostat temp -1` to clear temporary setpoint in auto mode - Syslog port selectable (#744) -### Fixed +## Fixed - telegrams matched to masterthermostat 0x18 - multiple roomcontrollers @@ -117,7 +139,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - wrong position of values #723, #732 - OTA Upload via Web on OSX -### Changed +## Changed - changed how telegram parameters are rendered for mqtt, console and web (#632) - split `show values` in smaller packages (edited) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 1d0b1fb9c..ea1320609 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -2,22 +2,8 @@ ## Added -- new command called `commands` which lists all available commands. `ems-esp/api/{device}/commands` -- More Home Assistant icons to match the UOMs -- new API. Using secure access tokens and OpenAPI standard. See `doc/EMS-ESP32 API.md` and [#50](https://github.com/emsesp/EMS-ESP32/issues/50) -- show log messages in Web UI [#71](https://github.com/emsesp/EMS-ESP32/issues/71) - ## Fixed -- HA thermostat mode was not in sync with actual mode [#66](https://github.com/emsesp/EMS-ESP32/issues/66) -- Don't publish rssi if Wifi is disabled and ethernet is being used -- Booleans are shown as true/false in API GETs - ## Changed -- `info` command always shows full names in API. For short names query the device or name directly, e.g. `http://ems-esp/api/boiler` -- free memory is shown in kilobytes -- boiler's warm water entities have ww added to the Home Assistant entity name [#67](https://github.com/emsesp/EMS-ESP32/issues/67) -- improved layout and rendering of device values in the WebUI, also the edit value screen - ## Removed diff --git a/src/version.h b/src/version.h index 396a1fa45..dcc705421 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.1b8" +#define EMSESP_APP_VERSION "3.1.2b0" From 3356a4ce1410a5844b32362e4f1d32059239e025 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 26 Jun 2021 11:14:45 +0200 Subject: [PATCH 002/122] fix v16 for nodejs --- .github/workflows/tagged_release.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tagged_release.yml b/.github/workflows/tagged_release.yml index 9bcc7719d..f9169eac9 100644 --- a/.github/workflows/tagged_release.yml +++ b/.github/workflows/tagged_release.yml @@ -12,26 +12,24 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout source code - uses: actions/checkout@v2 - - name: Compile locally - run: make + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: actions/setup-node@v2 + with: + node-version: '16' - - name: Setup Python - uses: actions/setup-python@v2 - - - name: Install pio + - name: Install PlatformIO run: | python -m pip install --upgrade pip pip install -U platformio platformio upgrade platformio update - - name: Build web + - name: Build WebUI run: | cd interface - npm install + npm ci npm run build - name: Build firmware From e419e67cb0c41df1d12e264be4f626c936217fe0 Mon Sep 17 00:00:00 2001 From: MichaelDvP <59284019+MichaelDvP@users.noreply.github.com> Date: Tue, 29 Jun 2021 09:37:05 +0200 Subject: [PATCH 003/122] fix memory leak (#78) --- src/web/WebAPIService.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index 2cd541b3f..c1e121300 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -182,11 +182,13 @@ void WebAPIService::parse(AsyncWebServerRequest * request, std::string & device_ // check for errors if (!ok) { + delete response; send_message_response(request, 400, "Problems parsing elements"); // Bad Request return; } if (!json.size()) { + delete response; send_message_response(request, 200, "OK"); // OK return; } From e2a5853ddefb676f2dcdda32545d5c494ebaf5bd Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 29 Jun 2021 22:57:21 +0200 Subject: [PATCH 004/122] rename WIFI in System settings to Network --- src/system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index 5368f1cae..fc06a5a64 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -781,7 +781,7 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject & node["version"] = EMSESP_APP_VERSION; EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { - node = json.createNestedObject("WIFI"); + node = json.createNestedObject("Network"); // node["ssid"] = settings.ssid; // commented out - people don't like others to see this node["hostname"] = settings.hostname; node["static_ip_config"] = settings.staticIPConfig; From 40e7e1b418353fbda2068a78a03b539c1d0f5180 Mon Sep 17 00:00:00 2001 From: MichaelDvP <59284019+MichaelDvP@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:13:18 +0200 Subject: [PATCH 005/122] always register subscriptions (#79) --- src/mqtt.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/mqtt.cpp b/src/mqtt.cpp index df8687fb5..49800ab16 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -57,9 +57,6 @@ uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON}; // subscribe to an MQTT topic, and store the associated callback function // only if it already hasn't been added void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_subfunction_p cb) { - if (!enabled()) { - return; - } // check if we already have the topic subscribed, if so don't add it again if (!mqtt_subfunctions_.empty()) { @@ -74,18 +71,18 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_ } } - LOG_DEBUG(F("Subscribing MQTT topic %s for device type %s"), topic.c_str(), EMSdevice::device_type_2_device_name(device_type).c_str()); - - // add to MQTT queue as a subscribe operation - auto message = queue_subscribe_message(topic); - - if (message == nullptr) { - return; - } - // register in our libary with the callback function. // We store the original topic without base mqtt_subfunctions_.emplace_back(device_type, std::move(topic), std::move(cb)); + + if (!enabled()) { + return; + } + + LOG_DEBUG(F("Subscribing MQTT topic %s for device type %s"), topic.c_str(), EMSdevice::device_type_2_device_name(device_type).c_str()); + + // add to MQTT queue as a subscribe operation + queue_subscribe_message(topic); } // subscribe to the command topic if it doesn't exist yet @@ -107,6 +104,10 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper LOG_DEBUG(F("Registering MQTT cmd %s with topic %s"), uuid::read_flash_string(cmd).c_str(), EMSdevice::device_type_2_device_name(device_type).c_str()); } + if (!enabled()) { + return; + } + // register the individual commands too (e.g. ems-esp/boiler/wwonetime) // https://github.com/emsesp/EMS-ESP32/issues/31 std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); From ae0846e877ccbe86b2b68ffe935c8d4b10970f43 Mon Sep 17 00:00:00 2001 From: MichaelDvP <59284019+MichaelDvP@users.noreply.github.com> Date: Wed, 30 Jun 2021 13:49:52 +0200 Subject: [PATCH 006/122] mqtt connect on enable-change (#79) --- lib/framework/MqttSettingsService.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index 67ce8b1f6..5f032f41c 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -202,6 +202,10 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT; newSettings.subscribe_format = root["subscribe_format"] | EMSESP_DEFAULT_SUBSCRIBE_FORMAT; + if (newSettings.enabled != settings.enabled) { + changed = true; + } + if (newSettings.mqtt_qos != settings.mqtt_qos) { emsesp::EMSESP::mqtt_.set_qos(newSettings.mqtt_qos); changed = true; From c634c3987412b13b9e3c0610550f321262a15547 Mon Sep 17 00:00:00 2001 From: MichaelDvP <59284019+MichaelDvP@users.noreply.github.com> Date: Wed, 30 Jun 2021 14:26:46 +0200 Subject: [PATCH 007/122] 3. fix for mqtt enable (#79) --- src/mqtt.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 49800ab16..36a16cf49 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -655,12 +655,9 @@ void Mqtt::on_connect() { EMSESP::shower_.send_mqtt_stat(false); // Send shower_activated as false EMSESP::system_.send_heartbeat(); // send heatbeat - if (connectcount_ > 1) { - // we doing a re-connect from a TCP break - // only re-subscribe again to all MQTT topics - resubscribe(); - EMSESP::reset_mqtt_ha(); // re-create all HA devices if there are any - } + // re-subscribe to all MQTT topics + resubscribe(); + EMSESP::reset_mqtt_ha(); // re-create all HA devices if there are any publish_retain(F("status"), "online", true); // say we're alive to the Last Will topic, with retain on From 16b3cf764d3fcc5a115c80fbf656745b729ae954 Mon Sep 17 00:00:00 2001 From: MichaelDvP <59284019+MichaelDvP@users.noreply.github.com> Date: Thu, 1 Jul 2021 09:34:34 +0200 Subject: [PATCH 008/122] don't count wrong tx-echos as rx-errors --- src/telegram.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/telegram.cpp b/src/telegram.cpp index 1d5893b3a..23d7910f6 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -154,8 +154,12 @@ void RxService::add(uint8_t * data, uint8_t length) { // validate the CRC. if it fails then increment the number of corrupt/incomplete telegrams and only report to console/syslog uint8_t crc = calculate_crc(data, length - 1); if (data[length - 1] != crc) { - telegram_error_count_++; - LOG_ERROR(F("Rx: %s (CRC %02X != %02X)"), Helpers::data_to_hex(data, length).c_str(), data[length - 1], crc); + if ((data[0] & 0x7F) != ems_bus_id()) { // do not count echos as errors + telegram_error_count_++; + LOG_WARNING(F("Rx: %s (CRC %02X != %02X)"), Helpers::data_to_hex(data, length).c_str(), data[length - 1], crc); + } else { + LOG_TRACE(F("Rx: %s (CRC %02X != %02X)"), Helpers::data_to_hex(data, length).c_str(), data[length - 1], crc); + } return; } From 0510189f546c61c78592fcb88bf12807c8a7f020 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 1 Jul 2021 11:19:00 +0200 Subject: [PATCH 009/122] slow down MQTT reconnect to 2 seconds --- lib/framework/MqttSettingsService.cpp | 9 ++++----- lib/framework/MqttSettingsService.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index 5f032f41c..140c37512 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -84,7 +84,7 @@ void MqttSettingsService::onMqttConnect(bool sessionPresent) { } void MqttSettingsService::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) { - // emsesp::EMSESP::logger().info(F("Disconnected from MQTT reason: %s"), (uint8_t)reason); + // emsesp::EMSESP::logger().info(F("Disconnected from MQTT reason: %d"), (uint8_t)reason); _disconnectReason = reason; _disconnectedAt = uuid::get_uptime(); } @@ -121,14 +121,13 @@ void MqttSettingsService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { } void MqttSettingsService::configureMqtt() { - // disconnect if currently connected - _mqttClient.disconnect(); - // only connect if WiFi is connected and MQTT is enabled if (_state.enabled && emsesp::EMSESP::system_.network_connected()) { + _mqttClient.disconnect(); _mqttClient.setServer(retainCstr(_state.host.c_str(), &_retainedHost), _state.port); if (_state.username.length() > 0) { - _mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername), retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword)); + _mqttClient.setCredentials(retainCstr(_state.username.c_str(), &_retainedUsername), + retainCstr(_state.password.length() > 0 ? _state.password.c_str() : nullptr, &_retainedPassword)); } else { _mqttClient.setCredentials(retainCstr(nullptr, &_retainedUsername), retainCstr(nullptr, &_retainedPassword)); } diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h index 0c2d53cda..eabeccc72 100644 --- a/lib/framework/MqttSettingsService.h +++ b/lib/framework/MqttSettingsService.h @@ -9,7 +9,7 @@ #include -#define MQTT_RECONNECTION_DELAY 1000 +#define MQTT_RECONNECTION_DELAY 2000 // 2 seconds #define MQTT_SETTINGS_FILE "/config/mqttSettings.json" #define MQTT_SETTINGS_SERVICE_PATH "/rest/mqttSettings" From 44a41b963d787c1ec2b22ea1b2c93644d9352941 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 2 Jul 2021 16:24:50 +0200 Subject: [PATCH 010/122] add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat - #80 --- CHANGELOG_LATEST.md | 3 +++ src/system.cpp | 26 ++++++++++++++------------ src/version.h | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index ea1320609..34c404120 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -6,4 +6,7 @@ ## Changed +- removed Rx echo failures counting as incomplete telegrams. BAd telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80) +- add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat + ## Removed diff --git a/src/system.cpp b/src/system.cpp index fc06a5a64..d7c37cf22 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -879,8 +879,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json node = json.createNestedObject("System"); - node["version"] = EMSESP_APP_VERSION; - node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); + node["version"] = EMSESP_APP_VERSION; + node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3); + node["uptime_sec"] = uuid::get_uptime_sec(); + #ifndef EMSESP_STANDALONE node["freemem"] = ESP.getFreeHeap() / 1000L; // kilobytes #endif @@ -901,17 +903,17 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json } if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) { - node["bus protocol"] = EMSbus::is_ht3() ? F("HT3") : F("Buderus"); - node["#telegrams received"] = EMSESP::rxservice_.telegram_count(); - node["#read requests sent"] = EMSESP::txservice_.telegram_read_count(); - node["#write requests sent"] = EMSESP::txservice_.telegram_write_count(); - node["#incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count(); - node["#tx fails"] = EMSESP::txservice_.telegram_fail_count(); - node["rx line quality"] = EMSESP::rxservice_.quality(); - node["tx line quality"] = EMSESP::txservice_.quality(); + node["bus protocol"] = EMSbus::is_ht3() ? F("HT3") : F("Buderus"); + node["telegrams received"] = EMSESP::rxservice_.telegram_count(); + node["read requests sent"] = EMSESP::txservice_.telegram_read_count(); + node["write requests sent"] = EMSESP::txservice_.telegram_write_count(); + node["incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count(); + node["tx fails"] = EMSESP::txservice_.telegram_fail_count(); + node["rx line quality"] = EMSESP::rxservice_.quality(); + node["tx line quality"] = EMSESP::txservice_.quality(); if (Mqtt::enabled()) { - node["#MQTT publishes"] = Mqtt::publish_count(); - node["#MQTT publish fails"] = Mqtt::publish_fails(); + node["MQTT publishes"] = Mqtt::publish_count(); + node["MQTT publish fails"] = Mqtt::publish_fails(); } if (EMSESP::dallas_enabled()) { node["#dallas sensors"] = EMSESP::sensor_devices().size(); diff --git a/src/version.h b/src/version.h index dcc705421..2c29bab9f 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.2b0" +#define EMSESP_APP_VERSION "3.1.2b1" From b09b650c1dc6ac73c158535f40241e920421fa9e Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Jul 2021 10:44:15 +0200 Subject: [PATCH 011/122] remove # from dallas in api/system/info --- src/system.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/system.cpp b/src/system.cpp index d7c37cf22..9d9a0b203 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -916,9 +916,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json node["MQTT publish fails"] = Mqtt::publish_fails(); } if (EMSESP::dallas_enabled()) { - node["#dallas sensors"] = EMSESP::sensor_devices().size(); - node["#dallas reads"] = EMSESP::sensor_reads(); - node["#dallas fails"] = EMSESP::sensor_fails(); + node["dallas sensors"] = EMSESP::sensor_devices().size(); + node["dallas reads"] = EMSESP::sensor_reads(); + node["dallas fails"] = EMSESP::sensor_fails(); } } From c09e180c48d89238736cd5fa6b995613c8d658b4 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Jul 2021 20:19:43 +0200 Subject: [PATCH 012/122] add linters --- makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/makefile b/makefile index 8bb37d0de..4b611b114 100644 --- a/makefile +++ b/makefile @@ -72,9 +72,9 @@ CPPFLAGS += -g3 CPPFLAGS += -Os CFLAGS += $(CPPFLAGS) -# CFLAGS += -Wall -# CFLAGS += -Wno-unused -Wno-restrict -# CFLAGS += -Wextra +CFLAGS += -Wall +CFLAGS += -Wno-unused -Wno-restrict +CFLAGS += -Wextra CXXFLAGS += $(CFLAGS) -MMD From 95a9808f3579523aff93ccb92c943d99e7d895ca Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Jul 2021 20:19:57 +0200 Subject: [PATCH 013/122] add debug target --- pio_local.ini_example | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/pio_local.ini_example b/pio_local.ini_example index cb609ae4b..a8ed24546 100644 --- a/pio_local.ini_example +++ b/pio_local.ini_example @@ -1,22 +1,31 @@ ; example custom platformio.ini file for EMS-ESP -[env] +[common] +; e.g. use build_flags = -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\" + +[env:esp32] upload_protocol = espota upload_flags = --port=8266 --auth=ems-esp-neo upload_port = 10.10.10.101 - -[common] -; options are EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_DEBUG_SENSOR -; plus all the settings in default_settings.h, e.g. -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\" -; debug_flags = -DEMSESP_DEBUG - -[env:esp32] -monitor_filters = esp32_exception_decoder -debug_tool = esp-prog -debug_init_break = tbreak setup extra_scripts = ; to prevent the web UI from building each time, comment out this next line ; pre:scripts/build_interface.py +; pio run -e debug +; options for debugging are: EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_DEBUG_SENSOR +[env:debug] +board = esp32dev +platform = espressif32 +board_build.partitions = esp32_partition_app1984k_spiffs64k.csv +upload_protocol = esptool +build_type = debug +monitor_filters = esp32_exception_decoder +debug_tool = esp-prog +debug_init_break = tbreak setup +build_flags = ${factory_settings.build_flags} -DEMSESP_DEBUG -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1 +extra_scripts = + ; pre:scripts/build_interface.py + scripts/upload_fw.py + From 5fba51103e20a2245611ab66dc7d71a9149ef580 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Jul 2021 20:20:07 +0200 Subject: [PATCH 014/122] cleanup --- platformio.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/platformio.ini b/platformio.ini index 66c02e074..07241650d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -61,11 +61,6 @@ extra_scripts = scripts/rename_fw.py board = esp32dev platform = espressif32 -; platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#idf-release/v4.2 -; toolchain-xtensa32 @ 2.80200.200226 -; toolchain-xtensa32 @ 5.100200.201223 -; toolchain-xtensa32 @ 2.80400.2020 -; platform = https://github.com/platformio/platform-espressif32.git board_build.partitions = esp32_partition_app1984k_spiffs64k.csv build_flags = ${common.build_flags} ${common.debug_flags} build_unflags = ${common.unbuild_flags} From 4a7308c5bbc06a73c9ec7f31e55fbafa1d973be6 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Jul 2021 20:20:27 +0200 Subject: [PATCH 015/122] add linux commands --- scripts/analyze_stackdmp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/analyze_stackdmp.py b/scripts/analyze_stackdmp.py index a686a5446..f6dfac8ed 100644 --- a/scripts/analyze_stackdmp.py +++ b/scripts/analyze_stackdmp.py @@ -23,4 +23,5 @@ call(['python', 'scripts/decoder.py ', '-s', '-e', os.getcwd()+"/.pio/build/esp1 # % cd EMS-ESP # % python scripts/decoder_linux.py -s -e .pio/build/esp12e/firmware.elf scripts/stackdmp.txt -# python decoder_linux.py -s -e ../.pio/build/esp8266-debug/firmware.elf stackdmp.txt \ No newline at end of file +# python decoder_linux.py -s -e ../.pio/build/esp8266-debug/firmware.elf stackdmp.txt +# python3 decoder.py -p ESP32 -e ../.pio/build/esp32/firmware.elf -t ~/.platformio/packages/toolchain-xtensa32 stackdmp.txt From c217a40710b328add2a4b86441d8c8f25d7439b9 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Jul 2021 20:20:43 +0200 Subject: [PATCH 016/122] remove CRC in warning message --- src/telegram.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/telegram.cpp b/src/telegram.cpp index 23d7910f6..d4f844443 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -156,9 +156,9 @@ void RxService::add(uint8_t * data, uint8_t length) { if (data[length - 1] != crc) { if ((data[0] & 0x7F) != ems_bus_id()) { // do not count echos as errors telegram_error_count_++; - LOG_WARNING(F("Rx: %s (CRC %02X != %02X)"), Helpers::data_to_hex(data, length).c_str(), data[length - 1], crc); + LOG_WARNING(F("Incomplete Rx: %s"), Helpers::data_to_hex(data, length).c_str()); } else { - LOG_TRACE(F("Rx: %s (CRC %02X != %02X)"), Helpers::data_to_hex(data, length).c_str(), data[length - 1], crc); + LOG_TRACE(F("Incomplete Rx: %s"), Helpers::data_to_hex(data, length).c_str()); } return; } From 3eb220211711969687b190af788ac6862f30312e Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 3 Jul 2021 20:20:56 +0200 Subject: [PATCH 017/122] add test to crash EMS-ESP --- src/test/test.cpp | 14 ++++++++++++-- src/test/test.h | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/test.cpp b/src/test/test.cpp index c2d35469d..7e58999c1 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -934,8 +934,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { shell.printfln(F("Testing RESTful API...")); Mqtt::ha_enabled(false); run_test("general"); - DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN); - JsonObject json = doc.to(); AsyncWebServerRequest request; request.method(HTTP_GET); request.url("/api/thermostat/seltemp"); @@ -945,6 +943,18 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { EMSESP::webAPIService.webAPIService_get(&request); #endif } + + if (command == "crash") { + shell.printfln(F("Forcing a crash...")); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdiv-by-zero" +#pragma GCC diagnostic ignored "-Wunused-variable" + uint8_t a = 2 / 0; + shell.printfln(F("Testing %s"), a); + +#pragma GCC diagnostic pop + } } // simulates a telegram in the Rx queue, but without the CRC which is added automatically diff --git a/src/test/test.h b/src/test/test.h index 3707bbfa3..df96d7e1f 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -38,7 +38,8 @@ namespace emsesp { // #define EMSESP_DEBUG_DEFAULT "board_profile" // #define EMSESP_DEBUG_DEFAULT "shower_alert" // #define EMSESP_DEBUG_DEFAULT "310" -#define EMSESP_DEBUG_DEFAULT "api" +// #define EMSESP_DEBUG_DEFAULT "api" +#define EMSESP_DEBUG_DEFAULT "crash" class Test { public: From 8429f650aa05a0a6cfe9f7a4fed3552c2d3f6f2f Mon Sep 17 00:00:00 2001 From: Proddy Date: Sat, 3 Jul 2021 20:32:46 +0200 Subject: [PATCH 018/122] fix lint warnings --- src/devices/solar.cpp | 9 ++++++--- src/devices/thermostat.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index 2edb644d3..32e81cae9 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -315,9 +315,12 @@ void Solar::process_SM100SolarCircuitConfig(std::shared_ptr tele * e.g. B0 0B F9 00 00 02 5A 00 00 6E */ void Solar::process_SM100ParamCfg(std::shared_ptr telegram) { - uint16_t t_id; - uint8_t of; - int32_t min, def, max, cur; + uint16_t t_id = EMS_VALUE_USHORT_NOTSET; + uint8_t of = EMS_VALUE_UINT_NOTSET; + int32_t min = EMS_VALUE_USHORT_NOTSET; + int32_t def = EMS_VALUE_USHORT_NOTSET; + int32_t max = EMS_VALUE_USHORT_NOTSET; + int32_t cur = EMS_VALUE_USHORT_NOTSET; has_update(telegram->read_value(t_id, 1)); has_update(telegram->read_value(of, 3)); has_update(telegram->read_value(min, 5)); diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index f80bcf45f..b817a30ef 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -1096,7 +1096,7 @@ void Thermostat::process_RCErrorMessage(std::shared_ptr telegram // data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr if (telegram->message_data[4] & 0x80) { // valid date char code[3]; - uint16_t codeNo; + uint16_t codeNo = EMS_VALUE_USHORT_NOTSET; code[0] = telegram->message_data[0]; code[1] = telegram->message_data[1]; code[2] = 0; From 5b26e27834216284ed8866962a06d66d5348b51c Mon Sep 17 00:00:00 2001 From: Proddy Date: Mon, 5 Jul 2021 16:53:27 +0200 Subject: [PATCH 019/122] Update to 16.8.1 --- lib/framework/NetworkSettingsService.cpp | 6 ++++++ lib/framework/NetworkSettingsService.h | 21 +++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/framework/NetworkSettingsService.cpp b/lib/framework/NetworkSettingsService.cpp index 587bf3906..2c5e7eb3f 100644 --- a/lib/framework/NetworkSettingsService.cpp +++ b/lib/framework/NetworkSettingsService.cpp @@ -19,6 +19,12 @@ NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, WiFi.mode(WIFI_MODE_MAX); WiFi.mode(WIFI_MODE_NULL); +#if defined(EMSESP_WIFI_TWEAK) + // https: //www.esp32.com/viewtopic.php?t=12055 + esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, WIFI_BW_HT20); + esp_wifi_set_bandwidth(ESP_IF_WIFI_AP, WIFI_BW_HT20); +#endif + WiFi.onEvent(std::bind(&NetworkSettingsService::WiFiEvent, this, _1)); addUpdateHandler([&](const String & originId) { reconfigureWiFiConnection(); }, false); diff --git a/lib/framework/NetworkSettingsService.h b/lib/framework/NetworkSettingsService.h index 64e11f498..6c3df278f 100644 --- a/lib/framework/NetworkSettingsService.h +++ b/lib/framework/NetworkSettingsService.h @@ -6,7 +6,12 @@ #include #include +#ifndef EMSESP_STANDALONE +#if defined(EMSESP_WIFI_TWEAK) +#include +#endif #include +#endif #define NETWORK_SETTINGS_FILE "/config/networkSettings.json" #define NETWORK_SETTINGS_SERVICE_PATH "/rest/networkSettings" @@ -27,10 +32,10 @@ class NetworkSettings { public: // core wifi configuration - String ssid; - String password; - String hostname; - bool staticIPConfig; + String ssid; + String password; + String hostname; + bool staticIPConfig; // optional configuration for static IP address IPAddress localIP; @@ -55,10 +60,10 @@ class NetworkSettings { } static StateUpdateResult update(JsonObject & root, NetworkSettings & settings) { - settings.ssid = root["ssid"] | FACTORY_WIFI_SSID; - settings.password = root["password"] | FACTORY_WIFI_PASSWORD; - settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME; - settings.staticIPConfig = root["static_ip_config"] | false; + settings.ssid = root["ssid"] | FACTORY_WIFI_SSID; + settings.password = root["password"] | FACTORY_WIFI_PASSWORD; + settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME; + settings.staticIPConfig = root["static_ip_config"] | false; // extended settings JsonUtils::readIP(root, "local_ip", settings.localIP); From 6f57beab2831313ae5e85aed4f468e567ce88fa8 Mon Sep 17 00:00:00 2001 From: Proddy Date: Mon, 5 Jul 2021 16:53:57 +0200 Subject: [PATCH 020/122] cleanup build --- esp32_partition_app1984k_spiffs64k.csv | 10 +++++----- platformio.ini | 8 ++------ scripts/partitions.bin | Bin 3072 -> 3072 bytes 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/esp32_partition_app1984k_spiffs64k.csv b/esp32_partition_app1984k_spiffs64k.csv index 3b428f9a9..7eaebdbc1 100644 --- a/esp32_partition_app1984k_spiffs64k.csv +++ b/esp32_partition_app1984k_spiffs64k.csv @@ -1,6 +1,6 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x1F0000, +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xE000, 0x2000, +app0, app, ota_0, 0x10000, 0x1F0000, app1, app, ota_1, 0x200000, 0x1F0000, -spiffs, data, spiffs, 0x3F0000,0x10000, +spiffs, data, spiffs, 0x3F0000, 0x10000, diff --git a/platformio.ini b/platformio.ini index 07241650d..88ac17869 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,10 +15,8 @@ core_build_flags = -D NDEBUG -D ARDUINO_ARCH_ESP32=1 -D ESP32=1 - ; -std=c++17 -std=gnu++17 core_unbuild_flags = - ; -std=gnu++11 build_flags = ${common.core_build_flags} @@ -30,9 +28,6 @@ build_flags = unbuild_flags = ${common.core_unbuild_flags} -; these are set in your pio_local.ini -debug_flags = - [env] framework = arduino monitor_speed = 115200 @@ -47,6 +42,7 @@ check_flags = clangtidy: --checks=-*,clang-analyzer-*,performance-* ; build for GitHub Actions CI +; the Web interface is built seperately [env:ci] extra_scripts = scripts/rename_fw.py board = esp32dev @@ -62,5 +58,5 @@ extra_scripts = board = esp32dev platform = espressif32 board_build.partitions = esp32_partition_app1984k_spiffs64k.csv -build_flags = ${common.build_flags} ${common.debug_flags} +build_flags = ${common.build_flags} build_unflags = ${common.unbuild_flags} diff --git a/scripts/partitions.bin b/scripts/partitions.bin index fa042d671dc72d5dc2236011331d6c28d4049ebe..7ae174cb41a9306f79861d89e115e83693114f10 100644 GIT binary patch delta 52 zcmZpWXporT#V9|~TbfmYfq_AOVzxA^J&?gTv0GK3;b^8=>W|gsLQy-+_NsrB-+0iT HoAEyYYETe> delta 52 zcmZpWXporT#V9w?Tbfm#fq_A8VzxA^Es()Hv0GKZu4M7dAFXqgT}AFWzuP{^XX8P8 HZpQxrZ3YnY From 0aaa35098dda90b07a8b28b86b68c41a0b738684 Mon Sep 17 00:00:00 2001 From: Proddy Date: Mon, 5 Jul 2021 16:54:15 +0200 Subject: [PATCH 021/122] remove obsolete scripts --- scripts/analyze_stackdmp.py | 27 --- scripts/decoder.py | 319 ------------------------------------ scripts/decoder.sh | 4 - 3 files changed, 350 deletions(-) delete mode 100644 scripts/analyze_stackdmp.py delete mode 100644 scripts/decoder.py delete mode 100644 scripts/decoder.sh diff --git a/scripts/analyze_stackdmp.py b/scripts/analyze_stackdmp.py deleted file mode 100644 index f6dfac8ed..000000000 --- a/scripts/analyze_stackdmp.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -from subprocess import call -import os - -# example stackdmp.txt would contain text like below copied & pasted from a 'crash dump' command: - -# >>>stack>>> -# 3fffff20: 3fff32f0 00000003 3fff3028 402101b2 -# 3fffff30: 3fffdad0 3fff3280 0000000d 402148aa -# 3fffff40: 3fffdad0 3fff3280 3fff326c 3fff32f0 -# 3fffff50: 0000000d 3fff326c 3fff3028 402103bd -# 3fffff60: 0000000d 3fff34cc 40211de4 3fff34cc -# 3fffff70: 3fff3028 3fff14c4 3fff301c 3fff34cc -# 3fffff80: 3fffdad0 3fff14c4 3fff3028 40210493 -# 3fffff90: 3fffdad0 00000000 3fff14c4 4020a738 -# 3fffffa0: 3fffdad0 00000000 3fff349c 40211e90 -# 3fffffb0: feefeffe feefeffe 3ffe8558 40100b01 -# <<[0-9]*)\\):$") -COUNTER_REGEX = re.compile('^epc1=(?P0x[0-9a-f]+) epc2=(?P0x[0-9a-f]+) epc3=(?P0x[0-9a-f]+) ' - 'excvaddr=(?P0x[0-9a-f]+) depc=(?P0x[0-9a-f]+)$') -CTX_REGEX = re.compile("^ctx: (?P.+)$") -POINTER_REGEX = re.compile('^sp: (?P[0-9a-f]+) end: (?P[0-9a-f]+) offset: (?P[0-9a-f]+)$') -STACK_BEGIN = '>>>stack>>>' -STACK_END = '<<[0-9a-f]+):\W+(?P[0-9a-f]+) (?P[0-9a-f]+) (?P[0-9a-f]+) (?P[0-9a-f]+)(\W.*)?$') - -StackLine = namedtuple("StackLine", ["offset", "content"]) - - -class ExceptionDataParser(object): - def __init__(self): - self.exception = None - - self.epc1 = None - self.epc2 = None - self.epc3 = None - self.excvaddr = None - self.depc = None - - self.ctx = None - - self.sp = None - self.end = None - self.offset = None - - self.stack = [] - - def _parse_backtrace(self, line): - if line.startswith('Backtrace:'): - self.stack = [StackLine(offset=0, content=(addr,)) for addr in BACKTRACE_REGEX.findall(line)] - return None - return self._parse_backtrace - - def _parse_exception(self, line): - match = EXCEPTION_REGEX.match(line) - if match is not None: - self.exception = int(match.group('exc')) - return self._parse_counters - return self._parse_exception - - def _parse_counters(self, line): - match = COUNTER_REGEX.match(line) - if match is not None: - self.epc1 = match.group("epc1") - self.epc2 = match.group("epc2") - self.epc3 = match.group("epc3") - self.excvaddr = match.group("excvaddr") - self.depc = match.group("depc") - return self._parse_ctx - return self._parse_counters - - def _parse_ctx(self, line): - match = CTX_REGEX.match(line) - if match is not None: - self.ctx = match.group("ctx") - return self._parse_pointers - return self._parse_ctx - - def _parse_pointers(self, line): - match = POINTER_REGEX.match(line) - if match is not None: - self.sp = match.group("sp") - self.end = match.group("end") - self.offset = match.group("offset") - return self._parse_stack_begin - return self._parse_pointers - - def _parse_stack_begin(self, line): - if line == STACK_BEGIN: - return self._parse_stack_line - return self._parse_stack_begin - - def _parse_stack_line(self, line): - if line != STACK_END: - match = STACK_REGEX.match(line) - if match is not None: - self.stack.append(StackLine(offset=match.group("off"), - content=(match.group("c1"), match.group("c2"), match.group("c3"), - match.group("c4")))) - return self._parse_stack_line - return None - - def parse_file(self, file, platform, stack_only=False): - if platform == 'ESP32': - func = self._parse_backtrace - else: - func = self._parse_exception - if stack_only: - func = self._parse_stack_begin - - for line in file: - func = func(line.strip()) - if func is None: - break - - if func is not None: - print("ERROR: Parser not complete!") - sys.exit(1) - - -class AddressResolver(object): - def __init__(self, tool_path, elf_path): - self._tool = tool_path - self._elf = elf_path - self._address_map = {} - - def _lookup(self, addresses): - cmd = [self._tool, "-aipfC", "-e", self._elf] + [addr for addr in addresses if addr is not None] - - if sys.version_info[0] < 3: - output = subprocess.check_output(cmd) - else: - output = subprocess.check_output(cmd, encoding="utf-8") - - line_regex = re.compile("^(?P[0-9a-fx]+): (?P.+)$") - - last = None - for line in output.splitlines(): - line = line.strip() - match = line_regex.match(line) - - if match is None: - if last is not None and line.startswith('(inlined by)'): - line = line [12:].strip() - self._address_map[last] += ("\n \-> inlined by: " + line) - continue - - if match.group("result") == '?? ??:0': - continue - - self._address_map[match.group("addr")] = match.group("result") - last = match.group("addr") - - def fill(self, parser): - addresses = [parser.epc1, parser.epc2, parser.epc3, parser.excvaddr, parser.sp, parser.end, parser.offset] - for line in parser.stack: - addresses.extend(line.content) - - self._lookup(addresses) - - def _sanitize_addr(self, addr): - if addr.startswith("0x"): - addr = addr[2:] - - fill = "0" * (8 - len(addr)) - return "0x" + fill + addr - - def resolve_addr(self, addr): - out = self._sanitize_addr(addr) - - if out in self._address_map: - out += ": " + self._address_map[out] - - return out - - def resolve_stack_addr(self, addr, full=True): - addr = self._sanitize_addr(addr) - if addr in self._address_map: - return addr + ": " + self._address_map[addr] - - if full: - return "[DATA (0x" + addr + ")]" - - return None - - -def print_addr(name, value, resolver): - print("{}:{} {}".format(name, " " * (8 - len(name)), resolver.resolve_addr(value))) - - -def print_stack_full(lines, resolver): - print("stack:") - for line in lines: - print(line.offset + ":") - for content in line.content: - print(" " + resolver.resolve_stack_addr(content)) - - -def print_stack(lines, resolver): - print("stack:") - for line in lines: - for content in line.content: - out = resolver.resolve_stack_addr(content, full=False) - if out is None: - continue - print(out) - - -def print_result(parser, resolver, platform, full=True, stack_only=False): - if platform == 'ESP8266' and not stack_only: - print('Exception: {} ({})'.format(parser.exception, EXCEPTIONS[parser.exception])) - - print("") - print_addr("epc1", parser.epc1, resolver) - print_addr("epc2", parser.epc2, resolver) - print_addr("epc3", parser.epc3, resolver) - print_addr("excvaddr", parser.excvaddr, resolver) - print_addr("depc", parser.depc, resolver) - - print("") - print("ctx: " + parser.ctx) - - print("") - print_addr("sp", parser.sp, resolver) - print_addr("end", parser.end, resolver) - print_addr("offset", parser.offset, resolver) - - print("") - if full: - print_stack_full(parser.stack, resolver) - else: - print_stack(parser.stack, resolver) - - -def parse_args(): - parser = argparse.ArgumentParser(description="decode ESP Stacktraces.") - - parser.add_argument("-p", "--platform", help="The platform to decode from", choices=PLATFORMS.keys(), - default="ESP8266") - parser.add_argument("-t", "--tool", help="Path to the xtensa toolchain", - default="~/.platformio/packages/toolchain-xtensa/") - parser.add_argument("-e", "--elf", help="path to elf file", required=True) - parser.add_argument("-f", "--full", help="Print full stack dump", action="store_true") - parser.add_argument("-s", "--stack_only", help="Decode only a stractrace", action="store_true") - parser.add_argument("file", help="The file to read the exception data from ('-' for STDIN)", default="-") - - return parser.parse_args() - - -if __name__ == "__main__": - args = parse_args() - - if args.file == "-": - file = sys.stdin - else: - if not os.path.exists(args.file): - print("ERROR: file " + args.file + " not found") - sys.exit(1) - file = open(args.file, "r") - - addr2line = os.path.join(os.path.abspath(os.path.expanduser(args.tool)), - "bin/xtensa-" + PLATFORMS[args.platform] + "-elf-addr2line") - if os.name == 'nt': - addr2line += '.exe' - if not os.path.exists(addr2line): - print("ERROR: addr2line not found (" + addr2line + ")") - - elf_file = os.path.abspath(os.path.expanduser(args.elf)) - if not os.path.exists(elf_file): - print("ERROR: elf file not found (" + elf_file + ")") - - parser = ExceptionDataParser() - resolver = AddressResolver(addr2line, elf_file) - - parser.parse_file(file, args.platform, args.stack_only) - resolver.fill(parser) - - print_result(parser, resolver, args.platform, args.full, args.stack_only) \ No newline at end of file diff --git a/scripts/decoder.sh b/scripts/decoder.sh deleted file mode 100644 index 93802af72..000000000 --- a/scripts/decoder.sh +++ /dev/null @@ -1,4 +0,0 @@ -# python decoder.py -s -e ../.pio/build/esp8266-local/firmware.elf stackdmp.txt - -python decoder.py -p ESP32 -e ../.pio/build/esp32-local/firmware.elf stackdmp.txt -t ~/.platformio/packages/toolchain-xtensa32 - From 109d8df7826af4151d7f241ddd194551407c360e Mon Sep 17 00:00:00 2001 From: Proddy Date: Mon, 5 Jul 2021 16:54:41 +0200 Subject: [PATCH 022/122] don't show mem updates on EMSESP_DEBUG --- src/system.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index 9d9a0b203..e60012370 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -245,7 +245,7 @@ void System::wifi_tweak() { bool s1 = WiFi.getSleep(); WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE bool s2 = WiFi.getSleep(); - LOG_DEBUG(F("Adjusting WiFi - Tx power %d->%d, Sleep %d->%d"), p1, p2, s1, s2); + LOG_DEBUG(F("[DEBUG] Adjusting WiFi - Tx power %d->%d, Sleep %d->%d"), p1, p2, s1, s2); #endif } @@ -415,11 +415,13 @@ void System::loop() { #ifndef EMSESP_STANDALONE #if defined(EMSESP_DEBUG) +/* static uint32_t last_memcheck_ = 0; if (currentMillis - last_memcheck_ > 10000) { // 10 seconds last_memcheck_ = currentMillis; show_mem("core"); } + */ #endif #endif From d924567e5f950712ee6a9e7933b83c5ed667ab14 Mon Sep 17 00:00:00 2001 From: Proddy Date: Mon, 5 Jul 2021 16:55:23 +0200 Subject: [PATCH 023/122] include debug target --- esp32_partition_debug.csv | 5 +++++ pio_local.ini_example | 18 +++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 esp32_partition_debug.csv diff --git a/esp32_partition_debug.csv b/esp32_partition_debug.csv new file mode 100644 index 000000000..2d4923c49 --- /dev/null +++ b/esp32_partition_debug.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xE000, 0x2000, +app0, app, ota_0, 0x10000, 0x210000, +spiffs, data, spiffs, 0x220000, 0x10000, \ No newline at end of file diff --git a/pio_local.ini_example b/pio_local.ini_example index a8ed24546..4d14b38a7 100644 --- a/pio_local.ini_example +++ b/pio_local.ini_example @@ -2,30 +2,30 @@ [common] ; e.g. use build_flags = -DEMSESP_DEFAULT_BOARD_PROFILE=\"NODEMCU\" +debug_flags = -DEMSESP_WIFI_TWEAK -DEMSESP_DEBUG +; debug_flags = [env:esp32] +; if using OTA enter your details below upload_protocol = espota upload_flags = --port=8266 --auth=ems-esp-neo upload_port = 10.10.10.101 -extra_scripts = - ; to prevent the web UI from building each time, comment out this next line - ; pre:scripts/build_interface.py +; to prevent the web UI from building each time, comment out this next line +; extra_scripts = ; pio run -e debug +; or from Visual Studio Code do PIO -> Project Tasks -> debug -> General -> Upload and Monitor ; options for debugging are: EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_DEBUG_SENSOR [env:debug] board = esp32dev platform = espressif32 -board_build.partitions = esp32_partition_app1984k_spiffs64k.csv +board_build.partitions = esp32_partition_debug.csv upload_protocol = esptool build_type = debug monitor_filters = esp32_exception_decoder debug_tool = esp-prog debug_init_break = tbreak setup -build_flags = ${factory_settings.build_flags} -DEMSESP_DEBUG -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1 -extra_scripts = - ; pre:scripts/build_interface.py - scripts/upload_fw.py - +build_flags = ${factory_settings.build_flags} ${common.debug_flags} -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1 +extra_scripts = pre:scripts/build_interface.py \ No newline at end of file From ac268f0f731abac22be80b4340aadec1e4297aef Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 5 Jul 2021 21:44:04 +0200 Subject: [PATCH 024/122] lower WiFi tx when using -DEMSESP_WIFI_TWEAK --- src/system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index e60012370..a97812312 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -240,7 +240,7 @@ void System::wifi_tweak() { // WIFI_POWER_2dBm = 8,// 2dBm // WIFI_POWER_MINUS_1dBm = -4// -1dBm wifi_power_t p1 = WiFi.getTxPower(); - (void)WiFi.setTxPower(WIFI_POWER_19_5dBm); + (void)WiFi.setTxPower(WIFI_POWER_17dBm); wifi_power_t p2 = WiFi.getTxPower(); bool s1 = WiFi.getSleep(); WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE From bb1602f179bff39904b23ef78de6f7d171e10bdd Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 5 Jul 2021 21:44:12 +0200 Subject: [PATCH 025/122] typo --- lib/framework/NetworkSettingsService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/framework/NetworkSettingsService.cpp b/lib/framework/NetworkSettingsService.cpp index 2c5e7eb3f..79cb60a0b 100644 --- a/lib/framework/NetworkSettingsService.cpp +++ b/lib/framework/NetworkSettingsService.cpp @@ -20,7 +20,7 @@ NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, WiFi.mode(WIFI_MODE_NULL); #if defined(EMSESP_WIFI_TWEAK) - // https: //www.esp32.com/viewtopic.php?t=12055 + // www.esp32.com/viewtopic.php?t=12055 esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, WIFI_BW_HT20); esp_wifi_set_bandwidth(ESP_IF_WIFI_AP, WIFI_BW_HT20); #endif From 75795ab1e95cd4c7601c78c6ada55f208021a262 Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 5 Jul 2021 21:44:38 +0200 Subject: [PATCH 026/122] fix shower on/off when using non-default MQTT booleans --- src/shower.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/shower.cpp b/src/shower.cpp index 6e0871e95..6d3e810d0 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -118,8 +118,11 @@ void Shower::send_mqtt_stat(bool state, bool force) { doc["uniq_id"] = FJSON("shower_active"); doc["~"] = Mqtt::base(); // default ems-esp doc["stat_t"] = FJSON("~/shower_active"); - JsonObject dev = doc.createNestedObject("dev"); - JsonArray ids = dev.createNestedArray("ids"); + char result[10]; + doc[F("payload_on")] = Helpers::render_boolean(result, true); + doc[F("payload_off")] = Helpers::render_boolean(result, false); + JsonObject dev = doc.createNestedObject("dev"); + JsonArray ids = dev.createNestedArray("ids"); ids.add("ems-esp"); char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; @@ -151,19 +154,9 @@ void Shower::shower_alert_start() { void Shower::publish_values() { StaticJsonDocument doc; - if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) { - doc["shower_timer"] = shower_timer_ ? "on" : "off"; - doc["shower_alert"] = shower_alert_ ? "on" : "off"; - } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) { - doc["shower_timer"] = shower_timer_ ? "ON" : "OFF"; - doc["shower_alert"] = shower_alert_ ? "ON" : "OFF"; - } else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) { - doc["shower_timer"] = shower_timer_; - doc["shower_alert"] = shower_alert_; - } else { - doc["shower_timer"] = shower_timer_ ? 1 : 0; - doc["shower_alert"] = shower_alert_ ? 1 : 0; - } + char result[10]; + doc["shower_timer"] = Helpers::render_boolean(result, shower_timer_); + doc["shower_alert"] = Helpers::render_boolean(result, shower_alert_); // only publish shower duration if there is a value if (duration_ > SHOWER_MIN_DURATION) { From 05cd96f2bebcabb711e5f9eb16e1c05baa1e2dee Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 5 Jul 2021 22:43:08 +0200 Subject: [PATCH 027/122] IPv6 support by MichaelDvP - #83 --- lib/AsyncTCP/src/AsyncTCP.cpp | 1017 ++++++++++++++++++--------------- lib/AsyncTCP/src/AsyncTCP.h | 226 ++++---- src/version.h | 2 +- src/web/WebStatusService.cpp | 14 + 4 files changed, 697 insertions(+), 562 deletions(-) diff --git a/lib/AsyncTCP/src/AsyncTCP.cpp b/lib/AsyncTCP/src/AsyncTCP.cpp index 89ff6ee32..aff7842c1 100644 --- a/lib/AsyncTCP/src/AsyncTCP.cpp +++ b/lib/AsyncTCP/src/AsyncTCP.cpp @@ -22,7 +22,7 @@ #include "Arduino.h" #include "AsyncTCP.h" -extern "C"{ +extern "C" { #include "lwip/opt.h" #include "lwip/tcp.h" #include "lwip/inet.h" @@ -36,44 +36,52 @@ extern "C"{ * */ typedef enum { - LWIP_TCP_SENT, LWIP_TCP_RECV, LWIP_TCP_FIN, LWIP_TCP_ERROR, LWIP_TCP_POLL, LWIP_TCP_CLEAR, LWIP_TCP_ACCEPT, LWIP_TCP_CONNECTED, LWIP_TCP_DNS + LWIP_TCP_SENT, + LWIP_TCP_RECV, + LWIP_TCP_FIN, + LWIP_TCP_ERROR, + LWIP_TCP_POLL, + LWIP_TCP_CLEAR, + LWIP_TCP_ACCEPT, + LWIP_TCP_CONNECTED, + LWIP_TCP_DNS } lwip_event_t; typedef struct { - lwip_event_t event; - void *arg; - union { - struct { - void * pcb; - int8_t err; - } connected; - struct { - int8_t err; - } error; - struct { - tcp_pcb * pcb; - uint16_t len; - } sent; - struct { - tcp_pcb * pcb; - pbuf * pb; - int8_t err; - } recv; - struct { - tcp_pcb * pcb; - int8_t err; - } fin; - struct { - tcp_pcb * pcb; - } poll; - struct { - AsyncClient * client; - } accept; - struct { - const char * name; - ip_addr_t addr; - } dns; - }; + lwip_event_t event; + void * arg; + union { + struct { + void * pcb; + int8_t err; + } connected; + struct { + int8_t err; + } error; + struct { + tcp_pcb * pcb; + uint16_t len; + } sent; + struct { + tcp_pcb * pcb; + pbuf * pb; + int8_t err; + } recv; + struct { + tcp_pcb * pcb; + int8_t err; + } fin; + struct { + tcp_pcb * pcb; + } poll; + struct { + AsyncClient * client; + } accept; + struct { + const char * name; + ip_addr_t addr; + } dns; + }; } lwip_event_packet_t; static xQueueHandle _async_queue; @@ -81,122 +89,122 @@ static TaskHandle_t _async_service_task_handle = NULL; SemaphoreHandle_t _slots_lock; -const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; -static uint32_t _closed_slots[_number_of_closed_slots]; -static uint32_t _closed_index = []() { +const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; +static uint32_t _closed_slots[_number_of_closed_slots]; +static uint32_t _closed_index = []() { _slots_lock = xSemaphoreCreateBinary(); xSemaphoreGive(_slots_lock); - for (int i = 0; i < _number_of_closed_slots; ++ i) { + for (int i = 0; i < _number_of_closed_slots; ++i) { _closed_slots[i] = 1; } return 1; }(); -static inline bool _init_async_event_queue(){ - if(!_async_queue){ +static inline bool _init_async_event_queue() { + if (!_async_queue) { _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t *)); - if(!_async_queue){ + if (!_async_queue) { return false; } } return true; } -static inline bool _send_async_event(lwip_event_packet_t ** e){ +static inline bool _send_async_event(lwip_event_packet_t ** e) { return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS; } -static inline bool _prepend_async_event(lwip_event_packet_t ** e){ +static inline bool _prepend_async_event(lwip_event_packet_t ** e) { return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; } -static inline bool _get_async_event(lwip_event_packet_t ** e){ +static inline bool _get_async_event(lwip_event_packet_t ** e) { return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; } -static bool _remove_events_with_arg(void * arg){ +static bool _remove_events_with_arg(void * arg) { lwip_event_packet_t * first_packet = NULL; - lwip_event_packet_t * packet = NULL; + lwip_event_packet_t * packet = NULL; - if(!_async_queue){ + if (!_async_queue) { return false; } //figure out which is the first packet so we can keep the order - while(!first_packet){ - if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS){ + while (!first_packet) { + if (xQueueReceive(_async_queue, &first_packet, 0) != pdPASS) { return false; } //discard packet if matching - if((int)first_packet->arg == (int)arg){ + if ((int)first_packet->arg == (int)arg) { free(first_packet); first_packet = NULL; - //return first packet to the back of the queue - } else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS){ + //return first packet to the back of the queue + } else if (xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS) { return false; } } - while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet){ - if(xQueueReceive(_async_queue, &packet, 0) != pdPASS){ + while (xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet) { + if (xQueueReceive(_async_queue, &packet, 0) != pdPASS) { return false; } - if((int)packet->arg == (int)arg){ + if ((int)packet->arg == (int)arg) { free(packet); packet = NULL; - } else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS){ + } else if (xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS) { return false; } } return true; } -static void _handle_async_event(lwip_event_packet_t * e){ - if(e->arg == NULL){ +static void _handle_async_event(lwip_event_packet_t * e) { + if (e->arg == NULL) { // do nothing when arg is NULL //ets_printf("event arg == NULL: 0x%08x\n", e->recv.pcb); - } else if(e->event == LWIP_TCP_CLEAR){ + } else if (e->event == LWIP_TCP_CLEAR) { _remove_events_with_arg(e->arg); - } else if(e->event == LWIP_TCP_RECV){ + } else if (e->event == LWIP_TCP_RECV) { //ets_printf("-R: 0x%08x\n", e->recv.pcb); AsyncClient::_s_recv(e->arg, e->recv.pcb, e->recv.pb, e->recv.err); - } else if(e->event == LWIP_TCP_FIN){ + } else if (e->event == LWIP_TCP_FIN) { //ets_printf("-F: 0x%08x\n", e->fin.pcb); AsyncClient::_s_fin(e->arg, e->fin.pcb, e->fin.err); - } else if(e->event == LWIP_TCP_SENT){ + } else if (e->event == LWIP_TCP_SENT) { //ets_printf("-S: 0x%08x\n", e->sent.pcb); AsyncClient::_s_sent(e->arg, e->sent.pcb, e->sent.len); - } else if(e->event == LWIP_TCP_POLL){ + } else if (e->event == LWIP_TCP_POLL) { //ets_printf("-P: 0x%08x\n", e->poll.pcb); AsyncClient::_s_poll(e->arg, e->poll.pcb); - } else if(e->event == LWIP_TCP_ERROR){ + } else if (e->event == LWIP_TCP_ERROR) { //ets_printf("-E: 0x%08x %d\n", e->arg, e->error.err); AsyncClient::_s_error(e->arg, e->error.err); - } else if(e->event == LWIP_TCP_CONNECTED){ + } else if (e->event == LWIP_TCP_CONNECTED) { //ets_printf("C: 0x%08x 0x%08x %d\n", e->arg, e->connected.pcb, e->connected.err); AsyncClient::_s_connected(e->arg, e->connected.pcb, e->connected.err); - } else if(e->event == LWIP_TCP_ACCEPT){ + } else if (e->event == LWIP_TCP_ACCEPT) { //ets_printf("A: 0x%08x 0x%08x\n", e->arg, e->accept.client); AsyncServer::_s_accepted(e->arg, e->accept.client); - } else if(e->event == LWIP_TCP_DNS){ + } else if (e->event == LWIP_TCP_DNS) { //ets_printf("D: 0x%08x %s = %s\n", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr)); AsyncClient::_s_dns_found(e->dns.name, &e->dns.addr, e->arg); } - free((void*)(e)); + free((void *)(e)); } -static void _async_service_task(void *pvParameters){ +static void _async_service_task(void * pvParameters) { lwip_event_packet_t * packet = NULL; for (;;) { - if(_get_async_event(&packet)){ + if (_get_async_event(&packet)) { #if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_add(NULL) != ESP_OK){ + if (esp_task_wdt_add(NULL) != ESP_OK) { log_e("Failed to add async task to WDT"); } #endif _handle_async_event(packet); #if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_delete(NULL) != ESP_OK){ + if (esp_task_wdt_delete(NULL) != ESP_OK) { log_e("Failed to remove loop task from WDT"); } #endif @@ -213,13 +221,13 @@ static void _stop_async_task(){ } } */ -static bool _start_async_task(){ - if(!_init_async_event_queue()){ +static bool _start_async_task() { + if (!_init_async_event_queue()) { return false; } - if(!_async_service_task_handle){ + if (!_async_service_task_handle) { xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE); - if(!_async_service_task_handle){ + if (!_async_service_task_handle) { return false; } } @@ -232,10 +240,10 @@ static bool _start_async_task(){ static int8_t _tcp_clear_events(void * arg) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CLEAR; - e->arg = arg; + e->event = LWIP_TCP_CLEAR; + e->arg = arg; if (!_prepend_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } @@ -243,12 +251,12 @@ static int8_t _tcp_clear_events(void * arg) { static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) { //ets_printf("+C: 0x%08x\n", pcb); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CONNECTED; - e->arg = arg; - e->connected.pcb = pcb; - e->connected.err = err; + e->event = LWIP_TCP_CONNECTED; + e->arg = arg; + e->connected.pcb = pcb; + e->connected.err = err; if (!_prepend_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } @@ -256,34 +264,34 @@ static int8_t _tcp_connected(void * arg, tcp_pcb * pcb, int8_t err) { static int8_t _tcp_poll(void * arg, struct tcp_pcb * pcb) { //ets_printf("+P: 0x%08x\n", pcb); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_POLL; - e->arg = arg; - e->poll.pcb = pcb; + e->event = LWIP_TCP_POLL; + e->arg = arg; + e->poll.pcb = pcb; if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } -static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { +static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf * pb, int8_t err) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->arg = arg; - if(pb){ + e->arg = arg; + if (pb) { //ets_printf("+R: 0x%08x\n", pcb); - e->event = LWIP_TCP_RECV; + e->event = LWIP_TCP_RECV; e->recv.pcb = pcb; - e->recv.pb = pb; + e->recv.pb = pb; e->recv.err = err; } else { //ets_printf("+F: 0x%08x\n", pcb); - e->event = LWIP_TCP_FIN; + e->event = LWIP_TCP_FIN; e->fin.pcb = pcb; e->fin.err = err; //close the PCB in LwIP thread AsyncClient::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); } if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } @@ -291,12 +299,12 @@ static int8_t _tcp_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_ static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { //ets_printf("+S: 0x%08x\n", pcb); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_SENT; - e->arg = arg; - e->sent.pcb = pcb; - e->sent.len = len; + e->event = LWIP_TCP_SENT; + e->arg = arg; + e->sent.pcb = pcb; + e->sent.len = len; if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } @@ -304,19 +312,19 @@ static int8_t _tcp_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { static void _tcp_error(void * arg, int8_t err) { //ets_printf("+E: 0x%08x\n", arg); lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ERROR; - e->arg = arg; - e->error.err = err; + e->event = LWIP_TCP_ERROR; + e->arg = arg; + e->error.err = err; if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } } static void _tcp_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); //ets_printf("+DNS: name=%s ipaddr=0x%08x arg=%x\n", name, ipaddr, arg); - e->event = LWIP_TCP_DNS; - e->arg = arg; + e->event = LWIP_TCP_DNS; + e->arg = arg; e->dns.name = name; if (ipaddr) { memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); @@ -324,18 +332,18 @@ static void _tcp_dns_found(const char * name, struct ip_addr * ipaddr, void * ar memset(&e->dns.addr, 0, sizeof(e->dns.addr)); } if (!_send_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } } //Used to switch out from LwIP thread static int8_t _tcp_accept(void * arg, AsyncClient * client) { lwip_event_packet_t * e = (lwip_event_packet_t *)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ACCEPT; - e->arg = arg; - e->accept.client = client; + e->event = LWIP_TCP_ACCEPT; + e->arg = arg; + e->accept.client = client; if (!_prepend_async_event(&e)) { - free((void*)(e)); + free((void *)(e)); } return ERR_OK; } @@ -348,76 +356,76 @@ static int8_t _tcp_accept(void * arg, AsyncClient * client) { typedef struct { struct tcpip_api_call_data call; - tcp_pcb * pcb; - int8_t closed_slot; - int8_t err; + tcp_pcb * pcb; + int8_t closed_slot; + int8_t err; union { - struct { - const char* data; - size_t size; - uint8_t apiflags; - } write; - size_t received; - struct { - ip_addr_t * addr; - uint16_t port; - tcp_connected_fn cb; - } connect; - struct { - ip_addr_t * addr; - uint16_t port; - } bind; - uint8_t backlog; + struct { + const char * data; + size_t size; + uint8_t apiflags; + } write; + size_t received; + struct { + ip_addr_t * addr; + uint16_t port; + tcp_connected_fn cb; + } connect; + struct { + ip_addr_t * addr; + uint16_t port; + } bind; + uint8_t backlog; }; } tcp_api_call_t; -static err_t _tcp_output_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_output_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + msg->err = ERR_CONN; + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { msg->err = tcp_output(msg->pcb); } return msg->err; } static esp_err_t _tcp_output(tcp_pcb * pcb, int8_t closed_slot) { - if(!pcb){ + if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; - msg.pcb = pcb; + msg.pcb = pcb; msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_write_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_write_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + msg->err = ERR_CONN; + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); } return msg->err; } -static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char* data, size_t size, uint8_t apiflags) { - if(!pcb){ +static esp_err_t _tcp_write(tcp_pcb * pcb, int8_t closed_slot, const char * data, size_t size, uint8_t apiflags) { + if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; - msg.write.data = data; - msg.write.size = size; + msg.pcb = pcb; + msg.closed_slot = closed_slot; + msg.write.data = data; + msg.write.size = size; msg.write.apiflags = apiflags; - tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_recved_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + msg->err = ERR_CONN; + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { msg->err = 0; tcp_recved(msg->pcb, msg->received); } @@ -425,112 +433,112 @@ static err_t _tcp_recved_api(struct tcpip_api_call_data *api_call_msg){ } static esp_err_t _tcp_recved(tcp_pcb * pcb, int8_t closed_slot, size_t len) { - if(!pcb){ + if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; - msg.pcb = pcb; + msg.pcb = pcb; msg.closed_slot = closed_slot; - msg.received = len; - tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg); + msg.received = len; + tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_close_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + msg->err = ERR_CONN; + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { msg->err = tcp_close(msg->pcb); } return msg->err; } static esp_err_t _tcp_close(tcp_pcb * pcb, int8_t closed_slot) { - if(!pcb){ + if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; - msg.pcb = pcb; + msg.pcb = pcb; msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_abort_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_abort_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = ERR_CONN; - if(msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { + msg->err = ERR_CONN; + if (msg->closed_slot == -1 || !_closed_slots[msg->closed_slot]) { tcp_abort(msg->pcb); } return msg->err; } static esp_err_t _tcp_abort(tcp_pcb * pcb, int8_t closed_slot) { - if(!pcb){ + if (!pcb) { return ERR_CONN; } tcp_api_call_t msg; - msg.pcb = pcb; + msg.pcb = pcb; msg.closed_slot = closed_slot; - tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg); + tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_connect_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_connect_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); + msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, msg->connect.cb); return msg->err; } static esp_err_t _tcp_connect(tcp_pcb * pcb, int8_t closed_slot, ip_addr_t * addr, uint16_t port, tcp_connected_fn cb) { - if(!pcb){ + if (!pcb) { return ESP_FAIL; } tcp_api_call_t msg; - msg.pcb = pcb; - msg.closed_slot = closed_slot; + msg.pcb = pcb; + msg.closed_slot = closed_slot; msg.connect.addr = addr; msg.connect.port = port; - msg.connect.cb = cb; - tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg); + msg.connect.cb = cb; + tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_bind_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_bind_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); + msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); return msg->err; } static esp_err_t _tcp_bind(tcp_pcb * pcb, ip_addr_t * addr, uint16_t port) { - if(!pcb){ + if (!pcb) { return ESP_FAIL; } tcp_api_call_t msg; - msg.pcb = pcb; + msg.pcb = pcb; msg.closed_slot = -1; - msg.bind.addr = addr; - msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); + msg.bind.addr = addr; + msg.bind.port = port; + tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data *)&msg); return msg.err; } -static err_t _tcp_listen_api(struct tcpip_api_call_data *api_call_msg){ +static err_t _tcp_listen_api(struct tcpip_api_call_data * api_call_msg) { tcp_api_call_t * msg = (tcp_api_call_t *)api_call_msg; - msg->err = 0; - msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); + msg->err = 0; + msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); return msg->err; } static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) { - if(!pcb){ + if (!pcb) { return NULL; } tcp_api_call_t msg; - msg.pcb = pcb; + msg.pcb = pcb; msg.closed_slot = -1; - msg.backlog = backlog?backlog:0xFF; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); + msg.backlog = backlog ? backlog : 0xFF; + tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data *)&msg); return msg.pcb; } @@ -540,34 +548,33 @@ static tcp_pcb * _tcp_listen_with_backlog(tcp_pcb * pcb, uint8_t backlog) { Async TCP Client */ -AsyncClient::AsyncClient(tcp_pcb* pcb) -: _connect_cb(0) -, _connect_cb_arg(0) -, _discard_cb(0) -, _discard_cb_arg(0) -, _sent_cb(0) -, _sent_cb_arg(0) -, _error_cb(0) -, _error_cb_arg(0) -, _recv_cb(0) -, _recv_cb_arg(0) -, _pb_cb(0) -, _pb_cb_arg(0) -, _timeout_cb(0) -, _timeout_cb_arg(0) -, _pcb_busy(false) -, _pcb_sent_at(0) -, _ack_pcb(true) -, _rx_last_packet(0) -, _rx_since_timeout(0) -, _ack_timeout(ASYNC_MAX_ACK_TIME) -, _connect_port(0) -, prev(NULL) -, next(NULL) -{ - _pcb = pcb; +AsyncClient::AsyncClient(tcp_pcb * pcb) + : _connect_cb(0) + , _connect_cb_arg(0) + , _discard_cb(0) + , _discard_cb_arg(0) + , _sent_cb(0) + , _sent_cb_arg(0) + , _error_cb(0) + , _error_cb_arg(0) + , _recv_cb(0) + , _recv_cb_arg(0) + , _pb_cb(0) + , _pb_cb_arg(0) + , _timeout_cb(0) + , _timeout_cb_arg(0) + , _pcb_busy(false) + , _pcb_sent_at(0) + , _ack_pcb(true) + , _rx_last_packet(0) + , _rx_since_timeout(0) + , _ack_timeout(ASYNC_MAX_ACK_TIME) + , _connect_port(0) + , prev(NULL) + , next(NULL) { + _pcb = pcb; _closed_slot = -1; - if(_pcb){ + if (_pcb) { _allocate_closed_slot(); _rx_last_packet = millis(); tcp_arg(_pcb, this); @@ -578,8 +585,8 @@ AsyncClient::AsyncClient(tcp_pcb* pcb) } } -AsyncClient::~AsyncClient(){ - if(_pcb) { +AsyncClient::~AsyncClient() { + if (_pcb) { _close(); } _free_closed_slot(); @@ -589,12 +596,12 @@ AsyncClient::~AsyncClient(){ * Operators * */ -AsyncClient& AsyncClient::operator=(const AsyncClient& other){ +AsyncClient & AsyncClient::operator=(const AsyncClient & other) { if (_pcb) { _close(); } - _pcb = other._pcb; + _pcb = other._pcb; _closed_slot = other._closed_slot; if (_pcb) { _rx_last_packet = millis(); @@ -607,20 +614,20 @@ AsyncClient& AsyncClient::operator=(const AsyncClient& other){ return *this; } -bool AsyncClient::operator==(const AsyncClient &other) { +bool AsyncClient::operator==(const AsyncClient & other) { return _pcb == other._pcb; } -AsyncClient & AsyncClient::operator+=(const AsyncClient &other) { - if(next == NULL){ - next = (AsyncClient*)(&other); +AsyncClient & AsyncClient::operator+=(const AsyncClient & other) { + if (next == NULL) { + next = (AsyncClient *)(&other); next->prev = this; } else { - AsyncClient *c = next; - while(c->next != NULL) { + AsyncClient * c = next; + while (c->next != NULL) { c = c->next; } - c->next =(AsyncClient*)(&other); + c->next = (AsyncClient *)(&other); c->next->prev = c; } return *this; @@ -630,43 +637,43 @@ AsyncClient & AsyncClient::operator+=(const AsyncClient &other) { * Callback Setters * */ -void AsyncClient::onConnect(AcConnectHandler cb, void* arg){ - _connect_cb = cb; +void AsyncClient::onConnect(AcConnectHandler cb, void * arg) { + _connect_cb = cb; _connect_cb_arg = arg; } -void AsyncClient::onDisconnect(AcConnectHandler cb, void* arg){ - _discard_cb = cb; +void AsyncClient::onDisconnect(AcConnectHandler cb, void * arg) { + _discard_cb = cb; _discard_cb_arg = arg; } -void AsyncClient::onAck(AcAckHandler cb, void* arg){ - _sent_cb = cb; +void AsyncClient::onAck(AcAckHandler cb, void * arg) { + _sent_cb = cb; _sent_cb_arg = arg; } -void AsyncClient::onError(AcErrorHandler cb, void* arg){ - _error_cb = cb; +void AsyncClient::onError(AcErrorHandler cb, void * arg) { + _error_cb = cb; _error_cb_arg = arg; } -void AsyncClient::onData(AcDataHandler cb, void* arg){ - _recv_cb = cb; +void AsyncClient::onData(AcDataHandler cb, void * arg) { + _recv_cb = cb; _recv_cb_arg = arg; } -void AsyncClient::onPacket(AcPacketHandler cb, void* arg){ - _pb_cb = cb; - _pb_cb_arg = arg; +void AsyncClient::onPacket(AcPacketHandler cb, void * arg) { + _pb_cb = cb; + _pb_cb_arg = arg; } -void AsyncClient::onTimeout(AcTimeoutHandler cb, void* arg){ - _timeout_cb = cb; +void AsyncClient::onTimeout(AcTimeoutHandler cb, void * arg) { + _timeout_cb = cb; _timeout_cb_arg = arg; } -void AsyncClient::onPoll(AcConnectHandler cb, void* arg){ - _poll_cb = cb; +void AsyncClient::onPoll(AcConnectHandler cb, void * arg) { + _poll_cb = cb; _poll_cb_arg = arg; } @@ -674,22 +681,18 @@ void AsyncClient::onPoll(AcConnectHandler cb, void* arg){ * Main Public Methods * */ -bool AsyncClient::connect(IPAddress ip, uint16_t port){ - if (_pcb){ +bool AsyncClient::_connect(ip_addr_t addr, uint16_t port) { + if (_pcb) { log_w("already connected, state %d", _pcb->state); return false; } - if(!_start_async_task()){ + if (!_start_async_task()) { log_e("failed to start task"); return false; } - ip_addr_t addr; - addr.type = IPADDR_TYPE_V4; - addr.u_addr.ip4.addr = ip; - - tcp_pcb* pcb = tcp_new_ip_type(IPADDR_TYPE_V4); - if (!pcb){ + tcp_pcb * pcb = tcp_new_ip_type(addr.type); + if (!pcb) { log_e("pcb == NULL"); return false; } @@ -699,23 +702,41 @@ bool AsyncClient::connect(IPAddress ip, uint16_t port){ tcp_recv(pcb, &_tcp_recv); tcp_sent(pcb, &_tcp_sent); tcp_poll(pcb, &_tcp_poll, 1); - //_tcp_connect(pcb, &addr, port,(tcp_connected_fn)&_s_connected); - _tcp_connect(pcb, _closed_slot, &addr, port,(tcp_connected_fn)&_tcp_connected); + _tcp_connect(pcb, _closed_slot, &addr, port, (tcp_connected_fn)&_tcp_connected); return true; } -bool AsyncClient::connect(const char* host, uint16_t port){ +bool AsyncClient::connect(IPAddress ip, uint16_t port) { ip_addr_t addr; - - if(!_start_async_task()){ - log_e("failed to start task"); - return false; + addr.type = IPADDR_TYPE_V4; + addr.u_addr.ip4.addr = ip; + + return _connect(addr, port); +} + +bool AsyncClient::connect(IPv6Address ip, uint16_t port) { + ip_addr_t addr; + addr.type = IPADDR_TYPE_V6; + memcpy(addr.u_addr.ip6.addr, static_cast(ip), sizeof(uint32_t) * 4); + + return _connect(addr, port); +} + +bool AsyncClient::connect(const char * host, uint16_t port) { + ip_addr_t addr; + + if (!_start_async_task()) { + log_e("failed to start task"); + return false; } - + err_t err = dns_gethostbyname(host, &addr, (dns_found_callback)&_tcp_dns_found, this); - if(err == ERR_OK) { + if (err == ERR_OK) { + if (addr.type == IPADDR_TYPE_V6) { + return connect(IPv6Address(addr.u_addr.ip6.addr), port); + } return connect(IPAddress(addr.u_addr.ip4.addr), port); - } else if(err == ERR_INPROGRESS) { + } else if (err == ERR_INPROGRESS) { _connect_port = port; return true; } @@ -723,82 +744,82 @@ bool AsyncClient::connect(const char* host, uint16_t port){ return false; } -void AsyncClient::close(bool now){ - if(_pcb){ +void AsyncClient::close(bool now) { + if (_pcb) { _tcp_recved(_pcb, _closed_slot, _rx_ack_len); } _close(); } -int8_t AsyncClient::abort(){ - if(_pcb) { - _tcp_abort(_pcb, _closed_slot ); +int8_t AsyncClient::abort() { + if (_pcb) { + _tcp_abort(_pcb, _closed_slot); _pcb = NULL; } return ERR_ABRT; } -size_t AsyncClient::space(){ - if((_pcb != NULL) && (_pcb->state == 4)){ +size_t AsyncClient::space() { + if ((_pcb != NULL) && (_pcb->state == 4)) { return tcp_sndbuf(_pcb); } return 0; } -size_t AsyncClient::add(const char* data, size_t size, uint8_t apiflags) { - if(!_pcb || size == 0 || data == NULL) { +size_t AsyncClient::add(const char * data, size_t size, uint8_t apiflags) { + if (!_pcb || size == 0 || data == NULL) { return 0; } size_t room = space(); - if(!room) { + if (!room) { return 0; } size_t will_send = (room < size) ? room : size; - int8_t err = ERR_OK; - err = _tcp_write(_pcb, _closed_slot, data, will_send, apiflags); - if(err != ERR_OK) { + int8_t err = ERR_OK; + err = _tcp_write(_pcb, _closed_slot, data, will_send, apiflags); + if (err != ERR_OK) { return 0; } return will_send; } -bool AsyncClient::send(){ +bool AsyncClient::send() { int8_t err = ERR_OK; - err = _tcp_output(_pcb, _closed_slot); - if(err == ERR_OK){ - _pcb_busy = true; + err = _tcp_output(_pcb, _closed_slot); + if (err == ERR_OK) { + _pcb_busy = true; _pcb_sent_at = millis(); return true; } return false; } -size_t AsyncClient::ack(size_t len){ - if(len > _rx_ack_len) +size_t AsyncClient::ack(size_t len) { + if (len > _rx_ack_len) len = _rx_ack_len; - if(len){ + if (len) { _tcp_recved(_pcb, _closed_slot, len); } _rx_ack_len -= len; return len; } -void AsyncClient::ackPacket(struct pbuf * pb){ - if(!pb){ - return; - } - _tcp_recved(_pcb, _closed_slot, pb->len); - pbuf_free(pb); +void AsyncClient::ackPacket(struct pbuf * pb) { + if (!pb) { + return; + } + _tcp_recved(_pcb, _closed_slot, pb->len); + pbuf_free(pb); } /* * Main Private Methods * */ -int8_t AsyncClient::_close(){ +int8_t AsyncClient::_close() { //ets_printf("X: 0x%08x\n", (uint32_t)this); int8_t err = ERR_OK; - if(_pcb) { + if (_pcb) { //log_i(""); tcp_arg(_pcb, NULL); tcp_sent(_pcb, NULL); @@ -807,24 +828,24 @@ int8_t AsyncClient::_close(){ tcp_poll(_pcb, NULL, 0); _tcp_clear_events(this); err = _tcp_close(_pcb, _closed_slot); - if(err != ERR_OK) { + if (err != ERR_OK) { err = abort(); } _pcb = NULL; - if(_discard_cb) { + if (_discard_cb) { _discard_cb(_discard_cb_arg, this); } } return err; } -void AsyncClient::_allocate_closed_slot(){ +void AsyncClient::_allocate_closed_slot() { xSemaphoreTake(_slots_lock, portMAX_DELAY); uint32_t closed_slot_min_index = 0; - for (int i = 0; i < _number_of_closed_slots; ++ i) { + for (int i = 0; i < _number_of_closed_slots; ++i) { if ((_closed_slot == -1 || _closed_slots[i] <= closed_slot_min_index) && _closed_slots[i] != 0) { closed_slot_min_index = _closed_slots[i]; - _closed_slot = i; + _closed_slot = i; } } if (_closed_slot != -1) { @@ -833,11 +854,11 @@ void AsyncClient::_allocate_closed_slot(){ xSemaphoreGive(_slots_lock); } -void AsyncClient::_free_closed_slot(){ +void AsyncClient::_free_closed_slot() { if (_closed_slot != -1) { _closed_slots[_closed_slot] = _closed_index; - _closed_slot = -1; - ++ _closed_index; + _closed_slot = -1; + ++_closed_index; } } @@ -845,25 +866,25 @@ void AsyncClient::_free_closed_slot(){ * Private Callbacks * */ -int8_t AsyncClient::_connected(void* pcb, int8_t err){ - _pcb = reinterpret_cast(pcb); - if(_pcb){ +int8_t AsyncClient::_connected(void * pcb, int8_t err) { + _pcb = reinterpret_cast(pcb); + if (_pcb) { _rx_last_packet = millis(); - _pcb_busy = false; -// tcp_recv(_pcb, &_tcp_recv); -// tcp_sent(_pcb, &_tcp_sent); -// tcp_poll(_pcb, &_tcp_poll, 1); + _pcb_busy = false; + // tcp_recv(_pcb, &_tcp_recv); + // tcp_sent(_pcb, &_tcp_sent); + // tcp_poll(_pcb, &_tcp_poll, 1); } - if(_connect_cb) { + if (_connect_cb) { _connect_cb(_connect_cb_arg, this); } return ERR_OK; } void AsyncClient::_error(int8_t err) { - if(_pcb){ + if (_pcb) { tcp_arg(_pcb, NULL); - if(_pcb->state == LISTEN) { + if (_pcb->state == LISTEN) { tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); @@ -871,28 +892,28 @@ void AsyncClient::_error(int8_t err) { } _pcb = NULL; } - if(_error_cb) { + if (_error_cb) { _error_cb(_error_cb_arg, this, err); } - if(_discard_cb) { + if (_discard_cb) { _discard_cb(_discard_cb_arg, this); } } //In LwIP Thread -int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) { - if(!_pcb || pcb != _pcb){ +int8_t AsyncClient::_lwip_fin(tcp_pcb * pcb, int8_t err) { + if (!_pcb || pcb != _pcb) { log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); return ERR_OK; } tcp_arg(_pcb, NULL); - if(_pcb->state == LISTEN) { + if (_pcb->state == LISTEN) { tcp_sent(_pcb, NULL); tcp_recv(_pcb, NULL); tcp_err(_pcb, NULL); tcp_poll(_pcb, NULL, 0); } - if(tcp_close(_pcb) != ERR_OK) { + if (tcp_close(_pcb) != ERR_OK) { tcp_abort(_pcb); } _free_closed_slot(); @@ -901,41 +922,41 @@ int8_t AsyncClient::_lwip_fin(tcp_pcb* pcb, int8_t err) { } //In Async Thread -int8_t AsyncClient::_fin(tcp_pcb* pcb, int8_t err) { +int8_t AsyncClient::_fin(tcp_pcb * pcb, int8_t err) { _tcp_clear_events(this); - if(_discard_cb) { + if (_discard_cb) { _discard_cb(_discard_cb_arg, this); } return ERR_OK; } -int8_t AsyncClient::_sent(tcp_pcb* pcb, uint16_t len) { +int8_t AsyncClient::_sent(tcp_pcb * pcb, uint16_t len) { _rx_last_packet = millis(); //log_i("%u", len); _pcb_busy = false; - if(_sent_cb) { + if (_sent_cb) { _sent_cb(_sent_cb_arg, this, len, (millis() - _pcb_sent_at)); } return ERR_OK; } -int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { - while(pb != NULL) { +int8_t AsyncClient::_recv(tcp_pcb * pcb, pbuf * pb, int8_t err) { + while (pb != NULL) { _rx_last_packet = millis(); //we should not ack before we assimilate the data _ack_pcb = true; - pbuf *b = pb; - pb = b->next; - b->next = NULL; - if(_pb_cb){ + pbuf * b = pb; + pb = b->next; + b->next = NULL; + if (_pb_cb) { _pb_cb(_pb_cb_arg, this, b); } else { - if(_recv_cb) { + if (_recv_cb) { _recv_cb(_recv_cb_arg, this, b->payload, b->len); } - if(!_ack_pcb) { + if (!_ack_pcb) { _rx_ack_len += b->len; - } else if(_pcb) { + } else if (_pcb) { _tcp_recved(_pcb, _closed_slot, b->len); } pbuf_free(b); @@ -944,12 +965,12 @@ int8_t AsyncClient::_recv(tcp_pcb* pcb, pbuf* pb, int8_t err) { return ERR_OK; } -int8_t AsyncClient::_poll(tcp_pcb* pcb){ - if(!_pcb){ +int8_t AsyncClient::_poll(tcp_pcb * pcb) { + if (!_pcb) { log_w("pcb is NULL"); return ERR_OK; } - if(pcb != _pcb){ + if (pcb != _pcb) { log_e("0x%08x != 0x%08x", (uint32_t)pcb, (uint32_t)_pcb); return ERR_OK; } @@ -957,34 +978,36 @@ int8_t AsyncClient::_poll(tcp_pcb* pcb){ uint32_t now = millis(); // ACK Timeout - if(_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout){ + if (_pcb_busy && _ack_timeout && (now - _pcb_sent_at) >= _ack_timeout) { _pcb_busy = false; log_w("ack timeout %d", pcb->state); - if(_timeout_cb) + if (_timeout_cb) _timeout_cb(_timeout_cb_arg, this, (now - _pcb_sent_at)); return ERR_OK; } // RX Timeout - if(_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)){ + if (_rx_since_timeout && (now - _rx_last_packet) >= (_rx_since_timeout * 1000)) { log_w("rx timeout %d", pcb->state); _close(); return ERR_OK; } // Everything is fine - if(_poll_cb) { + if (_poll_cb) { _poll_cb(_poll_cb_arg, this); } return ERR_OK; } -void AsyncClient::_dns_found(struct ip_addr *ipaddr){ - if(ipaddr && ipaddr->u_addr.ip4.addr){ +void AsyncClient::_dns_found(struct ip_addr * ipaddr) { + if (ipaddr && ipaddr->u_addr.ip4.addr) { connect(IPAddress(ipaddr->u_addr.ip4.addr), _connect_port); + } else if (ipaddr && ipaddr->u_addr.ip6.addr) { + connect(IPv6Address(ipaddr->u_addr.ip6.addr), _connect_port); } else { - if(_error_cb) { + if (_error_cb) { _error_cb(_error_cb_arg, this, -55); } - if(_discard_cb) { + if (_discard_cb) { _discard_cb(_discard_cb_arg, this); } } @@ -998,95 +1021,113 @@ void AsyncClient::stop() { close(false); } -bool AsyncClient::free(){ - if(!_pcb) { +bool AsyncClient::free() { + if (!_pcb) { return true; } - if(_pcb->state == 0 || _pcb->state > 4) { + if (_pcb->state == 0 || _pcb->state > 4) { return true; } return false; } -size_t AsyncClient::write(const char* data) { - if(data == NULL) { +size_t AsyncClient::write(const char * data) { + if (data == NULL) { return 0; } return write(data, strlen(data)); } -size_t AsyncClient::write(const char* data, size_t size, uint8_t apiflags) { +size_t AsyncClient::write(const char * data, size_t size, uint8_t apiflags) { size_t will_send = add(data, size, apiflags); - if(!will_send || !send()) { + if (!will_send || !send()) { return 0; } return will_send; } -void AsyncClient::setRxTimeout(uint32_t timeout){ +void AsyncClient::setRxTimeout(uint32_t timeout) { _rx_since_timeout = timeout; } -uint32_t AsyncClient::getRxTimeout(){ +uint32_t AsyncClient::getRxTimeout() { return _rx_since_timeout; } -uint32_t AsyncClient::getAckTimeout(){ +uint32_t AsyncClient::getAckTimeout() { return _ack_timeout; } -void AsyncClient::setAckTimeout(uint32_t timeout){ +void AsyncClient::setAckTimeout(uint32_t timeout) { _ack_timeout = timeout; } -void AsyncClient::setNoDelay(bool nodelay){ - if(!_pcb) { +void AsyncClient::setNoDelay(bool nodelay) { + if (!_pcb) { return; } - if(nodelay) { + if (nodelay) { tcp_nagle_disable(_pcb); } else { tcp_nagle_enable(_pcb); } } -bool AsyncClient::getNoDelay(){ - if(!_pcb) { +bool AsyncClient::getNoDelay() { + if (!_pcb) { return false; } return tcp_nagle_disabled(_pcb); } -uint16_t AsyncClient::getMss(){ - if(!_pcb) { +uint16_t AsyncClient::getMss() { + if (!_pcb) { return 0; } return tcp_mss(_pcb); } uint32_t AsyncClient::getRemoteAddress() { - if(!_pcb) { + if (!_pcb) { return 0; } return _pcb->remote_ip.u_addr.ip4.addr; } +ip6_addr_t AsyncClient::getRemoteAddress6() { + if (!_pcb) { + ip6_addr_t nulladdr; + ip6_addr_set_zero(&nulladdr); + return nulladdr; + } + return _pcb->remote_ip.u_addr.ip6; +} + uint16_t AsyncClient::getRemotePort() { - if(!_pcb) { + if (!_pcb) { return 0; } return _pcb->remote_port; } uint32_t AsyncClient::getLocalAddress() { - if(!_pcb) { + if (!_pcb) { return 0; } return _pcb->local_ip.u_addr.ip4.addr; } +ip6_addr_t AsyncClient::getLocalAddress6() { + if (!_pcb) { + ip6_addr_t nulladdr; + ip6_addr_set_zero(&nulladdr); + return nulladdr; + } + return _pcb->local_ip.u_addr.ip6; +} + uint16_t AsyncClient::getLocalPort() { - if(!_pcb) { + if (!_pcb) { return 0; } return _pcb->local_port; @@ -1096,6 +1137,10 @@ IPAddress AsyncClient::remoteIP() { return IPAddress(getRemoteAddress()); } +IPv6Address AsyncClient::remoteIP6() { + return IPv6Address(getRemoteAddress6().addr); +} + uint16_t AsyncClient::remotePort() { return getRemotePort(); } @@ -1104,93 +1149,127 @@ IPAddress AsyncClient::localIP() { return IPAddress(getLocalAddress()); } +IPv6Address AsyncClient::localIP6() { + return IPv6Address(getLocalAddress6().addr); +} + uint16_t AsyncClient::localPort() { return getLocalPort(); } uint8_t AsyncClient::state() { - if(!_pcb) { + if (!_pcb) { return 0; } return _pcb->state; } -bool AsyncClient::connected(){ +bool AsyncClient::connected() { if (!_pcb) { return false; } return _pcb->state == 4; } -bool AsyncClient::connecting(){ +bool AsyncClient::connecting() { if (!_pcb) { return false; } return _pcb->state > 0 && _pcb->state < 4; } -bool AsyncClient::disconnecting(){ +bool AsyncClient::disconnecting() { if (!_pcb) { return false; } return _pcb->state > 4 && _pcb->state < 10; } -bool AsyncClient::disconnected(){ +bool AsyncClient::disconnected() { if (!_pcb) { return true; } return _pcb->state == 0 || _pcb->state == 10; } -bool AsyncClient::freeable(){ +bool AsyncClient::freeable() { if (!_pcb) { return true; } return _pcb->state == 0 || _pcb->state > 4; } -bool AsyncClient::canSend(){ +bool AsyncClient::canSend() { return space() > 0; } -const char * AsyncClient::errorToString(int8_t error){ - switch(error){ - case ERR_OK: return "OK"; - case ERR_MEM: return "Out of memory error"; - case ERR_BUF: return "Buffer error"; - case ERR_TIMEOUT: return "Timeout"; - case ERR_RTE: return "Routing problem"; - case ERR_INPROGRESS: return "Operation in progress"; - case ERR_VAL: return "Illegal value"; - case ERR_WOULDBLOCK: return "Operation would block"; - case ERR_USE: return "Address in use"; - case ERR_ALREADY: return "Already connected"; - case ERR_CONN: return "Not connected"; - case ERR_IF: return "Low-level netif error"; - case ERR_ABRT: return "Connection aborted"; - case ERR_RST: return "Connection reset"; - case ERR_CLSD: return "Connection closed"; - case ERR_ARG: return "Illegal argument"; - case -55: return "DNS failed"; - default: return "UNKNOWN"; +const char * AsyncClient::errorToString(int8_t error) { + switch (error) { + case ERR_OK: + return "OK"; + case ERR_MEM: + return "Out of memory error"; + case ERR_BUF: + return "Buffer error"; + case ERR_TIMEOUT: + return "Timeout"; + case ERR_RTE: + return "Routing problem"; + case ERR_INPROGRESS: + return "Operation in progress"; + case ERR_VAL: + return "Illegal value"; + case ERR_WOULDBLOCK: + return "Operation would block"; + case ERR_USE: + return "Address in use"; + case ERR_ALREADY: + return "Already connected"; + case ERR_CONN: + return "Not connected"; + case ERR_IF: + return "Low-level netif error"; + case ERR_ABRT: + return "Connection aborted"; + case ERR_RST: + return "Connection reset"; + case ERR_CLSD: + return "Connection closed"; + case ERR_ARG: + return "Illegal argument"; + case -55: + return "DNS failed"; + default: + return "UNKNOWN"; } } -const char * AsyncClient::stateToString(){ - switch(state()){ - case 0: return "Closed"; - case 1: return "Listen"; - case 2: return "SYN Sent"; - case 3: return "SYN Received"; - case 4: return "Established"; - case 5: return "FIN Wait 1"; - case 6: return "FIN Wait 2"; - case 7: return "Close Wait"; - case 8: return "Closing"; - case 9: return "Last ACK"; - case 10: return "Time Wait"; - default: return "UNKNOWN"; +const char * AsyncClient::stateToString() { + switch (state()) { + case 0: + return "Closed"; + case 1: + return "Listen"; + case 2: + return "SYN Sent"; + case 3: + return "SYN Received"; + case 4: + return "Established"; + case 5: + return "FIN Wait 1"; + case 6: + return "FIN Wait 2"; + case 7: + return "Close Wait"; + case 8: + return "Closing"; + case 9: + return "Last ACK"; + case 10: + return "Time Wait"; + default: + return "UNKNOWN"; } } @@ -1198,36 +1277,36 @@ const char * AsyncClient::stateToString(){ * Static Callbacks (LwIP C2C++ interconnect) * */ -void AsyncClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg){ - reinterpret_cast(arg)->_dns_found(ipaddr); +void AsyncClient::_s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg) { + reinterpret_cast(arg)->_dns_found(ipaddr); } int8_t AsyncClient::_s_poll(void * arg, struct tcp_pcb * pcb) { - return reinterpret_cast(arg)->_poll(pcb); + return reinterpret_cast(arg)->_poll(pcb); } -int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf *pb, int8_t err) { - return reinterpret_cast(arg)->_recv(pcb, pb, err); +int8_t AsyncClient::_s_recv(void * arg, struct tcp_pcb * pcb, struct pbuf * pb, int8_t err) { + return reinterpret_cast(arg)->_recv(pcb, pb, err); } int8_t AsyncClient::_s_fin(void * arg, struct tcp_pcb * pcb, int8_t err) { - return reinterpret_cast(arg)->_fin(pcb, err); + return reinterpret_cast(arg)->_fin(pcb, err); } int8_t AsyncClient::_s_lwip_fin(void * arg, struct tcp_pcb * pcb, int8_t err) { - return reinterpret_cast(arg)->_lwip_fin(pcb, err); + return reinterpret_cast(arg)->_lwip_fin(pcb, err); } int8_t AsyncClient::_s_sent(void * arg, struct tcp_pcb * pcb, uint16_t len) { - return reinterpret_cast(arg)->_sent(pcb, len); + return reinterpret_cast(arg)->_sent(pcb, len); } void AsyncClient::_s_error(void * arg, int8_t err) { - reinterpret_cast(arg)->_error(err); + reinterpret_cast(arg)->_error(err); } -int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){ - return reinterpret_cast(arg)->_connected(pcb, err); +int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err) { + return reinterpret_cast(arg)->_connected(pcb, err); } /* @@ -1235,51 +1314,75 @@ int8_t AsyncClient::_s_connected(void * arg, void * pcb, int8_t err){ */ AsyncServer::AsyncServer(IPAddress addr, uint16_t port) -: _port(port) -, _addr(addr) -, _noDelay(false) -, _pcb(0) -, _connect_cb(0) -, _connect_cb_arg(0) -{} + : _port(port) + , _bind4(true) + , _addr(addr) + , _noDelay(false) + , _pcb(0) + , _connect_cb(0) + , _connect_cb_arg(0) { +} + +AsyncServer::AsyncServer(IPv6Address addr, uint16_t port) + : _port(port) + , _bind6(true) + , _addr6(addr) + , _noDelay(false) + , _pcb(0) + , _connect_cb(0) + , _connect_cb_arg(0) { +} AsyncServer::AsyncServer(uint16_t port) -: _port(port) -, _addr((uint32_t) IPADDR_ANY) -, _noDelay(false) -, _pcb(0) -, _connect_cb(0) -, _connect_cb_arg(0) -{} + : _port(port) + , _bind4(true) + , _bind6(true) + , _addr((uint32_t)IPADDR_ANY) + , _addr6() + , _noDelay(false) + , _pcb(0) + , _connect_cb(0) + , _connect_cb_arg(0) { +} -AsyncServer::~AsyncServer(){ +AsyncServer::~AsyncServer() { end(); } -void AsyncServer::onClient(AcConnectHandler cb, void* arg){ - _connect_cb = cb; +void AsyncServer::onClient(AcConnectHandler cb, void * arg) { + _connect_cb = cb; _connect_cb_arg = arg; } -void AsyncServer::begin(){ - if(_pcb) { +void AsyncServer::begin() { + if (_pcb) { return; } - if(!_start_async_task()){ + if (!_start_async_task()) { log_e("failed to start task"); return; } - int8_t err; - _pcb = tcp_new_ip_type(IPADDR_TYPE_V4); - if (!_pcb){ + int8_t err, bind_type; + + if (_bind4 && _bind6) { + bind_type = IPADDR_TYPE_ANY; + } else if (_bind6) { + bind_type = IPADDR_TYPE_V6; + } else { + bind_type = IPADDR_TYPE_V4; + } + + _pcb = tcp_new_ip_type(bind_type); + if (!_pcb) { log_e("_pcb == NULL"); return; } ip_addr_t local_addr; - local_addr.type = IPADDR_TYPE_V4; - local_addr.u_addr.ip4.addr = (uint32_t) _addr; + local_addr.type = bind_type; + local_addr.u_addr.ip4.addr = (uint32_t)_addr; + memcpy(local_addr.u_addr.ip6.addr, static_cast(_addr6), sizeof(uint32_t) * 4); err = _tcp_bind(_pcb, &local_addr, _port); if (err != ERR_OK) { @@ -1289,20 +1392,20 @@ void AsyncServer::begin(){ } static uint8_t backlog = 5; - _pcb = _tcp_listen_with_backlog(_pcb, backlog); + _pcb = _tcp_listen_with_backlog(_pcb, backlog); if (!_pcb) { log_e("listen_pcb == NULL"); return; } - tcp_arg(_pcb, (void*) this); + tcp_arg(_pcb, (void *)this); tcp_accept(_pcb, &_s_accept); } -void AsyncServer::end(){ - if(_pcb){ +void AsyncServer::end() { + if (_pcb) { tcp_arg(_pcb, NULL); tcp_accept(_pcb, NULL); - if(tcp_close(_pcb) != ERR_OK){ + if (tcp_close(_pcb) != ERR_OK) { _tcp_abort(_pcb, -1); } _pcb = NULL; @@ -1310,48 +1413,48 @@ void AsyncServer::end(){ } //runs on LwIP thread -int8_t AsyncServer::_accept(tcp_pcb* pcb, int8_t err){ +int8_t AsyncServer::_accept(tcp_pcb * pcb, int8_t err) { //ets_printf("+A: 0x%08x\n", pcb); - if(_connect_cb){ - AsyncClient *c = new AsyncClient(pcb); - if(c){ + if (_connect_cb) { + AsyncClient * c = new AsyncClient(pcb); + if (c) { c->setNoDelay(_noDelay); return _tcp_accept(this, c); } } - if(tcp_close(pcb) != ERR_OK){ + if (tcp_close(pcb) != ERR_OK) { tcp_abort(pcb); } log_e("FAIL"); return ERR_OK; } -int8_t AsyncServer::_accepted(AsyncClient* client){ - if(_connect_cb){ +int8_t AsyncServer::_accepted(AsyncClient * client) { + if (_connect_cb) { _connect_cb(_connect_cb_arg, client); } return ERR_OK; } -void AsyncServer::setNoDelay(bool nodelay){ +void AsyncServer::setNoDelay(bool nodelay) { _noDelay = nodelay; } -bool AsyncServer::getNoDelay(){ +bool AsyncServer::getNoDelay() { return _noDelay; } -uint8_t AsyncServer::status(){ +uint8_t AsyncServer::status() { if (!_pcb) { return 0; } return _pcb->state; } -int8_t AsyncServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err){ - return reinterpret_cast(arg)->_accept(pcb, err); +int8_t AsyncServer::_s_accept(void * arg, tcp_pcb * pcb, int8_t err) { + return reinterpret_cast(arg)->_accept(pcb, err); } -int8_t AsyncServer::_s_accepted(void *arg, AsyncClient* client){ - return reinterpret_cast(arg)->_accepted(client); -} +int8_t AsyncServer::_s_accepted(void * arg, AsyncClient * client) { + return reinterpret_cast(arg)->_accepted(client); +} \ No newline at end of file diff --git a/lib/AsyncTCP/src/AsyncTCP.h b/lib/AsyncTCP/src/AsyncTCP.h index ac87dedac..855f2f7f1 100644 --- a/lib/AsyncTCP/src/AsyncTCP.h +++ b/lib/AsyncTCP/src/AsyncTCP.h @@ -23,17 +23,20 @@ #define ASYNCTCP_H_ #include "IPAddress.h" +#include "IPv6Address.h" #include "sdkconfig.h" #include extern "C" { - #include "freertos/semphr.h" - #include "lwip/pbuf.h" +#include "freertos/semphr.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_addr.h" } //If core is not defined, then we are running in Arduino or PIO #ifndef CONFIG_ASYNC_TCP_RUNNING_CORE #define CONFIG_ASYNC_TCP_RUNNING_CORE -1 //any available core -#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event +#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event #endif class AsyncClient; @@ -42,127 +45,138 @@ class AsyncClient; #define ASYNC_WRITE_FLAG_COPY 0x01 //will allocate new buffer to hold the data while sending (else will hold reference to the data given) #define ASYNC_WRITE_FLAG_MORE 0x02 //will not send PSH flag, meaning that there should be more data to be sent before the application should react. -typedef std::function AcConnectHandler; -typedef std::function AcAckHandler; -typedef std::function AcErrorHandler; -typedef std::function AcDataHandler; -typedef std::function AcPacketHandler; -typedef std::function AcTimeoutHandler; +typedef std::function AcConnectHandler; +typedef std::function AcAckHandler; +typedef std::function AcErrorHandler; +typedef std::function AcDataHandler; +typedef std::function AcPacketHandler; +typedef std::function AcTimeoutHandler; struct tcp_pcb; struct ip_addr; class AsyncClient { public: - AsyncClient(tcp_pcb* pcb = 0); + AsyncClient(tcp_pcb * pcb = 0); ~AsyncClient(); - AsyncClient & operator=(const AsyncClient &other); - AsyncClient & operator+=(const AsyncClient &other); + AsyncClient & operator=(const AsyncClient & other); + AsyncClient & operator+=(const AsyncClient & other); - bool operator==(const AsyncClient &other); + bool operator==(const AsyncClient & other); - bool operator!=(const AsyncClient &other) { - return !(*this == other); + bool operator!=(const AsyncClient & other) { + return !(*this == other); } - bool connect(IPAddress ip, uint16_t port); - bool connect(const char* host, uint16_t port); - void close(bool now = false); - void stop(); + bool connect(IPAddress ip, uint16_t port); + bool connect(IPv6Address ip, uint16_t port); + bool connect(const char * host, uint16_t port); + void close(bool now = false); + void stop(); int8_t abort(); - bool free(); + bool free(); - bool canSend();//ack is not pending - size_t space();//space available in the TCP window - size_t add(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY);//add for sending - bool send();//send all data added with the method above + bool canSend(); //ack is not pending + size_t space(); //space available in the TCP window + size_t add(const char * data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //add for sending + bool send(); //send all data added with the method above //write equals add()+send() - size_t write(const char* data); - size_t write(const char* data, size_t size, uint8_t apiflags=ASYNC_WRITE_FLAG_COPY); //only when canSend() == true + size_t write(const char * data); + size_t write(const char * data, size_t size, uint8_t apiflags = ASYNC_WRITE_FLAG_COPY); //only when canSend() == true uint8_t state(); - bool connecting(); - bool connected(); - bool disconnecting(); - bool disconnected(); - bool freeable();//disconnected or disconnecting + bool connecting(); + bool connected(); + bool disconnecting(); + bool disconnected(); + bool freeable(); //disconnected or disconnecting uint16_t getMss(); uint32_t getRxTimeout(); - void setRxTimeout(uint32_t timeout);//no RX data timeout for the connection in seconds + void setRxTimeout(uint32_t timeout); //no RX data timeout for the connection in seconds uint32_t getAckTimeout(); - void setAckTimeout(uint32_t timeout);//no ACK timeout for the last sent packet in milliseconds + void setAckTimeout(uint32_t timeout); //no ACK timeout for the last sent packet in milliseconds void setNoDelay(bool nodelay); bool getNoDelay(); - uint32_t getRemoteAddress(); - uint16_t getRemotePort(); - uint32_t getLocalAddress(); - uint16_t getLocalPort(); + uint32_t getRemoteAddress(); + ip6_addr_t getRemoteAddress6(); + uint16_t getRemotePort(); + uint32_t getLocalAddress(); + ip6_addr_t getLocalAddress6(); + uint16_t getLocalPort(); //compatibility - IPAddress remoteIP(); - uint16_t remotePort(); - IPAddress localIP(); - uint16_t localPort(); + IPAddress remoteIP(); + IPv6Address remoteIP6(); + uint16_t remotePort(); + IPAddress localIP(); + IPv6Address localIP6(); + uint16_t localPort(); - void onConnect(AcConnectHandler cb, void* arg = 0); //on successful connect - void onDisconnect(AcConnectHandler cb, void* arg = 0); //disconnected - void onAck(AcAckHandler cb, void* arg = 0); //ack received - void onError(AcErrorHandler cb, void* arg = 0); //unsuccessful connect or error - void onData(AcDataHandler cb, void* arg = 0); //data received (called if onPacket is not used) - void onPacket(AcPacketHandler cb, void* arg = 0); //data received - void onTimeout(AcTimeoutHandler cb, void* arg = 0); //ack timeout - void onPoll(AcConnectHandler cb, void* arg = 0); //every 125ms when connected + void onConnect(AcConnectHandler cb, void * arg = 0); //on successful connect + void onDisconnect(AcConnectHandler cb, void * arg = 0); //disconnected + void onAck(AcAckHandler cb, void * arg = 0); //ack received + void onError(AcErrorHandler cb, void * arg = 0); //unsuccessful connect or error + void onData(AcDataHandler cb, void * arg = 0); //data received (called if onPacket is not used) + void onPacket(AcPacketHandler cb, void * arg = 0); //data received + void onTimeout(AcTimeoutHandler cb, void * arg = 0); //ack timeout + void onPoll(AcConnectHandler cb, void * arg = 0); //every 125ms when connected - void ackPacket(struct pbuf * pb);//ack pbuf from onPacket - size_t ack(size_t len); //ack data that you have not acked using the method below - void ackLater(){ _ack_pcb = false; } //will not ack the current packet. Call from onData + void ackPacket(struct pbuf * pb); //ack pbuf from onPacket + size_t ack(size_t len); //ack data that you have not acked using the method below + void ackLater() { + _ack_pcb = false; + } //will not ack the current packet. Call from onData const char * errorToString(int8_t error); const char * stateToString(); //Do not use any of the functions below! - static int8_t _s_poll(void *arg, struct tcp_pcb *tpcb); - static int8_t _s_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *pb, int8_t err); - static int8_t _s_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); - static int8_t _s_lwip_fin(void *arg, struct tcp_pcb *tpcb, int8_t err); - static void _s_error(void *arg, int8_t err); - static int8_t _s_sent(void *arg, struct tcp_pcb *tpcb, uint16_t len); - static int8_t _s_connected(void* arg, void* tpcb, int8_t err); - static void _s_dns_found(const char *name, struct ip_addr *ipaddr, void *arg); + static int8_t _s_poll(void * arg, struct tcp_pcb * tpcb); + static int8_t _s_recv(void * arg, struct tcp_pcb * tpcb, struct pbuf * pb, int8_t err); + static int8_t _s_fin(void * arg, struct tcp_pcb * tpcb, int8_t err); + static int8_t _s_lwip_fin(void * arg, struct tcp_pcb * tpcb, int8_t err); + static void _s_error(void * arg, int8_t err); + static int8_t _s_sent(void * arg, struct tcp_pcb * tpcb, uint16_t len); + static int8_t _s_connected(void * arg, void * tpcb, int8_t err); + static void _s_dns_found(const char * name, struct ip_addr * ipaddr, void * arg); - int8_t _recv(tcp_pcb* pcb, pbuf* pb, int8_t err); - tcp_pcb * pcb(){ return _pcb; } + int8_t _recv(tcp_pcb * pcb, pbuf * pb, int8_t err); + tcp_pcb * pcb() { + return _pcb; + } protected: - tcp_pcb* _pcb; - int8_t _closed_slot; + bool _connect(ip_addr_t addr, uint16_t port); + + tcp_pcb * _pcb; + int8_t _closed_slot; AcConnectHandler _connect_cb; - void* _connect_cb_arg; + void * _connect_cb_arg; AcConnectHandler _discard_cb; - void* _discard_cb_arg; - AcAckHandler _sent_cb; - void* _sent_cb_arg; - AcErrorHandler _error_cb; - void* _error_cb_arg; - AcDataHandler _recv_cb; - void* _recv_cb_arg; - AcPacketHandler _pb_cb; - void* _pb_cb_arg; + void * _discard_cb_arg; + AcAckHandler _sent_cb; + void * _sent_cb_arg; + AcErrorHandler _error_cb; + void * _error_cb_arg; + AcDataHandler _recv_cb; + void * _recv_cb_arg; + AcPacketHandler _pb_cb; + void * _pb_cb_arg; AcTimeoutHandler _timeout_cb; - void* _timeout_cb_arg; + void * _timeout_cb_arg; AcConnectHandler _poll_cb; - void* _poll_cb_arg; + void * _poll_cb_arg; - bool _pcb_busy; + bool _pcb_busy; uint32_t _pcb_sent_at; - bool _ack_pcb; + bool _ack_pcb; uint32_t _rx_ack_len; uint32_t _rx_last_packet; uint32_t _rx_since_timeout; @@ -170,48 +184,52 @@ class AsyncClient { uint16_t _connect_port; int8_t _close(); - void _free_closed_slot(); - void _allocate_closed_slot(); - int8_t _connected(void* pcb, int8_t err); - void _error(int8_t err); - int8_t _poll(tcp_pcb* pcb); - int8_t _sent(tcp_pcb* pcb, uint16_t len); - int8_t _fin(tcp_pcb* pcb, int8_t err); - int8_t _lwip_fin(tcp_pcb* pcb, int8_t err); - void _dns_found(struct ip_addr *ipaddr); + void _free_closed_slot(); + void _allocate_closed_slot(); + int8_t _connected(void * pcb, int8_t err); + void _error(int8_t err); + int8_t _poll(tcp_pcb * pcb); + int8_t _sent(tcp_pcb * pcb, uint16_t len); + int8_t _fin(tcp_pcb * pcb, int8_t err); + int8_t _lwip_fin(tcp_pcb * pcb, int8_t err); + void _dns_found(struct ip_addr * ipaddr); public: - AsyncClient* prev; - AsyncClient* next; + AsyncClient * prev; + AsyncClient * next; }; class AsyncServer { public: AsyncServer(IPAddress addr, uint16_t port); + AsyncServer(IPv6Address addr, uint16_t port); AsyncServer(uint16_t port); ~AsyncServer(); - void onClient(AcConnectHandler cb, void* arg); - void begin(); - void end(); - void setNoDelay(bool nodelay); - bool getNoDelay(); + void onClient(AcConnectHandler cb, void * arg); + void begin(); + void end(); + void setNoDelay(bool nodelay); + bool getNoDelay(); uint8_t status(); //Do not use any of the functions below! - static int8_t _s_accept(void *arg, tcp_pcb* newpcb, int8_t err); - static int8_t _s_accepted(void *arg, AsyncClient* client); + static int8_t _s_accept(void * arg, tcp_pcb * newpcb, int8_t err); + static int8_t _s_accepted(void * arg, AsyncClient * client); protected: - uint16_t _port; - IPAddress _addr; - bool _noDelay; - tcp_pcb* _pcb; + uint16_t _port; + bool _bind4 = false; + bool _bind6 = false; + IPAddress _addr; + IPv6Address _addr6; + bool _noDelay; + tcp_pcb * _pcb; AcConnectHandler _connect_cb; - void* _connect_cb_arg; + void * _connect_cb_arg; - int8_t _accept(tcp_pcb* newpcb, int8_t err); - int8_t _accepted(AsyncClient* client); + int8_t _accept(tcp_pcb * newpcb, int8_t err); + int8_t _accepted(AsyncClient * client); }; -#endif /* ASYNCTCP_H_ */ +#endif /* ASYNCTCP_H_ */ \ No newline at end of file diff --git a/src/version.h b/src/version.h index 2c29bab9f..931f7bffa 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.2b1" +#define EMSESP_APP_VERSION "3.1.2b2" diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index c7e972582..e1ded7f37 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -73,6 +73,20 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { EMSESP::system_.ethernet_connected(false); break; + case SYSTEM_EVENT_STA_CONNECTED: + WiFi.enableIpV6(); + break; + + case SYSTEM_EVENT_ETH_CONNECTED: + // ETH.enableIpV6(); // TODO this crashes + break; + + case SYSTEM_EVENT_GOT_IP6: +#ifndef EMSESP_STANDALONE + EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname()); +#endif + break; + default: break; } From 24d8ccc52f8844d4110df1411e3dafbdc8ec1046 Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 5 Jul 2021 22:55:39 +0200 Subject: [PATCH 028/122] fix crash with IPv6 and ETH - #83 --- src/web/WebStatusService.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index e1ded7f37..b58eacbb7 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -73,19 +73,23 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { EMSESP::system_.ethernet_connected(false); break; +#ifndef EMSESP_STANDALONE case SYSTEM_EVENT_STA_CONNECTED: WiFi.enableIpV6(); break; case SYSTEM_EVENT_ETH_CONNECTED: - // ETH.enableIpV6(); // TODO this crashes + ETH.enableIpV6(); break; case SYSTEM_EVENT_GOT_IP6: -#ifndef EMSESP_STANDALONE - EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname()); -#endif + if (EMSESP::system_.ethernet_connected()) { + EMSESP::logger().info(F("Ethernet Connected with IP=%s, speed %d Mbps"), ETH.localIPv6().toString().c_str(), ETH.linkSpeed()); + } else { + EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname()); + } break; +#endif default: break; From 7c71ed2dc6c98a80ec35903b1d476250eff3b5f1 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Tue, 6 Jul 2021 07:35:15 +0200 Subject: [PATCH 029/122] fix mem leak --- src/web/WebSettingsService.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index c5b9a37c9..0da7bac65 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -233,6 +233,7 @@ void WebSettingsService::board_profile(AsyncWebServerRequest * request, JsonVari root["tx_gpio"] = data[3]; root["pbutton_gpio"] = data[4]; } else { + delete response; AsyncWebServerResponse * response = request->beginResponse(200); request->send(response); return; From 48cd12ec3df080dd4a976ed3bb41c7dfb2767b91 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Tue, 6 Jul 2021 07:35:39 +0200 Subject: [PATCH 030/122] NTP time for weblog --- src/web/WebLogService.cpp | 39 +++++++++++++++++++++++++++++++-------- src/web/WebLogService.h | 3 +++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 05675c5c4..c1242d23e 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -80,6 +80,13 @@ void WebLogService::operator<<(std::shared_ptr message) { log_messages_.pop_front(); } log_messages_.emplace_back(log_message_id_++, std::move(message)); + EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) { + if (!settings.enabled || (time(nullptr) < 1500000000UL)) { + time_offset_ = 0; + } else if (!time_offset_) { + time_offset_ = time(nullptr) - uuid::get_uptime_sec(); + } + }); } void WebLogService::loop() { @@ -101,14 +108,26 @@ void WebLogService::loop() { } } +char * WebLogService::messagetime(char * out, const uint64_t t) { + if (!time_offset_) { + strcpy(out, uuid::log::format_timestamp_ms(t, 3).c_str()); + } else { + time_t t1 = time_offset_ + t / 1000ULL; + strftime(out, 25, "%F %T", localtime(&t1)); + snprintf_P(out, 25, "%s.%03d", out, (uint16_t)(t % 1000)); + } + return out; +} + // send to web eventsource void WebLogService::transmit(const QueuedLogMessage & message) { DynamicJsonDocument jsonDocument = DynamicJsonDocument(EMSESP_JSON_SIZE_SMALL); JsonObject logEvent = jsonDocument.to(); - logEvent["t"] = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3); - logEvent["l"] = message.content_->level; - logEvent["n"] = message.content_->name; - logEvent["m"] = message.content_->text; + char time_string[25]; + logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); + logEvent["l"] = message.content_->level; + logEvent["n"] = message.content_->name; + logEvent["m"] = message.content_->text; size_t len = measureJson(jsonDocument); char * buffer = new char[len + 1]; @@ -129,10 +148,11 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) { for (const auto & msg : log_messages_) { JsonObject logEvent = log.createNestedObject(); auto message = std::move(msg); - logEvent["t"] = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3); - logEvent["l"] = message.content_->level; - logEvent["n"] = message.content_->name; - logEvent["m"] = message.content_->text; + char time_string[25]; + logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); + logEvent["l"] = message.content_->level; + logEvent["n"] = message.content_->name; + logEvent["m"] = message.content_->text; } log_message_id_tail_ = log_messages_.back().id_; @@ -149,6 +169,9 @@ void WebLogService::setLevel(AsyncWebServerRequest * request, JsonVariant & json auto && body = json.as(); uuid::log::Level level = body["level"]; log_level(level); + if (level == uuid::log::Level::OFF) { + log_messages_.clear(); + } // send the value back AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL); diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index 12bf8afe6..c45c7213a 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -65,6 +65,8 @@ class WebLogService : public uuid::log::Handler { void fetchLog(AsyncWebServerRequest * request); void getLevel(AsyncWebServerRequest * request); + char * messagetime(char * out, const uint64_t t); + void setLevel(AsyncWebServerRequest * request, JsonVariant & json); AsyncCallbackJsonWebHandler _setLevel; // for POSTs @@ -73,6 +75,7 @@ class WebLogService : public uuid::log::Handler { unsigned long log_message_id_ = 0; // The next identifier to use for queued log messages unsigned long log_message_id_tail_ = 0; // last event shown on the screen after fetch std::list log_messages_; // Queued log messages, in the order they were received + time_t time_offset_ = 0; }; } // namespace emsesp From 3ea53a8012ecaa010c522068c7ce8cab77fbdf7e Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 7 Jul 2021 09:19:01 +0200 Subject: [PATCH 031/122] updated doc and version --- CHANGELOG_LATEST.md | 4 ++++ src/version.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 34c404120..a37fc6b77 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -2,11 +2,15 @@ ## Added +- support for IPv6 (#83) +- System Log in Web UI will show current time if the NTP Service is enabled (#82) + ## Fixed ## Changed - removed Rx echo failures counting as incomplete telegrams. BAd telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80) - add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat +- added debug target to PlatformIO build to help hunt down system crashes ## Removed diff --git a/src/version.h b/src/version.h index 931f7bffa..4c5afedb2 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.2b2" +#define EMSESP_APP_VERSION "3.1.2b3" From 2d7449aebac926282311438de2ff85ebc0085b20 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Thu, 8 Jul 2021 10:17:50 +0200 Subject: [PATCH 032/122] add network options, IPv6 for mqtt --- CHANGELOG_LATEST.md | 6 ++- interface/src/network/NetworkSettingsForm.tsx | 53 ++++++++++++++++++- interface/src/network/types.ts | 4 ++ interface/src/project/EMSESPSettingsForm.tsx | 10 ++++ interface/src/project/EMSESPtypes.ts | 1 + interface/src/validators/isIP.ts | 3 +- lib/async-mqtt-client/src/AsyncMqttClient.cpp | 33 +++++++++--- lib/async-mqtt-client/src/AsyncMqttClient.hpp | 3 ++ lib/framework/APSettingsService.cpp | 1 + lib/framework/MqttSettingsService.cpp | 1 + lib/framework/NetworkSettingsService.cpp | 19 ++++--- lib/framework/NetworkSettingsService.h | 14 ++++- src/system.cpp | 9 ++++ src/system.h | 8 ++- src/web/WebSettingsService.cpp | 4 +- src/web/WebSettingsService.h | 1 + src/web/WebStatusService.cpp | 37 ++++++++++--- 17 files changed, 180 insertions(+), 27 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index a37fc6b77..4478bab35 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -2,14 +2,16 @@ ## Added -- support for IPv6 (#83) +- support for IPv6 (web/api/mqtt, not syslog) (#83) - System Log in Web UI will show current time if the NTP Service is enabled (#82) +- Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode (#83) +- optional low clockrate (160 MHz) (#83) ## Fixed ## Changed -- removed Rx echo failures counting as incomplete telegrams. BAd telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80) +- removed Rx echo failures counting as incomplete telegrams. Bad telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80) - add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat - added debug target to PlatformIO build to help hunt down system crashes diff --git a/interface/src/network/NetworkSettingsForm.tsx b/interface/src/network/NetworkSettingsForm.tsx index 5928688a5..97325530d 100644 --- a/interface/src/network/NetworkSettingsForm.tsx +++ b/interface/src/network/NetworkSettingsForm.tsx @@ -51,7 +51,11 @@ class NetworkSettingsForm extends React.Component { ssid: selectedNetwork.ssid, password: '', hostname: props.data.hostname, - static_ip_config: false + static_ip_config: false, + enableIPv6: false, + bandwidth20: false, + tx_power: 20, + nosleep: false }; props.setData(networkSettings); } @@ -145,6 +149,53 @@ class NetworkSettingsForm extends React.Component { onChange={handleValueChange('hostname')} margin="normal" /> + + + } + label="Enable IPv6" + /> + + } + label="WiFi Low Bandwidth" + /> + + } + label="Disable Wifi Sleepmode" + /> { } label="Enable ADC" /> + + } + label="Low Clockrate (160MHz, changed on next reboot)" + /> #ifndef EMSESP_STANDALONE -#if defined(EMSESP_WIFI_TWEAK) #include -#endif #include #endif @@ -36,6 +34,10 @@ class NetworkSettings { String password; String hostname; bool staticIPConfig; + bool enableIPv6; + bool bandwidth20; + int8_t tx_power; + bool nosleep; // optional configuration for static IP address IPAddress localIP; @@ -50,6 +52,10 @@ class NetworkSettings { root["password"] = settings.password; root["hostname"] = settings.hostname; root["static_ip_config"] = settings.staticIPConfig; + root["enableIPv6"] = settings.enableIPv6; + root["bandwidth20"] = settings.bandwidth20; + root["tx_power"] = settings.tx_power; + root["nosleep"] = settings.nosleep; // extended settings JsonUtils::writeIP(root, "local_ip", settings.localIP); @@ -64,6 +70,10 @@ class NetworkSettings { settings.password = root["password"] | FACTORY_WIFI_PASSWORD; settings.hostname = root["hostname"] | FACTORY_WIFI_HOSTNAME; settings.staticIPConfig = root["static_ip_config"] | false; + settings.enableIPv6 = root["enableIPv6"] | false; + settings.bandwidth20 = root["bandwidth20"] | false; + settings.tx_power = root["tx_power"] | 20; + settings.nosleep = root["nosleep"] | false; // extended settings JsonUtils::readIP(root, "local_ip", settings.localIP); diff --git a/src/system.cpp b/src/system.cpp index a97812312..d752ba4ba 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -206,6 +206,9 @@ void System::get_settings() { // ADC analog_enabled_ = settings.analog_enabled; + // Sysclock + low_clock_ = settings.low_clock; + // Syslog syslog_enabled_ = settings.syslog_enabled; syslog_level_ = settings.syslog_level; @@ -275,6 +278,12 @@ void System::start(uint32_t heap_start) { // load in all the settings first get_settings(); +#ifndef EMSESP_STANDALONE + if (low_clock_) { + setCpuFrequencyMhz(160); + } +#endif + EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { hostname(networkSettings.hostname.c_str()); // sets the hostname LOG_INFO(F("System name: %s"), hostname().c_str()); diff --git a/src/system.h b/src/system.h index 721f24cd5..d786ad7f7 100644 --- a/src/system.h +++ b/src/system.h @@ -96,10 +96,14 @@ class System { void ethernet_connected(bool b) { ethernet_connected_ = b; } + void network_connected(bool b) { + network_connected_ = b; + } bool network_connected() { #ifndef EMSESP_STANDALONE - return (ethernet_connected_ || WiFi.isConnected()); + // return (ethernet_connected_ || WiFi.isConnected()); + return network_connected_; #else return true; #endif @@ -147,6 +151,7 @@ class System { uint32_t last_system_check_ = 0; bool upload_status_ = false; // true if we're in the middle of a OTA firmware upload bool ethernet_connected_ = false; + bool network_connected_ = false; uint16_t analog_; // settings @@ -155,6 +160,7 @@ class System { uint8_t led_gpio_; bool syslog_enabled_; bool analog_enabled_; + bool low_clock_; String board_profile_; uint8_t pbutton_gpio_; int8_t syslog_level_; diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 0da7bac65..5ecffddf4 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -55,6 +55,7 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) { root["dallas_parasite"] = settings.dallas_parasite; root["led_gpio"] = settings.led_gpio; root["hide_led"] = settings.hide_led; + root["low_clock"] = settings.low_clock; root["notoken_api"] = settings.notoken_api; root["analog_enabled"] = settings.analog_enabled; root["pbutton_gpio"] = settings.pbutton_gpio; @@ -165,9 +166,10 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.hide_led = root["hide_led"] | EMSESP_DEFAULT_HIDE_LED; check_flag(prev, settings.hide_led, ChangeFlags::LED); - // these both need reboots to be applied + // these need reboots to be applied settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID; settings.master_thermostat = root["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT; + settings.low_clock = root["low_clock"] | false;; // doesn't need any follow-up actions settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API; diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h index dccad6961..8a4a09418 100644 --- a/src/web/WebSettingsService.h +++ b/src/web/WebSettingsService.h @@ -50,6 +50,7 @@ class WebSettings { bool dallas_parasite; uint8_t led_gpio; bool hide_led; + bool low_clock; bool notoken_api; bool analog_enabled; uint8_t pbutton_gpio; diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index b58eacbb7..6d12a0223 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -35,15 +35,20 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { switch (event) { case SYSTEM_EVENT_STA_DISCONNECTED: EMSESP::logger().info(F("WiFi Disconnected. Reason code=%d"), info.disconnected.reason); + EMSESP::system_.network_connected(false); break; case SYSTEM_EVENT_STA_GOT_IP: #ifndef EMSESP_STANDALONE EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIP().toString().c_str(), WiFi.getHostname()); #endif - EMSESP::system_.wifi_tweak(); - EMSESP::system_.send_heartbeat(); - EMSESP::system_.syslog_start(); + EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { + if (!networkSettings.enableIPv6) { + EMSESP::system_.network_connected(true); + EMSESP::system_.send_heartbeat(); + EMSESP::system_.syslog_start(); + } + }); break; case SYSTEM_EVENT_ETH_START: @@ -57,8 +62,13 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { #ifndef EMSESP_STANDALONE EMSESP::logger().info(F("Ethernet Connected with IP=%s, speed %d Mbps"), ETH.localIP().toString().c_str(), ETH.linkSpeed()); #endif - EMSESP::system_.send_heartbeat(); - EMSESP::system_.syslog_start(); + EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { + if (!networkSettings.enableIPv6) { + EMSESP::system_.network_connected(true); + EMSESP::system_.send_heartbeat(); + EMSESP::system_.syslog_start(); + } + }); EMSESP::system_.ethernet_connected(true); } break; @@ -66,20 +76,30 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { case SYSTEM_EVENT_ETH_DISCONNECTED: EMSESP::logger().info(F("Ethernet Disconnected")); EMSESP::system_.ethernet_connected(false); + EMSESP::system_.network_connected(false); break; case SYSTEM_EVENT_ETH_STOP: EMSESP::logger().info(F("Ethernet Stopped")); EMSESP::system_.ethernet_connected(false); + EMSESP::system_.network_connected(false); break; #ifndef EMSESP_STANDALONE case SYSTEM_EVENT_STA_CONNECTED: - WiFi.enableIpV6(); + EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { + if (networkSettings.enableIPv6) { + WiFi.enableIpV6(); + } + }); break; case SYSTEM_EVENT_ETH_CONNECTED: - ETH.enableIpV6(); + EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { + if (networkSettings.enableIPv6) { + ETH.enableIpV6(); + } + }); break; case SYSTEM_EVENT_GOT_IP6: @@ -88,6 +108,9 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { } else { EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname()); } + EMSESP::system_.network_connected(true); + EMSESP::system_.send_heartbeat(); + EMSESP::system_.syslog_start(); break; #endif From 59913cdc4b2d23bc91b1b36b367168ed14761c7f Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Thu, 8 Jul 2021 18:56:24 +0200 Subject: [PATCH 033/122] disable bluetooth, show IPv6 in web, mqtt and console --- interface/src/network/NetworkStatusForm.tsx | 19 +++++++++++++++++-- interface/src/network/types.ts | 1 + lib/framework/NetworkStatus.cpp | 2 ++ src/mqtt.cpp | 6 ++++++ src/system.cpp | 14 +++++++++----- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/interface/src/network/NetworkStatusForm.tsx b/interface/src/network/NetworkStatusForm.tsx index ed4c3ca8e..6098f04c6 100644 --- a/interface/src/network/NetworkStatusForm.tsx +++ b/interface/src/network/NetworkStatusForm.tsx @@ -40,7 +40,22 @@ class NetworkStatusForm extends Component { if (!status.dns_ip_1) { return 'none'; } - return status.dns_ip_1 + (status.dns_ip_2 ? ',' + status.dns_ip_2 : ''); + if (!status.dns_ip_2 || status.dns_ip_2 === '0.0.0.0') { + return status.dns_ip_1; + } + return status.dns_ip_1 + ', ' + status.dns_ip_2; + } + IPs(status: NetworkStatus) { + if ( + !status.local_ipv6 || + status.local_ipv6 === '0000:0000:0000:0000:0000:0000:0000:0000' + ) { + return status.local_ip; + } + if (!status.local_ip || status.local_ip === '0.0.0.0') { + return status.local_ipv6; + } + return status.local_ip + ', ' + status.local_ipv6; } createListItems() { @@ -77,7 +92,7 @@ class NetworkStatusForm extends Component { IP - + diff --git a/interface/src/network/types.ts b/interface/src/network/types.ts index 03d56240d..538020903 100644 --- a/interface/src/network/types.ts +++ b/interface/src/network/types.ts @@ -21,6 +21,7 @@ export enum WiFiEncryptionType { export interface NetworkStatus { status: NetworkConnectionStatus; local_ip: string; + local_ipv6: string; mac_address: string; rssi: number; ssid: string; diff --git a/lib/framework/NetworkStatus.cpp b/lib/framework/NetworkStatus.cpp index cb0369d0e..fb369386c 100644 --- a/lib/framework/NetworkStatus.cpp +++ b/lib/framework/NetworkStatus.cpp @@ -25,6 +25,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) { // for Wifi if (wifi_status == WL_CONNECTED) { root["local_ip"] = WiFi.localIP().toString(); + root["local_ipv6"] = WiFi.localIPv6().toString(); root["mac_address"] = WiFi.macAddress(); root["rssi"] = WiFi.RSSI(); root["ssid"] = WiFi.SSID(); @@ -47,6 +48,7 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) { } else if (ethernet_connected) { // Ethernet root["local_ip"] = ETH.localIP().toString(); + root["local_ipv6"] = ETH.localIPv6().toString(); root["mac_address"] = ETH.macAddress(); root["subnet_mask"] = ETH.subnetMask().toString(); root["gateway_ip"] = ETH.gatewayIP().toString(); diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 36a16cf49..3ab848e21 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -640,8 +640,14 @@ void Mqtt::on_connect() { #ifndef EMSESP_STANDALONE if (EMSESP::system_.ethernet_connected()) { doc["ip"] = ETH.localIP().toString(); + if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") { + doc["ipv6"] = ETH.localIPv6().toString(); + } } else { doc["ip"] = WiFi.localIP().toString(); + if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") { + doc["ipv6"] = WiFi.localIPv6().toString(); + } } #endif publish(F_(info), doc.as()); // topic called "info" diff --git a/src/system.cpp b/src/system.cpp index d752ba4ba..43c432303 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -279,6 +279,8 @@ void System::start(uint32_t heap_start) { get_settings(); #ifndef EMSESP_STANDALONE + // disable bluetooth module + periph_module_disable(PERIPH_BT_MODULE); if (low_clock_) { setCpuFrequencyMhz(160); } @@ -305,12 +307,8 @@ void System::adc_init(bool refresh) { get_settings(); } #ifndef EMSESP_STANDALONE - // setCpuFrequencyMhz(160); // default is 240 - - // disable bluetooth & ADC + // disable ADC /* - btStop(); - esp_bt_controller_disable(); if (!analog_enabled_) { adc_power_release(); // turn off ADC to save power if not needed } @@ -719,6 +717,9 @@ void System::show_system(uuid::console::Shell & shell) { shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(WiFi.localIP()).c_str(), uuid::printable_to_string(WiFi.subnetMask()).c_str()); shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(WiFi.gatewayIP()).c_str()); shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(WiFi.dnsIP()).c_str()); + if (WiFi.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") { + shell.printfln(F("IPv6 address: %s"), uuid::printable_to_string(WiFi.localIPv6()).c_str()); + } break; case WL_CONNECT_FAILED: @@ -749,6 +750,9 @@ void System::show_system(uuid::console::Shell & shell) { shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str()); shell.printfln(F("IPv4 gateway: %s"), uuid::printable_to_string(ETH.gatewayIP()).c_str()); shell.printfln(F("IPv4 nameserver: %s"), uuid::printable_to_string(ETH.dnsIP()).c_str()); + if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") { + shell.printfln(F("IPv6 address: %s"), uuid::printable_to_string(ETH.localIPv6()).c_str()); + } } else { shell.printfln(F("Ethernet: disconnected")); } From d7486218bce08dab6851338c555b9e0c6f463f8e Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 13:50:56 +0200 Subject: [PATCH 034/122] enum start at zero --- src/devices/solar.cpp | 8 ++++---- src/devices/thermostat.cpp | 16 ++++++++-------- src/locale_EN.h | 18 ++++++++---------- src/telegram.h | 17 ++++++++++------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index 32e81cae9..ac98cd98f 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -429,9 +429,9 @@ void Solar::process_SM100Status2(std::shared_ptr telegram) { * e.g. B0 0B FF 00 02 80 50 64 00 00 29 01 00 00 01 */ void Solar::process_SM100CollectorConfig(std::shared_ptr telegram) { - has_update(telegram->read_value(climateZone_, 0, 1)); - has_update(telegram->read_value(collector1Area_, 3, 2)); - has_update(telegram->read_value(collector1Type_, 5, 1)); + has_update(telegram->read_value(climateZone_, 0)); + has_update(telegram->read_value(collector1Area_, 3)); + has_update(telegram->read_enumvalue(collector1Type_, 5, 1)); } /* @@ -688,7 +688,7 @@ bool Solar::set_collector1Type(const char * value, const int8_t id) { if (!Helpers::value2enum(value, num, FL_(enum_collectortype))) { return false; } - write_command(0x380, 5, num, 0x380); + write_command(0x380, 5, num + 1, 0x380); return true; } diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index b817a30ef..25057ce58 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -773,9 +773,9 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr telegram has_update(telegram->read_value(hc->curr_roomTemp, 4)); // value is * 10 has_update(telegram->read_value(hc->setpoint_roomTemp, 2)); // value is * 10 - has_update(telegram->read_value(hc->modetype, 0)); // 1 = nofrost, 2 = eco, 3 = heat - has_update(telegram->read_value(hc->mode, 1)); // 1 = manual, 2 = auto - hc->hamode = hc->mode; // set special HA mode + has_update(telegram->read_enumvalue(hc->modetype, 0, 1)); // 1 = nofrost, 2 = eco, 3 = heat + has_update(telegram->read_enumvalue(hc->mode, 1, 1)); // 1 = manual, 2 = auto + hc->hamode = hc->mode + 1; // set special HA mode } // type 0x02A5 - data from Worchester CRF200 @@ -917,7 +917,7 @@ void Thermostat::process_RC300OutdoorTemp(std::shared_ptr telegr // 0x240 RC300 parameter void Thermostat::process_RC300Settings(std::shared_ptr telegram) { - has_update(telegram->read_value(ibaBuildingType_, 9)); // 1=light, 2=medium, 3=heavy + has_update(telegram->read_enumvalue(ibaBuildingType_, 9, 1)); // 1=light, 2=medium, 3=heavy has_update(telegram->read_value(ibaMinExtTemperature_, 10)); } @@ -1199,11 +1199,11 @@ bool Thermostat::set_building(const char * value, const int8_t id) { if ((model() == EMS_DEVICE_FLAG_RC300) || (model() == EMS_DEVICE_FLAG_RC100)) { if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) { LOG_INFO(F("Setting building to %s"), value); - write_command(0x240, 9, bd, 0x240); + write_command(0x240, 9, bd + 1, 0x240); return true; } } else { - if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType2))) { + if (Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) { LOG_INFO(F("Setting building to %s"), value); write_command(EMS_TYPE_IBASettings, 6, bd, EMS_TYPE_IBASettings); return true; @@ -2180,7 +2180,7 @@ void Thermostat::register_device_values() { register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, - FL_(enum_ibaBuildingType2), + FL_(enum_ibaBuildingType), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building)); @@ -2210,7 +2210,7 @@ void Thermostat::register_device_values() { register_device_value(TAG_THERMOSTAT_DATA, &ibaBuildingType_, DeviceValueType::ENUM, - FL_(enum_ibaBuildingType2), + FL_(enum_ibaBuildingType), FL_(ibaBuildingType), DeviceValueUOM::NONE, MAKE_CF_CB(set_building)); diff --git a/src/locale_EN.h b/src/locale_EN.h index 2ea1b96a1..c04b65f73 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -248,7 +248,6 @@ MAKE_PSTR_WORD(light) MAKE_PSTR_WORD(medium) MAKE_PSTR_WORD(heavy) MAKE_PSTR_WORD(own_prog) -MAKE_PSTR(blank, "") MAKE_PSTR_WORD(start) MAKE_PSTR_WORD(heat) MAKE_PSTR_WORD(hold) @@ -309,25 +308,24 @@ MAKE_PSTR_LIST(enum_ibaMainDisplay, F_(smoke_temperature)) MAKE_PSTR_LIST(enum_ibaLanguage, F_(german), F_(dutch), F_(french), F_(italian)) MAKE_PSTR_LIST(enum_floordrystatus, F_(off), F_(start), F_(heat), F_(hold), F_(cool), F_(end)) -MAKE_PSTR_LIST(enum_ibaBuildingType, F_(blank), F_(light), F_(medium), F_(heavy)) // RC300 +MAKE_PSTR_LIST(enum_ibaBuildingType, F_(light), F_(medium), F_(heavy)) // RC300 MAKE_PSTR_LIST(enum_wwMode, F_(off), F_(low), F_(high), F_(auto), F_(own_prog)) MAKE_PSTR_LIST(enum_wwCircMode, F_(off), F_(on), F_(auto), F_(own_prog)) -MAKE_PSTR_LIST(enum_ibaBuildingType2, F_(light), F_(medium), F_(heavy)) // RC30, RC35 MAKE_PSTR_LIST(enum_wwMode2, F_(off), F_(on), F_(auto)) MAKE_PSTR_LIST(enum_wwCircMode2, F_(off), F_(on), F_(auto)) MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor)) MAKE_PSTR_LIST(enum_summermode, F_(summer), F_(auto), F_(winter)) -MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto)) // RC100, RC300, RC310 -MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto)) // RC20 -MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto)) // RC35, RC30 -MAKE_PSTR_LIST(enum_mode4, F_(blank), F_(manual), F_(auto), F_(holiday)) // JUNKERS -MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off)) // CRF +MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto)) // RC100, RC300, RC310 +MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto)) // RC20 +MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto)) // RC35, RC30 +MAKE_PSTR_LIST(enum_mode4, F_(manual), F_(auto), F_(holiday)) // JUNKERS +MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off)) // CRF MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort)) MAKE_PSTR_LIST(enum_modetype2, F_(day)) MAKE_PSTR_LIST(enum_modetype3, F_(night), F_(day)) -MAKE_PSTR_LIST(enum_modetype4, F_(blank), F_(nofrost), F_(eco), F_(heat)) +MAKE_PSTR_LIST(enum_modetype4, F_(nofrost), F_(eco), F_(heat)) MAKE_PSTR_LIST(enum_modetype5, F_(off), F_(on)) MAKE_PSTR_LIST(enum_reducemode, F_(nofrost), F_(reduce), F_(room), F_(outdoor)) @@ -341,7 +339,7 @@ MAKE_PSTR_LIST(enum_hamode, F_(off), F_(heat), F_(auto), F_(heat), F_(off), F_(h // solar list MAKE_PSTR_LIST(enum_solarmode, F_(constant), F("pwm"), F("analog")) -MAKE_PSTR_LIST(enum_collectortype, F_(blank), F("flat"), F("vacuum")) +MAKE_PSTR_LIST(enum_collectortype, F("flat"), F("vacuum")) // MQTT topics and full text for values and commands MAKE_PSTR(homeassistant, "homeassistant/") diff --git a/src/telegram.h b/src/telegram.h index df9401c87..d49f95a4b 100644 --- a/src/telegram.h +++ b/src/telegram.h @@ -92,10 +92,7 @@ class Telegram { } uint8_t val = value; value = (uint8_t)(((this->message_data[abs_index]) >> (bit)) & 0x01); - if (val != value) { - return true; - } - return false; + return (val != value); } // read a value from a telegram. We always store the value, regardless if its garbage @@ -116,10 +113,16 @@ class Telegram { for (uint8_t i = 0; i < num_bytes; i++) { value = (value << 8) + this->message_data[index - this->offset + i]; // shift by byte } - if (val != value) { - return true; + return (val != value); + } + + bool read_enumvalue(uint8_t & value, const uint8_t index, uint8_t start = 0) const { + if ((index < this->offset) || ((index - this->offset) >= this->message_length)) { + return false; } - return false; + uint8_t val = value; + value = this->message_data[index - this->offset] - start; + return (val != value); } private: From fb94cf953a0f845d3225cd992ad01ef3ba978ad1 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 13:52:16 +0200 Subject: [PATCH 035/122] no enum numbers for "hamode" --- src/emsdevice.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 05509f774..dd7d5a2bb 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -679,7 +679,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t switch (dv.type) { case DeviceValueType::ENUM: { - if (Helpers::hasValue((uint8_t)(*(uint8_t *)(dv.value_p)))) { + if (*(uint8_t *)(dv.value_p) < dv.options_size) { if (Mqtt::bool_format() == BOOL_FORMAT_10) { json[value] = (uint8_t)(*(uint8_t *)(dv.value_p)); } else { @@ -781,7 +781,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t } // add uom if it's not a " " (single space) - if ((dv.uom != DeviceValueUOM::NONE) && (dv.uom != DeviceValueUOM::NUM) && (dv.uom != DeviceValueUOM::BOOLEAN)) { + if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") { json["unit"] = EMSdevice::uom_to_string(dv.uom); } @@ -870,7 +870,8 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter // handle ENUMs else if ((dv.type == DeviceValueType::ENUM) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) { if (*(uint8_t *)(dv.value_p) < dv.options_size) { - if (Mqtt::bool_format() == BOOL_FORMAT_10) { + // check for numeric enum-format, but "hamode" always as text + if ((Mqtt::bool_format() == BOOL_FORMAT_10) && (dv.short_name != FL_(hamode)[0])) { json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)); } else { json[name] = dv.options[*(uint8_t *)(dv.value_p)]; From 5f44eb14ada94ecc6775ff4f8dcabe33fa620fb2 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 14:01:24 +0200 Subject: [PATCH 036/122] allow numbers for set_mode --- src/devices/thermostat.cpp | 44 +++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 25057ce58..335f18cee 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -455,13 +455,19 @@ bool Thermostat::thermostat_ha_cmd(const char * message, uint8_t hc_num) { } // check for mode first, which is a string - if (!set_mode(message, hc_num)) { + if (message[0] >= 'A') { + if (set_mode(message, hc_num)) { + return true; + } + } + if ((message[0] >= '0' && message[0] <= '9') || message[0] == '-') { // otherwise handle as a numerical temperature value and set the setpoint temp float f = strtof((char *)message, 0); set_temperature(f, HeatingCircuit::Mode::AUTO, hc_num); + return true; } - return true; + return false; } // decodes the thermostat mode for the heating circuit based on the thermostat type @@ -1467,13 +1473,35 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) { // sets the thermostat working mode, where mode is a string // converts string mode to HeatingCircuit::Mode bool Thermostat::set_mode(const char * value, const int8_t id) { - // quit if its numerical, as it could be mistaken as a temperature value - if (value[0] < 'A') { - return false; - } - std::string mode(10, '\0'); - if (!Helpers::value2string(value, mode)) { + + if (value[0] >= '0' && value[0] <= '9') { + uint8_t num = value[0] - '0'; + switch (model()) { + case EMSdevice::EMS_DEVICE_FLAG_RC20: + case EMSdevice::EMS_DEVICE_FLAG_RC20_N: + mode = uuid::read_flash_string(FL_(enum_mode2)[num]); + break; + case EMSdevice::EMS_DEVICE_FLAG_RC30: + case EMSdevice::EMS_DEVICE_FLAG_RC35: + case EMSdevice::EMS_DEVICE_FLAG_RC30_N: + mode = uuid::read_flash_string(FL_(enum_mode3)[num]); + break; + case EMSdevice::EMS_DEVICE_FLAG_RC300: + case EMSdevice::EMS_DEVICE_FLAG_RC100: + mode = uuid::read_flash_string(FL_(enum_mode)[num]); + break; + case EMSdevice::EMS_DEVICE_FLAG_JUNKERS: + mode = uuid::read_flash_string(FL_(enum_mode4)[num]); + break; + case EMSdevice::EMS_DEVICE_FLAG_CRF: + mode = uuid::read_flash_string(FL_(enum_mode5)[num]); + break; + default: + LOG_WARNING(F("Set mode: Invalid mode")); + return false; + } + } else if (!Helpers::value2string(value, mode)) { LOG_WARNING(F("Set mode: Invalid mode")); return false; } From bd33df2cc7d910850aa0807091a0e61473be2f16 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 14:15:39 +0200 Subject: [PATCH 037/122] Junkers mode/set_mode synchronized --- src/devices/thermostat.cpp | 8 +++++--- src/locale_EN.h | 10 +++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 335f18cee..80e44c575 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -702,6 +702,8 @@ void Thermostat::process_JunkersSet(std::shared_ptr telegram) { has_update(telegram->read_value(hc->daytemp, 17)); // is * 2 has_update(telegram->read_value(hc->nighttemp, 16)); // is * 2 has_update(telegram->read_value(hc->nofrosttemp, 15)); // is * 2 + has_update(telegram->read_enumvalue(hc->mode, 14, 1)); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto + hc->hamode = hc->mode ? hc->mode - 1 : 0; // set special HA mode: off, on, auto } // type 0x0179, ff @@ -714,6 +716,8 @@ void Thermostat::process_JunkersSet2(std::shared_ptr telegram) { has_update(telegram->read_value(hc->daytemp, 7)); // is * 2 has_update(telegram->read_value(hc->nighttemp, 6)); // is * 2 has_update(telegram->read_value(hc->nofrosttemp, 5)); // is * 2 + has_update(telegram->read_enumvalue(hc->mode, 4, 1)); // 0 = nofrost, 1 = eco, 2 = heat, 3 = auto + hc->hamode = hc->mode ? hc->mode - 1 : 0; // set special HA mode: off, on, auto } // type 0xA3 - for external temp settings from the the RC* thermostats (e.g. RC35) @@ -780,8 +784,6 @@ void Thermostat::process_JunkersMonitor(std::shared_ptr telegram has_update(telegram->read_value(hc->setpoint_roomTemp, 2)); // value is * 10 has_update(telegram->read_enumvalue(hc->modetype, 0, 1)); // 1 = nofrost, 2 = eco, 3 = heat - has_update(telegram->read_enumvalue(hc->mode, 1, 1)); // 1 = manual, 2 = auto - hc->hamode = hc->mode + 1; // set special HA mode } // type 0x02A5 - data from Worchester CRF200 @@ -1611,7 +1613,7 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) { } else { offset = EMS_OFFSET_JunkersSetMessage_set_mode; } - validate_typeid = monitor_typeids[hc_p]; + validate_typeid = set_typeids[hc_p]; if (mode == HeatingCircuit::Mode::NOFROST) { set_mode_value = 0x01; } else if (mode == HeatingCircuit::Mode::ECO || (mode == HeatingCircuit::Mode::NIGHT)) { diff --git a/src/locale_EN.h b/src/locale_EN.h index c04b65f73..e9bcd0eb3 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -316,11 +316,11 @@ MAKE_PSTR_LIST(enum_wwCircMode2, F_(off), F_(on), F_(auto)) MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor)) MAKE_PSTR_LIST(enum_summermode, F_(summer), F_(auto), F_(winter)) -MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto)) // RC100, RC300, RC310 -MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto)) // RC20 -MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto)) // RC35, RC30 -MAKE_PSTR_LIST(enum_mode4, F_(manual), F_(auto), F_(holiday)) // JUNKERS -MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off)) // CRF +MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto)) // RC100, RC300, RC310 +MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto)) // RC20 +MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto)) // RC35, RC30 +MAKE_PSTR_LIST(enum_mode4, F_(nofrost), F_(eco), F_(heat), F_(auto)) // JUNKERS +MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off)) // CRF MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort)) MAKE_PSTR_LIST(enum_modetype2, F_(day)) From 3519696bae7302c7d7061248aa6d9d0982211d0a Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 14:34:18 +0200 Subject: [PATCH 038/122] add boiler ww-hysteresis, maintenance settings --- src/devices/boiler.cpp | 95 ++++++++++++++++++++++++++++++++++++++++-- src/devices/boiler.h | 6 +++ src/locale_EN.h | 2 + 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 5e4084018..fcd4448e4 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -136,7 +136,6 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_device_value(TAG_BOILER_DATA, &serviceCode_, DeviceValueType::TEXT, nullptr, FL_(serviceCode), DeviceValueUOM::NONE); register_device_value(TAG_BOILER_DATA, &serviceCodeNumber_, DeviceValueType::USHORT, nullptr, FL_(serviceCodeNumber), DeviceValueUOM::NONE); register_device_value(TAG_BOILER_DATA, &maintenanceMessage_, DeviceValueType::TEXT, nullptr, FL_(maintenanceMessage), DeviceValueUOM::NONE); - register_device_value(TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, FL_(maintenanceDate), DeviceValueUOM::NONE); register_device_value(TAG_BOILER_DATA, &maintenanceType_, DeviceValueType::ENUM, @@ -144,7 +143,20 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const FL_(maintenanceType), DeviceValueUOM::NONE, MAKE_CF_CB(set_maintenance)); - register_device_value(TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, FL_(maintenanceTime), DeviceValueUOM::HOURS); + register_device_value(TAG_BOILER_DATA, + &maintenanceTime_, + DeviceValueType::USHORT, + nullptr, + FL_(maintenanceTime), + DeviceValueUOM::HOURS, + MAKE_CF_CB(set_maintenancetime)); + register_device_value(TAG_BOILER_DATA, + &maintenanceDate_, + DeviceValueType::TEXT, + nullptr, + FL_(maintenanceDate), + DeviceValueUOM::NONE, + MAKE_CF_CB(set_maintenancedate)); // heatpump info if (model() == EMS_DEVICE_FLAG_HEATPUMP) { register_device_value(TAG_BOILER_DATA, &upTimeControl_, DeviceValueType::TIME, FL_(div60), FL_(upTimeControl), DeviceValueUOM::MINUTES); @@ -192,6 +204,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_device_value( TAG_BOILER_DATA_WW, &wWCircPump_, DeviceValueType::BOOL, nullptr, FL_(wWCircPump), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_warmwater_circulation_pump)); register_device_value(TAG_BOILER_DATA_WW, &wWChargeType_, DeviceValueType::ENUM, FL_(enum_charge), FL_(wWChargeType), DeviceValueUOM::NONE); + register_device_value(TAG_BOILER_DATA_WW, &wWHystOn_, DeviceValueType::INT, nullptr, FL_(wWHystOn), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_hyst_on)); + register_device_value(TAG_BOILER_DATA_WW, &wWHystOff_, DeviceValueType::INT, nullptr, FL_(wWHystOff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ww_hyst_off)); register_device_value(TAG_BOILER_DATA_WW, &wWDisinfectionTemp_, DeviceValueType::UINT, @@ -315,8 +329,8 @@ void Boiler::process_UBAParameterWW(std::shared_ptr telegram) { // has_update(telegram->read_bitvalue(wwEquipt_,0,3)); // 8=boiler has ww has_update(telegram->read_value(wWActivated_, 1)); // 0xFF means on has_update(telegram->read_value(wWSelTemp_, 2)); - // has_update(telegram->read_value(wW?_, 3)); // Hyst on (default -5) - // has_update(telegram->read_value(wW?_, 4)); // (0xFF) Maybe: Hyst off -1? + has_update(telegram->read_value(wWHystOn_, 3)); // Hyst on (default -5) + has_update(telegram->read_value(wWHystOff_, 4)); // Hyst off (default -1) has_update(telegram->read_value(wWFlowTempOffset_, 5)); // default 40 has_update(telegram->read_value(wWCircPump_, 6)); // 0xFF means on has_update(telegram->read_value(wWCircMode_, 7)); // 1=1x3min 6=6x3min 7=continuous @@ -554,6 +568,8 @@ void Boiler::process_UBAParameterWWPlus(std::shared_ptr telegram 11)); // 1=1x3min... 6=6x3min, 7=continuous // has_update(telegram->read_value(wWDisinfectTemp_, 12)); // settings, status in E9 // has_update(telegram->read_value(wWSelTemp_, 6)); // settings, status in E9 + has_update(telegram->read_value(wWHystOn_, 7)); + has_update(telegram->read_value(wWHystOff_, 8)); } // 0xE9 - WW monitor ems+ @@ -902,6 +918,42 @@ bool Boiler::set_max_power(const char * value, const int8_t id) { return true; } +// set ww on hysteresis +bool Boiler::set_ww_hyst_on(const char * value, const int8_t id) { + int v = 0; + if (!Helpers::value2number(value, v)) { + LOG_WARNING(F("Set ww on hysteresis: Invalid value")); + return false; + } + + LOG_INFO(F("Setting ww on hysteresis on to %d C"), v); + if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) { + write_command(EMS_TYPE_UBAParameterWWPlus, 7, v, EMS_TYPE_UBAParameterWWPlus); + } else { + write_command(EMS_TYPE_UBAParameterWW, 3, v, EMS_TYPE_UBAParameterWW); + } + + return true; +} + +// set ww off hysteresis +bool Boiler::set_ww_hyst_off(const char * value, const int8_t id) { + int v = 0; + if (!Helpers::value2number(value, v)) { + LOG_WARNING(F("Set ww off hysteresis: Invalid value")); + return false; + } + + LOG_INFO(F("Setting ww off hysteresis off to %d C"), v); + if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) { + write_command(EMS_TYPE_UBAParameterWWPlus, 8, v, EMS_TYPE_UBAParameterWWPlus); + } else { + write_command(EMS_TYPE_UBAParameterWW, 4, v, EMS_TYPE_UBAParameterWW); + } + + return true; +} + // set warm water max power bool Boiler::set_warmwater_maxpower(const char * value, const int8_t id) { int v = 0; @@ -1268,5 +1320,40 @@ bool Boiler::set_maintenance(const char * value, const int8_t id) { LOG_WARNING(F("Setting maintenance: wrong format")); return false; } +//maintenance +bool Boiler::set_maintenancetime(const char * value, const int8_t id) { + int hrs; + if (Helpers::value2number(value, hrs)) { + if (hrs > 99 && hrs < 25600) { + LOG_INFO(F("Setting maintenance time %d hours"), hrs); + uint8_t data[2] = {1, (uint8_t)(hrs / 100)}; + write_command(0x15, 0, data, 2, 0x15); + return true; + } + } + LOG_WARNING(F("Setting maintenance: wrong format")); + return false; +} + +//maintenance +bool Boiler::set_maintenancedate(const char * value, const int8_t id) { + if (strlen(value) == 10) { // date + uint8_t day = (value[0] - '0') * 10 + (value[1] - '0'); + uint8_t month = (value[3] - '0') * 10 + (value[4] - '0'); + uint8_t year = (uint8_t)(Helpers::atoint(&value[6]) - 2000); + if (day > 0 && day < 32 && month > 0 && month < 13) { + LOG_INFO(F("Setting maintenance date to %02d.%02d.%04d"), day, month, year + 2000); + uint8_t data[5] = {2, (uint8_t)(maintenanceTime_ / 100), day, month, year}; + write_command(0x15, 0, data, 5, 0x15); + } else { + LOG_WARNING(F("Setting maintenance: wrong format %d.%d.%d"), day, month, year + 2000); + return false; + } + return true; + } + + LOG_WARNING(F("Setting maintenance: wrong format")); + return false; +} } // namespace emsesp diff --git a/src/devices/boiler.h b/src/devices/boiler.h index deeb3585c..7c3a0ba70 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -83,6 +83,8 @@ class Boiler : public EMSdevice { uint32_t wWStarts_; // Warm Water # starts uint32_t wWStarts2_; // Warm water control starts uint32_t wWWorkM_; // Warm Water # minutes + int8_t wWHystOn_; + int8_t wWHystOff_; uint16_t mixerTemp_; // mixing temperature uint16_t tankMiddleTemp_; // Tank middle temperature (TS3) @@ -220,6 +222,10 @@ class Boiler : public EMSdevice { bool set_pump_delay(const char * value, const int8_t id); bool set_reset(const char * value, const int8_t id); bool set_maintenance(const char * value, const int8_t id); + bool set_maintenancetime(const char * value, const int8_t id); + bool set_maintenancedate(const char * value, const int8_t id); + bool set_ww_hyst_on(const char * value, const int8_t id); + bool set_ww_hyst_off(const char * value, const int8_t id); }; } // namespace emsesp diff --git a/src/locale_EN.h b/src/locale_EN.h index e9bcd0eb3..64a5bb607 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -463,6 +463,8 @@ MAKE_PSTR_LIST(tankMiddleTemp, F("tankmiddletemp"), F("ww tank middle temperatur MAKE_PSTR_LIST(wWStarts, F("wwstarts"), F("ww # starts")) MAKE_PSTR_LIST(wWStarts2, F("wwstarts2"), F("ww # control starts")) MAKE_PSTR_LIST(wWWorkM, F("wwworkm"), F("ww active time")) +MAKE_PSTR_LIST(wWHystOn, F("wwhyston"), F("ww hysteresis on temperature")) +MAKE_PSTR_LIST(wWHystOff, F("wwhystoff"), F("ww hysteresis off temperature")) // thermostat // extra commands, with no long name so they don't show up in WebUI From 82978a25c5c4c75575808bf900dfeca9ce2ead5a Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 14:42:12 +0200 Subject: [PATCH 039/122] show wwtapactivated as value --- src/devices/boiler.cpp | 11 +++++++++-- src/devices/boiler.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index fcd4448e4..6d687b097 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -86,7 +86,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const } // MQTT commands for boiler topic register_device_value( - TAG_BOILER_DATA, &dummybool_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_tapwarmwater_activated)); + TAG_BOILER_DATA, &wWTapActivated_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_tapwarmwater_activated)); register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::ENUM, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset)); // add values @@ -303,6 +303,11 @@ void Boiler::check_active(const bool force) { Mqtt::publish(F_(heating_active), Helpers::render_boolean(s, b)); } + // check if we can use tapactivated in flow systems + if ((wWType_ == 1) && !Helpers::hasValue(wWTapActivated_, EMS_VALUE_BOOL)) { + wWTapActivated_= 1; + } + // check if tap water is active, bits 1 and 4 must be set // also check if there is a flowsensor and flow-type static bool flowsensor = false; @@ -1150,14 +1155,16 @@ bool Boiler::set_tapwarmwater_activated(const char * value, const int8_t id) { // a setting of 0x00 puts it back into normal operating mode // when in test mode we're able to mess around with the 3-way valve settings if (!v) { - // on + // DHW off message_data[0] = 0x5A; // test mode on message_data[1] = 0x00; // burner output 0% message_data[3] = 0x64; // boiler pump capacity 100% message_data[4] = 0xFF; // 3-way valve hot water only + wWTapActivated_= 0; } else { // get out of test mode. Send all zeros. // telegram: 0B 08 1D 00 00 + wWTapActivated_ = 1; } write_command(EMS_TYPE_UBAFunctionTest, 0, message_data, sizeof(message_data), 0); diff --git a/src/devices/boiler.h b/src/devices/boiler.h index 7c3a0ba70..f2ed15c0d 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -85,6 +85,7 @@ class Boiler : public EMSdevice { uint32_t wWWorkM_; // Warm Water # minutes int8_t wWHystOn_; int8_t wWHystOff_; + uint8_t wWTapActivated_; // maintenance-mode to switch DHW off uint16_t mixerTemp_; // mixing temperature uint16_t tankMiddleTemp_; // Tank middle temperature (TS3) From 358d6010b0048501a0b3f98f4d64e663091ef620 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 15:02:24 +0200 Subject: [PATCH 040/122] Web selectbox for enum commands (like bool) --- CHANGELOG_LATEST.md | 8 +++ interface/src/project/EMSESPDevicesForm.tsx | 1 + interface/src/project/EMSESPtypes.ts | 7 ++- interface/src/project/ValueForm.tsx | 42 +++++++++----- src/devices/boiler.cpp | 10 ++-- src/devices/solar.cpp | 4 +- src/devices/thermostat.cpp | 62 ++++++++++----------- src/devices/thermostat.h | 2 +- src/emsdevice.cpp | 19 ++++++- src/emsdevice.h | 6 +- src/locale_EN.h | 4 +- 11 files changed, 107 insertions(+), 58 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 4478bab35..bce2d5ed6 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -6,13 +6,21 @@ - System Log in Web UI will show current time if the NTP Service is enabled (#82) - Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode (#83) - optional low clockrate (160 MHz) (#83) +- selectbox for enumerated values in web +- settings for water hysteresis on/off ## Fixed +- set mode allow numbers +- Junkers thermostat shows mode as selected by set_mode +- HA thermostat mode if bool-format: numbers is selected + ## Changed - removed Rx echo failures counting as incomplete telegrams. Bad telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80) - add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat - added debug target to PlatformIO build to help hunt down system crashes +- enumerated values always start at zero +- maintenance settings for time/date as extra setting ## Removed diff --git a/interface/src/project/EMSESPDevicesForm.tsx b/interface/src/project/EMSESPDevicesForm.tsx index 98630dae2..b9bcb422b 100644 --- a/interface/src/project/EMSESPDevicesForm.tsx +++ b/interface/src/project/EMSESPDevicesForm.tsx @@ -125,6 +125,7 @@ function formatValue(value: any, uom: number) { case DeviceValueUOM.MINUTES: return value ? formatDuration(value) : '0 minutes'; case DeviceValueUOM.NONE: + case DeviceValueUOM.LIST: return value; case DeviceValueUOM.NUM: return new Intl.NumberFormat().format(value); diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts index 2294b384a..98527fe75 100644 --- a/interface/src/project/EMSESPtypes.ts +++ b/interface/src/project/EMSESPtypes.ts @@ -64,6 +64,7 @@ export interface DeviceValue { u: number; n: string; c: string; + l: string[]; } export interface EMSESPDeviceData { @@ -88,7 +89,8 @@ export enum DeviceValueUOM { SECONDS, DBM, NUM, - BOOLEAN + BOOLEAN, + LIST } export const DeviceValueUOM_s = [ @@ -108,5 +110,6 @@ export const DeviceValueUOM_s = [ 'seconds', 'dBm', 'number', - 'on/off' + 'on/off', + '' ]; diff --git a/interface/src/project/ValueForm.tsx b/interface/src/project/ValueForm.tsx index 3200a543b..e648a0fe9 100644 --- a/interface/src/project/ValueForm.tsx +++ b/interface/src/project/ValueForm.tsx @@ -51,24 +51,40 @@ class ValueForm extends React.Component { > Change Value - {devicevalue.u !== DeviceValueUOM.BOOLEAN && ( - - {DeviceValueUOM_s[devicevalue.u]} - - } - aria-describedby="outlined-value-helper-text" - inputProps={{ - 'aria-label': 'value' - }} - /> + variant="outlined" + > + {devicevalue.l.map((val) => ( + {val} + ))} + )} + {devicevalue.u !== DeviceValueUOM.BOOLEAN && + devicevalue.u !== DeviceValueUOM.LIST && ( + + {DeviceValueUOM_s[devicevalue.u]} + + } + aria-describedby="outlined-value-helper-text" + inputProps={{ + 'aria-label': 'value' + }} + /> + )} {devicevalue.u === DeviceValueUOM.BOOLEAN && ( mode, DeviceValueType::ENUM, FL_(enum_mode), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); - register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype), FL_(modetype), DeviceValueUOM::NONE); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode), FL_(mode), DeviceValueUOM::LIST, MAKE_CF_CB(set_mode)); + register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype), FL_(modetype), DeviceValueUOM::LIST); register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(ecotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ecotemp)); register_device_value(tag, &hc->manualtemp, DeviceValueType::UINT, FL_(div2), FL_(manualtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_manualtemp)); register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(comforttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_comforttemp)); @@ -2333,34 +2333,34 @@ void Thermostat::register_device_values_hc(std::shared_ptrroominfluence, DeviceValueType::UINT, nullptr, FL_(roominfluence), DeviceValueUOM::NONE, MAKE_CF_CB(set_roominfluence)); register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, nullptr, FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp)); register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES); - register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE); + register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::LIST); register_device_value( - tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), FL_(summersetmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_summermode)); + tag, &hc->summer_setmode, DeviceValueType::ENUM, FL_(enum_summermode), FL_(summersetmode), DeviceValueUOM::LIST, MAKE_CF_CB(set_summermode)); register_device_value(tag, &hc->summermode, DeviceValueType::BOOL, nullptr, FL_(summermode), DeviceValueUOM::BOOLEAN); register_device_value( - tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode)); + tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode), FL_(controlmode), DeviceValueUOM::LIST, MAKE_CF_CB(set_controlmode)); register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); register_device_value(tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp)); break; case EMS_DEVICE_FLAG_CRF: - register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode5), FL_(mode), DeviceValueUOM::NONE); - register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype5), FL_(modetype), DeviceValueUOM::NONE); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode5), FL_(mode), DeviceValueUOM::LIST); + register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype5), FL_(modetype), DeviceValueUOM::LIST); register_device_value(tag, &hc->targetflowtemp, DeviceValueType::UINT, nullptr, FL_(targetflowtemp), DeviceValueUOM::DEGREES); break; case EMS_DEVICE_FLAG_RC20: - register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::LIST, MAKE_CF_CB(set_mode)); break; case EMS_DEVICE_FLAG_RC20_N: - register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); - register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype2), FL_(modetype), DeviceValueUOM::NONE); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode2), FL_(mode), DeviceValueUOM::LIST, MAKE_CF_CB(set_mode)); + register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype2), FL_(modetype), DeviceValueUOM::LIST); register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp)); register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp)); register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); break; case EMS_DEVICE_FLAG_RC30_N: case EMS_DEVICE_FLAG_RC35: - register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); - register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), FL_(modetype), DeviceValueUOM::NONE); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode3), FL_(mode), DeviceValueUOM::LIST, MAKE_CF_CB(set_mode)); + register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype3), FL_(modetype), DeviceValueUOM::LIST); register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(daytemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_daytemp)); register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(nighttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nighttemp)); register_device_value(tag, &hc->designtemp, DeviceValueType::UINT, nullptr, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp)); @@ -2375,22 +2375,22 @@ void Thermostat::register_device_values_hc(std::shared_ptrminflowtemp, DeviceValueType::UINT, nullptr, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp)); register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT, nullptr, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp)); register_device_value(tag, &hc->flowtempoffset, DeviceValueType::UINT, nullptr, FL_(flowtempoffset), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowtempoffset)); - register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::NONE); - register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), FL_(reducemode), DeviceValueUOM::NONE, MAKE_CF_CB(set_reducemode)); + register_device_value(tag, &hc->heatingtype, DeviceValueType::ENUM, FL_(enum_heatingtype), FL_(heatingtype), DeviceValueUOM::LIST); + register_device_value(tag, &hc->reducemode, DeviceValueType::ENUM, FL_(enum_reducemode), FL_(reducemode), DeviceValueUOM::LIST, MAKE_CF_CB(set_reducemode)); register_device_value( - tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), FL_(controlmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_controlmode)); - register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control), FL_(control), DeviceValueUOM::NONE, MAKE_CF_CB(set_control)); + tag, &hc->controlmode, DeviceValueType::ENUM, FL_(enum_controlmode2), FL_(controlmode), DeviceValueUOM::LIST, MAKE_CF_CB(set_controlmode)); + register_device_value(tag, &hc->control, DeviceValueType::ENUM, FL_(enum_control), FL_(control), DeviceValueUOM::LIST, MAKE_CF_CB(set_control)); register_device_value(tag, &hc->program, DeviceValueType::UINT, nullptr, FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); register_device_value(tag, &hc->pause, DeviceValueType::UINT, nullptr, FL_(pause), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pause)); register_device_value(tag, &hc->party, DeviceValueType::UINT, nullptr, FL_(party), DeviceValueUOM::HOURS, MAKE_CF_CB(set_party)); register_device_value(tag, &hc->tempautotemp, DeviceValueType::UINT, FL_(div2), FL_(tempautotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_tempautotemp)); register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, nullptr, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp)); register_device_value(tag, &hc->remotetemp, DeviceValueType::SHORT, FL_(div10), FL_(remotetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_remotetemp)); - register_device_value(tag, &dummychar_, DeviceValueType::TEXT, nullptr, FL_(switchtime), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime)); + register_device_value(tag, &dummy_, DeviceValueType::CMD, nullptr, FL_(switchtime), DeviceValueUOM::NONE, MAKE_CF_CB(set_switchtime)); break; case EMS_DEVICE_FLAG_JUNKERS: - register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode4), FL_(mode), DeviceValueUOM::NONE, MAKE_CF_CB(set_mode)); - register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype4), FL_(modetype), DeviceValueUOM::NONE); + register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode4), FL_(mode), DeviceValueUOM::LIST, MAKE_CF_CB(set_mode)); + register_device_value(tag, &hc->modetype, DeviceValueType::ENUM, FL_(enum_modetype4), FL_(modetype), DeviceValueUOM::LIST); register_device_value(tag, &hc->daytemp, DeviceValueType::UINT, FL_(div2), FL_(heattemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_heattemp)); register_device_value(tag, &hc->nighttemp, DeviceValueType::UINT, FL_(div2), FL_(ecotemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_ecotemp)); register_device_value(tag, &hc->nofrosttemp, DeviceValueType::INT, FL_(div2), FL_(nofrosttemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_nofrosttemp)); diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 9a76726cc..edf994582 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -146,7 +146,7 @@ class Thermostat : public EMSdevice { char errorCode_[15]; // code from 0xA2 as string i.e. "A22(816)" uint16_t errorNumber_; // used internally to build error code char lastCode_[30]; // error log - char dummychar_[5]; // for commands with no output + uint8_t dummy_; // for commands with no output // Installation parameters uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index dd7d5a2bb..dc59a3399 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -40,7 +40,8 @@ static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__ F_(seconds), F_(dbm), F_(num), - F_(bool) + F_(bool), + F_(blank) }; @@ -580,6 +581,12 @@ void EMSdevice::generate_values_json_web(JsonObject & json) { } } + // handle commands without value + else if(dv.type == DeviceValueType::CMD) { + obj = data.createNestedObject(); + obj["v"] = ""; + } + else { // handle Integers and Floats // If a divider is specified, do the division to 2 decimals places and send back as double/float @@ -634,6 +641,16 @@ void EMSdevice::generate_values_json_web(JsonObject & json) { } else { obj["c"] = ""; } + + // add enum and text option settings + if ((dv.uom == DeviceValueUOM::LIST) && dv.has_cmd) { + JsonArray l = obj.createNestedArray("l"); + for (uint8_t i = 0; i < dv.options_size; i++) { + if (!uuid::read_flash_string(dv.options[i]).empty()) { + l.add(uuid::read_flash_string(dv.options[i])); + } + } + } } } } diff --git a/src/emsdevice.h b/src/emsdevice.h index bd414aa0d..ef19c7494 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -39,7 +39,8 @@ enum DeviceValueType : uint8_t { ULONG, TIME, // same as ULONG (32 bits) ENUM, - TEXT + TEXT, + CMD }; @@ -64,7 +65,8 @@ enum DeviceValueUOM : uint8_t { SECONDS, // 13 DBM, // 14 NUM, // 15 - BOOLEAN // 16 + BOOLEAN, // 16 + LIST // 17 }; diff --git a/src/locale_EN.h b/src/locale_EN.h index 64a5bb607..592f74cbe 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -175,6 +175,7 @@ MAKE_PSTR(seconds, "seconds") MAKE_PSTR(dbm, "dBm") MAKE_PSTR(num, " ") // this is hack so HA renders numbers correctly MAKE_PSTR(bool, " ") // this is hack so HA renders numbers correctly +MAKE_PSTR(blank, " ") // this is hack so HA renders numbers correctly // TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp // use empty string if want to suppress showing tags @@ -467,8 +468,9 @@ MAKE_PSTR_LIST(wWHystOn, F("wwhyston"), F("ww hysteresis on temperature")) MAKE_PSTR_LIST(wWHystOff, F("wwhystoff"), F("ww hysteresis off temperature")) // thermostat +// extra commands +MAKE_PSTR_LIST(switchtime, F("switchtime"), F("single program switchtime")) // extra commands, with no long name so they don't show up in WebUI -MAKE_PSTR_LIST(switchtime, F("switchtime")) MAKE_PSTR_LIST(temp, F("temp")) MAKE_PSTR_LIST(hatemp, F("hatemp")) MAKE_PSTR_LIST(hamode, F("hamode")) From 6c2bae62961510232de371a6ac09b9c2d2d59d54 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 15:19:51 +0200 Subject: [PATCH 041/122] remove SM10 collector min/max wrong values --- src/devices/solar.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index ae4f776bd..96020aa2d 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -91,11 +91,8 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s TAG_NONE, &solarPumpTurnonDiff_, DeviceValueType::UINT, nullptr, FL_(solarPumpTurnonDiff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TurnonDiff)); register_device_value( TAG_NONE, &solarPumpTurnoffDiff_, DeviceValueType::UINT, nullptr, FL_(solarPumpTurnoffDiff), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_TurnoffDiff)); - register_device_value( - TAG_NONE, &collectorMaxTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMaxTemp)); - register_device_value( - TAG_NONE, &collectorMinTemp_, DeviceValueType::UINT, nullptr, FL_(collectorMinTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_CollectorMinTemp)); register_device_value(TAG_NONE, &collectorShutdown_, DeviceValueType::BOOL, nullptr, FL_(collectorShutdown), DeviceValueUOM::BOOLEAN); + register_device_value(TAG_NONE, &tankHeated_, DeviceValueType::BOOL, nullptr, FL_(tankHeated), DeviceValueUOM::BOOLEAN); register_device_value(TAG_NONE, &solarPower_, DeviceValueType::ULONG, nullptr, FL_(solarPower), DeviceValueUOM::W); register_device_value(TAG_NONE, &energyLastHour_, DeviceValueType::ULONG, FL_(div10), FL_(energyLastHour), DeviceValueUOM::WH); register_device_value(TAG_NONE, &maxFlow_, DeviceValueType::UINT, FL_(div10), FL_(maxFlow), DeviceValueUOM::LMIN, MAKE_CF_CB(set_SM10MaxFlow)); @@ -219,12 +216,6 @@ bool Solar::publish_ha_config() { // Solar(0x30) -> All(0x00), (0x96), data: FF 18 19 0A 02 5A 27 0A 05 2D 1E 0F 64 28 0A void Solar::process_SM10Config(std::shared_ptr telegram) { has_update(telegram->read_value(solarIsEnabled_, 0)); // FF on - uint8_t colmax = collectorMaxTemp_ / 10; - has_update(telegram->read_value(colmax, 3)); - collectorMaxTemp_ = colmax * 10; - uint8_t colmin = collectorMinTemp_ / 10; - has_update(telegram->read_value(colmin, 4)); - collectorMinTemp_ = colmin * 10; has_update(telegram->read_value(solarPumpMinMod_, 2)); has_update(telegram->read_value(solarPumpTurnonDiff_, 7)); has_update(telegram->read_value(solarPumpTurnoffDiff_, 8)); @@ -237,7 +228,7 @@ void Solar::process_SM10Monitor(std::shared_ptr telegram) { uint8_t solarpumpmod = solarPumpModulation_; has_update(telegram->read_bitvalue(collectorShutdown_, 0, 3)); - // has_update(telegram->read_bitvalue(tankHeated_, 0, x)); // tank full, to be determined + has_update(telegram->read_bitvalue(tankHeated_, 0, 2)); // tankMaxTemp reached has_update(telegram->read_value(collectorTemp_, 2)); // collector temp from SM10, is *10 has_update(telegram->read_value(tankBottomTemp_, 5)); // tank bottom temp from SM10, is *10 has_update(telegram->read_value(solarPumpModulation_, 4)); // modulation solar pump From 1e61b5670e794864678a6933c8c5cb15e7edb0c0 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 11 Jul 2021 15:50:15 +0200 Subject: [PATCH 042/122] delay in emsesp-loop as mentioned in #78 --- src/emsesp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 9fda46f59..fc384d844 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -1231,7 +1231,8 @@ void EMSESP::loop() { console_.loop(); // telnet/serial console - // delay(1); // helps telnet catch up. don't think its needed in ESP32 3.1.0 + // https://github.com/emsesp/EMS-ESP32/issues/78#issuecomment-877599145 + delay(1); // helps telnet catch up. don't think its needed in ESP32 3.1.0 } } // namespace emsesp From 65c9bf7e5284f2dc9112afcecabe57bf45fdf287 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Wed, 14 Jul 2021 17:00:14 +0200 Subject: [PATCH 043/122] check bufferlength, add formatstrings --- lib/ESPAsyncWebServer/WebResponses.cpp | 2 +- src/devices/thermostat.cpp | 6 +++++- src/emsesp.cpp | 18 +++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/ESPAsyncWebServer/WebResponses.cpp b/lib/ESPAsyncWebServer/WebResponses.cpp index adeab988b..6e24444b7 100644 --- a/lib/ESPAsyncWebServer/WebResponses.cpp +++ b/lib/ESPAsyncWebServer/WebResponses.cpp @@ -321,7 +321,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u free(buf); return 0; } - outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen; + outLen = snprintf_P((char*)buf+headLen, sizeof(buf)-headLen-2, PSTR("%x"), readLen) + headLen; while(outLen < headLen + 4) buf[outLen++] = ' '; buf[outLen++] = '\r'; buf[outLen++] = '\n'; diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 66c58d5cc..b57d9bc69 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -1475,7 +1475,11 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) { // sets the thermostat working mode, where mode is a string // converts string mode to HeatingCircuit::Mode bool Thermostat::set_mode(const char * value, const int8_t id) { - std::string mode(10, '\0'); + std::string mode(20, '\0'); + if (strlen(value) >= 20) { + LOG_WARNING(F("Set mode: Invalid mode")); + return false; + } if (value[0] >= '0' && value[0] <= '9') { uint8_t num = value[0] - '0'; diff --git a/src/emsesp.cpp b/src/emsesp.cpp index fc384d844..aa125ceec 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -613,10 +613,10 @@ std::string EMSESP::pretty_telegram(std::shared_ptr telegram) { uint8_t offset = telegram->offset; // find name for src and dest by looking up known devices - std::string src_name; - std::string dest_name; - std::string type_name; - std::string direction; + std::string src_name(""); + std::string dest_name(""); + std::string type_name(""); + std::string direction(""); for (const auto & emsdevice : emsdevices) { if (emsdevice) { // get src & dest @@ -774,7 +774,7 @@ void EMSESP::process_version(std::shared_ptr telegram) { bool EMSESP::process_telegram(std::shared_ptr telegram) { // if watching or reading... if ((telegram->type_id == read_id_) && (telegram->dest == txservice_.ems_bus_id())) { - LOG_NOTICE(pretty_telegram(telegram).c_str()); + LOG_NOTICE(F("%s"), pretty_telegram(telegram).c_str()); publish_response(telegram); if (!read_next_) { read_id_ = WATCH_ID_NONE; @@ -783,12 +783,12 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { } else if (watch() == WATCH_ON) { if ((watch_id_ == WATCH_ID_NONE) || (telegram->type_id == watch_id_) || ((watch_id_ < 0x80) && ((telegram->src == watch_id_) || (telegram->dest == watch_id_)))) { - LOG_NOTICE(pretty_telegram(telegram).c_str()); + LOG_NOTICE(F("%s"), pretty_telegram(telegram).c_str()); } else if (!trace_raw_) { - LOG_TRACE(pretty_telegram(telegram).c_str()); + LOG_TRACE(F("%s"), pretty_telegram(telegram).c_str()); } } else if (!trace_raw_) { - LOG_TRACE(pretty_telegram(telegram).c_str()); + LOG_TRACE(F("%s"), pretty_telegram(telegram).c_str()); } // only process broadcast telegrams or ones sent to us on request @@ -840,7 +840,7 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { if (!found) { LOG_DEBUG(F("No telegram type handler found for ID 0x%02X (src 0x%02X)"), telegram->type_id, telegram->src); if (watch() == WATCH_UNKNOWN) { - LOG_NOTICE(pretty_telegram(telegram).c_str()); + LOG_NOTICE(F("%s"), pretty_telegram(telegram).c_str()); } if (first_scan_done_ && !knowndevice && (telegram->src != EMSbus::ems_bus_id()) && (telegram->src != 0x0B) && (telegram->src != 0x0C) && (telegram->src != 0x0D)) { From 7a0fe3819bbc0256deb3468fd250739bf6e4ef20 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Wed, 14 Jul 2021 17:00:54 +0200 Subject: [PATCH 044/122] reset Settingsflags after update --- src/web/WebSettingsService.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 5ecffddf4..2df431e26 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -20,7 +20,7 @@ namespace emsesp { -uint8_t WebSettings::flags_; +uint8_t WebSettings::flags_ = 0; using namespace std::placeholders; // for `_1` etc @@ -127,9 +127,7 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT; check_flag(prev, settings.syslog_port, ChangeFlags::SYSLOG); - prev = settings.trace_raw; settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW; - check_flag(prev, settings.trace_raw, ChangeFlags::SYSLOG); EMSESP::trace_raw(settings.trace_raw); // adc @@ -209,6 +207,8 @@ void WebSettingsService::onUpdate() { if (WebSettings::has_flags(WebSettings::ChangeFlags::LED)) { EMSESP::system_.led_init(true); // reload settings } + + WebSettings::reset_flags(); } void WebSettingsService::begin() { From 736eee79df8c9dae836d9fb69858bed8708f8610 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Wed, 14 Jul 2021 18:10:04 +0200 Subject: [PATCH 045/122] fix change-check --- src/web/WebSettingsService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 2df431e26..6d96b781d 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -119,7 +119,7 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) String old_syslog_host = settings.syslog_host; settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST; - if (old_syslog_host.equals(settings.syslog_host.c_str())) { + if (!old_syslog_host.equals(settings.syslog_host)) { add_flags(ChangeFlags::SYSLOG); } From e581539cf979350c3b21de73ae0d0b5350b47a44 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Thu, 15 Jul 2021 11:09:59 +0200 Subject: [PATCH 046/122] Fix #78 --- src/telegram.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/telegram.cpp b/src/telegram.cpp index d4f844443..b5ce8b0ca 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -573,8 +573,8 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request: %s"), (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), - MAXIMUM_TX_RETRIES), - telegram_last_->to_string().c_str(); + MAXIMUM_TX_RETRIES, + telegram_last_->to_string().c_str()); return; } From 1f793c49ae712e83d72283d77cf1be88fa96bcd0 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 16 Jul 2021 10:16:32 +0200 Subject: [PATCH 047/122] Move dallas/bool/enum formats to Settings #76 --- CHANGELOG_LATEST.md | 1 + interface/src/mqtt/MqttSettingsForm.tsx | 26 ------- interface/src/mqtt/types.ts | 2 - interface/src/project/EMSESPSettingsForm.tsx | 78 +++++++++++++++++--- interface/src/project/EMSESPtypes.ts | 3 + lib/framework/MqttSettingsService.cpp | 9 --- lib/framework/MqttSettingsService.h | 2 - lib_standalone/ESP8266React.h | 5 +- mock-api/server.js | 5 +- src/dallassensor.cpp | 21 +++--- src/dallassensor.h | 11 +++ src/default_settings.h | 4 + src/emsdevice.cpp | 32 ++++---- src/emsesp.cpp | 2 + src/emsesp.h | 18 +++++ src/helpers.cpp | 4 +- src/mqtt.cpp | 4 - src/mqtt.h | 17 ----- src/system.cpp | 5 +- src/web/WebSettingsService.cpp | 12 +++ src/web/WebSettingsService.h | 6 ++ 21 files changed, 162 insertions(+), 105 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index bce2d5ed6..700ea295e 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -22,5 +22,6 @@ - added debug target to PlatformIO build to help hunt down system crashes - enumerated values always start at zero - maintenance settings for time/date as extra setting +- move api/mqtt formats to `settings`, add `enum format` ## Removed diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index d4ecc3b0f..b612e79b8 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -187,32 +187,6 @@ class MqttSettingsForm extends React.Component { nested on a single topic as individual topics - - by Sensor ID - by Number - - - "on"/"off" - true/false - 1/0 - "ON"/"OFF" - { /> )} - - } - label="Bypass Access Token authorization on API calls" - /> { /> +

+ + API and MQTT Options + + + + } + label="Bypass Access Token authorization on API calls" + /> + + + + "on"/"off" + "ON"/"OFF" + true/false + 1/0 + + + + + Text + Number + + + + + by Sensor ID + by Number + + + +

Syslog diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts index 98527fe75..f6b9bbf7a 100644 --- a/interface/src/project/EMSESPtypes.ts +++ b/interface/src/project/EMSESPtypes.ts @@ -22,6 +22,9 @@ export interface EMSESPSettings { pbutton_gpio: number; trace_raw: boolean; board_profile: string; + bool_format: number; + dallas_format: number; + enum_format: number; } export enum busConnectionStatus { diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index 430125fa1..a68d478c3 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -163,8 +163,6 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) { root["publish_time_sensor"] = settings.publish_time_sensor; root["mqtt_qos"] = settings.mqtt_qos; root["mqtt_retain"] = settings.mqtt_retain; - root["dallas_format"] = settings.dallas_format; - root["bool_format"] = settings.bool_format; root["ha_climate_format"] = settings.ha_climate_format; root["ha_enabled"] = settings.ha_enabled; root["nested_format"] = settings.nested_format; @@ -195,8 +193,6 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting newSettings.publish_time_other = root["publish_time_other"] | EMSESP_DEFAULT_PUBLISH_TIME; newSettings.publish_time_sensor = root["publish_time_sensor"] | EMSESP_DEFAULT_PUBLISH_TIME; - newSettings.dallas_format = root["dallas_format"] | EMSESP_DEFAULT_DALLAS_FORMAT; - newSettings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT; newSettings.ha_climate_format = root["ha_climate_format"] | EMSESP_DEFAULT_HA_CLIMATE_FORMAT; newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED; newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT; @@ -211,11 +207,6 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting changed = true; } - if (newSettings.dallas_format != settings.dallas_format) { - emsesp::EMSESP::mqtt_.dallas_format(newSettings.dallas_format); - changed = true; - } - if (newSettings.nested_format != settings.nested_format) { changed = true; } diff --git a/lib/framework/MqttSettingsService.h b/lib/framework/MqttSettingsService.h index eabeccc72..14d51c8b4 100644 --- a/lib/framework/MqttSettingsService.h +++ b/lib/framework/MqttSettingsService.h @@ -86,8 +86,6 @@ class MqttSettings { uint16_t publish_time_sensor; uint8_t mqtt_qos; bool mqtt_retain; - uint8_t dallas_format; - uint8_t bool_format; uint8_t ha_climate_format; bool ha_enabled; uint8_t nested_format; diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index d6913e319..e1a94119e 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -26,13 +26,15 @@ class DummySettings { bool shower_alert = false; bool hide_led = false; bool notoken_api = false; + uint8_t bool_format = 1; // on off + uint8_t enum_format = 1; + uint8_t dallas_format = 1; // MQTT uint16_t publish_time = 10; // seconds uint8_t mqtt_qos = 0; bool mqtt_retain = false; bool enabled = true; - uint8_t dallas_format = 1; uint8_t nested_format = 1; uint8_t ha_climate_format = 1; bool ha_enabled = true; @@ -56,7 +58,6 @@ class DummySettings { uint16_t publish_time_mixer = 10; uint16_t publish_time_other = 10; uint16_t publish_time_sensor = 10; - uint8_t bool_format = 1; // on off #define FACTORY_MQTT_MAX_TOPIC_LENGTH 128 diff --git a/mock-api/server.js b/mock-api/server.js index 7cda4f533..c77aad7ef 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -214,8 +214,6 @@ const mqtt_settings = { publish_time_sensor: 10, mqtt_qos: 0, mqtt_retain: false, - dallas_format: 1, - bool_format: 1, ha_climate_format: 1, ha_enabled: true, nested_format: 1, @@ -307,6 +305,9 @@ const emsesp_settings = { analog_enabled: false, pbutton_gpio: 0, board_profile: 'S32', + dallas_format: 1, + bool_format: 1, + enum_format: 1, } const emsesp_alldevices = { devices: [ diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index 1bade3efe..238d6b194 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -57,8 +57,9 @@ void DallasSensor::start() { // load the MQTT settings void DallasSensor::reload() { EMSESP::webSettingsService.read([&](WebSettings & settings) { - dallas_gpio_ = settings.dallas_gpio; - parasite_ = settings.dallas_parasite; + dallas_gpio_ = settings.dallas_gpio; + parasite_ = settings.dallas_parasite; + dallas_format_ = settings.dallas_format; }); if (Mqtt::ha_enabled()) { @@ -338,7 +339,7 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject dataSensor["temp"] = (float)(sensor.temperature_c) / 10; } } else { // show according to format - if (Mqtt::dallas_format() == Mqtt::Dallas_Format::SENSORID && Helpers::hasValue(sensor.temperature_c)) { + if (dallas_format_ == Dallas_Format::SENSORID && Helpers::hasValue(sensor.temperature_c)) { json[sensor.to_string()] = (float)(sensor.temperature_c) / 10; } else if (Helpers::hasValue(sensor.temperature_c)) { json[sensorID] = (float)(sensor.temperature_c) / 10; @@ -360,19 +361,15 @@ void DallasSensor::publish_values(const bool force) { DynamicJsonDocument doc(100 * num_sensors); uint8_t sensor_no = 1; - // dallas format is overriden when using Home Assistant - // uint8_t dallas_format = Mqtt::ha_enabled() ? Mqtt::Dallas_Format::NUMBER : Mqtt::dallas_format(); - uint8_t dallas_format = Mqtt::dallas_format(); - for (const auto & sensor : sensors_) { char sensorID[10]; // sensor{1-n} snprintf_P(sensorID, 10, PSTR("sensor%d"), sensor_no); - if (dallas_format == Mqtt::Dallas_Format::SENSORID) { + if (dallas_format_ == Dallas_Format::SENSORID) { // e.g. dallassensor_data = {"28-EA41-9497-0E03":23.3,"28-233D-9497-0C03":24.0} if (Helpers::hasValue(sensor.temperature_c)) { doc[sensor.to_string()] = (float)(sensor.temperature_c) / 10; } - } else if (dallas_format == Mqtt::Dallas_Format::NUMBER) { + } else if (dallas_format_ == Dallas_Format::NUMBER) { // e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03","temp":23.3},"sensor2":{"id":"28-233D-9497-0C03","temp":24.0}} JsonObject dataSensor = doc.createNestedObject(sensorID); dataSensor["id"] = sensor.to_string(); @@ -395,7 +392,7 @@ void DallasSensor::publish_values(const bool force) { config["unit_of_meas"] = FJSON("°C"); char str[50]; - if (dallas_format == Mqtt::Dallas_Format::SENSORID) { + if (dallas_format_ == Dallas_Format::SENSORID) { snprintf_P(str, sizeof(str), PSTR("{{value_json['%s']}}"), sensor.to_string().c_str()); } else { snprintf_P(str, sizeof(str), PSTR("{{value_json.sensor%d.temp}}"), sensor_no); @@ -403,7 +400,7 @@ void DallasSensor::publish_values(const bool force) { config["val_tpl"] = str; // name as sensor number not the long unique ID - if (dallas_format == Mqtt::Dallas_Format::SENSORID) { + if (dallas_format_ == Dallas_Format::SENSORID) { snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %s"), sensor.to_string().c_str()); } else { snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %d"), sensor_no); @@ -418,7 +415,7 @@ void DallasSensor::publish_values(const bool force) { ids.add("ems-esp"); char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - if (dallas_format == Mqtt::Dallas_Format::SENSORID) { + if (dallas_format_ == Dallas_Format::SENSORID) { // use '_' as HA doesn't like '-' in the topic name std::string topicname = sensor.to_string(); std::replace(topicname.begin(), topicname.end(), '-', '_'); diff --git a/src/dallassensor.h b/src/dallassensor.h index c5b75a926..cb6db2ddd 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -36,6 +36,8 @@ namespace emsesp { +enum Dallas_Format : uint8_t { SENSORID = 1, NUMBER, NAME }; + class DallasSensor { public: class Sensor { @@ -76,6 +78,14 @@ class DallasSensor { return (dallas_gpio_ != 0); } + uint8_t dallas_format() { + return dallas_format_; + } + + void dallas_format(uint8_t dallas_format) { + dallas_format_ = dallas_format; + } + private: static constexpr uint8_t MAX_SENSORS = 20; @@ -134,6 +144,7 @@ class DallasSensor { uint32_t sensorfails_ = 0; uint32_t sensorreads_ = 0; int8_t scanretry_ = 0; + uint8_t dallas_format_ = 0; }; } // namespace emsesp diff --git a/src/default_settings.h b/src/default_settings.h index 7f8dfd6ec..cd4fdfba0 100644 --- a/src/default_settings.h +++ b/src/default_settings.h @@ -84,6 +84,10 @@ #define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off #endif +#ifndef EMSESP_DEFAULT_ENUM_FORMAT +#define EMSESP_DEFAULT_ENUM_FORMAT 1 // Text +#endif + #ifndef EMSESP_DEFAULT_ANALOG_ENABLED #define EMSESP_DEFAULT_ANALOG_ENABLED false #endif diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index dc59a3399..b90930ade 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -697,7 +697,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t switch (dv.type) { case DeviceValueType::ENUM: { if (*(uint8_t *)(dv.value_p) < dv.options_size) { - if (Mqtt::bool_format() == BOOL_FORMAT_10) { + if (EMSESP::enum_format() == ENUM_FORMAT_NUMBER) { json[value] = (uint8_t)(*(uint8_t *)(dv.value_p)); } else { json[value] = dv.options[*(uint8_t *)(dv.value_p)]; // text @@ -770,7 +770,16 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t case DeviceValueType::BOOL: { if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { - json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false; + uint8_t bool_format = EMSESP::bool_format(); + if (bool_format == BOOL_FORMAT_ONOFF) { + json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(on) : F_(off); + } else if (bool_format == BOOL_FORMAT_ONOFF_CAP) { + json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(ON) : F_(OFF); + } else if (bool_format == BOOL_FORMAT_TRUEFALSE) { + json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false; + } else { + json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? 1 : 0; + } } json[type] = F("boolean"); break; @@ -858,24 +867,17 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { // see how to render the value depending on the setting // when in console mode we always use on and off - if ((Mqtt::bool_format() == BOOL_FORMAT_ONOFF) || console) { - // on or off as strings + uint8_t bool_format = EMSESP::bool_format(); + if ((bool_format == BOOL_FORMAT_ONOFF) || console) { json[name] = *(uint8_t *)(dv.value_p) ? F_(on) : F_(off); - has_value = true; - } else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) { - // on or off as strings + } else if (bool_format == BOOL_FORMAT_ONOFF_CAP) { json[name] = *(uint8_t *)(dv.value_p) ? F_(ON) : F_(OFF); - has_value = true; - } else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) { - // true or false values (as real booleans, not strings) + } else if (bool_format == BOOL_FORMAT_TRUEFALSE) { json[name] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false; - has_value = true; } else { - // numerical 1 or 0 json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0; - has_value = true; } - + has_value = true; } // handle TEXT strings @@ -888,7 +890,7 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter else if ((dv.type == DeviceValueType::ENUM) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) { if (*(uint8_t *)(dv.value_p) < dv.options_size) { // check for numeric enum-format, but "hamode" always as text - if ((Mqtt::bool_format() == BOOL_FORMAT_10) && (dv.short_name != FL_(hamode)[0])) { + if ((EMSESP::enum_format() == ENUM_FORMAT_NUMBER) && (dv.short_name != FL_(hamode)[0])) { json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)); } else { json[name] = dv.options[*(uint8_t *)(dv.value_p)]; diff --git a/src/emsesp.cpp b/src/emsesp.cpp index aa125ceec..9aa799ff1 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -75,6 +75,8 @@ uint8_t EMSESP::publish_all_idx_ = 0; uint8_t EMSESP::unique_id_count_ = 0; bool EMSESP::trace_raw_ = false; uint64_t EMSESP::tx_delay_ = 0; +uint8_t EMSESP::bool_format_ = 1; +uint8_t EMSESP::enum_format_ = 1; // for a specific EMS device go and request data values // or if device_id is 0 it will fetch from all our known and active devices diff --git a/src/emsesp.h b/src/emsesp.h index c262663ea..6f146417b 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -144,6 +144,22 @@ class EMSESP { return (dallassensor_.dallas_enabled()); } + static uint8_t bool_format() { + return bool_format_; + } + + static void bool_format(uint8_t format) { + bool_format_ = format; + } + + static uint8_t enum_format() { + return enum_format_; + } + + static void enum_format(uint8_t format) { + enum_format_ = format; + } + enum Watch : uint8_t { WATCH_OFF, WATCH_ON, WATCH_RAW, WATCH_UNKNOWN }; static void watch_id(uint16_t id); static uint16_t watch_id() { @@ -246,6 +262,8 @@ class EMSESP { static uint8_t unique_id_count_; static bool trace_raw_; static uint64_t tx_delay_; + static uint8_t bool_format_; + static uint8_t enum_format_; }; } // namespace emsesp diff --git a/src/helpers.cpp b/src/helpers.cpp index 1474fe9a3..38b3cc264 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -17,7 +17,7 @@ */ #include "helpers.h" -#include "mqtt.h" +#include "emsesp.h" namespace emsesp { @@ -124,7 +124,7 @@ char * Helpers::smallitoa(char * result, const uint16_t value) { // work out how to display booleans char * Helpers::render_boolean(char * result, bool value) { - uint8_t bool_format_ = Mqtt::bool_format(); + uint8_t bool_format_ = EMSESP::bool_format(); if (bool_format_ == BOOL_FORMAT_ONOFF) { strlcpy(result, value ? uuid::read_flash_string(F_(on)).c_str() : uuid::read_flash_string(F_(off)).c_str(), 5); } else if (bool_format_ == BOOL_FORMAT_ONOFF_CAP) { diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 3ab848e21..1ae9f60d7 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -35,8 +35,6 @@ uint32_t Mqtt::publish_time_mixer_; uint32_t Mqtt::publish_time_sensor_; uint32_t Mqtt::publish_time_other_; bool Mqtt::mqtt_enabled_; -uint8_t Mqtt::dallas_format_; -uint8_t Mqtt::bool_format_; uint8_t Mqtt::ha_climate_format_; bool Mqtt::ha_enabled_; uint8_t Mqtt::nested_format_; @@ -485,8 +483,6 @@ void Mqtt::load_settings() { mqtt_enabled_ = mqttSettings.enabled; ha_enabled_ = mqttSettings.ha_enabled; ha_climate_format_ = mqttSettings.ha_climate_format; - dallas_format_ = mqttSettings.dallas_format; - bool_format_ = mqttSettings.bool_format; nested_format_ = mqttSettings.nested_format; subscribe_format_ = mqttSettings.subscribe_format; diff --git a/src/mqtt.h b/src/mqtt.h index bea1e2f2a..9e7e5eb7e 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -44,8 +44,6 @@ using uuid::console::Shell; // size of queue #define MAX_MQTT_MESSAGES 200 -enum { BOOL_FORMAT_ONOFF = 1, BOOL_FORMAT_TRUEFALSE, BOOL_FORMAT_10, BOOL_FORMAT_ONOFF_CAP }; // matches Web UI settings - namespace emsesp { using mqtt_subfunction_p = std::function; @@ -83,7 +81,6 @@ class Mqtt { enum Operation { PUBLISH, SUBSCRIBE }; - enum Dallas_Format : uint8_t { SENSORID = 1, NUMBER }; enum HA_Climate_Format : uint8_t { CURRENT = 1, SETPOINT, ZERO }; static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // note this should really match the user setting in mqttSettings.maxTopicLength @@ -159,14 +156,6 @@ class Mqtt { return ha_climate_format_; } - static uint8_t dallas_format() { - return dallas_format_; - } - - static uint8_t bool_format() { - return bool_format_; - } - static uint8_t nested_format() { return nested_format_; } @@ -183,10 +172,6 @@ class Mqtt { ha_climate_format_ = ha_climate_format; } - static void dallas_format(uint8_t dallas_format) { - dallas_format_ = dallas_format; - } - static void ha_enabled(bool ha_enabled) { ha_enabled_ = ha_enabled; } @@ -279,8 +264,6 @@ class Mqtt { static uint32_t publish_time_other_; static uint32_t publish_time_sensor_; static bool mqtt_enabled_; - static uint8_t dallas_format_; - static uint8_t bool_format_; static uint8_t ha_climate_format_; static bool ha_enabled_; static uint8_t nested_format_; diff --git a/src/system.cpp b/src/system.cpp index 43c432303..d3d462d4e 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -835,8 +835,6 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject & 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["dallas_format"] = settings.dallas_format; - node["bool_format"] = settings.bool_format; node["ha_climate_format"] = settings.ha_climate_format; node["ha_enabled"] = settings.ha_enabled; node["mqtt_qos"] = settings.mqtt_qos; @@ -879,6 +877,9 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject & node["led_gpio"] = settings.led_gpio; node["hide_led"] = settings.hide_led; node["notoken_api"] = settings.notoken_api; + node["dallas_format"] = settings.dallas_format; + node["bool_format"] = settings.bool_format; + node["enum_format"] = settings.enum_format; node["analog_enabled"] = settings.analog_enabled; node["pbutton_gpio"] = settings.pbutton_gpio; node["board_profile"] = settings.board_profile; diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 6d96b781d..d5e0182e6 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -61,6 +61,9 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) { root["pbutton_gpio"] = settings.pbutton_gpio; root["solar_maxflow"] = settings.solar_maxflow; root["board_profile"] = settings.board_profile; + root["dallas_format"] = settings.dallas_format; + root["bool_format"] = settings.bool_format; + root["enum_format"] = settings.enum_format; } // call on initialization and also when settings are updated via web or console @@ -173,6 +176,15 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API; settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW; + settings.dallas_format = root["dallas_format"] | EMSESP_DEFAULT_DALLAS_FORMAT; + EMSESP::dallassensor_.dallas_format(settings.dallas_format); + + settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT; + EMSESP::bool_format(settings.bool_format); + + settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT; + EMSESP::enum_format(settings.enum_format); + return StateUpdateResult::CHANGED; } diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h index 8a4a09418..6e673a5f9 100644 --- a/src/web/WebSettingsService.h +++ b/src/web/WebSettingsService.h @@ -30,6 +30,9 @@ namespace emsesp { +enum { BOOL_FORMAT_ONOFF = 1, BOOL_FORMAT_ONOFF_CAP, BOOL_FORMAT_TRUEFALSE, BOOL_FORMAT_10 }; // matches Web UI settings +enum { ENUM_FORMAT_TEXT = 1, ENUM_FORMAT_NUMBER }; // matches Web UI settings + class WebSettings { public: uint8_t tx_mode; @@ -56,6 +59,9 @@ class WebSettings { uint8_t pbutton_gpio; uint8_t solar_maxflow; String board_profile; + uint8_t dallas_format; + uint8_t bool_format; + uint8_t enum_format; static void read(WebSettings & settings, JsonObject & root); static StateUpdateResult update(JsonObject & root, WebSettings & settings); From 0f48d3e72cae80d49cf30f8c2ede107fcb8f2404 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 16 Jul 2021 10:33:12 +0200 Subject: [PATCH 048/122] add `sensorname` console command --- CHANGELOG_LATEST.md | 1 + interface/src/project/EMSESPSettingsForm.tsx | 1 + lib/framework/StatefulService.h | 2 +- lib_standalone/StatefulService.h | 2 +- src/console.cpp | 42 +++++++++ src/dallassensor.cpp | 91 ++++++++++++++++---- src/dallassensor.h | 3 + src/default_settings.h | 4 + src/helpers.cpp | 9 +- src/locale_EN.h | 2 + src/version.h | 2 +- src/web/WebSettingsService.cpp | 38 ++++++-- src/web/WebSettingsService.h | 8 ++ 13 files changed, 177 insertions(+), 28 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 700ea295e..3534e34c7 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -8,6 +8,7 @@ - optional low clockrate (160 MHz) (#83) - selectbox for enumerated values in web - settings for water hysteresis on/off +- sensorname console-command, replace sensorid with a unique name ## Fixed diff --git a/interface/src/project/EMSESPSettingsForm.tsx b/interface/src/project/EMSESPSettingsForm.tsx index 4bdd3c620..ebbdb43ae 100644 --- a/interface/src/project/EMSESPSettingsForm.tsx +++ b/interface/src/project/EMSESPSettingsForm.tsx @@ -507,6 +507,7 @@ class EMSESPSettingsForm extends Component { > by Sensor ID by Number + by Name diff --git a/lib/framework/StatefulService.h b/lib/framework/StatefulService.h index 541488c62..0a6d4725e 100644 --- a/lib/framework/StatefulService.h +++ b/lib/framework/StatefulService.h @@ -10,7 +10,7 @@ #include #ifndef DEFAULT_BUFFER_SIZE -#define DEFAULT_BUFFER_SIZE 1024 +#define DEFAULT_BUFFER_SIZE 2048 #endif enum class StateUpdateResult { diff --git a/lib_standalone/StatefulService.h b/lib_standalone/StatefulService.h index 41db7d3d5..027cf911f 100644 --- a/lib_standalone/StatefulService.h +++ b/lib_standalone/StatefulService.h @@ -8,7 +8,7 @@ #include #ifndef DEFAULT_BUFFER_SIZE -#define DEFAULT_BUFFER_SIZE 1024 +#define DEFAULT_BUFFER_SIZE 2048 #endif enum class StateUpdateResult { diff --git a/src/console.cpp b/src/console.cpp index 06a0f3a53..76183aef6 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -711,6 +711,48 @@ void Console::load_system_commands(unsigned int context) { }); }); + EMSESPShell::commands + ->add_command(context, + CommandFlags::ADMIN, + flash_string_vector{F_(sensorname)}, + flash_string_vector{F_(sensorid_optional), F_(name_optional), F_(offset_optional)}, + [](Shell & shell, const std::vector & arguments) { + if (arguments.size() == 0) { + EMSESP::webSettingsService.read([&](WebSettings & settings) { + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (!settings.sensor[i].id.isEmpty()) { + shell.print(settings.sensor[i].id); + shell.print(" : "); + shell.print(settings.sensor[i].name); + shell.print(" : "); + char buf[10]; + shell.println(Helpers::render_value(buf, settings.sensor[i].offset, 10)); + } + } + }); + return; + } + if (arguments.size() == 1) { + EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", 0); + // shell.println(EMSESP::dallassensor_.get_name(arguments.front().c_str())); + return; + } + int16_t offset = 0; + float val; + if (arguments.size() == 2) { + if (Helpers::value2float(arguments.back().c_str(), val)) { + offset = (10 * val); + EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", offset); + return; + } + } else if (arguments.size() == 3) { + if (Helpers::value2float(arguments.back().c_str(), val)) { + offset = (10 * val); + } + } + EMSESP::dallassensor_.add_name(arguments.front().c_str(), arguments[1].c_str(), offset); + }); + EMSESPShell::commands ->add_command(context, CommandFlags::ADMIN, diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index 238d6b194..ba3192c2d 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -294,7 +294,7 @@ uint64_t DallasSensor::Sensor::id() const { return id_; } -std::string DallasSensor::Sensor::to_string() const { +std::string DallasSensor::Sensor::id_string() const { std::string str(20, '\0'); snprintf_P(&str[0], str.capacity() + 1, @@ -306,6 +306,70 @@ std::string DallasSensor::Sensor::to_string() const { return str; } +std::string DallasSensor::Sensor::to_string() const { + std::string str = id_string(); + EMSESP::webSettingsService.read([&](WebSettings & settings) { + if (settings.dallas_format == Dallas_Format::NAME) { + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { + str = settings.sensor[i].name.c_str(); + } + } + } + }); + + return str; +} + +void DallasSensor::add_name(const char * id, const char * name, int16_t offset) { + EMSESP::webSettingsService.update([&](WebSettings & settings) { + // check for new name of stored id + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (strcmp(id, settings.sensor[i].id.c_str()) == 0) { + if (strlen(name) == 0 && offset == 0) { // delete entry if name and offset is empty + settings.sensor[i].id = ""; + settings.sensor[i].name = ""; + settings.sensor[i].offset = 0; + LOG_INFO(F("Deleting entry of sensor %s"), id); + } else { + settings.sensor[i].name = (strlen(name) == 0) ? id : name; + settings.sensor[i].offset = offset; + LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + } + return StateUpdateResult::CHANGED; + } + } + // check for free place + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (settings.sensor[i].id.isEmpty()) { + settings.sensor[i].id = id; + settings.sensor[i].name = (strlen(name) == 0) ? id : name; + settings.sensor[i].offset = offset; + LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + return StateUpdateResult::CHANGED; + } + } + // check if there is a unused id and overwrite it + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + bool found = false; + for (const auto & sensor : sensors_) { + if (strcmp(sensor.id_string().c_str(), settings.sensor[i].id.c_str()) == 0) { + found = true; + } + } + if (!found) { + settings.sensor[i].id = id; + settings.sensor[i].name = (strlen(name) == 0) ? id : name; + settings.sensor[i].offset = offset; + LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + return StateUpdateResult::CHANGED; + } + } + LOG_ERROR(F("List full, remove one sensorname first")); + return StateUpdateResult::UNCHANGED; + }, "local"); +} + // check to see if values have been updated bool DallasSensor::updated_values() { if (changed_) { @@ -339,10 +403,10 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject dataSensor["temp"] = (float)(sensor.temperature_c) / 10; } } else { // show according to format - if (dallas_format_ == Dallas_Format::SENSORID && Helpers::hasValue(sensor.temperature_c)) { - json[sensor.to_string()] = (float)(sensor.temperature_c) / 10; - } else if (Helpers::hasValue(sensor.temperature_c)) { + if (dallas_format_ == Dallas_Format::NUMBER && Helpers::hasValue(sensor.temperature_c)) { json[sensorID] = (float)(sensor.temperature_c) / 10; + } else if (Helpers::hasValue(sensor.temperature_c)) { + json[sensor.to_string()] = (float)(sensor.temperature_c) / 10; } } } @@ -364,18 +428,15 @@ void DallasSensor::publish_values(const bool force) { for (const auto & sensor : sensors_) { char sensorID[10]; // sensor{1-n} snprintf_P(sensorID, 10, PSTR("sensor%d"), sensor_no); - if (dallas_format_ == Dallas_Format::SENSORID) { - // e.g. dallassensor_data = {"28-EA41-9497-0E03":23.3,"28-233D-9497-0C03":24.0} - if (Helpers::hasValue(sensor.temperature_c)) { - doc[sensor.to_string()] = (float)(sensor.temperature_c) / 10; - } - } else if (dallas_format_ == Dallas_Format::NUMBER) { + if (dallas_format_ == Dallas_Format::NUMBER) { // e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03","temp":23.3},"sensor2":{"id":"28-233D-9497-0C03","temp":24.0}} JsonObject dataSensor = doc.createNestedObject(sensorID); dataSensor["id"] = sensor.to_string(); if (Helpers::hasValue(sensor.temperature_c)) { dataSensor["temp"] = (float)(sensor.temperature_c) / 10; } + } else if (Helpers::hasValue(sensor.temperature_c)) { + doc[sensor.to_string()] = (float)(sensor.temperature_c) / 10; } // create the HA MQTT config @@ -392,7 +453,7 @@ void DallasSensor::publish_values(const bool force) { config["unit_of_meas"] = FJSON("°C"); char str[50]; - if (dallas_format_ == Dallas_Format::SENSORID) { + if (dallas_format_ != Dallas_Format::NUMBER) { snprintf_P(str, sizeof(str), PSTR("{{value_json['%s']}}"), sensor.to_string().c_str()); } else { snprintf_P(str, sizeof(str), PSTR("{{value_json.sensor%d.temp}}"), sensor_no); @@ -400,7 +461,7 @@ void DallasSensor::publish_values(const bool force) { config["val_tpl"] = str; // name as sensor number not the long unique ID - if (dallas_format_ == Dallas_Format::SENSORID) { + if (dallas_format_ != Dallas_Format::NUMBER) { snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %s"), sensor.to_string().c_str()); } else { snprintf_P(str, sizeof(str), PSTR("Dallas Sensor %d"), sensor_no); @@ -415,13 +476,13 @@ void DallasSensor::publish_values(const bool force) { ids.add("ems-esp"); char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; - if (dallas_format_ == Dallas_Format::SENSORID) { + if (dallas_format_ == Dallas_Format::NUMBER) { + snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%d/config"), Mqtt::base().c_str(), sensor_no); + } else { // use '_' as HA doesn't like '-' in the topic name std::string topicname = sensor.to_string(); std::replace(topicname.begin(), topicname.end(), '-', '_'); snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%s/config"), Mqtt::base().c_str(), topicname.c_str()); - } else { - snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%d/config"), Mqtt::base().c_str(), sensor_no); } Mqtt::publish_ha(topic, config.as()); diff --git a/src/dallassensor.h b/src/dallassensor.h index cb6db2ddd..9dd674973 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -46,6 +46,7 @@ class DallasSensor { ~Sensor() = default; uint64_t id() const; + std::string id_string() const; std::string to_string() const; int16_t temperature_c = EMS_VALUE_SHORT_NOTSET; @@ -86,6 +87,8 @@ class DallasSensor { dallas_format_ = dallas_format; } + void add_name(const char * id, const char * name, int16_t offset); + private: static constexpr uint8_t MAX_SENSORS = 20; diff --git a/src/default_settings.h b/src/default_settings.h index cd4fdfba0..a18142b35 100644 --- a/src/default_settings.h +++ b/src/default_settings.h @@ -160,4 +160,8 @@ #define EMSESP_DEFAULT_SOLAR_MAXFLOW 30 #endif +#ifndef EMSESP_DEFAULT_SENSOR_NAME +#define EMSESP_DEFAULT_SENSOR_NAME "" +#endif + #endif diff --git a/src/helpers.cpp b/src/helpers.cpp index 38b3cc264..6504f04df 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -417,12 +417,15 @@ bool Helpers::value2number(const char * v, int & value) { // checks if we can convert a char string to a float value bool Helpers::value2float(const char * v, float & value) { + value = 0; if ((v == nullptr) || (strlen(v) == 0)) { - value = 0; return false; } - value = atof((char *)v); - return true; + if (v[0] == '-' || v[0] == '.' || (v[0] >= '0' && v[0] <= '9')) { + value = atof((char *)v); + return true; + } + return false; } // https://stackoverflow.com/questions/313970/how-to-convert-stdstring-to-lower-case diff --git a/src/locale_EN.h b/src/locale_EN.h index 592f74cbe..2671619c4 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -71,6 +71,7 @@ MAKE_PSTR_WORD(pin) MAKE_PSTR_WORD(publish) MAKE_PSTR_WORD(timeout) MAKE_PSTR_WORD(board_profile) +MAKE_PSTR_WORD(sensorname) // for commands MAKE_PSTR_WORD(call) @@ -124,6 +125,7 @@ MAKE_PSTR(invalid_watch, "Invalid watch type") MAKE_PSTR(data_mandatory, "\"XX XX ...\"") MAKE_PSTR(asterisks, "********") MAKE_PSTR(n_mandatory, "") +MAKE_PSTR(sensorid_optional, "[sensor ID]") MAKE_PSTR(id_optional, "[id|hc]") MAKE_PSTR(data_optional, "[data]") MAKE_PSTR(offset_optional, "[offset]") diff --git a/src/version.h b/src/version.h index 4c5afedb2..f2ab76dcf 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.2b3" +#define EMSESP_APP_VERSION "3.1.2b4" diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index d5e0182e6..0d0099cac 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -64,13 +64,25 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) { root["dallas_format"] = settings.dallas_format; root["bool_format"] = settings.bool_format; root["enum_format"] = settings.enum_format; + + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + char buf[20]; + snprintf_P(buf, sizeof(buf), PSTR("sensor_id%d"), i); + root[buf] = settings.sensor[i].id; + snprintf_P(buf, sizeof(buf), PSTR("sensor_name%d"), i); + root[buf] = settings.sensor[i].name; + snprintf_P(buf, sizeof(buf), PSTR("sensor_offset%d"), i); + root[buf] = settings.sensor[i].offset; + } } // call on initialization and also when settings are updated via web or console StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) { // load default GPIO configuration based on board profile std::vector data; // led, dallas, rx, tx, button - settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE; + + String old_board_profile = settings.board_profile; + settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE; if (!System::load_board_profile(data, settings.board_profile.c_str())) { settings.board_profile = EMSESP_DEFAULT_BOARD_PROFILE; // invalid board configuration, override the default in case it has been misspelled } @@ -81,13 +93,15 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) uint8_t default_tx_gpio = data[3]; uint8_t default_pbutton_gpio = data[4]; - EMSESP::logger().info(F("EMS-ESP version %s"), EMSESP_APP_VERSION); + if (old_board_profile != settings.board_profile) { + EMSESP::logger().info(F("EMS-ESP version %s"), EMSESP_APP_VERSION); - // check to see if we have a settings file, if not it's a fresh install - if (!root.size()) { - EMSESP::logger().info(F("Initializing configuration with board profile %s"), settings.board_profile.c_str()); - } else { - EMSESP::logger().info(F("Using configuration from board profile %s"), settings.board_profile.c_str()); + // check to see if we have a settings file, if not it's a fresh install + if (!root.size()) { + EMSESP::logger().info(F("Initializing configuration with board profile %s"), settings.board_profile.c_str()); + } else { + EMSESP::logger().info(F("Using configuration from board profile %s"), settings.board_profile.c_str()); + } } int prev; @@ -185,6 +199,16 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT; EMSESP::enum_format(settings.enum_format); + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + char buf[20]; + snprintf_P(buf, sizeof(buf), PSTR("sensor_id%d"), i); + settings.sensor[i].id = root[buf] | EMSESP_DEFAULT_SENSOR_NAME; + snprintf_P(buf, sizeof(buf), PSTR("sensor_name%d"), i); + settings.sensor[i].name = root[buf] | EMSESP_DEFAULT_SENSOR_NAME; + snprintf_P(buf, sizeof(buf), PSTR("sensor_offset%d"), i); + settings.sensor[i].offset = root[buf] | 0; + } + return StateUpdateResult::CHANGED; } diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h index 6e673a5f9..f35c54079 100644 --- a/src/web/WebSettingsService.h +++ b/src/web/WebSettingsService.h @@ -28,6 +28,8 @@ #define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings" #define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile" +#define NUM_SENSOR_NAMES 10 + namespace emsesp { enum { BOOL_FORMAT_ONOFF = 1, BOOL_FORMAT_ONOFF_CAP, BOOL_FORMAT_TRUEFALSE, BOOL_FORMAT_10 }; // matches Web UI settings @@ -63,6 +65,12 @@ class WebSettings { uint8_t bool_format; uint8_t enum_format; + struct { + String id; + String name; + int16_t offset; + } sensor[NUM_SENSOR_NAMES]; + static void read(WebSettings & settings, JsonObject & root); static StateUpdateResult update(JsonObject & root, WebSettings & settings); From 7dec674452ad60b1e7797694dec18d054fa98706 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 16 Jul 2021 16:24:03 +0200 Subject: [PATCH 049/122] moving dallas-setting, cleanup --- lib/framework/MqttSettingsService.cpp | 2 -- src/test/test.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/framework/MqttSettingsService.cpp b/lib/framework/MqttSettingsService.cpp index a68d478c3..04efccd7b 100644 --- a/lib/framework/MqttSettingsService.cpp +++ b/lib/framework/MqttSettingsService.cpp @@ -138,8 +138,6 @@ void MqttSettingsService::configureMqtt() { _mqttClient.setMaxTopicLength(_state.maxTopicLength); _mqttClient.connect(); } - - emsesp::EMSESP::dallassensor_.reload(); // added by Proddy for EMS-ESP } void MqttSettings::read(MqttSettings & settings, JsonObject & root) { diff --git a/src/test/test.cpp b/src/test/test.cpp index 7e58999c1..94e8bc111 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -194,7 +194,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { // init stuff Mqtt::ha_enabled(true); - Mqtt::dallas_format(1); + EMSESP::dallassensor_.dallas_format(1); Mqtt::ha_climate_format(1); EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS); EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw From 64e15542a2a77478913ba562fce67f620f142b22 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 16 Jul 2021 16:25:20 +0200 Subject: [PATCH 050/122] show api type of commands as `command` instead of `unknown` --- src/emsdevice.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index b90930ade..99bfe7b30 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -801,6 +801,10 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t json[type] = F_(text); break; + case DeviceValueType::CMD: + json[type] = F_(command); + break; + default: json[type] = F_(unknown); break; From 046a9ef6f20f88a44fe9e2cdc3fec971dff1f68a Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 16 Jul 2021 16:26:58 +0200 Subject: [PATCH 051/122] show dallas-temperatures always with digit on web --- interface/src/project/EMSESPDevicesForm.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/interface/src/project/EMSESPDevicesForm.tsx b/interface/src/project/EMSESPDevicesForm.tsx index b9bcb422b..9db3de44a 100644 --- a/interface/src/project/EMSESPDevicesForm.tsx +++ b/interface/src/project/EMSESPDevicesForm.tsx @@ -118,7 +118,7 @@ export const formatDuration = (duration_min: number) => { const pluralize = (count: number, noun: string, suffix = 's') => ` ${count} ${noun}${count !== 1 ? suffix : ''} `; -function formatValue(value: any, uom: number) { +function formatValue(value: any, uom: number, digit: number) { switch (uom) { case DeviceValueUOM.HOURS: return value ? formatDuration(value * 60) : '0 hours'; @@ -131,6 +131,14 @@ function formatValue(value: any, uom: number) { return new Intl.NumberFormat().format(value); case DeviceValueUOM.BOOLEAN: return value ? 'on' : 'off'; + case DeviceValueUOM.DEGREES: + return ( + new Intl.NumberFormat(undefined, { + minimumFractionDigits: digit + }).format(value) + + ' ' + + DeviceValueUOM_s[uom] + ); default: return ( new Intl.NumberFormat().format(value) + ' ' + DeviceValueUOM_s[uom] @@ -313,7 +321,7 @@ class EMSESPDevicesForm extends Component< {sensorData.id} - {formatValue(sensorData.temp, DeviceValueUOM.DEGREES)} + {formatValue(sensorData.temp, DeviceValueUOM.DEGREES, 1)} ))} @@ -476,7 +484,7 @@ class EMSESPDevicesForm extends Component< {item.n} - {formatValue(item.v, item.u)} + {formatValue(item.v, item.u, 0)} ))} From f8579f7c968b82d46db5cb4eeacc3386e908a2ee Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 16 Jul 2021 16:45:38 +0200 Subject: [PATCH 052/122] formatting --- src/dallassensor.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/dallassensor.h b/src/dallassensor.h index 9dd674973..092eded07 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -139,14 +139,14 @@ class DallasSensor { bool registered_ha_[MAX_SENSORS]; - int8_t scancnt_ = SCAN_START; - uint8_t firstscan_ = 0; - uint8_t dallas_gpio_ = 0; - bool parasite_ = false; - bool changed_ = false; - uint32_t sensorfails_ = 0; - uint32_t sensorreads_ = 0; - int8_t scanretry_ = 0; + int8_t scancnt_ = SCAN_START; + uint8_t firstscan_ = 0; + uint8_t dallas_gpio_ = 0; + bool parasite_ = false; + bool changed_ = false; + uint32_t sensorfails_ = 0; + uint32_t sensorreads_ = 0; + int8_t scanretry_ = 0; uint8_t dallas_format_ = 0; }; From d7bc821bbe3b5e8ad63b9aea848473dfaa1e1808 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sat, 17 Jul 2021 06:33:14 +0200 Subject: [PATCH 053/122] calculate dallassensor offset --- src/dallassensor.cpp | 22 ++++++++++++++++++++-- src/dallassensor.h | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index ba3192c2d..b23eb898f 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -140,7 +140,11 @@ void DallasSensor::loop() { bool found = false; for (auto & sensor : sensors_) { if (sensor.id() == get_id(addr)) { - changed_ |= (t != sensor.temperature_c); + t += sensor.offset(); + if (t != sensor.temperature_c) { + sensor.temperature_c = t; + changed_ |= true; + } sensor.temperature_c = t; sensor.read = true; found = true; @@ -150,7 +154,7 @@ void DallasSensor::loop() { // add new sensor if (!found && (sensors_.size() < (MAX_SENSORS - 1))) { sensors_.emplace_back(addr); - sensors_.back().temperature_c = t; + sensors_.back().temperature_c = t + sensors_.back().offset(); sensors_.back().read = true; changed_ = true; } @@ -321,6 +325,20 @@ std::string DallasSensor::Sensor::to_string() const { return str; } +int16_t DallasSensor::Sensor::offset() const { + std::string str = id_string(); + int16_t offset = 0; + EMSESP::webSettingsService.read([&](WebSettings & settings) { + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { + offset = settings.sensor[i].offset; + } + } + }); + + return offset; +} + void DallasSensor::add_name(const char * id, const char * name, int16_t offset) { EMSESP::webSettingsService.update([&](WebSettings & settings) { // check for new name of stored id diff --git a/src/dallassensor.h b/src/dallassensor.h index 092eded07..ec410d0e8 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -48,6 +48,7 @@ class DallasSensor { uint64_t id() const; std::string id_string() const; std::string to_string() const; + int16_t offset() const; int16_t temperature_c = EMS_VALUE_SHORT_NOTSET; bool read = false; From ae1e2eccd23f2a40957cc5db1129e67831129285 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 18 Jul 2021 20:40:51 +0200 Subject: [PATCH 054/122] minExtTemp for RC300 --- src/devices/thermostat.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index b57d9bc69..3a8d1de30 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -2173,6 +2173,13 @@ void Thermostat::register_device_values() { FL_(ibaBuildingType), DeviceValueUOM::LIST, MAKE_CF_CB(set_building)); + register_device_value(TAG_THERMOSTAT_DATA, + &ibaMinExtTemperature_, + DeviceValueType::INT, + nullptr, + FL_(ibaMinExtTemperature), + DeviceValueUOM::DEGREES, + MAKE_CF_CB(set_minexttemp)); register_device_value(TAG_THERMOSTAT_DATA, &wwSetTemp_, DeviceValueType::UINT, nullptr, FL_(wwSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwtemp)); register_device_value(TAG_THERMOSTAT_DATA, &wwMode_, DeviceValueType::ENUM, FL_(enum_wwMode), FL_(wwMode), DeviceValueUOM::LIST, MAKE_CF_CB(set_wwmode)); register_device_value( From 4f6d5164a42440cd65c91db3e7f187a1c90f1bb0 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Sun, 18 Jul 2021 20:42:02 +0200 Subject: [PATCH 055/122] Set sensornames from web --- interface/src/project/EMSESPDevicesForm.tsx | 131 +++++++++++++++++++- interface/src/project/EMSESPtypes.ts | 7 +- interface/src/project/SensorForm.tsx | 92 ++++++++++++++ mock-api/server.js | 1 + src/dallassensor.cpp | 27 +++- src/dallassensor.h | 5 +- src/system.h | 9 ++ src/web/WebDevicesService.cpp | 43 ++++++- src/web/WebDevicesService.h | 4 +- 9 files changed, 302 insertions(+), 17 deletions(-) create mode 100644 interface/src/project/SensorForm.tsx diff --git a/interface/src/project/EMSESPDevicesForm.tsx b/interface/src/project/EMSESPDevicesForm.tsx index 9db3de44a..b0d6ca421 100644 --- a/interface/src/project/EMSESPDevicesForm.tsx +++ b/interface/src/project/EMSESPDevicesForm.tsx @@ -45,16 +45,19 @@ import { Device, DeviceValue, DeviceValueUOM, - DeviceValueUOM_s + DeviceValueUOM_s, + Sensor } from './EMSESPtypes'; import ValueForm from './ValueForm'; +import SensorForm from './SensorForm'; import { ENDPOINT_ROOT } from '../api'; export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + 'scanDevices'; export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + 'deviceData'; export const WRITE_VALUE_ENDPOINT = ENDPOINT_ROOT + 'writeValue'; +export const WRITE_SENSOR_ENDPOINT = ENDPOINT_ROOT + 'writeSensor'; const StyledTableCell = withStyles((theme: Theme) => createStyles({ @@ -94,6 +97,7 @@ interface EMSESPDevicesFormState { deviceData?: EMSESPDeviceData; selectedDevice?: number; edit_devicevalue?: DeviceValue; + edit_sensorname?: Sensor; } type EMSESPDevicesFormProps = RestFormProps & @@ -215,6 +219,65 @@ class EMSESPDevicesForm extends Component< this.setState({ edit_devicevalue: dv }); }; + handleSensorChange = (name: keyof Sensor) => ( + event: React.ChangeEvent + ) => { + this.setState({ + edit_sensorname: { + ...this.state.edit_sensorname!, + [name]: extractEventValue(event) + } + }); + }; + + cancelEditingSensor = () => { + this.setState({ edit_sensorname: undefined }); + }; + + doneEditingSensor = () => { + const { edit_sensorname } = this.state; + + redirectingAuthorizedFetch(WRITE_SENSOR_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ + sensorname: edit_sensorname + }), + headers: { + 'Content-Type': 'application/json' + } + }) + .then((response) => { + if (response.status === 200) { + this.props.enqueueSnackbar('Sensorname set', { + variant: 'success' + }); + } else if (response.status === 204) { + this.props.enqueueSnackbar('Sensorname not set', { + variant: 'error' + }); + } else if (response.status === 403) { + this.props.enqueueSnackbar('Write access denied', { + variant: 'error' + }); + } else { + throw Error('Unexpected response code: ' + response.status); + } + }) + .catch((error) => { + this.props.enqueueSnackbar(error.message || 'Problem writing value', { + variant: 'error' + }); + }); + + if (edit_sensorname) { + this.setState({ edit_sensorname: undefined }); + } + }; + + sendSensor = (sn: Sensor) => { + this.setState({ edit_sensorname: sn }); + }; + noDevices = () => { return this.props.data.devices.length === 0; }; @@ -298,28 +361,47 @@ class EMSESPDevicesForm extends Component< renderSensorItems() { const { data } = this.props; + const me = this.props.authenticatedContext.me; return (

- Dallas Sensors + Sensors {!this.noSensors() && ( + Sensor # - ID + ID / Name Temperature {data.sensors.map((sensorData) => ( - + + + {me.admin && ( + + this.sendSensor(sensorData)} + > + + + + )} + {sensorData.no} - {sensorData.id} + {sensorData.id} {formatValue(sensorData.temp, DeviceValueUOM.DEGREES, 1)} @@ -339,6 +421,34 @@ class EMSESPDevicesForm extends Component< ); } + renderAnalog() { + const { data } = this.props; + return ( + + {data.analog > 0 && ( +
+ + + Sensortype + Value + + + + + + Analog Input + + + {formatValue(data.analog, DeviceValueUOM.MV, 0)} + + + +
+ )} +
+ ); + } + renderScanDevicesDialog() { return (

{this.renderDeviceItems()} {this.renderDeviceData()} {this.renderSensorItems()} + {this.renderAnalog()}

@@ -542,6 +653,14 @@ class EMSESPDevicesForm extends Component< handleValueChange={this.handleValueChange} /> )} + {edit_sensorname && ( + + )} ); } diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts index f6b9bbf7a..18e306f27 100644 --- a/interface/src/project/EMSESPtypes.ts +++ b/interface/src/project/EMSESPtypes.ts @@ -60,6 +60,7 @@ export interface Sensor { export interface EMSESPDevices { devices: Device[]; sensors: Sensor[]; + analog: number; } export interface DeviceValue { @@ -93,7 +94,8 @@ export enum DeviceValueUOM { DBM, NUM, BOOLEAN, - LIST + LIST, + MV } export const DeviceValueUOM_s = [ @@ -114,5 +116,6 @@ export const DeviceValueUOM_s = [ 'dBm', 'number', 'on/off', - '' + '', + 'mV' ]; diff --git a/interface/src/project/SensorForm.tsx b/interface/src/project/SensorForm.tsx new file mode 100644 index 000000000..fa6ee246d --- /dev/null +++ b/interface/src/project/SensorForm.tsx @@ -0,0 +1,92 @@ +import React, { RefObject } from 'react'; +import { ValidatorForm } from 'react-material-ui-form-validator'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + FormHelperText, + OutlinedInput +} from '@material-ui/core'; + +import { FormButton } from '../components'; +import { Sensor } from './EMSESPtypes'; + +interface SensorFormProps { + sensor: Sensor; + onDoneEditing: () => void; + onCancelEditing: () => void; + handleSensorChange: ( + data: keyof Sensor + ) => (event: React.ChangeEvent) => void; +} + +class SensorForm extends React.Component { + formRef: RefObject = React.createRef(); + + submit = () => { + this.formRef.current.submit(); + }; + + render() { + const { + sensor, + handleSensorChange, + onDoneEditing, + onCancelEditing + } = this.props; + + return ( + + + + Change Sensor Name + + + + Name of sensor #{sensor.no} + + + + (optional 'offset' separated by space) + + + + + Cancel + + + Done + + + + + ); + } +} + +export default SensorForm; diff --git a/mock-api/server.js b/mock-api/server.js index c77aad7ef..947a24e04 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -282,6 +282,7 @@ const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData' const EMSESP_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'emsespStatus' const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile' const WRITE_VALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeValue' ++const WRITE_SENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeSensor' const emsesp_settings = { tx_mode: 1, tx_delay: 0, diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index b23eb898f..ae78d39a5 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -310,10 +310,10 @@ std::string DallasSensor::Sensor::id_string() const { return str; } -std::string DallasSensor::Sensor::to_string() const { +std::string DallasSensor::Sensor::to_string(const bool name) const { std::string str = id_string(); EMSESP::webSettingsService.read([&](WebSettings & settings) { - if (settings.dallas_format == Dallas_Format::NAME) { + if (settings.dallas_format == Dallas_Format::NAME || name) { for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { str = settings.sensor[i].name.c_str(); @@ -339,7 +339,24 @@ int16_t DallasSensor::Sensor::offset() const { return offset; } -void DallasSensor::add_name(const char * id, const char * name, int16_t offset) { +bool DallasSensor::add_name(const char * idstr, const char * name, int16_t offset) { + bool ok = false; + char id[20]; + strlcpy(id, idstr, sizeof(id)); + + // check for number and convert to id + if (strlen(id) > 0 && strlen(id) <= 2 && id[0] >= '1' && id[0] <= '9') { + uint8_t no = atoi(idstr) - 1; + if (no < sensors_.size()) { + strlcpy(id, sensors_[no].id_string().c_str(), sizeof(id)); + } + } + // check valid id + if (strlen(id) != 17 || id[2] != '-' || id[7] != '-' || id[12] !='-') { + LOG_WARNING(F("Invalid sensor id: %s"), id); + return ok; + } + EMSESP::webSettingsService.update([&](WebSettings & settings) { // check for new name of stored id for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { @@ -354,6 +371,7 @@ void DallasSensor::add_name(const char * id, const char * name, int16_t offset) settings.sensor[i].offset = offset; LOG_INFO(F("Setting name of sensor %s to %s"), id, name); } + ok = true; return StateUpdateResult::CHANGED; } } @@ -364,6 +382,7 @@ void DallasSensor::add_name(const char * id, const char * name, int16_t offset) settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + ok = true; return StateUpdateResult::CHANGED; } } @@ -380,12 +399,14 @@ void DallasSensor::add_name(const char * id, const char * name, int16_t offset) settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + ok = true; return StateUpdateResult::CHANGED; } } LOG_ERROR(F("List full, remove one sensorname first")); return StateUpdateResult::UNCHANGED; }, "local"); + return ok; } // check to see if values have been updated diff --git a/src/dallassensor.h b/src/dallassensor.h index ec410d0e8..6d469dd04 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -47,7 +47,7 @@ class DallasSensor { uint64_t id() const; std::string id_string() const; - std::string to_string() const; + std::string to_string(const bool name = false) const; int16_t offset() const; int16_t temperature_c = EMS_VALUE_SHORT_NOTSET; @@ -88,7 +88,7 @@ class DallasSensor { dallas_format_ = dallas_format; } - void add_name(const char * id, const char * name, int16_t offset); + bool add_name(const char * idstr, const char * name, int16_t offset); private: static constexpr uint8_t MAX_SENSORS = 20; @@ -134,7 +134,6 @@ class DallasSensor { bool command_commands(const char * value, const int8_t id, JsonObject & json); uint32_t last_activity_ = uuid::get_uptime(); - uint32_t last_publish_ = uuid::get_uptime(); State state_ = State::IDLE; std::vector sensors_; diff --git a/src/system.h b/src/system.h index d786ad7f7..d4b784932 100644 --- a/src/system.h +++ b/src/system.h @@ -81,6 +81,14 @@ class System { static bool is_valid_gpio(uint8_t pin); static bool load_board_profile(std::vector & data, const std::string & board_profile); + bool analog_enabled() { + return analog_enabled_; + } + + uint16_t analog() { + return analog_; + } + std::string hostname() { return hostname_; } @@ -96,6 +104,7 @@ class System { void ethernet_connected(bool b) { ethernet_connected_ = b; } + void network_connected(bool b) { network_connected_ = b; } diff --git a/src/web/WebDevicesService.cpp b/src/web/WebDevicesService.cpp index f5ed5afab..e5bb70911 100644 --- a/src/web/WebDevicesService.cpp +++ b/src/web/WebDevicesService.cpp @@ -26,7 +26,9 @@ WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * : _device_dataHandler(DEVICE_DATA_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) , _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH, - securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { + securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) + , _writesensor_dataHandler(WRITE_SENSOR_SERVICE_PATH, + securityManager->wrapCallback(std::bind(&WebDevicesService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); @@ -41,6 +43,10 @@ WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * _writevalue_dataHandler.setMethod(HTTP_POST); _writevalue_dataHandler.setMaxContentLength(256); server->addHandler(&_writevalue_dataHandler); + + _writesensor_dataHandler.setMethod(HTTP_POST); + _writesensor_dataHandler.setMaxContentLength(256); + server->addHandler(&_writesensor_dataHandler); } void WebDevicesService::scan_devices(AsyncWebServerRequest * request) { @@ -73,10 +79,13 @@ void WebDevicesService::all_devices(AsyncWebServerRequest * request) { for (const auto & sensor : EMSESP::sensor_devices()) { JsonObject obj = sensors.createNestedObject(); obj["no"] = i++; - obj["id"] = sensor.to_string(); + obj["id"] = sensor.to_string(true); obj["temp"] = Helpers::render_value(s, sensor.temperature_c, 10); } } + if (EMSESP::system_.analog_enabled()) { + root["analog"] = EMSESP::system_.analog(); + } response->setLength(); request->send(response); @@ -146,4 +155,34 @@ void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant request->send(response); } +// takes a sensorname and optional offset from the Web +void WebDevicesService::write_sensor(AsyncWebServerRequest * request, JsonVariant & json) { + bool ok = false; + if (json.is()) { + JsonObject sn = json["sensorname"]; + std::string id = sn["id"]; + uint8_t no = sn["no"]; + char nostr[3]; + char name[25]; + int16_t offset = 0; + + strlcpy(name, id.c_str(), sizeof(name)); + if (no > 0 && no < 100) { + itoa(no, nostr, 10); + char * c = strchr(name, ' '); // find space + if (c != nullptr) { + *c = '\0'; + float v; + if (Helpers::value2float((c + 1), v)) { + offset = v * 10; + } + } + ok = EMSESP::dallassensor_.add_name(nostr, name, offset); + } + } + + AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204); + request->send(response); +} + } // namespace emsesp \ No newline at end of file diff --git a/src/web/WebDevicesService.h b/src/web/WebDevicesService.h index a5bbe87e7..00f12c9a4 100644 --- a/src/web/WebDevicesService.h +++ b/src/web/WebDevicesService.h @@ -28,6 +28,7 @@ #define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices" #define DEVICE_DATA_SERVICE_PATH "/rest/deviceData" #define WRITE_VALUE_SERVICE_PATH "/rest/writeValue" +#define WRITE_SENSOR_SERVICE_PATH "/rest/writeSensor" namespace emsesp { @@ -43,8 +44,9 @@ class WebDevicesService { // POST void device_data(AsyncWebServerRequest * request, JsonVariant & json); void write_value(AsyncWebServerRequest * request, JsonVariant & json); + void write_sensor(AsyncWebServerRequest * request, JsonVariant & json); - AsyncCallbackJsonWebHandler _device_dataHandler, _writevalue_dataHandler; + AsyncCallbackJsonWebHandler _device_dataHandler, _writevalue_dataHandler, _writesensor_dataHandler; }; } // namespace emsesp From ec8312309095eac5fa7301b838e3f27c9098beec Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 18 Jul 2021 21:43:25 +0200 Subject: [PATCH 056/122] rename Makefile --- makefile => Makefile | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename makefile => Makefile (100%) diff --git a/makefile b/Makefile similarity index 100% rename from makefile rename to Makefile From 239ba335b1c76ecbd0cb0c8a5f657b236b60694d Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 18 Jul 2021 21:44:24 +0200 Subject: [PATCH 057/122] changes to make it compile standalone --- lib_standalone/Arduino.h | 9 ++++++++- lib_standalone/ESP8266React.h | 6 ++++++ lib_standalone/WString.h | 5 +++++ src/emsesp.cpp | 1 + src/web/WebDevicesService.cpp | 2 +- src/web/WebLogService.cpp | 6 ++---- src/web/WebSettingsService.cpp | 9 ++++++--- 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lib_standalone/Arduino.h b/lib_standalone/Arduino.h index 1884c238b..2b1f3f3bb 100644 --- a/lib_standalone/Arduino.h +++ b/lib_standalone/Arduino.h @@ -26,6 +26,8 @@ #include #include // for count_if +#include "WString.h" + #include #define IPAddress std::string @@ -129,7 +131,12 @@ class Print { size_t println(unsigned long value) { return print(std::to_string(value).c_str()) + println(); } + virtual void flush(){}; + + size_t print(const String & str) { + return print(str.c_str()); + } }; class Stream : public Print { @@ -206,6 +213,6 @@ void yield(void); void setup(void); void loop(void); -#include "WString.h" + #endif diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index e1a94119e..908b776b6 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -58,6 +58,7 @@ class DummySettings { uint16_t publish_time_mixer = 10; uint16_t publish_time_other = 10; uint16_t publish_time_sensor = 10; + bool enableIPv6 = false; #define FACTORY_MQTT_MAX_TOPIC_LENGTH 128 @@ -80,6 +81,7 @@ class DummySettingsService : public StatefulService { #define NetworkSettings DummySettings #define SecuritySettings DummySettings #define MqttSettings DummySettings +#define NTPSettings DummySettings class ESP8266React { public: @@ -110,6 +112,10 @@ class ESP8266React { return &_settings; } + StatefulService * getNTPSettingsService() { + return &_settings; + } + private: DummySettingsService _settings; SecuritySettingsService _securitySettingsService; diff --git a/lib_standalone/WString.h b/lib_standalone/WString.h index 98cb2dcbb..5914fd141 100644 --- a/lib_standalone/WString.h +++ b/lib_standalone/WString.h @@ -56,6 +56,11 @@ inline bool operator==(const std::string & lhs, const ::String & rhs) { return lhs == rhs.c_str(); } +inline bool operator!=(const ::String & lhs, const ::String & rhs) { + return lhs.c_str() != rhs.c_str(); +} + + size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize); size_t strlcat(char * dst, const char * src, size_t siz); diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 9aa799ff1..3a8820f0a 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -1200,6 +1200,7 @@ void EMSESP::start() { shower_.start(); // initialize shower timer and shower alert dallassensor_.start(); // dallas external sensors webServer.begin(); // start web server + webLogService.start(); // start web log service emsdevices.reserve(5); // reserve space for initially 5 devices to avoid mem frag issues diff --git a/src/web/WebDevicesService.cpp b/src/web/WebDevicesService.cpp index e5bb70911..8577aa539 100644 --- a/src/web/WebDevicesService.cpp +++ b/src/web/WebDevicesService.cpp @@ -168,7 +168,7 @@ void WebDevicesService::write_sensor(AsyncWebServerRequest * request, JsonVarian strlcpy(name, id.c_str(), sizeof(name)); if (no > 0 && no < 100) { - itoa(no, nostr, 10); + Helpers::itoa(nostr, no, 10); char * c = strchr(name, ' '); // find space if (c != nullptr) { *c = '\0'; diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index c1242d23e..9674f91fd 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -38,15 +38,13 @@ WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * security // for setting a level server->addHandler(&_setLevel); - - // start event source service - start(); } void WebLogService::forbidden(AsyncWebServerRequest * request) { request->send(403); } +// start event source service void WebLogService::start() { uuid::log::Logger::register_handler(this, uuid::log::Level::INFO); // default is INFO } @@ -81,7 +79,7 @@ void WebLogService::operator<<(std::shared_ptr message) { } log_messages_.emplace_back(log_message_id_++, std::move(message)); EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) { - if (!settings.enabled || (time(nullptr) < 1500000000UL)) { + if (!settings.enabled || (time(nullptr) < 1500000000L)) { time_offset_ = 0; } else if (!time_offset_) { time_offset_ = time(nullptr) - uuid::get_uptime_sec(); diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 0d0099cac..3b9aa6b3c 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -134,11 +134,13 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL; check_flag(prev, settings.syslog_mark_interval, ChangeFlags::SYSLOG); +#ifndef EMSESP_STANDALONE String old_syslog_host = settings.syslog_host; settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST; if (!old_syslog_host.equals(settings.syslog_host)) { add_flags(ChangeFlags::SYSLOG); } +#endif prev = settings.syslog_port; settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT; @@ -184,7 +186,8 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) // these need reboots to be applied settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID; settings.master_thermostat = root["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT; - settings.low_clock = root["low_clock"] | false;; + settings.low_clock = root["low_clock"] | false; + ; // doesn't need any follow-up actions settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API; @@ -193,10 +196,10 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) settings.dallas_format = root["dallas_format"] | EMSESP_DEFAULT_DALLAS_FORMAT; EMSESP::dallassensor_.dallas_format(settings.dallas_format); - settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT; + settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT; EMSESP::bool_format(settings.bool_format); - settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT; + settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT; EMSESP::enum_format(settings.enum_format); for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { From 561d1c0e556efaa84f114519f1aacb16e755ac03 Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 18 Jul 2021 21:48:18 +0200 Subject: [PATCH 058/122] fix typo --- pio_local.ini_example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pio_local.ini_example b/pio_local.ini_example index 4d14b38a7..cd31cc22e 100644 --- a/pio_local.ini_example +++ b/pio_local.ini_example @@ -12,7 +12,7 @@ upload_flags = --port=8266 --auth=ems-esp-neo upload_port = 10.10.10.101 -; to prevent the web UI from building each time, comment out this next line +; to prevent the web UI from building each time, uncomment this next line ; extra_scripts = ; pio run -e debug From add09e5a1c5f609713f3ab9fa53d7457ae8a533f Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 19 Jul 2021 16:44:46 +0200 Subject: [PATCH 059/122] update mock api to work with latest changes --- mock-api/server.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mock-api/server.js b/mock-api/server.js index 947a24e04..3f8a6a6e1 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -108,6 +108,8 @@ const network_settings = { ssid: 'myWifi', password: 'myPassword', hostname: 'ems-esp', + nosleep: true, + tx_power: 20, static_ip_config: false, } const network_status = { @@ -282,7 +284,7 @@ const EMSESP_DEVICEDATA_ENDPOINT = REST_ENDPOINT_ROOT + 'deviceData' const EMSESP_STATUS_ENDPOINT = REST_ENDPOINT_ROOT + 'emsespStatus' const EMSESP_BOARDPROFILE_ENDPOINT = REST_ENDPOINT_ROOT + 'boardProfile' const WRITE_VALUE_ENDPOINT = REST_ENDPOINT_ROOT + 'writeValue' -+const WRITE_SENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeSensor' +const WRITE_SENSOR_ENDPOINT = REST_ENDPOINT_ROOT + 'writeSensor' const emsesp_settings = { tx_mode: 1, tx_delay: 0, @@ -909,6 +911,17 @@ app.post(WRITE_VALUE_ENDPOINT, (req, res) => { res.sendStatus(200) }) +app.post(WRITE_SENSOR_ENDPOINT, (req, res) => { + const sensorname = req.body.sensorname + const id = sensorname.id + const no = sensorname.no + + console.log(id) + console.log(no) + + res.sendStatus(200) +}) + app.post(EMSESP_BOARDPROFILE_ENDPOINT, (req, res) => { const board_profile = req.body.code From 37d001e7b51b13cf31b9dbe1c21102e53a50436b Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 19 Jul 2021 16:44:53 +0200 Subject: [PATCH 060/122] formatting --- interface/src/mqtt/MqttSettingsForm.tsx | 20 ++++++------ interface/src/network/NetworkSettingsForm.tsx | 8 ++--- interface/src/project/EMSESPSettingsForm.tsx | 32 +++++++++---------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index b612e79b8..b53b863a5 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -159,7 +159,7 @@ class MqttSettingsForm extends React.Component { value="clean_session" /> } - label="Clean Session" + label="Set Clean Session" /> { value="mqtt_retain" /> } - label="Retain Flag" + label="Use Retain Flag" />

@@ -184,8 +184,8 @@ class MqttSettingsForm extends React.Component { onChange={handleValueChange('nested_format')} margin="normal" > - nested on a single topic - as individual topics + Nested on a single topic + As individual topics { onChange={handleValueChange('subscribe_format')} margin="normal" > - general device topic - individual topics, main heating circuit - individual topics, all heating circuits + General device topic + Individual topics, main heating circuit + Individual topics, all heating circuits { onChange={handleValueChange('ha_climate_format')} margin="normal" > - use Current temperature (default) - use Setpoint temperature - Fix to 0 + Use Current temperature (default) + Use Setpoint temperature + Always set to 0 )}

diff --git a/interface/src/network/NetworkSettingsForm.tsx b/interface/src/network/NetworkSettingsForm.tsx index 97325530d..24c482de8 100644 --- a/interface/src/network/NetworkSettingsForm.tsx +++ b/interface/src/network/NetworkSettingsForm.tsx @@ -174,7 +174,7 @@ class NetworkSettingsForm extends React.Component { onChange={handleValueChange('enableIPv6')} /> } - label="Enable IPv6" + label="Enable IPv6 support" /> { onChange={handleValueChange('bandwidth20')} /> } - label="WiFi Low Bandwidth" + label="Use Lower WiFi Bandwidth" /> { onChange={handleValueChange('nosleep')} /> } - label="Disable Wifi Sleepmode" + label="Disable WiFi Sleep Mode" /> { onChange={handleValueChange('static_ip_config')} /> } - label="Static IP Config" + label="Use Static IPs" /> {data.static_ip_config && ( diff --git a/interface/src/project/EMSESPSettingsForm.tsx b/interface/src/project/EMSESPSettingsForm.tsx index ebbdb43ae..21b49eed8 100644 --- a/interface/src/project/EMSESPSettingsForm.tsx +++ b/interface/src/project/EMSESPSettingsForm.tsx @@ -365,7 +365,7 @@ class EMSESPSettingsForm extends Component {

- Options + General Options {data.led_gpio !== 0 && ( @@ -390,7 +390,7 @@ class EMSESPSettingsForm extends Component { value="dallas_parasite" /> } - label="Enable Dallas parasite mode" + label="Use Dallas Sensor parasite power" /> )} @@ -412,7 +412,17 @@ class EMSESPSettingsForm extends Component { value="low_clock" /> } - label="Low Clockrate (160MHz, changed on next reboot)" + label="Use lower CPU clock speed (only applied after restart)" + /> + + } + label="Bypass Access Token authorization on API calls" /> {

- API and MQTT Options + Formatting Options - - } - label="Bypass Access Token authorization on API calls" - /> { - by Sensor ID + by ID by Number by Name From dd3a0a706df707c73ce46d5f4b5dde699d7a063c Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 19 Jul 2021 16:55:41 +0200 Subject: [PATCH 061/122] auto-formatting --- src/console.cpp | 81 +++++++++++++++-------------- src/dallassensor.cpp | 96 ++++++++++++++++++----------------- src/devices/boiler.cpp | 31 +++++------ src/devices/thermostat.cpp | 6 +-- src/emsdevice.cpp | 4 +- src/locale_EN.h | 4 +- src/mqtt.cpp | 1 - src/web/WebDevicesService.cpp | 4 +- src/web/WebLogService.h | 2 +- src/web/WebSettingsService.h | 8 +-- 10 files changed, 116 insertions(+), 121 deletions(-) diff --git a/src/console.cpp b/src/console.cpp index 76183aef6..d1c1309fe 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -711,47 +711,46 @@ void Console::load_system_commands(unsigned int context) { }); }); - EMSESPShell::commands - ->add_command(context, - CommandFlags::ADMIN, - flash_string_vector{F_(sensorname)}, - flash_string_vector{F_(sensorid_optional), F_(name_optional), F_(offset_optional)}, - [](Shell & shell, const std::vector & arguments) { - if (arguments.size() == 0) { - EMSESP::webSettingsService.read([&](WebSettings & settings) { - for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { - if (!settings.sensor[i].id.isEmpty()) { - shell.print(settings.sensor[i].id); - shell.print(" : "); - shell.print(settings.sensor[i].name); - shell.print(" : "); - char buf[10]; - shell.println(Helpers::render_value(buf, settings.sensor[i].offset, 10)); - } - } - }); - return; - } - if (arguments.size() == 1) { - EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", 0); - // shell.println(EMSESP::dallassensor_.get_name(arguments.front().c_str())); - return; - } - int16_t offset = 0; - float val; - if (arguments.size() == 2) { - if (Helpers::value2float(arguments.back().c_str(), val)) { - offset = (10 * val); - EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", offset); - return; - } - } else if (arguments.size() == 3) { - if (Helpers::value2float(arguments.back().c_str(), val)) { - offset = (10 * val); - } - } - EMSESP::dallassensor_.add_name(arguments.front().c_str(), arguments[1].c_str(), offset); - }); + EMSESPShell::commands->add_command(context, + CommandFlags::ADMIN, + flash_string_vector{F_(sensorname)}, + flash_string_vector{F_(sensorid_optional), F_(name_optional), F_(offset_optional)}, + [](Shell & shell, const std::vector & arguments) { + if (arguments.size() == 0) { + EMSESP::webSettingsService.read([&](WebSettings & settings) { + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (!settings.sensor[i].id.isEmpty()) { + shell.print(settings.sensor[i].id); + shell.print(" : "); + shell.print(settings.sensor[i].name); + shell.print(" : "); + char buf[10]; + shell.println(Helpers::render_value(buf, settings.sensor[i].offset, 10)); + } + } + }); + return; + } + if (arguments.size() == 1) { + EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", 0); + // shell.println(EMSESP::dallassensor_.get_name(arguments.front().c_str())); + return; + } + int16_t offset = 0; + float val; + if (arguments.size() == 2) { + if (Helpers::value2float(arguments.back().c_str(), val)) { + offset = (10 * val); + EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", offset); + return; + } + } else if (arguments.size() == 3) { + if (Helpers::value2float(arguments.back().c_str(), val)) { + offset = (10 * val); + } + } + EMSESP::dallassensor_.add_name(arguments.front().c_str(), arguments[1].c_str(), offset); + }); EMSESPShell::commands ->add_command(context, diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index ae78d39a5..f4db8d0d9 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -315,12 +315,12 @@ std::string DallasSensor::Sensor::to_string(const bool name) const { EMSESP::webSettingsService.read([&](WebSettings & settings) { if (settings.dallas_format == Dallas_Format::NAME || name) { for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { - if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { + if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { str = settings.sensor[i].name.c_str(); } } } - }); + }); return str; } @@ -330,7 +330,7 @@ int16_t DallasSensor::Sensor::offset() const { int16_t offset = 0; EMSESP::webSettingsService.read([&](WebSettings & settings) { for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { - if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { + if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { offset = settings.sensor[i].offset; } } @@ -352,60 +352,62 @@ bool DallasSensor::add_name(const char * idstr, const char * name, int16_t offse } } // check valid id - if (strlen(id) != 17 || id[2] != '-' || id[7] != '-' || id[12] !='-') { + if (strlen(id) != 17 || id[2] != '-' || id[7] != '-' || id[12] != '-') { LOG_WARNING(F("Invalid sensor id: %s"), id); return ok; } - EMSESP::webSettingsService.update([&](WebSettings & settings) { - // check for new name of stored id - for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { - if (strcmp(id, settings.sensor[i].id.c_str()) == 0) { - if (strlen(name) == 0 && offset == 0) { // delete entry if name and offset is empty - settings.sensor[i].id = ""; - settings.sensor[i].name = ""; - settings.sensor[i].offset = 0; - LOG_INFO(F("Deleting entry of sensor %s"), id); - } else { + EMSESP::webSettingsService.update( + [&](WebSettings & settings) { + // check for new name of stored id + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (strcmp(id, settings.sensor[i].id.c_str()) == 0) { + if (strlen(name) == 0 && offset == 0) { // delete entry if name and offset is empty + settings.sensor[i].id = ""; + settings.sensor[i].name = ""; + settings.sensor[i].offset = 0; + LOG_INFO(F("Deleting entry of sensor %s"), id); + } else { + settings.sensor[i].name = (strlen(name) == 0) ? id : name; + settings.sensor[i].offset = offset; + LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + } + ok = true; + return StateUpdateResult::CHANGED; + } + } + // check for free place + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + if (settings.sensor[i].id.isEmpty()) { + settings.sensor[i].id = id; settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; LOG_INFO(F("Setting name of sensor %s to %s"), id, name); - } - ok = true; - return StateUpdateResult::CHANGED; - } - } - // check for free place - for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { - if (settings.sensor[i].id.isEmpty()) { - settings.sensor[i].id = id; - settings.sensor[i].name = (strlen(name) == 0) ? id : name; - settings.sensor[i].offset = offset; - LOG_INFO(F("Setting name of sensor %s to %s"), id, name); - ok = true; - return StateUpdateResult::CHANGED; - } - } - // check if there is a unused id and overwrite it - for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { - bool found = false; - for (const auto & sensor : sensors_) { - if (strcmp(sensor.id_string().c_str(), settings.sensor[i].id.c_str()) == 0) { - found = true; + ok = true; + return StateUpdateResult::CHANGED; } } - if (!found) { - settings.sensor[i].id = id; - settings.sensor[i].name = (strlen(name) == 0) ? id : name; - settings.sensor[i].offset = offset; - LOG_INFO(F("Setting name of sensor %s to %s"), id, name); - ok = true; - return StateUpdateResult::CHANGED; + // check if there is a unused id and overwrite it + for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { + bool found = false; + for (const auto & sensor : sensors_) { + if (strcmp(sensor.id_string().c_str(), settings.sensor[i].id.c_str()) == 0) { + found = true; + } + } + if (!found) { + settings.sensor[i].id = id; + settings.sensor[i].name = (strlen(name) == 0) ? id : name; + settings.sensor[i].offset = offset; + LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + ok = true; + return StateUpdateResult::CHANGED; + } } - } - LOG_ERROR(F("List full, remove one sensorname first")); - return StateUpdateResult::UNCHANGED; - }, "local"); + LOG_ERROR(F("List full, remove one sensorname first")); + return StateUpdateResult::UNCHANGED; + }, + "local"); return ok; } diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 3ad94e705..b5e216e66 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -85,8 +85,13 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor)); } // MQTT commands for boiler topic - register_device_value( - TAG_BOILER_DATA, &wWTapActivated_, DeviceValueType::BOOL, nullptr, FL_(wwtapactivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_tapwarmwater_activated)); + register_device_value(TAG_BOILER_DATA, + &wWTapActivated_, + DeviceValueType::BOOL, + nullptr, + FL_(wwtapactivated), + DeviceValueUOM::BOOLEAN, + MAKE_CF_CB(set_tapwarmwater_activated)); register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::LIST, MAKE_CF_CB(set_reset)); // add values @@ -143,20 +148,10 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const FL_(maintenanceType), DeviceValueUOM::LIST, MAKE_CF_CB(set_maintenance)); - register_device_value(TAG_BOILER_DATA, - &maintenanceTime_, - DeviceValueType::USHORT, - nullptr, - FL_(maintenanceTime), - DeviceValueUOM::HOURS, - MAKE_CF_CB(set_maintenancetime)); - register_device_value(TAG_BOILER_DATA, - &maintenanceDate_, - DeviceValueType::TEXT, - nullptr, - FL_(maintenanceDate), - DeviceValueUOM::NONE, - MAKE_CF_CB(set_maintenancedate)); + register_device_value( + TAG_BOILER_DATA, &maintenanceTime_, DeviceValueType::USHORT, nullptr, FL_(maintenanceTime), DeviceValueUOM::HOURS, MAKE_CF_CB(set_maintenancetime)); + register_device_value( + TAG_BOILER_DATA, &maintenanceDate_, DeviceValueType::TEXT, nullptr, FL_(maintenanceDate), DeviceValueUOM::NONE, MAKE_CF_CB(set_maintenancedate)); // heatpump info if (model() == EMS_DEVICE_FLAG_HEATPUMP) { register_device_value(TAG_BOILER_DATA, &upTimeControl_, DeviceValueType::TIME, FL_(div60), FL_(upTimeControl), DeviceValueUOM::MINUTES); @@ -305,7 +300,7 @@ void Boiler::check_active(const bool force) { // check if we can use tapactivated in flow systems if ((wWType_ == 1) && !Helpers::hasValue(wWTapActivated_, EMS_VALUE_BOOL)) { - wWTapActivated_= 1; + wWTapActivated_ = 1; } // check if tap water is active, bits 1 and 4 must be set @@ -1160,7 +1155,7 @@ bool Boiler::set_tapwarmwater_activated(const char * value, const int8_t id) { message_data[1] = 0x00; // burner output 0% message_data[3] = 0x64; // boiler pump capacity 100% message_data[4] = 0xFF; // 3-way valve hot water only - wWTapActivated_= 0; + wWTapActivated_ = 0; } else { // get out of test mode. Send all zeros. // telegram: 0B 08 1D 00 00 diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 3a8d1de30..03425e53e 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -1105,9 +1105,9 @@ void Thermostat::process_RCErrorMessage(std::shared_ptr telegram if (telegram->message_data[4] & 0x80) { // valid date char code[3]; uint16_t codeNo = EMS_VALUE_USHORT_NOTSET; - code[0] = telegram->message_data[0]; - code[1] = telegram->message_data[1]; - code[2] = 0; + code[0] = telegram->message_data[0]; + code[1] = telegram->message_data[1]; + code[2] = 0; telegram->read_value(codeNo, 2); uint16_t year = (telegram->message_data[4] & 0x7F) + 2000; uint8_t month = telegram->message_data[5]; diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 99bfe7b30..0adefa2c6 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -582,7 +582,7 @@ void EMSdevice::generate_values_json_web(JsonObject & json) { } // handle commands without value - else if(dv.type == DeviceValueType::CMD) { + else if (dv.type == DeviceValueType::CMD) { obj = data.createNestedObject(); obj["v"] = ""; } @@ -881,7 +881,7 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter } else { json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0; } - has_value = true; + has_value = true; } // handle TEXT strings diff --git a/src/locale_EN.h b/src/locale_EN.h index 2671619c4..ebfe70655 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -175,8 +175,8 @@ MAKE_PSTR(w, "W") MAKE_PSTR(kb, "KB") MAKE_PSTR(seconds, "seconds") MAKE_PSTR(dbm, "dBm") -MAKE_PSTR(num, " ") // this is hack so HA renders numbers correctly -MAKE_PSTR(bool, " ") // this is hack so HA renders numbers correctly +MAKE_PSTR(num, " ") // this is hack so HA renders numbers correctly +MAKE_PSTR(bool, " ") // this is hack so HA renders numbers correctly MAKE_PSTR(blank, " ") // this is hack so HA renders numbers correctly // TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 1ae9f60d7..a5db4a438 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -55,7 +55,6 @@ uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON}; // subscribe to an MQTT topic, and store the associated callback function // only if it already hasn't been added void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_subfunction_p cb) { - // check if we already have the topic subscribed, if so don't add it again if (!mqtt_subfunctions_.empty()) { for (auto & mqtt_subfunction : mqtt_subfunctions_) { diff --git a/src/web/WebDevicesService.cpp b/src/web/WebDevicesService.cpp index 8577aa539..fbc822bfe 100644 --- a/src/web/WebDevicesService.cpp +++ b/src/web/WebDevicesService.cpp @@ -28,7 +28,7 @@ WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * , _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH, securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) , _writesensor_dataHandler(WRITE_SENSOR_SERVICE_PATH, - securityManager->wrapCallback(std::bind(&WebDevicesService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { + securityManager->wrapCallback(std::bind(&WebDevicesService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); @@ -178,7 +178,7 @@ void WebDevicesService::write_sensor(AsyncWebServerRequest * request, JsonVarian } } ok = EMSESP::dallassensor_.add_name(nostr, name, offset); - } + } } AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204); diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index c45c7213a..9c4329f28 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -75,7 +75,7 @@ class WebLogService : public uuid::log::Handler { unsigned long log_message_id_ = 0; // The next identifier to use for queued log messages unsigned long log_message_id_tail_ = 0; // last event shown on the screen after fetch std::list log_messages_; // Queued log messages, in the order they were received - time_t time_offset_ = 0; + time_t time_offset_ = 0; }; } // namespace emsesp diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h index f35c54079..2f8730f3f 100644 --- a/src/web/WebSettingsService.h +++ b/src/web/WebSettingsService.h @@ -33,7 +33,7 @@ namespace emsesp { enum { BOOL_FORMAT_ONOFF = 1, BOOL_FORMAT_ONOFF_CAP, BOOL_FORMAT_TRUEFALSE, BOOL_FORMAT_10 }; // matches Web UI settings -enum { ENUM_FORMAT_TEXT = 1, ENUM_FORMAT_NUMBER }; // matches Web UI settings +enum { ENUM_FORMAT_TEXT = 1, ENUM_FORMAT_NUMBER }; // matches Web UI settings class WebSettings { public: @@ -66,9 +66,9 @@ class WebSettings { uint8_t enum_format; struct { - String id; - String name; - int16_t offset; + String id; + String name; + int16_t offset; } sensor[NUM_SENSOR_NAMES]; static void read(WebSettings & settings, JsonObject & root); From f3cfc38adc94c7bf6ff3417696cf4a98753c6cba Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 19 Jul 2021 19:21:03 +0200 Subject: [PATCH 062/122] update regex for ipv6 #83 --- interface/src/validators/isIP.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/validators/isIP.ts b/interface/src/validators/isIP.ts index b9e39f073..36ffee158 100644 --- a/interface/src/validators/isIP.ts +++ b/interface/src/validators/isIP.ts @@ -1,6 +1,5 @@ -const ipAddressRegexp = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; -const ipv6AddressRegexp = /^([a-fA-F0-9]{0,4}:([a-fA-F0-9]{0,4}:){1,6}[a-fA-F0-9]{0,4})$/; +const ipAddressRegexp = /((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/; export default function isIp(ipAddress: string) { - return ipAddressRegexp.test(ipAddress) || ipv6AddressRegexp.test(ipAddress); + return ipAddressRegexp.test(ipAddress); } From 747cda79db2c5533cb59d78afef36c55d9aa4fa0 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 12:59:37 +0200 Subject: [PATCH 063/122] rename add_name() to update(). show offset in log msg --- src/dallassensor.cpp | 23 +++++++++++++++-------- src/dallassensor.h | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index f4db8d0d9..5cb170ff2 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -188,7 +188,7 @@ void DallasSensor::loop() { scancnt_ = 0; } else if (scancnt_ == SCAN_START + 1) { // startup firstscan_ = sensors_.size(); - LOG_DEBUG(F("Adding %d dallassensor(s) from first scan"), firstscan_); + LOG_DEBUG(F("Adding %d dallas sensor(s) from first scan"), firstscan_); } else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor # scancnt_ = SCAN_START; sensors_.clear(); // restart scaning and clear to get correct numbering @@ -327,7 +327,7 @@ std::string DallasSensor::Sensor::to_string(const bool name) const { int16_t DallasSensor::Sensor::offset() const { std::string str = id_string(); - int16_t offset = 0; + int16_t offset = 0; // default value EMSESP::webSettingsService.read([&](WebSettings & settings) { for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) { @@ -339,7 +339,8 @@ int16_t DallasSensor::Sensor::offset() const { return offset; } -bool DallasSensor::add_name(const char * idstr, const char * name, int16_t offset) { +// update dallas information like name and offset +bool DallasSensor::update(const char * idstr, const char * name, int16_t offset) { bool ok = false; char id[20]; strlcpy(id, idstr, sizeof(id)); @@ -351,6 +352,7 @@ bool DallasSensor::add_name(const char * idstr, const char * name, int16_t offse strlcpy(id, sensors_[no].id_string().c_str(), sizeof(id)); } } + // check valid id if (strlen(id) != 17 || id[2] != '-' || id[7] != '-' || id[12] != '-') { LOG_WARNING(F("Invalid sensor id: %s"), id); @@ -366,27 +368,31 @@ bool DallasSensor::add_name(const char * idstr, const char * name, int16_t offse settings.sensor[i].id = ""; settings.sensor[i].name = ""; settings.sensor[i].offset = 0; - LOG_INFO(F("Deleting entry of sensor %s"), id); + LOG_INFO(F("Deleting entry for sensor %s"), id); } else { settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; - LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + char result[10]; + LOG_INFO(F("Renaming sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10)); } ok = true; return StateUpdateResult::CHANGED; } } + // check for free place for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { if (settings.sensor[i].id.isEmpty()) { settings.sensor[i].id = id; settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; - LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + char result[10]; + LOG_INFO(F("Renaming sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10)); ok = true; return StateUpdateResult::CHANGED; } } + // check if there is a unused id and overwrite it for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { bool found = false; @@ -399,12 +405,13 @@ bool DallasSensor::add_name(const char * idstr, const char * name, int16_t offse settings.sensor[i].id = id; settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; - LOG_INFO(F("Setting name of sensor %s to %s"), id, name); + LOG_INFO(F("Renaming sensor %s to %s"), id, name); ok = true; return StateUpdateResult::CHANGED; } } - LOG_ERROR(F("List full, remove one sensorname first")); + + LOG_ERROR(F("List full, remove a sensor first")); return StateUpdateResult::UNCHANGED; }, "local"); diff --git a/src/dallassensor.h b/src/dallassensor.h index 6d469dd04..bc352fa6c 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -88,7 +88,7 @@ class DallasSensor { dallas_format_ = dallas_format; } - bool add_name(const char * idstr, const char * name, int16_t offset); + bool update(const char * idstr, const char * name, int16_t offset); private: static constexpr uint8_t MAX_SENSORS = 20; From 93885d0dd543b8c06219f9dfe2ecb740902fa260 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 12:59:48 +0200 Subject: [PATCH 064/122] update --- CHANGELOG_LATEST.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 3534e34c7..cfc6515d8 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -5,10 +5,10 @@ - support for IPv6 (web/api/mqtt, not syslog) (#83) - System Log in Web UI will show current time if the NTP Service is enabled (#82) - Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode (#83) -- optional low clockrate (160 MHz) (#83) -- selectbox for enumerated values in web +- optional low CPU clockrate (160 MHz) (#83) +- select format for enumerated values in web - settings for water hysteresis on/off -- sensorname console-command, replace sensorid with a unique name +- dallas sensor name editable. `sensorname` console-command, replace sensorid with a unique name (#84) ## Fixed @@ -24,5 +24,4 @@ - enumerated values always start at zero - maintenance settings for time/date as extra setting - move api/mqtt formats to `settings`, add `enum format` - -## Removed +- UI improvements for editing Dallas Sensor details From 33adf518ae05a2990ea5d42a2a95ac342c55f1ef Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 13:00:01 +0200 Subject: [PATCH 065/122] update --- CONTRIBUTING.md | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f72d11fae..facf3ff90 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,17 +24,17 @@ This document describes rules that are in effect for this repository, meant for ## Triaging of Issues/PR's -1. Any contributor to the project can participate in the triaging process, if he/she chooses to do so. -2. An issue that needs to be closed, either due to not complying with this policy, or for other reasons, should be closed by a contributor. -3. Issues that are accepted should be marked with appropriate labels. -4. Issues that could impact functionality for many users should be considered severe. -5. Issues caused by the SDK or chip should not be marked severe, as there usually isn’t much to be done. Common sense should be applied when deciding. Such issues should be documented in the documentation, for reference by users. -6. Issues with feature requests should be discussed for viability/desirability. -7. Feature requests or changes that are meant to address a very specific/limited use case, especially if at the expense of increased code complexity, may be denied, or may be required to be redesigned, generalized, or simplified. -8. Feature requests that are not accompanied by a PR: - * could be closed immediately (denied). - * could be closed after some predetermined period of time (left as candidate for somebody to pick up). -9. In some cases, feedback may be requested from the issue reporter, either as additional info for clarification, additional testing, or other. If no feedback is provided, the issue may be closed by a contributor or after 40 days by the STALE bot. +1. Any contributor to the project can participate in the triaging process, if he/she chooses to do so. +2. An issue that needs to be closed, either due to not complying with this policy, or for other reasons, should be closed by a contributor. +3. Issues that are accepted should be marked with appropriate labels. +4. Issues that could impact functionality for many users should be considered severe. +5. Issues caused by the SDK or chip should not be marked severe, as there usually isn’t much to be done. Common sense should be applied when deciding. Such issues should be documented in the documentation, for reference by users. +6. Issues with feature requests should be discussed for viability/desirability. +7. Feature requests or changes that are meant to address a very specific/limited use case, especially if at the expense of increased code complexity, may be denied, or may be required to be redesigned, generalized, or simplified. +8. Feature requests that are not accompanied by a PR: + - could be closed immediately (denied). + - could be closed after some predetermined period of time (left as candidate for somebody to pick up). +9. In some cases, feedback may be requested from the issue reporter, either as additional info for clarification, additional testing, or other. If no feedback is provided, the issue may be closed by a contributor or after 40 days by the STALE bot. ## Pull requests @@ -42,24 +42,24 @@ A Pull Request (PR) is the process where code modifications are managed in GitHu The process is straight-forward. - - Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) by Kubernetes (but skip step 0) - - Fork the EMS-ESP Repository [git repository](https://github.com/emsesp/EMS-ESP32). - - Write/Change the code in your Fork for a new feature, bug fix, new sensor, optimization, etc. - - Ensure tests work. - - Create a Pull Request against the [**dev**](https://github.com/emsesp/EMS-ESP32/tree/dev) branch of EMS-ESP. +- Read [How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#best-practices-for-faster-reviews) by Kubernetes (but skip step 0) +- Fork the EMS-ESP Repository [git repository](https://github.com/emsesp/EMS-ESP32). +- Write/Change the code in your Fork for a new feature, bug fix, new sensor, optimization, etc. +- Ensure tests work. +- Create a Pull Request against the [**dev**](https://github.com/emsesp/EMS-ESP32/tree/dev) branch of EMS-ESP. 1. All pull requests must be done against the dev branch. -2. Make sure code is formatting per the `.clang-format` -3. Only relevant files should be touched (Also beware if your editor has auto-formatting feature enabled). -4. Only one feature/fix should be added per PR. -5. PRs that don't compile (fail in CI Tests) or cause coding errors will not be merged. Please fix the issue. Same goes for PRs that are raised against older commit in dev - you might need to rebase and resolve conflicts. -6. All pull requests should undergo peer review by at least one contributor other than the creator, excepts for the owner. -7. All pull requests should consider updates to the documentation. -8. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority. -9. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged. -10. By submitting a PR, it is needed to use the provided PR template and check all boxes, performing the required tasks and accepting the CLA. -11. Pull requests that don't meet the above will be denied and closed. - +2. Make sure code is formatting per the `.clang-format`. +3. Make sure any new code is clearly commented explaining what the function/logic does. +4. Only relevant files should be touched (Also beware if your editor has auto-formatting feature enabled). +5. Only one feature/fix should be added per PR. +6. PRs that don't compile (fail in CI Tests) or cause coding errors will not be merged. Please fix the issue. Same goes for PRs that are raised against older commit in dev - you might need to rebase and resolve conflicts. +7. All pull requests should undergo peer review by at least one contributor other than the creator, excepts for the owner. +8. All pull requests should consider updates to the documentation. +9. Pull requests that address an outstanding issue, particularly an issue deemed to be severe, should be given priority. +10. If a PR is accepted, then it should undergo review and updated based on the feedback provided, then merged. +11. By submitting a PR, it is needed to use the provided PR template and check all boxes, performing the required tasks and accepting the CLA. +12. Pull requests that don't meet the above will be denied and closed. ## Semantic Commit Messages @@ -92,7 +92,7 @@ More Examples: References: -- https://www.conventionalcommits.org/ +- -------------------------------------- @@ -139,4 +139,4 @@ A __CLA__ enables a contributor to grant "inbound" rights to a project. - \ No newline at end of file + From ba295385ab5d3190d9a93a2ca06cdba1d247572d Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 13:01:22 +0200 Subject: [PATCH 066/122] rename allDevices endpoint to data. improve dallas sensor form --- interface/src/project/EMSESPDashboard.tsx | 13 ++--- .../src/project/EMSESPDataController.tsx | 35 +++++++++++ ...SESPDevicesForm.tsx => EMSESPDataForm.tsx} | 58 +++++++++---------- .../src/project/EMSESPDevicesController.tsx | 35 ----------- interface/src/project/EMSESPtypes.ts | 5 +- interface/src/project/ProjectMenu.tsx | 2 +- interface/src/project/SensorForm.tsx | 28 ++++----- mock-api/server.js | 22 +++---- src/emsesp.cpp | 15 +++-- src/emsesp.h | 4 +- src/version.h | 2 +- ...bDevicesService.cpp => WebDataService.cpp} | 56 ++++++++---------- .../{WebDevicesService.h => WebDataService.h} | 10 ++-- 13 files changed, 140 insertions(+), 145 deletions(-) create mode 100644 interface/src/project/EMSESPDataController.tsx rename interface/src/project/{EMSESPDevicesForm.tsx => EMSESPDataForm.tsx} (93%) delete mode 100644 interface/src/project/EMSESPDevicesController.tsx rename src/web/{WebDevicesService.cpp => WebDataService.cpp} (76%) rename src/web/{WebDevicesService.h => WebDataService.h} (87%) diff --git a/interface/src/project/EMSESPDashboard.tsx b/interface/src/project/EMSESPDashboard.tsx index b567aa79f..5c781aff9 100644 --- a/interface/src/project/EMSESPDashboard.tsx +++ b/interface/src/project/EMSESPDashboard.tsx @@ -8,7 +8,7 @@ import { MenuAppBar } from '../components'; import { AuthenticatedRoute } from '../authentication'; import EMSESPStatusController from './EMSESPStatusController'; -import EMSESPDevicesController from './EMSESPDevicesController'; +import EMSESPDataController from './EMSESPDataController'; import EMSESPHelp from './EMSESPHelp'; class EMSESP extends Component { @@ -24,18 +24,15 @@ class EMSESP extends Component { onChange={(e, path) => this.handleTabChange(path)} variant="fullWidth" > - + { path={`/${PROJECT_PATH}/help`} component={EMSESPHelp} /> - + ); diff --git a/interface/src/project/EMSESPDataController.tsx b/interface/src/project/EMSESPDataController.tsx new file mode 100644 index 000000000..5320497cd --- /dev/null +++ b/interface/src/project/EMSESPDataController.tsx @@ -0,0 +1,35 @@ +import React, { Component } from 'react'; + +import { + restController, + RestControllerProps, + RestFormLoader, + SectionContent +} from '../components'; + +import { ENDPOINT_ROOT } from '../api'; +import EMSESPDataForm from './EMSESPDataForm'; +import { EMSESPData } from './EMSESPtypes'; + +export const EMSESP_DATA_ENDPOINT = ENDPOINT_ROOT + 'data'; + +type EMSESPDataControllerProps = RestControllerProps; + +class EMSESPDataController extends Component { + componentDidMount() { + this.props.loadData(); + } + + render() { + return ( + + } + /> + + ); + } +} + +export default restController(EMSESP_DATA_ENDPOINT, EMSESPDataController); diff --git a/interface/src/project/EMSESPDevicesForm.tsx b/interface/src/project/EMSESPDataForm.tsx similarity index 93% rename from interface/src/project/EMSESPDevicesForm.tsx rename to interface/src/project/EMSESPDataForm.tsx index b0d6ca421..dba74295f 100644 --- a/interface/src/project/EMSESPDevicesForm.tsx +++ b/interface/src/project/EMSESPDataForm.tsx @@ -40,7 +40,7 @@ import { import { RestFormProps, FormButton, extractEventValue } from '../components'; import { - EMSESPDevices, + EMSESPData, EMSESPDeviceData, Device, DeviceValue, @@ -91,16 +91,16 @@ function compareDevices(a: Device, b: Device) { return 0; } -interface EMSESPDevicesFormState { +interface EMSESPDataFormState { confirmScanDevices: boolean; processing: boolean; deviceData?: EMSESPDeviceData; selectedDevice?: number; edit_devicevalue?: DeviceValue; - edit_sensorname?: Sensor; + edit_Sensor?: Sensor; } -type EMSESPDevicesFormProps = RestFormProps & +type EMSESPDataFormProps = RestFormProps & AuthenticatedContextProps & WithWidthProps; @@ -150,16 +150,16 @@ function formatValue(value: any, uom: number, digit: number) { } } -class EMSESPDevicesForm extends Component< - EMSESPDevicesFormProps, - EMSESPDevicesFormState +class EMSESPDataForm extends Component< + EMSESPDataFormProps, + EMSESPDataFormState > { - state: EMSESPDevicesFormState = { + state: EMSESPDataFormState = { confirmScanDevices: false, processing: false }; - handleValueChange = (name: keyof DeviceValue) => ( + handleDeviceValueChange = (name: keyof DeviceValue) => ( event: React.ChangeEvent ) => { this.setState({ @@ -170,11 +170,11 @@ class EMSESPDevicesForm extends Component< }); }; - cancelEditingValue = () => { + cancelEditingDeviceValue = () => { this.setState({ edit_devicevalue: undefined }); }; - doneEditingValue = () => { + doneEditingDeviceValue = () => { const { edit_devicevalue, selectedDevice } = this.state; redirectingAuthorizedFetch(WRITE_VALUE_ENDPOINT, { @@ -223,24 +223,24 @@ class EMSESPDevicesForm extends Component< event: React.ChangeEvent ) => { this.setState({ - edit_sensorname: { - ...this.state.edit_sensorname!, + edit_Sensor: { + ...this.state.edit_Sensor!, [name]: extractEventValue(event) } }); }; cancelEditingSensor = () => { - this.setState({ edit_sensorname: undefined }); + this.setState({ edit_Sensor: undefined }); }; doneEditingSensor = () => { - const { edit_sensorname } = this.state; + const { edit_Sensor } = this.state; redirectingAuthorizedFetch(WRITE_SENSOR_ENDPOINT, { method: 'POST', body: JSON.stringify({ - sensorname: edit_sensorname + sensor: edit_Sensor }), headers: { 'Content-Type': 'application/json' @@ -248,11 +248,11 @@ class EMSESPDevicesForm extends Component< }) .then((response) => { if (response.status === 200) { - this.props.enqueueSnackbar('Sensorname set', { + this.props.enqueueSnackbar('Sensor updated', { variant: 'success' }); } else if (response.status === 204) { - this.props.enqueueSnackbar('Sensorname not set', { + this.props.enqueueSnackbar('Sensor change failed', { variant: 'error' }); } else if (response.status === 403) { @@ -269,13 +269,13 @@ class EMSESPDevicesForm extends Component< }); }); - if (edit_sensorname) { - this.setState({ edit_sensorname: undefined }); + if (edit_Sensor) { + this.setState({ edit_Sensor: undefined }); } }; sendSensor = (sn: Sensor) => { - this.setState({ edit_sensorname: sn }); + this.setState({ edit_Sensor: sn }); }; noDevices = () => { @@ -386,7 +386,7 @@ class EMSESPDevicesForm extends Component< {me.admin && ( - +

@@ -648,14 +648,14 @@ class EMSESPDevicesForm extends Component< {edit_devicevalue && ( )} - {edit_sensorname && ( + {edit_Sensor && ( ; - -class EMSESPDevicesController extends Component { - componentDidMount() { - this.props.loadData(); - } - - render() { - return ( - - } - /> - - ); - } -} - -export default restController(EMSESP_DEVICES_ENDPOINT, EMSESPDevicesController); diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts index 18e306f27..764f03e55 100644 --- a/interface/src/project/EMSESPtypes.ts +++ b/interface/src/project/EMSESPtypes.ts @@ -54,10 +54,11 @@ export interface Device { export interface Sensor { no: number; id: string; - temp: string; + temp: number; + offset: number; } -export interface EMSESPDevices { +export interface EMSESPData { devices: Device[]; sensors: Sensor[]; analog: number; diff --git a/interface/src/project/ProjectMenu.tsx b/interface/src/project/ProjectMenu.tsx index 844c60664..f0bc9811d 100644 --- a/interface/src/project/ProjectMenu.tsx +++ b/interface/src/project/ProjectMenu.tsx @@ -23,7 +23,7 @@ class ProjectMenu extends Component { to="/ems-esp/" selected={ path.startsWith('/ems-esp/status') || - path.startsWith('/ems-esp/devices') || + path.startsWith('/ems-esp/data') || path.startsWith('/ems-esp/help') } button diff --git a/interface/src/project/SensorForm.tsx b/interface/src/project/SensorForm.tsx index fa6ee246d..9096fd94d 100644 --- a/interface/src/project/SensorForm.tsx +++ b/interface/src/project/SensorForm.tsx @@ -6,7 +6,8 @@ import { DialogContent, DialogActions, FormHelperText, - OutlinedInput + OutlinedInput, + InputAdornment } from '@material-ui/core'; import { FormButton } from '../components'; @@ -45,26 +46,27 @@ class SensorForm extends React.Component { open > - Change Sensor Name + Editing Sensor #{sensor.no} - - Name of sensor #{sensor.no} - + Name (no spaces) - - (optional 'offset' separated by space) - + + Custom Offset + °C} + /> { app.post(EMSESP_SETTINGS_ENDPOINT, (req, res) => { res.json(emsesp_settings) }) -app.get(EMSESP_ALLDEVICES_ENDPOINT, (req, res) => { - res.json(emsesp_alldevices) +app.get(EMSESP_DATA_ENDPOINT, (req, res) => { + res.json(emsesp_data) }) app.post(EMSESP_SCANDEVICES_ENDPOINT, (req, res) => { res.sendStatus(200) @@ -904,7 +905,6 @@ app.post(EMSESP_DEVICEDATA_ENDPOINT, (req, res) => { app.post(WRITE_VALUE_ENDPOINT, (req, res) => { const devicevalue = req.body.devicevalue const id = req.body.id - console.log(id) console.log(devicevalue) @@ -912,12 +912,8 @@ app.post(WRITE_VALUE_ENDPOINT, (req, res) => { }) app.post(WRITE_SENSOR_ENDPOINT, (req, res) => { - const sensorname = req.body.sensorname - const id = sensorname.id - const no = sensorname.no - - console.log(id) - console.log(no) + const sensor = req.body.sensor + console.log(sensor) res.sendStatus(200) }) diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 3a8820f0a..ebc25c244 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -37,10 +37,10 @@ ESP8266React EMSESP::esp8266React(&webServer, &LITTLEFS); WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &LITTLEFS, EMSESP::esp8266React.getSecurityManager()); #endif -WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); -WebDevicesService EMSESP::webDevicesService = WebDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); -WebAPIService EMSESP::webAPIService = WebAPIService(&webServer, EMSESP::esp8266React.getSecurityManager()); -WebLogService EMSESP::webLogService = WebLogService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebDataService EMSESP::webDataService = WebDataService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebAPIService EMSESP::webAPIService = WebAPIService(&webServer, EMSESP::esp8266React.getSecurityManager()); +WebLogService EMSESP::webLogService = WebLogService(&webServer, EMSESP::esp8266React.getSecurityManager()); using DeviceFlags = EMSdevice; using DeviceType = EMSdevice::DeviceType; @@ -363,8 +363,13 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) { shell.printfln(F("Dallas temperature sensors:")); uint8_t i = 1; char s[7]; + char s2[7]; for (const auto & device : sensor_devices()) { - shell.printfln(F(" Sensor %d, ID: %s, Temperature: %s °C"), i++, device.to_string().c_str(), Helpers::render_value(s, device.temperature_c, 10)); + shell.printfln(F(" Sensor %d, ID: %s, Temperature: %s °C (offset %s)"), + i++, + device.to_string().c_str(), + Helpers::render_value(s, device.temperature_c, 10), + Helpers::render_value(s2, device.offset(), 10)); } shell.println(); } diff --git a/src/emsesp.h b/src/emsesp.h index 6f146417b..aefa08f5b 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -36,7 +36,7 @@ #include #include "web/WebStatusService.h" -#include "web/WebDevicesService.h" +#include "web/WebDataService.h" #include "web/WebSettingsService.h" #include "web/WebAPIService.h" #include "web/WebLogService.h" @@ -220,7 +220,7 @@ class EMSESP { static ESP8266React esp8266React; static WebSettingsService webSettingsService; static WebStatusService webStatusService; - static WebDevicesService webDevicesService; + static WebDataService webDataService; static WebAPIService webAPIService; static WebLogService webLogService; diff --git a/src/version.h b/src/version.h index f2ab76dcf..30875cd06 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.2b4" +#define EMSESP_APP_VERSION "3.1.2b5" diff --git a/src/web/WebDevicesService.cpp b/src/web/WebDataService.cpp similarity index 76% rename from src/web/WebDevicesService.cpp rename to src/web/WebDataService.cpp index fbc822bfe..e3ee34a38 100644 --- a/src/web/WebDevicesService.cpp +++ b/src/web/WebDataService.cpp @@ -22,19 +22,19 @@ namespace emsesp { using namespace std::placeholders; // for `_1` etc -WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager) +WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager) : _device_dataHandler(DEVICE_DATA_SERVICE_PATH, - securityManager->wrapCallback(std::bind(&WebDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) + securityManager->wrapCallback(std::bind(&WebDataService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) , _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH, - securityManager->wrapCallback(std::bind(&WebDevicesService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) + securityManager->wrapCallback(std::bind(&WebDataService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) , _writesensor_dataHandler(WRITE_SENSOR_SERVICE_PATH, - securityManager->wrapCallback(std::bind(&WebDevicesService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { - server->on(EMSESP_DEVICES_SERVICE_PATH, + securityManager->wrapCallback(std::bind(&WebDataService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) { + server->on(EMSESP_DATA_SERVICE_PATH, HTTP_GET, - securityManager->wrapRequest(std::bind(&WebDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); + securityManager->wrapRequest(std::bind(&WebDataService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); server->on(SCAN_DEVICES_SERVICE_PATH, HTTP_GET, - securityManager->wrapRequest(std::bind(&WebDevicesService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); + securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); _device_dataHandler.setMethod(HTTP_POST); _device_dataHandler.setMaxContentLength(256); @@ -49,12 +49,12 @@ WebDevicesService::WebDevicesService(AsyncWebServer * server, SecurityManager * server->addHandler(&_writesensor_dataHandler); } -void WebDevicesService::scan_devices(AsyncWebServerRequest * request) { +void WebDataService::scan_devices(AsyncWebServerRequest * request) { EMSESP::scan_devices(); request->send(200); } -void WebDevicesService::all_devices(AsyncWebServerRequest * request) { +void WebDataService::all_devices(AsyncWebServerRequest * request) { AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN); JsonObject root = response->getRoot(); @@ -75,14 +75,15 @@ void WebDevicesService::all_devices(AsyncWebServerRequest * request) { JsonArray sensors = root.createNestedArray("sensors"); if (EMSESP::have_sensors()) { uint8_t i = 1; - char s[8]; for (const auto & sensor : EMSESP::sensor_devices()) { JsonObject obj = sensors.createNestedObject(); obj["no"] = i++; obj["id"] = sensor.to_string(true); - obj["temp"] = Helpers::render_value(s, sensor.temperature_c, 10); + obj["temp"] = (float)(sensor.temperature_c) / 10; + obj["offset"] = (float)(sensor.offset()) / 10; } } + if (EMSESP::system_.analog_enabled()) { root["analog"] = EMSESP::system_.analog(); } @@ -93,7 +94,7 @@ void WebDevicesService::all_devices(AsyncWebServerRequest * request) { // The unique_id is the unique record ID from the Web table to identify which device to load // Compresses the JSON using MsgPack https://msgpack.org/index.html -void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant & json) { +void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) { if (json.is()) { MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); for (const auto & emsdevice : EMSESP::emsdevices) { @@ -117,7 +118,7 @@ void WebDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant } // takes a command and its data value from a specific Device, from the Web -void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant & json) { +void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & json) { if (json.is()) { JsonObject dv = json["devicevalue"]; uint8_t id = json["id"]; @@ -156,28 +157,21 @@ void WebDevicesService::write_value(AsyncWebServerRequest * request, JsonVariant } // takes a sensorname and optional offset from the Web -void WebDevicesService::write_sensor(AsyncWebServerRequest * request, JsonVariant & json) { +void WebDataService::write_sensor(AsyncWebServerRequest * request, JsonVariant & json) { bool ok = false; if (json.is()) { - JsonObject sn = json["sensorname"]; - std::string id = sn["id"]; - uint8_t no = sn["no"]; - char nostr[3]; - char name[25]; - int16_t offset = 0; + JsonObject sensor = json["sensor"]; - strlcpy(name, id.c_str(), sizeof(name)); + // if valid add. + uint8_t no = sensor["no"]; if (no > 0 && no < 100) { - Helpers::itoa(nostr, no, 10); - char * c = strchr(name, ' '); // find space - if (c != nullptr) { - *c = '\0'; - float v; - if (Helpers::value2float((c + 1), v)) { - offset = v * 10; - } - } - ok = EMSESP::dallassensor_.add_name(nostr, name, offset); + char name[25]; + std::string id = sensor["id"]; + strlcpy(name, id.c_str(), sizeof(name)); + float offset = sensor["offset"]; // this will be a float value. We'll convert it to int and * 10 it + int16_t offset10 = offset * 10; + char idstr[3]; + ok = EMSESP::dallassensor_.update(Helpers::itoa(idstr, no, 10), name, offset10); } } diff --git a/src/web/WebDevicesService.h b/src/web/WebDataService.h similarity index 87% rename from src/web/WebDevicesService.h rename to src/web/WebDataService.h index 00f12c9a4..adb114662 100644 --- a/src/web/WebDevicesService.h +++ b/src/web/WebDataService.h @@ -16,15 +16,15 @@ * along with this program. If not, see . */ -#ifndef WebDevicesService_h -#define WebDevicesService_h +#ifndef WebDataService_h +#define WebDataService_h #include #include #include #include -#define EMSESP_DEVICES_SERVICE_PATH "/rest/allDevices" +#define EMSESP_DATA_SERVICE_PATH "/rest/data" #define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices" #define DEVICE_DATA_SERVICE_PATH "/rest/deviceData" #define WRITE_VALUE_SERVICE_PATH "/rest/writeValue" @@ -32,9 +32,9 @@ namespace emsesp { -class WebDevicesService { +class WebDataService { public: - WebDevicesService(AsyncWebServer * server, SecurityManager * securityManager); + WebDataService(AsyncWebServer * server, SecurityManager * securityManager); private: // GET From f299a7ad146a892ba9319a6d068a3dd0d2acf47c Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 13:01:45 +0200 Subject: [PATCH 067/122] update calls to dallas library --- src/console.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/console.cpp b/src/console.cpp index d1c1309fe..9cf210108 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -732,7 +732,7 @@ void Console::load_system_commands(unsigned int context) { return; } if (arguments.size() == 1) { - EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", 0); + EMSESP::dallassensor_.update(arguments.front().c_str(), "", 0); // shell.println(EMSESP::dallassensor_.get_name(arguments.front().c_str())); return; } @@ -741,7 +741,7 @@ void Console::load_system_commands(unsigned int context) { if (arguments.size() == 2) { if (Helpers::value2float(arguments.back().c_str(), val)) { offset = (10 * val); - EMSESP::dallassensor_.add_name(arguments.front().c_str(), "", offset); + EMSESP::dallassensor_.update(arguments.front().c_str(), "", offset); return; } } else if (arguments.size() == 3) { @@ -749,7 +749,7 @@ void Console::load_system_commands(unsigned int context) { offset = (10 * val); } } - EMSESP::dallassensor_.add_name(arguments.front().c_str(), arguments[1].c_str(), offset); + EMSESP::dallassensor_.update(arguments.front().c_str(), arguments[1].c_str(), offset); }); EMSESPShell::commands From 37dae04715615884481d08ee2f703ec618cc98cf Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 13:01:56 +0200 Subject: [PATCH 068/122] clean up ts --- interface/src/project/ValueForm.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/interface/src/project/ValueForm.tsx b/interface/src/project/ValueForm.tsx index e648a0fe9..8da27aa49 100644 --- a/interface/src/project/ValueForm.tsx +++ b/interface/src/project/ValueForm.tsx @@ -69,7 +69,7 @@ class ValueForm extends React.Component { {devicevalue.u !== DeviceValueUOM.BOOLEAN && devicevalue.u !== DeviceValueUOM.LIST && ( { {DeviceValueUOM_s[devicevalue.u]} } - aria-describedby="outlined-value-helper-text" - inputProps={{ - 'aria-label': 'value' - }} /> )} {devicevalue.u === DeviceValueUOM.BOOLEAN && ( { off )} - - {devicevalue.n} - + {devicevalue.n} From 0762d9e124d68cdd325aa26893c187dfd94fd1a6 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 21:43:03 +0200 Subject: [PATCH 069/122] bump to b6 --- .gitignore | 2 +- CHANGELOG_LATEST.md | 12 +++++++----- src/version.h | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index eeaa51aa9..dd1e52450 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ emsesp /interface/build node_modules /interface/.eslintcache - +test.sh \ No newline at end of file diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index cfc6515d8..1767f907e 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -2,13 +2,14 @@ ## Added -- support for IPv6 (web/api/mqtt, not syslog) (#83) -- System Log in Web UI will show current time if the NTP Service is enabled (#82) -- Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode (#83) -- optional low CPU clockrate (160 MHz) (#83) +- support for IPv6 (web/api/mqtt, not syslog yet) [#83](https://github.com/emsesp/EMS-ESP32/issues/83) +- System Log in Web UI will show current time if the NTP Service is enabled [#82](https://github.com/emsesp/EMS-ESP32/issues/82) +- Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode [#83](https://github.com/emsesp/EMS-ESP32/issues/83) +- optional low CPU clockrate (160 MHz) [#83](https://github.com/emsesp/EMS-ESP32/issues/83) - select format for enumerated values in web - settings for water hysteresis on/off -- dallas sensor name editable. `sensorname` console-command, replace sensorid with a unique name (#84) +- dallas sensor name editable. `sensorname` console-command, replace sensorid with a unique name [#84](https://github.com/emsesp/EMS-ESP32/issues/84) +- 'restart' system command. Can be invoked via API with authentication. [#87](https://github.com/emsesp/EMS-ESP32/issues/87) ## Fixed @@ -25,3 +26,4 @@ - maintenance settings for time/date as extra setting - move api/mqtt formats to `settings`, add `enum format` - UI improvements for editing Dallas Sensor details +- Rest GET commands can also require authentication (via bearer access token) for better security diff --git a/src/version.h b/src/version.h index 30875cd06..141b1482d 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.2b5" +#define EMSESP_APP_VERSION "3.1.2b6" From 77f6a180751690f727683a5a4e0695bb7b90dbdf Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 21:45:29 +0200 Subject: [PATCH 070/122] commands take a set of flags, like NEED_ADMIN or HIDDEN --- src/command.cpp | 93 ++++++++++++++++++++++++-------------- src/command.h | 57 +++++++++++++++++------ src/console.cpp | 29 ++++++------ src/emsdevice.h | 7 +-- src/mqtt.cpp | 44 ++++++++++-------- src/mqtt.h | 17 ++++++- src/shower.cpp | 4 +- src/system.cpp | 29 ++++++++---- src/system.h | 8 ++-- src/test/test.cpp | 15 +++++- src/test/test.h | 4 +- src/web/WebAPIService.cpp | 31 ++++++++----- src/web/WebDataService.cpp | 13 +++--- 13 files changed, 229 insertions(+), 122 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 0123d30d1..e324dc32e 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -28,57 +28,66 @@ std::vector Command::cmdfunctions_; // calls a command // id may be used to represent a heating circuit for example, it's optional -// returns false if error or not found -bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) { +// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed +uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id) { int8_t id_new = id; char cmd_new[20] = {'\0'}; strlcpy(cmd_new, cmd, 20); + // find the command auto cf = find_command(device_type, cmd_new, id_new); if ((cf == nullptr) || (cf->cmdfunction_json_)) { LOG_WARNING(F("Command %s on %s not found"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); - return false; // command not found, or requires a json + return 2; // command not found + } + + // check if we're allowed to call it + if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) { + LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); + return 4; // command not allowed } -#ifdef EMSESP_DEBUG std::string dname = EMSdevice::device_type_2_device_name(device_type); if (value == nullptr) { - LOG_DEBUG(F("[DEBUG] Calling %s command '%s'"), dname.c_str(), cmd); + LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd); } else if (id == -1) { - LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value); + LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value); } else { - LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id); + LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id); } -#endif return ((cf->cmdfunction_)(value, id_new)); } // calls a command. Takes a json object for output. // id may be used to represent a heating circuit for example -// returns false if error or not found -bool Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json) { +// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed +uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json) { int8_t id_new = id; char cmd_new[20] = {'\0'}; strlcpy(cmd_new, cmd, 20); auto cf = find_command(device_type, cmd_new, id_new); -#ifdef EMSESP_DEBUG + // check if we're allowed to call it + if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) { + LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); + return 4; // command not allowed + } + std::string dname = EMSdevice::device_type_2_device_name(device_type); if (value == nullptr) { - LOG_DEBUG(F("[DEBUG] Calling %s command '%s'"), dname.c_str(), cmd); + LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd); } else if (id == -1) { - LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value); + LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value); } else { - LOG_DEBUG(F("[DEBUG] Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id); + LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id); } -#endif // check if json object is empty, if so quit if (json.isNull()) { - LOG_WARNING(F("Ignore call for command %s in %s because no json"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); - return false; + LOG_WARNING(F("Ignore call for command %s in %s because it has no json body"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); + return 3; } // this is for endpoints that don't have commands, i.e not writable (e.g. boiler/syspress) @@ -98,20 +107,24 @@ bool Command::call(const uint8_t device_type, const char * cmd, const char * val // strip prefixes, check, and find command Command::CmdFunction * Command::find_command(const uint8_t device_type, char * cmd, int8_t & id) { + // TODO special cases for id=0 and id=-1 will be removed in V3 API // no command for id0 if (id == 0) { return nullptr; } + // empty command is info with id0 if (cmd[0] == '\0') { strcpy(cmd, "info"); id = 0; } + // convert cmd to lowercase for (char * p = cmd; *p; p++) { *p = tolower(*p); } + // TODO hack for commands that could have hc or wwc prefixed. will be removed in new API V3 eventually // scan for prefix hc. for (uint8_t i = DeviceValueTAG::TAG_HC1; i <= DeviceValueTAG::TAG_HC4; i++) { const char * tag = EMSdevice::tag_to_string(i).c_str(); @@ -151,33 +164,40 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, char * c } // add a command to the list, which does not return json -void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flag) { +// these commands are not callable directly via MQTT subscriptions either +void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flags) { // if the command already exists for that device type don't add it if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) { return; } - // if the description is empty, it's hidden which means it will not show up in Web or Console as an available command - bool hidden = (description == nullptr); + // if the description is empty, it's hidden which means it will not show up in Web API or Console as an available command + // TODO check whether we still need this piece of code + if (description == nullptr) { + flags |= CommandFlag::HIDDEN; + } - cmdfunctions_.emplace_back(device_type, flag, cmd, cb, nullptr, description, hidden); // callback for json is nullptr + cmdfunctions_.emplace_back(device_type, flags, cmd, cb, nullptr, description); // callback for json is nullptr // see if we need to subscribe if (Mqtt::enabled()) { - Mqtt::register_command(device_type, cmd, cb, flag); + Mqtt::register_command(device_type, cmd, cb, flags); } } -// add a command to the list, which does return json object as output -// flag is fixed -// optional parameter hidden for commands that will not show up on the Console -void Command::add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, bool hidden) { +// add a command to the list, which does return a json object as output +// flag is fixed to MqttSubFlag::FLAG_NOSUB +void Command::add_returns_json(const uint8_t device_type, + const __FlashStringHelper * cmd, + cmdfunction_json_p cb, + const __FlashStringHelper * description, + uint8_t flags) { // if the command already exists for that device type don't add it if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) { return; } - cmdfunctions_.emplace_back(device_type, MqttSubFlag::FLAG_NOSUB, cmd, nullptr, cb, description, hidden); // callback for json is included + cmdfunctions_.emplace_back(device_type, CommandFlag::MQTT_SUB_FLAG_NOSUB | flags, cmd, nullptr, cb, description); // callback for json is included } // see if a command exists for that device type @@ -213,7 +233,7 @@ bool Command::list(const uint8_t device_type, JsonObject & json) { // create a list of commands, sort them std::list sorted_cmds; for (const auto & cf : cmdfunctions_) { - if ((cf.device_type_ == device_type) && !cf.hidden_) { + if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) { sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_)); } } @@ -221,7 +241,7 @@ bool Command::list(const uint8_t device_type, JsonObject & json) { for (auto & cl : sorted_cmds) { for (const auto & cf : cmdfunctions_) { - if ((cf.device_type_ == device_type) && !cf.hidden_ && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) { + if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) { json[cl] = cf.description_; } } @@ -240,7 +260,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo // create a list of commands, sort them std::list sorted_cmds; for (const auto & cf : cmdfunctions_) { - if ((cf.device_type_ == device_type) && !cf.hidden_) { + if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) { sorted_cmds.push_back(uuid::read_flash_string(cf.cmd_)); } } @@ -261,13 +281,13 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo for (auto & cl : sorted_cmds) { // find and print the description for (const auto & cf : cmdfunctions_) { - if ((cf.device_type_ == device_type) && !cf.hidden_ && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) { + if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == uuid::read_flash_string(cf.cmd_))) { uint8_t i = cl.length(); shell.print(" "); - if (cf.flag_ == FLAG_HC) { + if (cf.has_flags(MQTT_SUB_FLAG_HC)) { shell.print("[hc] "); i += 5; - } else if (cf.flag_ == FLAG_WWC) { + } else if (cf.has_flags(MQTT_SUB_FLAG_WWC)) { shell.print("[wwc] "); i += 6; } @@ -278,6 +298,11 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo } shell.print(COLOR_BRIGHT_CYAN); shell.print(uuid::read_flash_string(cf.description_)); + if (cf.has_flags(CommandFlag::ADMIN_ONLY)) { + shell.print(' '); + shell.print(COLOR_BRIGHT_RED); + shell.print('*'); + } shell.print(COLOR_RESET); } } @@ -337,7 +362,7 @@ void Command::show_devices(uuid::console::Shell & shell) { // output list of all commands to console // calls show with verbose mode set void Command::show_all(uuid::console::Shell & shell) { - shell.println(F("Available commands per device: ")); + shell.println(F("Available commands: ")); // show system first shell.print(COLOR_BOLD_ON); diff --git a/src/command.h b/src/command.h index 08ec8b5b3..daee30e80 100644 --- a/src/command.h +++ b/src/command.h @@ -34,6 +34,17 @@ using uuid::console::Shell; namespace emsesp { +// mqtt flags for command subscriptions +enum CommandFlag : uint8_t { + MQTT_SUB_FLAG_NORMAL = 0, // 0 + MQTT_SUB_FLAG_HC = (1 << 0), // 1 + MQTT_SUB_FLAG_WWC = (1 << 1), // 2 + MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4 + HIDDEN = (1 << 3), // 8 + ADMIN_ONLY = (1 << 4) // 16 + +}; + using cmdfunction_p = std::function; using cmdfunction_json_p = std::function; @@ -41,27 +52,37 @@ class Command { public: struct CmdFunction { uint8_t device_type_; // DeviceType:: - uint8_t flag_; // mqtt flags for command subscriptions + uint8_t flags_; // mqtt flags for command subscriptions const __FlashStringHelper * cmd_; cmdfunction_p cmdfunction_; cmdfunction_json_p cmdfunction_json_; const __FlashStringHelper * description_; - bool hidden_; // if its command not to be shown on the Console CmdFunction(const uint8_t device_type, - const uint8_t flag, + const uint8_t flags, const __FlashStringHelper * cmd, cmdfunction_p cmdfunction, cmdfunction_json_p cmdfunction_json, - const __FlashStringHelper * description, - bool hidden = false) + const __FlashStringHelper * description) : device_type_(device_type) - , flag_(flag) + , flags_(flags) , cmd_(cmd) , cmdfunction_(cmdfunction) , cmdfunction_json_(cmdfunction_json) - , description_(description) - , hidden_(hidden) { + , description_(description) { + } + + inline void add_flags(uint8_t flags) { + flags_ |= flags; + } + inline bool has_flags(uint8_t flags) const { + return (flags_ & flags) == flags; + } + inline void remove_flags(uint8_t flags) { + flags_ &= ~flags; + } + inline uint8_t flags() const { + return flags_; } }; @@ -69,11 +90,21 @@ class Command { return cmdfunctions_; } - static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id, JsonObject & json); - static bool call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = -1); - static void add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flag = 0); - static void - add_with_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, bool hidden = false); + static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id, JsonObject & json); + static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, bool authenticated, const int8_t id = -1); + + static void add(const uint8_t device_type, + const __FlashStringHelper * cmd, + cmdfunction_p cb, + const __FlashStringHelper * description, + uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); + + static void add_returns_json(const uint8_t device_type, + const __FlashStringHelper * cmd, + cmdfunction_json_p cb, + const __FlashStringHelper * description, + uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); + static void show_all(uuid::console::Shell & shell); static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd); static Command::CmdFunction * find_command(const uint8_t device_type, char * cmd, int8_t & id); diff --git a/src/console.cpp b/src/console.cpp index 9cf210108..cc654e63e 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -376,15 +376,8 @@ void EMSESPShell::add_console_commands() { DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN); JsonObject json = doc.to(); - bool ok = false; // validate that a command is present if (arguments.size() < 2) { - // // no cmd specified, default to empty command - // if (Command::call(device_type, "", "", -1, json)) { - // serializeJsonPretty(doc, shell); - // shell.println(); - // return; - // } shell.print(F("Missing command. Available commands are: ")); Command::show(shell, device_type, false); // non-verbose mode return; @@ -392,30 +385,36 @@ void EMSESPShell::add_console_commands() { const char * cmd = arguments[1].c_str(); + uint8_t cmd_return = 1; // OK + if (arguments.size() == 2) { // no value specified, just the cmd - ok = Command::call(device_type, cmd, nullptr, -1, json); + cmd_return = Command::call(device_type, cmd, nullptr, true, -1, json); } else if (arguments.size() == 3) { if (strncmp(cmd, "info", 4) == 0) { // info has a id but no value - ok = Command::call(device_type, cmd, nullptr, atoi(arguments.back().c_str()), json); + cmd_return = Command::call(device_type, cmd, nullptr, true, atoi(arguments.back().c_str()), json); } else { - // has a value but no id - ok = Command::call(device_type, cmd, arguments.back().c_str(), -1, json); + // has a value but no id so use -1 + cmd_return = Command::call(device_type, cmd, arguments.back().c_str(), true, -1, json); } } else { // use value, which could be an id or hc - ok = Command::call(device_type, cmd, arguments[2].c_str(), atoi(arguments[3].c_str()), json); + cmd_return = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json); } - if (ok && json.size()) { + if (cmd_return == 1 && json.size()) { serializeJsonPretty(doc, shell); shell.println(); return; - } else if (!ok) { - shell.println(F("Unknown command, value, or id.")); + } + + if (cmd_return == 2) { + shell.println(F("Unknown command")); shell.print(F("Available commands are: ")); Command::show(shell, device_type, false); // non-verbose mode + } else if (cmd_return == 3) { + shell.println(F("Bad syntax")); } }, [&](Shell & shell __attribute__((unused)), const std::vector & arguments) -> std::vector { diff --git a/src/emsdevice.h b/src/emsdevice.h index ef19c7494..4a771f0c6 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -120,9 +120,6 @@ enum DeviceValueTAG : uint8_t { }; -// mqtt flags for command subscriptions -enum MqttSubFlag : uint8_t { FLAG_NORMAL = 0, FLAG_HC, FLAG_WWC, FLAG_NOSUB }; - // mqtt-HA flags enum DeviceValueHA : uint8_t { HA_NONE = 0, HA_VALUE, HA_DONE }; @@ -170,6 +167,7 @@ class EMSdevice { return ((device_id & 0x7F) == (device_id_ & 0x7F)); } + // flags inline void add_flags(uint8_t flags) { flags_ |= flags; } @@ -281,15 +279,14 @@ class EMSdevice { const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom); - // void register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, int32_t min, uint32_t max); void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid); void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid); void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value); + void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0); void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f); - // void register_cmd(const __FlashStringHelper * cmd, cmdfunction_p f, uint8_t flag = 0); void publish_mqtt_ha_sensor(); diff --git a/src/mqtt.cpp b/src/mqtt.cpp index a5db4a438..5d0efe268 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -83,7 +83,7 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_ } // subscribe to the command topic if it doesn't exist yet -void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flag) { +void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flags) { std::string cmd_topic = EMSdevice::device_type_2_device_name(device_type); // thermostat, boiler, etc... // see if we have already a handler for the device type (boiler, thermostat). If not add it @@ -108,7 +108,7 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper // register the individual commands too (e.g. ems-esp/boiler/wwonetime) // https://github.com/emsesp/EMS-ESP32/issues/31 std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); - if (subscribe_format_ == 2 && flag == MqttSubFlag::FLAG_HC) { + if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_MAIN_HC && ((flags & CommandFlag::MQTT_SUB_FLAG_HC) == CommandFlag::MQTT_SUB_FLAG_HC)) { topic = cmd_topic + "/hc1/" + uuid::read_flash_string(cmd); queue_subscribe_message(topic); topic = cmd_topic + "/hc2/" + uuid::read_flash_string(cmd); @@ -117,7 +117,7 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper queue_subscribe_message(topic); topic = cmd_topic + "/hc4/" + uuid::read_flash_string(cmd); queue_subscribe_message(topic); - } else if (subscribe_format_ && flag != MqttSubFlag::FLAG_NOSUB) { + } else if (subscribe_format_ != Subscribe_Format::GENERAL && ((flags & CommandFlag::MQTT_SUB_FLAG_NOSUB) == CommandFlag::MQTT_SUB_FLAG_NOSUB)) { topic = cmd_topic + "/" + uuid::read_flash_string(cmd); queue_subscribe_message(topic); } @@ -140,7 +140,7 @@ void Mqtt::resubscribe() { } for (const auto & cf : Command::commands()) { std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); - if (subscribe_format_ == 2 && cf.flag_ == MqttSubFlag::FLAG_HC) { + if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_MAIN_HC && cf.has_flags(CommandFlag::MQTT_SUB_FLAG_HC)) { topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc1/" + uuid::read_flash_string(cf.cmd_); queue_subscribe_message(topic); topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc2/" + uuid::read_flash_string(cf.cmd_); @@ -149,7 +149,7 @@ void Mqtt::resubscribe() { queue_subscribe_message(topic); topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc4/" + uuid::read_flash_string(cf.cmd_); queue_subscribe_message(topic); - } else if (subscribe_format_ && cf.flag_ != MqttSubFlag::FLAG_NOSUB) { + } else if (subscribe_format_ != Subscribe_Format::GENERAL && !cf.has_flags(CommandFlag::MQTT_SUB_FLAG_NOSUB)) { topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/" + uuid::read_flash_string(cf.cmd_); queue_subscribe_message(topic); } @@ -225,7 +225,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) { shell.printfln(F(" %s/%s"), mqtt_base_.c_str(), mqtt_subfunction.topic_.c_str()); } for (const auto & cf : Command::commands()) { - if (subscribe_format_ == 2 && cf.flag_ == MqttSubFlag::FLAG_HC) { + if (subscribe_format_ == 2 && cf.has_flags(CommandFlag::MQTT_SUB_FLAG_HC)) { shell.printfln(F(" %s/%s/hc1/%s"), mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), @@ -242,7 +242,7 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) { mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), uuid::read_flash_string(cf.cmd_).c_str()); - } else if (subscribe_format_ && cf.flag_ != MqttSubFlag::FLAG_NOSUB) { + } else if (subscribe_format_ == 1 && !cf.has_flags(CommandFlag::MQTT_SUB_FLAG_NOSUB)) { shell.printfln(F(" %s/%s/%s"), mqtt_base_.c_str(), EMSdevice::device_type_2_device_name(cf.device_type_).c_str(), @@ -346,8 +346,13 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) } cmd_only++; // skip the / // LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s), mf.device_type_, topic, cmd_only, message); - if (!Command::call(mf.device_type_, cmd_only, message)) { - LOG_ERROR(F("No matching cmd (%s) in topic %s, or invalid data"), cmd_only, topic); + // call command, assume admin authentication is allowed + uint8_t cmd_return = Command::call(mf.device_type_, cmd_only, message, true); + if (cmd_return == 2) { + LOG_ERROR(F("No matching cmd (%s) in topic %s"), cmd_only, topic); + Mqtt::publish(F_(response), "unknown"); + } else if (cmd_return == 3) { + LOG_ERROR(F("Invalid data with cmd (%s) in topic %s"), cmd_only, topic); Mqtt::publish(F_(response), "unknown"); } return; @@ -376,29 +381,32 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) n = doc["id"]; } - bool cmd_known = false; - JsonVariant data = doc["data"]; + uint8_t cmd_return = 1; // OK + JsonVariant data = doc["data"]; if (data.is()) { - cmd_known = Command::call(mf.device_type_, command, data.as(), n); + cmd_return = Command::call(mf.device_type_, command, data.as(), true, n); } else if (data.is()) { char data_str[10]; - cmd_known = Command::call(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as()), n); + cmd_return = Command::call(mf.device_type_, command, Helpers::itoa(data_str, (int16_t)data.as()), true, n); } else if (data.is()) { char data_str[10]; - cmd_known = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as(), 2), n); + cmd_return = Command::call(mf.device_type_, command, Helpers::render_value(data_str, (float)data.as(), 2), true, n); } else if (data.isNull()) { DynamicJsonDocument resp(EMSESP_JSON_SIZE_XLARGE_DYN); JsonObject json = resp.to(); - cmd_known = Command::call(mf.device_type_, command, "", n, json); - if (cmd_known && json.size()) { + cmd_return = Command::call(mf.device_type_, command, "", true, n, json); + if (json.size()) { Mqtt::publish(F_(response), resp.as()); return; } } - if (!cmd_known) { - LOG_ERROR(F("No matching cmd (%s) or invalid data"), command); + if (cmd_return == 2) { + LOG_ERROR(F("No matching cmd (%s)"), command); + Mqtt::publish(F_(response), "unknown"); + } else if (cmd_return == 3) { + LOG_ERROR(F("Invalid data for cmd (%s)"), command); Mqtt::publish(F_(response), "unknown"); } diff --git a/src/mqtt.h b/src/mqtt.h index 9e7e5eb7e..51cc6ca8c 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -81,7 +81,20 @@ class Mqtt { enum Operation { PUBLISH, SUBSCRIBE }; - enum HA_Climate_Format : uint8_t { CURRENT = 1, SETPOINT, ZERO }; + enum HA_Climate_Format : uint8_t { + CURRENT = 1, // 1 + SETPOINT, // 2 + ZERO // 3 + + }; + + // subscribe_format + enum Subscribe_Format : uint8_t { + GENERAL = 0, // 0 + INDIVIDUAL_MAIN_HC, // 1 + INDIVIDUAL_ALL_HC // 2 + + }; static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // note this should really match the user setting in mqttSettings.maxTopicLength @@ -109,7 +122,7 @@ class Mqtt { const uint8_t device_type, const __FlashStringHelper * entity, const uint8_t uom = 0); - static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t tag = 0); + static void register_command(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, uint8_t flags = 0); static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type); static void show_mqtt(uuid::console::Shell & shell); diff --git a/src/shower.cpp b/src/shower.cpp index 6d3e810d0..bf1ae4d25 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -135,7 +135,7 @@ void Shower::send_mqtt_stat(bool state, bool force) { void Shower::shower_alert_stop() { if (doing_cold_shot_) { LOG_DEBUG(F("Shower Alert stopped")); - Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true"); + (void) Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true", true); // no need to check authentication doing_cold_shot_ = false; } } @@ -143,7 +143,7 @@ void Shower::shower_alert_stop() { void Shower::shower_alert_start() { if (shower_alert_) { LOG_DEBUG(F("Shower Alert started")); - Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false"); + (void) Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false", true); // no need to check authentication doing_cold_shot_ = true; alert_timer_start_ = uuid::get_uptime(); // timer starts now } diff --git a/src/system.cpp b/src/system.cpp index d3d462d4e..e20124b18 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -624,13 +624,14 @@ void System::system_check() { // these commands respond to the topic "system" and take a payload like {cmd:"", data:"", id:""} // no individual subscribe for pin command because id is needed void System::commands_init() { - Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, F("set GPIO"), MqttSubFlag::FLAG_NOSUB); - Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram")); - Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish")); - Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values")); - Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("system status")); - Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("list system settings")); - Command::add_with_json(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("list system commands")); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(pin), System::command_pin, F("set GPIO"), CommandFlag::MQTT_SUB_FLAG_NOSUB | CommandFlag::ADMIN_ONLY); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"), CommandFlag::ADMIN_ONLY); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish"), CommandFlag::ADMIN_ONLY); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"), CommandFlag::ADMIN_ONLY); + Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restarts EMS-ESP"), CommandFlag::ADMIN_ONLY); + Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("system status")); + Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("list system settings")); + Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("list system commands")); #if defined(EMSESP_DEBUG) Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("run tests")); #endif @@ -795,11 +796,12 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject & node = json.createNestedObject("System"); node["version"] = EMSESP_APP_VERSION; + // hide ssid from this list EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { - node = json.createNestedObject("Network"); - // node["ssid"] = settings.ssid; // commented out - people don't like others to see this + node = json.createNestedObject("Network"); node["hostname"] = settings.hostname; node["static_ip_config"] = settings.staticIPConfig; + node["enableIPv6"] = settings.enableIPv6; JsonUtils::writeIP(node, "local_ip", settings.localIP); JsonUtils::writeIP(node, "gateway_ip", settings.gatewayIP); JsonUtils::writeIP(node, "subnet_mask", settings.subnetMask); @@ -839,6 +841,7 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject & node["ha_enabled"] = settings.ha_enabled; node["mqtt_qos"] = settings.mqtt_qos; node["mqtt_retain"] = settings.mqtt_retain; + node["subscribe_format"] = settings.subscribe_format; }); #ifndef EMSESP_STANDALONE @@ -996,4 +999,12 @@ bool System::load_board_profile(std::vector & data, const std::string & return true; } +// restart command - perform a hard reset +bool System::command_restart(const char * value, const int8_t id) { +#ifndef EMSESP_STANDALONE + ESP.restart(); +#endif + return true; +} + } // namespace emsesp diff --git a/src/system.h b/src/system.h index d4b784932..300baec68 100644 --- a/src/system.h +++ b/src/system.h @@ -52,13 +52,15 @@ class System { static bool command_send(const char * value, const int8_t id); static bool command_publish(const char * value, const int8_t id); static bool command_fetch(const char * value, const int8_t id); - static bool command_info(const char * value, const int8_t id, JsonObject & json); - static bool command_settings(const char * value, const int8_t id, JsonObject & json); - static bool command_commands(const char * value, const int8_t id, JsonObject & json); + static bool command_restart(const char * value, const int8_t id); #if defined(EMSESP_DEBUG) static bool command_test(const char * value, const int8_t id); #endif + static bool command_info(const char * value, const int8_t id, JsonObject & json); + static bool command_settings(const char * value, const int8_t id, JsonObject & json); + static bool command_commands(const char * value, const int8_t id, JsonObject & json); + void restart(); void format(uuid::console::Shell & shell); void upload_status(bool in_progress); diff --git a/src/test/test.cpp b/src/test/test.cpp index 94e8bc111..16b73f49f 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -352,7 +352,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { if (emsdevice) { doc.clear(); JsonObject json = doc.to(); - Command::call(emsdevice->device_type(), "info", nullptr, -1, json); + Command::call(emsdevice->device_type(), "info", nullptr, true, -1, json); Serial.print(COLOR_YELLOW); if (json.size() != 0) { @@ -424,7 +424,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { run_test("boiler"); // device type, command, data - Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false"); + Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false", true); } if (command == "fr120") { @@ -935,12 +935,23 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { Mqtt::ha_enabled(false); run_test("general"); AsyncWebServerRequest request; + + // GET request.method(HTTP_GET); request.url("/api/thermostat/seltemp"); EMSESP::webAPIService.webAPIService_get(&request); request.url("/api/boiler/syspress"); EMSESP::webAPIService.webAPIService_get(&request); + + request.url("/api/system/commands"); + EMSESP::webAPIService.webAPIService_get(&request); + + // POST + request.method(HTTP_POST); + request.url("/api/system/commands"); + EMSESP::webAPIService.webAPIService_get(&request); + #endif } diff --git a/src/test/test.h b/src/test/test.h index df96d7e1f..87e0c6bf0 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -38,8 +38,8 @@ namespace emsesp { // #define EMSESP_DEBUG_DEFAULT "board_profile" // #define EMSESP_DEBUG_DEFAULT "shower_alert" // #define EMSESP_DEBUG_DEFAULT "310" -// #define EMSESP_DEBUG_DEFAULT "api" -#define EMSESP_DEBUG_DEFAULT "crash" +#define EMSESP_DEBUG_DEFAULT "api" +// #define EMSESP_DEBUG_DEFAULT "crash" class Test { public: diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index c1e121300..20f06110f 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -26,7 +26,7 @@ namespace emsesp { WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager) : _securityManager(securityManager) - , _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS + , _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS, must use 'Content-Type: application/json' in header server->on("/api", HTTP_GET, std::bind(&WebAPIService::webAPIService_get, this, _1)); // for GETS server->addHandler(&_apiHandler); } @@ -47,7 +47,7 @@ void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) { // HTTP_POST | HTTP_PUT | HTTP_PATCH // POST/PUT /{device}[/{hc}][/{name}] void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) { - // extra the params from the json body + // if no body then treat it as a secure GET if (not json.is()) { webAPIService_get(request); return; @@ -158,33 +158,42 @@ void WebAPIService::parse(AsyncWebServerRequest * request, std::string & device_ // check that we have permissions first. We require authenticating on 1 or more of these conditions: // 1. any HTTP POSTs or PUTs - // 2. a HTTP GET which has a 'data' parameter which is not empty (to keep v2 compatibility) - auto method = request->method(); - bool have_data = !value_s.empty(); - bool admin_allowed; + // 2. an HTTP GET which has a 'data' parameter which is not empty (to keep v2 compatibility) + auto method = request->method(); + bool have_data = !value_s.empty(); + bool authenticated = false; EMSESP::webSettingsService.read([&](WebSettings & settings) { Authentication authentication = _securityManager->authenticateRequest(request); - admin_allowed = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication); + authenticated = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication); }); if ((method != HTTP_GET) || ((method == HTTP_GET) && have_data)) { - if (!admin_allowed) { + if (!authenticated) { send_message_response(request, 401, "Bad credentials"); // Unauthorized return; } } - // now we have all the parameters go and execute the command PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN); JsonObject json = response->getRoot(); - bool ok = Command::call(device_type, cmd_s.c_str(), (have_data ? value_s.c_str() : nullptr), id_n, json); + // now we have all the parameters go and execute the command + // the function will also determine if authentication is needed to execute its command + uint8_t cmd_reply = Command::call(device_type, cmd_s.c_str(), (have_data ? value_s.c_str() : nullptr), authenticated, id_n, json); // check for errors - if (!ok) { + if (cmd_reply == 2) { + delete response; + send_message_response(request, 400, "Command not found"); // Bad Request + return; + } else if (cmd_reply == 3) { delete response; send_message_response(request, 400, "Problems parsing elements"); // Bad Request return; + } else if (cmd_reply == 4) { + delete response; + send_message_response(request, 401, "Bad credentials"); // Unauthorized + return; } if (!json.size()) { diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index e3ee34a38..86c5f142f 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -118,6 +118,7 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & } // takes a command and its data value from a specific Device, from the Web +// assumes the service has been checked for admin authentication void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & json) { if (json.is()) { JsonObject dv = json["devicevalue"]; @@ -129,22 +130,22 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & if (emsdevice->unique_id() == id) { const char * cmd = dv["c"]; uint8_t device_type = emsdevice->device_type(); - bool ok = false; + uint8_t cmd_return = 1; // OK char s[10]; // the data could be in any format, but we need string JsonVariant data = dv["v"]; if (data.is()) { - ok = Command::call(device_type, cmd, data.as()); + cmd_return = Command::call(device_type, cmd, data.as(), true); } else if (data.is()) { - ok = Command::call(device_type, cmd, Helpers::render_value(s, data.as(), 0)); + cmd_return = Command::call(device_type, cmd, Helpers::render_value(s, data.as(), 0), true); } else if (data.is()) { - ok = Command::call(device_type, cmd, Helpers::render_value(s, (float)data.as(), 1)); + cmd_return = Command::call(device_type, cmd, Helpers::render_value(s, (float)data.as(), 1), true); } else if (data.is()) { - ok = Command::call(device_type, cmd, data.as() ? "true" : "false"); + cmd_return = Command::call(device_type, cmd, data.as() ? "true" : "false", true); } // send "Write command sent to device" or "Write command failed" - AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204); + AsyncWebServerResponse * response = request->beginResponse((cmd_return == 1) ? 200 : 204); request->send(response); return; } From 074ae2a5a14533d6a09748a35b06fb7d099b0960 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 21:45:59 +0200 Subject: [PATCH 071/122] some refactoring --- interface/src/project/EMSESPSettingsForm.tsx | 2 +- src/dallassensor.cpp | 4 ++-- src/emsdevice.cpp | 23 ++++++++++++-------- src/emsesp.cpp | 8 +++---- src/emsesp.h | 4 ++-- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/interface/src/project/EMSESPSettingsForm.tsx b/interface/src/project/EMSESPSettingsForm.tsx index 21b49eed8..48b38998e 100644 --- a/interface/src/project/EMSESPSettingsForm.tsx +++ b/interface/src/project/EMSESPSettingsForm.tsx @@ -422,7 +422,7 @@ class EMSESPSettingsForm extends Component { value="notoken_api" /> } - label="Bypass Access Token authorization on API calls" + label="Bypass Access Token authorization on API calls (warning! security vulnerability)" /> = TAG_HC1 && tag <= TAG_HC4) { - Command::add(device_type_, name[0], f, name[1], FLAG_HC); - } else if (tag >= TAG_WWC1 && tag <= TAG_WWC4) { - Command::add(device_type_, name[0], f, name[1], FLAG_WWC); - } else { - Command::add(device_type_, name[0], f, name[1], 0); - } + + // add a new command if it has a function attached + if (f == nullptr) { + return; + } + + if (tag >= TAG_HC1 && tag <= TAG_HC4) { + Command::add(device_type_, name[0], f, name[1], CommandFlag::MQTT_SUB_FLAG_HC | CommandFlag::ADMIN_ONLY); + } else if (tag >= TAG_WWC1 && tag <= TAG_WWC4) { + Command::add(device_type_, name[0], f, name[1], CommandFlag::MQTT_SUB_FLAG_WWC | CommandFlag::ADMIN_ONLY); + } else { + Command::add(device_type_, name[0], f, name[1], CommandFlag::MQTT_SUB_FLAG_NORMAL | CommandFlag::ADMIN_ONLY); } } diff --git a/src/emsesp.cpp b/src/emsesp.cpp index ebc25c244..b856d1d8e 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -982,18 +982,18 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std:: return true; } - Command::add_with_json( + Command::add_returns_json( device_type, F_(info), [device_type](const char * value, const int8_t id, JsonObject & json) { return command_info(device_type, json, id, true); }, F_(info_cmd)); - Command::add_with_json( + Command::add_returns_json( device_type, F("info_short"), [device_type](const char * value, const int8_t id, JsonObject & json) { return command_info(device_type, json, id, false); }, nullptr, - true); // this command is hidden - Command::add_with_json( + CommandFlag::HIDDEN); // this command is hidden + Command::add_returns_json( device_type, F_(commands), [device_type](const char * value, const int8_t id, JsonObject & json) { return command_commands(device_type, json, id); }, diff --git a/src/emsesp.h b/src/emsesp.h index aefa08f5b..791652a8f 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -65,8 +65,8 @@ #define EMSESP_JSON_SIZE_XXLARGE_DYN 8192 // for extra very very large json docs, using DynamicJsonDocument // helpers for callback functions -#define MAKE_PF_CB(__f) [&](std::shared_ptr t) { __f(t); } // for process function callbacks to register_telegram_type() -#define MAKE_CF_CB(__f) [&](const char * value, const int8_t id) { return __f(value, id); } // for command function callbacks to register_mqtt_cmd() +#define MAKE_PF_CB(__f) [&](std::shared_ptr t) { __f(t); } // for Process Function callbacks to EMSDevice::process_function_p +#define MAKE_CF_CB(__f) [&](const char * value, const int8_t id) { return __f(value, id); } // for Command Function callbacks Command::cmdfunction_p namespace emsesp { From dfd6798377086c67be55ca7ca6150c5b4ff0f9c4 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 23:35:52 +0200 Subject: [PATCH 072/122] added log id and changed format layout --- interface/src/system/LogEventConsole.tsx | 8 +++++++- interface/src/system/types.ts | 1 + mock-api/server.js | 14 ++++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/interface/src/system/LogEventConsole.tsx b/interface/src/system/LogEventConsole.tsx index 19a96bb77..4051181a4 100644 --- a/interface/src/system/LogEventConsole.tsx +++ b/interface/src/system/LogEventConsole.tsx @@ -110,7 +110,12 @@ const LogEventConsole: FC = (props) => { const paddedNameLabel = (name: string) => { const label = '[' + name + ']'; - return label.padStart(8, '\xa0'); + return label.padEnd(12, '\xa0'); + }; + + const paddedIDLabel = (id: number) => { + const label = id + ':'; + return label.padEnd(7, '\xa0'); }; return ( @@ -119,6 +124,7 @@ const LogEventConsole: FC = (props) => {
{e.t} {paddedLevelLabel(e.l)} + {paddedIDLabel(e.i)} {paddedNameLabel(e.n)} {e.m}
diff --git a/interface/src/system/types.ts b/interface/src/system/types.ts index c7e522440..fdc51626c 100644 --- a/interface/src/system/types.ts +++ b/interface/src/system/types.ts @@ -50,6 +50,7 @@ export enum LogLevel { export interface LogEvent { t: string; l: LogLevel; + i: number; n: string; m: string; } diff --git a/mock-api/server.js b/mock-api/server.js index 2c8b365bc..27edccf69 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -27,37 +27,43 @@ const fetch_log = { { t: '000+00:00:00.001', l: 3, + i: 1, n: 'system', m: 'this is message 3', }, { t: '000+00:00:00.002', l: 4, - n: 'system', + i: 2, + n: 'ntp', m: 'this is message 4', }, { t: '000+00:00:00.002', l: 5, - n: 'system', + i: 3, + n: 'mqtt', m: 'this is message 5', }, { t: '000+00:00:00.002', l: 6, - n: 'system', + i: 444, + n: 'command', m: 'this is message 6', }, { t: '000+00:00:00.002', l: 7, + i: 5555, n: 'emsesp', m: 'this is message 7', }, { t: '000+00:00:00.002', l: 8, - n: 'mqtt', + i: 666666, + n: 'thermostat', m: 'this is message 8', }, ], From 5de3b69e2c9167cccd8fce80dd2a5ccb961df2eb Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 23:36:16 +0200 Subject: [PATCH 073/122] refresh sync changed from 200 to 300ms --- src/web/WebLogService.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index 9c4329f28..4c3401a7e 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -35,6 +35,7 @@ namespace emsesp { class WebLogService : public uuid::log::Handler { public: static constexpr size_t MAX_LOG_MESSAGES = 30; + static constexpr size_t REFRESH_SYNC = 200; WebLogService(AsyncWebServer * server, SecurityManager * securityManager); From 2e0ed9ce9fccd39c9928ca453eb32ccacc23a951 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 23:36:49 +0200 Subject: [PATCH 074/122] fix error where eventsource wasn't flushed correctly & included the log ID --- src/web/WebLogService.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 9674f91fd..0ce55aa47 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -77,7 +77,9 @@ void WebLogService::operator<<(std::shared_ptr message) { if (log_messages_.size() >= maximum_log_messages_) { log_messages_.pop_front(); } + log_messages_.emplace_back(log_message_id_++, std::move(message)); + EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) { if (!settings.enabled || (time(nullptr) < 1500000000L)) { time_offset_ = 0; @@ -94,18 +96,27 @@ void WebLogService::loop() { // put a small delay in const uint64_t now = uuid::get_uptime_ms(); - if (now < last_transmit_ || now - last_transmit_ < 100) { + if (now < last_transmit_ || now - last_transmit_ < REFRESH_SYNC) { return; } // see if we've advanced - if (log_messages_.back().id_ > log_message_id_tail_) { - transmit(log_messages_.back()); - log_message_id_tail_ = log_messages_.back().id_; - last_transmit_ = uuid::get_uptime_ms(); + if (log_messages_.back().id_ <= log_message_id_tail_) { + return; } + + // flush + for (auto it = log_messages_.begin(); it != log_messages_.end(); it++) { + if (it->id_ > log_message_id_tail_) { + transmit(*it); + } + } + + log_message_id_tail_ = log_messages_.back().id_; + last_transmit_ = uuid::get_uptime_ms(); } +// convert time to real offset char * WebLogService::messagetime(char * out, const uint64_t t) { if (!time_offset_) { strcpy(out, uuid::log::format_timestamp_ms(t, 3).c_str()); @@ -122,8 +133,10 @@ void WebLogService::transmit(const QueuedLogMessage & message) { DynamicJsonDocument jsonDocument = DynamicJsonDocument(EMSESP_JSON_SIZE_SMALL); JsonObject logEvent = jsonDocument.to(); char time_string[25]; + logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); logEvent["l"] = message.content_->level; + logEvent["i"] = message.id_; logEvent["n"] = message.content_->name; logEvent["m"] = message.content_->text; @@ -147,8 +160,10 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) { JsonObject logEvent = log.createNestedObject(); auto message = std::move(msg); char time_string[25]; + logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); logEvent["l"] = message.content_->level; + logEvent["i"] = message.id_; logEvent["n"] = message.content_->name; logEvent["m"] = message.content_->text; } From d43886686430e244f5cac0ada39282da3b8e422e Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 20 Jul 2021 23:37:56 +0200 Subject: [PATCH 075/122] updated with log changes --- CHANGELOG_LATEST.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 1767f907e..c1fda2e59 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -16,6 +16,7 @@ - set mode allow numbers - Junkers thermostat shows mode as selected by set_mode - HA thermostat mode if bool-format: numbers is selected +- Web UI System Log sometimes skipped a few log messages when watching real-time ## Changed From 220a69938f240df4b3376af8f8112fd4d9d1056f Mon Sep 17 00:00:00 2001 From: MichaelDvP <59284019+MichaelDvP@users.noreply.github.com> Date: Wed, 21 Jul 2021 09:37:46 +0200 Subject: [PATCH 076/122] fix mqtt individual subscriptions --- src/mqtt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 5d0efe268..da307095f 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -108,7 +108,7 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper // register the individual commands too (e.g. ems-esp/boiler/wwonetime) // https://github.com/emsesp/EMS-ESP32/issues/31 std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); - if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_MAIN_HC && ((flags & CommandFlag::MQTT_SUB_FLAG_HC) == CommandFlag::MQTT_SUB_FLAG_HC)) { + if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_ALL_HC && ((flags & CommandFlag::MQTT_SUB_FLAG_HC) == CommandFlag::MQTT_SUB_FLAG_HC)) { topic = cmd_topic + "/hc1/" + uuid::read_flash_string(cmd); queue_subscribe_message(topic); topic = cmd_topic + "/hc2/" + uuid::read_flash_string(cmd); @@ -140,7 +140,7 @@ void Mqtt::resubscribe() { } for (const auto & cf : Command::commands()) { std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); - if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_MAIN_HC && cf.has_flags(CommandFlag::MQTT_SUB_FLAG_HC)) { + if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_ALL_HC && cf.has_flags(CommandFlag::MQTT_SUB_FLAG_HC)) { topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc1/" + uuid::read_flash_string(cf.cmd_); queue_subscribe_message(topic); topic = EMSdevice::device_type_2_device_name(cf.device_type_) + "/hc2/" + uuid::read_flash_string(cf.cmd_); From f6d22732a0d5907680f1cfc8c0c4a5951f544371 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 21 Jul 2021 15:59:29 +0200 Subject: [PATCH 077/122] formatting --- src/shower.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shower.cpp b/src/shower.cpp index bf1ae4d25..22322328b 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -135,7 +135,7 @@ void Shower::send_mqtt_stat(bool state, bool force) { void Shower::shower_alert_stop() { if (doing_cold_shot_) { LOG_DEBUG(F("Shower Alert stopped")); - (void) Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true", true); // no need to check authentication + (void)Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "true", true); // no need to check authentication doing_cold_shot_ = false; } } @@ -143,7 +143,7 @@ void Shower::shower_alert_stop() { void Shower::shower_alert_start() { if (shower_alert_) { LOG_DEBUG(F("Shower Alert started")); - (void) Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false", true); // no need to check authentication + (void)Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false", true); // no need to check authentication doing_cold_shot_ = true; alert_timer_start_ = uuid::get_uptime(); // timer starts now } From c6db2a1adf32e8a0183784dfd15a17848bb6ed29 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 21 Jul 2021 15:59:44 +0200 Subject: [PATCH 078/122] label change --- interface/src/system/LogEventForm.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/system/LogEventForm.tsx b/interface/src/system/LogEventForm.tsx index 16de6ba0e..9c69db7ef 100644 --- a/interface/src/system/LogEventForm.tsx +++ b/interface/src/system/LogEventForm.tsx @@ -67,7 +67,6 @@ class LogEventForm extends Component { { { - (the last {data.max_messages} messages are buffered and new log - events are shown in real time) +  (the last {data.max_messages} messages are retained and + all new log events are shown in real time below) From a95837404a09e8cdd192f1e6639d3be3e55f8da1 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 21 Jul 2021 16:00:39 +0200 Subject: [PATCH 079/122] valiadtion in sensor fields, auto refresh after change #84 --- interface/src/project/EMSESPDataForm.tsx | 9 +++++- interface/src/project/SensorForm.tsx | 38 ++++++++++++++---------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/interface/src/project/EMSESPDataForm.tsx b/interface/src/project/EMSESPDataForm.tsx index dba74295f..c7376fda3 100644 --- a/interface/src/project/EMSESPDataForm.tsx +++ b/interface/src/project/EMSESPDataForm.tsx @@ -240,7 +240,13 @@ class EMSESPDataForm extends Component< redirectingAuthorizedFetch(WRITE_SENSOR_ENDPOINT, { method: 'POST', body: JSON.stringify({ - sensor: edit_Sensor + // because input field with type=number doens't like negative values, force it here + sensor: { + no: edit_Sensor?.no, + id: edit_Sensor?.id, + temp: edit_Sensor?.temp, + offset: Number(edit_Sensor?.offset) + } }), headers: { 'Content-Type': 'application/json' @@ -251,6 +257,7 @@ class EMSESPDataForm extends Component< this.props.enqueueSnackbar('Sensor updated', { variant: 'success' }); + this.props.loadData(); } else if (response.status === 204) { this.props.enqueueSnackbar('Sensor change failed', { variant: 'error' diff --git a/interface/src/project/SensorForm.tsx b/interface/src/project/SensorForm.tsx index 9096fd94d..ce7825090 100644 --- a/interface/src/project/SensorForm.tsx +++ b/interface/src/project/SensorForm.tsx @@ -1,13 +1,10 @@ import React, { RefObject } from 'react'; -import { ValidatorForm } from 'react-material-ui-form-validator'; +import { ValidatorForm, TextValidator } from 'react-material-ui-form-validator'; import { Dialog, DialogTitle, DialogContent, - DialogActions, - FormHelperText, - OutlinedInput, - InputAdornment + DialogActions } from '@material-ui/core'; import { FormButton } from '../components'; @@ -49,23 +46,32 @@ class SensorForm extends React.Component { Editing Sensor #{sensor.no} - Name (no spaces) - - - Custom Offset - °C} /> From 2fda59d7db1675a54683d3b3b2c09172f48a26c4 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 21 Jul 2021 16:25:42 +0200 Subject: [PATCH 080/122] latest asyncmqttclient dev changes - to safeguard against stuck connections --- lib/async-mqtt-client/src/AsyncMqttClient.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/async-mqtt-client/src/AsyncMqttClient.cpp b/lib/async-mqtt-client/src/AsyncMqttClient.cpp index bdaafcca2..68ea0c5fd 100644 --- a/lib/async-mqtt-client/src/AsyncMqttClient.cpp +++ b/lib/async-mqtt-client/src/AsyncMqttClient.cpp @@ -199,6 +199,8 @@ void AsyncMqttClient::_clear() { _clearQueue(true); // keep session data for now _parsingInformation.bufferState = AsyncMqttClientInternals::BufferState::NONE; + + _client.setRxTimeout(0); } /* TCP */ @@ -286,6 +288,7 @@ void AsyncMqttClient::_onData(char* data, size_t len) { case AsyncMqttClientInternals::PacketType.CONNACK: log_i("rcv CONNACK"); _currentParsedPacket = new AsyncMqttClientInternals::ConnAckPacket(&_parsingInformation, std::bind(&AsyncMqttClient::_onConnAck, this, std::placeholders::_1, std::placeholders::_2)); + _client.setRxTimeout(0); break; case AsyncMqttClientInternals::PacketType.PINGRESP: log_i("rcv PINGRESP"); @@ -708,6 +711,8 @@ void AsyncMqttClient::connect() { log_i("CONNECTING"); _state = CONNECTING; + _client.setRxTimeout(_keepAlive); + #if ASYNC_TCP_SSL_ENABLED if (_useIp) { _client.connect(_ip, _port, _secure); From fc1cb00523e6aa6a56be1abda0cdab7c3d3acd33 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Thu, 22 Jul 2021 12:54:02 +0200 Subject: [PATCH 081/122] reload values after write and update --- interface/src/project/EMSESPDataForm.tsx | 7 ++++--- interface/src/project/SensorForm.tsx | 1 + src/emsesp.cpp | 4 ++++ src/emsesp.h | 7 +++++++ src/telegram.cpp | 7 +++++++ src/web/WebDataService.cpp | 7 ++++++- 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/interface/src/project/EMSESPDataForm.tsx b/interface/src/project/EMSESPDataForm.tsx index c7376fda3..afd1c61d8 100644 --- a/interface/src/project/EMSESPDataForm.tsx +++ b/interface/src/project/EMSESPDataForm.tsx @@ -192,6 +192,7 @@ class EMSESPDataForm extends Component< this.props.enqueueSnackbar('Write command sent to device', { variant: 'success' }); + this.handleRowClick(selectedDevice); } else if (response.status === 204) { this.props.enqueueSnackbar('Write command failed', { variant: 'error' @@ -315,7 +316,7 @@ class EMSESPDataForm extends Component< this.handleRowClick(device)} + onClick={() => this.handleRowClick(device.id)} > { - this.setState({ selectedDevice: device.id, deviceData: undefined }); + this.setState({ selectedDevice: device, deviceData: undefined }); redirectingAuthorizedFetch(DEVICE_DATA_ENDPOINT, { method: 'POST', - body: JSON.stringify({ id: device.id }), + body: JSON.stringify({ id: device }), headers: { 'Content-Type': 'application/json' } diff --git a/interface/src/project/SensorForm.tsx b/interface/src/project/SensorForm.tsx index ce7825090..e981007ef 100644 --- a/interface/src/project/SensorForm.tsx +++ b/interface/src/project/SensorForm.tsx @@ -66,6 +66,7 @@ class SensorForm extends React.Component { ]} label="Custom Offset (°C)" name="offset" + type="number" value={sensor.offset} fullWidth variant="outlined" diff --git a/src/emsesp.cpp b/src/emsesp.cpp index b856d1d8e..fd9d6e419 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -77,6 +77,7 @@ bool EMSESP::trace_raw_ = false; uint64_t EMSESP::tx_delay_ = 0; uint8_t EMSESP::bool_format_ = 1; uint8_t EMSESP::enum_format_ = 1; +uint16_t EMSESP::wait_validate_ = 0; // for a specific EMS device go and request data values // or if device_id is 0 it will fetch from all our known and active devices @@ -839,6 +840,9 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { publish_device_values(emsdevice->device_type()); // publish to MQTT if we explicitly have too } } + if (wait_validate_ == telegram->type_id) { + wait_validate_ = 0; + } break; } } diff --git a/src/emsesp.h b/src/emsesp.h index 791652a8f..b8c336e0d 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -178,6 +178,12 @@ class EMSESP { static void set_read_id(uint16_t id) { read_id_ = id; } + static bool wait_validate() { + return (wait_validate_ != 0); + } + static void wait_validate(uint16_t wait) { + wait_validate_ = wait; + } enum Bus_status : uint8_t { BUS_STATUS_CONNECTED = 0, BUS_STATUS_TX_ERRORS, BUS_STATUS_OFFLINE }; static uint8_t bus_status(); @@ -264,6 +270,7 @@ class EMSESP { static uint64_t tx_delay_; static uint8_t bool_format_; static uint8_t enum_format_; + static uint16_t wait_validate_; }; } // namespace emsesp diff --git a/src/telegram.cpp b/src/telegram.cpp index b5ce8b0ca..450a0740e 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -431,6 +431,9 @@ void TxService::add(const uint8_t operation, } else { tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validateid); // add to back of queue } + if (validateid != 0) { + EMSESP::wait_validate(validateid); + } } // builds a Tx telegram and adds to queue @@ -507,6 +510,9 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt // tx_telegrams_.push_back(qtxt); // add to back of queue tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validate_id); // add to back of queue } + if (validate_id != 0) { + EMSESP::wait_validate(validate_id); + } } // send a Tx telegram to request data from an EMS device @@ -570,6 +576,7 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui if (++retry_count_ > MAXIMUM_TX_RETRIES) { reset_retry_count(); // give up increment_telegram_fail_count(); // another Tx fail + EMSESP::wait_validate(0); // do not wait for validation LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request: %s"), (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 86c5f142f..1bf492944 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -100,6 +100,11 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice) { if (emsdevice->unique_id() == json["id"]) { + // wait max 2.5 sec for updated data (post_send_delay is 2 sec) + for (uint16_t i = 0; i < 2500 && EMSESP::wait_validate(); i++) { + delay(1); + } + EMSESP::wait_validate(0); // reset in case of timeout #ifndef EMSESP_STANDALONE JsonObject root = response->getRoot(); emsdevice->generate_values_json_web(root); @@ -166,7 +171,7 @@ void WebDataService::write_sensor(AsyncWebServerRequest * request, JsonVariant & // if valid add. uint8_t no = sensor["no"]; if (no > 0 && no < 100) { - char name[25]; + char name[20]; std::string id = sensor["id"]; strlcpy(name, id.c_str(), sizeof(name)); float offset = sensor["offset"]; // this will be a float value. We'll convert it to int and * 10 it From 694f647a2cc5938373a67091ced293d9a553ac7a Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Thu, 22 Jul 2021 13:00:16 +0200 Subject: [PATCH 082/122] Command returns as enum --- src/command.h | 10 ++++++++++ src/console.cpp | 8 ++++---- src/mqtt.cpp | 10 +++++----- src/web/WebAPIService.cpp | 6 +++--- src/web/WebDataService.cpp | 4 ++-- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/command.h b/src/command.h index daee30e80..c68a6fd7b 100644 --- a/src/command.h +++ b/src/command.h @@ -45,6 +45,16 @@ enum CommandFlag : uint8_t { }; +// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed +enum CommandRet: uint8_t { + ERRORED = 0, + OK, + NOT_FOUND, + ERROR, + NOT_ALLOWED + +}; + using cmdfunction_p = std::function; using cmdfunction_json_p = std::function; diff --git a/src/console.cpp b/src/console.cpp index cc654e63e..cd35f8aa7 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -385,7 +385,7 @@ void EMSESPShell::add_console_commands() { const char * cmd = arguments[1].c_str(); - uint8_t cmd_return = 1; // OK + uint8_t cmd_return = CommandRet::OK; if (arguments.size() == 2) { // no value specified, just the cmd @@ -403,17 +403,17 @@ void EMSESPShell::add_console_commands() { cmd_return = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json); } - if (cmd_return == 1 && json.size()) { + if (cmd_return == CommandRet::OK && json.size()) { serializeJsonPretty(doc, shell); shell.println(); return; } - if (cmd_return == 2) { + if (cmd_return == CommandRet::NOT_FOUND) { shell.println(F("Unknown command")); shell.print(F("Available commands are: ")); Command::show(shell, device_type, false); // non-verbose mode - } else if (cmd_return == 3) { + } else if (cmd_return == CommandRet::ERROR) { shell.println(F("Bad syntax")); } }, diff --git a/src/mqtt.cpp b/src/mqtt.cpp index da307095f..238b6f1ac 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -348,10 +348,10 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) // LOG_INFO(F("devicetype= %d, topic = %s, cmd = %s, message = %s), mf.device_type_, topic, cmd_only, message); // call command, assume admin authentication is allowed uint8_t cmd_return = Command::call(mf.device_type_, cmd_only, message, true); - if (cmd_return == 2) { + if (cmd_return == CommandRet::NOT_FOUND) { LOG_ERROR(F("No matching cmd (%s) in topic %s"), cmd_only, topic); Mqtt::publish(F_(response), "unknown"); - } else if (cmd_return == 3) { + } else if (cmd_return == CommandRet::ERROR) { LOG_ERROR(F("Invalid data with cmd (%s) in topic %s"), cmd_only, topic); Mqtt::publish(F_(response), "unknown"); } @@ -381,7 +381,7 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) n = doc["id"]; } - uint8_t cmd_return = 1; // OK + uint8_t cmd_return = CommandRet::OK; JsonVariant data = doc["data"]; if (data.is()) { @@ -402,10 +402,10 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) } } - if (cmd_return == 2) { + if (cmd_return == CommandRet::NOT_FOUND) { LOG_ERROR(F("No matching cmd (%s)"), command); Mqtt::publish(F_(response), "unknown"); - } else if (cmd_return == 3) { + } else if (cmd_return == CommandRet::ERROR) { LOG_ERROR(F("Invalid data for cmd (%s)"), command); Mqtt::publish(F_(response), "unknown"); } diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index 20f06110f..8fd60f05b 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -182,15 +182,15 @@ void WebAPIService::parse(AsyncWebServerRequest * request, std::string & device_ uint8_t cmd_reply = Command::call(device_type, cmd_s.c_str(), (have_data ? value_s.c_str() : nullptr), authenticated, id_n, json); // check for errors - if (cmd_reply == 2) { + if (cmd_reply == CommandRet::NOT_FOUND) { delete response; send_message_response(request, 400, "Command not found"); // Bad Request return; - } else if (cmd_reply == 3) { + } else if (cmd_reply == CommandRet::ERROR) { delete response; send_message_response(request, 400, "Problems parsing elements"); // Bad Request return; - } else if (cmd_reply == 4) { + } else if (cmd_reply == CommandRet::NOT_ALLOWED) { delete response; send_message_response(request, 401, "Bad credentials"); // Unauthorized return; diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 1bf492944..631bc8984 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -135,7 +135,7 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & if (emsdevice->unique_id() == id) { const char * cmd = dv["c"]; uint8_t device_type = emsdevice->device_type(); - uint8_t cmd_return = 1; // OK + uint8_t cmd_return = CommandRet::OK; char s[10]; // the data could be in any format, but we need string JsonVariant data = dv["v"]; @@ -150,7 +150,7 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & } // send "Write command sent to device" or "Write command failed" - AsyncWebServerResponse * response = request->beginResponse((cmd_return == 1) ? 200 : 204); + AsyncWebServerResponse * response = request->beginResponse((cmd_return == CommandRet::OK) ? 200 : 204); request->send(response); return; } From 5b55902cd9d135a9ba92151f4a2101e1f33fafdc Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 22 Jul 2021 13:51:59 +0200 Subject: [PATCH 083/122] sync up with HA MQTT Discovery when dallas sensor changed #84 --- src/dallassensor.cpp | 41 ++++++++++++++++++++++++++++++----------- src/dallassensor.h | 2 ++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index 4cab99544..a7659acf3 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -339,6 +339,21 @@ int16_t DallasSensor::Sensor::offset() const { return offset; } +// if HA enabled with MQTT Discovery, delete the old config entry by sending an empty topic +// if we're using the name in the MQTT topic name (Dallas format = NAME) +void DallasSensor::delete_ha_config(uint8_t index, const char * name) { + if (Mqtt::ha_enabled() && (dallas_format_ == Dallas_Format::NAME)) { + char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; + // use '_' as HA doesn't like '-' in the topic name + std::string topicname = name; + std::replace(topicname.begin(), topicname.end(), '-', '_'); + + snprintf_P(topic, sizeof(topic), PSTR("homeassistant/sensor/%s/dallassensor_%s/config"), Mqtt::base().c_str(), topicname.c_str()); + Mqtt::publish(topic); + registered_ha_[index] = false; // forces a recreate of the HA config topic + } +} + // update dallas information like name and offset bool DallasSensor::update(const char * idstr, const char * name, int16_t offset) { bool ok = false; @@ -365,15 +380,17 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset) for (uint8_t i = 0; i < NUM_SENSOR_NAMES; i++) { if (strcmp(id, settings.sensor[i].id.c_str()) == 0) { if (strlen(name) == 0 && offset == 0) { // delete entry if name and offset is empty + LOG_INFO(F("Deleting entry for sensor %s"), id); + delete_ha_config(i, settings.sensor[i].name.c_str()); settings.sensor[i].id = ""; settings.sensor[i].name = ""; settings.sensor[i].offset = 0; - LOG_INFO(F("Deleting entry for sensor %s"), id); } else { - settings.sensor[i].name = (strlen(name) == 0) ? id : name; - settings.sensor[i].offset = offset; char result[10]; LOG_INFO(F("Renaming sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10)); + delete_ha_config(i, settings.sensor[i].name.c_str()); // remove old name in HA + settings.sensor[i].name = (strlen(name) == 0) ? id : name; + settings.sensor[i].offset = offset; } ok = true; return StateUpdateResult::CHANGED; @@ -387,7 +404,7 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset) settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; char result[10]; - LOG_INFO(F("Renaming sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10)); + LOG_INFO(F("Adding sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10)); ok = true; return StateUpdateResult::CHANGED; } @@ -402,16 +419,18 @@ bool DallasSensor::update(const char * idstr, const char * name, int16_t offset) } } if (!found) { + char result[10]; + LOG_INFO(F("Renaming sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10)); + delete_ha_config(i, settings.sensor[i].name.c_str()); // remove old name in HA settings.sensor[i].id = id; settings.sensor[i].name = (strlen(name) == 0) ? id : name; settings.sensor[i].offset = offset; - LOG_INFO(F("Renaming sensor %s to %s"), id, name); - ok = true; + ok = true; return StateUpdateResult::CHANGED; } } - LOG_ERROR(F("List full, remove a sensor first")); + LOG_ERROR(F("No more empty sensor slots, remove one first")); return StateUpdateResult::UNCHANGED; }, "local"); @@ -488,7 +507,7 @@ void DallasSensor::publish_values(const bool force) { } // create the HA MQTT config - // to e.g. homeassistant/sensor/ems-esp/dallas_28-233D-9497-0C03/config + // to e.g. homeassistant/sensor/ems-esp/dallassensor_28-233D-9497-0C03/config if (Mqtt::ha_enabled()) { if (!(registered_ha_[sensor_no - 1]) || force) { StaticJsonDocument config; @@ -516,7 +535,7 @@ void DallasSensor::publish_values(const bool force) { } config["name"] = str; - snprintf_P(str, sizeof(str), PSTR("dallas_%s"), sensor.to_string().c_str()); + snprintf_P(str, sizeof(str), PSTR("dallasensor_%s"), sensor.to_string().c_str()); config["uniq_id"] = str; JsonObject dev = config.createNestedObject("dev"); @@ -525,12 +544,12 @@ void DallasSensor::publish_values(const bool force) { char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; if (dallas_format_ == Dallas_Format::NUMBER) { - snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%d/config"), Mqtt::base().c_str(), sensor_no); + snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallassensor_%d/config"), Mqtt::base().c_str(), sensor_no); } else { // use '_' as HA doesn't like '-' in the topic name std::string topicname = sensor.to_string(); std::replace(topicname.begin(), topicname.end(), '-', '_'); - snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallas_sensor%s/config"), Mqtt::base().c_str(), topicname.c_str()); + snprintf_P(topic, sizeof(topic), PSTR("sensor/%s/dallassensor_%s/config"), Mqtt::base().c_str(), topicname.c_str()); } Mqtt::publish_ha(topic, config.as()); diff --git a/src/dallassensor.h b/src/dallassensor.h index bc352fa6c..7e46ab78d 100644 --- a/src/dallassensor.h +++ b/src/dallassensor.h @@ -133,6 +133,8 @@ class DallasSensor { bool command_info(const char * value, const int8_t id, JsonObject & json); bool command_commands(const char * value, const int8_t id, JsonObject & json); + void delete_ha_config(uint8_t index, const char * name); + uint32_t last_activity_ = uuid::get_uptime(); State state_ = State::IDLE; std::vector sensors_; From 6d94335079053f36c8894bd0fbffe73f37d3bf92 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Thu, 22 Jul 2021 19:35:27 +0200 Subject: [PATCH 084/122] Fix crash on unknown commands (cf==nullptr) --- src/command.cpp | 24 +++++++++++++----------- src/console.cpp | 2 +- src/mqtt.cpp | 4 ++-- src/web/WebAPIService.cpp | 8 ++++---- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index e324dc32e..deb5fcc3a 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -38,13 +38,13 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * auto cf = find_command(device_type, cmd_new, id_new); if ((cf == nullptr) || (cf->cmdfunction_json_)) { LOG_WARNING(F("Command %s on %s not found"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); - return 2; // command not found + return CommandRet::NOT_FOUND; } // check if we're allowed to call it if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) { LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); - return 4; // command not allowed + return CommandRet::NOT_ALLOWED; } std::string dname = EMSdevice::device_type_2_device_name(device_type); @@ -56,7 +56,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id); } - return ((cf->cmdfunction_)(value, id_new)); + return ((cf->cmdfunction_)(value, id_new)) ? CommandRet::OK : CommandRet::ERROR; } // calls a command. Takes a json object for output. @@ -70,9 +70,11 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * auto cf = find_command(device_type, cmd_new, id_new); // check if we're allowed to call it - if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated) { - LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); - return 4; // command not allowed + if (cf != nullptr) { + if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !authenticated && value != nullptr) { + LOG_WARNING(F("Command %s on %s not permitted. requires admin."), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); + return CommandRet::NOT_ALLOWED; // command not allowed + } } std::string dname = EMSdevice::device_type_2_device_name(device_type); @@ -87,21 +89,21 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * // check if json object is empty, if so quit if (json.isNull()) { LOG_WARNING(F("Ignore call for command %s in %s because it has no json body"), cmd, EMSdevice::device_type_2_device_name(device_type).c_str()); - return 3; + return CommandRet::ERROR; } // this is for endpoints that don't have commands, i.e not writable (e.g. boiler/syspress) if (cf == nullptr) { - return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type); + return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type) ? CommandRet::OK : CommandRet::ERROR; } if (cf->cmdfunction_json_) { - return ((cf->cmdfunction_json_)(value, id_new, json)); + return ((cf->cmdfunction_json_)(value, id_new, json)) ? CommandRet::OK : CommandRet::ERROR; } else { if ((device_type != EMSdevice::DeviceType::SYSTEM) && (value == nullptr || strlen(value) == 0 || strcmp(value, "?") == 0 || strcmp(value, "*") == 0)) { - return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type); + return EMSESP::get_device_value_info(json, cmd_new, id_new, device_type) ? CommandRet::OK : CommandRet::ERROR; } - return ((cf->cmdfunction_)(value, id_new)); + return ((cf->cmdfunction_)(value, id_new)) ? CommandRet::OK : CommandRet::ERROR; } } diff --git a/src/console.cpp b/src/console.cpp index cd35f8aa7..997871c1c 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -413,7 +413,7 @@ void EMSESPShell::add_console_commands() { shell.println(F("Unknown command")); shell.print(F("Available commands are: ")); Command::show(shell, device_type, false); // non-verbose mode - } else if (cmd_return == CommandRet::ERROR) { + } else if (cmd_return != CommandRet::OK) { shell.println(F("Bad syntax")); } }, diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 238b6f1ac..fba589c11 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -351,7 +351,7 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) if (cmd_return == CommandRet::NOT_FOUND) { LOG_ERROR(F("No matching cmd (%s) in topic %s"), cmd_only, topic); Mqtt::publish(F_(response), "unknown"); - } else if (cmd_return == CommandRet::ERROR) { + } else if (cmd_return != CommandRet::OK) { LOG_ERROR(F("Invalid data with cmd (%s) in topic %s"), cmd_only, topic); Mqtt::publish(F_(response), "unknown"); } @@ -405,7 +405,7 @@ void Mqtt::on_message(const char * fulltopic, const char * payload, size_t len) if (cmd_return == CommandRet::NOT_FOUND) { LOG_ERROR(F("No matching cmd (%s)"), command); Mqtt::publish(F_(response), "unknown"); - } else if (cmd_return == CommandRet::ERROR) { + } else if (cmd_return != CommandRet::OK) { LOG_ERROR(F("Invalid data for cmd (%s)"), command); Mqtt::publish(F_(response), "unknown"); } diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index 8fd60f05b..f0d0002b5 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -186,14 +186,14 @@ void WebAPIService::parse(AsyncWebServerRequest * request, std::string & device_ delete response; send_message_response(request, 400, "Command not found"); // Bad Request return; - } else if (cmd_reply == CommandRet::ERROR) { - delete response; - send_message_response(request, 400, "Problems parsing elements"); // Bad Request - return; } else if (cmd_reply == CommandRet::NOT_ALLOWED) { delete response; send_message_response(request, 401, "Bad credentials"); // Unauthorized return; + } else if (cmd_reply != CommandRet::OK) { + delete response; + send_message_response(request, 400, "Problems parsing elements"); // Bad Request + return; } if (!json.size()) { From 8930c52adac894ee62d51f9375509ca2ceec5973 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 23 Jul 2021 12:09:41 +0200 Subject: [PATCH 085/122] analog input use calibrated mV --- src/system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index e20124b18..7e0f789e1 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -511,7 +511,7 @@ void System::measure_analog() { if (!measure_last_ || (uint32_t)(uuid::get_uptime() - measure_last_) >= SYSTEM_MEASURE_ANALOG_INTERVAL) { measure_last_ = uuid::get_uptime(); #if defined(ESP32) - uint16_t a = analogRead(36); + uint16_t a = analogReadMilliVolts(36); #else uint16_t a = 0; // standalone #endif From e22b191a48765bf789d92a831bb5eef8e0e5bad7 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 24 Jul 2021 11:56:39 +0200 Subject: [PATCH 086/122] comments, fix typos, prep for v3.2 --- src/command.h | 12 ++++++------ src/devices/boiler.cpp | 5 +---- src/devices/boiler.h | 2 +- src/devices/solar.cpp | 2 +- src/devices/solar.h | 2 +- src/emsdevice.cpp | 2 +- src/system.cpp | 2 +- src/system.h | 2 +- src/version.h | 2 +- 9 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/command.h b/src/command.h index c68a6fd7b..653758ff7 100644 --- a/src/command.h +++ b/src/command.h @@ -45,13 +45,13 @@ enum CommandFlag : uint8_t { }; -// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed -enum CommandRet: uint8_t { +// return status after calling a Command +enum CommandRet : uint8_t { ERRORED = 0, - OK, - NOT_FOUND, - ERROR, - NOT_ALLOWED + OK, // 1 or TRUE + NOT_FOUND, // 2 + ERROR, // 3 + NOT_ALLOWED // needs authentication }; diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index b5e216e66..538ef90e8 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -468,9 +468,6 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr telegram 17)); // can be 0 if no sensor, handled in export_values has_update(telegram->read_value(sysPress_, 21)); - //has_update(telegram->read_value(temperatur_, 13)); // unknown temperature - //has_update(telegram->read_value(temperatur_, 27)); // unknown temperature - // read 3 char service code / installation status as appears on the display if ((telegram->message_length > 3) && (telegram->offset == 0)) { serviceCode_[0] = (serviceCode_[0] == '~') ? 0xF0 : serviceCode_[0]; @@ -540,7 +537,7 @@ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr telegram /* * UBAParametersPlus - type 0xE6 - * parameters originaly taken from + * parameters originally taken from * https://github.com/Th3M3/buderus_ems-wiki/blob/master/Einstellungen%20des%20Regelger%C3%A4ts%20MC110.md * 88 0B E6 00 01 46 00 00 46 0A 00 01 06 FA 0A 01 02 64 01 00 00 1E 00 3C 01 00 00 00 01 00 9A * from: issue #732 diff --git a/src/devices/boiler.h b/src/devices/boiler.h index f2ed15c0d..766e7ebd8 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -49,7 +49,7 @@ class Boiler : public EMSdevice { static constexpr uint8_t EMS_TYPE_UBAParameters = 0x16; static constexpr uint8_t EMS_TYPE_UBAParametersPlus = 0xE6; static constexpr uint8_t EMS_TYPE_UBAParameterWWPlus = 0xEA; - static constexpr uint16_t EMS_TYPE_UBAInfomration = 0x495; + static constexpr uint16_t EMS_TYPE_UBAInformation = 0x495; static constexpr uint16_t EMS_TYPE_UBAEnergySupplied = 0x494; static constexpr uint8_t EMS_BOILER_SELFLOWTEMP_HEATING = 20; // was originally 70, changed to 30 for issue #193, then to 20 with issue #344 diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index 96020aa2d..2339c61c8 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -340,7 +340,7 @@ void Solar::process_SM100Monitor(std::shared_ptr telegram) { has_update(telegram->read_value(heatExchangerTemp_, 20)); // is *10 - TS6: Heat exchanger temperature sensor } -// SM100wwTemperatur - 0x07D6 +// SM100wwTemperature - 0x07D6 // Solar Module(0x2A) -> (0x00), (0x7D6), data: 01 C1 00 00 02 5B 01 AF 01 AD 80 00 01 90 void Solar::process_SM100wwTemperature(std::shared_ptr telegram) { has_update(telegram->read_value(wwTemp_1_, 0)); diff --git a/src/devices/solar.h b/src/devices/solar.h index 502cd249e..b0a68d0ea 100644 --- a/src/devices/solar.h +++ b/src/devices/solar.h @@ -87,7 +87,7 @@ class Solar : public EMSdevice { // SM10Config - 0x96 uint8_t wwMinTemp_; - uint8_t maxFlow_; // set this to caltulate power + uint8_t maxFlow_; // set this to calculate power uint32_t solarPower_; // calculated from maxFlow std::deque energy; diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 6762abad3..e36e04594 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -989,7 +989,7 @@ void EMSdevice::publish_mqtt_ha_sensor() { void EMSdevice::ha_config_clear() { for (auto & dv : devicevalues_) { - // dv.ha &= ~DeviceValueHA::HA_DONE; // repubish all with values + // dv.ha &= ~DeviceValueHA::HA_DONE; // republish all with values dv.ha = DeviceValueHA::HA_NONE; // also wait for new value } ha_config_done(false); diff --git a/src/system.cpp b/src/system.cpp index e20124b18..42a4719a4 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -511,7 +511,7 @@ void System::measure_analog() { if (!measure_last_ || (uint32_t)(uuid::get_uptime() - measure_last_) >= SYSTEM_MEASURE_ANALOG_INTERVAL) { measure_last_ = uuid::get_uptime(); #if defined(ESP32) - uint16_t a = analogRead(36); + uint16_t a = analogRead(ADC_CH0_GPIO); #else uint16_t a = 0; // standalone #endif diff --git a/src/system.h b/src/system.h index 300baec68..74cce4a7e 100644 --- a/src/system.h +++ b/src/system.h @@ -113,7 +113,6 @@ class System { bool network_connected() { #ifndef EMSESP_STANDALONE - // return (ethernet_connected_ || WiFi.isConnected()); return network_connected_; #else return true; @@ -144,6 +143,7 @@ class System { static constexpr uint32_t SYSTEM_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min) static constexpr uint32_t SYSTEM_MEASURE_ANALOG_INTERVAL = 500; static constexpr uint8_t LED_ON = HIGH; // LED + static constexpr uint8_t ADC_CH0_GPIO = 36; #ifndef EMSESP_STANDALONE static uuid::syslog::SyslogService syslog_; diff --git a/src/version.h b/src/version.h index 141b1482d..838811571 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.1.2b6" +#define EMSESP_APP_VERSION "3.2.0b0" From 2f01000665b533af9d683f94c6bcc4ae21b98db9 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 24 Jul 2021 15:01:53 +0200 Subject: [PATCH 087/122] minor debug text changed --- src/mqtt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mqtt.cpp b/src/mqtt.cpp index fba589c11..0d1ce33ac 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -894,7 +894,7 @@ void Mqtt::process_queue() { // else try and publish it uint16_t packet_id = mqttClient_->publish(topic, mqtt_qos_, message->retain, message->payload.c_str(), message->payload.size(), false, mqtt_message.id_); - LOG_DEBUG(F("Publishing topic %s (#%02d, retain=%d, try#%d, size %d, pid %d)"), + LOG_DEBUG(F("Publishing topic %s (#%02d, retain=%d, retry=%d, size=%d, pid=%d)"), topic, mqtt_message.id_, message->retain, From 008983be263d1cebbbea2bc1f32ebbdb7315eabc Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 24 Jul 2021 15:02:12 +0200 Subject: [PATCH 088/122] comment update --- src/devices/boiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 538ef90e8..4f99b2824 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -551,7 +551,7 @@ void Boiler::process_UBAParametersPlus(std::shared_ptr telegram) has_update(telegram->read_value(boilHystOff_, 8)); has_update(telegram->read_value(boilHystOn_, 9)); has_update(telegram->read_value(burnMinPeriod_, 10)); - // has_update(telegram->read_value(pumpType_, 11)); // guess, RC300 manual: powercontroled, pressurcontrolled 1-4? + // has_update(telegram->read_value(pumpType_, 11)); // guess, RC300 manual: power controlled, pressure controlled 1-4? // has_update(telegram->read_value(pumpDelay_, 12)); // guess // has_update(telegram->read_value(pumpModMax_, 13)); // guess // has_update(telegram->read_value(pumpModMin_, 14)); // guess From 15c682cd1ec39fa1cfe911d63a954253b2e11879 Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 24 Jul 2021 15:02:58 +0200 Subject: [PATCH 089/122] don't show CRC in log (irrelevant to most people) --- src/emsesp.cpp | 2 +- src/telegram.cpp | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/emsesp.cpp b/src/emsesp.cpp index fd9d6e419..46a44a18e 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -556,7 +556,7 @@ void EMSESP::publish_response(std::shared_ptr telegram) { doc["dest"] = Helpers::hextoa(buffer, telegram->dest); doc["type"] = Helpers::hextoa(buffer, telegram->type_id); doc["offset"] = Helpers::hextoa(buffer, telegram->offset); - strcpy(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str()); + strcpy(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length - 1).c_str()); // exclude CRC doc["data"] = buffer; if (telegram->message_length <= 4) { diff --git a/src/telegram.cpp b/src/telegram.cpp index 450a0740e..5792cc4ba 100644 --- a/src/telegram.cpp +++ b/src/telegram.cpp @@ -126,14 +126,6 @@ std::string Telegram::to_string_message() const { // checks if we have an Rx telegram that needs processing void RxService::loop() { - /* - while (!rx_telegrams_.empty()) { - auto telegram = rx_telegrams_.pop().telegram_; - (void)EMSESP::process_telegram(telegram); // further process the telegram - increment_telegram_count(); // increase rx count - } - */ - while (!rx_telegrams_.empty()) { auto telegram = rx_telegrams_.front().telegram_; (void)EMSESP::process_telegram(telegram); // further process the telegram @@ -156,9 +148,9 @@ void RxService::add(uint8_t * data, uint8_t length) { if (data[length - 1] != crc) { if ((data[0] & 0x7F) != ems_bus_id()) { // do not count echos as errors telegram_error_count_++; - LOG_WARNING(F("Incomplete Rx: %s"), Helpers::data_to_hex(data, length).c_str()); + LOG_WARNING(F("Incomplete Rx: %s"), Helpers::data_to_hex(data, length - 1).c_str()); // exclude CRC } else { - LOG_TRACE(F("Incomplete Rx: %s"), Helpers::data_to_hex(data, length).c_str()); + LOG_TRACE(F("Incomplete Rx: %s"), Helpers::data_to_hex(data, length - 1).c_str()); // exclude CRC } return; } @@ -204,6 +196,7 @@ void RxService::add(uint8_t * data, uint8_t length) { } // if we're watching and "raw" print out actual telegram as bytes to the console + // including the CRC at the end if (EMSESP::watch() == EMSESP::Watch::WATCH_RAW) { uint16_t trace_watch_id = EMSESP::watch_id(); if ((trace_watch_id == WATCH_ID_NONE) || (type_id == trace_watch_id) @@ -367,7 +360,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) { LOG_DEBUG(F("Sending %s Tx [#%d], telegram: %s"), (telegram->operation == Telegram::Operation::TX_WRITE) ? F("write") : F("read"), tx_telegram.id_, - Helpers::data_to_hex(telegram_raw, length).c_str()); + Helpers::data_to_hex(telegram_raw, length - 1).c_str()); // exclude the last CRC byte set_post_send_query(tx_telegram.validateid_); // send the telegram to the UART Tx @@ -590,7 +583,7 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui (operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"), retry_count_, telegram_last_->to_string().c_str(), - Helpers::data_to_hex(data, length).c_str()); + Helpers::data_to_hex(data, length - 1).c_str()); #endif // add to the top of the queue From f97cdcb4d6132e3298e90613913d2a8c3184192a Mon Sep 17 00:00:00 2001 From: MichaelDvP <59284019+MichaelDvP@users.noreply.github.com> Date: Sun, 25 Jul 2021 11:52:42 +0200 Subject: [PATCH 090/122] fix #89 Also Norbert have 0xFF on his list. --- src/devices/boiler.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 4f99b2824..7f0b10978 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -1112,17 +1112,12 @@ bool Boiler::set_warmwater_activated(const char * value, const int8_t id) { LOG_INFO(F("Setting boiler warm water active %s"), v ? "on" : "off"); // https://github.com/emsesp/EMS-ESP/issues/268 - uint8_t n; - if (EMSbus::is_ht3()) { - n = (v ? 0x08 : 0x00); // 0x08 is on, 0x00 is off - } else { - n = (v ? 0xFF : 0x00); // 0xFF is on, 0x00 is off - } + // 08 for HT3 seems to be wrong, see https://github.com/emsesp/EMS-ESP32/issues/89 if (get_toggle_fetch(EMS_TYPE_UBAParameterWWPlus)) { write_command(EMS_TYPE_UBAParameterWWPlus, 1, v ? 1 : 0, EMS_TYPE_UBAParameterWWPlus); } else { - write_command(EMS_TYPE_UBAParameterWW, 1, n, 0x34); + write_command(EMS_TYPE_UBAParameterWW, 1, v ? 0xFF : 0, EMS_TYPE_UBAParameterWW); } return true; From 1df427366f5e436fabd2c9ea636de9953c15a468 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 26 Jul 2021 11:06:23 +0200 Subject: [PATCH 091/122] syslog add hostname, rename host_ to ip_, query status --- lib/uuid-syslog/src/syslog.cpp | 52 ++++++++++++++++++++++--------- lib/uuid-syslog/src/uuid/syslog.h | 18 ++++++++++- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/lib/uuid-syslog/src/syslog.cpp b/lib/uuid-syslog/src/syslog.cpp index b5913d1a6..8d8b18625 100644 --- a/lib/uuid-syslog/src/syslog.cpp +++ b/lib/uuid-syslog/src/syslog.cpp @@ -18,9 +18,6 @@ #include "uuid/syslog.h" -#include -#include -#include #include "../../../src/emsesp.h" @@ -153,16 +150,38 @@ void SyslogService::maximum_log_messages(size_t count) { } std::pair SyslogService::destination() const { - return std::make_pair(host_, port_); + return std::make_pair(ip_, port_); } void SyslogService::destination(IPAddress host, uint16_t port) { - host_ = host; + ip_ = host; port_ = port; - if ((uint32_t)host_ == (uint32_t)0) { + if ((uint32_t)ip_ == (uint32_t)0) { started_ = false; remove_queued_messages(log_level()); + host_.clear(); + } +} + +void SyslogService::destination(const char * host, uint16_t port) { + if (host == nullptr || host[0] == '\0') { + started_ = false; + remove_queued_messages(log_level()); + ip_ = (IPAddress)(uint32_t)0; + host_.clear(); + return; + } + host_ = host; + port_ = port; + if (ip_.fromString(host)) { + host_.clear(); + if ((uint32_t)ip_ == (uint32_t)0) { + started_ = false; + remove_queued_messages(log_level()); + } + } else { + ip_ = (IPAddress)(uint32_t)0; } } @@ -257,12 +276,15 @@ void SyslogService::loop() { } bool SyslogService::can_transmit() { + if (!host_.empty() && (uint32_t)ip_ == 0) { + WiFi.hostByName(host_.c_str(), ip_); + } #if UUID_SYSLOG_HAVE_IPADDRESS_TYPE - if (host_.isV4() && (uint32_t)host_ == (uint32_t)0) { + if (ip_.isV4() && (uint32_t)ip_ == (uint32_t)0) { return false; } #else - if ((uint32_t)host_ == (uint32_t)0) { + if ((uint32_t)ip_ == (uint32_t)0) { return false; } #endif @@ -276,14 +298,14 @@ bool SyslogService::can_transmit() { #if UUID_SYSLOG_ARP_CHECK #if UUID_SYSLOG_HAVE_IPADDRESS_TYPE - if (host_.isV4()) + if (ip_.isV4()) #endif { message_delay = 10; } #endif #if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE - if (host_.isV6()) { + if (ip_.isV6()) { message_delay = 10; } #endif @@ -294,12 +316,12 @@ bool SyslogService::can_transmit() { #if UUID_SYSLOG_ARP_CHECK #if UUID_SYSLOG_HAVE_IPADDRESS_TYPE - if (host_.isV4()) + if (ip_.isV4()) #endif { ip4_addr_t ipaddr; - ip4_addr_set_u32(&ipaddr, (uint32_t)host_); + ip4_addr_set_u32(&ipaddr, (uint32_t)ip_); if (!ip4_addr_isloopback(&ipaddr) && !ip4_addr_ismulticast(&ipaddr) && !ip4_addr_isbroadcast(&ipaddr, netif_default)) { struct eth_addr * eth_ret = nullptr; @@ -326,10 +348,10 @@ bool SyslogService::can_transmit() { #endif #if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE - if (host_.isV6()) { + if (ip_.isV6()) { ip6_addr_t ip6addr; - IP6_ADDR(&ip6addr, host_.raw6()[0], host_.raw6()[1], host_.raw6()[2], host_.raw6()[3]); + IP6_ADDR(&ip6addr, ip_.raw6()[0], ip_.raw6()[1], ip_.raw6()[2], ip_.raw6()[3]); ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif_default); if (!ip6_addr_isloopback(&ip6addr) && !ip6_addr_ismulticast(&ip6addr)) { @@ -400,7 +422,7 @@ bool SyslogService::transmit(const QueuedLogMessage & message) { tzm = diff < 0 ? (0 - diff) % 60 : diff % 60; } - if (udp_.beginPacket(host_, port_) != 1) { + if (udp_.beginPacket(ip_, port_) != 1) { last_transmit_ = uuid::get_uptime_ms(); return false; } diff --git a/lib/uuid-syslog/src/uuid/syslog.h b/lib/uuid-syslog/src/uuid/syslog.h index 70c94cdd1..7d1d3299e 100644 --- a/lib/uuid-syslog/src/uuid/syslog.h +++ b/lib/uuid-syslog/src/uuid/syslog.h @@ -129,6 +129,7 @@ class SyslogService : public uuid::log::Handler { * @since 2.0.0 */ void destination(IPAddress host, uint16_t port = DEFAULT_PORT); + void destination(const char * host, uint16_t port = DEFAULT_PORT); /** * Get local hostname. @@ -183,6 +184,20 @@ class SyslogService : public uuid::log::Handler { */ virtual void operator<<(std::shared_ptr message); + /** + * added MichaelDvP + * query status variables + */ + size_t queued() { + return log_messages_.size(); + } + bool started() { + return started_; + } + IPAddress ip() { + return ip_; + } + private: /** * Log message that has been queued. @@ -244,7 +259,8 @@ class SyslogService : public uuid::log::Handler { bool started_ = false; /*!< Flag to indicate that messages have started being transmitted. @since 1.0.0 */ WiFiUDP udp_; /*!< UDP client. @since 1.0.0 */ - IPAddress host_; /*!< Host to send messages to. @since 1.0.0 */ + IPAddress ip_; /*!< Host-IP to send messages to. @since 1.0.0 */ + std::string host_; /*!< Host to send messages to. */ uint16_t port_ = DEFAULT_PORT; /*!< Port to send messages to. @since 1.0.0 */ uint64_t last_transmit_ = 0; /*!< Last transmit time. @since 1.0.0 */ std::string hostname_{'-'}; /*!< Local hostname. @since 1.0.0 */ From 1cf938e16a5799ac14b7b8915fb15f0fe664fac2 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Mon, 26 Jul 2021 11:41:23 +0200 Subject: [PATCH 092/122] Syslog host setting as ipv4 or hostname --- interface/src/project/EMSESPSettingsForm.tsx | 13 ++-- interface/src/validators/index.ts | 1 + interface/src/validators/isIPv4.ts | 5 ++ src/system.cpp | 71 +++++++++----------- src/system.h | 3 +- src/web/WebSettingsService.cpp | 3 +- 6 files changed, 47 insertions(+), 49 deletions(-) create mode 100644 interface/src/validators/isIPv4.ts diff --git a/interface/src/project/EMSESPSettingsForm.tsx b/interface/src/project/EMSESPSettingsForm.tsx index 48b38998e..944e1bbb0 100644 --- a/interface/src/project/EMSESPSettingsForm.tsx +++ b/interface/src/project/EMSESPSettingsForm.tsx @@ -32,7 +32,7 @@ import { BlockFormControlLabel } from '../components'; -import { isIP, optional } from '../validators'; +import { isIPv4, optional, isHostname, or } from '../validators'; import { EMSESPSettings } from './EMSESPtypes'; @@ -55,7 +55,10 @@ class EMSESPSettingsForm extends Component { }; componentDidMount() { - ValidatorForm.addValidationRule('isOptionalIP', optional(isIP)); + ValidatorForm.addValidationRule( + 'isOptionalIPorHost', + optional(or(isIPv4, isHostname)) + ); } changeBoardProfile = (event: React.ChangeEvent) => { @@ -538,10 +541,10 @@ class EMSESPSettingsForm extends Component { > (syslog_level_))); shell.print(F(" ")); shell.printfln(F_(mark_interval_fmt), syslog_mark_interval_); + shell.printfln(F(" Queued: %d"),syslog_.queued()); } #endif @@ -939,6 +926,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json node["dallas reads"] = EMSESP::sensor_reads(); node["dallas fails"] = EMSESP::sensor_fails(); } + if (EMSESP::system_.syslog_enabled_) { + node["syslog_ip"] = syslog_.ip(); + node["syslog_started"] = syslog_.started(); + } } JsonArray devices2 = json.createNestedArray("Devices"); diff --git a/src/system.h b/src/system.h index 4e41a8cab..bed600cca 100644 --- a/src/system.h +++ b/src/system.h @@ -74,7 +74,6 @@ class System { void send_heartbeat(); void led_init(bool refresh); - void syslog_init(bool refresh); void adc_init(bool refresh); void network_init(bool refresh); void button_init(bool refresh); @@ -168,7 +167,7 @@ class System { std::string hostname_ = "ems-esp"; bool hide_led_; uint8_t led_gpio_; - bool syslog_enabled_; + bool syslog_enabled_ = false; bool analog_enabled_; bool low_clock_; String board_profile_; diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 3b9aa6b3c..f0d8061d9 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -231,8 +231,7 @@ void WebSettingsService::onUpdate() { } if (WebSettings::has_flags(WebSettings::ChangeFlags::SYSLOG)) { - EMSESP::system_.syslog_init(true); // reload settings - EMSESP::system_.syslog_start(); // re-start (or stop) + EMSESP::system_.syslog_start(); // re-start (or stop) } if (WebSettings::has_flags(WebSettings::ChangeFlags::ADC)) { From 3a866b1aea7eb94cdd3aa2b86fd8e11504b9348e Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 26 Jul 2021 11:46:26 +0200 Subject: [PATCH 093/122] fix browser warning --- interface/src/system/LogEventConsole.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/system/LogEventConsole.tsx b/interface/src/system/LogEventConsole.tsx index 4051181a4..f3d2cc05a 100644 --- a/interface/src/system/LogEventConsole.tsx +++ b/interface/src/system/LogEventConsole.tsx @@ -121,7 +121,7 @@ const LogEventConsole: FC = (props) => { return ( {events.map((e) => ( -
+
{e.t} {paddedLevelLabel(e.l)} {paddedIDLabel(e.i)} From ce2b2658adeff932e3e04fa2be0bff51519010dd Mon Sep 17 00:00:00 2001 From: proddy Date: Mon, 26 Jul 2021 11:49:42 +0200 Subject: [PATCH 094/122] mention wwactivated fix --- CHANGELOG_LATEST.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index c1fda2e59..072f20974 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -17,6 +17,7 @@ - Junkers thermostat shows mode as selected by set_mode - HA thermostat mode if bool-format: numbers is selected - Web UI System Log sometimes skipped a few log messages when watching real-time +- fix wwactivated [#89](https://github.com/emsesp/EMS-ESP32/issues/89) ## Changed From d5d75eee63959800e15f20f5df146b7fc5cfaecb Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 27 Jul 2021 21:42:37 +0200 Subject: [PATCH 095/122] remove comment --- interface/src/security/GenerateToken.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/security/GenerateToken.tsx b/interface/src/security/GenerateToken.tsx index 5904c25b8..6c6ee19b2 100644 --- a/interface/src/security/GenerateToken.tsx +++ b/interface/src/security/GenerateToken.tsx @@ -44,7 +44,6 @@ class GenerateToken extends React.Component< } }) .then((generatedToken) => { - // console.log(generatedToken); this.setState({ token: generatedToken.token }); }) .catch((error) => { From c5688ab632948464a1114f79c956c2ab595a58d7 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 27 Jul 2021 21:43:17 +0200 Subject: [PATCH 096/122] manually merge in official 0.9.0 version --- lib/async-mqtt-client/LICENSE | 2 +- lib/async-mqtt-client/src/AsyncMqttClient.cpp | 23 ++++++++++--------- lib/async-mqtt-client/src/AsyncMqttClient.hpp | 3 ++- .../src/AsyncMqttClient/DisconnectReasons.hpp | 2 +- .../AsyncMqttClient/Packets/Out/PubAck.cpp | 1 - 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/async-mqtt-client/LICENSE b/lib/async-mqtt-client/LICENSE index a6183c687..a3ee21720 100644 --- a/lib/async-mqtt-client/LICENSE +++ b/lib/async-mqtt-client/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Marvin Roger +Copyright (c) 2015-2021 Marvin Roger Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/async-mqtt-client/src/AsyncMqttClient.cpp b/lib/async-mqtt-client/src/AsyncMqttClient.cpp index 68ea0c5fd..98f0e5f29 100644 --- a/lib/async-mqtt-client/src/AsyncMqttClient.cpp +++ b/lib/async-mqtt-client/src/AsyncMqttClient.cpp @@ -6,7 +6,7 @@ AsyncMqttClient::AsyncMqttClient() , _tail(nullptr) , _sent(0) , _state(DISCONNECTED) -, _tlsBadFingerprint(false) +, _disconnectReason(AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) , _lastClientActivity(0) , _lastServerActivity(0) , _lastPingRequestTime(0) @@ -194,7 +194,6 @@ void AsyncMqttClient::_freeCurrentParsedPacket() { void AsyncMqttClient::_clear() { _lastPingRequestTime = 0; - _tlsBadFingerprint = false; _freeCurrentParsedPacket(); _clearQueue(true); // keep session data for now @@ -219,7 +218,7 @@ void AsyncMqttClient::_onConnect() { } if (!sslFoundFingerprint) { - _tlsBadFingerprint = true; + _disconnectReason = AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT; _client.close(true); return; } @@ -243,17 +242,10 @@ void AsyncMqttClient::_onConnect() { void AsyncMqttClient::_onDisconnect() { log_i("TCP disconn"); _state = DISCONNECTED; - AsyncMqttClientDisconnectReason reason; - - if (_tlsBadFingerprint) { - reason = AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT; - } else { - reason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED; - } _clear(); - for (auto callback : _onDisconnectUserCallbacks) callback(reason); + for (auto callback : _onDisconnectUserCallbacks) callback(_disconnectReason); } /* @@ -541,6 +533,8 @@ void AsyncMqttClient::_onConnAck(bool sessionPresent, uint8_t connectReturnCode) for (auto callback : _onConnectUserCallbacks) callback(sessionPresent); } else { // Callbacks are handled by the onDisconnect function which is called from the AsyncTcp lib + _disconnectReason = static_cast(connectReturnCode); + return; } _handleQueue(); // send any remaining data from continued session } @@ -710,6 +704,7 @@ void AsyncMqttClient::connect() { if (_state != DISCONNECTED) return; log_i("CONNECTING"); _state = CONNECTING; + _disconnectReason = AsyncMqttClientDisconnectReason::TCP_DISCONNECTED; // reset any previous _client.setRxTimeout(_keepAlive); @@ -770,6 +765,12 @@ uint16_t AsyncMqttClient::publish(const char* topic, uint8_t qos, bool retain, c return msg->packetId(); } +bool AsyncMqttClient::clearQueue() { + if (_state != DISCONNECTED) return false; + _clearQueue(false); + return true; +} + const char* AsyncMqttClient::getClientId() const { return _clientId; } diff --git a/lib/async-mqtt-client/src/AsyncMqttClient.hpp b/lib/async-mqtt-client/src/AsyncMqttClient.hpp index de42cd25a..fc2462601 100644 --- a/lib/async-mqtt-client/src/AsyncMqttClient.hpp +++ b/lib/async-mqtt-client/src/AsyncMqttClient.hpp @@ -82,6 +82,7 @@ class AsyncMqttClient { uint16_t subscribe(const char* topic, uint8_t qos); uint16_t unsubscribe(const char* topic); uint16_t publish(const char* topic, uint8_t qos, bool retain, const char* payload = nullptr, size_t length = 0, bool dup = false, uint16_t message_id = 0); + bool clearQueue(); // Not MQTT compliant! const char* getClientId() const; @@ -96,7 +97,7 @@ class AsyncMqttClient { DISCONNECTING, DISCONNECTED } _state; - bool _tlsBadFingerprint; + AsyncMqttClientDisconnectReason _disconnectReason; uint32_t _lastClientActivity; uint32_t _lastServerActivity; uint32_t _lastPingRequestTime; diff --git a/lib/async-mqtt-client/src/AsyncMqttClient/DisconnectReasons.hpp b/lib/async-mqtt-client/src/AsyncMqttClient/DisconnectReasons.hpp index f4cbda8af..a15114000 100644 --- a/lib/async-mqtt-client/src/AsyncMqttClient/DisconnectReasons.hpp +++ b/lib/async-mqtt-client/src/AsyncMqttClient/DisconnectReasons.hpp @@ -1,6 +1,6 @@ #pragma once -enum class AsyncMqttClientDisconnectReason : int8_t { +enum class AsyncMqttClientDisconnectReason : uint8_t { TCP_DISCONNECTED = 0, MQTT_UNACCEPTABLE_PROTOCOL_VERSION = 1, diff --git a/lib/async-mqtt-client/src/AsyncMqttClient/Packets/Out/PubAck.cpp b/lib/async-mqtt-client/src/AsyncMqttClient/Packets/Out/PubAck.cpp index 3352bd0d2..634607ba5 100644 --- a/lib/async-mqtt-client/src/AsyncMqttClient/Packets/Out/PubAck.cpp +++ b/lib/async-mqtt-client/src/AsyncMqttClient/Packets/Out/PubAck.cpp @@ -10,7 +10,6 @@ PubAckOutPacket::PubAckOutPacket(PendingAck pendingAck) { _packetId = pendingAck.packetId; _data[2] = pendingAck.packetId >> 8; _data[3] = pendingAck.packetId & 0xFF; - // _released = false; if (packetType() == AsyncMqttClientInternals::PacketType.PUBREL || packetType() == AsyncMqttClientInternals::PacketType.PUBREC) { _released = false; From dc8c322b4249cf211334d98c218790c6e7ea4fd2 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 27 Jul 2021 21:43:36 +0200 Subject: [PATCH 097/122] auto-formatting --- src/system.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/system.cpp b/src/system.cpp index f19582ea5..e8de1b3af 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -747,17 +747,17 @@ void System::show_system(uuid::console::Shell & shell) { if (!syslog_enabled_) { shell.printfln(F("Syslog: disabled")); } else { - shell.printfln(F("Syslog: %s"),syslog_.started() ? "started" : "stopped"); + shell.printfln(F("Syslog: %s"), syslog_.started() ? "started" : "stopped"); shell.print(F(" ")); shell.printfln(F_(host_fmt), !syslog_host_.isEmpty() ? syslog_host_.c_str() : uuid::read_flash_string(F_(unset)).c_str()); - shell.printfln(F(" IP: %s"),uuid::printable_to_string(syslog_.ip()).c_str()); + shell.printfln(F(" IP: %s"), uuid::printable_to_string(syslog_.ip()).c_str()); shell.print(F(" ")); shell.printfln(F_(port_fmt), syslog_port_); shell.print(F(" ")); shell.printfln(F_(log_level_fmt), uuid::log::format_level_lowercase(static_cast(syslog_level_))); shell.print(F(" ")); shell.printfln(F_(mark_interval_fmt), syslog_mark_interval_); - shell.printfln(F(" Queued: %d"),syslog_.queued()); + shell.printfln(F(" Queued: %d"), syslog_.queued()); } #endif From e809ed3743155d6d3fca1d399e93fde27d862268 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 27 Jul 2021 21:44:12 +0200 Subject: [PATCH 098/122] add max messages and make web log dynamic - #71 --- interface/src/system/LogEventConsole.tsx | 31 ++-- interface/src/system/LogEventController.tsx | 164 ++++++++++++++++++-- interface/src/system/LogEventForm.tsx | 107 ------------- mock-api/server.js | 20 ++- src/version.h | 2 +- src/web/WebLogService.cpp | 61 ++++---- src/web/WebLogService.h | 10 +- 7 files changed, 223 insertions(+), 172 deletions(-) delete mode 100644 interface/src/system/LogEventForm.tsx diff --git a/interface/src/system/LogEventConsole.tsx b/interface/src/system/LogEventConsole.tsx index f3d2cc05a..43a6df419 100644 --- a/interface/src/system/LogEventConsole.tsx +++ b/interface/src/system/LogEventConsole.tsx @@ -6,6 +6,8 @@ import { useWindowSize } from '../components'; interface LogEventConsoleProps { events: LogEvent[]; + compact: boolean; + level: number; } interface Offsets { @@ -63,7 +65,9 @@ const useStyles = makeStyles((theme: Theme) => ({ const LogEventConsole: FC = (props) => { useWindowSize(); const classes = useStyles({ topOffset, leftOffset }); - const { events } = props; + const { events, compact, level } = props; + + const filter_events = events.filter((e) => e.l <= level); const styleLevel = (level: LogLevel) => { switch (level) { @@ -103,29 +107,34 @@ const LogEventConsole: FC = (props) => { } }; - const paddedLevelLabel = (level: LogLevel) => { + const paddedLevelLabel = (level: LogLevel, compact: boolean) => { const label = levelLabel(level); - return label.padStart(8, '\xa0'); + return compact ? ' ' + label[0] : label.padStart(8, '\xa0'); }; - const paddedNameLabel = (name: string) => { + const paddedNameLabel = (name: string, compact: boolean) => { const label = '[' + name + ']'; - return label.padEnd(12, '\xa0'); + return compact ? label : label.padEnd(12, '\xa0'); }; - const paddedIDLabel = (id: number) => { + const paddedIDLabel = (id: number, compact: boolean) => { const label = id + ':'; - return label.padEnd(7, '\xa0'); + return compact ? label : label.padEnd(7, '\xa0'); }; return ( - {events.map((e) => ( + {filter_events.map((e) => (
{e.t} - {paddedLevelLabel(e.l)} - {paddedIDLabel(e.i)} - {paddedNameLabel(e.n)} + {compact && {paddedLevelLabel(e.l, compact)} } + {!compact && ( + + {paddedLevelLabel(e.l, compact)}{' '} + + )} + {paddedIDLabel(e.i, compact)} + {paddedNameLabel(e.n, compact)} {e.m}
))} diff --git a/interface/src/system/LogEventController.tsx b/interface/src/system/LogEventController.tsx index cdeffafb1..f34fed1c2 100644 --- a/interface/src/system/LogEventController.tsx +++ b/interface/src/system/LogEventController.tsx @@ -3,19 +3,27 @@ import { Component } from 'react'; import { restController, RestControllerProps, - RestFormLoader, - SectionContent + SectionContent, + BlockFormControlLabel } from '../components'; -import { addAccessTokenParameter } from '../authentication'; +import { + ValidatorForm, + SelectValidator +} from 'react-material-ui-form-validator'; + +import { Grid, Slider, FormLabel, Checkbox, MenuItem } from '@material-ui/core'; + +import { + addAccessTokenParameter, + redirectingAuthorizedFetch +} from '../authentication'; import { ENDPOINT_ROOT, EVENT_SOURCE_ROOT } from '../api'; export const FETCH_LOG_ENDPOINT = ENDPOINT_ROOT + 'fetchLog'; export const LOG_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'logSettings'; - export const LOG_EVENT_EVENT_SOURCE_URL = EVENT_SOURCE_ROOT + 'log'; -import LogEventForm from './LogEventForm'; import LogEventConsole from './LogEventConsole'; import { LogEvent, LogSettings } from './types'; @@ -26,6 +34,9 @@ const decoder = new Decoder(); interface LogEventControllerState { eventSource?: EventSource; events: LogEvent[]; + compact: boolean; + level: number; + max_messages: number; } type LogEventControllerProps = RestControllerProps; @@ -40,12 +51,15 @@ class LogEventController extends Component< constructor(props: LogEventControllerProps) { super(props); this.state = { - events: [] + events: [], + compact: false, + level: 6, + max_messages: 25 }; } componentDidMount() { - this.props.loadData(); + this.fetchValues(); this.fetchLog(); this.configureEventSource(); } @@ -59,6 +73,15 @@ class LogEventController extends Component< } } + changeCompact = ( + event: React.ChangeEvent, + checked: boolean + ) => { + this.setState({ + compact: checked + }); + }; + fetchLog = () => { fetch(FETCH_LOG_ENDPOINT) .then((response) => { @@ -78,6 +101,25 @@ class LogEventController extends Component< }); }; + fetchValues = () => { + redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT) + .then((response) => { + if (response.status === 200) { + return response.json(); + } + throw Error('Unexpected status code: ' + response.status); + }) + .then((json) => { + this.setState({ level: json.level, max_messages: json.max_messages }); + }) + .catch((error) => { + const errorMessage = error.message || 'Unknown error'; + this.props.enqueueSnackbar('Problem fetching: ' + errorMessage, { + variant: 'error' + }); + }); + }; + configureEventSource = () => { this.eventSource = new EventSource( addAccessTokenParameter(LOG_EVENT_EVENT_SOURCE_URL) @@ -102,14 +144,114 @@ class LogEventController extends Component< } }; + changeMaxMessages = ( + event: React.ChangeEvent<{}>, + value: number | number[] + ) => { + this.setState({ + max_messages: value as number + }); + this.send_data(this.state.level, value as number); + }; + + changeLevel = (event: React.ChangeEvent) => { + this.setState({ + level: parseInt(event.target.value) + }); + this.send_data(parseInt(event.target.value), this.state.max_messages); + }; + + send_data = (level: number, max_messages: number) => { + redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ + level: level, + max_messages: max_messages + }), + headers: { + 'Content-Type': 'application/json' + } + }) + .then((response) => { + if (response.status !== 200) { + throw Error('Unexpected response code: ' + response.status); + } + }) + .catch((error) => { + this.props.enqueueSnackbar( + error.message || 'Problem applying log settings', + { variant: 'warning' } + ); + }); + }; + render() { + const { saveData } = this.props; return ( - } + + + + + ERROR + WARNING + NOTICE + INFO + DEBUG + ALL + + + + Buffer size + + + + + } + label="Compact Layout" + /> + + + + + - ); } diff --git a/interface/src/system/LogEventForm.tsx b/interface/src/system/LogEventForm.tsx deleted file mode 100644 index 9c69db7ef..000000000 --- a/interface/src/system/LogEventForm.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Component } from 'react'; - -import { - ValidatorForm, - SelectValidator -} from 'react-material-ui-form-validator'; - -import { Typography, Grid } from '@material-ui/core'; - -import MenuItem from '@material-ui/core/MenuItem'; - -import { - redirectingAuthorizedFetch, - withAuthenticatedContext, - AuthenticatedContextProps -} from '../authentication'; - -import { RestFormProps } from '../components'; -import { LogSettings } from './types'; - -import { ENDPOINT_ROOT } from '../api'; -export const LOG_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'logSettings'; - -type LogEventFormProps = AuthenticatedContextProps & RestFormProps; - -class LogEventForm extends Component { - changeLevel = (event: React.ChangeEvent) => { - const { data, setData } = this.props; - setData({ - ...data, - level: parseInt(event.target.value) - }); - - redirectingAuthorizedFetch(LOG_SETTINGS_ENDPOINT, { - method: 'POST', - body: JSON.stringify({ level: event.target.value }), - headers: { - 'Content-Type': 'application/json' - } - }) - .then((response) => { - if (response.status === 200) { - return response.json(); - } - throw Error('Unexpected response code: ' + response.status); - }) - .then((json) => { - this.props.enqueueSnackbar('Log settings changed', { - variant: 'success' - }); - setData({ - ...data, - level: json.level - }); - }) - .catch((error) => { - this.props.enqueueSnackbar( - error.message || 'Problem changing log settings', - { variant: 'warning' } - ); - }); - }; - - render() { - const { data, saveData } = this.props; - return ( - - - - - OFF - ERROR - WARNING - NOTICE - INFO - DEBUG - ALL - - - - - -  (the last {data.max_messages} messages are retained and - all new log events are shown in real time below) - - - - - - ); - } -} - -export default withAuthenticatedContext(LogEventForm); diff --git a/mock-api/server.js b/mock-api/server.js index 27edccf69..46b2600a2 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -18,7 +18,7 @@ const ES_ENDPOINT_ROOT = '/es/' const LOG_SETTINGS_ENDPOINT = REST_ENDPOINT_ROOT + 'logSettings' const log_settings = { level: 6, - max_messages: 30, + max_messages: 50, } const FETCH_LOG_ENDPOINT = REST_ENDPOINT_ROOT + 'fetchLog' @@ -770,14 +770,22 @@ app.get(FETCH_LOG_ENDPOINT, (req, res) => { res.end(null, 'binary') }) app.get(LOG_SETTINGS_ENDPOINT, (req, res) => { + console.log( + 'Fetching log settings ' + + log_settings.level + + ',' + + log_settings.max_messages, + ) res.json(log_settings) }) app.post(LOG_SETTINGS_ENDPOINT, (req, res) => { - console.log('New log level is ' + req.body.level) - const data = { - level: req.body.level, - } - res.json(data) + console.log( + 'Setting new level=' + + req.body.level + + ' max_messages=' + + req.body.max_messages, + ) + res.sendStatus(200) }) // NETWORK diff --git a/src/version.h b/src/version.h index 838811571..1dc1c7f43 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.2.0b0" +#define EMSESP_APP_VERSION "3.2.0b1" diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 0ce55aa47..5f4f6d652 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -23,21 +23,21 @@ using namespace std::placeholders; namespace emsesp { WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager) - : _events(EVENT_SOURCE_LOG_PATH) - , _setLevel(LOG_SETTINGS_PATH, std::bind(&WebLogService::setLevel, this, _1, _2), 256) { // for POSTS + : events_(EVENT_SOURCE_LOG_PATH) + , setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2), 256) { // for POSTS - _events.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); - server->addHandler(&_events); + events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN)); + server->addHandler(&events_); server->on(EVENT_SOURCE_LOG_PATH, HTTP_GET, std::bind(&WebLogService::forbidden, this, _1)); // for bring back the whole log server->on(FETCH_LOG_PATH, HTTP_GET, std::bind(&WebLogService::fetchLog, this, _1)); // get when page is loaded - server->on(LOG_SETTINGS_PATH, HTTP_GET, std::bind(&WebLogService::getLevel, this, _1)); + server->on(LOG_SETTINGS_PATH, HTTP_GET, std::bind(&WebLogService::getValues, this, _1)); // for setting a level - server->addHandler(&_setLevel); + server->addHandler(&setValues_); } void WebLogService::forbidden(AsyncWebServerRequest * request) { @@ -90,7 +90,7 @@ void WebLogService::operator<<(std::shared_ptr message) { } void WebLogService::loop() { - if (!_events.count() || log_messages_.empty()) { + if (!events_.count() || log_messages_.empty()) { return; } @@ -144,12 +144,12 @@ void WebLogService::transmit(const QueuedLogMessage & message) { char * buffer = new char[len + 1]; if (buffer) { serializeJson(jsonDocument, buffer, len + 1); - _events.send(buffer, "message", millis()); + events_.send(buffer, "message", millis()); } delete[] buffer; } -// send the current log buffer to the API +// send the complete log buffer to the API, filtering on log level void WebLogService::fetchLog(AsyncWebServerRequest * request) { MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); // 8kb buffer JsonObject root = response->getRoot(); @@ -157,15 +157,17 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) { JsonArray log = root.createNestedArray("events"); for (const auto & msg : log_messages_) { - JsonObject logEvent = log.createNestedObject(); - auto message = std::move(msg); - char time_string[25]; + if (msg.content_->level <= log_level()) { + JsonObject logEvent = log.createNestedObject(); + auto message = std::move(msg); + char time_string[25]; - logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); - logEvent["l"] = message.content_->level; - logEvent["i"] = message.id_; - logEvent["n"] = message.content_->name; - logEvent["m"] = message.content_->text; + logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); + logEvent["l"] = message.content_->level; + logEvent["i"] = message.id_; + logEvent["n"] = message.content_->name; + logEvent["m"] = message.content_->text; + } } log_message_id_tail_ = log_messages_.back().id_; @@ -174,28 +176,25 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) { request->send(response); } -// sets the level after a POST -void WebLogService::setLevel(AsyncWebServerRequest * request, JsonVariant & json) { +// sets the values like level after a POST +void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) { if (not json.is()) { return; } - auto && body = json.as(); + + auto && body = json.as(); + uuid::log::Level level = body["level"]; log_level(level); - if (level == uuid::log::Level::OFF) { - log_messages_.clear(); - } - // send the value back - AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL); - JsonObject root = response->getRoot(); - root["level"] = log_level(); - response->setLength(); - request->send(response); + uint8_t max_messages = body["max_messages"]; + maximum_log_messages(max_messages); + + request->send(200); // OK } -// return the current log level after a GET -void WebLogService::getLevel(AsyncWebServerRequest * request) { +// return the current value settings after a GET +void WebLogService::getValues(AsyncWebServerRequest * request) { AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL); JsonObject root = response->getRoot(); root["level"] = log_level(); diff --git a/src/web/WebLogService.h b/src/web/WebLogService.h index 4c3401a7e..885739e9d 100644 --- a/src/web/WebLogService.h +++ b/src/web/WebLogService.h @@ -34,7 +34,7 @@ namespace emsesp { class WebLogService : public uuid::log::Handler { public: - static constexpr size_t MAX_LOG_MESSAGES = 30; + static constexpr size_t MAX_LOG_MESSAGES = 50; static constexpr size_t REFRESH_SYNC = 200; WebLogService(AsyncWebServer * server, SecurityManager * securityManager); @@ -49,7 +49,7 @@ class WebLogService : public uuid::log::Handler { virtual void operator<<(std::shared_ptr message); private: - AsyncEventSource _events; + AsyncEventSource events_; class QueuedLogMessage { public: @@ -64,12 +64,12 @@ class WebLogService : public uuid::log::Handler { void forbidden(AsyncWebServerRequest * request); void transmit(const QueuedLogMessage & message); void fetchLog(AsyncWebServerRequest * request); - void getLevel(AsyncWebServerRequest * request); + void getValues(AsyncWebServerRequest * request); char * messagetime(char * out, const uint64_t t); - void setLevel(AsyncWebServerRequest * request, JsonVariant & json); - AsyncCallbackJsonWebHandler _setLevel; // for POSTs + void setValues(AsyncWebServerRequest * request, JsonVariant & json); + AsyncCallbackJsonWebHandler setValues_; // for POSTs uint64_t last_transmit_ = 0; // Last transmit time size_t maximum_log_messages_ = MAX_LOG_MESSAGES; // Maximum number of log messages to buffer before they are output From 362fead7e87cc920a860fd88044ee29b3d85d9d4 Mon Sep 17 00:00:00 2001 From: proddy Date: Tue, 27 Jul 2021 21:48:42 +0200 Subject: [PATCH 099/122] make standalone compile --- src/system.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/system.cpp b/src/system.cpp index e8de1b3af..4f6eba475 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -926,10 +926,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json node["dallas reads"] = EMSESP::sensor_reads(); node["dallas fails"] = EMSESP::sensor_fails(); } +#ifndef EMSESP_STANDALONE if (EMSESP::system_.syslog_enabled_) { node["syslog_ip"] = syslog_.ip(); node["syslog_started"] = syslog_.started(); } +#endif } JsonArray devices2 = json.createNestedArray("Devices"); From 39fef489150b47e97192f88f67a80994c509c5c3 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 28 Jul 2021 10:06:51 +0200 Subject: [PATCH 100/122] Update ArduinoJson to 6.18.3 --- CHANGELOG_LATEST.md | 3 +- lib/ArduinoJson/CHANGELOG.md | 25 + lib/ArduinoJson/README.md | 26 +- lib/ArduinoJson/src/ArduinoJson.hpp | 10 +- .../src/ArduinoJson/Array/ArrayRef.hpp | 16 +- .../src/ArduinoJson/Array/ElementProxy.hpp | 10 +- .../ArduinoJson/Collection/CollectionImpl.hpp | 6 +- .../src/ArduinoJson/Configuration.hpp | 262 ++--- .../Deserialization/DeserializationError.hpp | 2 +- .../ArduinoJson/Deserialization/Reader.hpp | 8 +- .../src/ArduinoJson/Document/JsonDocument.hpp | 12 +- .../src/ArduinoJson/Json/Latch.hpp | 3 +- .../src/ArduinoJson/Json/TextFormatter.hpp | 1 - .../src/ArduinoJson/Json/Utf16.hpp | 16 +- .../src/ArduinoJson/Memory/MemoryPool.hpp | 21 +- .../MsgPack/MsgPackDeserializer.hpp | 940 +++++++++--------- lib/ArduinoJson/src/ArduinoJson/Namespace.hpp | 24 +- .../src/ArduinoJson/Numbers/Integer.hpp | 12 +- .../src/ArduinoJson/Numbers/convertNumber.hpp | 36 +- .../src/ArduinoJson/Object/MemberProxy.hpp | 10 +- .../src/ArduinoJson/Object/ObjectRef.hpp | 11 +- .../src/ArduinoJson/Polyfills/assert.hpp | 6 +- .../src/ArduinoJson/Polyfills/attributes.hpp | 54 +- .../src/ArduinoJson/Polyfills/limits.hpp | 6 +- .../ArduinoJson/Polyfills/static_array.hpp | 32 +- .../src/ArduinoJson/Polyfills/type_traits.hpp | 1 + .../Polyfills/type_traits/is_convertible.hpp | 6 +- .../type_traits/is_floating_point.hpp | 15 +- .../Polyfills/type_traits/is_integral.hpp | 36 +- .../Polyfills/type_traits/is_signed.hpp | 45 +- .../Polyfills/type_traits/is_unsigned.hpp | 37 +- .../Polyfills/type_traits/make_void.hpp | 14 + .../Polyfills/type_traits/remove_cv.hpp | 27 + .../src/ArduinoJson/Serialization/Writer.hpp | 8 +- .../Writers/ArduinoStringWriter.hpp | 15 +- .../StringStorage/StringCopier.hpp | 8 +- .../{ => Adapters}/ArduinoStringAdapter.hpp | 28 +- .../{ => Adapters}/ConstRamStringAdapter.hpp | 27 +- .../{ => Adapters}/FlashStringAdapter.hpp | 22 +- .../Strings/Adapters/JsonStringAdapter.hpp | 27 + .../Strings/Adapters/RamStringAdapter.hpp | 29 + .../SizedFlashStringAdapter.hpp | 20 +- .../{ => Adapters}/SizedRamStringAdapter.hpp | 20 +- .../Strings/Adapters/StdStringAdapter.hpp | 46 + .../Strings/Adapters/StringViewAdapter.hpp | 44 + .../Strings/FlashStringIterator.hpp | 44 - .../src/ArduinoJson/Strings/IsString.hpp | 18 - .../ArduinoJson/Strings/IsWriteableString.hpp | 4 +- .../ArduinoJson/Strings/RamStringAdapter.hpp | 43 - .../ArduinoJson/Strings/StdStringAdapter.hpp | 65 -- .../src/ArduinoJson/Strings/String.hpp | 25 - .../src/ArduinoJson/Strings/StringAdapter.hpp | 32 + .../ArduinoJson/Strings/StringAdapters.hpp | 19 +- .../src/ArduinoJson/Variant/Converter.hpp | 5 + .../src/ArduinoJson/Variant/ConverterImpl.hpp | 73 +- .../ArduinoJson/Variant/VariantCompare.hpp | 2 +- .../src/ArduinoJson/Variant/VariantData.hpp | 23 +- .../src/ArduinoJson/Variant/VariantImpl.hpp | 5 + .../src/ArduinoJson/Variant/VariantRef.hpp | 26 +- lib/ArduinoJson/src/ArduinoJson/version.hpp | 4 +- lib_standalone/WString.h | 8 +- 61 files changed, 1259 insertions(+), 1164 deletions(-) create mode 100644 lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp create mode 100644 lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp rename lib/ArduinoJson/src/ArduinoJson/Strings/{ => Adapters}/ArduinoStringAdapter.hpp (63%) rename lib/ArduinoJson/src/ArduinoJson/Strings/{ => Adapters}/ConstRamStringAdapter.hpp (61%) rename lib/ArduinoJson/src/ArduinoJson/Strings/{ => Adapters}/FlashStringAdapter.hpp (62%) create mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonStringAdapter.hpp create mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamStringAdapter.hpp rename lib/ArduinoJson/src/ArduinoJson/Strings/{ => Adapters}/SizedFlashStringAdapter.hpp (61%) rename lib/ArduinoJson/src/ArduinoJson/Strings/{ => Adapters}/SizedRamStringAdapter.hpp (61%) create mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdStringAdapter.hpp create mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringViewAdapter.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/FlashStringIterator.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/RamStringAdapter.hpp delete mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/StdStringAdapter.hpp create mode 100644 lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 072f20974..b933294b5 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -28,4 +28,5 @@ - maintenance settings for time/date as extra setting - move api/mqtt formats to `settings`, add `enum format` - UI improvements for editing Dallas Sensor details -- Rest GET commands can also require authentication (via bearer access token) for better security +- RESTful GET commands can also require authentication (via bearer access token) for better security +- Updated AsyncMqttClient to 0.9.0 and ArduinoJson to 6.18.3 diff --git a/lib/ArduinoJson/CHANGELOG.md b/lib/ArduinoJson/CHANGELOG.md index f3866b3c8..a1bbc5d24 100644 --- a/lib/ArduinoJson/CHANGELOG.md +++ b/lib/ArduinoJson/CHANGELOG.md @@ -1,6 +1,31 @@ ArduinoJson: change log ======================= +v6.18.3 (2021-07-27) +------- + +* Changed return type of `convertToJson()` and `Converter::toJson()` to `void` +* Added `as()` and `is()` + +v6.18.2 (2021-07-19) +------- + +* Removed a symlink because the Arduino Library Specification forbids it + +v6.18.1 (2021-07-03) +------- + +* Fixed support for `volatile float` and `volatile double` (issue #1557) +* Fixed error `[Pe070]: incomplete type is not allowed` on IAR (issue #1560) +* Fixed `serializeJson(doc, String)` when allocation fails (issue #1572) +* Fixed clang-tidy warnings (issue #1574, PR #1577 by @armandas) +* Added fake class `InvalidConversion` to easily identify invalid conversions (issue #1585) +* Added support for `std::string_view` (issue #1578, PR #1554 by @0xFEEDC0DE64) +* Fixed warning `definition of implicit copy constructor for 'MsgPackDeserializer' is deprecated because it has a user-declared copy assignment operator` +* Added `JsonArray::clear()` (issue #1597) +* Fixed `JsonVariant::as()` (issue #1601) +* Added support for ESP-IDF component build (PR #1562 by @qt1, PR #1599 by @andreaskuster) + v6.18.0 (2021-05-05) ------- diff --git a/lib/ArduinoJson/README.md b/lib/ArduinoJson/README.md index c30028435..a9dfbc7f5 100644 --- a/lib/ArduinoJson/README.md +++ b/lib/ArduinoJson/README.md @@ -2,7 +2,7 @@ --- -[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.18.0)](https://www.ardu-badge.com/ArduinoJson/6.18.0) +[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.18.3)](https://www.ardu-badge.com/ArduinoJson/6.18.3) [![Continuous Integration](https://github.com/bblanchon/ArduinoJson/workflows/Continuous%20Integration/badge.svg?branch=6.x)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x) [![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson) @@ -33,15 +33,15 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). * [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme) * Deduplicates strings * Versatile - * [Supports custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme) - * Supports [Arduino's `String`](https://arduinojson.org/v6/api/config/enable_arduino_string/?utm_source=github&utm_medium=readme) and [STL's `std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme) - * Supports [Arduino's `Stream`](https://arduinojson.org/v6/api/config/enable_arduino_stream/?utm_source=github&utm_medium=readme) and [STL's `std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme) - * [Supports Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme) + * Supports [custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme) + * Supports [`String`](https://arduinojson.org/v6/api/config/enable_arduino_string/?utm_source=github&utm_medium=readme), [`std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme) and [`std::string_view`](https://arduinojson.org/v6/api/config/enable_string_view/?utm_source=github&utm_medium=readme) + * Supports [`Stream`](https://arduinojson.org/v6/api/config/enable_arduino_stream/?utm_source=github&utm_medium=readme) and [`std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme) + * Supports [Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme) * Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme#custom-writer) - * Supports custom converters + * Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/?utm_source=github&utm_medium=readme) * Portable * Usable on any C++ project (not limited to Arduino) - * Compatible with C++98 + * Compatible with C++98, C++11, C++14 and C++17 * Zero warnings with `-Wall -Wextra -pedantic` and `/W4` * [Header-only library](https://en.wikipedia.org/wiki/Header-only) * Works with virtually any board @@ -81,15 +81,17 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). * [GCC 4.4, 4.6, 4.7, 4.8, 4.9, 5, 6, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22) * [Clang 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22) * [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson) + * Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/) * Well documented * [Tutorials](https://arduinojson.org/v6/doc/deserialization/?utm_source=github&utm_medium=readme) * [Examples](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme) * [How-tos](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme) * [FAQ](https://arduinojson.org/v6/faq/?utm_source=github&utm_medium=readme) + * [Troubleshooter](https://arduinojson.org/v6/troubleshooter/?utm_source=github&utm_medium=readme) * [Book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme) * [Changelog](CHANGELOG.md) * Vibrant user community - * Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories) and [PlatformIO](https://platformio.org/lib/search) + * Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories) * [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson) * [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed) @@ -132,9 +134,11 @@ serializeJson(doc, Serial); See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme) -## Support the project +## Support the project ❤️ -Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)! +Do you like this library? +Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)! What? You don't like it but you *love* it? -We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time. +You can support the project by [purchasing my book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme). +Alternatively, you can make a recurring donation via [GitHub Sponsors](https://github.com/sponsors/bblanchon). diff --git a/lib/ArduinoJson/src/ArduinoJson.hpp b/lib/ArduinoJson/src/ArduinoJson.hpp index 28d42eef5..d16ca08cd 100644 --- a/lib/ArduinoJson/src/ArduinoJson.hpp +++ b/lib/ArduinoJson/src/ArduinoJson.hpp @@ -7,11 +7,11 @@ #include "ArduinoJson/Configuration.hpp" #if !ARDUINOJSON_DEBUG -#ifdef __clang__ -#pragma clang system_header -#elif defined __GNUC__ -#pragma GCC system_header -#endif +# ifdef __clang__ +# pragma clang system_header +# elif defined __GNUC__ +# pragma GCC system_header +# endif #endif #include "ArduinoJson/Array/ArrayRef.hpp" diff --git a/lib/ArduinoJson/src/ArduinoJson/Array/ArrayRef.hpp b/lib/ArduinoJson/src/ArduinoJson/Array/ArrayRef.hpp index a991db0d7..4f8d0c631 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Array/ArrayRef.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Array/ArrayRef.hpp @@ -161,14 +161,20 @@ class ArrayRef : public ArrayRefBase, _data->removeElement(index); } + void clear() const { + if (!_data) + return; + _data->clear(); + } + private: MemoryPool* _pool; }; template <> struct Converter { - static bool toJson(VariantConstRef src, VariantRef dst) { - return variantCopyFrom(getData(dst), getData(src), getPool(dst)); + static void toJson(VariantConstRef src, VariantRef dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); } static ArrayConstRef fromJson(VariantConstRef src) { @@ -183,8 +189,8 @@ struct Converter { template <> struct Converter { - static bool toJson(VariantConstRef src, VariantRef dst) { - return variantCopyFrom(getData(dst), getData(src), getPool(dst)); + static void toJson(VariantConstRef src, VariantRef dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); } static ArrayRef fromJson(VariantRef src) { @@ -193,6 +199,8 @@ struct Converter { return ArrayRef(pool, data != 0 ? data->asArray() : 0); } + static InvalidConversion fromJson(VariantConstRef); + static bool checkJson(VariantConstRef) { return false; } diff --git a/lib/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp b/lib/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp index c6062e492..c1016eb01 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp @@ -10,8 +10,8 @@ #include #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4522) +# pragma warning(push) +# pragma warning(disable : 4522) #endif namespace ARDUINOJSON_NAMESPACE { @@ -178,8 +178,8 @@ class ElementProxy : public VariantOperators >, return _array.getOrAddElement(_index); } - friend bool convertToJson(const this_type& src, VariantRef dst) { - return dst.set(src.getUpstreamElement()); + friend void convertToJson(const this_type& src, VariantRef dst) { + dst.set(src.getUpstreamElement()); } TArray _array; @@ -189,5 +189,5 @@ class ElementProxy : public VariantOperators >, } // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER -#pragma warning(pop) +# pragma warning(pop) #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp b/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp index 49a24beed..f814bcdf2 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp @@ -62,9 +62,9 @@ inline bool CollectionData::copyFrom(const CollectionData& src, VariantData* var; if (s->key() != 0) { if (s->ownsKey()) - var = addMember(RamStringAdapter(s->key()), pool); + var = addMember(adaptString(const_cast(s->key())), pool); else - var = addMember(ConstRamStringAdapter(s->key()), pool); + var = addMember(adaptString(s->key()), pool); } else { var = addElement(pool); } @@ -107,7 +107,7 @@ template inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const { VariantSlot* slot = _head; while (slot) { - if (key.equals(slot->key())) + if (key.compare(slot->key()) == 0) break; slot = slot->next(); } diff --git a/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp b/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp index 0c0d4c489..332abb6a7 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Configuration.hpp @@ -5,251 +5,269 @@ #pragma once #if __cplusplus >= 201103L -#define ARDUINOJSON_HAS_LONG_LONG 1 -#define ARDUINOJSON_HAS_NULLPTR 1 -#define ARDUINOJSON_HAS_RVALUE_REFERENCES 1 +# define ARDUINOJSON_HAS_LONG_LONG 1 +# define ARDUINOJSON_HAS_RVALUE_REFERENCES 1 #else -#define ARDUINOJSON_HAS_LONG_LONG 0 -#define ARDUINOJSON_HAS_NULLPTR 0 -#define ARDUINOJSON_HAS_RVALUE_REFERENCES 0 +# define ARDUINOJSON_HAS_LONG_LONG 0 +# define ARDUINOJSON_HAS_RVALUE_REFERENCES 0 +#endif + +#ifndef ARDUINOJSON_HAS_NULLPTR +# if __cplusplus >= 201103L +# define ARDUINOJSON_HAS_NULLPTR 1 +# else +# define ARDUINOJSON_HAS_NULLPTR 0 +# endif #endif #if defined(_MSC_VER) && !ARDUINOJSON_HAS_LONG_LONG -#define ARDUINOJSON_HAS_INT64 1 +# define ARDUINOJSON_HAS_INT64 1 #else -#define ARDUINOJSON_HAS_INT64 0 +# define ARDUINOJSON_HAS_INT64 0 #endif // Small or big machine? #ifndef ARDUINOJSON_EMBEDDED_MODE -#if defined(ARDUINO) /* Arduino*/ \ - || defined(__IAR_SYSTEMS_ICC__) /* IAR Embedded Workbench */ \ - || defined(__XC) /* MPLAB XC compiler */ \ - || defined(__ARMCC_VERSION) /* Keil ARM Compiler */ \ - || defined(__AVR) /* Atmel AVR8/GNU C Compiler */ -#define ARDUINOJSON_EMBEDDED_MODE 1 -#else -#define ARDUINOJSON_EMBEDDED_MODE 0 -#endif +# if defined(ARDUINO) /* Arduino*/ \ + || defined(__IAR_SYSTEMS_ICC__) /* IAR Embedded Workbench */ \ + || defined(__XC) /* MPLAB XC compiler */ \ + || defined(__ARMCC_VERSION) /* Keil ARM Compiler */ \ + || defined(__AVR) /* Atmel AVR8/GNU C Compiler */ +# define ARDUINOJSON_EMBEDDED_MODE 1 +# else +# define ARDUINOJSON_EMBEDDED_MODE 0 +# endif #endif // Auto enable std::stream if the right headers are here and no conflicting // macro is defined #if !defined(ARDUINOJSON_ENABLE_STD_STREAM) && defined(__has_include) -#if __has_include() && \ +# if __has_include() && \ __has_include() && \ !defined(min) && \ !defined(max) -#define ARDUINOJSON_ENABLE_STD_STREAM 1 -#else -#define ARDUINOJSON_ENABLE_STD_STREAM 0 -#endif +# define ARDUINOJSON_ENABLE_STD_STREAM 1 +# else +# define ARDUINOJSON_ENABLE_STD_STREAM 0 +# endif #endif // Auto enable std::string if the right header is here and no conflicting // macro is defined #if !defined(ARDUINOJSON_ENABLE_STD_STRING) && defined(__has_include) -#if __has_include() && !defined(min) && !defined(max) -#define ARDUINOJSON_ENABLE_STD_STRING 1 -#else -#define ARDUINOJSON_ENABLE_STD_STRING 0 +# if __has_include() && !defined(min) && !defined(max) +# define ARDUINOJSON_ENABLE_STD_STRING 1 +# else +# define ARDUINOJSON_ENABLE_STD_STRING 0 +# endif #endif + +#ifndef ARDUINOJSON_ENABLE_STRING_VIEW +# ifdef __has_include +# if __has_include() && __cplusplus >= 201703L +# define ARDUINOJSON_ENABLE_STRING_VIEW 1 +# endif +# endif +#endif +#ifndef ARDUINOJSON_ENABLE_STRING_VIEW +# define ARDUINOJSON_ENABLE_STRING_VIEW 0 #endif #if ARDUINOJSON_EMBEDDED_MODE // Store floats by default to reduce the memory usage (issue #134) -#ifndef ARDUINOJSON_USE_DOUBLE -#define ARDUINOJSON_USE_DOUBLE 0 -#endif +# ifndef ARDUINOJSON_USE_DOUBLE +# define ARDUINOJSON_USE_DOUBLE 0 +# endif // Store longs by default, because they usually match the size of a float. -#ifndef ARDUINOJSON_USE_LONG_LONG -#define ARDUINOJSON_USE_LONG_LONG 0 -#endif +# ifndef ARDUINOJSON_USE_LONG_LONG +# define ARDUINOJSON_USE_LONG_LONG 0 +# endif // Embedded systems usually don't have std::string -#ifndef ARDUINOJSON_ENABLE_STD_STRING -#define ARDUINOJSON_ENABLE_STD_STRING 0 -#endif +# ifndef ARDUINOJSON_ENABLE_STD_STRING +# define ARDUINOJSON_ENABLE_STD_STRING 0 +# endif // Embedded systems usually don't have std::stream -#ifndef ARDUINOJSON_ENABLE_STD_STREAM -#define ARDUINOJSON_ENABLE_STD_STREAM 0 -#endif +# ifndef ARDUINOJSON_ENABLE_STD_STREAM +# define ARDUINOJSON_ENABLE_STD_STREAM 0 +# endif // Limit nesting as the stack is likely to be small -#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT -#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 -#endif +# ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT +# define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10 +# endif // Number of bits to store the pointer to next node // (saves RAM but limits the number of values in a document) -#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE -#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 2 +# ifndef ARDUINOJSON_SLOT_OFFSET_SIZE +# if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 2 // Address space == 16-bit => max 127 values -#define ARDUINOJSON_SLOT_OFFSET_SIZE 1 -#else +# define ARDUINOJSON_SLOT_OFFSET_SIZE 1 +# else // Address space > 16-bit => max 32767 values -#define ARDUINOJSON_SLOT_OFFSET_SIZE 2 -#endif -#endif +# define ARDUINOJSON_SLOT_OFFSET_SIZE 2 +# endif +# endif #else // ARDUINOJSON_EMBEDDED_MODE // On a computer we have plenty of memory so we can use doubles -#ifndef ARDUINOJSON_USE_DOUBLE -#define ARDUINOJSON_USE_DOUBLE 1 -#endif +# ifndef ARDUINOJSON_USE_DOUBLE +# define ARDUINOJSON_USE_DOUBLE 1 +# endif // Use long long when available -#ifndef ARDUINOJSON_USE_LONG_LONG -#if ARDUINOJSON_HAS_LONG_LONG || ARDUINOJSON_HAS_INT64 -#define ARDUINOJSON_USE_LONG_LONG 1 -#else -#define ARDUINOJSON_USE_LONG_LONG 0 -#endif -#endif +# ifndef ARDUINOJSON_USE_LONG_LONG +# if ARDUINOJSON_HAS_LONG_LONG || ARDUINOJSON_HAS_INT64 +# define ARDUINOJSON_USE_LONG_LONG 1 +# else +# define ARDUINOJSON_USE_LONG_LONG 0 +# endif +# endif // On a computer, we can use std::string -#ifndef ARDUINOJSON_ENABLE_STD_STRING -#define ARDUINOJSON_ENABLE_STD_STRING 1 -#endif +# ifndef ARDUINOJSON_ENABLE_STD_STRING +# define ARDUINOJSON_ENABLE_STD_STRING 1 +# endif // On a computer, we can assume std::stream -#ifndef ARDUINOJSON_ENABLE_STD_STREAM -#define ARDUINOJSON_ENABLE_STD_STREAM 1 -#endif +# ifndef ARDUINOJSON_ENABLE_STD_STREAM +# define ARDUINOJSON_ENABLE_STD_STREAM 1 +# endif // On a computer, the stack is large so we can increase nesting limit -#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT -#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 50 -#endif +# ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT +# define ARDUINOJSON_DEFAULT_NESTING_LIMIT 50 +# endif // Number of bits to store the pointer to next node -#ifndef ARDUINOJSON_SLOT_OFFSET_SIZE -#define ARDUINOJSON_SLOT_OFFSET_SIZE 4 -#endif +# ifndef ARDUINOJSON_SLOT_OFFSET_SIZE +# define ARDUINOJSON_SLOT_OFFSET_SIZE 4 +# endif #endif // ARDUINOJSON_EMBEDDED_MODE #ifdef ARDUINO -#include +# include // Enable support for Arduino's String class -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING -#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 -#endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +# define ARDUINOJSON_ENABLE_ARDUINO_STRING 1 +# endif // Enable support for Arduino's Stream class -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM -#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 -#endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1 +# endif // Enable support for Arduino's Print class -#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT -#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1 -#endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 1 +# endif #else // ARDUINO // Disable support for Arduino's String class -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING -#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 -#endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING +# define ARDUINOJSON_ENABLE_ARDUINO_STRING 0 +# endif // Disable support for Arduino's Stream class -#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM -#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 -#endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM +# define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0 +# endif // Disable support for Arduino's Print class -#ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT -#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0 -#endif +# ifndef ARDUINOJSON_ENABLE_ARDUINO_PRINT +# define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0 +# endif #endif // ARDUINO #ifndef ARDUINOJSON_ENABLE_PROGMEM -#if defined(PROGMEM) && defined(pgm_read_byte) && defined(pgm_read_dword) && \ - defined(pgm_read_ptr) && defined(pgm_read_float) -#define ARDUINOJSON_ENABLE_PROGMEM 1 -#else -#define ARDUINOJSON_ENABLE_PROGMEM 0 -#endif +# if defined(PROGMEM) && defined(pgm_read_byte) && defined(pgm_read_dword) && \ + defined(pgm_read_ptr) && defined(pgm_read_float) +# define ARDUINOJSON_ENABLE_PROGMEM 1 +# else +# define ARDUINOJSON_ENABLE_PROGMEM 0 +# endif #endif // Convert unicode escape sequence (\u0123) to UTF-8 #ifndef ARDUINOJSON_DECODE_UNICODE -#define ARDUINOJSON_DECODE_UNICODE 1 +# define ARDUINOJSON_DECODE_UNICODE 1 #endif // Ignore comments in input #ifndef ARDUINOJSON_ENABLE_COMMENTS -#define ARDUINOJSON_ENABLE_COMMENTS 0 +# define ARDUINOJSON_ENABLE_COMMENTS 0 #endif // Support NaN in JSON #ifndef ARDUINOJSON_ENABLE_NAN -#define ARDUINOJSON_ENABLE_NAN 0 +# define ARDUINOJSON_ENABLE_NAN 0 #endif // Support Infinity in JSON #ifndef ARDUINOJSON_ENABLE_INFINITY -#define ARDUINOJSON_ENABLE_INFINITY 0 +# define ARDUINOJSON_ENABLE_INFINITY 0 #endif // Control the exponentiation threshold for big numbers // CAUTION: cannot be more that 1e9 !!!! #ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD -#define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7 +# define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7 #endif // Control the exponentiation threshold for small numbers #ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD -#define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 +# define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 #endif #ifndef ARDUINOJSON_LITTLE_ENDIAN -#if defined(_MSC_VER) || \ - (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ - defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64) -#define ARDUINOJSON_LITTLE_ENDIAN 1 -#else -#define ARDUINOJSON_LITTLE_ENDIAN 0 -#endif +# if defined(_MSC_VER) || \ + (defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__i386) || defined(__x86_64) +# define ARDUINOJSON_LITTLE_ENDIAN 1 +# else +# define ARDUINOJSON_LITTLE_ENDIAN 0 +# endif #endif #ifndef ARDUINOJSON_ENABLE_ALIGNMENT -#if defined(__AVR) -#define ARDUINOJSON_ENABLE_ALIGNMENT 0 -#else -#define ARDUINOJSON_ENABLE_ALIGNMENT 1 -#endif +# if defined(__AVR) +# define ARDUINOJSON_ENABLE_ALIGNMENT 0 +# else +# define ARDUINOJSON_ENABLE_ALIGNMENT 1 +# endif #endif #ifndef ARDUINOJSON_TAB -#define ARDUINOJSON_TAB " " +# define ARDUINOJSON_TAB " " #endif #ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION -#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1 +# define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1 #endif #ifndef ARDUINOJSON_STRING_BUFFER_SIZE -#define ARDUINOJSON_STRING_BUFFER_SIZE 32 +# define ARDUINOJSON_STRING_BUFFER_SIZE 32 #endif #ifndef ARDUINOJSON_DEBUG -#ifdef __PLATFORMIO_BUILD_DEBUG__ -#define ARDUINOJSON_DEBUG 1 -#else -#define ARDUINOJSON_DEBUG 0 -#endif +# ifdef __PLATFORMIO_BUILD_DEBUG__ +# define ARDUINOJSON_DEBUG 1 +# else +# define ARDUINOJSON_DEBUG 0 +# endif #endif #if ARDUINOJSON_HAS_NULLPTR && defined(nullptr) -#error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr +# error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr // See https://github.com/bblanchon/ArduinoJson/issues/1355 #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp b/lib/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp index 7b617111a..11ec7df66 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp @@ -9,7 +9,7 @@ #include #if ARDUINOJSON_ENABLE_STD_STREAM -#include +# include #endif namespace ARDUINOJSON_NAMESPACE { diff --git a/lib/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp b/lib/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp index e965c8256..9ae2d5987 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp @@ -40,17 +40,17 @@ struct BoundedReader { #include #if ARDUINOJSON_ENABLE_ARDUINO_STREAM -#include +# include #endif #if ARDUINOJSON_ENABLE_ARDUINO_STRING -#include +# include #endif #if ARDUINOJSON_ENABLE_PROGMEM -#include +# include #endif #if ARDUINOJSON_ENABLE_STD_STREAM -#include +# include #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp b/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp index d67d93496..d81853c3a 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp @@ -32,7 +32,7 @@ class JsonDocument : public Visitable { void clear() { _pool.clear(); - _data.setNull(); + _data.init(); } template @@ -304,15 +304,15 @@ class JsonDocument : public Visitable { protected: JsonDocument() : _pool(0, 0) { - _data.setNull(); + _data.init(); } JsonDocument(MemoryPool pool) : _pool(pool) { - _data.setNull(); + _data.init(); } JsonDocument(char* buf, size_t capa) : _pool(buf, capa) { - _data.setNull(); + _data.init(); } ~JsonDocument() {} @@ -337,8 +337,8 @@ class JsonDocument : public Visitable { JsonDocument& operator=(const JsonDocument&); }; -inline bool convertToJson(const JsonDocument& src, VariantRef dst) { - return dst.set(src.as()); +inline void convertToJson(const JsonDocument& src, VariantRef dst) { + dst.set(src.as()); } } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/Latch.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/Latch.hpp index aef1fe368..70866d6b7 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/Latch.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/Latch.hpp @@ -45,7 +45,8 @@ class Latch { } TReader _reader; - char _current; + char _current; // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject) + // Not initialized in constructor (+10 bytes on AVR) bool _loaded; #if ARDUINOJSON_DEBUG bool _ended; diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp index 18694f14e..7795671ff 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp @@ -155,7 +155,6 @@ class TextFormatter { protected: CountingDecorator _writer; - size_t _length; private: TextFormatter &operator=(const TextFormatter &); // cannot be assigned diff --git a/lib/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp b/lib/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp index 4e2750f3b..e2b901056 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp @@ -12,10 +12,10 @@ // we choose to ignore the problem to reduce the size of the code // Garbage in => Garbage out #if defined(__GNUC__) -#if __GNUC__ >= 7 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif +# if __GNUC__ >= 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# endif #endif namespace ARDUINOJSON_NAMESPACE { @@ -31,7 +31,7 @@ inline bool isLowSurrogate(uint16_t codeunit) { class Codepoint { public: - Codepoint() : _highSurrogate(0) {} + Codepoint() : _highSurrogate(0), _codepoint(0) {} bool append(uint16_t codeunit) { if (isHighSurrogate(codeunit)) { @@ -61,7 +61,7 @@ class Codepoint { } // namespace ARDUINOJSON_NAMESPACE #if defined(__GNUC__) -#if __GNUC__ >= 8 -#pragma GCC diagnostic pop -#endif +# if __GNUC__ >= 8 +# pragma GCC diagnostic pop +# endif #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp b/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp index 49debf856..04e5b2d28 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include // memmove @@ -37,7 +38,8 @@ class MemoryPool { } void* buffer() { - return _begin; + return _begin; // NOLINT(clang-analyzer-unix.Malloc) + // movePointers() alters this pointer } // Gets the capacity of the memoryPool in bytes @@ -63,7 +65,7 @@ class MemoryPool { return 0; #if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION - const char* existingCopy = findString(str.begin()); + const char* existingCopy = findString(str); if (existingCopy) return existingCopy; #endif @@ -85,7 +87,7 @@ class MemoryPool { const char* saveStringFromFreeZone(size_t len) { #if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION - const char* dup = findString(_left); + const char* dup = findString(adaptString(_left)); if (dup) return dup; #endif @@ -162,16 +164,11 @@ class MemoryPool { } #if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION - template - const char* findString(TIterator str) { + template + const char* findString(const TAdaptedString& str) { for (char* next = _begin; next < _left; ++next) { - char* begin = next; - - // try to match - for (TIterator it = str; *it == *next; ++it) { - if (*next++ == 0) - return begin; - } + if (str.compare(next) == 0) + return next; // jump to next terminator while (*next) ++next; diff --git a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index 8988173e3..fbeb05df2 100644 --- a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -15,468 +15,475 @@ namespace ARDUINOJSON_NAMESPACE { template class MsgPackDeserializer { - public: - MsgPackDeserializer(MemoryPool & pool, TReader reader, TStringStorage stringStorage) - : _pool(&pool) - , _reader(reader) - , _stringStorage(stringStorage) - , _error(DeserializationError::Ok) - , _foundSomething(false) { - } + public: + MsgPackDeserializer(MemoryPool &pool, TReader reader, + TStringStorage stringStorage) + : _pool(&pool), + _reader(reader), + _stringStorage(stringStorage), + _error(DeserializationError::Ok), + _foundSomething(false) {} - template - DeserializationError parse(VariantData & variant, TFilter filter, NestingLimit nestingLimit) { - parseVariant(&variant, filter, nestingLimit); - return _foundSomething ? _error : DeserializationError::EmptyInput; - } + template + DeserializationError parse(VariantData &variant, TFilter filter, + NestingLimit nestingLimit) { + parseVariant(&variant, filter, nestingLimit); + return _foundSomething ? _error : DeserializationError::EmptyInput; + } - private: - // Prevent VS warning "assignment operator could not be generated" - MsgPackDeserializer & operator=(const MsgPackDeserializer &); + private: + bool invalidInput() { + _error = DeserializationError::InvalidInput; + return false; + } - bool invalidInput() { - _error = DeserializationError::InvalidInput; - return false; - } + template + bool parseVariant(VariantData *variant, TFilter filter, + NestingLimit nestingLimit) { + uint8_t code = 0; // TODO: why do we need to initialize this variable? + if (!readByte(code)) + return false; - template - bool parseVariant(VariantData * variant, TFilter filter, NestingLimit nestingLimit) { - uint8_t code = 0; - if (!readByte(code)) - return false; + _foundSomething = true; - _foundSomething = true; + bool allowValue = filter.allowValue(); - bool allowValue = filter.allowValue(); + switch (code) { + case 0xc0: + // already null + return true; - switch (code) { - case 0xc0: - // already null - return true; - - case 0xc1: - return invalidInput(); - - case 0xc2: - if (allowValue) - variant->setBoolean(false); - return true; - - case 0xc3: - if (allowValue) - variant->setBoolean(true); - return true; - - case 0xc4: // bin 8 (not supported) - return skipString(); - - case 0xc5: // bin 16 (not supported) - return skipString(); - - case 0xc6: // bin 32 (not supported) - return skipString(); - - case 0xc7: // ext 8 (not supported) - return skipExt(); - - case 0xc8: // ext 16 (not supported) - return skipExt(); - - case 0xc9: // ext 32 (not supported) - return skipExt(); - - case 0xca: - if (allowValue) - return readFloat(variant); - else - return skipBytes(4); - - case 0xcb: - if (allowValue) - return readDouble(variant); - else - return skipBytes(8); - - case 0xcc: - if (allowValue) - return readInteger(variant); - else - return skipBytes(1); - - case 0xcd: - if (allowValue) - return readInteger(variant); - else - return skipBytes(2); - - case 0xce: - if (allowValue) - return readInteger(variant); - else - return skipBytes(4); - - case 0xcf: -#if ARDUINOJSON_USE_LONG_LONG - if (allowValue) - return readInteger(variant); - else - return skipBytes(8); -#else - return skipBytes(8); // not supported -#endif - - case 0xd0: - if (allowValue) - return readInteger(variant); - else - return skipBytes(1); - - case 0xd1: - if (allowValue) - return readInteger(variant); - else - return skipBytes(2); - - case 0xd2: - if (allowValue) - return readInteger(variant); - else - return skipBytes(4); - - case 0xd3: -#if ARDUINOJSON_USE_LONG_LONG - if (allowValue) - return readInteger(variant); - else - return skipBytes(8); // not supported -#else - return skipBytes(8); -#endif - - case 0xd4: // fixext 1 (not supported) - return skipBytes(2); - - case 0xd5: // fixext 2 (not supported) - return skipBytes(3); - - case 0xd6: // fixext 4 (not supported) - return skipBytes(5); - - case 0xd7: // fixext 8 (not supported) - return skipBytes(9); - - case 0xd8: // fixext 16 (not supported) - return skipBytes(17); - - case 0xd9: - if (allowValue) - return readString(variant); - else - return skipString(); - - case 0xda: - if (allowValue) - return readString(variant); - else - return skipString(); - - case 0xdb: - if (allowValue) - return readString(variant); - else - return skipString(); - - case 0xdc: - return readArray(variant, filter, nestingLimit); - - case 0xdd: - return readArray(variant, filter, nestingLimit); - - case 0xde: - return readObject(variant, filter, nestingLimit); - - case 0xdf: - return readObject(variant, filter, nestingLimit); - } - - switch (code & 0xf0) { - case 0x80: - return readObject(variant, code & 0x0F, filter, nestingLimit); - - case 0x90: - return readArray(variant, code & 0x0F, filter, nestingLimit); - } - - if ((code & 0xe0) == 0xa0) { - if (allowValue) - return readString(variant, code & 0x1f); - else - return skipBytes(code & 0x1f); - } + case 0xc1: + return invalidInput(); + case 0xc2: if (allowValue) - variant->setInteger(static_cast(code)); - + variant->setBoolean(false); return true; + + case 0xc3: + if (allowValue) + variant->setBoolean(true); + return true; + + case 0xc4: // bin 8 (not supported) + return skipString(); + + case 0xc5: // bin 16 (not supported) + return skipString(); + + case 0xc6: // bin 32 (not supported) + return skipString(); + + case 0xc7: // ext 8 (not supported) + return skipExt(); + + case 0xc8: // ext 16 (not supported) + return skipExt(); + + case 0xc9: // ext 32 (not supported) + return skipExt(); + + case 0xca: + if (allowValue) + return readFloat(variant); + else + return skipBytes(4); + + case 0xcb: + if (allowValue) + return readDouble(variant); + else + return skipBytes(8); + + case 0xcc: + if (allowValue) + return readInteger(variant); + else + return skipBytes(1); + + case 0xcd: + if (allowValue) + return readInteger(variant); + else + return skipBytes(2); + + case 0xce: + if (allowValue) + return readInteger(variant); + else + return skipBytes(4); + + case 0xcf: +#if ARDUINOJSON_USE_LONG_LONG + if (allowValue) + return readInteger(variant); + else + return skipBytes(8); +#else + return skipBytes(8); // not supported +#endif + + case 0xd0: + if (allowValue) + return readInteger(variant); + else + return skipBytes(1); + + case 0xd1: + if (allowValue) + return readInteger(variant); + else + return skipBytes(2); + + case 0xd2: + if (allowValue) + return readInteger(variant); + else + return skipBytes(4); + + case 0xd3: +#if ARDUINOJSON_USE_LONG_LONG + if (allowValue) + return readInteger(variant); + else + return skipBytes(8); // not supported +#else + return skipBytes(8); +#endif + + case 0xd4: // fixext 1 (not supported) + return skipBytes(2); + + case 0xd5: // fixext 2 (not supported) + return skipBytes(3); + + case 0xd6: // fixext 4 (not supported) + return skipBytes(5); + + case 0xd7: // fixext 8 (not supported) + return skipBytes(9); + + case 0xd8: // fixext 16 (not supported) + return skipBytes(17); + + case 0xd9: + if (allowValue) + return readString(variant); + else + return skipString(); + + case 0xda: + if (allowValue) + return readString(variant); + else + return skipString(); + + case 0xdb: + if (allowValue) + return readString(variant); + else + return skipString(); + + case 0xdc: + return readArray(variant, filter, nestingLimit); + + case 0xdd: + return readArray(variant, filter, nestingLimit); + + case 0xde: + return readObject(variant, filter, nestingLimit); + + case 0xdf: + return readObject(variant, filter, nestingLimit); } - bool readByte(uint8_t & value) { - int c = _reader.read(); - if (c < 0) { - _error = DeserializationError::IncompleteInput; - return false; - } - value = static_cast(c); - return true; + switch (code & 0xf0) { + case 0x80: + return readObject(variant, code & 0x0F, filter, nestingLimit); + + case 0x90: + return readArray(variant, code & 0x0F, filter, nestingLimit); } - bool readBytes(uint8_t * p, size_t n) { - if (_reader.readBytes(reinterpret_cast(p), n) == n) - return true; + if ((code & 0xe0) == 0xa0) { + if (allowValue) + return readString(variant, code & 0x1f); + else + return skipBytes(code & 0x1f); + } + + if (allowValue) + variant->setInteger(static_cast(code)); + + return true; + } + + bool readByte(uint8_t &value) { + int c = _reader.read(); + if (c < 0) { + _error = DeserializationError::IncompleteInput; + return false; + } + value = static_cast(c); + return true; + } + + bool readBytes(uint8_t *p, size_t n) { + if (_reader.readBytes(reinterpret_cast(p), n) == n) + return true; + _error = DeserializationError::IncompleteInput; + return false; + } + + template + bool readBytes(T &value) { + return readBytes(reinterpret_cast(&value), sizeof(value)); + } + + bool skipBytes(size_t n) { + for (; n; --n) { + if (_reader.read() < 0) { _error = DeserializationError::IncompleteInput; return false; + } + } + return true; + } + + template + bool readInteger(T &value) { + if (!readBytes(value)) + return false; + fixEndianess(value); + return true; + } + + template + bool readInteger(VariantData *variant) { + T value; + if (!readInteger(value)) + return false; + variant->setInteger(value); + return true; + } + + template + typename enable_if::type readFloat( + VariantData *variant) { + T value; + if (!readBytes(value)) + return false; + fixEndianess(value); + variant->setFloat(value); + return true; + } + + template + typename enable_if::type readDouble( + VariantData *variant) { + T value; + if (!readBytes(value)) + return false; + fixEndianess(value); + variant->setFloat(value); + return true; + } + + template + typename enable_if::type readDouble( + VariantData *variant) { + uint8_t i[8]; // input is 8 bytes + T value; // output is 4 bytes + uint8_t *o = reinterpret_cast(&value); + if (!readBytes(i, 8)) + return false; + doubleToFloat(i, o); + fixEndianess(value); + variant->setFloat(value); + return true; + } + + template + bool readString(VariantData *variant) { + T size; + if (!readInteger(size)) + return false; + return readString(variant, size); + } + + template + bool readString() { + T size; + if (!readInteger(size)) + return false; + return readString(size); + } + + template + bool skipString() { + T size; + if (!readInteger(size)) + return false; + return skipBytes(size); + } + + bool readString(VariantData *variant, size_t n) { + if (!readString(n)) + return false; + variant->setStringPointer(_stringStorage.save(), + typename TStringStorage::storage_policy()); + return true; + } + + bool readString(size_t n) { + _stringStorage.startString(); + for (; n; --n) { + uint8_t c; + if (!readBytes(c)) + return false; + _stringStorage.append(static_cast(c)); + } + _stringStorage.append('\0'); + if (!_stringStorage.isValid()) { + _error = DeserializationError::NoMemory; + return false; } - template - bool readBytes(T & value) { - return readBytes(reinterpret_cast(&value), sizeof(value)); + return true; + } + + template + bool readArray(VariantData *variant, TFilter filter, + NestingLimit nestingLimit) { + TSize size; + if (!readInteger(size)) + return false; + return readArray(variant, size, filter, nestingLimit); + } + + template + bool readArray(VariantData *variant, size_t n, TFilter filter, + NestingLimit nestingLimit) { + if (nestingLimit.reached()) { + _error = DeserializationError::TooDeep; + return false; } - bool skipBytes(size_t n) { - for (; n; --n) { - if (_reader.read() < 0) { - _error = DeserializationError::IncompleteInput; - return false; - } + bool allowArray = filter.allowArray(); + + CollectionData *array = allowArray ? &variant->toArray() : 0; + + TFilter memberFilter = filter[0U]; + + for (; n; --n) { + VariantData *value; + + if (memberFilter.allow()) { + value = array->addElement(_pool); + if (!value) { + _error = DeserializationError::NoMemory; + return false; } - return true; + } else { + value = 0; + } + + if (!parseVariant(value, memberFilter, nestingLimit.decrement())) + return false; } - template - bool readInteger(T & value) { - if (!readBytes(value)) - return false; - fixEndianess(value); - return true; + return true; + } + + template + bool readObject(VariantData *variant, TFilter filter, + NestingLimit nestingLimit) { + TSize size; + if (!readInteger(size)) + return false; + return readObject(variant, size, filter, nestingLimit); + } + + template + bool readObject(VariantData *variant, size_t n, TFilter filter, + NestingLimit nestingLimit) { + if (nestingLimit.reached()) { + _error = DeserializationError::TooDeep; + return false; } - template - bool readInteger(VariantData * variant) { - T value; - if (!readInteger(value)) - return false; - variant->setInteger(value); - return true; - } + CollectionData *object = filter.allowObject() ? &variant->toObject() : 0; - template - typename enable_if::type readFloat(VariantData * variant) { - T value; - if (!readBytes(value)) - return false; - fixEndianess(value); - variant->setFloat(value); - return true; - } + for (; n; --n) { + if (!readKey()) + return false; - template - typename enable_if::type readDouble(VariantData * variant) { - T value; - if (!readBytes(value)) - return false; - fixEndianess(value); - variant->setFloat(value); - return true; - } + const char *key = _stringStorage.c_str(); + TFilter memberFilter = filter[key]; + VariantData *member; - template - typename enable_if::type readDouble(VariantData * variant) { - uint8_t i[8]; // input is 8 bytes - T value; // output is 4 bytes - uint8_t * o = reinterpret_cast(&value); - if (!readBytes(i, 8)) - return false; - doubleToFloat(i, o); - fixEndianess(value); - variant->setFloat(value); - return true; - } + if (memberFilter.allow()) { + // Save key in memory pool. + // This MUST be done before adding the slot. + key = _stringStorage.save(); - template - bool readString(VariantData * variant) { - T size; - if (!readInteger(size)) - return false; - return readString(variant, size); - } - - template - bool readString() { - T size; - if (!readInteger(size)) - return false; - return readString(size); - } - - template - bool skipString() { - T size; - if (!readInteger(size)) - return false; - return skipBytes(size); - } - - bool readString(VariantData * variant, size_t n) { - if (!readString(n)) - return false; - variant->setStringPointer(_stringStorage.save(), typename TStringStorage::storage_policy()); - return true; - } - - bool readString(size_t n) { - _stringStorage.startString(); - for (; n; --n) { - uint8_t c; - if (!readBytes(c)) - return false; - _stringStorage.append(static_cast(c)); - } - _stringStorage.append('\0'); - if (!_stringStorage.isValid()) { - _error = DeserializationError::NoMemory; - return false; + VariantSlot *slot = object->addSlot(_pool); + if (!slot) { + _error = DeserializationError::NoMemory; + return false; } - return true; + slot->setKey(key, typename TStringStorage::storage_policy()); + + member = slot->data(); + } else { + member = 0; + } + + if (!parseVariant(member, memberFilter, nestingLimit.decrement())) + return false; } - template - bool readArray(VariantData * variant, TFilter filter, NestingLimit nestingLimit) { - TSize size; - if (!readInteger(size)) - return false; - return readArray(variant, size, filter, nestingLimit); + return true; + } + + bool readKey() { + uint8_t code; + if (!readByte(code)) + return false; + + if ((code & 0xe0) == 0xa0) + return readString(code & 0x1f); + + switch (code) { + case 0xd9: + return readString(); + + case 0xda: + return readString(); + + case 0xdb: + return readString(); + + default: + return invalidInput(); } + } - template - bool readArray(VariantData * variant, size_t n, TFilter filter, NestingLimit nestingLimit) { - if (nestingLimit.reached()) { - _error = DeserializationError::TooDeep; - return false; - } + template + bool skipExt() { + T size; + if (!readInteger(size)) + return false; + return skipBytes(size + 1); + } - bool allowArray = filter.allowArray(); - - CollectionData * array = allowArray ? &variant->toArray() : 0; - - TFilter memberFilter = filter[0U]; - - for (; n; --n) { - VariantData * value; - - if (memberFilter.allow()) { - value = array->addElement(_pool); - if (!value) { - _error = DeserializationError::NoMemory; - return false; - } - } else { - value = 0; - } - - if (!parseVariant(value, memberFilter, nestingLimit.decrement())) - return false; - } - - return true; - } - - template - bool readObject(VariantData * variant, TFilter filter, NestingLimit nestingLimit) { - TSize size; - if (!readInteger(size)) - return false; - return readObject(variant, size, filter, nestingLimit); - } - - template - bool readObject(VariantData * variant, size_t n, TFilter filter, NestingLimit nestingLimit) { - if (nestingLimit.reached()) { - _error = DeserializationError::TooDeep; - return false; - } - - CollectionData * object = filter.allowObject() ? &variant->toObject() : 0; - - for (; n; --n) { - if (!readKey()) - return false; - - const char * key = _stringStorage.c_str(); - TFilter memberFilter = filter[key]; - VariantData * member; - - if (memberFilter.allow()) { - // Save key in memory pool. - // This MUST be done before adding the slot. - key = _stringStorage.save(); - - VariantSlot * slot = object->addSlot(_pool); - if (!slot) { - _error = DeserializationError::NoMemory; - return false; - } - - slot->setKey(key, typename TStringStorage::storage_policy()); - - member = slot->data(); - } else { - member = 0; - } - - if (!parseVariant(member, memberFilter, nestingLimit.decrement())) - return false; - } - - return true; - } - - bool readKey() { - uint8_t code; - if (!readByte(code)) - return false; - - if ((code & 0xe0) == 0xa0) - return readString(code & 0x1f); - - switch (code) { - case 0xd9: - return readString(); - - case 0xda: - return readString(); - - case 0xdb: - return readString(); - - default: - return invalidInput(); - } - } - - template - bool skipExt() { - T size; - if (!readInteger(size)) - return false; - return skipBytes(size + 1); - } - - MemoryPool * _pool; - TReader _reader; - TStringStorage _stringStorage; - DeserializationError _error; - bool _foundSomething; + MemoryPool *_pool; + TReader _reader; + TStringStorage _stringStorage; + DeserializationError _error; + bool _foundSomething; }; // @@ -484,18 +491,25 @@ class MsgPackDeserializer { // // ... = NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & input, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, nestingLimit, AllowAllFilter()); +DeserializationError deserializeMsgPack( + JsonDocument &doc, const TString &input, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, nestingLimit, + AllowAllFilter()); } // ... = Filter, NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & input, Filter filter, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, nestingLimit, filter); +DeserializationError deserializeMsgPack( + JsonDocument &doc, const TString &input, Filter filter, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, nestingLimit, filter); } // ... = NestingLimit, Filter template -DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & input, NestingLimit nestingLimit, Filter filter) { - return deserialize(doc, input, nestingLimit, filter); +DeserializationError deserializeMsgPack(JsonDocument &doc, const TString &input, + NestingLimit nestingLimit, + Filter filter) { + return deserialize(doc, input, nestingLimit, filter); } // @@ -503,18 +517,25 @@ DeserializationError deserializeMsgPack(JsonDocument & doc, const TString & inpu // // ... = NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, nestingLimit, AllowAllFilter()); +DeserializationError deserializeMsgPack( + JsonDocument &doc, TStream &input, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, nestingLimit, + AllowAllFilter()); } // ... = Filter, NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, Filter filter, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, nestingLimit, filter); +DeserializationError deserializeMsgPack( + JsonDocument &doc, TStream &input, Filter filter, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, nestingLimit, filter); } // ... = NestingLimit, Filter template -DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, NestingLimit nestingLimit, Filter filter) { - return deserialize(doc, input, nestingLimit, filter); +DeserializationError deserializeMsgPack(JsonDocument &doc, TStream &input, + NestingLimit nestingLimit, + Filter filter) { + return deserialize(doc, input, nestingLimit, filter); } // @@ -522,18 +543,25 @@ DeserializationError deserializeMsgPack(JsonDocument & doc, TStream & input, Nes // // ... = NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, nestingLimit, AllowAllFilter()); +DeserializationError deserializeMsgPack( + JsonDocument &doc, TChar *input, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, nestingLimit, + AllowAllFilter()); } // ... = Filter, NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, Filter filter, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, nestingLimit, filter); +DeserializationError deserializeMsgPack( + JsonDocument &doc, TChar *input, Filter filter, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, nestingLimit, filter); } // ... = NestingLimit, Filter template -DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, NestingLimit nestingLimit, Filter filter) { - return deserialize(doc, input, nestingLimit, filter); +DeserializationError deserializeMsgPack(JsonDocument &doc, TChar *input, + NestingLimit nestingLimit, + Filter filter) { + return deserialize(doc, input, nestingLimit, filter); } // @@ -541,18 +569,28 @@ DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, Nesti // // ... = NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, size_t inputSize, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, inputSize, nestingLimit, AllowAllFilter()); +DeserializationError deserializeMsgPack( + JsonDocument &doc, TChar *input, size_t inputSize, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, inputSize, nestingLimit, + AllowAllFilter()); } // ... = Filter, NestingLimit template -DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, size_t inputSize, Filter filter, NestingLimit nestingLimit = NestingLimit()) { - return deserialize(doc, input, inputSize, nestingLimit, filter); +DeserializationError deserializeMsgPack( + JsonDocument &doc, TChar *input, size_t inputSize, Filter filter, + NestingLimit nestingLimit = NestingLimit()) { + return deserialize(doc, input, inputSize, nestingLimit, + filter); } // ... = NestingLimit, Filter template -DeserializationError deserializeMsgPack(JsonDocument & doc, TChar * input, size_t inputSize, NestingLimit nestingLimit, Filter filter) { - return deserialize(doc, input, inputSize, nestingLimit, filter); +DeserializationError deserializeMsgPack(JsonDocument &doc, TChar *input, + size_t inputSize, + NestingLimit nestingLimit, + Filter filter) { + return deserialize(doc, input, inputSize, nestingLimit, + filter); } -} // namespace ARDUINOJSON_NAMESPACE +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Namespace.hpp b/lib/ArduinoJson/src/ArduinoJson/Namespace.hpp index 2d85440aa..26def8ab0 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Namespace.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Namespace.hpp @@ -10,17 +10,17 @@ #ifndef ARDUINOJSON_NAMESPACE -#define ARDUINOJSON_NAMESPACE \ - ARDUINOJSON_CONCAT4( \ - ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \ - ARDUINOJSON_VERSION_MINOR, \ - ARDUINOJSON_VERSION_REVISION), \ - _, \ - ARDUINOJSON_HEX_DIGIT(ARDUINOJSON_ENABLE_PROGMEM, \ - ARDUINOJSON_USE_LONG_LONG, ARDUINOJSON_USE_DOUBLE, \ - ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \ - ARDUINOJSON_HEX_DIGIT( \ - ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ - ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE)) +# define ARDUINOJSON_NAMESPACE \ + ARDUINOJSON_CONCAT4( \ + ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR, \ + ARDUINOJSON_VERSION_MINOR, \ + ARDUINOJSON_VERSION_REVISION), \ + _, \ + ARDUINOJSON_HEX_DIGIT( \ + ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG, \ + ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \ + ARDUINOJSON_HEX_DIGIT( \ + ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY, \ + ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE)) #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Numbers/Integer.hpp b/lib/ArduinoJson/src/ArduinoJson/Numbers/Integer.hpp index fb656a7d9..e0ed519a1 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Numbers/Integer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Numbers/Integer.hpp @@ -22,11 +22,11 @@ typedef unsigned long UInt; } // namespace ARDUINOJSON_NAMESPACE #if ARDUINOJSON_HAS_LONG_LONG && !ARDUINOJSON_USE_LONG_LONG -#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ - static_assert(sizeof(T) <= sizeof(ARDUINOJSON_NAMESPACE::Integer), \ - "To use 64-bit integers with ArduinoJson, you must set " \ - "ARDUINOJSON_USE_LONG_LONG to 1. See " \ - "https://arduinojson.org/v6/api/config/use_long_long/"); +# define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) \ + static_assert(sizeof(T) <= sizeof(ARDUINOJSON_NAMESPACE::Integer), \ + "To use 64-bit integers with ArduinoJson, you must set " \ + "ARDUINOJSON_USE_LONG_LONG to 1. See " \ + "https://arduinojson.org/v6/api/config/use_long_long/"); #else -#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) +# define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T) #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp b/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp index 02bbefa50..3087459f4 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp @@ -5,13 +5,13 @@ #pragma once #if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wconversion" #elif defined(__GNUC__) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic push -#endif -#pragma GCC diagnostic ignored "-Wconversion" +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# pragma GCC diagnostic push +# endif +# pragma GCC diagnostic ignored "-Wconversion" #endif #include @@ -71,9 +71,23 @@ canConvertNumber(TIn) { } // int32 -> uint32 +// int32 -> uint64 template typename enable_if::value && is_signed::value && - is_integral::value && is_unsigned::value, + is_integral::value && is_unsigned::value && + sizeof(TOut) >= sizeof(TIn), + bool>::type +canConvertNumber(TIn value) { + if (value < 0) + return false; + return TOut(value) <= numeric_limits::highest(); +} + +// int32 -> uint16 +template +typename enable_if::value && is_signed::value && + is_integral::value && is_unsigned::value && + sizeof(TOut) < sizeof(TIn), bool>::type canConvertNumber(TIn value) { if (value < 0) @@ -99,9 +113,9 @@ TOut convertNumber(TIn value) { } // namespace ARDUINOJSON_NAMESPACE #if defined(__clang__) -#pragma clang diagnostic pop +# pragma clang diagnostic pop #elif defined(__GNUC__) -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic pop -#endif +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# pragma GCC diagnostic pop +# endif #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp index 0bee84bf2..f1463a3dd 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp @@ -12,8 +12,8 @@ #include #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4522) +# pragma warning(push) +# pragma warning(disable : 4522) #endif namespace ARDUINOJSON_NAMESPACE { @@ -187,8 +187,8 @@ class MemberProxy : public VariantOperators >, return _object.getOrAddMember(_key); } - friend bool convertToJson(const this_type &src, VariantRef dst) { - return dst.set(src.getUpstreamMember()); + friend void convertToJson(const this_type &src, VariantRef dst) { + dst.set(src.getUpstreamMember()); } TObject _object; @@ -198,5 +198,5 @@ class MemberProxy : public VariantOperators >, } // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER -#pragma warning(pop) +# pragma warning(pop) #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Object/ObjectRef.hpp b/lib/ArduinoJson/src/ArduinoJson/Object/ObjectRef.hpp index c945fb6ca..047d9b1b5 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Object/ObjectRef.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Object/ObjectRef.hpp @@ -239,8 +239,8 @@ class ObjectRef : public ObjectRefBase, template <> struct Converter { - static bool toJson(VariantConstRef src, VariantRef dst) { - return variantCopyFrom(getData(dst), getData(src), getPool(dst)); + static void toJson(VariantConstRef src, VariantRef dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); } static ObjectConstRef fromJson(VariantConstRef src) { @@ -255,8 +255,8 @@ struct Converter { template <> struct Converter { - static bool toJson(VariantConstRef src, VariantRef dst) { - return variantCopyFrom(getData(dst), getData(src), getPool(dst)); + static void toJson(VariantConstRef src, VariantRef dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); } static ObjectRef fromJson(VariantRef src) { @@ -265,6 +265,9 @@ struct Converter { return ObjectRef(pool, data != 0 ? data->asObject() : 0); } + static InvalidConversion fromJson( + VariantConstRef); + static bool checkJson(VariantConstRef) { return false; } diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp index 1030ec60e..f5fa94673 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp @@ -7,8 +7,8 @@ #include #if ARDUINOJSON_DEBUG -#include -#define ARDUINOJSON_ASSERT(X) assert(X) +# include +# define ARDUINOJSON_ASSERT(X) assert(X) #else -#define ARDUINOJSON_ASSERT(X) ((void)0) +# define ARDUINOJSON_ASSERT(X) ((void)0) #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp index f04c9acce..8ef33afef 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp @@ -6,49 +6,49 @@ #ifdef _MSC_VER // Visual Studio -#define FORCE_INLINE // __forceinline causes C4714 when returning std::string -#define NO_INLINE __declspec(noinline) +# define FORCE_INLINE // __forceinline causes C4714 when returning std::string +# define NO_INLINE __declspec(noinline) -#ifndef ARDUINOJSON_DEPRECATED -#define ARDUINOJSON_DEPRECATED(msg) __declspec(deprecated(msg)) -#endif +# ifndef ARDUINOJSON_DEPRECATED +# define ARDUINOJSON_DEPRECATED(msg) __declspec(deprecated(msg)) +# endif #elif defined(__GNUC__) // GCC or Clang -#define FORCE_INLINE __attribute__((always_inline)) -#define NO_INLINE __attribute__((noinline)) +# define FORCE_INLINE __attribute__((always_inline)) +# define NO_INLINE __attribute__((noinline)) -#ifndef ARDUINOJSON_DEPRECATED -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) -#define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated(msg))) -#else -#define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated)) -#endif -#endif +# ifndef ARDUINOJSON_DEPRECATED +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +# define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated(msg))) +# else +# define ARDUINOJSON_DEPRECATED(msg) __attribute__((deprecated)) +# endif +# endif #else // Other compilers -#define FORCE_INLINE -#define NO_INLINE +# define FORCE_INLINE +# define NO_INLINE -#ifndef ARDUINOJSON_DEPRECATED -#define ARDUINOJSON_DEPRECATED(msg) -#endif +# ifndef ARDUINOJSON_DEPRECATED +# define ARDUINOJSON_DEPRECATED(msg) +# endif #endif #if __cplusplus >= 201103L -#define NOEXCEPT noexcept +# define NOEXCEPT noexcept #else -#define NOEXCEPT throw() +# define NOEXCEPT throw() #endif #if defined(__has_attribute) -#if __has_attribute(no_sanitize) -#define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) +# if __has_attribute(no_sanitize) +# define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check))) +# else +# define ARDUINOJSON_NO_SANITIZE(check) +# endif #else -#define ARDUINOJSON_NO_SANITIZE(check) -#endif -#else -#define ARDUINOJSON_NO_SANITIZE(check) +# define ARDUINOJSON_NO_SANITIZE(check) #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp index 80048284b..68a2bf8a6 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp @@ -7,8 +7,8 @@ #include "type_traits.hpp" #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4310) +# pragma warning(push) +# pragma warning(disable : 4310) #endif namespace ARDUINOJSON_NAMESPACE { @@ -41,5 +41,5 @@ struct numeric_limits< } // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER -#pragma warning(pop) +# pragma warning(pop) #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp index a877b4caa..b070628cc 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp @@ -8,27 +8,27 @@ #if ARDUINOJSON_ENABLE_PROGMEM -#include +# include -#ifndef ARDUINOJSON_DEFINE_STATIC_ARRAY -#define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \ - static type const name[] PROGMEM = value; -#endif +# ifndef ARDUINOJSON_DEFINE_STATIC_ARRAY +# define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \ + static type const name[] PROGMEM = value; +# endif -#ifndef ARDUINOJSON_READ_STATIC_ARRAY -#define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) \ - pgm_read(name + index) -#endif +# ifndef ARDUINOJSON_READ_STATIC_ARRAY +# define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) \ + pgm_read(name + index) +# endif #else // i.e. ARDUINOJSON_ENABLE_PROGMEM == 0 -#ifndef ARDUINOJSON_DEFINE_STATIC_ARRAY -#define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \ - static type const name[] = value; -#endif +# ifndef ARDUINOJSON_DEFINE_STATIC_ARRAY +# define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \ + static type const name[] = value; +# endif -#ifndef ARDUINOJSON_READ_STATIC_ARRAY -#define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) name[index] -#endif +# ifndef ARDUINOJSON_READ_STATIC_ARRAY +# define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) name[index] +# endif #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp index 4a8ff4b94..24440b196 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp @@ -20,5 +20,6 @@ #include "type_traits/is_signed.hpp" #include "type_traits/is_unsigned.hpp" #include "type_traits/make_unsigned.hpp" +#include "type_traits/make_void.hpp" #include "type_traits/remove_const.hpp" #include "type_traits/remove_reference.hpp" diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp index 847525a93..b3a073b83 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp @@ -7,9 +7,9 @@ #include "declval.hpp" #ifdef _MSC_VER -#pragma warning(push) +# pragma warning(push) // conversion from 'T' to 'To', possible loss of data -#pragma warning(disable : 4244) +# pragma warning(disable : 4244) #endif // clang-format off @@ -37,7 +37,7 @@ struct is_convertible { } // namespace ARDUINOJSON_NAMESPACE #ifdef _MSC_VER -#pragma warning(pop) +# pragma warning(pop) #endif // clang-format off diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp index b7e9d3d24..fb42f0598 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp @@ -5,15 +5,16 @@ #pragma once #include "integral_constant.hpp" +#include "is_same.hpp" +#include "remove_cv.hpp" namespace ARDUINOJSON_NAMESPACE { -template -struct is_floating_point : false_type {}; +template +struct is_floating_point + : integral_constant< + bool, // + is_same::type>::value || + is_same::type>::value> {}; -template <> -struct is_floating_point : true_type {}; - -template <> -struct is_floating_point : true_type {}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp index 26e895c8a..65918cf9c 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp @@ -5,29 +5,33 @@ #pragma once #include + +#include "integral_constant.hpp" #include "is_same.hpp" +#include "remove_cv.hpp" namespace ARDUINOJSON_NAMESPACE { -// A meta-function that returns true if T is an integral type. +// clang-format off template -struct is_integral { - static const bool value = - is_same::value || is_same::value || - is_same::value || is_same::value || - is_same::value || is_same::value || - is_same::value || is_same::value || +struct is_integral : integral_constant::type, signed char>::value || + is_same::type, unsigned char>::value || + is_same::type, signed short>::value || + is_same::type, unsigned short>::value || + is_same::type, signed int>::value || + is_same::type, unsigned int>::value || + is_same::type, signed long>::value || + is_same::type, unsigned long>::value || #if ARDUINOJSON_HAS_LONG_LONG - is_same::value || - is_same::value || + is_same::type, signed long long>::value || + is_same::type, unsigned long long>::value || #endif #if ARDUINOJSON_HAS_INT64 - is_same::value || - is_same::value || + is_same::type, signed __int64>::value || + is_same::type, unsigned __int64>::value || #endif - is_same::value || is_same::value; -}; - -template -struct is_integral : is_integral {}; + is_same::type, char>::value || + is_same::type, bool>::value> {}; +// clang-format on } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp index fbb701cf7..3e064e315 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp @@ -5,39 +5,26 @@ #pragma once #include "integral_constant.hpp" +#include "is_same.hpp" +#include "remove_cv.hpp" + namespace ARDUINOJSON_NAMESPACE { -template -struct is_signed : false_type {}; - -template <> -struct is_signed : true_type {}; - -template <> -struct is_signed : true_type {}; - -template <> -struct is_signed : true_type {}; - -template <> -struct is_signed : true_type {}; - -template <> -struct is_signed : true_type {}; - -template <> -struct is_signed : true_type {}; - -template <> -struct is_signed : true_type {}; - +// clang-format off +template +struct is_signed : integral_constant::type, char>::value || + is_same::type, signed char>::value || + is_same::type, signed short>::value || + is_same::type, signed int>::value || + is_same::type, signed long>::value || #if ARDUINOJSON_HAS_LONG_LONG -template <> -struct is_signed : true_type {}; + is_same::type, signed long long>::value || #endif - #if ARDUINOJSON_HAS_INT64 -template <> -struct is_signed : true_type {}; + is_same::type, signed __int64>::value || #endif + is_same::type, float>::value || + is_same::type, double>::value> {}; +// clang-format on } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp index be2649829..57acff232 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp @@ -5,33 +5,24 @@ #pragma once #include "integral_constant.hpp" +#include "is_same.hpp" +#include "remove_cv.hpp" + namespace ARDUINOJSON_NAMESPACE { -template -struct is_unsigned : false_type {}; - -template <> -struct is_unsigned : true_type {}; - -template <> -struct is_unsigned : true_type {}; - -template <> -struct is_unsigned : true_type {}; - -template <> -struct is_unsigned : true_type {}; - -template <> -struct is_unsigned : true_type {}; - +// clang-format off +template +struct is_unsigned : integral_constant::type, unsigned char>::value || + is_same::type, unsigned short>::value || + is_same::type, unsigned int>::value || + is_same::type, unsigned long>::value || #if ARDUINOJSON_HAS_INT64 -template <> -struct is_unsigned : true_type {}; + is_same::type, unsigned __int64>::value || #endif - #if ARDUINOJSON_HAS_LONG_LONG -template <> -struct is_unsigned : true_type {}; + is_same::type, unsigned long long>::value || #endif + is_same::type, bool>::value> {}; +// clang-format on } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp new file mode 100644 index 000000000..cb2ebde52 --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp @@ -0,0 +1,14 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +namespace ARDUINOJSON_NAMESPACE { + +template +struct make_void { + typedef void type; +}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp new file mode 100644 index 000000000..021e0ec5c --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp @@ -0,0 +1,27 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include + +namespace ARDUINOJSON_NAMESPACE { + +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +template +struct remove_cv { + typedef T type; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp b/lib/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp index 52bd1175d..f9a979855 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp @@ -31,17 +31,17 @@ class Writer { #include #if ARDUINOJSON_ENABLE_STD_STRING -#include +# include #endif #if ARDUINOJSON_ENABLE_ARDUINO_STRING -#include +# include #endif #if ARDUINOJSON_ENABLE_STD_STREAM -#include +# include #endif #if ARDUINOJSON_ENABLE_ARDUINO_PRINT -#include +# include #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp b/lib/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp index 5efa6e492..5bb1ebd78 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp @@ -22,10 +22,10 @@ class Writer< ::String, void> { } size_t write(uint8_t c) { - ARDUINOJSON_ASSERT(_size < bufferCapacity); - _buffer[_size++] = static_cast(c); if (_size + 1 >= bufferCapacity) - flush(); + if (flush() != 0) + return 0; + _buffer[_size++] = static_cast(c); return 1; } @@ -36,14 +36,15 @@ class Writer< ::String, void> { return n; } - private: - void flush() { + size_t flush() { ARDUINOJSON_ASSERT(_size < bufferCapacity); _buffer[_size] = 0; - *_destination += _buffer; - _size = 0; + if (_destination->concat(_buffer)) + _size = 0; + return _size; } + private: ::String *_destination; char _buffer[bufferCapacity]; size_t _size; diff --git a/lib/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp b/lib/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp index 8b1104b4c..80670aad3 100644 --- a/lib/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp @@ -55,8 +55,12 @@ class StringCopier { private: MemoryPool* _pool; + + // These fields aren't initialized by the constructor but startString() + // + // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject) char* _ptr; - size_t _size; - size_t _capacity; + // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject) + size_t _size, _capacity; }; } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/ArduinoStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/ArduinoStringAdapter.hpp similarity index 63% rename from lib/ArduinoJson/src/ArduinoJson/Strings/ArduinoStringAdapter.hpp rename to lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/ArduinoStringAdapter.hpp index 24646ccda..5e4b62403 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/ArduinoStringAdapter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/ArduinoStringAdapter.hpp @@ -7,14 +7,15 @@ #include #include -#include #include +#include namespace ARDUINOJSON_NAMESPACE { -class ArduinoStringAdapter { +template <> +class StringAdapter< ::String> { public: - ArduinoStringAdapter(const ::String& str) : _str(&str) {} + StringAdapter(const ::String& str) : _str(&str) {} void copyTo(char* p, size_t n) const { memcpy(p, _str->c_str(), n); @@ -31,18 +32,10 @@ class ArduinoStringAdapter { return safe_strcmp(me, other); } - bool equals(const char* expected) const { - return compare(expected) == 0; - } - size_t size() const { return _str->length(); } - const char* begin() const { - return _str->c_str(); - } - typedef storage_policies::store_by_copy storage_policy; private: @@ -50,13 +43,10 @@ class ArduinoStringAdapter { }; template <> -struct IsString< ::String> : true_type {}; - -template <> -struct IsString< ::StringSumHelper> : true_type {}; - -inline ArduinoStringAdapter adaptString(const ::String& str) { - return ArduinoStringAdapter(str); -} +class StringAdapter< ::StringSumHelper> : public StringAdapter< ::String> { + public: + StringAdapter< ::StringSumHelper>(const ::String& s) + : StringAdapter< ::String>(s) {} +}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/ConstRamStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/ConstRamStringAdapter.hpp similarity index 61% rename from lib/ArduinoJson/src/ArduinoJson/Strings/ConstRamStringAdapter.hpp rename to lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/ConstRamStringAdapter.hpp index ec7d53a6d..1ca7c02cc 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/ConstRamStringAdapter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/ConstRamStringAdapter.hpp @@ -8,23 +8,20 @@ #include // strcmp #include -#include #include +#include namespace ARDUINOJSON_NAMESPACE { -class ConstRamStringAdapter { +template <> +class StringAdapter { public: - ConstRamStringAdapter(const char* str = 0) : _str(str) {} + StringAdapter(const char* str = 0) : _str(str) {} int compare(const char* other) const { return safe_strcmp(_str, other); } - bool equals(const char* expected) const { - return compare(expected) == 0; - } - bool isNull() const { return !_str; } @@ -39,24 +36,16 @@ class ConstRamStringAdapter { return _str; } - const char* begin() const { - return _str; - } - typedef storage_policies::store_by_address storage_policy; protected: const char* _str; }; -template <> -struct IsString : true_type {}; - template -struct IsString : true_type {}; - -inline ConstRamStringAdapter adaptString(const char* str) { - return ConstRamStringAdapter(str); -} +class StringAdapter : public StringAdapter { + public: + StringAdapter(const char* s) : StringAdapter(s) {} +}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/FlashStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/FlashStringAdapter.hpp similarity index 62% rename from lib/ArduinoJson/src/ArduinoJson/Strings/FlashStringAdapter.hpp rename to lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/FlashStringAdapter.hpp index 292e348b4..3a958181d 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/FlashStringAdapter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/FlashStringAdapter.hpp @@ -5,15 +5,15 @@ #pragma once #include -#include -#include #include +#include namespace ARDUINOJSON_NAMESPACE { -class FlashStringAdapter { +template <> +class StringAdapter { public: - FlashStringAdapter(const __FlashStringHelper* str) : _str(str) {} + StringAdapter(const __FlashStringHelper* str) : _str(str) {} int compare(const char* other) const { if (!other && !_str) @@ -25,10 +25,6 @@ class FlashStringAdapter { return -strcmp_P(other, reinterpret_cast(_str)); } - bool equals(const char* expected) const { - return compare(expected) == 0; - } - bool isNull() const { return !_str; } @@ -43,20 +39,10 @@ class FlashStringAdapter { return strlen_P(reinterpret_cast(_str)); } - FlashStringIterator begin() const { - return FlashStringIterator(_str); - } - typedef storage_policies::store_by_copy storage_policy; private: const __FlashStringHelper* _str; }; -inline FlashStringAdapter adaptString(const __FlashStringHelper* str) { - return FlashStringAdapter(str); -} - -template <> -struct IsString : true_type {}; } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonStringAdapter.hpp new file mode 100644 index 000000000..c34abce5e --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonStringAdapter.hpp @@ -0,0 +1,27 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include +#include + +namespace ARDUINOJSON_NAMESPACE { + +template <> +class StringAdapter : public StringAdapter { + public: + StringAdapter(const String& str) + : StringAdapter(str.c_str()), _isStatic(str.isStatic()) {} + + bool isStatic() const { + return _isStatic; + } + + typedef storage_policies::decide_at_runtime storage_policy; + + private: + bool _isStatic; +}; +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamStringAdapter.hpp new file mode 100644 index 000000000..f2b01d171 --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamStringAdapter.hpp @@ -0,0 +1,29 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include +#include +#include + +namespace ARDUINOJSON_NAMESPACE { + +template +class StringAdapter::value>::type> + : public StringAdapter { + public: + StringAdapter(const TChar* str) + : StringAdapter(reinterpret_cast(str)) {} + + void copyTo(char* p, size_t n) const { + memcpy(p, _str, n); + } + + typedef ARDUINOJSON_NAMESPACE::storage_policies::store_by_copy storage_policy; +}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/SizedFlashStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/SizedFlashStringAdapter.hpp similarity index 61% rename from lib/ArduinoJson/src/ArduinoJson/Strings/SizedFlashStringAdapter.hpp rename to lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/SizedFlashStringAdapter.hpp index b9cc0bff8..b2d012fce 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/SizedFlashStringAdapter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/SizedFlashStringAdapter.hpp @@ -5,15 +5,15 @@ #pragma once #include -#include -#include #include +#include namespace ARDUINOJSON_NAMESPACE { -class SizedFlashStringAdapter { +template <> +class StringAdapter { public: - SizedFlashStringAdapter(const __FlashStringHelper* str, size_t sz) + StringAdapter(const __FlashStringHelper* str, size_t sz) : _str(str), _size(sz) {} int compare(const char* other) const { @@ -26,10 +26,6 @@ class SizedFlashStringAdapter { return -strncmp_P(other, reinterpret_cast(_str), _size); } - bool equals(const char* expected) const { - return compare(expected) == 0; - } - bool isNull() const { return !_str; } @@ -42,10 +38,6 @@ class SizedFlashStringAdapter { return _size; } - FlashStringIterator begin() const { - return FlashStringIterator(_str); - } - typedef storage_policies::store_by_copy storage_policy; private: @@ -53,8 +45,4 @@ class SizedFlashStringAdapter { size_t _size; }; -inline SizedFlashStringAdapter adaptString(const __FlashStringHelper* str, - size_t sz) { - return SizedFlashStringAdapter(str, sz); -} } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/SizedRamStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/SizedRamStringAdapter.hpp similarity index 61% rename from lib/ArduinoJson/src/ArduinoJson/Strings/SizedRamStringAdapter.hpp rename to lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/SizedRamStringAdapter.hpp index fe23408f8..a18d5ab92 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/SizedRamStringAdapter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/SizedRamStringAdapter.hpp @@ -5,25 +5,22 @@ #pragma once #include -#include #include +#include #include // strcmp namespace ARDUINOJSON_NAMESPACE { -class SizedRamStringAdapter { +template +class StringAdapter { public: - SizedRamStringAdapter(const char* str, size_t n) : _str(str), _size(n) {} + StringAdapter(const char* str, size_t n) : _str(str), _size(n) {} int compare(const char* other) const { return safe_strncmp(_str, other, _size); } - bool equals(const char* expected) const { - return compare(expected) == 0; - } - bool isNull() const { return !_str; } @@ -36,10 +33,6 @@ class SizedRamStringAdapter { return _size; } - const char* begin() const { - return _str; - } - typedef storage_policies::store_by_copy storage_policy; private: @@ -47,9 +40,4 @@ class SizedRamStringAdapter { size_t _size; }; -template -inline SizedRamStringAdapter adaptString(const TChar* str, size_t size) { - return SizedRamStringAdapter(reinterpret_cast(str), size); -} - } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdStringAdapter.hpp new file mode 100644 index 000000000..4d2d32c52 --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdStringAdapter.hpp @@ -0,0 +1,46 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include +#include +#include + +#include + +namespace ARDUINOJSON_NAMESPACE { + +template +class StringAdapter > { + public: + typedef std::basic_string string_type; + + StringAdapter(const string_type& str) : _str(&str) {} + + void copyTo(char* p, size_t n) const { + memcpy(p, _str->c_str(), n); + } + + bool isNull() const { + return false; + } + + int compare(const char* other) const { + if (!other) + return 1; + return _str->compare(other); + } + + size_t size() const { + return _str->size(); + } + + typedef storage_policies::store_by_copy storage_policy; + + private: + const string_type* _str; +}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringViewAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringViewAdapter.hpp new file mode 100644 index 000000000..787f7c21f --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringViewAdapter.hpp @@ -0,0 +1,44 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include +#include +#include + +#include + +namespace ARDUINOJSON_NAMESPACE { + +template <> +class StringAdapter { + public: + StringAdapter(std::string_view str) : _str(str) {} + + void copyTo(char* p, size_t n) const { + memcpy(p, _str.data(), n); + } + + bool isNull() const { + return false; + } + + int compare(const char* other) const { + if (!other) + return 1; + return _str.compare(other); + } + + size_t size() const { + return _str.size(); + } + + typedef storage_policies::store_by_copy storage_policy; + + private: + std::string_view _str; +}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/FlashStringIterator.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/FlashStringIterator.hpp deleted file mode 100644 index 9a97f3247..000000000 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/FlashStringIterator.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// ArduinoJson - https://arduinojson.org -// Copyright Benoit Blanchon 2014-2021 -// MIT License - -#pragma once - -namespace ARDUINOJSON_NAMESPACE { - -class FlashStringIterator { - public: - explicit FlashStringIterator(const __FlashStringHelper* ptr) - : _ptr(reinterpret_cast(ptr)) {} - - explicit FlashStringIterator(const char* ptr) : _ptr(ptr) {} - - FlashStringIterator operator+(ptrdiff_t d) const { - return FlashStringIterator(_ptr + d); - } - - ptrdiff_t operator-(FlashStringIterator other) const { - return _ptr - other._ptr; - } - - FlashStringIterator operator++(int) { - return FlashStringIterator(_ptr++); - } - - FlashStringIterator operator++() { - return FlashStringIterator(++_ptr); - } - - bool operator!=(FlashStringIterator other) const { - return _ptr != other._ptr; - } - - char operator*() const { - return char(pgm_read_byte(_ptr)); - } - - private: - const char* _ptr; -}; - -} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp deleted file mode 100644 index af5a91a22..000000000 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// ArduinoJson - https://arduinojson.org -// Copyright Benoit Blanchon 2014-2021 -// MIT License - -#pragma once - -#include - -namespace ARDUINOJSON_NAMESPACE { -template -struct IsString : false_type {}; - -template -struct IsString : IsString {}; - -template -struct IsString : IsString {}; -} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/IsWriteableString.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/IsWriteableString.hpp index 32039e3dd..556c4765a 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/IsWriteableString.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/IsWriteableString.hpp @@ -8,11 +8,11 @@ #include #if ARDUINOJSON_ENABLE_ARDUINO_STRING -#include +# include #endif #if ARDUINOJSON_ENABLE_STD_STRING -#include +# include #endif namespace ARDUINOJSON_NAMESPACE { diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/RamStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/RamStringAdapter.hpp deleted file mode 100644 index eded6c97d..000000000 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/RamStringAdapter.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// ArduinoJson - https://arduinojson.org -// Copyright Benoit Blanchon 2014-2021 -// MIT License - -#pragma once - -#include -#include -#include - -namespace ARDUINOJSON_NAMESPACE { - -class RamStringAdapter : public ConstRamStringAdapter { - public: - RamStringAdapter(const char* str) : ConstRamStringAdapter(str) {} - - void copyTo(char* p, size_t n) const { - memcpy(p, _str, n); - } - - typedef ARDUINOJSON_NAMESPACE::storage_policies::store_by_copy storage_policy; -}; - -template -inline RamStringAdapter adaptString(const TChar* str) { - return RamStringAdapter(reinterpret_cast(str)); -} - -inline RamStringAdapter adaptString(char* str) { - return RamStringAdapter(str); -} - -template -struct IsString { - static const bool value = sizeof(TChar) == 1; -}; - -template <> -struct IsString { - static const bool value = false; -}; - -} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/StdStringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/StdStringAdapter.hpp deleted file mode 100644 index ebf4c3969..000000000 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/StdStringAdapter.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// ArduinoJson - https://arduinojson.org -// Copyright Benoit Blanchon 2014-2021 -// MIT License - -#pragma once - -#include -#include -#include - -#include - -namespace ARDUINOJSON_NAMESPACE { - -template -class StdStringAdapter { - public: - StdStringAdapter(const TString& str) : _str(&str) {} - - void copyTo(char* p, size_t n) const { - memcpy(p, _str->c_str(), n); - } - - bool isNull() const { - return false; - } - - int compare(const char* other) const { - if (!other) - return 1; - return _str->compare(other); - } - - bool equals(const char* expected) const { - if (!expected) - return false; - return *_str == expected; - } - - size_t size() const { - return _str->size(); - } - - const char* begin() const { - return _str->c_str(); - } - - typedef storage_policies::store_by_copy storage_policy; - - private: - const TString* _str; -}; - -template -struct IsString > : true_type { -}; - -template -inline StdStringAdapter > -adaptString(const std::basic_string& str) { - return StdStringAdapter >( - str); -} - -} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/String.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/String.hpp index 4f2abdea4..fff4077d9 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/String.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/String.hpp @@ -4,10 +4,6 @@ #pragma once -#include -#include -#include - namespace ARDUINOJSON_NAMESPACE { class String { @@ -53,25 +49,4 @@ class String { bool _isStatic; }; -class StringAdapter : public RamStringAdapter { - public: - StringAdapter(const String& str) - : RamStringAdapter(str.c_str()), _isStatic(str.isStatic()) {} - - bool isStatic() const { - return _isStatic; - } - - typedef storage_policies::decide_at_runtime storage_policy; - - private: - bool _isStatic; -}; - -template <> -struct IsString : true_type {}; - -inline StringAdapter adaptString(const String& str) { - return StringAdapter(str); -} } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp new file mode 100644 index 000000000..1d55b2123 --- /dev/null +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp @@ -0,0 +1,32 @@ +// ArduinoJson - https://arduinojson.org +// Copyright Benoit Blanchon 2014-2021 +// MIT License + +#pragma once + +#include + +namespace ARDUINOJSON_NAMESPACE { + +template +class StringAdapter; + +template +inline StringAdapter adaptString(const T& str) { + return StringAdapter(str); +} + +template +inline StringAdapter adaptString(const T& str, size_t sz) { + return StringAdapter(str, sz); +} + +template +struct IsString : false_type {}; + +template +struct IsString< + T, typename make_void::storage_policy>::type> + : true_type {}; + +} // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp b/lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp index 3d294d29c..ba763a67f 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp @@ -4,19 +4,24 @@ #pragma once -#include -#include -#include +#include +#include +#include +#include #if ARDUINOJSON_ENABLE_STD_STRING -#include +# include +#endif + +#if ARDUINOJSON_ENABLE_STRING_VIEW +# include #endif #if ARDUINOJSON_ENABLE_ARDUINO_STRING -#include +# include #endif #if ARDUINOJSON_ENABLE_PROGMEM -#include -#include +# include +# include #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp b/lib/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp index 476ca8fdf..88afa526d 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp @@ -9,4 +9,9 @@ namespace ARDUINOJSON_NAMESPACE { template struct Converter; +// clang-format off +template +class InvalidConversion; // Error here? See https://arduinojson.org/v6/invalid-conversion/ +// clang-format on + } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp b/lib/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp index 34e12bbb7..33f8c6509 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp @@ -12,9 +12,9 @@ namespace ARDUINOJSON_NAMESPACE { template struct Converter { - static bool toJson(const T& src, VariantRef dst) { + static void toJson(const T& src, VariantRef dst) { // clang-format off - return convertToJson(src, dst); // Error here? See https://arduinojson.org/v6/unsupported-set/ + convertToJson(src, dst); // Error here? See https://arduinojson.org/v6/unsupported-set/ // clang-format on } @@ -38,13 +38,11 @@ template struct Converter< T, typename enable_if::value && !is_same::value && !is_same::value>::type> { - static bool toJson(T src, VariantRef dst) { + static void toJson(T src, VariantRef dst) { VariantData* data = getData(dst); ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T); - if (!data) - return false; - data->setInteger(src); - return true; + if (data) + data->setInteger(src); } static T fromJson(VariantConstRef src) { @@ -61,8 +59,8 @@ struct Converter< template struct Converter::value>::type> { - static bool toJson(T src, VariantRef dst) { - return dst.set(static_cast(src)); + static void toJson(T src, VariantRef dst) { + dst.set(static_cast(src)); } static T fromJson(VariantConstRef src) { @@ -78,12 +76,10 @@ struct Converter::value>::type> { template <> struct Converter { - static bool toJson(bool src, VariantRef dst) { + static void toJson(bool src, VariantRef dst) { VariantData* data = getData(dst); - if (!data) - return false; - data->setBoolean(src); - return true; + if (data) + data->setBoolean(src); } static bool fromJson(VariantConstRef src) { @@ -99,12 +95,10 @@ struct Converter { template struct Converter::value>::type> { - static bool toJson(T src, VariantRef dst) { + static void toJson(T src, VariantRef dst) { VariantData* data = getData(dst); - if (!data) - return false; - data->setFloat(static_cast(src)); - return true; + if (data) + data->setFloat(static_cast(src)); } static T fromJson(VariantConstRef src) { @@ -120,8 +114,8 @@ struct Converter::value>::type> { template <> struct Converter { - static bool toJson(const char* src, VariantRef dst) { - return variantSetString(getData(dst), adaptString(src), getPool(dst)); + static void toJson(const char* src, VariantRef dst) { + variantSetString(getData(dst), adaptString(src), getPool(dst)); } static const char* fromJson(VariantConstRef src) { @@ -163,12 +157,10 @@ canConvertFromJson(VariantConstRef src, const T&) { template <> struct Converter > { - static bool toJson(SerializedValue src, VariantRef dst) { + static void toJson(SerializedValue src, VariantRef dst) { VariantData* data = getData(dst); - if (!data) - return false; - data->setLinkedRaw(src); - return true; + if (data) + data->setLinkedRaw(src); } }; @@ -178,10 +170,11 @@ struct Converter > { template struct Converter, typename enable_if::value>::type> { - static bool toJson(SerializedValue src, VariantRef dst) { + static void toJson(SerializedValue src, VariantRef dst) { VariantData* data = getData(dst); MemoryPool* pool = getPool(dst); - return data != 0 && data->setOwnedRaw(src, pool); + if (data) + data->setOwnedRaw(src, pool); } }; @@ -189,9 +182,8 @@ struct Converter, template <> struct Converter { - static bool toJson(decltype(nullptr), VariantRef dst) { + static void toJson(decltype(nullptr), VariantRef dst) { variantSetNull(getData(dst)); - return true; } static decltype(nullptr) fromJson(VariantConstRef) { return nullptr; @@ -247,20 +239,33 @@ class MemoryPoolPrint : public Print { size_t _capacity; }; -inline bool convertToJson(const ::Printable& src, VariantRef dst) { +inline void convertToJson(const ::Printable& src, VariantRef dst) { MemoryPool* pool = getPool(dst); VariantData* data = getData(dst); if (!pool || !data) - return false; + return; MemoryPoolPrint print(pool); src.printTo(print); if (print.overflowed()) { pool->markAsOverflowed(); data->setNull(); - return false; + return; } data->setStringPointer(print.c_str(), storage_policies::store_by_copy()); - return true; +} + +#endif + +#if ARDUINOJSON_ENABLE_STRING_VIEW + +inline void convertFromJson(VariantConstRef src, std::string_view& dst) { + const char* str = src.as(); + if (str) // the standard doesn't allow passing null to the constructor + dst = std::string_view(str); +} + +inline bool canConvertFromJson(VariantConstRef src, const std::string_view&) { + return src.is(); } #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp index 025ef90a2..4e0471a60 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace ARDUINOJSON_NAMESPACE { diff --git a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp index 82ae745d4..388b7e251 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp @@ -7,17 +7,17 @@ #include #include #include -#include +#include #include // VariantData can't have a constructor (to be a POD), so we have no way to fix // this warning #if defined(__GNUC__) -#if __GNUC__ >= 7 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#pragma GCC diagnostic ignored "-Wuninitialized" -#endif +# if __GNUC__ >= 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# pragma GCC diagnostic ignored "-Wuninitialized" +# endif #endif namespace ARDUINOJSON_NAMESPACE { @@ -33,7 +33,7 @@ class VariantData { // - no virtual // - no inheritance void init() { - _flags = 0; + _flags = VALUE_IS_NULL; } template @@ -103,7 +103,8 @@ class VariantData { case VALUE_IS_OBJECT: return toObject().copyFrom(src._content.asCollection, pool); case VALUE_IS_OWNED_STRING: - return setString(RamStringAdapter(src._content.asString), pool); + return setString(adaptString(const_cast(src._content.asString)), + pool); case VALUE_IS_OWNED_RAW: return setOwnedRaw( serialized(src._content.asRaw.data, src._content.asRaw.size), pool); @@ -362,7 +363,7 @@ class VariantData { } // namespace ARDUINOJSON_NAMESPACE #if defined(__GNUC__) -#if __GNUC__ >= 8 -#pragma GCC diagnostic pop -#endif +# if __GNUC__ >= 8 +# pragma GCC diagnostic pop +# endif #endif diff --git a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp index b91e58450..ea49a92f6 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp @@ -140,4 +140,9 @@ inline VariantConstRef operator|(VariantConstRef preferedValue, VariantConstRef defaultValue) { return preferedValue ? preferedValue : defaultValue; } + +// Out of class definition to avoid #1560 +inline bool VariantRef::set(char value) const { + return set(value); +} } // namespace ARDUINOJSON_NAMESPACE diff --git a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantRef.hpp b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantRef.hpp index 8d4c41088..b05ed90ca 100644 --- a/lib/ArduinoJson/src/ArduinoJson/Variant/VariantRef.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/Variant/VariantRef.hpp @@ -85,18 +85,18 @@ class VariantRef : public VariantRefBase, template FORCE_INLINE bool set(const T &value) const { - return Converter::toJson(value, *this); + Converter::toJson(value, *this); + return _pool && !_pool->overflowed(); } - FORCE_INLINE bool ARDUINOJSON_DEPRECATED( + bool ARDUINOJSON_DEPRECATED( "Support for char is deprecated, use int8_t or uint8_t instead") - set(char value) const { - return set(value); - } + set(char value) const; template FORCE_INLINE bool set(T *value) const { - return Converter::toJson(value, *this); + Converter::toJson(value, *this); + return _pool && !_pool->overflowed(); } template @@ -341,16 +341,22 @@ class VariantConstRef : public VariantRefBase, template <> struct Converter { - static bool toJson(VariantRef src, VariantRef dst) { - return variantCopyFrom(getData(dst), getData(src), getPool(dst)); + static void toJson(VariantRef src, VariantRef dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); } + static VariantRef fromJson(VariantRef src) { return src; } + + static InvalidConversion fromJson( + VariantConstRef); + static bool checkJson(VariantRef src) { VariantData *data = getData(src); return !!data; } + static bool checkJson(VariantConstRef) { return false; } @@ -358,8 +364,8 @@ struct Converter { template <> struct Converter { - static bool toJson(VariantConstRef src, VariantRef dst) { - return variantCopyFrom(getData(dst), getData(src), getPool(dst)); + static void toJson(VariantConstRef src, VariantRef dst) { + variantCopyFrom(getData(dst), getData(src), getPool(dst)); } static VariantConstRef fromJson(VariantConstRef src) { diff --git a/lib/ArduinoJson/src/ArduinoJson/version.hpp b/lib/ArduinoJson/src/ArduinoJson/version.hpp index d8aaf77d1..6ddc84486 100644 --- a/lib/ArduinoJson/src/ArduinoJson/version.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/version.hpp @@ -4,7 +4,7 @@ #pragma once -#define ARDUINOJSON_VERSION "6.18.0" +#define ARDUINOJSON_VERSION "6.18.3" #define ARDUINOJSON_VERSION_MAJOR 6 #define ARDUINOJSON_VERSION_MINOR 18 -#define ARDUINOJSON_VERSION_REVISION 0 +#define ARDUINOJSON_VERSION_REVISION 3 diff --git a/lib_standalone/WString.h b/lib_standalone/WString.h index 5914fd141..04cb3d2e5 100644 --- a/lib_standalone/WString.h +++ b/lib_standalone/WString.h @@ -45,6 +45,13 @@ class String { return _str == s; } + bool concat(const char * rhs) { + if (!rhs) { + return 0; + } + _str += rhs; + return 1; + } private: std::string _str; @@ -60,7 +67,6 @@ inline bool operator!=(const ::String & lhs, const ::String & rhs) { return lhs.c_str() != rhs.c_str(); } - size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize); size_t strlcat(char * dst, const char * src, size_t siz); From aa9ba65f7076a251e5f47af3f2789fcf212e0c01 Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 28 Jul 2021 10:40:17 +0200 Subject: [PATCH 101/122] comment changes --- src/test/test.cpp | 3 +++ src/web/WebAPIService.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/test.cpp b/src/test/test.cpp index 16b73f49f..019a56ef9 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -947,6 +947,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) { request.url("/api/system/commands"); EMSESP::webAPIService.webAPIService_get(&request); + request.url("/api/boiler/info"); + EMSESP::webAPIService.webAPIService_get(&request); + // POST request.method(HTTP_POST); request.url("/api/system/commands"); diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index f0d0002b5..c2452c167 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -126,7 +126,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, std::string & device_ id_n = Helpers::atoint(request->getParam("hc")->value().c_str()); } } else { - // parse paths and json data + // parse paths and json data from the OpenAPI standard // /{device}[/{hc}][/{name}] // first param must be a valid device, which includes "system" device_s = p.paths().front(); From 87e6691433597f14a25b897b1073285f27684b9d Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 28 Jul 2021 18:00:14 +0200 Subject: [PATCH 102/122] remove comment --- lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index fbeb05df2..1b153a7ca 100644 --- a/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/lib/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -40,7 +40,7 @@ class MsgPackDeserializer { template bool parseVariant(VariantData *variant, TFilter filter, NestingLimit nestingLimit) { - uint8_t code = 0; // TODO: why do we need to initialize this variable? + uint8_t code = 0; if (!readByte(code)) return false; From c55385d6d888690beb7ca60441a62edd22ac3e6b Mon Sep 17 00:00:00 2001 From: proddy Date: Wed, 28 Jul 2021 18:00:26 +0200 Subject: [PATCH 103/122] update mock data --- mock-api/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mock-api/server.js b/mock-api/server.js index 46b2600a2..215d50f08 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -246,7 +246,7 @@ const UPLOAD_FIRMWARE_ENDPOINT = REST_ENDPOINT_ROOT + 'uploadFirmware' const SIGN_IN_ENDPOINT = REST_ENDPOINT_ROOT + 'signIn' const GENERATE_TOKEN_ENDPOINT = REST_ENDPOINT_ROOT + 'generateToken' const system_status = { - emsesp_version: '3.1 demo', + emsesp_version: '3.x demo', esp_platform: 'ESP32', max_alloc_heap: 113792, psram_size: 0, @@ -349,7 +349,7 @@ const emsesp_data = { }, ], sensors: [ - { no: 1, id: '28-233D-9497-0C03', temp: 25.7, offset: 12 }, + { no: 1, id: '28-233D-9497-0C03', temp: 25.7, offset: 1.2 }, { no: 2, id: '28-243D-7437-1E3A', temp: 26.1, offset: 0 }, ], } From a8382dd6cedc10fe828a958361f032a423cafc2f Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 29 Jul 2021 16:52:06 +0200 Subject: [PATCH 104/122] bump version --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 1dc1c7f43..ab4fe1303 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.2.0b1" +#define EMSESP_APP_VERSION "3.2.0b2" From 8f438e80458832a8d882b1d0c93b573e095fdf69 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 29 Jul 2021 16:52:33 +0200 Subject: [PATCH 105/122] rename add_json() --- src/command.cpp | 2 +- src/command.h | 2 +- src/dallassensor.cpp | 4 ++-- src/emsesp.cpp | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index deb5fcc3a..03456ca95 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -189,7 +189,7 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cm // add a command to the list, which does return a json object as output // flag is fixed to MqttSubFlag::FLAG_NOSUB -void Command::add_returns_json(const uint8_t device_type, +void Command::add_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, diff --git a/src/command.h b/src/command.h index 653758ff7..e7df2e672 100644 --- a/src/command.h +++ b/src/command.h @@ -109,7 +109,7 @@ class Command { const __FlashStringHelper * description, uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); - static void add_returns_json(const uint8_t device_type, + static void add_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, diff --git a/src/dallassensor.cpp b/src/dallassensor.cpp index a7659acf3..c1ebc8574 100644 --- a/src/dallassensor.cpp +++ b/src/dallassensor.cpp @@ -41,12 +41,12 @@ void DallasSensor::start() { bus_.begin(dallas_gpio_); #endif // API calls - Command::add_returns_json( + Command::add_json( EMSdevice::DeviceType::DALLASSENSOR, F_(info), [&](const char * value, const int8_t id, JsonObject & json) { return command_info(value, id, json); }, F_(info_cmd)); - Command::add_returns_json( + Command::add_json( EMSdevice::DeviceType::DALLASSENSOR, F_(commands), [&](const char * value, const int8_t id, JsonObject & json) { return command_commands(value, id, json); }, diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 46a44a18e..6eb36ab0e 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -986,18 +986,18 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std:: return true; } - Command::add_returns_json( + Command::add_json( device_type, F_(info), [device_type](const char * value, const int8_t id, JsonObject & json) { return command_info(device_type, json, id, true); }, F_(info_cmd)); - Command::add_returns_json( + Command::add_json( device_type, F("info_short"), [device_type](const char * value, const int8_t id, JsonObject & json) { return command_info(device_type, json, id, false); }, nullptr, CommandFlag::HIDDEN); // this command is hidden - Command::add_returns_json( + Command::add_json( device_type, F_(commands), [device_type](const char * value, const int8_t id, JsonObject & json) { return command_commands(device_type, json, id); }, From 049be2484e8b873918d9ba4bbf6a2c584e357a63 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 29 Jul 2021 16:53:06 +0200 Subject: [PATCH 106/122] don't show dallas in system/info as its not an ems device --- src/system.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/system.cpp b/src/system.cpp index 4f6eba475..bb1ab41ff 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -614,9 +614,9 @@ void System::commands_init() { Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish"), CommandFlag::ADMIN_ONLY); Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"), CommandFlag::ADMIN_ONLY); Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restarts EMS-ESP"), CommandFlag::ADMIN_ONLY); - Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("system status")); - Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("list system settings")); - Command::add_returns_json(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("list system commands")); + Command::add_json(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("system status")); + Command::add_json(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("list system settings")); + Command::add_json(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("list system commands")); #if defined(EMSESP_DEBUG) Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("run tests")); #endif @@ -775,7 +775,7 @@ bool System::command_commands(const char * value, const int8_t id, JsonObject & } // export all settings to JSON text -// e.g. http://ems-esp/api?device=system&cmd=settings +// http://ems-esp/api/system/settings // value and id are ignored bool System::command_settings(const char * value, const int8_t id, JsonObject & json) { JsonObject node; @@ -878,8 +878,8 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject & return true; } -// export status information including some basic settings -// e.g. http://ems-esp/api?device=system&cmd=info +// export status information including the device information +// http://ems-esp/api/system/info bool System::command_info(const char * value, const int8_t id, JsonObject & json) { JsonObject node; @@ -934,12 +934,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json #endif } - JsonArray devices2 = json.createNestedArray("Devices"); - + // show EMS devices + JsonArray devices = json.createNestedArray("Devices"); for (const auto & device_class : EMSFactory::device_handlers()) { for (const auto & emsdevice : EMSESP::emsdevices) { if ((emsdevice) && (emsdevice->device_type() == device_class.first)) { - JsonObject obj = devices2.createNestedObject(); + JsonObject obj = devices.createNestedObject(); obj["type"] = emsdevice->device_type_name(); obj["name"] = emsdevice->to_string(); char result[200]; @@ -947,11 +947,6 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & json } } } - if (EMSESP::sensor_devices().size()) { - JsonObject obj = devices2.createNestedObject(); - obj["type"] = F_(Dallassensor); - obj["name"] = F_(Dallassensor); - } return true; } From 6af28b1c291d7e5cfd31065b0842d70d03f15bc5 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 29 Jul 2021 16:53:42 +0200 Subject: [PATCH 107/122] download button for log --- interface/src/api/Env.ts | 1 + interface/src/system/LogEventController.tsx | 72 ++++++++++++++++++++- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/interface/src/api/Env.ts b/interface/src/api/Env.ts index 3bfef1d74..16d361cf3 100644 --- a/interface/src/api/Env.ts +++ b/interface/src/api/Env.ts @@ -4,6 +4,7 @@ export const PROJECT_PATH = process.env.REACT_APP_PROJECT_PATH!; export const ENDPOINT_ROOT = calculateEndpointRoot('/rest/'); export const WEB_SOCKET_ROOT = calculateWebSocketRoot('/ws/'); export const EVENT_SOURCE_ROOT = calculateEndpointRoot('/es/'); +export const API_ENDPOINT_ROOT = calculateEndpointRoot('/api/'); function calculateEndpointRoot(endpointPath: string) { const httpRoot = process.env.REACT_APP_HTTP_ROOT; diff --git a/interface/src/system/LogEventController.tsx b/interface/src/system/LogEventController.tsx index f34fed1c2..fff3f9478 100644 --- a/interface/src/system/LogEventController.tsx +++ b/interface/src/system/LogEventController.tsx @@ -12,21 +12,29 @@ import { SelectValidator } from 'react-material-ui-form-validator'; -import { Grid, Slider, FormLabel, Checkbox, MenuItem } from '@material-ui/core'; +import { + Grid, + Slider, + FormLabel, + Checkbox, + MenuItem, + Button +} from '@material-ui/core'; import { addAccessTokenParameter, redirectingAuthorizedFetch } from '../authentication'; +import DownloadIcon from '@material-ui/icons/GetApp'; + import { ENDPOINT_ROOT, EVENT_SOURCE_ROOT } from '../api'; export const FETCH_LOG_ENDPOINT = ENDPOINT_ROOT + 'fetchLog'; export const LOG_SETTINGS_ENDPOINT = ENDPOINT_ROOT + 'logSettings'; export const LOG_EVENT_EVENT_SOURCE_URL = EVENT_SOURCE_ROOT + 'log'; import LogEventConsole from './LogEventConsole'; - -import { LogEvent, LogSettings } from './types'; +import { LogEvent, LogSettings, LogLevel } from './types'; import { Decoder } from '@msgpack/msgpack'; const decoder = new Decoder(); @@ -185,6 +193,54 @@ class LogEventController extends Component< }); }; + levelLabel = (level: LogLevel) => { + switch (level) { + case LogLevel.ERROR: + return 'E'; + case LogLevel.WARNING: + return 'W'; + case LogLevel.NOTICE: + return 'N'; + case LogLevel.INFO: + return 'I'; + case LogLevel.DEBUG: + return 'D'; + case LogLevel.TRACE: + return 'TRACE'; + default: + return ''; + } + }; + + onDownload = () => { + const { events, level } = this.state; + let result = ''; + for (const i in events) { + if (events[i].l <= level) { + result += + events[i].t + + ' ' + + this.levelLabel(events[i].l) + + ' ' + + events[i].i + + ': [' + + events[i].n + + '] ' + + events[i].m + + '\n'; + } + } + const a = document.createElement('a'); + a.setAttribute( + 'href', + 'data:text/plain;charset=utf-8,' + encodeURIComponent(result) + ); + a.setAttribute('download', 'log.txt'); + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + }; + render() { const { saveData } = this.props; return ( @@ -244,6 +300,16 @@ class LogEventController extends Component< label="Compact Layout" /> + + + From f210466cb17d8797818bf4d494fa174572e258ad Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 29 Jul 2021 16:54:08 +0200 Subject: [PATCH 108/122] download buttons for settings --- interface/src/project/EMSESPHelp.tsx | 89 +++++++++++++++++----------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/interface/src/project/EMSESPHelp.tsx b/interface/src/project/EMSESPHelp.tsx index 46905d542..363eb94c1 100644 --- a/interface/src/project/EMSESPHelp.tsx +++ b/interface/src/project/EMSESPHelp.tsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import { Component } from 'react'; import { Typography, Box, @@ -14,14 +14,40 @@ import CommentIcon from '@material-ui/icons/CommentTwoTone'; import MenuBookIcon from '@material-ui/icons/MenuBookTwoTone'; import GitHubIcon from '@material-ui/icons/GitHub'; import StarIcon from '@material-ui/icons/Star'; -import ImportExportIcon from '@material-ui/icons/ImportExport'; -import BugReportIcon from '@material-ui/icons/BugReportTwoTone'; +import DownloadIcon from '@material-ui/icons/GetApp'; -export const WebAPISystemSettings = - window.location.origin + '/api/system/settings'; -export const WebAPISystemInfo = window.location.origin + '/api/system/info'; +import { FormButton } from '../components'; + +import { API_ENDPOINT_ROOT } from '../api'; + +import { redirectingAuthorizedFetch } from '../authentication'; class EMSESPHelp extends Component { + onDownload = (endpoint: string) => { + redirectingAuthorizedFetch(API_ENDPOINT_ROOT + 'system/' + endpoint) + .then((response) => { + if (response.status === 200) { + return response.json(); + } + throw Error( + 'Device returned unexpected response code: ' + response.status + ); + }) + .then((json) => { + const a = document.createElement('a'); + const filename = 'emsesp_system_' + endpoint + '.txt'; + a.href = URL.createObjectURL( + new Blob([JSON.stringify(json, null, 2)], { + type: 'text/plain' + }) + ); + a.setAttribute('download', filename); + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + }); + }; + render() { return ( @@ -31,9 +57,9 @@ class EMSESPHelp extends Component { - For the latest news and updates go to the{' '} + For help and information on the latest updates visit the{' '} - {'official documentation'} website + {'online documentation'} @@ -55,41 +81,36 @@ class EMSESPHelp extends Component { - To report an issue or feature request go to{' '} + To report an issue or request a feature go to{' '} - {'click here'} - - - - - - - - - - To export your system settings{' '} - - {'click here'} - - - - - - - - - - To export the current status of EMS-ESP{' '} - - {'click here'} + {'GitHub'} + + } + variant="contained" + color="primary" + onClick={() => this.onDownload('info')} + > + download system info + + } + variant="contained" + color="primary" + onClick={() => this.onDownload('settings')} + > + download all settings + + + EMS-ESP is free and open-source. From 2ee0411582d3f94df76e2db76521ab2990f90709 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 29 Jul 2021 16:54:22 +0200 Subject: [PATCH 109/122] text changes --- interface/src/project/EMSESPSettingsForm.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/interface/src/project/EMSESPSettingsForm.tsx b/interface/src/project/EMSESPSettingsForm.tsx index 944e1bbb0..2aea95c4d 100644 --- a/interface/src/project/EMSESPSettingsForm.tsx +++ b/interface/src/project/EMSESPSettingsForm.tsx @@ -112,15 +112,17 @@ class EMSESPSettingsForm extends Component { - Adjust any of the EMS-ESP settings here. For help refer to the{' '} - - {'online documentation'} - - . + + consult the + + {' documentation'} + +  for help explaining the settings below + From e29fb9ba8a8af1fa37906d059479d2357fe66006 Mon Sep 17 00:00:00 2001 From: proddy Date: Thu, 29 Jul 2021 16:55:36 +0200 Subject: [PATCH 110/122] updated 3.2.0b2 --- CHANGELOG_LATEST.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index b933294b5..7e75d5f10 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -10,6 +10,7 @@ - settings for water hysteresis on/off - dallas sensor name editable. `sensorname` console-command, replace sensorid with a unique name [#84](https://github.com/emsesp/EMS-ESP32/issues/84) - 'restart' system command. Can be invoked via API with authentication. [#87](https://github.com/emsesp/EMS-ESP32/issues/87) +- add Download button in Web UI for log ## Fixed @@ -30,3 +31,4 @@ - UI improvements for editing Dallas Sensor details - RESTful GET commands can also require authentication (via bearer access token) for better security - Updated AsyncMqttClient to 0.9.0 and ArduinoJson to 6.18.3 +- Download buttons for settings and info under the Help tab From d16502c872442a42148f67ef221ae6d2d3d44b2f Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 1 Aug 2021 11:34:48 +0200 Subject: [PATCH 111/122] auto-formatting --- src/command.cpp | 6 +----- src/command.h | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 03456ca95..cfd886304 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -189,11 +189,7 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cm // add a command to the list, which does return a json object as output // flag is fixed to MqttSubFlag::FLAG_NOSUB -void Command::add_json(const uint8_t device_type, - const __FlashStringHelper * cmd, - cmdfunction_json_p cb, - const __FlashStringHelper * description, - uint8_t flags) { +void Command::add_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, uint8_t flags) { // if the command already exists for that device type don't add it if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) { return; diff --git a/src/command.h b/src/command.h index e7df2e672..bbc27a9fb 100644 --- a/src/command.h +++ b/src/command.h @@ -110,10 +110,10 @@ class Command { uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); static void add_json(const uint8_t device_type, - const __FlashStringHelper * cmd, - cmdfunction_json_p cb, - const __FlashStringHelper * description, - uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); + const __FlashStringHelper * cmd, + cmdfunction_json_p cb, + const __FlashStringHelper * description, + uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); static void show_all(uuid::console::Shell & shell); static Command::CmdFunction * find_command(const uint8_t device_type, const char * cmd); From b5892f5b5eef3cc518d01f359d9c18baf5085298 Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 1 Aug 2021 11:34:55 +0200 Subject: [PATCH 112/122] text changes --- interface/src/mqtt/MqttSettingsForm.tsx | 4 ++-- interface/src/project/EMSESPSettingsForm.tsx | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/mqtt/MqttSettingsForm.tsx b/interface/src/mqtt/MqttSettingsForm.tsx index b53b863a5..544ce786a 100644 --- a/interface/src/mqtt/MqttSettingsForm.tsx +++ b/interface/src/mqtt/MqttSettingsForm.tsx @@ -109,7 +109,7 @@ class MqttSettingsForm extends React.Component { /> { onChange={handleValueChange('ha_climate_format')} margin="normal" > - Use Current temperature (default) + Use Current temperature Use Setpoint temperature Always set to 0 diff --git a/interface/src/project/EMSESPSettingsForm.tsx b/interface/src/project/EMSESPSettingsForm.tsx index 2aea95c4d..af03d4c1e 100644 --- a/interface/src/project/EMSESPSettingsForm.tsx +++ b/interface/src/project/EMSESPSettingsForm.tsx @@ -113,7 +113,7 @@ class EMSESPSettingsForm extends Component { - consult the + Refer to the { > {' documentation'} -  for help explaining the settings below +  for information on each setting @@ -207,7 +207,7 @@ class EMSESPSettingsForm extends Component { Select a pre-configured board layout to automatically set the GPIO - pins, or set your own custom configuration + pins. Select "Custom..." to view or manually edit the values. @@ -417,7 +417,7 @@ class EMSESPSettingsForm extends Component { value="low_clock" /> } - label="Use lower CPU clock speed (only applied after restart)" + label="Run at a lower CPU clock speed" /> { value="notoken_api" /> } - label="Bypass Access Token authorization on API calls (warning! security vulnerability)" + label="Bypass Access Token authorization on API calls" /> { onChange={handleValueChange('dallas_format')} margin="normal" > - by ID - by Number - by Name + ID + Number + Name @@ -611,7 +611,7 @@ class EMSESPSettingsForm extends Component { 'Max value is 10' ]} name="syslog_mark_interval" - label="Mark Interval seconds (0=off)" + label="Mark Interval (seconds, 0=off)" fullWidth variant="outlined" value={data.syslog_mark_interval} @@ -628,7 +628,7 @@ class EMSESPSettingsForm extends Component { value="trace_raw" /> } - label="Output EMS telegrams in raw format" + label="Output EMS telegrams as hexadecimal bytes" /> )} From 4863ecc329155c607798a58d0c228e1a91654d8e Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 1 Aug 2021 11:55:12 +0200 Subject: [PATCH 113/122] add KB195i --- src/device_library.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device_library.h b/src/device_library.h index b3cf33d41..3a3c92233 100644 --- a/src/device_library.h +++ b/src/device_library.h @@ -32,7 +32,7 @@ {122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {131, DeviceType::BOILER, F("GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, -{133, DeviceType::BOILER, F("GB125/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, +{133, DeviceType::BOILER, F("Logano GB125/KB195i/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, {172, DeviceType::BOILER, F("Enviline/Compress 6000AW/Hybrid 7000iAW/SupraEco"), DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP}, From 53e25ae2130fd4fb11cfbcccc3aad024fa3da9c9 Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 1 Aug 2021 23:44:40 +0200 Subject: [PATCH 114/122] lint warning changes --- mock-api/server.js | 6 +++--- src/helpers.cpp | 10 ++++++---- src/mqtt.cpp | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/mock-api/server.js b/mock-api/server.js index 215d50f08..5ade146ce 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -899,17 +899,17 @@ app.get(EMSESP_STATUS_ENDPOINT, (req, res) => { }) app.post(EMSESP_DEVICEDATA_ENDPOINT, (req, res) => { const id = req.body.id - if (id == 1) { + if (id === 1) { const encoded = msgpack.encode(emsesp_devicedata_1) res.write(encoded, 'binary') res.end(null, 'binary') } - if (id == 2) { + if (id === 2) { const encoded = msgpack.encode(emsesp_devicedata_2) res.write(encoded, 'binary') res.end(null, 'binary') } - if (id == 3) { + if (id === 3) { const encoded = msgpack.encode(emsesp_devicedata_3) res.write(encoded, 'binary') res.end(null, 'binary') diff --git a/src/helpers.cpp b/src/helpers.cpp index 6504f04df..949326686 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -36,14 +36,14 @@ char * Helpers::hextoa(char * result, const uint8_t value) { #ifdef EMSESP_STANDALONE // special function to work outside of ESP's libraries char * Helpers::ultostr(char * ptr, uint32_t value, const uint8_t base) { - unsigned long t = 0, res = 0; - unsigned long tmp = value; - int count = 0; - if (NULL == ptr) { return NULL; } + unsigned long t = 0; + unsigned long tmp = value; + int count = 0; + if (tmp == 0) { count++; } @@ -57,6 +57,8 @@ char * Helpers::ultostr(char * ptr, uint32_t value, const uint8_t base) { *ptr = '\0'; + unsigned long res = 0; + do { res = value - base * (t = value / base); if (res < 10) { diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 0d1ce33ac..0e3476a79 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -107,8 +107,8 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper // register the individual commands too (e.g. ems-esp/boiler/wwonetime) // https://github.com/emsesp/EMS-ESP32/issues/31 - std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); if (subscribe_format_ == Subscribe_Format::INDIVIDUAL_ALL_HC && ((flags & CommandFlag::MQTT_SUB_FLAG_HC) == CommandFlag::MQTT_SUB_FLAG_HC)) { + std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); topic = cmd_topic + "/hc1/" + uuid::read_flash_string(cmd); queue_subscribe_message(topic); topic = cmd_topic + "/hc2/" + uuid::read_flash_string(cmd); @@ -118,6 +118,7 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper topic = cmd_topic + "/hc4/" + uuid::read_flash_string(cmd); queue_subscribe_message(topic); } else if (subscribe_format_ != Subscribe_Format::GENERAL && ((flags & CommandFlag::MQTT_SUB_FLAG_NOSUB) == CommandFlag::MQTT_SUB_FLAG_NOSUB)) { + std::string topic(MQTT_TOPIC_MAX_SIZE, '\0'); topic = cmd_topic + "/" + uuid::read_flash_string(cmd); queue_subscribe_message(topic); } From 9515e3d00bf3bb8195c5f49dc6b3cc02cafcb626 Mon Sep 17 00:00:00 2001 From: Proddy Date: Tue, 3 Aug 2021 13:21:43 +0200 Subject: [PATCH 115/122] text changes --- interface/src/project/EMSESPDataForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/project/EMSESPDataForm.tsx b/interface/src/project/EMSESPDataForm.tsx index afd1c61d8..d3905e3dc 100644 --- a/interface/src/project/EMSESPDataForm.tsx +++ b/interface/src/project/EMSESPDataForm.tsx @@ -421,7 +421,7 @@ class EMSESPDataForm extends Component< {this.noSensors() && ( - no external temperature sensors were detected + no connected Dallas temperature sensors were detected )} From e503c6cd797961df563d14ccddd0d5ed910fd185 Mon Sep 17 00:00:00 2001 From: Proddy Date: Tue, 3 Aug 2021 13:22:02 +0200 Subject: [PATCH 116/122] remove msg moves --- src/web/WebLogService.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/web/WebLogService.cpp b/src/web/WebLogService.cpp index 5f4f6d652..d489b27e4 100644 --- a/src/web/WebLogService.cpp +++ b/src/web/WebLogService.cpp @@ -156,10 +156,9 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) { JsonArray log = root.createNestedArray("events"); - for (const auto & msg : log_messages_) { - if (msg.content_->level <= log_level()) { + for (const auto & message : log_messages_) { + if (message.content_->level <= log_level()) { JsonObject logEvent = log.createNestedObject(); - auto message = std::move(msg); char time_string[25]; logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); From 171521886495f30073c6c7b54980cf78d7e26966 Mon Sep 17 00:00:00 2001 From: Proddy Date: Tue, 3 Aug 2021 13:22:13 +0200 Subject: [PATCH 117/122] update to b3 --- CHANGELOG_LATEST.md | 1 + src/version.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 7e75d5f10..f71987471 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -19,6 +19,7 @@ - HA thermostat mode if bool-format: numbers is selected - Web UI System Log sometimes skipped a few log messages when watching real-time - fix wwactivated [#89](https://github.com/emsesp/EMS-ESP32/issues/89) +- don't show commands (like reset) as Device values in the Web or Console ## Changed diff --git a/src/version.h b/src/version.h index ab4fe1303..50c966841 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.2.0b2" +#define EMSESP_APP_VERSION "3.2.0b3" From 19e26d0d6432c2eac20f5b5c86fb6edab7ef197b Mon Sep 17 00:00:00 2001 From: Proddy Date: Tue, 3 Aug 2021 13:22:45 +0200 Subject: [PATCH 118/122] don't show commands in web or console --- src/devices/boiler.cpp | 14 +++++++------- src/emsdevice.cpp | 22 +++++++++++----------- src/locale_EN.h | 1 + 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 7f0b10978..416f21a73 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -84,22 +84,22 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const register_telegram_type(0x48D, F("HpPower"), false, MAKE_PF_CB(process_HpPower)); register_telegram_type(0x48F, F("HpOutdoor"), false, MAKE_PF_CB(process_HpOutdoor)); } + // MQTT commands for boiler topic + + // first commands register_device_value(TAG_BOILER_DATA, &wWTapActivated_, - DeviceValueType::BOOL, - nullptr, + DeviceValueType::CMD, + FL_(enum_bool), FL_(wwtapactivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_tapwarmwater_activated)); + // reset is a command, so uses a dummy variable which is unused register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::LIST, MAKE_CF_CB(set_reset)); - // add values - // reserve_device_values(90); - - // main - boiler_data topic register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE); - id_ = product_id; + id_ = product_id; // note, must set the value after it has been initialized to have affect register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::BOOLEAN); register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::BOOLEAN); diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index e36e04594..069bb035a 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -422,12 +422,12 @@ void EMSdevice::show_mqtt_handlers(uuid::console::Shell & shell) { Mqtt::show_topic_handlers(shell, device_type_); } -void EMSdevice::register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f) { +void EMSdevice::register_mqtt_topic(const std::string & topic, const mqtt_sub_function_p f) { Mqtt::subscribe(device_type_, topic, f); } // register a callback function for a specific telegram type -void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p f) { +void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, const process_function_p f) { telegram_functions_.emplace_back(telegram_type_id, telegram_type_name, fetch, f); } @@ -487,7 +487,7 @@ void EMSdevice::register_device_value(uint8_t tag, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, - cmdfunction_p f, + const cmd_function_p f, int32_t min, uint32_t max) { register_device_value(tag, value_p, type, options, name[0], name[1], uom, (f != nullptr), min, max); @@ -513,7 +513,7 @@ void EMSdevice::register_device_value(uint8_t tag, const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, - cmdfunction_p f) { + const cmd_function_p f) { register_device_value(tag, value_p, type, options, name, uom, f, 0, 0); } @@ -562,8 +562,8 @@ void EMSdevice::generate_values_json_web(JsonObject & json) { JsonArray data = json.createNestedArray("data"); for (const auto & dv : devicevalues_) { - // ignore if full_name empty - if (dv.full_name != nullptr) { + // ignore if full_name empty and also commands + if ((dv.full_name != nullptr) && (dv.type != DeviceValueType::CMD)) { JsonObject obj; // create the object, if needed // handle Booleans (true, false) @@ -586,11 +586,11 @@ void EMSdevice::generate_values_json_web(JsonObject & json) { } } - // handle commands without value - else if (dv.type == DeviceValueType::CMD) { - obj = data.createNestedObject(); - obj["v"] = ""; - } + // // handle commands without value + // else if (dv.type == DeviceValueType::CMD) { + // obj = data.createNestedObject(); + // obj["v"] = ""; + // } else { // handle Integers and Floats diff --git a/src/locale_EN.h b/src/locale_EN.h index ebfe70655..0e7f9d454 100644 --- a/src/locale_EN.h +++ b/src/locale_EN.h @@ -245,6 +245,7 @@ MAKE_PSTR_LIST(enum_charge, F_(chargepump), F_(3wayvalve)) MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent)) MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer)) MAKE_PSTR_LIST(enum_reset, F_(maintenance), F_(error)) +MAKE_PSTR_LIST(enum_bool, F_(off), F_(on)) // thermostat MAKE_PSTR_WORD(light) From 98a7932dee7ea241bc57122e94e0a28089e30b2e Mon Sep 17 00:00:00 2001 From: Proddy Date: Tue, 3 Aug 2021 13:23:09 +0200 Subject: [PATCH 119/122] optimizations --- src/system.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/system.cpp b/src/system.cpp index bb1ab41ff..bd03d2790 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -662,7 +662,7 @@ void System::show_users(uuid::console::Shell & shell) { #ifndef EMSESP_STANDALONE EMSESP::esp8266React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) { - for (User user : securitySettings.users) { + for (const User & user : securitySettings.users) { shell.printfln(F(" username: %s, password: %s, is_admin: %s"), user.username.c_str(), user.password.c_str(), user.admin ? F("yes") : F("no")); } }); From bfd20e559e8f2821a8890d1df5b1c015442cf48e Mon Sep 17 00:00:00 2001 From: Proddy Date: Tue, 3 Aug 2021 13:23:33 +0200 Subject: [PATCH 120/122] minor changes, make cppcheck happy --- src/command.cpp | 9 ++++++--- src/command.h | 16 ++++++++-------- src/emsdevice.h | 12 ++++++------ src/emsesp.h | 2 +- src/mqtt.cpp | 4 ++-- src/mqtt.h | 16 ++++++++-------- 6 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index cfd886304..c3ea653c0 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -167,14 +167,13 @@ Command::CmdFunction * Command::find_command(const uint8_t device_type, char * c // add a command to the list, which does not return json // these commands are not callable directly via MQTT subscriptions either -void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_p cb, const __FlashStringHelper * description, uint8_t flags) { +void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, const cmd_function_p cb, const __FlashStringHelper * description, uint8_t flags) { // if the command already exists for that device type don't add it if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) { return; } // if the description is empty, it's hidden which means it will not show up in Web API or Console as an available command - // TODO check whether we still need this piece of code if (description == nullptr) { flags |= CommandFlag::HIDDEN; } @@ -189,7 +188,11 @@ void Command::add(const uint8_t device_type, const __FlashStringHelper * cmd, cm // add a command to the list, which does return a json object as output // flag is fixed to MqttSubFlag::FLAG_NOSUB -void Command::add_json(const uint8_t device_type, const __FlashStringHelper * cmd, cmdfunction_json_p cb, const __FlashStringHelper * description, uint8_t flags) { +void Command::add_json(const uint8_t device_type, + const __FlashStringHelper * cmd, + const cmd_json_function_p cb, + const __FlashStringHelper * description, + uint8_t flags) { // if the command already exists for that device type don't add it if (find_command(device_type, uuid::read_flash_string(cmd).c_str()) != nullptr) { return; diff --git a/src/command.h b/src/command.h index bbc27a9fb..69d988ccb 100644 --- a/src/command.h +++ b/src/command.h @@ -55,8 +55,8 @@ enum CommandRet : uint8_t { }; -using cmdfunction_p = std::function; -using cmdfunction_json_p = std::function; +using cmd_function_p = std::function; +using cmd_json_function_p = std::function; class Command { public: @@ -64,15 +64,15 @@ class Command { uint8_t device_type_; // DeviceType:: uint8_t flags_; // mqtt flags for command subscriptions const __FlashStringHelper * cmd_; - cmdfunction_p cmdfunction_; - cmdfunction_json_p cmdfunction_json_; + const cmd_function_p cmdfunction_; + const cmd_json_function_p cmdfunction_json_; const __FlashStringHelper * description_; CmdFunction(const uint8_t device_type, const uint8_t flags, const __FlashStringHelper * cmd, - cmdfunction_p cmdfunction, - cmdfunction_json_p cmdfunction_json, + const cmd_function_p cmdfunction, + const cmd_json_function_p cmdfunction_json, const __FlashStringHelper * description) : device_type_(device_type) , flags_(flags) @@ -105,13 +105,13 @@ class Command { static void add(const uint8_t device_type, const __FlashStringHelper * cmd, - cmdfunction_p cb, + const cmd_function_p cb, const __FlashStringHelper * description, uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); static void add_json(const uint8_t device_type, const __FlashStringHelper * cmd, - cmdfunction_json_p cb, + const cmd_json_function_p cb, const __FlashStringHelper * description, uint8_t flags = CommandFlag::MQTT_SUB_FLAG_NORMAL); diff --git a/src/emsdevice.h b/src/emsdevice.h index 4a771f0c6..38cc4f25c 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -40,7 +40,7 @@ enum DeviceValueType : uint8_t { TIME, // same as ULONG (32 bits) ENUM, TEXT, - CMD + CMD // special for commands only }; @@ -239,7 +239,7 @@ class EMSdevice { using process_function_p = std::function)>; - void register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p cb); + void register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, const process_function_p cb); bool handle_telegram(std::shared_ptr telegram); std::string get_value_uom(const char * key); @@ -263,7 +263,7 @@ class EMSdevice { const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, - cmdfunction_p f, + const cmd_function_p f, int32_t min, uint32_t max); void register_device_value(uint8_t tag, @@ -272,7 +272,7 @@ class EMSdevice { const __FlashStringHelper * const * options, const __FlashStringHelper * const * name, uint8_t uom, - cmdfunction_p f); + const cmd_function_p f); void register_device_value(uint8_t tag, void * value_p, uint8_t type, @@ -286,7 +286,7 @@ class EMSdevice { void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0); - void register_mqtt_topic(const std::string & topic, mqtt_subfunction_p f); + void register_mqtt_topic(const std::string & topic, const mqtt_sub_function_p f); void publish_mqtt_ha_sensor(); @@ -402,7 +402,7 @@ class EMSdevice { bool fetch_; // if this type_id be queried automatically process_function_p process_function_; - TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p process_function) + TelegramFunction(uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, const process_function_p process_function) : telegram_type_id_(telegram_type_id) , telegram_type_name_(telegram_type_name) , fetch_(fetch) diff --git a/src/emsesp.h b/src/emsesp.h index b8c336e0d..47ef4858f 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -66,7 +66,7 @@ // helpers for callback functions #define MAKE_PF_CB(__f) [&](std::shared_ptr t) { __f(t); } // for Process Function callbacks to EMSDevice::process_function_p -#define MAKE_CF_CB(__f) [&](const char * value, const int8_t id) { return __f(value, id); } // for Command Function callbacks Command::cmdfunction_p +#define MAKE_CF_CB(__f) [&](const char * value, const int8_t id) { return __f(value, id); } // for Command Function callbacks Command::cmd_function_p namespace emsesp { diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 0e3476a79..61aeb1969 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -54,7 +54,7 @@ uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON}; // subscribe to an MQTT topic, and store the associated callback function // only if it already hasn't been added -void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_subfunction_p cb) { +void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_sub_function_p cb) { // check if we already have the topic subscribed, if so don't add it again if (!mqtt_subfunctions_.empty()) { for (auto & mqtt_subfunction : mqtt_subfunctions_) { @@ -126,7 +126,7 @@ void Mqtt::register_command(const uint8_t device_type, const __FlashStringHelper // subscribe to an MQTT topic, and store the associated callback function // For generic functions not tied to a specific device -void Mqtt::subscribe(const std::string & topic, mqtt_subfunction_p cb) { +void Mqtt::subscribe(const std::string & topic, mqtt_sub_function_p cb) { subscribe(0, topic, cb); // no device_id needed if generic to EMS-ESP } diff --git a/src/mqtt.h b/src/mqtt.h index 51cc6ca8c..609e6d545 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -46,8 +46,8 @@ using uuid::console::Shell; namespace emsesp { -using mqtt_subfunction_p = std::function; -using cmdfunction_p = std::function; +using mqtt_sub_function_p = std::function; +using cmdfunction_p = std::function; struct MqttMessage { const uint8_t operation; @@ -100,8 +100,8 @@ class Mqtt { static void on_connect(); - static void subscribe(const uint8_t device_type, const std::string & topic, mqtt_subfunction_p cb); - static void subscribe(const std::string & topic, mqtt_subfunction_p cb); + static void subscribe(const uint8_t device_type, const std::string & topic, mqtt_sub_function_p cb); + static void subscribe(const std::string & topic, mqtt_sub_function_p cb); static void resubscribe(); static void publish(const std::string & topic, const std::string & payload); @@ -239,11 +239,11 @@ class Mqtt { // function handlers for MQTT subscriptions struct MQTTSubFunction { - uint8_t device_type_; // which device type, from DeviceType:: - const std::string topic_; // short topic name - mqtt_subfunction_p mqtt_subfunction_; // can be empty + uint8_t device_type_; // which device type, from DeviceType:: + const std::string topic_; // short topic name + mqtt_sub_function_p mqtt_subfunction_; // can be empty - MQTTSubFunction(uint8_t device_type, const std::string && topic, mqtt_subfunction_p mqtt_subfunction) + MQTTSubFunction(uint8_t device_type, const std::string && topic, mqtt_sub_function_p mqtt_subfunction) : device_type_(device_type) , topic_(topic) , mqtt_subfunction_(mqtt_subfunction) { From 1450737d948b3d29ce4857571ded0611484f9561 Mon Sep 17 00:00:00 2001 From: Proddy Date: Tue, 3 Aug 2021 13:28:04 +0200 Subject: [PATCH 121/122] make boiler id first in device registry, so it's first in MQTT payload --- src/devices/boiler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 416f21a73..d2710b230 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -87,6 +87,9 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const // MQTT commands for boiler topic + register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE); + id_ = product_id; // note, must set the value after it has been initialized to have affect + // first commands register_device_value(TAG_BOILER_DATA, &wWTapActivated_, @@ -95,12 +98,9 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const FL_(wwtapactivated), DeviceValueUOM::BOOLEAN, MAKE_CF_CB(set_tapwarmwater_activated)); - // reset is a command, so uses a dummy variable which is unused + // reset is a command, so uses a dummy variable which is unused. It will not be shown in MQTT, Web or Console register_device_value(TAG_BOILER_DATA, &dummy8u_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::LIST, MAKE_CF_CB(set_reset)); - register_device_value(TAG_BOILER_DATA, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE); - id_ = product_id; // note, must set the value after it has been initialized to have affect - register_device_value(TAG_BOILER_DATA, &heatingActive_, DeviceValueType::BOOL, nullptr, FL_(heatingActive), DeviceValueUOM::BOOLEAN); register_device_value(TAG_BOILER_DATA, &tapwaterActive_, DeviceValueType::BOOL, nullptr, FL_(tapwaterActive), DeviceValueUOM::BOOLEAN); register_device_value(TAG_BOILER_DATA, &selFlowTemp_, DeviceValueType::UINT, nullptr, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp)); From 5e4f5916f2762e823f834c3defd090364993f06f Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 6 Aug 2021 12:01:42 +0200 Subject: [PATCH 122/122] prep for 3.2.0 release --- CHANGELOG.md | 36 ++++++++++++++++++++++++++++++++++++ CHANGELOG_LATEST.md | 26 -------------------------- src/version.h | 2 +- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4e330167..84360649b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,42 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [3.2.0] August 6 2021 + +## Added + +- support for IPv6 (web/api/mqtt, not syslog yet) [#83](https://github.com/emsesp/EMS-ESP32/issues/83) +- System Log in Web UI will show current time if the NTP Service is enabled [#82](https://github.com/emsesp/EMS-ESP32/issues/82) +- Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode [#83](https://github.com/emsesp/EMS-ESP32/issues/83) +- optional low CPU clockrate (160 MHz) [#83](https://github.com/emsesp/EMS-ESP32/issues/83) +- select format for enumerated values in web +- settings for water hysteresis on/off +- dallas sensor name editable. `sensorname` console-command, replace sensorid with a unique name [#84](https://github.com/emsesp/EMS-ESP32/issues/84) +- 'restart' system command. Can be invoked via API with authentication. [#87](https://github.com/emsesp/EMS-ESP32/issues/87) +- add Download button in Web UI for log + +## Fixed + +- set mode allow numbers +- Junkers thermostat shows mode as selected by set_mode +- HA thermostat mode if bool-format: numbers is selected +- Web UI System Log sometimes skipped a few log messages when watching real-time +- fix wwactivated [#89](https://github.com/emsesp/EMS-ESP32/issues/89) +- don't show commands (like reset) as Device values in the Web or Console + +## Changed + +- removed Rx echo failures counting as incomplete telegrams. Bad telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80) +- add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat +- added debug target to PlatformIO build to help hunt down system crashes +- enumerated values always start at zero +- maintenance settings for time/date as extra setting +- move api/mqtt formats to `settings`, add `enum format` +- UI improvements for editing Dallas Sensor details +- RESTful GET commands can also require authentication (via bearer access token) for better security +- Updated AsyncMqttClient to 0.9.0 and ArduinoJson to 6.18.3 +- Download buttons for settings and info under the Help tab + # [3.1.1] June 26 2021 ## Changed diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index f71987471..1858462a6 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -2,34 +2,8 @@ ## Added -- support for IPv6 (web/api/mqtt, not syslog yet) [#83](https://github.com/emsesp/EMS-ESP32/issues/83) -- System Log in Web UI will show current time if the NTP Service is enabled [#82](https://github.com/emsesp/EMS-ESP32/issues/82) -- Network settings for Tx-power, WiFi-bandwidth, WiFi-sleepmode [#83](https://github.com/emsesp/EMS-ESP32/issues/83) -- optional low CPU clockrate (160 MHz) [#83](https://github.com/emsesp/EMS-ESP32/issues/83) -- select format for enumerated values in web -- settings for water hysteresis on/off -- dallas sensor name editable. `sensorname` console-command, replace sensorid with a unique name [#84](https://github.com/emsesp/EMS-ESP32/issues/84) -- 'restart' system command. Can be invoked via API with authentication. [#87](https://github.com/emsesp/EMS-ESP32/issues/87) -- add Download button in Web UI for log - ## Fixed -- set mode allow numbers -- Junkers thermostat shows mode as selected by set_mode -- HA thermostat mode if bool-format: numbers is selected -- Web UI System Log sometimes skipped a few log messages when watching real-time -- fix wwactivated [#89](https://github.com/emsesp/EMS-ESP32/issues/89) -- don't show commands (like reset) as Device values in the Web or Console ## Changed -- removed Rx echo failures counting as incomplete telegrams. Bad telegrams show as Warning and not Errors. [#80](https://github.com/emsesp/EMS-ESP32/issues/80) -- add upload_sec to `api/system/info` and removed # from some names to keep consistent with MQTT heartbeat -- added debug target to PlatformIO build to help hunt down system crashes -- enumerated values always start at zero -- maintenance settings for time/date as extra setting -- move api/mqtt formats to `settings`, add `enum format` -- UI improvements for editing Dallas Sensor details -- RESTful GET commands can also require authentication (via bearer access token) for better security -- Updated AsyncMqttClient to 0.9.0 and ArduinoJson to 6.18.3 -- Download buttons for settings and info under the Help tab diff --git a/src/version.h b/src/version.h index 50c966841..db7ef603e 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.2.0b3" +#define EMSESP_APP_VERSION "3.2.1b0"