From 5e9e995e4bb41e7c833ab951e0704e53936e077e Mon Sep 17 00:00:00 2001 From: Proddy Date: Sun, 1 May 2022 22:59:35 +0200 Subject: [PATCH] added shower trigger and coldshot times - #436 --- CHANGELOG_LATEST.md | 5 +- interface/src/project/SettingsApplication.tsx | 40 ++++++++-- interface/src/project/types.ts | 6 +- interface/src/project/validators.ts | 4 + lib_standalone/ESP8266React.h | 68 ++++++++-------- mock-api/server.js | 4 +- src/default_settings.h | 8 ++ src/shower.cpp | 10 ++- src/shower.h | 10 +-- src/system.cpp | 4 + src/version.h | 2 +- src/web/WebDataService.cpp | 3 +- src/web/WebSettingsService.cpp | 78 ++++++++++--------- src/web/WebSettingsService.h | 2 + 14 files changed, 155 insertions(+), 89 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index bd71d324d..2a610339d 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -35,6 +35,8 @@ - Read time from IVT-controller [#439](https://github.com/emsesp/EMS-ESP32/issues/439) - Hybrid Heatpump product-id 168 [#459](https://github.com/emsesp/EMS-ESP32/issues/459), thermostat settings - Junkers ISM2 and IPM in warm water mode [#437](https://github.com/emsesp/EMS-ESP32/issues/437) +- Added Shower Alert trigger time and cold shot time [#436](https://github.com/emsesp/EMS-ESP32/issues/436) +- Improved Table layout in Web UI (searching, filtering, sorting, exporting to CSV) ### Fixed @@ -53,6 +55,7 @@ - Burner selected max power can have a value higher than 100% [#314](https://github.com/emsesp/EMS-ESP32/issues/314) - some missing fahrenheit calculations - limited number of exclusions [#339](https://github.com/emsesp/EMS-ESP32/issues/339) +- MQTT sometimes would not reconnect after a WiFi outage ### Changed @@ -63,7 +66,7 @@ - Show Sensors quality in WebUI - Controller not shown in WebUI dashboard - renamed "Home Assistant Integration" to "MQTT Discovery" in MQTT Settings [#290](https://github.com/emsesp/EMS-ESP32/issues/290) -- Show ems tx reads and writes separatly +- Show ems tx reads and writes separately - Show ems device handlers separated for received, fetched and pending handlers. - Wired renamed to Ethernet - removed system/pin command, new commands in analogsensors diff --git a/interface/src/project/SettingsApplication.tsx b/interface/src/project/SettingsApplication.tsx index 0ea86722b..353f1faf0 100644 --- a/interface/src/project/SettingsApplication.tsx +++ b/interface/src/project/SettingsApplication.tsx @@ -343,11 +343,6 @@ const SettingsApplication: FC = () => { label="Convert temperature values to Fahrenheit" disabled={saving} /> - } - label="Underclock CPU speed" - disabled={saving} - /> } label="Bypass Access Token authorization on API calls" @@ -358,6 +353,11 @@ const SettingsApplication: FC = () => { label="Enable Read only mode (blocks all outgoing EMS Tx write commands)" disabled={saving} /> + } + label="Underclock CPU speed" + disabled={saving} + /> } @@ -367,8 +367,36 @@ const SettingsApplication: FC = () => { } label="Enable Shower Alert" - disabled={saving} + disabled={!data.shower_timer} /> + {data.shower_alert && ( + <> + + + + + + + + )} Formatting Options diff --git a/interface/src/project/types.ts b/interface/src/project/types.ts index c78745e55..0d4d048b6 100644 --- a/interface/src/project/types.ts +++ b/interface/src/project/types.ts @@ -9,6 +9,8 @@ export interface Settings { master_thermostat: number; shower_timer: boolean; shower_alert: boolean; + shower_alert_coldshot: number; + shower_alert_trigger: number; rx_gpio: number; tx_gpio: number; telnet_enabled: boolean; @@ -41,8 +43,8 @@ export enum busConnectionStatus { export interface Stat { id: string; // name s: number; // success - f: number; // fail - q: number; // quality + f: number; // fail + q: number; // quality } export interface Status { status: busConnectionStatus; diff --git a/interface/src/project/validators.ts b/interface/src/project/validators.ts index 88742d3e4..a626489d2 100644 --- a/interface/src/project/validators.ts +++ b/interface/src/project/validators.ts @@ -40,5 +40,9 @@ export const createSettingsValidator = (settings: Settings) => { required: true, message: 'Mark interval is required' }, { type: 'number', min: 0, max: 10, message: 'Port must be between 0 and 10' } ] + }), + ...(settings.shower_alert && { + shower_alert_trigger: [{ type: 'number', min: 1, max: 20, message: 'Time must be between 1 and 20 minutes' }], + shower_alert_coldshot: [{ type: 'number', min: 1, max: 10, message: 'Time must be between 1 and 10 seconds' }] }) }); diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index 600a1c74a..4b31d671c 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -19,39 +19,41 @@ class DummySettings { public: - uint8_t tx_mode = 1; - uint8_t ems_bus_id = 0x0B; - bool syslog_enabled = false; - int8_t syslog_level = 3; // uuid::log::Level - uint32_t syslog_mark_interval = 0; - String syslog_host = "192.168.1.4"; - uint16_t syslog_port = 514; - uint8_t master_thermostat = 0; - bool shower_timer = true; - bool shower_alert = false; - bool hide_led = false; - bool notoken_api = false; - bool readonly_mode = false; - uint8_t bool_format = 1; // using "on" and "off" - uint8_t enum_format = 1; - bool nosleep = false; - bool fahrenheit = false; - bool bandwidth20 = false; - bool telnet_enabled = false; - String board_profile = "CUSTOM"; - bool trace_raw = false; - bool analog_enabled = true; // analog is enabled - int8_t weblog_level = 1; - uint8_t weblog_buffer = 50; - bool weblog_compact = true; - uint8_t rx_gpio = 0; - uint8_t tx_gpio = 0; - uint8_t dallas_gpio = 16; // to ensure its enabled - bool dallas_parasite = false; - uint8_t led_gpio = 0; - bool low_clock = false; - uint8_t pbutton_gpio = false; - uint8_t solar_maxflow = 30; + uint8_t tx_mode = 1; + uint8_t ems_bus_id = 0x0B; + bool syslog_enabled = false; + int8_t syslog_level = 3; // uuid::log::Level + uint32_t syslog_mark_interval = 0; + String syslog_host = "192.168.1.4"; + uint16_t syslog_port = 514; + uint8_t master_thermostat = 0; + bool shower_timer = true; + bool shower_alert = false; + uint8_t shower_alert_coldshot = 10; + uint8_t shower_alert_trigger = 7; + bool hide_led = false; + bool notoken_api = false; + bool readonly_mode = false; + uint8_t bool_format = 1; // using "on" and "off" + uint8_t enum_format = 1; + bool nosleep = false; + bool fahrenheit = false; + bool bandwidth20 = false; + bool telnet_enabled = false; + String board_profile = "CUSTOM"; + bool trace_raw = false; + bool analog_enabled = true; // analog is enabled + int8_t weblog_level = 1; + uint8_t weblog_buffer = 50; + bool weblog_compact = true; + uint8_t rx_gpio = 0; + uint8_t tx_gpio = 0; + uint8_t dallas_gpio = 16; // to ensure its enabled + bool dallas_parasite = false; + uint8_t led_gpio = 0; + bool low_clock = false; + uint8_t pbutton_gpio = false; + uint8_t solar_maxflow = 30; // MQTT uint16_t publish_time = 10; diff --git a/mock-api/server.js b/mock-api/server.js index 898d644e0..9f3666962 100644 --- a/mock-api/server.js +++ b/mock-api/server.js @@ -316,7 +316,9 @@ settings = { syslog_port: 514, master_thermostat: 0, shower_timer: true, - shower_alert: false, + shower_alert: true, + shower_alert_trigger: 7, + shower_alert_coldshot: 10, rx_gpio: 23, tx_gpio: 5, phy_type: 0, diff --git a/src/default_settings.h b/src/default_settings.h index b70fef920..134ff4dc8 100644 --- a/src/default_settings.h +++ b/src/default_settings.h @@ -64,6 +64,14 @@ #define EMSESP_DEFAULT_SHOWER_ALERT false #endif +#ifndef EMSESP_DEFAULT_SHOWER_ALERT_TRIGGER +#define EMSESP_DEFAULT_SHOWER_ALERT_TRIGGER 7 +#endif + +#ifndef EMSESP_DEFAULT_SHOWER_ALERT_COLDSHOT +#define EMSESP_DEFAULT_SHOWER_ALERT_COLDSHOT 10 +#endif + #ifndef EMSESP_DEFAULT_HIDE_LED #define EMSESP_DEFAULT_HIDE_LED false #endif diff --git a/src/shower.cpp b/src/shower.cpp index f9c8a2e8e..8f024dfa4 100644 --- a/src/shower.cpp +++ b/src/shower.cpp @@ -24,8 +24,10 @@ uuid::log::Logger Shower::logger_{F_(shower), uuid::log::Facility::CONSOLE}; void Shower::start() { EMSESP::webSettingsService.read([&](WebSettings & settings) { - shower_timer_ = settings.shower_timer; - shower_alert_ = settings.shower_alert; + shower_timer_ = settings.shower_timer; + shower_alert_ = settings.shower_alert; + shower_alert_trigger_ = settings.shower_alert_trigger * 60000; // convert from minutes + shower_alert_coldshot_ = settings.shower_alert_coldshot * 1000; // convert from seconds }); set_shower_state(false, true); // turns shower to off and creates HA topic if not already done @@ -59,7 +61,7 @@ void Shower::loop() { LOG_DEBUG(F("[Shower] hot water still running, starting shower timer")); } // check if the shower has been on too long - else if ((time_now - timer_start_) > SHOWER_MAX_DURATION) { + else if ((time_now - timer_start_) > shower_alert_trigger_) { shower_alert_start(); } } @@ -95,7 +97,7 @@ void Shower::loop() { // at this point we're in the shower cold shot (doing_cold_shot_ == true) // keep repeating until the time is up - if ((time_now - alert_timer_start_) > SHOWER_COLDSHOT_DURATION) { + if ((time_now - alert_timer_start_) > shower_alert_coldshot_) { shower_alert_stop(); } } diff --git a/src/shower.h b/src/shower.h index dc9dafda4..809a18031 100644 --- a/src/shower.h +++ b/src/shower.h @@ -49,11 +49,9 @@ class Shower { private: static uuid::log::Logger logger_; - static constexpr uint32_t SHOWER_PAUSE_TIME = 15000; // in ms. 15 seconds, max time if water is switched off & on during a shower - static constexpr uint32_t SHOWER_MIN_DURATION = 120000; // in ms. 2 minutes, before recognizing its a shower - static constexpr uint32_t SHOWER_OFFSET_TIME = 5000; // in ms. 5 seconds grace time, to calibrate actual time under the shower - static constexpr uint32_t SHOWER_COLDSHOT_DURATION = 10000; // 10 seconds for cold water before turning back hot water - static constexpr uint32_t SHOWER_MAX_DURATION = 420000; // in ms. 7 minutes, before trigger a shot of cold water + static constexpr uint32_t SHOWER_PAUSE_TIME = 15000; // in ms. 15 seconds, max time if water is switched off & on during a shower + static constexpr uint32_t SHOWER_MIN_DURATION = 120000; // in ms. 2 minutes, before recognizing its a shower + static constexpr uint32_t SHOWER_OFFSET_TIME = 5000; // in ms. 5 seconds grace time, to calibrate actual time under the shower void publish_shower_data() const; void shower_alert_start(); @@ -61,6 +59,8 @@ class Shower { bool shower_timer_; // true if we want to report back on shower times bool shower_alert_; // true if we want the alert of cold water + uint32_t shower_alert_trigger_; // default 7 minutes, before trigger a shot of cold water + uint32_t shower_alert_coldshot_; // default 10 seconds for cold water before turning back hot water bool ha_configdone_ = false; // for HA MQTT Discovery bool shower_state_; uint32_t timer_start_; // ms diff --git a/src/system.cpp b/src/system.cpp index 4eaa88ec0..8c3620941 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -998,6 +998,10 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject & node["shower_timer"] = settings.shower_timer; node["shower_alert"] = settings.shower_alert; + if (settings.shower_alert) { + node["shower_alert_coldshot"] = settings.shower_alert_coldshot / 1000; // seconds + node["shower_alert_trigger"] = settings.shower_alert_trigger / 60000; // minutes + } node["rx_gpio"] = settings.rx_gpio; node["tx_gpio"] = settings.tx_gpio; diff --git a/src/version.h b/src/version.h index 4b88090e3..60abd800d 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.4.0b14" +#define EMSESP_APP_VERSION "3.4.0b15" diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp index 0d9347ce5..c32bed325 100644 --- a/src/web/WebDataService.cpp +++ b/src/web/WebDataService.cpp @@ -137,8 +137,9 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) { for (const auto & sensor : EMSESP::analogsensor_.sensors()) { // don't send if it's marked for removal if (sensor.type() != AnalogSensor::AnalogType::MARK_DELETED) { + count++; JsonObject obj = analogs.createNestedObject(); - obj["id"] = Helpers::smallitoa(buffer, ++count); // needed for sorting table + obj["id"] = Helpers::smallitoa(buffer, count); // needed for sorting table obj["g"] = sensor.gpio(); obj["n"] = sensor.name(); obj["u"] = sensor.uom(); diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index a5d9ef98d..dca3ab72f 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -37,41 +37,43 @@ WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, Securit } void WebSettings::read(WebSettings & settings, JsonObject & root) { - root["tx_mode"] = settings.tx_mode; - root["ems_bus_id"] = settings.ems_bus_id; - root["syslog_enabled"] = settings.syslog_enabled; - root["syslog_level"] = settings.syslog_level; - root["trace_raw"] = settings.trace_raw; - root["syslog_mark_interval"] = settings.syslog_mark_interval; - root["syslog_host"] = settings.syslog_host; - root["syslog_port"] = settings.syslog_port; - root["master_thermostat"] = settings.master_thermostat; - root["shower_timer"] = settings.shower_timer; - root["shower_alert"] = settings.shower_alert; - root["rx_gpio"] = settings.rx_gpio; - root["tx_gpio"] = settings.tx_gpio; - root["dallas_gpio"] = settings.dallas_gpio; - 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["telnet_enabled"] = settings.telnet_enabled; - root["notoken_api"] = settings.notoken_api; - root["readonly_mode"] = settings.readonly_mode; - root["analog_enabled"] = settings.analog_enabled; - root["pbutton_gpio"] = settings.pbutton_gpio; - root["solar_maxflow"] = settings.solar_maxflow; - root["board_profile"] = settings.board_profile; - root["fahrenheit"] = settings.fahrenheit; - root["bool_format"] = settings.bool_format; - root["enum_format"] = settings.enum_format; - root["weblog_level"] = settings.weblog_level; - root["weblog_buffer"] = settings.weblog_buffer; - root["weblog_compact"] = settings.weblog_compact; - root["phy_type"] = settings.phy_type; - root["eth_power"] = settings.eth_power; - root["eth_phy_addr"] = settings.eth_phy_addr; - root["eth_clock_mode"] = settings.eth_clock_mode; + root["tx_mode"] = settings.tx_mode; + root["ems_bus_id"] = settings.ems_bus_id; + root["syslog_enabled"] = settings.syslog_enabled; + root["syslog_level"] = settings.syslog_level; + root["trace_raw"] = settings.trace_raw; + root["syslog_mark_interval"] = settings.syslog_mark_interval; + root["syslog_host"] = settings.syslog_host; + root["syslog_port"] = settings.syslog_port; + root["master_thermostat"] = settings.master_thermostat; + root["shower_timer"] = settings.shower_timer; + root["shower_alert"] = settings.shower_alert; + root["shower_alert_coldshot"] = settings.shower_alert_coldshot; + root["shower_alert_trigger"] = settings.shower_alert_trigger; + root["rx_gpio"] = settings.rx_gpio; + root["tx_gpio"] = settings.tx_gpio; + root["dallas_gpio"] = settings.dallas_gpio; + 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["telnet_enabled"] = settings.telnet_enabled; + root["notoken_api"] = settings.notoken_api; + root["readonly_mode"] = settings.readonly_mode; + root["analog_enabled"] = settings.analog_enabled; + root["pbutton_gpio"] = settings.pbutton_gpio; + root["solar_maxflow"] = settings.solar_maxflow; + root["board_profile"] = settings.board_profile; + root["fahrenheit"] = settings.fahrenheit; + root["bool_format"] = settings.bool_format; + root["enum_format"] = settings.enum_format; + root["weblog_level"] = settings.weblog_level; + root["weblog_buffer"] = settings.weblog_buffer; + root["weblog_compact"] = settings.weblog_compact; + root["phy_type"] = settings.phy_type; + root["eth_power"] = settings.eth_power; + root["eth_phy_addr"] = settings.eth_phy_addr; + root["eth_clock_mode"] = settings.eth_clock_mode; } // call on initialization and also when settings are updated via web or console @@ -152,6 +154,12 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) prev = settings.shower_alert; settings.shower_alert = root["shower_alert"] | EMSESP_DEFAULT_SHOWER_ALERT; check_flag(prev, settings.shower_alert, ChangeFlags::SHOWER); + prev = settings.shower_alert_trigger; + settings.shower_alert_trigger = root["shower_alert_trigger"] | EMSESP_DEFAULT_SHOWER_ALERT_TRIGGER; + check_flag(prev, settings.shower_alert_trigger, ChangeFlags::SHOWER); + prev = settings.shower_alert_coldshot; + settings.shower_alert_coldshot = root["shower_alert_coldshot"] | EMSESP_DEFAULT_SHOWER_ALERT_COLDSHOT; + check_flag(prev, settings.shower_alert_coldshot, ChangeFlags::SHOWER); // led prev = settings.led_gpio; diff --git a/src/web/WebSettingsService.h b/src/web/WebSettingsService.h index 5ff6ef1c9..16bf3aa60 100644 --- a/src/web/WebSettingsService.h +++ b/src/web/WebSettingsService.h @@ -34,6 +34,8 @@ class WebSettings { uint8_t master_thermostat; bool shower_timer; bool shower_alert; + uint8_t shower_alert_trigger; + uint8_t shower_alert_coldshot; bool syslog_enabled; int8_t syslog_level; // uuid::log::Level uint32_t syslog_mark_interval;