Merge branch 'dev' of https://github.com/emsesp/EMS-ESP32 into dev_no_master_thermostat

This commit is contained in:
MichaelDvP
2022-03-08 10:53:13 +01:00
40 changed files with 642 additions and 625 deletions

56
.github/workflows/sonar_check.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Sonar Check
on:
push:
branches:
- dev
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
env:
# https://binaries.sonarsource.com/?prefix=Distribution/sonar-scanner-cli/
# SONAR_SCANNER_VERSION: 4.6.1.2450
SONAR_SCANNER_VERSION: 4.7.0.2747
SONAR_SERVER_URL: "https://sonarcloud.io"
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache SonarCloud packages
uses: actions/cache@v1
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Download and set up sonar-scanner
env:
SONAR_SCANNER_DOWNLOAD_URL: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip
run: |
mkdir -p $HOME/.sonar
curl -sSLo $HOME/.sonar/sonar-scanner.zip ${{ env.SONAR_SCANNER_DOWNLOAD_URL }}
unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
echo "$HOME/.sonar/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> $GITHUB_PATH
- name: Download and set up build-wrapper
env:
BUILD_WRAPPER_DOWNLOAD_URL: ${{ env.SONAR_SERVER_URL }}/static/cpp/build-wrapper-linux-x86.zip
run: |
curl -sSLo $HOME/.sonar/build-wrapper-linux-x86.zip ${{ env.BUILD_WRAPPER_DOWNLOAD_URL }}
unzip -o $HOME/.sonar/build-wrapper-linux-x86.zip -d $HOME/.sonar/
echo "$HOME/.sonar/build-wrapper-linux-x86" >> $GITHUB_PATH
- name: Run build-wrapper
run: |
make clean
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} make clean all
- name: Run sonar-scanner
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
sonar-scanner

5
.gitignore vendored
View File

@@ -28,3 +28,8 @@ node_modules
test.sh test.sh
scripts/__pycache__ scripts/__pycache__
.temp .temp
# sonar
.scannerwork/
sonar/
build_wrapper_output_directory/

View File

@@ -113,6 +113,7 @@ COMPILE.cpp = $(CXX) $(CXX_STANDARD) $(CXXFLAGS) $(DEPFLAGS) -c $< -o $@
#---------------------------------------------------------------------- #----------------------------------------------------------------------
# Targets # Targets
#---------------------------------------------------------------------- #----------------------------------------------------------------------
.PHONY: all
all: $(OUTPUT) all: $(OUTPUT)
$(OUTPUT): $(OBJS) $(OUTPUT): $(OBJS)
@@ -138,6 +139,7 @@ cppcheck: $(SOURCES)
run: $(OUTPUT) run: $(OUTPUT)
@$< @$<
.PHONY: clean
clean: clean:
@$(RM) -r $(BUILD) $(OUTPUT) @$(RM) -r $(BUILD) $(OUTPUT)

View File

