From 0f48d3e72cae80d49cf30f8c2ede107fcb8f2404 Mon Sep 17 00:00:00 2001 From: MichaelDvP Date: Fri, 16 Jul 2021 10:33:12 +0200 Subject: [PATCH] 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);