@@ -16,7 +16,7 @@
"@types/lodash": "^4.14.179", "@types/lodash": "^4.14.179",
"@types/node": "^17.0.21", "@types/node": "^17.0.21",
"@types/react": "^17.0.39", "@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.13",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"async-validator": "^4.0.7", "async-validator": "^4.0.7",
"axios": "^0.26.0", "axios": "^0.26.0",
@@ -3793,9 +3793,9 @@
} }
}, },
"node_modules/@types/react-dom": { "node_modules/@types/react-dom": {
"version": "17.0.11", "version": "17.0.13",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz",
"integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==",
"dependencies": { "dependencies": {
"@types/react": "*" "@types/react": "*"
} }
@@ -20246,9 +20246,9 @@
} }
}, },
"@types/react-dom": { "@types/react-dom": {
"version": "17.0.11", "version": "17.0.13",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz",
"integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==", "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==",
"requires": { "requires": {
"@types/react": "*" "@types/react": "*"
} }

View File

@@ -12,7 +12,7 @@
"@types/lodash": "^4.14.179", "@types/lodash": "^4.14.179",
"@types/node": "^17.0.21", "@types/node": "^17.0.21",
"@types/react": "^17.0.39", "@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.13",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"async-validator": "^4.0.7", "async-validator": "^4.0.7",
"axios": "^0.26.0", "axios": "^0.26.0",

8
scripts/run_sonar.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
# make sure you add the soanr token here
# export SONAR_TOKEN=""
make clean
build-wrapper-linux-x86-64 --out-dir build_wrapper_output_directory make all
sonar-scanner

11
sonar-project.properties Normal file
View File

@@ -0,0 +1,11 @@
sonar.organization=emsesp
sonar.projectKey=emsesp_EMS-ESP32
sonar.projectName=EMS-ESP32
sonar.projectVersion=3.4
sonar.sources=./src
sonar.cfamily.build-wrapper-output=build_wrapper_output_directory
sonar.sourceEncoding=UTF-8
sonar.host.url=https://sonarcloud.io
sonar.cfamily.threads=8
sonar.cfamily.cache.enabled=false
; sonar.cfamily.cache.path=./sonar/cache

View File

@@ -64,12 +64,11 @@ void AnalogSensor::reload() {
// load the list of analog sensors from the customization service // load the list of analog sensors from the customization service
// and store them locally and then activate them // and store them locally and then activate them
EMSESP::webCustomizationService.read([&](WebCustomization & settings) { EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
auto sensors = settings.analogCustomizations;
auto it = sensors_.begin(); auto it = sensors_.begin();
for (auto & sensor_ : sensors_) { for (auto & sensor_ : sensors_) {
// update existing sensors // update existing sensors
bool found = false; bool found = false;
for (auto & sensor : sensors) { //search customlist for (const auto & sensor : settings.analogCustomizations) { //search customlist
if (sensor_.id() == sensor.id) { if (sensor_.id() == sensor.id) {
// for output sensors set value to new start-value // for output sensors set value to new start-value
if ((sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT) if ((sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT)
@@ -90,10 +89,11 @@ void AnalogSensor::reload() {
} }
it++; it++;
} }
// add new sensors from list // add new sensors from list
for (auto & sensor : sensors) { for (const auto & sensor : settings.analogCustomizations) {
bool found = false; bool found = false;
for (auto & sensor_ : sensors_) { for (const auto & sensor_ : sensors_) {
if (sensor_.id() == sensor.id) { if (sensor_.id() == sensor.id) {
found = true; found = true;
} }
@@ -295,7 +295,7 @@ bool AnalogSensor::update(uint8_t id, const std::string & name, float offset, fl
if (!found_sensor) { if (!found_sensor) {
EMSESP::webCustomizationService.update( EMSESP::webCustomizationService.update(
[&](WebCustomization & settings) { [&](WebCustomization & settings) {
AnalogCustomization newSensor = AnalogCustomization(); auto newSensor = AnalogCustomization();
newSensor.id = id; newSensor.id = id;
newSensor.name = name; newSensor.name = name;
newSensor.offset = offset; newSensor.offset = offset;
@@ -325,7 +325,7 @@ bool AnalogSensor::updated_values() {
} }
// publish a single sensor to MQTT // publish a single sensor to MQTT
void AnalogSensor::publish_sensor(const Sensor & sensor) { void AnalogSensor::publish_sensor(const Sensor & sensor) const {
if (Mqtt::publish_single()) { if (Mqtt::publish_single()) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
if (Mqtt::publish_single2cmd()) { if (Mqtt::publish_single2cmd()) {
@@ -339,7 +339,7 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) {
} }
// send empty config topic to remove the entry from HA // send empty config topic to remove the entry from HA
void AnalogSensor::remove_ha_topic(const uint8_t id) { void AnalogSensor::remove_ha_topic(const uint8_t id) const {
if (!Mqtt::ha_enabled()) { if (!Mqtt::ha_enabled()) {
return; return;
} }
@@ -363,7 +363,6 @@ void AnalogSensor::publish_values(const bool force) {
for (const auto & sensor : sensors_) { for (const auto & sensor : sensors_) {
publish_sensor(sensor); publish_sensor(sensor);
} }
// return;
} }
DynamicJsonDocument doc(120 * num_sensors); DynamicJsonDocument doc(120 * num_sensors);
@@ -383,18 +382,15 @@ void AnalogSensor::publish_values(const bool force) {
case AnalogType::PWM_0: case AnalogType::PWM_0:
case AnalogType::PWM_1: case AnalogType::PWM_1:
case AnalogType::PWM_2: case AnalogType::PWM_2:
dataSensor["value"] = (float)sensor.value(); // float dataSensor["value"] = sensor.value(); // float
break; break;
case AnalogType::DIGITAL_IN:
case AnalogType::DIGITAL_OUT:
default: default:
dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0 dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0
break; break;
} }
// create HA config // create HA config
if (Mqtt::ha_enabled()) { if (Mqtt::ha_enabled() && (!sensor.ha_registered || force)) {
if (!sensor.ha_registered || force) {
LOG_DEBUG(F("Recreating HA config for analog sensor ID %d"), sensor.id()); LOG_DEBUG(F("Recreating HA config for analog sensor ID %d"), sensor.id());
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config; StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
@@ -424,7 +420,7 @@ void AnalogSensor::publish_values(const bool force) {
sensor.ha_registered = true; sensor.ha_registered = true;
} }
}
} else { } else {
// not nested // not nested
@@ -432,12 +428,13 @@ void AnalogSensor::publish_values(const bool force) {
} }
} }
} }
Mqtt::publish(F("analogsensor_data"), doc.as<JsonObject>()); Mqtt::publish(F("analogsensor_data"), doc.as<JsonObject>());
} }
// called from emsesp.cpp, similar to the emsdevice->get_value_info // called from emsesp.cpp, similar to the emsdevice->get_value_info
// searches by name // searches by name
bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) { bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) const {
for (const auto & sensor : sensors_) { for (const auto & sensor : sensors_) {
if (strcmp(cmd, sensor.name().c_str()) == 0) { if (strcmp(cmd, sensor.name().c_str()) == 0) {
output["id"] = sensor.id(); output["id"] = sensor.id();
@@ -455,8 +452,8 @@ bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const i
// creates JSON doc from values // creates JSON doc from values
// returns false if there are no sensors // returns false if there are no sensors
bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject & output) { bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject & output) const {
if (sensors_.size() == 0) { if (sensors_.empty()) {
return false; return false;
} }
@@ -532,7 +529,7 @@ bool AnalogSensor::command_setvalue(const char * value, const int8_t id) {
return true; return true;
} else if (sensor.type() == AnalogType::DIGITAL_OUT) { } else if (sensor.type() == AnalogType::DIGITAL_OUT) {
uint8_t v = val; uint8_t v = val;
if ((sensor.id() == 25 || sensor.id() == 26)) { if (sensor.id() == 25 || sensor.id() == 26) {
sensor.set_offset(v); sensor.set_offset(v);
sensor.set_value(v); sensor.set_value(v);
pinMode(sensor.id(), OUTPUT); pinMode(sensor.id(), OUTPUT);
@@ -573,7 +570,6 @@ bool AnalogSensor::command_commands(const char * value, const int8_t id, JsonObj
// hard coded tests // hard coded tests
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
void AnalogSensor::test() { void AnalogSensor::test() {
// Sensor(const uint8_t id, const std::string & name, const float offset, const float factor, const uint8_t uom, const int8_t type);
sensors_.emplace_back(36, "test12", 0, 0.1, 17, AnalogType::ADC); sensors_.emplace_back(36, "test12", 0, 0.1, 17, AnalogType::ADC);
sensors_.back().set_value(12.4); sensors_.back().set_value(12.4);

View File

@@ -127,38 +127,38 @@ class AnalogSensor {
void start(); void start();
void loop(); void loop();
void publish_sensor(const Sensor & sensor); void publish_sensor(const Sensor & sensor) const;
void publish_values(const bool force); void publish_values(const bool force);
void reload(); void reload();
bool updated_values(); bool updated_values();
// return back reference to the sensor list, used by other classes // return back reference to the sensor list, used by other classes
const std::vector<Sensor> sensors() const { std::vector<Sensor> sensors() const {
return sensors_; return sensors_;
} }
uint32_t reads() { uint32_t reads() const {
return sensorreads_; return sensorreads_;
} }
uint32_t fails() { uint32_t fails() const {
return sensorfails_; return sensorfails_;
} }
bool analog_enabled() { bool analog_enabled() const {
return (analog_enabled_); return (analog_enabled_);
} }
bool have_sensors() { bool have_sensors() const {
return (sensors_.size() > 0); return (!sensors_.empty());
} }
size_t no_sensors() { size_t no_sensors() const {
return sensors_.size(); return sensors_.size();
} }
bool update(uint8_t id, const std::string & name, float offset, float factor, uint8_t uom, int8_t type); bool update(uint8_t id, const std::string & name, float offset, float factor, uint8_t uom, int8_t type);
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id); bool get_value_info(JsonObject & output, const char * cmd, const int8_t id) const;
#ifdef EMSESP_DEBUG #ifdef EMSESP_DEBUG
void test(); void test();
@@ -170,10 +170,10 @@ class AnalogSensor {
static uuid::log::Logger logger_; static uuid::log::Logger logger_;
void remove_ha_topic(const uint8_t id); void remove_ha_topic(const uint8_t id) const;
bool command_setvalue(const char * value, const int8_t id); bool command_setvalue(const char * value, const int8_t id);
void measure(); void measure();
bool command_info(const char * value, const int8_t id, JsonObject & output); bool command_info(const char * value, const int8_t id, JsonObject & output) const;
bool command_commands(const char * value, const int8_t id, JsonObject & output); bool command_commands(const char * value, const int8_t id, JsonObject & output);
std::vector<Sensor> sensors_; // our list of sensors std::vector<Sensor> sensors_; // our list of sensors

View File

@@ -39,13 +39,13 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
} }
// check first if it's from API, if so strip the "api/" // check first if it's from API, if so strip the "api/"
if ((p.paths().front() == "api")) { if (p.paths().front() == "api") {
p.paths().erase(p.paths().begin()); p.paths().erase(p.paths().begin());
} else { } else {
// not /api, so must be MQTT path. Check for base and remove it. // not /api, so must be MQTT path. Check for base and remove it.
if (!strncmp(path, Mqtt::base().c_str(), Mqtt::base().length())) { if (!strncmp(path, Mqtt::base().c_str(), Mqtt::base().length())) {
char new_path[Mqtt::MQTT_TOPIC_MAX_SIZE]; char new_path[Mqtt::MQTT_TOPIC_MAX_SIZE];
strncpy(new_path, path, sizeof(new_path)); strlcpy(new_path, path, sizeof(new_path));
p.parse(new_path + Mqtt::base().length() + 1); // re-parse the stripped path p.parse(new_path + Mqtt::base().length() + 1); // re-parse the stripped path
} else { } else {
return message(CommandRet::ERROR, "unrecognized path", output); // error return message(CommandRet::ERROR, "unrecognized path", output); // error
@@ -151,7 +151,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
return_code = Command::call(device_type, command_p, Helpers::itoa((int16_t)data.as<int>(), data_str), is_admin, id_n, output); return_code = Command::call(device_type, command_p, Helpers::itoa((int16_t)data.as<int>(), data_str), is_admin, id_n, output);
} else if (data.is<float>()) { } else if (data.is<float>()) {
char data_str[10]; char data_str[10];
return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, (float)data.as<float>(), 2), is_admin, id_n, output); return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, data.as<float>(), 2), is_admin, id_n, output);
} else if (data.isNull()) { } else if (data.isNull()) {
return_code = Command::call(device_type, command_p, "", is_admin, id_n, output); // empty, will do a query instead return_code = Command::call(device_type, command_p, "", is_admin, id_n, output); // empty, will do a query instead
} else { } else {
@@ -160,22 +160,19 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
return return_code; return return_code;
} }
const std::string Command::return_code_string(const uint8_t return_code) { std::string Command::return_code_string(const uint8_t return_code) {
switch (return_code) { switch (return_code) {
case CommandRet::ERROR: case CommandRet::ERROR:
return read_flash_string(F("Error")); return read_flash_string(F("Error"));
break;
case CommandRet::OK: case CommandRet::OK:
return read_flash_string(F("OK")); return read_flash_string(F("OK"));
break;
case CommandRet::NOT_FOUND: case CommandRet::NOT_FOUND:
return read_flash_string(F("Not Found")); return read_flash_string(F("Not Found"));
break;
case CommandRet::NOT_ALLOWED: case CommandRet::NOT_ALLOWED:
return read_flash_string(F("Not Authorized")); return read_flash_string(F("Not Authorized"));
break;
case CommandRet::FAIL: case CommandRet::FAIL:
return read_flash_string(F("Failed")); return read_flash_string(F("Failed"));
default:
break; break;
} }
char s[4]; char s[4];
@@ -191,7 +188,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
// make a copy of the string command for parsing // make a copy of the string command for parsing
char command_s[100]; char command_s[100];
strncpy(command_s, command, sizeof(command_s)); strlcpy(command_s, command, sizeof(command_s));
// look for a delimeter and split the string // look for a delimeter and split the string
char * p = command_s; char * p = command_s;
@@ -365,7 +362,7 @@ bool Command::list(const uint8_t device_type, JsonObject & output) {
} }
sorted_cmds.sort(); sorted_cmds.sort();
for (auto & cl : sorted_cmds) { for (const auto & cl : sorted_cmds) {
for (const auto & cf : cmdfunctions_) { for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) { if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
output[cl] = cf.description_; output[cl] = cf.description_;
@@ -394,7 +391,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
// if not in verbose mode, just print them on a single line // if not in verbose mode, just print them on a single line
if (!verbose) { if (!verbose) {
for (auto & cl : sorted_cmds) { for (const auto & cl : sorted_cmds) {
shell.print(cl); shell.print(cl);
shell.print(" "); shell.print(" ");
} }
@@ -404,7 +401,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
// verbose mode // verbose mode
shell.println(); shell.println();
for (auto & cl : sorted_cmds) { for (const auto & cl : sorted_cmds) {
// find and print the description // find and print the description
for (const auto & cf : cmdfunctions_) { for (const auto & cf : cmdfunctions_) {
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) { if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == read_flash_string(cf.cmd_))) {
@@ -462,7 +459,7 @@ bool Command::device_has_commands(const uint8_t device_type) {
} }
for (const auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_type)) { if (emsdevice && (emsdevice->device_type() == device_type)) {
// device found, now see if it has any commands // device found, now see if it has any commands
for (const auto & cf : cmdfunctions_) { for (const auto & cf : cmdfunctions_) {
if (cf.device_type_ == device_type) { if (cf.device_type_ == device_type) {
@@ -488,7 +485,7 @@ void Command::show_devices(uuid::console::Shell & shell) {
for (const auto & device_class : EMSFactory::device_handlers()) { for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first) && (device_has_commands(device_class.first))) { if (emsdevice && (emsdevice->device_type() == device_class.first) && (device_has_commands(device_class.first))) {
shell.printf("%s ", EMSdevice::device_type_2_device_name(device_class.first).c_str()); shell.printf("%s ", EMSdevice::device_type_2_device_name(device_class.first).c_str());
break; // we only want to show one (not multiple of the same device types) break; // we only want to show one (not multiple of the same device types)
} }
@@ -541,7 +538,7 @@ void Command::show_all(uuid::console::Shell & shell) {
// e.g. //one/two////three/// becomes /one/two/three // e.g. //one/two////three/// becomes /one/two/three
std::string SUrlParser::path() { std::string SUrlParser::path() {
std::string s = "/"; // set up the beginning slash std::string s = "/"; // set up the beginning slash
for (std::string & f : m_folders) { for (const std::string & f : m_folders) {
s += f; s += f;
s += "/"; s += "/";
} }
@@ -554,6 +551,14 @@ SUrlParser::SUrlParser(const char * uri) {
} }
bool SUrlParser::parse(const char * uri) { bool SUrlParser::parse(const char * uri) {
if (uri == nullptr) {
return false;
}
if (*uri == '\0') {
return false;
}
m_folders.clear(); m_folders.clear();
m_keysvalues.clear(); m_keysvalues.clear();
enum Type { begin, folder, param, value }; enum Type { begin, folder, param, value };
@@ -563,7 +568,6 @@ bool SUrlParser::parse(const char * uri) {
enum Type t = Type::begin; enum Type t = Type::begin;
std::string last_param; std::string last_param;
if (c != nullptr || *c != '\0') {
do { do {
if (*c == '/') { if (*c == '/') {
if (s.length() > 0) { if (s.length() > 0) {
@@ -610,7 +614,7 @@ bool SUrlParser::parse(const char * uri) {
s += *c; s += *c;
} }
} while (*c++ != '\0'); } while (*c++ != '\0');
}
return true; return true;
} }

View File

@@ -125,22 +125,22 @@ class Command {
static const char * parse_command_string(const char * command, int8_t & id); static const char * parse_command_string(const char * command, int8_t & id);
static const std::string return_code_string(const uint8_t return_code); static std::string return_code_string(const uint8_t return_code);
private: private:
static uuid::log::Logger logger_; static uuid::log::Logger logger_;
static std::vector<CmdFunction> cmdfunctions_; // the list of commands static std::vector<CmdFunction> cmdfunctions_; // the list of commands
inline static uint8_t message(uint8_t error_code, const char * message, JsonObject & output) { inline static uint8_t message(uint8_t error_code, const char * message, const JsonObject & output) {
output.clear(); output.clear();
output["message"] = (const char *)message; output["message"] = message;
return error_code; return error_code;
} }
}; };
typedef std::unordered_map<std::string, std::string> KeyValueMap_t; using KeyValueMap_t = std::unordered_map<std::string, std::string>;
typedef std::vector<std::string> Folder_t; using Folder_t = std::vector<std::string>;
class SUrlParser { class SUrlParser {
private: private:
@@ -148,7 +148,7 @@ class SUrlParser {
Folder_t m_folders; Folder_t m_folders;
public: public:
SUrlParser(){}; SUrlParser() = default;
SUrlParser(const char * url); SUrlParser(const char * url);
bool parse(const char * url); bool parse(const char * url);

View File

@@ -31,8 +31,7 @@ std::shared_ptr<Commands> EMSESPShell::commands = [] {
return commands; return commands;
}(); }();
static std::shared_ptr<EMSESPShell> shell; std::shared_ptr<EMSESPShell> shell;
std::vector<bool> EMSESPStreamConsole::ptys_; std::vector<bool> EMSESPStreamConsole::ptys_;
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
@@ -447,7 +446,7 @@ void Console::load_standard_commands(unsigned int context) {
flash_string_vector{F("test")}, flash_string_vector{F("test")},
flash_string_vector{F_(name_optional), F_(data_optional)}, flash_string_vector{F_(name_optional), F_(data_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) { [](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size() == 0) { if (arguments.empty()) {
Test::run_test(shell, "default"); Test::run_test(shell, "default");
} else if (arguments.size() == 1) { } else if (arguments.size() == 1) {
Test::run_test(shell, arguments.front()); Test::run_test(shell, arguments.front());
@@ -467,7 +466,7 @@ void Console::load_standard_commands(unsigned int context) {
flash_string_vector{F_(debug)}, flash_string_vector{F_(debug)},
flash_string_vector{F_(name_optional)}, flash_string_vector{F_(name_optional)},
[](Shell & shell, const std::vector<std::string> & arguments) { [](Shell & shell, const std::vector<std::string> & arguments) {
if (arguments.size() == 0) { if (arguments.empty()) {
Test::debug(shell, "default"); Test::debug(shell, "default");
} else { } else {
Test::debug(shell, arguments.front()); Test::debug(shell, arguments.front());
@@ -515,10 +514,7 @@ void Console::load_standard_commands(unsigned int context) {
EMSESPShell::commands->add_command(context, EMSESPShell::commands->add_command(context,
CommandFlags::USER, CommandFlags::USER,
flash_string_vector{F_(exit)}, flash_string_vector{F_(exit)},
[=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { [=](Shell & shell, const std::vector<std::string> & arguments __attribute__((unused))) { shell.stop(); });
shell.stop();
// shell.exit_context();
});
EMSESPShell::commands->add_command(context, EMSESPShell::commands->add_command(context,
CommandFlags::USER, CommandFlags::USER,

View File

@@ -354,7 +354,7 @@ bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObj
// creates JSON doc from values // creates JSON doc from values
// returns false if there are no sensors // returns false if there are no sensors
bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & output) { bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & output) {
if (sensors_.size() == 0) { if (sensors_.empty()) {
return false; return false;
} }
@@ -435,7 +435,6 @@ void DallasSensor::publish_values(const bool force) {
for (const auto & sensor : sensors_) { for (const auto & sensor : sensors_) {
publish_sensor(sensor); publish_sensor(sensor);
} }
// return;
} }
DynamicJsonDocument doc(120 * num_sensors); DynamicJsonDocument doc(120 * num_sensors);
@@ -536,8 +535,8 @@ std::string DallasSensor::Sensor::name() const {
bool DallasSensor::Sensor::apply_customization() { bool DallasSensor::Sensor::apply_customization() {
EMSESP::webCustomizationService.read([&](WebCustomization & settings) { EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
auto sensors = settings.sensorCustomizations; auto sensors = settings.sensorCustomizations;
if (sensors.size() != 0) { if (sensors.empty()) {
for (auto & sensor : sensors) { for (const auto & sensor : sensors) {
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
LOG_DEBUG(F("Loading customization for dallas sensor %s"), sensor.id_str.c_str()); LOG_DEBUG(F("Loading customization for dallas sensor %s"), sensor.id_str.c_str());
#endif #endif

View File

@@ -85,7 +85,7 @@ class DallasSensor {
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id); bool get_value_info(JsonObject & output, const char * cmd, const int8_t id);
// return back reference to the sensor list, used by other classes // return back reference to the sensor list, used by other classes
const std::vector<Sensor> sensors() const { std::vector<Sensor> sensors() const {
return sensors_; return sensors_;
} }
@@ -102,7 +102,7 @@ class DallasSensor {
} }
bool have_sensors() { bool have_sensors() {
return (sensors_.size() > 0); return (!sensors_.empty());
} }
size_t no_sensors() { size_t no_sensors() {

View File

@@ -532,7 +532,7 @@ void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, wwDisinfectionTemp_, 8); has_update(telegram, wwDisinfectionTemp_, 8);
has_bitupdate(telegram, wwChargeType_, 10, 0); // 0 = charge pump, 0xff = 3-way valve has_bitupdate(telegram, wwChargeType_, 10, 0); // 0 = charge pump, 0xff = 3-way valve
uint8_t wwComfort; uint8_t wwComfort = EMS_VALUE_UINT_NOTSET;
telegram->read_value(wwComfort, 9); telegram->read_value(wwComfort, 9);
if (wwComfort == 0) { if (wwComfort == 0) {
wwComfort = 0; // Hot wwComfort = 0; // Hot
@@ -594,7 +594,7 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
// read 3 char service code / installation status as appears on the display // read 3 char service code / installation status as appears on the display
if ((telegram->message_length > 3) && (telegram->offset == 0)) { if ((telegram->message_length > 3) && (telegram->offset == 0)) {
char serviceCode[4]; char serviceCode[4] = {0};
telegram->read_value(serviceCode[0], 1); telegram->read_value(serviceCode[0], 1);
serviceCode[0] = (serviceCode[0] == (char)0xF0) ? '~' : serviceCode[0]; serviceCode[0] = (serviceCode[0] == (char)0xF0) ? '~' : serviceCode[0];
telegram->read_value(serviceCode[1], 2); telegram->read_value(serviceCode[1], 2);
@@ -878,8 +878,8 @@ void Boiler::process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram) {
if (telegram->message_data[4] & 0x80) { // valid date if (telegram->message_data[4] & 0x80) { // valid date
static uint32_t lastCodeDate_ = 0; // last code date static uint32_t lastCodeDate_ = 0; // last code date
char code[3]; char code[3] = {0};
uint16_t codeNo; uint16_t codeNo = EMS_VALUE_SHORT_NOTSET;
code[0] = telegram->message_data[0]; code[0] = telegram->message_data[0];
code[1] = telegram->message_data[1]; code[1] = telegram->message_data[1];
code[2] = 0; code[2] = 0;
@@ -890,7 +890,7 @@ void Boiler::process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram) {
uint8_t hour = telegram->message_data[6]; uint8_t hour = telegram->message_data[6];
uint8_t min = telegram->message_data[8]; uint8_t min = telegram->message_data[8];
uint32_t date = (year - 2000) * 535680UL + month * 44640UL + day * 1440UL + hour * 60 + min; uint32_t date = (year - 2000) * 535680UL + month * 44640UL + day * 1440UL + hour * 60 + min;
uint16_t duration; uint16_t duration = EMS_VALUE_SHORT_NOTSET;
telegram->read_value(duration, 9); telegram->read_value(duration, 9);
// store only the newest code from telegrams 10 and 11 // store only the newest code from telegrams 10 and 11
if (date > lastCodeDate_) { if (date > lastCodeDate_) {
@@ -908,8 +908,8 @@ void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram)
return; return;
} }
char code[sizeof(lastCode_)]; char code[sizeof(lastCode_)] = {0};
uint16_t codeNo; uint16_t codeNo = EMS_VALUE_SHORT_NOTSET;
code[0] = telegram->message_data[5]; code[0] = telegram->message_data[5];
code[1] = telegram->message_data[6]; code[1] = telegram->message_data[6];
code[2] = telegram->message_data[7]; code[2] = telegram->message_data[7];
@@ -1488,6 +1488,10 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
// maintenance // maintenance
bool Boiler::set_maintenance(const char * value, const int8_t id) { bool Boiler::set_maintenance(const char * value, const int8_t id) {
if (value == nullptr) {
return false;
}
std::string s; std::string s;
if (Helpers::value2string(value, s)) { if (Helpers::value2string(value, s)) {
if (s == Helpers::toLower(read_flash_string(F_(reset)))) { if (s == Helpers::toLower(read_flash_string(F_(reset)))) {
@@ -1549,7 +1553,7 @@ bool Boiler::set_maintenancetime(const char * value, const int8_t id) {
//maintenance //maintenance
bool Boiler::set_maintenancedate(const char * value, const int8_t id) { bool Boiler::set_maintenancedate(const char * value, const int8_t id) {
if (strlen(value) == 10) { // date if ((value != nullptr) && strlen(value) == 10) { // date
uint8_t day = (value[0] - '0') * 10 + (value[1] - '0'); uint8_t day = (value[0] - '0') * 10 + (value[1] - '0');
uint8_t month = (value[3] - '0') * 10 + (value[4] - '0'); uint8_t month = (value[3] - '0') * 10 + (value[4] - '0');
uint8_t year = (uint8_t)(Helpers::atoint(&value[6]) - 2000); uint8_t year = (uint8_t)(Helpers::atoint(&value[6]) - 2000);

View File

@@ -293,7 +293,7 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
*/ */
// if it's the first set the status flag // if it's the first set the status flag
if (heating_circuits_.size() == 0) { if (heating_circuits_.empty()) {
strlcpy(status_, "online", sizeof(status_)); strlcpy(status_, "online", sizeof(status_));
} }
@@ -333,7 +333,7 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
} }
// add the HVAC/Climate HA component for the HC // add the HVAC/Climate HA component for the HC
void Thermostat::add_ha_climate(std::shared_ptr<HeatingCircuit> hc) { void Thermostat::add_ha_climate(std::shared_ptr<HeatingCircuit> hc) const {
if (!Mqtt::ha_enabled()) { if (!Mqtt::ha_enabled()) {
hc->climate = EMS_VALUE_UINT_NOTSET; hc->climate = EMS_VALUE_UINT_NOTSET;
return; return;
@@ -452,65 +452,44 @@ std::string Thermostat::mode_tostring(uint8_t mode) {
switch (mode) { switch (mode) {
case HeatingCircuit::Mode::OFF: case HeatingCircuit::Mode::OFF:
return read_flash_string(F_(off)); return read_flash_string(F_(off));
break;
case HeatingCircuit::Mode::MANUAL: case HeatingCircuit::Mode::MANUAL:
return read_flash_string(F_(manual)); return read_flash_string(F_(manual));
break;
case HeatingCircuit::Mode::DAY: case HeatingCircuit::Mode::DAY:
return read_flash_string(F_(day)); return read_flash_string(F_(day));
break;
case HeatingCircuit::Mode::NIGHT: case HeatingCircuit::Mode::NIGHT:
return read_flash_string(F_(night)); return read_flash_string(F_(night));
break;
case HeatingCircuit::Mode::ECO: case HeatingCircuit::Mode::ECO:
return read_flash_string(F_(eco)); return read_flash_string(F_(eco));
break;
case HeatingCircuit::Mode::COMFORT: case HeatingCircuit::Mode::COMFORT:
return read_flash_string(F_(comfort)); return read_flash_string(F_(comfort));
break;
case HeatingCircuit::Mode::HEAT: case HeatingCircuit::Mode::HEAT:
return read_flash_string(F_(heat)); return read_flash_string(F_(heat));
break;
case HeatingCircuit::Mode::HOLIDAY: case HeatingCircuit::Mode::HOLIDAY:
return read_flash_string(F_(holiday)); return read_flash_string(F_(holiday));
break;
case HeatingCircuit::Mode::NOFROST: case HeatingCircuit::Mode::NOFROST:
return read_flash_string(F_(nofrost)); return read_flash_string(F_(nofrost));
break;
case HeatingCircuit::Mode::AUTO: case HeatingCircuit::Mode::AUTO:
return read_flash_string(F_(auto)); return read_flash_string(F_(auto));
break;
case HeatingCircuit::Mode::SUMMER: case HeatingCircuit::Mode::SUMMER:
return read_flash_string(F_(summer)); return read_flash_string(F_(summer));
break;
case HeatingCircuit::Mode::OFFSET: case HeatingCircuit::Mode::OFFSET:
return read_flash_string(F_(offset)); return read_flash_string(F_(offset));
break;
case HeatingCircuit::Mode::DESIGN: case HeatingCircuit::Mode::DESIGN:
return read_flash_string(F_(design)); return read_flash_string(F_(design));
break;
case HeatingCircuit::Mode::MINFLOW: case HeatingCircuit::Mode::MINFLOW:
return read_flash_string(F_(minflow)); return read_flash_string(F_(minflow));
break;
case HeatingCircuit::Mode::MAXFLOW: case HeatingCircuit::Mode::MAXFLOW:
return read_flash_string(F_(maxflow)); return read_flash_string(F_(maxflow));
break;
case HeatingCircuit::Mode::ROOMINFLUENCE: case HeatingCircuit::Mode::ROOMINFLUENCE:
return read_flash_string(F_(roominfluence[0])); return read_flash_string(F_(roominfluence[0]));
break;
case HeatingCircuit::Mode::FLOWOFFSET: case HeatingCircuit::Mode::FLOWOFFSET:
return read_flash_string(F_(flowtempoffset[0])); return read_flash_string(F_(flowtempoffset[0]));
break;
case HeatingCircuit::Mode::TEMPAUTO: case HeatingCircuit::Mode::TEMPAUTO:
return read_flash_string(F_(tempauto)); return read_flash_string(F_(tempauto));
break;
case HeatingCircuit::Mode::NOREDUCE: case HeatingCircuit::Mode::NOREDUCE:
return read_flash_string(F_(noreduce)); return read_flash_string(F_(noreduce));
break;
default: default:
case HeatingCircuit::Mode::UNKNOWN:
return read_flash_string(F_(unknown)); return read_flash_string(F_(unknown));
break;
} }
} }
@@ -1237,7 +1216,7 @@ void Thermostat::process_RCErrorMessage(std::shared_ptr<const Telegram> telegram
// data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr // data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr
if (telegram->message_data[4] & 0x80) { // valid date if (telegram->message_data[4] & 0x80) { // valid date
char code[sizeof(lastCode_)]; char code[sizeof(lastCode_)] = {0};
uint16_t codeNo = EMS_VALUE_USHORT_NOTSET; uint16_t codeNo = EMS_VALUE_USHORT_NOTSET;
code[0] = telegram->message_data[0]; code[0] = telegram->message_data[0];
code[1] = telegram->message_data[1]; code[1] = telegram->message_data[1];
@@ -1248,7 +1227,7 @@ void Thermostat::process_RCErrorMessage(std::shared_ptr<const Telegram> telegram
uint8_t day = telegram->message_data[7]; uint8_t day = telegram->message_data[7];
uint8_t hour = telegram->message_data[6]; uint8_t hour = telegram->message_data[6];
uint8_t min = telegram->message_data[8]; uint8_t min = telegram->message_data[8];
uint16_t duration; uint16_t duration = EMS_VALUE_SHORT_NOTSET;
telegram->read_value(duration, 9); telegram->read_value(duration, 9);
snprintf(&code[2], sizeof(code) - 2, "(%d) %02d.%02d.%d %02d:%02d (%d min)", codeNo, day, month, year, hour, min, duration); snprintf(&code[2], sizeof(code) - 2, "(%d) %02d.%02d.%d %02d:%02d (%d min)", codeNo, day, month, year, hour, min, duration);
has_update(lastCode_, code, sizeof(lastCode_)); has_update(lastCode_, code, sizeof(lastCode_));
@@ -1292,7 +1271,7 @@ bool Thermostat::set_calinttemp(const char * value, const int8_t id) {
return false; return false;
} }
int8_t t = (int8_t)(ct * 10); auto t = (int8_t)(ct * 10);
LOG_DEBUG(F("Calibrating internal temperature to %d.%d C"), t / 10, t < 0 ? -t % 10 : t % 10); LOG_DEBUG(F("Calibrating internal temperature to %d.%d C"), t / 10, t < 0 ? -t % 10 : t % 10);
if (model() == EMS_DEVICE_FLAG_RC10) { if (model() == EMS_DEVICE_FLAG_RC10) {
@@ -1658,9 +1637,11 @@ bool Thermostat::set_wwCircProg(const char * value, const int8_t id) {
bool Thermostat::set_holiday(const char * value, const int8_t id, const bool vacation) { bool Thermostat::set_holiday(const char * value, const int8_t id, const bool vacation) {
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id; uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num); std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
if (hc == nullptr) {
if ((hc == nullptr) || (value == nullptr)) {
return false; return false;
} }
if (strlen(value) != 21) { if (strlen(value) != 21) {
return false; return false;
} }
@@ -1805,7 +1786,7 @@ bool Thermostat::set_roominfl_factor(const char * value, const int8_t id) {
// sets the thermostat working mode, where mode is a string // sets the thermostat working mode, where mode is a string
// converts string mode to HeatingCircuit::Mode // converts string mode to HeatingCircuit::Mode
bool Thermostat::set_mode(const char * value, const int8_t id) { bool Thermostat::set_mode(const char * value, const int8_t id) {
if (strlen(value) >= 20) { if ((value == nullptr) || (strlen(value) >= 20)) {
return false; return false;
} }
@@ -1907,9 +1888,7 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
set_mode_value = 1; set_mode_value = 1;
break; break;
default: default: // AUTO & ECO
case HeatingCircuit::Mode::AUTO:
case HeatingCircuit::Mode::ECO:
set_mode_value = 2; set_mode_value = 2;
break; break;
} }
@@ -2133,7 +2112,7 @@ bool Thermostat::set_switchtime(const char * value, const uint16_t type_id, char
} }
const char * s_mode = doc["mode"]; const char * s_mode = doc["mode"];
const char * s_time = doc["time"]; const char * s_time = doc["time"];
if ((model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N)) { if (model() == EMS_DEVICE_FLAG_RC35 || model() == EMS_DEVICE_FLAG_RC30_N) {
bool b; bool b;
if (Helpers::value2bool(s_mode, b)) { if (Helpers::value2bool(s_mode, b)) {
on = b ? 1 : 0; on = b ? 1 : 0;
@@ -2348,6 +2327,8 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = 4; offset = 4;
} }
break; break;
default:
break;
} }
} else if (model == EMS_DEVICE_FLAG_RC20) { } else if (model == EMS_DEVICE_FLAG_RC20) {
switch (mode) { switch (mode) {
@@ -2381,6 +2362,8 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = EMS_OFFSET_RC20Set_temp_auto; offset = EMS_OFFSET_RC20Set_temp_auto;
} }
break; break;
default:
break;
} }
} else if (model == EMS_DEVICE_FLAG_RC30) { } else if (model == EMS_DEVICE_FLAG_RC30) {
@@ -2460,7 +2443,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
factor = 1; factor = 1;
break; break;
default: default:
case HeatingCircuit::Mode::AUTO: // HeatingCircuit::Mode::AUTO:
uint8_t mode_ = hc->get_mode(); uint8_t mode_ = hc->get_mode();
if (mode_ == HeatingCircuit::Mode::MANUAL) { if (mode_ == HeatingCircuit::Mode::MANUAL) {
offset = 0x0A; // manual offset offset = 0x0A; // manual offset
@@ -2499,7 +2482,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = EMS_OFFSET_RC20_2_Set_temp_day; offset = EMS_OFFSET_RC20_2_Set_temp_day;
break; break;
default: default:
case HeatingCircuit::Mode::AUTO: // automatic selection, if no type is defined, we use the standard code // automatic selection, if no type is defined, we use the standard code
uint8_t mode_ = hc->get_mode(); uint8_t mode_ = hc->get_mode();
if (mode_ == HeatingCircuit::Mode::NIGHT) { if (mode_ == HeatingCircuit::Mode::NIGHT) {
offset = EMS_OFFSET_RC20_2_Set_temp_night; offset = EMS_OFFSET_RC20_2_Set_temp_night;
@@ -2570,7 +2553,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
factor = 1; factor = 1;
break; break;
default: default:
case HeatingCircuit::Mode::AUTO: // automatic selection, if no type is defined, we use the standard code // automatic selection, if no type is defined, we use the standard code
validate_typeid = monitor_typeids[hc->hc()]; //get setpoint roomtemp back validate_typeid = monitor_typeids[hc->hc()]; //get setpoint roomtemp back
if (model == EMS_DEVICE_FLAG_RC35) { if (model == EMS_DEVICE_FLAG_RC35) {
uint8_t mode_ = hc->get_mode(); uint8_t mode_ = hc->get_mode();
@@ -2606,7 +2589,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = EMS_OFFSET_JunkersSetMessage_day_temp; offset = EMS_OFFSET_JunkersSetMessage_day_temp;
break; break;
default: default:
case HeatingCircuit::Mode::AUTO: // automatic selection, if no type is defined, we use the standard code // automatic selection, if no type is defined, we use the standard code
uint8_t modetype = hc->get_mode_type(); uint8_t modetype = hc->get_mode_type();
if (modetype == HeatingCircuit::Mode::NIGHT || modetype == HeatingCircuit::Mode::ECO) { if (modetype == HeatingCircuit::Mode::NIGHT || modetype == HeatingCircuit::Mode::ECO) {
offset = EMS_OFFSET_JunkersSetMessage_night_temp; offset = EMS_OFFSET_JunkersSetMessage_night_temp;
@@ -2633,7 +2616,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
offset = EMS_OFFSET_JunkersSetMessage2_heat_temp; offset = EMS_OFFSET_JunkersSetMessage2_heat_temp;
break; break;
default: default:
case HeatingCircuit::Mode::AUTO: // automatic selection, if no type is defined, we use the standard code // automatic selection, if no type is defined, we use the standard code
uint8_t modetype = hc->get_mode_type(); uint8_t modetype = hc->get_mode_type();
if (modetype == HeatingCircuit::Mode::NIGHT || modetype == HeatingCircuit::Mode::ECO) { if (modetype == HeatingCircuit::Mode::NIGHT || modetype == HeatingCircuit::Mode::ECO) {
offset = EMS_OFFSET_JunkersSetMessage2_eco_temp; offset = EMS_OFFSET_JunkersSetMessage2_eco_temp;
@@ -2651,7 +2634,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
if (offset != -1) { if (offset != -1) {
// add the write command to the Tx queue. value is *2 // add the write command to the Tx queue. value is *2
// post validate is the corresponding monitor or set type IDs as they can differ per model // post validate is the corresponding monitor or set type IDs as they can differ per model
write_command(set_typeid, offset, (uint8_t)((float)temperature * (float)factor), validate_typeid); write_command(set_typeid, offset, (uint8_t)(temperature * (float)factor), validate_typeid);
return true; return true;
} }
@@ -3300,6 +3283,8 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode4), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program)); register_device_value(tag, &hc->program, DeviceValueType::ENUM, FL_(enum_progMode4), FL_(program), DeviceValueUOM::NONE, MAKE_CF_CB(set_program));
register_device_value(tag, &hc->remotetemp, DeviceValueType::SHORT, FL_(div10), FL_(remotetemp), DeviceValueUOM::DEGREES); register_device_value(tag, &hc->remotetemp, DeviceValueType::SHORT, FL_(div10), FL_(remotetemp), DeviceValueUOM::DEGREES);
break; break;
default:
break;
} }
} }

View File

@@ -93,7 +93,7 @@ class Thermostat : public EMSdevice {
} }
// determines if the heating circuit is actually present and has data // determines if the heating circuit is actually present and has data
bool is_active() { bool is_active() const {
return Helpers::hasValue(selTemp); return Helpers::hasValue(selTemp);
} }
@@ -292,7 +292,7 @@ class Thermostat : public EMSdevice {
void register_device_values_hc(std::shared_ptr<Thermostat::HeatingCircuit> hc); void register_device_values_hc(std::shared_ptr<Thermostat::HeatingCircuit> hc);
void add_ha_climate(std::shared_ptr<HeatingCircuit> hc); void add_ha_climate(std::shared_ptr<HeatingCircuit> hc) const;
void process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram); void process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram);
void process_IBASettings(std::shared_ptr<const Telegram> telegram); void process_IBASettings(std::shared_ptr<const Telegram> telegram);

View File

@@ -34,7 +34,7 @@ uint8_t EMSdevice::count_entities() {
} }
// see if there are entities, excluding any commands // see if there are entities, excluding any commands
bool EMSdevice::has_entities() { bool EMSdevice::has_entities() const {
for (const auto & dv : devicevalues_) { for (const auto & dv : devicevalues_) {
if (dv.type != DeviceValueType::CMD) { if (dv.type != DeviceValueType::CMD) {
return true; return true;
@@ -43,107 +43,71 @@ bool EMSdevice::has_entities() {
return false; return false;
} }
const std::string EMSdevice::tag_to_string(uint8_t tag) { std::string EMSdevice::tag_to_string(uint8_t tag) {
return read_flash_string(DeviceValue::DeviceValueTAG_s[tag]); return read_flash_string(DeviceValue::DeviceValueTAG_s[tag]);
} }
const std::string EMSdevice::tag_to_mqtt(uint8_t tag) { std::string EMSdevice::tag_to_mqtt(uint8_t tag) {
return read_flash_string(DeviceValue::DeviceValueTAG_mqtt[tag]); return read_flash_string(DeviceValue::DeviceValueTAG_mqtt[tag]);
} }
const std::string EMSdevice::uom_to_string(uint8_t uom) { std::string EMSdevice::uom_to_string(uint8_t uom) {
if (EMSESP::system_.fahrenheit() && (uom == DeviceValueUOM::DEGREES || uom == DeviceValueUOM::DEGREES_R)) { if (EMSESP::system_.fahrenheit() && (uom == DeviceValueUOM::DEGREES || uom == DeviceValueUOM::DEGREES_R)) {
return read_flash_string(DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT]); return read_flash_string(DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT]);
} }
return read_flash_string(DeviceValue::DeviceValueUOM_s[uom]); return read_flash_string(DeviceValue::DeviceValueUOM_s[uom]);
} }
const std::string EMSdevice::brand_to_string() const { std::string EMSdevice::brand_to_string() const {
switch (brand_) { switch (brand_) {
case EMSdevice::Brand::BOSCH: case EMSdevice::Brand::BOSCH:
return read_flash_string(F("Bosch")); return read_flash_string(F("Bosch"));
break;
case EMSdevice::Brand::JUNKERS: case EMSdevice::Brand::JUNKERS:
return read_flash_string(F("Junkers")); return read_flash_string(F("Junkers"));
break;
case EMSdevice::Brand::BUDERUS: case EMSdevice::Brand::BUDERUS:
return read_flash_string(F("Buderus")); return read_flash_string(F("Buderus"));
break;
case EMSdevice::Brand::NEFIT: case EMSdevice::Brand::NEFIT:
return read_flash_string(F("Nefit")); return read_flash_string(F("Nefit"));
break;
case EMSdevice::Brand::SIEGER: case EMSdevice::Brand::SIEGER:
return read_flash_string(F("Sieger")); return read_flash_string(F("Sieger"));
break;
case EMSdevice::Brand::WORCESTER: case EMSdevice::Brand::WORCESTER:
return read_flash_string(F("Worcester")); return read_flash_string(F("Worcester"));
break;
case EMSdevice::Brand::IVT: case EMSdevice::Brand::IVT:
return read_flash_string(F("IVT")); return read_flash_string(F("IVT"));
break;
case EMSdevice::Brand::NO_BRAND:
default: default:
return read_flash_string(F("")); return read_flash_string(F(""));
break;
} }
return std::string{};
} }
// returns the name of the MQTT topic to use for a specific device, without the base // returns the name of the MQTT topic to use for a specific device, without the base
const std::string EMSdevice::device_type_2_device_name(const uint8_t device_type) { std::string EMSdevice::device_type_2_device_name(const uint8_t device_type) {
switch (device_type) { switch (device_type) {
case DeviceType::SYSTEM: case DeviceType::SYSTEM:
return read_flash_string(F_(system)); return read_flash_string(F_(system));
break;
case DeviceType::BOILER: case DeviceType::BOILER:
return read_flash_string(F_(boiler)); return read_flash_string(F_(boiler));
break;
case DeviceType::THERMOSTAT: case DeviceType::THERMOSTAT:
return read_flash_string(F_(thermostat)); return read_flash_string(F_(thermostat));
break;
case DeviceType::HEATPUMP: case DeviceType::HEATPUMP:
return read_flash_string(F_(heatpump)); return read_flash_string(F_(heatpump));
break;
case DeviceType::SOLAR: case DeviceType::SOLAR:
return read_flash_string(F_(solar)); return read_flash_string(F_(solar));
break;
case DeviceType::CONNECT: case DeviceType::CONNECT:
return read_flash_string(F_(connect)); return read_flash_string(F_(connect));
break;
case DeviceType::MIXER: case DeviceType::MIXER:
return read_flash_string(F_(mixer)); return read_flash_string(F_(mixer));
break;
case DeviceType::DALLASSENSOR: case DeviceType::DALLASSENSOR:
return read_flash_string(F_(dallassensor)); return read_flash_string(F_(dallassensor));
break;
case DeviceType::ANALOGSENSOR: case DeviceType::ANALOGSENSOR:
return read_flash_string(F_(analogsensor)); return read_flash_string(F_(analogsensor));
break;
case DeviceType::CONTROLLER: case DeviceType::CONTROLLER:
return read_flash_string(F_(controller)); return read_flash_string(F_(controller));
break;
case DeviceType::SWITCH: case DeviceType::SWITCH:
return read_flash_string(F_(switch)); return read_flash_string(F_(switch));
break;
case DeviceType::GATEWAY: case DeviceType::GATEWAY:
return read_flash_string(F_(gateway)); return read_flash_string(F_(gateway));
break;
default: default:
return read_flash_string(F_(unknown)); return read_flash_string(F_(unknown));
break;
} }
} }
@@ -196,7 +160,7 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
} }
// return name of the device type, capitalized // return name of the device type, capitalized
const std::string EMSdevice::device_type_name() const { std::string EMSdevice::device_type_name() const {
std::string s = device_type_2_device_name(device_type_); std::string s = device_type_2_device_name(device_type_);
s[0] = toupper(s[0]); s[0] = toupper(s[0]);
return s; return s;
@@ -207,34 +171,25 @@ uint8_t EMSdevice::decode_brand(uint8_t value) {
switch (value) { switch (value) {
case 1: case 1:
return EMSdevice::Brand::BOSCH; return EMSdevice::Brand::BOSCH;
break;
case 2: case 2:
return EMSdevice::Brand::JUNKERS; return EMSdevice::Brand::JUNKERS;
break;
case 3: case 3:
return EMSdevice::Brand::BUDERUS; return EMSdevice::Brand::BUDERUS;
break;
case 4: case 4:
return EMSdevice::Brand::NEFIT; return EMSdevice::Brand::NEFIT;
break;
case 5: case 5:
return EMSdevice::Brand::SIEGER; return EMSdevice::Brand::SIEGER;
break;
case 11: case 11:
return EMSdevice::Brand::WORCESTER; return EMSdevice::Brand::WORCESTER;
break;
case 13: case 13:
return EMSdevice::Brand::IVT; return EMSdevice::Brand::IVT;
break;
case 0:
default: default:
return EMSdevice::Brand::NO_BRAND; return EMSdevice::Brand::NO_BRAND;
break;
} }
} }
// returns string of a human friendly description of the EMS device // returns string of a human friendly description of the EMS device
const std::string EMSdevice::to_string() const { std::string EMSdevice::to_string() const {
// for devices that haven't been lookup yet, don't show all details // for devices that haven't been lookup yet, don't show all details
if (product_id_ == 0) { if (product_id_ == 0) {
return name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ")"; return name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
@@ -249,7 +204,7 @@ const std::string EMSdevice::to_string() const {
} }
// returns out brand + device name // returns out brand + device name
const std::string EMSdevice::to_string_short() const { std::string EMSdevice::to_string_short() const {
if (brand_ == Brand::NO_BRAND) { if (brand_ == Brand::NO_BRAND) {
return device_type_name() + ": " + name_; return device_type_name() + ": " + name_;
} }
@@ -280,8 +235,8 @@ void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
} }
// get status of automatic fetch for a telegramID // get status of automatic fetch for a telegramID
bool EMSdevice::is_fetch(uint16_t telegram_id) { bool EMSdevice::is_fetch(uint16_t telegram_id) const {
for (auto & tf : telegram_functions_) { for (const auto & tf : telegram_functions_) {
if (tf.telegram_type_id_ == telegram_id) { if (tf.telegram_type_id_ == telegram_id) {
return tf.fetch_; return tf.fetch_;
} }
@@ -301,7 +256,7 @@ bool EMSdevice::has_tag(const uint8_t tag) {
// list of registered device entries // list of registered device entries
// called from the command 'entities' // called from the command 'entities'
void EMSdevice::list_device_entries(JsonObject & output) { void EMSdevice::list_device_entries(JsonObject & output) const {
for (const auto & dv : devicevalues_) { for (const auto & dv : devicevalues_) {
if (dv.has_state(DeviceValueState::DV_VISIBLE) && dv.type != DeviceValueType::CMD && dv.full_name) { if (dv.has_state(DeviceValueState::DV_VISIBLE) && dv.type != DeviceValueType::CMD && dv.full_name) {
// if we have a tag prefix it // if we have a tag prefix it
@@ -326,8 +281,8 @@ void EMSdevice::list_device_entries(JsonObject & output) {
} }
// list all the telegram type IDs for this device // list all the telegram type IDs for this device
void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) { void EMSdevice::show_telegram_handlers(uuid::console::Shell & shell) const {
if (telegram_functions_.size() == 0) { if (telegram_functions_.empty()) {
return; return;
} }
/* /*
@@ -393,7 +348,7 @@ char * EMSdevice::show_telegram_handlers(char * result, uint8_t handlers) {
} }
// list all the mqtt handlers for this device // list all the mqtt handlers for this device
void EMSdevice::show_mqtt_handlers(uuid::console::Shell & shell) { void EMSdevice::show_mqtt_handlers(uuid::console::Shell & shell) const {
Mqtt::show_topic_handlers(shell, device_type_); Mqtt::show_topic_handlers(shell, device_type_);
} }
@@ -426,7 +381,7 @@ void EMSdevice::register_device_value(uint8_t tag,
uint16_t max) { uint16_t max) {
// initialize the device value depending on it's type // initialize the device value depending on it's type
if (type == DeviceValueType::STRING) { if (type == DeviceValueType::STRING) {
*(char *)(value_p) = {'\0'}; *(char *)(value_p) = {'\0'}; // this is important for string functions like strlen() to work later
} else if (type == DeviceValueType::INT) { } else if (type == DeviceValueType::INT) {
*(int8_t *)(value_p) = EMS_VALUE_INT_NOTSET; *(int8_t *)(value_p) = EMS_VALUE_INT_NOTSET;
} else if (type == DeviceValueType::SHORT) { } else if (type == DeviceValueType::SHORT) {
@@ -447,7 +402,7 @@ void EMSdevice::register_device_value(uint8_t tag,
uint8_t i = 0; uint8_t i = 0;
while (options[i++]) { while (options[i++]) {
options_size++; options_size++;
}; }
} }
// this is the unique id set for the device entity. it's a simple sequence number // this is the unique id set for the device entity. it's a simple sequence number
@@ -456,9 +411,6 @@ void EMSdevice::register_device_value(uint8_t tag,
// determine state // determine state
uint8_t state = DeviceValueState::DV_VISIBLE; // default to visible uint8_t state = DeviceValueState::DV_VISIBLE; // default to visible
// if (!full_name) {
// state = DeviceValueState::DV_DEFAULT; // don't show if the full_name is empty
// } else {
// scan through customizations to see if it's on the exclusion list by matching the productID and deviceID // scan through customizations to see if it's on the exclusion list by matching the productID and deviceID
EMSESP::webCustomizationService.read([&](WebCustomization & settings) { EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
for (EntityCustomization entityCustomization : settings.entityCustomizations) { for (EntityCustomization entityCustomization : settings.entityCustomizations) {
@@ -472,7 +424,6 @@ void EMSdevice::register_device_value(uint8_t tag,
} }
} }
}); });
// }
// add the device // add the device
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom, 0, has_cmd, min, max, state, dv_id); devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom, 0, has_cmd, min, max, state, dv_id);
@@ -536,7 +487,7 @@ void EMSdevice::register_device_value(uint8_t tag,
} }
// check if value is visible // check if value is visible
bool EMSdevice::is_visible(void * value_p) { bool EMSdevice::is_visible(const void * value_p) const {
for (const auto & dv : devicevalues_) { for (const auto & dv : devicevalues_) {
if (dv.value_p == value_p) { if (dv.value_p == value_p) {
return dv.has_state(DeviceValueState::DV_VISIBLE); return dv.has_state(DeviceValueState::DV_VISIBLE);
@@ -546,10 +497,11 @@ bool EMSdevice::is_visible(void * value_p) {
} }
// publish a single value on change // publish a single value on change
void EMSdevice::publish_value(void * value_p) { void EMSdevice::publish_value(void * value_p) const {
if (!Mqtt::publish_single() || value_p == nullptr) { if (!Mqtt::publish_single() || value_p == nullptr) {
return; return;
} }
for (const auto & dv : devicevalues_) { for (const auto & dv : devicevalues_) {
if (dv.value_p == value_p && dv.has_state(DeviceValueState::DV_VISIBLE)) { if (dv.value_p == value_p && dv.has_state(DeviceValueState::DV_VISIBLE)) {
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
@@ -593,7 +545,6 @@ void EMSdevice::publish_value(void * value_p) {
} }
break; break;
} }
case DeviceValueType::USHORT: case DeviceValueType::USHORT:
Helpers::render_value(payload, *(uint16_t *)(value_p), divider, fahrenheit); Helpers::render_value(payload, *(uint16_t *)(value_p), divider, fahrenheit);
break; break;
@@ -617,11 +568,12 @@ void EMSdevice::publish_value(void * value_p) {
Helpers::render_value(payload, *(uint32_t *)(value_p), divider); Helpers::render_value(payload, *(uint32_t *)(value_p), divider);
break; break;
case DeviceValueType::STRING: case DeviceValueType::STRING:
default:
if (Helpers::hasValue((char *)(value_p))) { if (Helpers::hasValue((char *)(value_p))) {
strlcpy(payload, (char *)(value_p), sizeof(payload)); strlcpy(payload, (char *)(value_p), sizeof(payload));
} }
break; break;
default:
break;
} }
if (payload[0] != '\0') { if (payload[0] != '\0') {
@@ -632,7 +584,7 @@ void EMSdevice::publish_value(void * value_p) {
} }
// looks up the UOM for a given key from the device value table // looks up the UOM for a given key from the device value table
const std::string EMSdevice::get_value_uom(const char * key) { std::string EMSdevice::get_value_uom(const char * key) const {
// the key may have a TAG string prefixed at the beginning. If so, remove it // the key may have a TAG string prefixed at the beginning. If so, remove it
char new_key[80]; char new_key[80];
strlcpy(new_key, key, sizeof(new_key)); strlcpy(new_key, key, sizeof(new_key));
@@ -651,8 +603,7 @@ const std::string EMSdevice::get_value_uom(const char * key) {
// look up key in our device value list // look up key in our device value list
for (const auto & dv : devicevalues_) { for (const auto & dv : devicevalues_) {
if (dv.has_state(DeviceValueState::DV_VISIBLE) && dv.full_name) { if ((dv.has_state(DeviceValueState::DV_VISIBLE) && dv.full_name) && (read_flash_string(dv.full_name) == key_p)) {
if (read_flash_string(dv.full_name) == key_p) {
// ignore TIME since "minutes" is already added to the string value // ignore TIME since "minutes" is already added to the string value
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) { if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
break; break;
@@ -660,7 +611,6 @@ const std::string EMSdevice::get_value_uom(const char * key) {
return EMSdevice::uom_to_string(dv.uom); return EMSdevice::uom_to_string(dv.uom);
} }
} }
}
return std::string{}; // not found return std::string{}; // not found
} }
@@ -685,9 +635,9 @@ void EMSdevice::generate_values_web(JsonObject & output) {
// handle Booleans (true, false) // handle Booleans (true, false)
if (dv.type == DeviceValueType::BOOL) { if (dv.type == DeviceValueType::BOOL) {
bool value_b = *(bool *)(dv.value_p); bool value_b = *(bool *)(dv.value_p);
if ((EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE)) { if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
obj["v"] = value_b ? "true" : "false"; obj["v"] = value_b ? "true" : "false";
} else if ((EMSESP::system_.bool_format() == BOOL_FORMAT_10)) { } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
obj["v"] = value_b ? 1 : 0; obj["v"] = value_b ? 1 : 0;
} else { } else {
char s[7]; char s[7];
@@ -816,9 +766,9 @@ void EMSdevice::generate_values_web_all(JsonArray & output) {
// handle Booleans (true, false) // handle Booleans (true, false)
if (dv.type == DeviceValueType::BOOL) { if (dv.type == DeviceValueType::BOOL) {
bool value_b = *(bool *)(dv.value_p); bool value_b = *(bool *)(dv.value_p);
if ((EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE)) { if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
obj["v"] = value_b; obj["v"] = value_b;
} else if ((EMSESP::system_.bool_format() == BOOL_FORMAT_10)) { } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
obj["v"] = value_b ? 1 : 0; obj["v"] = value_b ? 1 : 0;
} else { } else {
char s[7]; char s[7];
@@ -844,7 +794,8 @@ void EMSdevice::generate_values_web_all(JsonArray & output) {
uint8_t divider = 0; uint8_t divider = 0;
uint8_t factor = 1; uint8_t factor = 1;
if (dv.options_size == 1) { if (dv.options_size == 1) {
const char * s = read_flash_string(dv.options[0]).c_str(); auto s_str = read_flash_string(dv.options[0]); // prevent object backing the pointer will be destroyed at the end of the full-expression
const char * s = s_str.c_str();
if (s[0] == '*') { if (s[0] == '*') {
factor = Helpers::atoint(&s[1]); factor = Helpers::atoint(&s[1]);
} else { } else {
@@ -853,13 +804,13 @@ void EMSdevice::generate_values_web_all(JsonArray & output) {
} }
if (dv.type == DeviceValueType::INT) { if (dv.type == DeviceValueType::INT) {
obj["v"] = (divider) ? Helpers::round2(*(int8_t *)(dv.value_p), divider) : *(int8_t *)(dv.value_p) * factor; obj["v"] = divider ? Helpers::round2(*(int8_t *)(dv.value_p), divider) : *(int8_t *)(dv.value_p) * factor;
} else if (dv.type == DeviceValueType::UINT) { } else if (dv.type == DeviceValueType::UINT) {
obj["v"] = (divider) ? Helpers::round2(*(uint8_t *)(dv.value_p), divider) : *(uint8_t *)(dv.value_p) * factor; obj["v"] = divider ? Helpers::round2(*(uint8_t *)(dv.value_p), divider) : *(uint8_t *)(dv.value_p) * factor;
} else if (dv.type == DeviceValueType::SHORT) { } else if (dv.type == DeviceValueType::SHORT) {
obj["v"] = (divider) ? Helpers::round2(*(int16_t *)(dv.value_p), divider) : *(int16_t *)(dv.value_p) * factor; obj["v"] = divider ? Helpers::round2(*(int16_t *)(dv.value_p), divider) : *(int16_t *)(dv.value_p) * factor;
} else if (dv.type == DeviceValueType::USHORT) { } else if (dv.type == DeviceValueType::USHORT) {
obj["v"] = (divider) ? Helpers::round2(*(uint16_t *)(dv.value_p), divider) : *(uint16_t *)(dv.value_p) * factor; obj["v"] = divider ? Helpers::round2(*(uint16_t *)(dv.value_p), divider) : *(uint16_t *)(dv.value_p) * factor;
} else if (dv.type == DeviceValueType::ULONG) { } else if (dv.type == DeviceValueType::ULONG) {
obj["v"] = divider ? Helpers::round2(*(uint32_t *)(dv.value_p), divider) : *(uint32_t *)(dv.value_p) * factor; obj["v"] = divider ? Helpers::round2(*(uint32_t *)(dv.value_p), divider) : *(uint32_t *)(dv.value_p) * factor;
} else if (dv.type == DeviceValueType::TIME) { } else if (dv.type == DeviceValueType::TIME) {
@@ -988,10 +939,10 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
case DeviceValueType::BOOL: case DeviceValueType::BOOL:
if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
bool value_b = (bool)(*(uint8_t *)(dv.value_p)); auto value_b = (bool)(*(uint8_t *)(dv.value_p));
if ((EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE)) { if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
json[value] = value_b; json[value] = value_b;
} else if ((EMSESP::system_.bool_format() == BOOL_FORMAT_10)) { } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
json[value] = value_b ? 1 : 0; json[value] = value_b ? 1 : 0;
} else { } else {
char s[7]; char s[7];
@@ -1106,10 +1057,10 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
if (have_tag) { if (have_tag) {
snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(dv.full_name).c_str()); // prefix the tag snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(dv.full_name).c_str()); // prefix the tag
} else { } else {
strcpy(name, read_flash_string(dv.full_name).c_str()); // use full name strlcpy(name, read_flash_string(dv.full_name).c_str(), sizeof(name)); // use full name
} }
} else { } else {
strcpy(name, read_flash_string(dv.short_name).c_str()); // use short name strlcpy(name, read_flash_string(dv.short_name).c_str(), sizeof(name)); // use short name
// if we have a tag, and its different to the last one create a nested object. only for hc, wwc and hs // if we have a tag, and its different to the last one create a nested object. only for hc, wwc and hs
if (dv.tag != old_tag) { if (dv.tag != old_tag) {
@@ -1123,14 +1074,14 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
// handle Booleans // handle Booleans
if (dv.type == DeviceValueType::BOOL && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) { 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 // see how to render the value depending on the setting
bool value_b = (bool)*(uint8_t *)(dv.value_p); auto value_b = (bool)*(uint8_t *)(dv.value_p);
if (Mqtt::ha_enabled() && (output_target == OUTPUT_TARGET::MQTT)) { if (Mqtt::ha_enabled() && (output_target == OUTPUT_TARGET::MQTT)) {
char s[7]; char s[7];
json[name] = Helpers::render_boolean(s, value_b); // for HA always render as string json[name] = Helpers::render_boolean(s, value_b); // for HA always render as string
} else { } else {
if ((EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE)) { if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
json[name] = value_b; json[name] = value_b;
} else if ((EMSESP::system_.bool_format() == BOOL_FORMAT_10)) { } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
json[name] = value_b ? 1 : 0; json[name] = value_b ? 1 : 0;
} else { } else {
char s[7]; char s[7];
@@ -1147,7 +1098,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
// handle ENUMs // handle ENUMs
else if ((dv.type == DeviceValueType::ENUM) && (*(uint8_t *)(dv.value_p) < dv.options_size)) { else if ((dv.type == DeviceValueType::ENUM) && (*(uint8_t *)(dv.value_p) < dv.options_size)) {
// check for numeric enum-format // check for numeric enum-format
if ((EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX)) { if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) {
json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)); json[name] = (uint8_t)(*(uint8_t *)(dv.value_p));
} else { } else {
json[name] = dv.options[*(uint8_t *)(dv.value_p)]; json[name] = dv.options[*(uint8_t *)(dv.value_p)];
@@ -1164,7 +1115,8 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
uint8_t divider = 0; uint8_t divider = 0;
uint8_t factor = 1; uint8_t factor = 1;
if (dv.options_size == 1) { if (dv.options_size == 1) {
const char * s = read_flash_string(dv.options[0]).c_str(); auto s_str = read_flash_string(dv.options[0]); // prevent object backing the pointer will be destroyed at the end of the full-expression
const char * s = s_str.c_str();
if (s[0] == '*') { if (s[0] == '*') {
factor = Helpers::atoint(&s[1]); factor = Helpers::atoint(&s[1]);
} else { } else {
@@ -1300,7 +1252,7 @@ void EMSdevice::ha_config_clear() {
ha_config_done(false); // this will force the recreation of the main HA device config ha_config_done(false); // this will force the recreation of the main HA device config
} }
bool EMSdevice::has_telegram_id(uint16_t id) { bool EMSdevice::has_telegram_id(uint16_t id) const {
for (const auto & tf : telegram_functions_) { for (const auto & tf : telegram_functions_) {
if (tf.telegram_type_id_ == id) { if (tf.telegram_type_id_ == id) {
return true; return true;
@@ -1310,7 +1262,7 @@ bool EMSdevice::has_telegram_id(uint16_t id) {
} }
// return the name of the telegram type // return the name of the telegram type
const std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegram) { std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegram) const {
// see if it's one of the common ones, like Version // see if it's one of the common ones, like Version
if (telegram->type_id == EMS_TYPE_VERSION) { if (telegram->type_id == EMS_TYPE_VERSION) {
return read_flash_string(F("Version")); return read_flash_string(F("Version"));
@@ -1351,22 +1303,22 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
} }
// send Tx write with a data block // send Tx write with a data block
void EMSdevice::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 EMSdevice::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) const {
EMSESP::send_write_request(type_id, device_id(), offset, message_data, message_length, validate_typeid); EMSESP::send_write_request(type_id, device_id(), offset, message_data, message_length, validate_typeid);
} }
// send Tx write with a single value // send Tx write with a single value
void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) { void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) const {
EMSESP::send_write_request(type_id, device_id(), offset, value, validate_typeid); EMSESP::send_write_request(type_id, device_id(), offset, value, validate_typeid);
} }
// send Tx write with a single value, with no post validation // send Tx write with a single value, with no post validation
void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value) { void EMSdevice::write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value) const {
EMSESP::send_write_request(type_id, device_id(), offset, value, 0); EMSESP::send_write_request(type_id, device_id(), offset, value, 0);
} }
// send Tx read command to the device // send Tx read command to the device
void EMSdevice::read_command(const uint16_t type_id, const uint8_t offset, const uint8_t length) { void EMSdevice::read_command(const uint16_t type_id, const uint8_t offset, const uint8_t length) const {
EMSESP::send_read_request(type_id, device_id(), offset, length); EMSESP::send_read_request(type_id, device_id(), offset, length);
} }

View File

@@ -44,13 +44,13 @@ class EMSdevice {
strlcpy(version_, version, sizeof(version_)); strlcpy(version_, version, sizeof(version_));
} }
const std::string device_type_name() const; std::string device_type_name() const;
static const std::string device_type_2_device_name(const uint8_t device_type); static std::string device_type_2_device_name(const uint8_t device_type);
static uint8_t device_name_2_device_type(const char * topic); static uint8_t device_name_2_device_type(const char * topic);
static const std::string uom_to_string(uint8_t uom); static std::string uom_to_string(uint8_t uom);
static const std::string tag_to_string(uint8_t tag); static std::string tag_to_string(uint8_t tag);
static const std::string tag_to_mqtt(uint8_t tag); static std::string tag_to_mqtt(uint8_t tag);
bool has_tag(const uint8_t tag); bool has_tag(const uint8_t tag);
@@ -66,7 +66,7 @@ class EMSdevice {
product_id_ = product_id; product_id_ = product_id;
} }
inline bool is_device_id(uint8_t device_id) { inline bool is_device_id(uint8_t device_id) const {
return ((device_id & 0x7F) == (device_id_ & 0x7F)); return ((device_id & 0x7F) == (device_id_ & 0x7F));
} }
@@ -92,7 +92,7 @@ class EMSdevice {
strlcpy(version_, version, sizeof(version_)); strlcpy(version_, version, sizeof(version_));
} }
inline const char * version() { inline const char * version() const {
return version_; return version_;
} }
@@ -134,7 +134,7 @@ class EMSdevice {
publish_value(value); publish_value(value);
} }
inline void has_update(char * value, char * newvalue, size_t len) { inline void has_update(char * value, const char * newvalue, size_t len) {
if (strcmp(value, newvalue) != 0) { if (strcmp(value, newvalue) != 0) {
strlcpy(value, newvalue, len); strlcpy(value, newvalue, len);
has_update_ = true; has_update_ = true;
@@ -173,18 +173,18 @@ class EMSdevice {
} }
} }
const std::string brand_to_string() const; std::string brand_to_string() const;
static uint8_t decode_brand(uint8_t value); static uint8_t decode_brand(uint8_t value);
const std::string to_string() const; std::string to_string() const;
const std::string to_string_short() const; std::string to_string_short() const;
enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING }; enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING };
void show_telegram_handlers(uuid::console::Shell & shell); void show_telegram_handlers(uuid::console::Shell & shell) const;
char * show_telegram_handlers(char * result, uint8_t handlers); char * show_telegram_handlers(char * result, uint8_t handlers);
void show_mqtt_handlers(uuid::console::Shell & shell); void show_mqtt_handlers(uuid::console::Shell & shell) const;
void list_device_entries(JsonObject & output); void list_device_entries(JsonObject & output) const;
void exclude_entity(uint8_t entity_id); void exclude_entity(uint8_t entity_id);
void reset_exclude_entities(); void reset_exclude_entities();
@@ -193,7 +193,7 @@ class EMSdevice {
void register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, const 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<const Telegram> telegram); bool handle_telegram(std::shared_ptr<const Telegram> telegram);
const std::string get_value_uom(const char * key); std::string get_value_uom(const char * key) const;
bool get_value_info(JsonObject & root, const char * cmd, const int8_t id); bool get_value_info(JsonObject & root, const char * cmd, const int8_t id);
void get_dv_info(JsonObject & json); void get_dv_info(JsonObject & json);
@@ -238,25 +238,25 @@ class EMSdevice {
const __FlashStringHelper * const * name, const __FlashStringHelper * const * name,
uint8_t uom); uint8_t uom);
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, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) const;
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, const uint16_t validate_typeid) const;
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value); void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value) const;
void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0); void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0) const;
bool is_visible(void * value_p); bool is_visible(const void * value_p) const;
void publish_value(void * value_p); void publish_value(void * value_p) const;
void publish_all_values(); void publish_all_values();
void mqtt_ha_entity_config_create(); void mqtt_ha_entity_config_create();
void mqtt_ha_entity_config_remove(); void mqtt_ha_entity_config_remove();
const std::string telegram_type_name(std::shared_ptr<const Telegram> telegram); std::string telegram_type_name(std::shared_ptr<const Telegram> telegram) const;
void fetch_values(); void fetch_values();
void toggle_fetch(uint16_t telegram_id, bool toggle); void toggle_fetch(uint16_t telegram_id, bool toggle);
bool is_fetch(uint16_t telegram_id); bool is_fetch(uint16_t telegram_id) const;
bool has_telegram_id(uint16_t id); bool has_telegram_id(uint16_t id) const;
void ha_config_clear(); void ha_config_clear();
bool ha_config_done() const { bool ha_config_done() const {
@@ -347,7 +347,7 @@ class EMSdevice {
static constexpr uint8_t EMS_DEVICE_FLAG_CRF = 12; // CRF200 only monitor static constexpr uint8_t EMS_DEVICE_FLAG_CRF = 12; // CRF200 only monitor
uint8_t count_entities(); uint8_t count_entities();
bool has_entities(); bool has_entities() const;
private: private:
uint8_t unique_id_; uint8_t unique_id_;

View File

@@ -77,7 +77,6 @@ bool EMSESP::wait_km_ = true;
// or if device_id is 0 it will fetch from all our known and active devices // or if device_id is 0 it will fetch from all our known and active devices
void EMSESP::fetch_device_values(const uint8_t device_id) { void EMSESP::fetch_device_values(const uint8_t device_id) {
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if (emsdevice) {
if ((device_id == 0) || emsdevice->is_device_id(device_id)) { if ((device_id == 0) || emsdevice->is_device_id(device_id)) {
emsdevice->fetch_values(); emsdevice->fetch_values();
if (device_id != 0) { if (device_id != 0) {
@@ -86,24 +85,21 @@ void EMSESP::fetch_device_values(const uint8_t device_id) {
} }
} }
} }
}
// see if the deviceID exists // see if the deviceID exists
bool EMSESP::valid_device(const uint8_t device_id) { bool EMSESP::valid_device(const uint8_t device_id) {
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if (emsdevice) { if (emsdevice && emsdevice->is_device_id(device_id)) {
if (emsdevice->is_device_id(device_id)) {
return true; return true;
} }
} }
}
return false; // can't find it return false; // can't find it
} }
// for a specific EMS device type go and request data values // for a specific EMS device type go and request data values
void EMSESP::fetch_device_values_type(const uint8_t device_type) { void EMSESP::fetch_device_values_type(const uint8_t device_type) {
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_type)) { if (emsdevice && (emsdevice->device_type() == device_type)) {
emsdevice->fetch_values(); emsdevice->fetch_values();
} }
} }
@@ -170,9 +166,9 @@ void EMSESP::watch_id(uint16_t watch_id) {
// resets all counters and bumps the UART // resets all counters and bumps the UART
// this is called when the tx_mode is persisted in the FS either via Web UI or the console // this is called when the tx_mode is persisted in the FS either via Web UI or the console
void EMSESP::uart_init() { void EMSESP::uart_init() {
uint8_t tx_mode; uint8_t tx_mode = 0;
uint8_t rx_gpio; uint8_t rx_gpio = 0;
uint8_t tx_gpio; uint8_t tx_gpio = 0;
EMSESP::webSettingsService.read([&](WebSettings & settings) { EMSESP::webSettingsService.read([&](WebSettings & settings) {
tx_mode = settings.tx_mode; tx_mode = settings.tx_mode;
rx_gpio = settings.rx_gpio; rx_gpio = settings.rx_gpio;
@@ -217,11 +213,9 @@ uint8_t EMSESP::bus_status() {
} }
// Tx Failure rate > 10% // Tx Failure rate > 10%
if (total_fail < total_sent) { if ((total_fail < total_sent) && (((total_fail * 100) / total_sent) > EMSbus::EMS_TX_ERROR_LIMIT)) {
if (((total_fail * 100) / total_sent) > EMSbus::EMS_TX_ERROR_LIMIT) {
return BUS_STATUS_TX_ERRORS; return BUS_STATUS_TX_ERRORS;
} }
}
return BUS_STATUS_CONNECTED; return BUS_STATUS_CONNECTED;
} }
@@ -236,7 +230,6 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
case BUS_STATUS_TX_ERRORS: case BUS_STATUS_TX_ERRORS:
shell.printfln(F("EMS Bus is connected, but Tx is not stable.")); shell.printfln(F("EMS Bus is connected, but Tx is not stable."));
break; break;
case BUS_STATUS_CONNECTED:
default: default:
shell.printfln(F("EMS Bus is connected.")); shell.printfln(F("EMS Bus is connected."));
break; break;
@@ -248,7 +241,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
shell.printfln(F("EMS Bus info:")); shell.printfln(F("EMS Bus info:"));
EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(F(" Tx mode: %d"), settings.tx_mode); }); EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(F(" Tx mode: %d"), settings.tx_mode); });
shell.printfln(F(" Bus protocol: %s"), EMSbus::is_ht3() ? F("HT3") : F("Buderus")); shell.printfln(F(" Bus protocol: %s"), EMSbus::is_ht3() ? F("HT3") : F("Buderus"));
shell.printfln(F(" #recognized EMS devices: %d"), (EMSESP::emsdevices).size()); shell.printfln(F(" #recognized EMS devices: %d"), EMSESP::emsdevices.size());
shell.printfln(F(" #telegrams received: %d"), rxservice_.telegram_count()); shell.printfln(F(" #telegrams received: %d"), rxservice_.telegram_count());
shell.printfln(F(" #read requests sent: %d"), txservice_.telegram_read_count()); shell.printfln(F(" #read requests sent: %d"), txservice_.telegram_read_count());
shell.printfln(F(" #write requests sent: %d"), txservice_.telegram_write_count()); shell.printfln(F(" #write requests sent: %d"), txservice_.telegram_write_count());
@@ -308,7 +301,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
// do this in the order of factory classes to keep a consistent order when displaying // do this in the order of factory classes to keep a consistent order when displaying
for (const auto & device_class : EMSFactory::device_handlers()) { for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) { if (emsdevice && (emsdevice->device_type() == device_class.first)) {
// print header // print header
shell.printfln(F("%s: %s (%d)"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str(), emsdevice->count_entities()); shell.printfln(F("%s: %s (%d)"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str(), emsdevice->count_entities());
@@ -330,7 +323,7 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
shell.print(data.as<int>()); shell.print(data.as<int>());
} else if (data.is<float>()) { } else if (data.is<float>()) {
char s[10]; char s[10];
shell.print(Helpers::render_value(s, (float)data.as<float>(), 1)); shell.print(Helpers::render_value(s, data.as<float>(), 1));
} else if (data.is<bool>()) { } else if (data.is<bool>()) {
shell.print(data.as<bool>() ? F_(on) : F_(off)); shell.print(data.as<bool>() ? F_(on) : F_(off));
} }
@@ -400,8 +393,8 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
sensor.offset()); sensor.offset());
break; break;
default: default:
case AnalogSensor::AnalogType::DIGITAL_IN: // case AnalogSensor::AnalogType::DIGITAL_IN:
case AnalogSensor::AnalogType::COUNTER: // case AnalogSensor::AnalogType::COUNTER:
shell.printfln(F(" %s: %s%d%s (Type: %s)"), shell.printfln(F(" %s: %s%d%s (Type: %s)"),
sensor.name().c_str(), sensor.name().c_str(),
COLOR_BRIGHT_GREEN, COLOR_BRIGHT_GREEN,
@@ -582,7 +575,7 @@ void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
doc["dest"] = Helpers::hextoa(buffer, telegram->dest); doc["dest"] = Helpers::hextoa(buffer, telegram->dest);
doc["type"] = Helpers::hextoa(buffer, telegram->type_id); doc["type"] = Helpers::hextoa(buffer, telegram->type_id);
doc["offset"] = Helpers::hextoa(buffer, telegram->offset); doc["offset"] = Helpers::hextoa(buffer, telegram->offset);
strcpy(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str()); // telegram is without crc strlcpy(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str(), sizeof(buffer)); // telegram is without crc
doc["data"] = buffer; doc["data"] = buffer;
if (telegram->message_length <= 4) { if (telegram->message_length <= 4) {
@@ -830,7 +823,6 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
bool found = false; bool found = false;
bool knowndevice = false; bool knowndevice = false;
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if (emsdevice) {
if (emsdevice->is_device_id(telegram->src) || emsdevice->is_device_id(telegram->dest)) { if (emsdevice->is_device_id(telegram->src) || emsdevice->is_device_id(telegram->dest)) {
knowndevice = true; knowndevice = true;
found = emsdevice->handle_telegram(telegram); found = emsdevice->handle_telegram(telegram);
@@ -856,7 +848,6 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
break; break;
} }
} }
}
if (!found) { if (!found) {
LOG_DEBUG(F("No telegram type handler found for ID 0x%02X (src 0x%02X)"), telegram->type_id, telegram->src); LOG_DEBUG(F("No telegram type handler found for ID 0x%02X (src 0x%02X)"), telegram->type_id, telegram->src);
@@ -874,12 +865,10 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
// return true if we have this device already registered // return true if we have this device already registered
bool EMSESP::device_exists(const uint8_t device_id) { bool EMSESP::device_exists(const uint8_t device_id) {
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if (emsdevice) { if (emsdevice && emsdevice->is_device_id(device_id)) {
if (emsdevice->is_device_id(device_id)) {
return true; return true;
} }
} }
}
return false; // not found return false; // not found
} }
@@ -898,7 +887,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
// count the number of thermostats // count the number of thermostats
uint8_t num_thermostats = 0; uint8_t num_thermostats = 0;
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == DeviceType::THERMOSTAT)) { if (emsdevice && (emsdevice->device_type() == DeviceType::THERMOSTAT)) {
num_thermostats++; num_thermostats++;
} }
} }
@@ -907,7 +896,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
// so we keep a consistent order // so we keep a consistent order
for (const auto & device_class : EMSFactory::device_handlers()) { for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) { if (emsdevice && (emsdevice->device_type() == device_class.first)) {
shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str()); shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
shell.println(); shell.println();
emsdevice->show_telegram_handlers(shell); emsdevice->show_telegram_handlers(shell);
@@ -931,8 +920,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
// first check to see if we already have it, if so update the record // first check to see if we already have it, if so update the record
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if (emsdevice) { if (emsdevice && emsdevice->is_device_id(device_id)) {
if (emsdevice->is_device_id(device_id)) {
LOG_DEBUG(F("Updating details for already active deviceID 0x%02X"), device_id); LOG_DEBUG(F("Updating details for already active deviceID 0x%02X"), device_id);
emsdevice->product_id(product_id); emsdevice->product_id(product_id);
emsdevice->version(version); emsdevice->version(version);
@@ -951,7 +939,6 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
return true; // finish up return true; // finish up
} }
} }
}
// look up the rest of the details using the product_id and create the new device object // look up the rest of the details using the product_id and create the new device object
Device_record * device_p = nullptr; Device_record * device_p = nullptr;
@@ -1085,7 +1072,7 @@ bool EMSESP::command_entities(uint8_t device_type, JsonObject & output, const in
JsonObject node; JsonObject node;
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_type)) { if (emsdevice && (emsdevice->device_type() == device_type)) {
emsdevice->list_device_entries(output); emsdevice->list_device_entries(output);
return true; return true;
} }
@@ -1213,14 +1200,13 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
txservice_.send_poll(); // close the bus txservice_.send_poll(); // close the bus
txservice_.reset_retry_count(); txservice_.reset_retry_count();
tx_successful = true; tx_successful = true;
// if telegram is longer read next part with offset +25 for ems+ or +27 for ems1.0 // if telegram is longer read next part with offset +25 for ems+ or +27 for ems1.0
if (length == 32) { if ((length == 32) && (txservice_.read_next_tx(data[3]) == read_id_)) {
if (txservice_.read_next_tx(data[3]) == read_id_) {
read_next_ = true; read_next_ = true;
} }
} }
} }
}
// if Tx wasn't successful, retry or just give up // if Tx wasn't successful, retry or just give up
if (!tx_successful) { if (!tx_successful) {
@@ -1341,7 +1327,7 @@ void EMSESP::scheduled_fetch_values() {
if (txservice_.tx_queue_empty()) { if (txservice_.tx_queue_empty()) {
uint8_t i = 0; uint8_t i = 0;
for (const auto & emsdevice : emsdevices) { for (const auto & emsdevice : emsdevices) {
if (emsdevice && ++i >= no) { if (++i >= no) {
emsdevice->fetch_values(); emsdevice->fetch_values();
no++; no++;
return; return;

View File

@@ -64,8 +64,8 @@ std::string Helpers::hextoa(const uint16_t value, bool prefix) {
#ifdef EMSESP_STANDALONE #ifdef EMSESP_STANDALONE
// special function to work outside of ESP's libraries // special function to work outside of ESP's libraries
char * Helpers::ultostr(char * ptr, uint32_t value, const uint8_t base) { char * Helpers::ultostr(char * ptr, uint32_t value, const uint8_t base) {
if (NULL == ptr) { if (nullptr == ptr) {
return NULL; return nullptr;
} }
unsigned long t = 0; unsigned long t = 0;
@@ -191,7 +191,7 @@ char * Helpers::render_boolean(char * result, bool value) {
// render for native char strings // render for native char strings
char * Helpers::render_value(char * result, const char * value, const int8_t format __attribute__((unused))) { char * Helpers::render_value(char * result, const char * value, const int8_t format __attribute__((unused))) {
strcpy(result, value); strcpy(result, value); // un-safe but we don't care
return result; return result;
} }
@@ -257,7 +257,7 @@ char * Helpers::render_value(char * result, const float value, const int8_t form
uint32_t p[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; uint32_t p[] = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
char * ret = result; char * ret = result;
int32_t whole = (int32_t)value; auto whole = (int32_t)value;
itoa(whole, result, 10); itoa(whole, result, 10);
@@ -356,12 +356,11 @@ char * Helpers::render_value(char * result, const uint32_t value, const int8_t f
} else { } else {
strlcpy(result, ltoa(new_value * format * -1, s, 10), sizeof(s)); strlcpy(result, ltoa(new_value * format * -1, s, 10), sizeof(s));
} }
#else #else
if (!format) { if (!format) {
strlcpy(result, ultostr(s, new_value, 10), sizeof(s)); // format is 0 strlcpy(result, ultostr(s, new_value, 10), sizeof(s)); // format is 0
} else { } else {
strncpy(result, ultostr(s, new_value / format, 10), sizeof(s)); strlcpy(result, ultostr(s, new_value / format, 10), sizeof(s));
strlcat(result, ".", sizeof(s)); strlcat(result, ".", sizeof(s));
strncat(result, ultostr(s, new_value % format, 10), sizeof(s)); strncat(result, ultostr(s, new_value % format, 10), sizeof(s));
} }
@@ -481,7 +480,7 @@ bool Helpers::hasValue(const int8_t & v) {
return (v != EMS_VALUE_INT_NOTSET); return (v != EMS_VALUE_INT_NOTSET);
} }
bool Helpers::hasValue(char * v) { bool Helpers::hasValue(const char * v) {
if ((v == nullptr) || (strlen(v) == 0)) { if ((v == nullptr) || (strlen(v) == 0)) {
return false; return false;
} }
@@ -508,7 +507,8 @@ bool Helpers::value2number(const char * v, int & value, const int min, const int
value = 0; value = 0;
return false; return false;
} }
value = atoi((char *)v);
value = atoi(v);
if (value >= min && value <= max) { if (value >= min && value <= max) {
return true; return true;
} }
@@ -521,12 +521,14 @@ bool Helpers::value2float(const char * v, float & value) {
if ((v == nullptr) || (strlen(v) == 0)) { if ((v == nullptr) || (strlen(v) == 0)) {
return false; return false;
} }
if (v[0] == '-' || v[0] == '.' || (v[0] >= '0' && v[0] <= '9')) { if (v[0] == '-' || v[0] == '.' || (v[0] >= '0' && v[0] <= '9')) {
value = atof((char *)v); value = atof(v);
return true; return true;
} }
if (v[0] == '+' && (v[1] == '.' || (v[1] >= '0' && v[1] <= '9'))) { if (v[0] == '+' && (v[1] == '.' || (v[1] >= '0' && v[1] <= '9'))) {
value = atof((char *)(v + 1)); value = atof(v + 1);
return true; return true;
} }
return false; return false;
@@ -584,12 +586,12 @@ bool Helpers::value2bool(const char * v, bool & value) {
std::string bool_str = toLower(v); // convert to lower case std::string bool_str = toLower(v); // convert to lower case
if ((bool_str == read_flash_string(F_(on))) || (bool_str == "1") or (bool_str == "true")) { if ((bool_str == read_flash_string(F_(on))) || (bool_str == "1") || (bool_str == "true")) {
value = true; value = true;
return true; // is a bool return true; // is a bool
} }
if ((bool_str == read_flash_string(F_(off))) || (bool_str == "0") or (bool_str == "false")) { if ((bool_str == read_flash_string(F_(off))) || (bool_str == "0") || (bool_str == "false")) {
value = false; value = false;
return true; // is a bool return true; // is a bool
} }

View File

@@ -63,7 +63,7 @@ class Helpers {
static bool hasValue(const int16_t & v); static bool hasValue(const int16_t & v);
static bool hasValue(const uint16_t & v); static bool hasValue(const uint16_t & v);
static bool hasValue(const uint32_t & v); static bool hasValue(const uint32_t & v);
static bool hasValue(char * v); static bool hasValue(const char * v);
static bool value2number(const char * v, int & value, const int min = -2147483648, const int max = 2147483647); static bool value2number(const char * v, int & value, const int min = -2147483648, const int max = 2147483647);
static bool value2float(const char * v, float & value); static bool value2float(const char * v, float & value);

View File

@@ -379,7 +379,18 @@ MAKE_PSTR_LIST(enum_j_control, F_(off), F("fb10"), F("fb110"))
MAKE_PSTR_LIST(enum_wwProgMode, F("std Prog"), F_(own_prog)) MAKE_PSTR_LIST(enum_wwProgMode, F("std Prog"), F_(own_prog))
MAKE_PSTR_LIST(enum_dayOfWeek, F("Mo"), F("Di"), F("Mi"), F("Do"), F("Fr"), F("Sa"), F("So"), F("Alle")) MAKE_PSTR_LIST(enum_dayOfWeek, F("Mo"), F("Di"), F("Mi"), F("Do"), F("Fr"), F("Sa"), F("So"), F("Alle"))
MAKE_PSTR_LIST(enum_progMode, F("Prog_1"), F("Prog_2")) MAKE_PSTR_LIST(enum_progMode, F("Prog_1"), F("Prog_2"))
MAKE_PSTR_LIST(enum_progMode2, F("Eigen_1"), F("Familie"), F("Morgends"), F("Abends"), F("Vormittag"), F("Nachmittag"), F("Mittag"), F("Singles"), F("Senioren"), F("Neu"), F("Eigen_2")) MAKE_PSTR_LIST(enum_progMode2,
F("Eigen_1"),
F("Familie"),
F("Morgends"),
F("Abends"),
F("Vormittag"),
F("Nachmittag"),
F("Mittag"),
F("Singles"),
F("Senioren"),
F("Neu"),
F("Eigen_2"))
MAKE_PSTR_LIST(enum_progMode3, F("Familie"), F("Morgends"), F("Abends"), F("Vormittag"), F("Nachmittag"), F("Mittag"), F("Singles"), F("Senioren")) MAKE_PSTR_LIST(enum_progMode3, F("Familie"), F("Morgends"), F("Abends"), F("Vormittag"), F("Nachmittag"), F("Mittag"), F("Singles"), F("Senioren"))
MAKE_PSTR_LIST(enum_progMode4, F("prog_a"), F("prog_b"), F("prog_c"), F("prog_d"), F("prog_e"), F("prog_f")) MAKE_PSTR_LIST(enum_progMode4, F("prog_a"), F("prog_b"), F("prog_c"), F("prog_d"), F("prog_e"), F("prog_f"))

View File

@@ -244,14 +244,16 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
// simulate receiving a MQTT message, used only for testing // simulate receiving a MQTT message, used only for testing
void Mqtt::incoming(const char * topic, const char * payload) { void Mqtt::incoming(const char * topic, const char * payload) {
if (payload != nullptr) {
on_message(topic, payload, strlen(payload)); on_message(topic, payload, strlen(payload));
} }
}
#endif #endif
// received an MQTT message that we subscribed too // received an MQTT message that we subscribed too
// topic is the full path // topic is the full path
// payload is json or a single string and converted to a json with key 'value' // payload is json or a single string and converted to a json with key 'value'
void Mqtt::on_message(const char * topic, const char * payload, size_t len) { void Mqtt::on_message(const char * topic, const char * payload, size_t len) const {
// sometimes the payload is not terminated correctly, so make a copy // sometimes the payload is not terminated correctly, so make a copy
// convert payload to a null-terminated char string // convert payload to a null-terminated char string
char message[len + 2]; char message[len + 2];
@@ -280,8 +282,7 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
// add the base back // add the base back
char full_topic[MQTT_TOPIC_MAX_SIZE]; char full_topic[MQTT_TOPIC_MAX_SIZE];
snprintf(full_topic, sizeof(full_topic), "%s/%s", mqtt_base_.c_str(), mf.topic_.c_str()); snprintf(full_topic, sizeof(full_topic), "%s/%s", mqtt_base_.c_str(), mf.topic_.c_str());
if (!strcmp(topic, full_topic)) { if ((!strcmp(topic, full_topic)) && (mf.mqtt_subfunction_)) {
if (mf.mqtt_subfunction_) {
if (!(mf.mqtt_subfunction_)(message)) { if (!(mf.mqtt_subfunction_)(message)) {
LOG_ERROR(F("error: invalid payload %s for this topic %s"), message, topic); LOG_ERROR(F("error: invalid payload %s for this topic %s"), message, topic);
if (send_response_) { if (send_response_) {
@@ -291,7 +292,6 @@ void Mqtt::on_message(const char * topic, const char * payload, size_t len) {
return; return;
} }
} }
}
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc; StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> input_doc;
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE_DYN> output_doc; StaticJsonDocument<EMSESP_JSON_SIZE_LARGE_DYN> output_doc;
@@ -353,7 +353,7 @@ void Mqtt::show_topic_handlers(uuid::console::Shell & shell, const uint8_t devic
// its a poor man's QOS we assume the ACK represents the last Publish sent // its a poor man's QOS we assume the ACK represents the last Publish sent
// check if ACK matches the last Publish we sent, if not report an error. Only if qos is 1 or 2 // check if ACK matches the last Publish we sent, if not report an error. Only if qos is 1 or 2
// and always remove from queue // and always remove from queue
void Mqtt::on_publish(uint16_t packetId) { void Mqtt::on_publish(uint16_t packetId) const {
// find the MQTT message in the queue and remove it // find the MQTT message in the queue and remove it
if (mqtt_messages_.empty()) { if (mqtt_messages_.empty()) {
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
@@ -462,7 +462,7 @@ void Mqtt::start() {
snprintf(will_topic, MQTT_TOPIC_MAX_SIZE, "%s/status", mqtt_base_.c_str()); snprintf(will_topic, MQTT_TOPIC_MAX_SIZE, "%s/status", mqtt_base_.c_str());
mqttClient_->setWill(will_topic, 1, true, "offline"); // with qos 1, retain true mqttClient_->setWill(will_topic, 1, true, "offline"); // with qos 1, retain true
mqttClient_->onMessage([this](char * topic, char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { mqttClient_->onMessage([this](const char * topic, const char * payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
on_message(topic, payload, len); // receiving mqtt on_message(topic, payload, len); // receiving mqtt
}); });
@@ -804,7 +804,7 @@ void Mqtt::process_queue() {
char topic[MQTT_TOPIC_MAX_SIZE]; char topic[MQTT_TOPIC_MAX_SIZE];
if (message->topic.find(discovery_prefix_) == 0) { if (message->topic.find(discovery_prefix_) == 0) {
strcpy(topic, message->topic.c_str()); // leave topic as it is strlcpy(topic, message->topic.c_str(), sizeof(topic)); // leave topic as it is
} else { } else {
snprintf(topic, MQTT_TOPIC_MAX_SIZE, "%s/%s", mqtt_base_.c_str(), message->topic.c_str()); snprintf(topic, MQTT_TOPIC_MAX_SIZE, "%s/%s", mqtt_base_.c_str(), message->topic.c_str());
} }
@@ -1313,7 +1313,7 @@ void Mqtt::publish_ha_climate_config(uint8_t tag, bool has_roomtemp, bool remove
// based on the device and tag, create the MQTT topic name (without the basename) // based on the device and tag, create the MQTT topic name (without the basename)
// differs based on whether MQTT nested is enabled // differs based on whether MQTT nested is enabled
// tag = EMSdevice::DeviceValueTAG // tag = EMSdevice::DeviceValueTAG
const std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) { std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
// the system device is treated differently. The topic is 'heartbeat' and doesn't follow the usual convention // the system device is treated differently. The topic is 'heartbeat' and doesn't follow the usual convention
if (device_type == EMSdevice::DeviceType::SYSTEM) { if (device_type == EMSdevice::DeviceType::SYSTEM) {
return EMSdevice::tag_to_mqtt(tag); return EMSdevice::tag_to_mqtt(tag);

View File

@@ -30,7 +30,7 @@
using uuid::console::Shell; using uuid::console::Shell;
// size of queue // size of queue
#define MAX_MQTT_MESSAGES 300 static constexpr uint16_t MAX_MQTT_MESSAGES = 300;
namespace emsesp { namespace emsesp {
@@ -208,11 +208,11 @@ class Mqtt {
send_response_ = send_response; send_response_ = send_response;
} }
void set_qos(uint8_t mqtt_qos) { void set_qos(uint8_t mqtt_qos) const {
mqtt_qos_ = mqtt_qos; mqtt_qos_ = mqtt_qos;
} }
void set_retain(bool mqtt_retain) { void set_retain(bool mqtt_retain) const {
mqtt_retain_ = mqtt_retain; mqtt_retain_ = mqtt_retain;
} }
@@ -220,20 +220,18 @@ class Mqtt {
return mqtt_messages_.empty(); return mqtt_messages_.empty();
} }
static const std::string tag_to_topic(uint8_t device_type, uint8_t tag); static std::string tag_to_topic(uint8_t device_type, uint8_t tag);
struct QueuedMqttMessage { struct QueuedMqttMessage {
const uint32_t id_; const uint32_t id_;
const std::shared_ptr<const MqttMessage> content_; const std::shared_ptr<const MqttMessage> content_;
uint8_t retry_count_; uint8_t retry_count_ = 0;
uint16_t packet_id_; uint16_t packet_id_ = 0;
~QueuedMqttMessage() = default; ~QueuedMqttMessage() = default;
QueuedMqttMessage(uint32_t id, std::shared_ptr<MqttMessage> && content) QueuedMqttMessage(uint32_t id, std::shared_ptr<MqttMessage> && content)
: id_(id) : id_(id)
, content_(std::move(content)) { , content_(std::move(content)) {
retry_count_ = 0;
packet_id_ = 0;
} }
}; };
static std::deque<QueuedMqttMessage> mqtt_messages_; static std::deque<QueuedMqttMessage> mqtt_messages_;
@@ -253,8 +251,8 @@ class Mqtt {
static std::shared_ptr<const MqttMessage> queue_subscribe_message(const std::string & topic); static std::shared_ptr<const MqttMessage> queue_subscribe_message(const std::string & topic);
static std::shared_ptr<const MqttMessage> queue_unsubscribe_message(const std::string & topic); static std::shared_ptr<const MqttMessage> queue_unsubscribe_message(const std::string & topic);
void on_publish(uint16_t packetId); void on_publish(uint16_t packetId) const;
void on_message(const char * topic, const char * payload, size_t len); void on_message(const char * topic, const char * payload, size_t len) const;
void process_queue(); void process_queue();
// function handlers for MQTT subscriptions // function handlers for MQTT subscriptions

View File

@@ -65,12 +65,12 @@ void Shower::loop() {
} }
} else { // hot water is off } else { // hot water is off
// if it just turned off, record the time as it could be a short pause // if it just turned off, record the time as it could be a short pause
if ((timer_start_) && (timer_pause_ == 0)) { if (timer_start_ && (timer_pause_ == 0)) {
timer_pause_ = time_now; timer_pause_ = time_now;
} }
// if shower has been off for longer than the wait time // if shower has been off for longer than the wait time
if ((timer_pause_) && ((time_now - timer_pause_) > SHOWER_PAUSE_TIME)) { if (timer_pause_ && ((time_now - timer_pause_) > SHOWER_PAUSE_TIME)) {
// it is over the wait period, so assume that the shower has finished and calculate the total time and publish // it is over the wait period, so assume that the shower has finished and calculate the total time and publish
// because its unsigned long, can't have negative so check if length is less than OFFSET_TIME // because its unsigned long, can't have negative so check if length is less than OFFSET_TIME
if ((timer_pause_ - timer_start_) > SHOWER_OFFSET_TIME) { if ((timer_pause_ - timer_start_) > SHOWER_OFFSET_TIME) {
@@ -121,7 +121,7 @@ void Shower::shower_alert_start() {
// Publish to the shower_data topic // Publish to the shower_data topic
// showing whether the shower timer and alert are enabled or disabled // showing whether the shower timer and alert are enabled or disabled
// and the duration of the last shower // and the duration of the last shower
void Shower::publish_shower_data() { void Shower::publish_shower_data() const {
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc; StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {

View File

@@ -55,7 +55,7 @@ class Shower {
static constexpr uint32_t SHOWER_COLDSHOT_DURATION = 10000; // 10 seconds for cold water before turning back hot water 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_MAX_DURATION = 420000; // in ms. 7 minutes, before trigger a shot of cold water
void publish_shower_data(); void publish_shower_data() const;
void shower_alert_start(); void shower_alert_start();
void shower_alert_stop(); void shower_alert_stop();

View File

@@ -484,7 +484,7 @@ bool System::upload_status() {
void System::upload_status(bool in_progress) { void System::upload_status(bool in_progress) {
// if we've just started an upload // if we've just started an upload
if ((!upload_status_) && (in_progress)) { if (!upload_status_ && in_progress) {
EMSuart::stop(); EMSuart::stop();
} }
upload_status_ = in_progress; upload_status_ = in_progress;
@@ -615,7 +615,7 @@ void System::network_init(bool refresh) {
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0 // ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16 // ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock // ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
eth_clock_mode_t clock_mode = (eth_clock_mode_t)eth_clock_mode_; auto clock_mode = (eth_clock_mode_t)eth_clock_mode_;
ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode); ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
} }
@@ -1133,9 +1133,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
node["bus status"] = (F("connected, tx issues - try a different tx-mode")); node["bus status"] = (F("connected, tx issues - try a different tx-mode"));
break; break;
case EMSESP::BUS_STATUS_CONNECTED: case EMSESP::BUS_STATUS_CONNECTED:
default:
node["bus status"] = (F("connected")); node["bus status"] = (F("connected"));
break; break;
default:
node["bus status"] = (F("unknown"));
break;
} }
if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) { if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) {
@@ -1180,7 +1182,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
JsonArray devices = output.createNestedArray("Devices"); JsonArray devices = output.createNestedArray("Devices");
for (const auto & device_class : EMSFactory::device_handlers()) { for (const auto & device_class : EMSFactory::device_handlers()) {
for (const auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) { if (emsdevice && (emsdevice->device_type() == device_class.first)) {
JsonObject obj = devices.createNestedObject(); JsonObject obj = devices.createNestedObject();
obj["type"] = emsdevice->device_type_name(); obj["type"] = emsdevice->device_type_name();
// obj["name"] = emsdevice->to_string(); // obj["name"] = emsdevice->to_string();
@@ -1278,7 +1280,7 @@ bool System::command_restart(const char * value, const int8_t id) {
return true; return true;
} }
const std::string System::reset_reason(uint8_t cpu) { std::string System::reset_reason(uint8_t cpu) const {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
switch (rtc_get_reset_reason(cpu)) { switch (rtc_get_reset_reason(cpu)) {
case 1: case 1:

View File

@@ -65,7 +65,7 @@ class System {
static bool command_customizations(const char * value, const int8_t id, JsonObject & output); static bool command_customizations(const char * value, const int8_t id, JsonObject & output);
static bool command_commands(const char * value, const int8_t id, JsonObject & output); static bool command_commands(const char * value, const int8_t id, JsonObject & output);
const std::string reset_reason(uint8_t cpu); std::string reset_reason(uint8_t cpu) const;
void system_restart(); void system_restart();
void format(uuid::console::Shell & shell); void format(uuid::console::Shell & shell);

View File

@@ -261,7 +261,7 @@ void TxService::start() {
} }
// sends a 1 byte poll which is our own deviceID // sends a 1 byte poll which is our own deviceID
void TxService::send_poll() { void TxService::send_poll() const {
//LOG_DEBUG(F("Ack %02X"),ems_bus_id() ^ ems_mask()); //LOG_DEBUG(F("Ack %02X"),ems_bus_id() ^ ems_mask());
if (tx_mode()) { if (tx_mode()) {
EMSuart::send_poll(ems_bus_id() ^ ems_mask()); EMSuart::send_poll(ems_bus_id() ^ ems_mask());
@@ -568,7 +568,7 @@ void TxService::send_raw(const char * telegram_data) {
while (p != 0) { while (p != 0) {
if ((p = strtok(nullptr, " ,"))) { if ((p = strtok(nullptr, " ,"))) {
strlcpy(value, p, sizeof(value)); strlcpy(value, p, sizeof(value));
uint8_t val = (uint8_t)strtol(value, 0, 16); auto val = (uint8_t)strtol(value, 0, 16);
data[++count] = val; data[++count] = val;
} }
} }

View File

@@ -87,11 +87,12 @@ class Telegram {
// reads a bit value from a given telegram position // reads a bit value from a given telegram position
bool read_bitvalue(uint8_t & value, const uint8_t index, const uint8_t bit) const { bool read_bitvalue(uint8_t & value, const uint8_t index, const uint8_t bit) const {
uint8_t abs_index = (index - this->offset); uint8_t abs_index = (index - this->offset);
if (abs_index >= this->message_length) { if ((abs_index >= this->message_length) || (abs_index > EMS_MAX_TELEGRAM_MESSAGE_LENGTH)) {
return false; // out of bounds return false; // out of bounds
} }
uint8_t val = value; uint8_t val = value;
value = (uint8_t)(((this->message_data[abs_index]) >> (bit)) & 0x01); value = (uint8_t)(((this->message_data[abs_index]) >> bit) & 0x01);
return (val != value); return (val != value);
} }
@@ -105,14 +106,17 @@ class Telegram {
bool read_value(Value & value, const uint8_t index, uint8_t s = 0) const { bool read_value(Value & value, const uint8_t index, uint8_t s = 0) const {
uint8_t num_bytes = (!s) ? sizeof(Value) : s; uint8_t num_bytes = (!s) ? sizeof(Value) : s;
// check for out of bounds, if so don't modify the value // check for out of bounds, if so don't modify the value
if ((index < this->offset) || ((index - this->offset + num_bytes - 1) >= this->message_length)) { auto msg_size = (index - this->offset + num_bytes - 1);
if ((index < this->offset) || (msg_size >= this->message_length) || (msg_size > EMS_MAX_TELEGRAM_MESSAGE_LENGTH)) {
return false; return false;
} }
auto val = value;
Value val = value;
value = 0; value = 0;
for (uint8_t i = 0; i < num_bytes; i++) { for (uint8_t i = 0; i < num_bytes; i++) {
value = (value << 8) + this->message_data[index - this->offset + i]; // shift by byte value = (value << 8) + this->message_data[index - this->offset + i]; // shift by byte
} }
return (val != value); return (val != value);
} }
@@ -120,6 +124,7 @@ class Telegram {
if ((index < this->offset) || ((index - this->offset) >= this->message_length)) { if ((index < this->offset) || ((index - this->offset) >= this->message_length)) {
return false; return false;
} }
uint8_t val = value; uint8_t val = value;
value = this->message_data[index - this->offset] - start; value = this->message_data[index - this->offset] - start;
return (val != value); return (val != value);
@@ -265,7 +270,7 @@ class RxService : public EMSbus {
} }
}; };
const std::deque<QueuedRxTelegram> queue() const { std::deque<QueuedRxTelegram> queue() const {
return rx_telegrams_; return rx_telegrams_;
} }
@@ -300,7 +305,7 @@ class TxService : public EMSbus {
void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front = false); void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front = false);
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0); void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
void send_raw(const char * telegram_data); void send_raw(const char * telegram_data);
void send_poll(); void send_poll() const;
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length); void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);
bool is_last_tx(const uint8_t src, const uint8_t dest) const; bool is_last_tx(const uint8_t src, const uint8_t dest) const;
uint16_t post_send_query(); uint16_t post_send_query();
@@ -318,7 +323,7 @@ class TxService : public EMSbus {
telegram_last_post_send_query_ = type_id; telegram_last_post_send_query_ = type_id;
} }
uint16_t get_post_send_query() { uint16_t get_post_send_query() const {
return telegram_last_post_send_query_; return telegram_last_post_send_query_;
} }
@@ -362,14 +367,14 @@ class TxService : public EMSbus {
if (telegram_read_fail_count_ == 0) { if (telegram_read_fail_count_ == 0) {
return 100; // all good, 100% return 100; // all good, 100%
} }
return (100 - (uint8_t)((telegram_read_fail_count_ * 100 / (telegram_read_fail_count_ + telegram_read_count_)))); return (100 - (uint8_t)(telegram_read_fail_count_ * 100 / (telegram_read_fail_count_ + telegram_read_count_)));
} }
uint8_t write_quality() const { uint8_t write_quality() const {
if (telegram_write_fail_count_ == 0) { if (telegram_write_fail_count_ == 0) {
return 100; // all good, 100% return 100; // all good, 100%
} }
return (100 - (uint8_t)((telegram_write_fail_count_ * 100 / (telegram_write_fail_count_ + telegram_write_count_)))); return (100 - (uint8_t)(telegram_write_fail_count_ * 100 / (telegram_write_fail_count_ + telegram_write_count_)));
} }
void increment_telegram_read_fail_count() { void increment_telegram_read_fail_count() {
@@ -395,12 +400,12 @@ class TxService : public EMSbus {
} }
}; };
const std::deque<QueuedTxTelegram> queue() const { std::deque<QueuedTxTelegram> queue() const {
return tx_telegrams_; return tx_telegrams_;
} }
bool tx_queue_empty() { bool tx_queue_empty() const {
return tx_telegrams_.size() == 0; return tx_telegrams_.empty();
} }
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)

View File

@@ -804,28 +804,28 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// test command parse // test command parse
int8_t id_n; int8_t id_n;
const char * cmd; const char * ncmd;
char command_s[100]; char command_s[100];
id_n = -1; id_n = -1;
strcpy(command_s, "hc2/seltemp"); strlcpy(command_s, "hc2/seltemp", sizeof(command_s));
cmd = Command::parse_command_string(command_s, id_n); ncmd = Command::parse_command_string(command_s, id_n);
shell.printfln("test cmd parse cmd=%s id=%d", cmd, id_n); shell.printfln("test cmd parse cmd=%s id=%d", ncmd, id_n);
id_n = -1; id_n = -1;
strcpy(command_s, "seltemp"); strlcpy(command_s, "seltemp", sizeof(command_s));
cmd = Command::parse_command_string(command_s, id_n); ncmd = Command::parse_command_string(command_s, id_n);
shell.printfln("test cmd parse cmd=%s id=%d", cmd, id_n); shell.printfln("test cmd parse cmd=%s id=%d", ncmd, id_n);
id_n = -1; id_n = -1;
strcpy(command_s, "xyz/seltemp"); strlcpy(command_s, "xyz/seltemp", sizeof(command_s));
cmd = Command::parse_command_string(command_s, id_n); ncmd = Command::parse_command_string(command_s, id_n);
shell.printfln("test cmd parse cmd=%s id=%d", cmd, id_n); shell.printfln("test cmd parse cmd=%s id=%d", ncmd, id_n);
id_n = -1; id_n = -1;
strcpy(command_s, "wwc4/seltemp"); strlcpy(command_s, "wwc4/seltemp", sizeof(command_s));
cmd = Command::parse_command_string(command_s, id_n); ncmd = Command::parse_command_string(command_s, id_n);
shell.printfln("test cmd parse cmd=%s id=%d", cmd, id_n); shell.printfln("test cmd parse cmd=%s id=%d", ncmd, id_n);
id_n = -1; id_n = -1;
strcpy(command_s, "hc3_seltemp"); strlcpy(command_s, "hc3_seltemp", sizeof(command_s));
cmd = Command::parse_command_string(command_s, id_n); ncmd = Command::parse_command_string(command_s, id_n);
shell.printfln("test cmd parse cmd=%s id=%d", cmd, id_n); shell.printfln("test cmd parse cmd=%s id=%d", ncmd, id_n);
#endif #endif
@@ -1335,12 +1335,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
Mqtt::show_mqtt(shell); // show queue Mqtt::show_mqtt(shell); // show queue
strcpy(boiler_topic, "ems-esp/boiler"); strlcpy(boiler_topic, "ems-esp/boiler", sizeof(boiler_topic));
strcpy(thermostat_topic, "ems-esp/thermostat"); strlcpy(thermostat_topic, "ems-esp/thermostat", sizeof(thermostat_topic));
strcpy(system_topic, "ems-esp/system"); strlcpy(system_topic, "ems-esp/system", sizeof(system_topic));
// test publishing // test publishing
EMSESP::EMSESP::mqtt_.publish(boiler_topic, "test me"); EMSESP::mqtt_.publish(boiler_topic, "test me");
// test receiving // test receiving
EMSESP::mqtt_.incoming(boiler_topic, ""); // test if ignore empty payloads, should return values EMSESP::mqtt_.incoming(boiler_topic, ""); // test if ignore empty payloads, should return values

View File

@@ -49,7 +49,7 @@ void WebAPIService::webAPIService_get(AsyncWebServerRequest * request) {
// POST /{device}[/{hc|id}][/{name}] // POST /{device}[/{hc|id}][/{name}]
void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) { void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json) {
// if no body then treat it as a secure GET // if no body then treat it as a secure GET
if (not json.is<JsonObject>()) { if (!json.is<JsonObject>()) {
webAPIService_get(request); webAPIService_get(request);
return; return;
} }
@@ -63,10 +63,10 @@ void WebAPIService::webAPIService_post(AsyncWebServerRequest * request, JsonVari
// reporting back any errors // reporting back any errors
void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) { void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
// check if the user has admin privileges (token is included and authorized) // check if the user has admin privileges (token is included and authorized)
bool is_admin; bool is_admin = false;
EMSESP::webSettingsService.read([&](WebSettings & settings) { EMSESP::webSettingsService.read([&](WebSettings & settings) {
Authentication authentication = _securityManager->authenticateRequest(request); Authentication authentication = _securityManager->authenticateRequest(request);
is_admin = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication); is_admin = settings.notoken_api || AuthenticationPredicates::IS_ADMIN(authentication);
}); });
// check for query parameters first, the old style from v2 // check for query parameters first, the old style from v2
@@ -97,7 +97,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
} }
// output json buffer // output json buffer
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); auto * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
JsonObject output = response->getRoot(); JsonObject output = response->getRoot();
// call command // call command

View File

@@ -99,7 +99,7 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
if (root["sensors"].is<JsonArray>()) { if (root["sensors"].is<JsonArray>()) {
for (const JsonObject sensorJson : root["sensors"].as<JsonArray>()) { for (const JsonObject sensorJson : root["sensors"].as<JsonArray>()) {
// create each of the sensor, overwritting any previous settings // create each of the sensor, overwritting any previous settings
SensorCustomization sensor = SensorCustomization(); auto sensor = SensorCustomization();
sensor.id_str = sensorJson["id_str"].as<std::string>(); sensor.id_str = sensorJson["id_str"].as<std::string>();
sensor.name = sensorJson["name"].as<std::string>(); sensor.name = sensorJson["name"].as<std::string>();
sensor.offset = sensorJson["offset"]; sensor.offset = sensorJson["offset"];
@@ -112,7 +112,7 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
if (root["analogs"].is<JsonArray>()) { if (root["analogs"].is<JsonArray>()) {
for (const JsonObject analogJson : root["analogs"].as<JsonArray>()) { for (const JsonObject analogJson : root["analogs"].as<JsonArray>()) {
// create each of the sensor, overwritting any previous settings // create each of the sensor, overwritting any previous settings
AnalogCustomization sensor = AnalogCustomization(); auto sensor = AnalogCustomization();
sensor.id = analogJson["id"]; sensor.id = analogJson["id"];
sensor.name = analogJson["name"].as<std::string>(); sensor.name = analogJson["name"].as<std::string>();
sensor.offset = analogJson["offset"]; sensor.offset = analogJson["offset"];
@@ -127,7 +127,7 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
settings.entityCustomizations.clear(); settings.entityCustomizations.clear();
if (root["exclude_entities"].is<JsonArray>()) { if (root["exclude_entities"].is<JsonArray>()) {
for (const JsonObject exclude_entities : root["exclude_entities"].as<JsonArray>()) { for (const JsonObject exclude_entities : root["exclude_entities"].as<JsonArray>()) {
EntityCustomization new_entry = EntityCustomization(); auto new_entry = EntityCustomization();
new_entry.product_id = exclude_entities["product_id"]; new_entry.product_id = exclude_entities["product_id"];
new_entry.device_id = exclude_entities["device_id"]; new_entry.device_id = exclude_entities["device_id"];
@@ -158,11 +158,11 @@ void WebCustomizationService::reset_customization(AsyncWebServerRequest * reques
// send back a short list devices used in the customization page // send back a short list devices used in the customization page
void WebCustomizationService::devices(AsyncWebServerRequest * request) { void WebCustomizationService::devices(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN); auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
JsonArray devices = root.createNestedArray("devices"); JsonArray devices = root.createNestedArray("devices");
for (auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice->has_entities()) { if (emsdevice->has_entities()) {
JsonObject obj = devices.createNestedObject(); JsonObject obj = devices.createNestedObject();
obj["i"] = emsdevice->unique_id(); // a unique id obj["i"] = emsdevice->unique_id(); // a unique id
@@ -185,9 +185,8 @@ void WebCustomizationService::devices(AsyncWebServerRequest * request) {
// send back list device entities // send back list device entities
void WebCustomizationService::device_entities(AsyncWebServerRequest * request, JsonVariant & json) { void WebCustomizationService::device_entities(AsyncWebServerRequest * request, JsonVariant & json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(true, EMSESP_JSON_SIZE_XXLARGE_DYN); auto * response = new MsgpackAsyncJsonResponse(true, EMSESP_JSON_SIZE_XXLARGE_DYN);
for (const auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice) {
if (emsdevice->unique_id() == json["id"]) { if (emsdevice->unique_id() == json["id"]) {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
JsonArray output = response->getRoot(); JsonArray output = response->getRoot();
@@ -199,7 +198,6 @@ void WebCustomizationService::device_entities(AsyncWebServerRequest * request, J
} }
} }
} }
}
// invalid, but send OK anyway // invalid, but send OK anyway
AsyncWebServerResponse * response = request->beginResponse(200); AsyncWebServerResponse * response = request->beginResponse(200);

View File

@@ -70,13 +70,13 @@ void WebDataService::scan_devices(AsyncWebServerRequest * request) {
// this is used in the dashboard and contains all ems device information // this is used in the dashboard and contains all ems device information
// /coreData endpoint // /coreData endpoint
void WebDataService::core_data(AsyncWebServerRequest * request) { void WebDataService::core_data(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN); auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
// list is already sorted by device type // list is already sorted by device type
// Ignore Contoller // Ignore Contoller
JsonArray devices = root.createNestedArray("devices"); JsonArray devices = root.createNestedArray("devices");
for (auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice && emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER) { if (emsdevice && emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER) {
JsonObject obj = devices.createNestedObject(); JsonObject obj = devices.createNestedObject();
obj["i"] = emsdevice->unique_id(); // a unique id obj["i"] = emsdevice->unique_id(); // a unique id
@@ -102,7 +102,7 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
// /sensorData endpoint // /sensorData endpoint
// the "sensors" and "analogs" are arrays and must exist // the "sensors" and "analogs" are arrays and must exist
void WebDataService::sensor_data(AsyncWebServerRequest * request) { void WebDataService::sensor_data(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN); auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
// dallas sensors // dallas sensors
@@ -158,9 +158,8 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
// Compresses the JSON using MsgPack https://msgpack.org/index.html // Compresses the JSON using MsgPack https://msgpack.org/index.html
void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) { void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXXLARGE_DYN); auto * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXXLARGE_DYN);
for (const auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice) {
if (emsdevice->unique_id() == json["id"]) { if (emsdevice->unique_id() == json["id"]) {
// wait max 2.5 sec for updated data (post_send_delay is 2 sec) // wait max 2.5 sec for updated data (post_send_delay is 2 sec)
for (uint16_t i = 0; i < (emsesp::TxService::POST_SEND_DELAY + 500) && EMSESP::wait_validate(); i++) { for (uint16_t i = 0; i < (emsesp::TxService::POST_SEND_DELAY + 500) && EMSESP::wait_validate(); i++) {
@@ -177,7 +176,6 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
} }
} }
} }
}
// invalid but send ok // invalid but send ok
AsyncWebServerResponse * response = request->beginResponse(200); AsyncWebServerResponse * response = request->beginResponse(200);
@@ -195,7 +193,6 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
// using the unique ID from the web find the real device type // using the unique ID from the web find the real device type
// id is the selected device // id is the selected device
for (const auto & emsdevice : EMSESP::emsdevices) { for (const auto & emsdevice : EMSESP::emsdevices) {
if (emsdevice) {
if (emsdevice->unique_id() == unique_id) { if (emsdevice->unique_id() == unique_id) {
// parse the command as it could have a hc or wwc prefixed, e.g. hc2/seltemp // parse the command as it could have a hc or wwc prefixed, e.g. hc2/seltemp
const char * cmd = dv["c"]; // the command const char * cmd = dv["c"]; // the command
@@ -203,7 +200,7 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
cmd = Command::parse_command_string(cmd, id); // extract hc or wwc cmd = Command::parse_command_string(cmd, id); // extract hc or wwc
// create JSON for output // create JSON for output
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL); auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
JsonObject output = response->getRoot(); JsonObject output = response->getRoot();
// the data could be in any format, but we need string // the data could be in any format, but we need string
@@ -218,7 +215,7 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0), true, id, output); return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0), true, id, output);
} else if (data.is<float>()) { } else if (data.is<float>()) {
char s[10]; char s[10];
return_code = Command::call(device_type, cmd, Helpers::render_value(s, (float)data.as<float>(), 1), true, id, output); return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<float>(), 1), true, id, output);
} else if (data.is<bool>()) { } else if (data.is<bool>()) {
return_code = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false", true, id, output); return_code = Command::call(device_type, cmd, data.as<bool>() ? "true" : "false", true, id, output);
} }
@@ -237,7 +234,6 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
} }
} }
} }
}
AsyncWebServerResponse * response = request->beginResponse(204); // Write command failed AsyncWebServerResponse * response = request->beginResponse(204); // Write command failed
request->send(response); request->send(response);

View File

@@ -90,7 +90,7 @@ void WebLogService::maximum_log_messages(size_t count) {
"local"); "local");
} }
bool WebLogService::compact() { bool WebLogService::compact() const {
return compact_; return compact_;
} }
@@ -156,24 +156,24 @@ void WebLogService::loop() {
} }
// convert time to real offset // convert time to real offset
char * WebLogService::messagetime(char * out, const uint64_t t) { char * WebLogService::messagetime(char * out, const uint64_t t, const size_t bufsize) {
if (!time_offset_) { if (!time_offset_) {
strcpy(out, uuid::log::format_timestamp_ms(t, 3).c_str()); strlcpy(out, uuid::log::format_timestamp_ms(t, 3).c_str(), bufsize);
} else { } else {
time_t t1 = time_offset_ + t / 1000ULL; time_t t1 = time_offset_ + t / 1000ULL;
strftime(out, 25, "%F %T", localtime(&t1)); strftime(out, bufsize, "%F %T", localtime(&t1));
snprintf(out, 25, "%s.%03d", out, (uint16_t)(t % 1000)); snprintf(out, bufsize, "%s.%03d", out, (uint16_t)(t % 1000));
} }
return out; return out;
} }
// send to web eventsource // send to web eventsource
void WebLogService::transmit(const QueuedLogMessage & message) { void WebLogService::transmit(const QueuedLogMessage & message) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(EMSESP_JSON_SIZE_MEDIUM); auto jsonDocument = DynamicJsonDocument(EMSESP_JSON_SIZE_MEDIUM);
JsonObject logEvent = jsonDocument.to<JsonObject>(); JsonObject logEvent = jsonDocument.to<JsonObject>();
char time_string[25]; char time_string[25];
logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); logEvent["t"] = messagetime(time_string, message.content_->uptime_ms, sizeof(time_string));
logEvent["l"] = message.content_->level; logEvent["l"] = message.content_->level;
logEvent["i"] = message.id_; logEvent["i"] = message.id_;
logEvent["n"] = message.content_->name; logEvent["n"] = message.content_->name;
@@ -190,7 +190,7 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
// send the complete log buffer to the API, not filtering on log level // send the complete log buffer to the API, not filtering on log level
void WebLogService::fetchLog(AsyncWebServerRequest * request) { void WebLogService::fetchLog(AsyncWebServerRequest * request) {
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN + 192 * log_messages_.size()); auto * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN + 192 * log_messages_.size());
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
JsonArray log = root.createNestedArray("events"); JsonArray log = root.createNestedArray("events");
@@ -200,7 +200,7 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) {
JsonObject logEvent = log.createNestedObject(); JsonObject logEvent = log.createNestedObject();
char time_string[25]; char time_string[25];
logEvent["t"] = messagetime(time_string, message.content_->uptime_ms); logEvent["t"] = messagetime(time_string, message.content_->uptime_ms, sizeof(time_string));
logEvent["l"] = message.content_->level; logEvent["l"] = message.content_->level;
logEvent["i"] = message.id_; logEvent["i"] = message.id_;
logEvent["n"] = message.content_->name; logEvent["n"] = message.content_->name;
@@ -213,7 +213,7 @@ void WebLogService::fetchLog(AsyncWebServerRequest * request) {
// sets the values like level after a POST // sets the values like level after a POST
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) { void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) {
if (not json.is<JsonObject>()) { if (!json.is<JsonObject>()) {
return; return;
} }
@@ -233,7 +233,7 @@ void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & jso
// return the current value settings after a GET // return the current value settings after a GET
void WebLogService::getValues(AsyncWebServerRequest * request) { void WebLogService::getValues(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL); auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["level"] = log_level(); root["level"] = log_level();
root["max_messages"] = maximum_log_messages(); root["max_messages"] = maximum_log_messages();

View File

@@ -38,7 +38,7 @@ class WebLogService : public uuid::log::Handler {
void log_level(uuid::log::Level level); void log_level(uuid::log::Level level);
size_t maximum_log_messages() const; size_t maximum_log_messages() const;
void maximum_log_messages(size_t count); void maximum_log_messages(size_t count);
bool compact(); bool compact() const;
void compact(bool compact); void compact(bool compact);
void loop(); void loop();
@@ -62,9 +62,10 @@ class WebLogService : public uuid::log::Handler {
void fetchLog(AsyncWebServerRequest * request); void fetchLog(AsyncWebServerRequest * request);
void getValues(AsyncWebServerRequest * request); void getValues(AsyncWebServerRequest * request);
char * messagetime(char * out, const uint64_t t); char * messagetime(char * out, const uint64_t t, const size_t bufsize);
void setValues(AsyncWebServerRequest * request, JsonVariant & json); void setValues(AsyncWebServerRequest * request, JsonVariant & json);
AsyncCallbackJsonWebHandler setValues_; // for POSTs AsyncCallbackJsonWebHandler setValues_; // for POSTs
uint64_t last_transmit_ = 0; // Last transmit time uint64_t last_transmit_ = 0; // Last transmit time

View File

@@ -271,7 +271,7 @@ void WebSettingsService::save() {
// build the json profile to send back // build the json profile to send back
void WebSettingsService::board_profile(AsyncWebServerRequest * request, JsonVariant & json) { void WebSettingsService::board_profile(AsyncWebServerRequest * request, JsonVariant & json) {
if (json.is<JsonObject>()) { if (json.is<JsonObject>()) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM); auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
if (json.containsKey("board_profile")) { if (json.containsKey("board_profile")) {

View File

@@ -126,7 +126,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
} }
void WebStatusService::webStatusService(AsyncWebServerRequest * request) { void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM_DYN); auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM_DYN);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
root["status"] = EMSESP::bus_status(); // 0, 1 or 2 root["status"] = EMSESP::bus_status(); // 0, 1 or 2
@@ -163,7 +163,7 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
} }
// start the multicast UDP service so EMS-ESP is discoverable via .local // start the multicast UDP service so EMS-ESP is discoverable via .local
void WebStatusService::mDNS_start() { void WebStatusService::mDNS_start() const {
#ifndef EMSESP_STANDALONE #ifndef EMSESP_STANDALONE
MDNS.end(); MDNS.end();
if (!MDNS.begin(EMSESP::system_.hostname().c_str())) { if (!MDNS.begin(EMSESP::system_.hostname().c_str())) {

View File

@@ -32,7 +32,7 @@ class WebStatusService {
private: private:
void webStatusService(AsyncWebServerRequest * request); void webStatusService(AsyncWebServerRequest * request);
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info); void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
void mDNS_start(); void mDNS_start() const;
}; };
} // namespace emsesp } // namespace emsesp