mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-05-03 12:36:57 +00:00
Merge branch 'dev'
This commit is contained in:
@@ -128,7 +128,8 @@ void AnalogSensor::reload() {
|
||||
[&](const char * value, const int8_t id) { return command_setvalue(value, sensor.gpio); },
|
||||
sensor.type == AnalogType::COUNTER ? FL_(counter)
|
||||
: sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out)
|
||||
: FL_(pwm));
|
||||
: FL_(pwm),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -259,7 +260,7 @@ void AnalogSensor::measure() {
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::TIMER
|
||||
|| sensor.type() == AnalogType::RATE) {
|
||||
auto old_value = sensor.value(); // remember current value before reading
|
||||
auto old_value = sensor.value(); // remember current value before reading
|
||||
auto current_reading = digitalRead(sensor.gpio());
|
||||
if (sensor.poll_ != current_reading) { // check for pinchange
|
||||
sensor.polltime_ = uuid::get_uptime(); // remember time of pinchange
|
||||
@@ -273,7 +274,7 @@ void AnalogSensor::measure() {
|
||||
} else if (!sensor.poll_) { // falling edge
|
||||
if (sensor.type() == AnalogType::COUNTER) {
|
||||
sensor.set_value(old_value + sensor.factor());
|
||||
} else if (sensor.type() == AnalogType::RATE) { // dafault uom: Hz (1/sec) with factor 1
|
||||
} else if (sensor.type() == AnalogType::RATE) { // dafault uom: Hz (1/sec) with factor 1
|
||||
sensor.set_value(sensor.factor() * 1000 / (sensor.polltime_ - sensor.last_polltime_));
|
||||
} else if (sensor.type() == AnalogType::TIMER) { // default seconds with factor 1
|
||||
sensor.set_value(sensor.factor() * (sensor.polltime_ - sensor.last_polltime_) / 1000);
|
||||
@@ -300,7 +301,8 @@ void AnalogSensor::loop() {
|
||||
}
|
||||
|
||||
// update analog information name and offset
|
||||
bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type) {
|
||||
// a type of -1 is used to delete the sensor
|
||||
bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted) {
|
||||
boolean found_sensor = false; // see if we can find the sensor in our customization list
|
||||
|
||||
EMSESP::webCustomizationService.update(
|
||||
@@ -312,7 +314,7 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset,
|
||||
if (AnalogCustomization.gpio == gpio) {
|
||||
found_sensor = true; // found the record
|
||||
// see if it's marked for deletion
|
||||
if (type == AnalogType::MARK_DELETED) {
|
||||
if (deleted) {
|
||||
LOG_DEBUG("Removing analog sensor GPIO %02d", gpio);
|
||||
settings.analogCustomizations.remove(AnalogCustomization);
|
||||
} else {
|
||||
@@ -379,7 +381,7 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) const {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(analogsensor), "_data", sensor.name().c_str());
|
||||
}
|
||||
char payload[10];
|
||||
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as doubles
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as doubles
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,14 +390,14 @@ void AnalogSensor::remove_ha_topic(const int8_t type, const uint8_t gpio) const
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
#ifdef EMSESP_DEBUG
|
||||
|
||||
LOG_DEBUG("Removing HA config for analog sensor GPIO %02d", gpio);
|
||||
#endif
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (type == AnalogType::DIGITAL_OUT && gpio != 25 && gpio != 26) {
|
||||
#else
|
||||
if (type == AnalogType::DIGITAL_OUT)
|
||||
if (type == AnalogType::DIGITAL_OUT) {
|
||||
#endif
|
||||
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
} else if (type == AnalogType::DIGITAL_OUT) { // DAC
|
||||
@@ -407,7 +409,7 @@ void AnalogSensor::remove_ha_topic(const int8_t type, const uint8_t gpio) const
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
|
||||
}
|
||||
Mqtt::publish_ha(topic);
|
||||
Mqtt::queue_remove_topic(topic);
|
||||
}
|
||||
|
||||
// send all sensor values as a JSON package to MQTT
|
||||
@@ -442,13 +444,32 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
case AnalogType::PWM_2:
|
||||
dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // double
|
||||
break;
|
||||
case AnalogType::DIGITAL_IN:
|
||||
case AnalogType::DIGITAL_OUT:
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
dataSensor["value"] = sensor.value() != 0;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
dataSensor["value"] = sensor.value() != 0 ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
dataSensor["value"] = Helpers::render_boolean(result, sensor.value() != 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0
|
||||
break;
|
||||
}
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
doc[sensor.name()] = sensor.value() != 0;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
doc[sensor.name()] = sensor.value() != 0 ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
doc[sensor.name()] = Helpers::render_boolean(result, sensor.value() != 0);
|
||||
}
|
||||
} else {
|
||||
// not nested
|
||||
doc[sensor.name()] = sensor.value();
|
||||
char s[10];
|
||||
doc[sensor.name()] = serialized(Helpers::render_value(s, sensor.value(), 2));
|
||||
}
|
||||
|
||||
// create HA config
|
||||
@@ -458,22 +479,26 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); // use base path
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::basename().c_str()); // use basename
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
char val_obj[50];
|
||||
char val_cond[65];
|
||||
char val_cond[95];
|
||||
if (Mqtt::is_nested()) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%02d'].value", sensor.gpio());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%02d'] is defined", sensor.gpio());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%02d'] is defined and %s is defined", sensor.gpio(), val_obj);
|
||||
} else {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
||||
char sample_val[12] = "0";
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
Helpers::render_boolean(sample_val, false);
|
||||
}
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
|
||||
|
||||
char uniq_s[70];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "%s_analogsensor_%02d", Mqtt::basename().c_str(), sensor.gpio());
|
||||
} else {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "analogsensor_%02d", sensor.gpio());
|
||||
@@ -497,7 +522,7 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (sensor.type() == AnalogType::DIGITAL_OUT && sensor.gpio() != 25 && sensor.gpio() != 26) {
|
||||
#else
|
||||
if (sensor.type() == AnalogType::DIGITAL_OUT)
|
||||
if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
#endif
|
||||
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
|
||||
@@ -538,6 +563,17 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
// config["step"] = sensor.factor();
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
config["pl_on"] = true;
|
||||
config["pl_off"] = false;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
config["pl_on"] = 1;
|
||||
config["pl_off"] = 0;
|
||||
} else {
|
||||
char result[12];
|
||||
config["pl_on"] = Helpers::render_boolean(result, true);
|
||||
config["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
|
||||
config["stat_cla"] = "measurement";
|
||||
@@ -545,19 +581,17 @@ void AnalogSensor::publish_values(const bool force) {
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
ids.add(Mqtt::basename());
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
|
||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||
|
||||
sensor.ha_registered = true;
|
||||
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::publish("analogsensor_data", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("analogsensor_data", doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
// called from emsesp.cpp, similar to the emsdevice->get_value_info
|
||||
@@ -579,7 +613,7 @@ bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const i
|
||||
}
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (strcmp(command_s, sensor.name().c_str()) == 0 || Helpers::atoint(command_s) == sensor.gpio()) {
|
||||
if (Helpers::toLower(command_s) == Helpers::toLower(sensor.name().c_str()) || Helpers::atoint(command_s) == sensor.gpio()) {
|
||||
output["gpio"] = sensor.gpio();
|
||||
output["name"] = sensor.name();
|
||||
output["type"] = F_(number);
|
||||
@@ -651,7 +685,7 @@ bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject
|
||||
dataSensor["value"] = sensor.value();
|
||||
} else if (id == 0) { // output values command
|
||||
output[sensor.name()] = sensor.value();
|
||||
} else { // if someone wants gpio numbers
|
||||
} else { // if someone wants gpio numbers
|
||||
char gpio_str[9];
|
||||
snprintf(gpio_str, sizeof(gpio_str), "gpio_%02d", sensor.gpio());
|
||||
output[gpio_str] = sensor.value();
|
||||
@@ -760,7 +794,7 @@ bool AnalogSensor::command_commands(const char * value, const int8_t id, JsonObj
|
||||
}
|
||||
|
||||
// hard coded tests
|
||||
#ifdef EMSESP_DEBUG
|
||||
#ifdef EMSESP_TEST
|
||||
void AnalogSensor::test() {
|
||||
sensors_.emplace_back(36, "test12", 0, 0.1, 17, AnalogType::ADC);
|
||||
sensors_.back().set_value(12.4);
|
||||
@@ -770,4 +804,4 @@ void AnalogSensor::test() {
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -112,8 +112,7 @@ class AnalogSensor {
|
||||
~AnalogSensor() = default;
|
||||
|
||||
enum AnalogType : int8_t {
|
||||
MARK_DELETED = -1, // mark for deletion
|
||||
NOTUSED, // 0 - disabled
|
||||
NOTUSED, // 0 - disabled
|
||||
DIGITAL_IN,
|
||||
COUNTER,
|
||||
ADC,
|
||||
@@ -157,10 +156,10 @@ class AnalogSensor {
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
bool update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type);
|
||||
bool update(uint8_t gpio, const std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted = false);
|
||||
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id) const;
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_TEST)
|
||||
void test();
|
||||
#endif
|
||||
|
||||
|
||||
113
src/command.cpp
113
src/command.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -46,18 +46,12 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
if (!strncmp(path, Mqtt::base().c_str(), Mqtt::base().length())) {
|
||||
char new_path[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
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 {
|
||||
return message(CommandRet::ERROR, "unrecognized path", output); // error
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_USE_SERIAL)
|
||||
// Serial.println(p.path().c_str()); // dump paths, for debugging
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// re-calculate new path
|
||||
// if there is only a path (URL) and no body then error!
|
||||
size_t num_paths = p.paths().size();
|
||||
@@ -151,6 +145,40 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
data = input["value"];
|
||||
}
|
||||
|
||||
// check if data is entity like device/hc/name/value
|
||||
if (data.is<const char *>()) {
|
||||
const char * d = data.as<const char *>();
|
||||
if (strlen(d)) {
|
||||
char * device_end = (char *)strchr(d, '/');
|
||||
if (device_end != nullptr) {
|
||||
char device_s[15] = {'\0'};
|
||||
const char * device_p = device_s;
|
||||
const char * data_p = nullptr;
|
||||
strlcpy(device_s, d, device_end - d + 1);
|
||||
data_p = device_end + 1;
|
||||
int8_t id_d = -1;
|
||||
data_p = parse_command_string(data_p, id_d);
|
||||
if (data_p == nullptr) {
|
||||
return CommandRet::INVALID;
|
||||
}
|
||||
char data_s[40];
|
||||
strlcpy(data_s, Helpers::toLower(data_p).c_str(), 30);
|
||||
if (strstr(data_s, "/value") == nullptr) {
|
||||
strcat(data_s, "/value");
|
||||
}
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(device_p);
|
||||
if (CommandRet::OK != Command::call(device_type, data_s, "", true, id_d, output)) {
|
||||
return CommandRet::INVALID;
|
||||
}
|
||||
if (!output.containsKey("api_data")) {
|
||||
return CommandRet::INVALID;
|
||||
}
|
||||
data = output["api_data"];
|
||||
output.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call the command based on the type
|
||||
uint8_t return_code = CommandRet::ERROR;
|
||||
if (data.is<const char *>()) {
|
||||
@@ -166,7 +194,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
} else if (data.isNull()) {
|
||||
return_code = Command::call(device_type, command_p, "", is_admin, id_n, output); // empty, will do a query instead
|
||||
} else {
|
||||
return message(CommandRet::ERROR, "cannot parse command", output); // can't process
|
||||
return message(CommandRet::ERROR, "cannot parse command", output); // can't process
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
@@ -231,11 +259,14 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
id = command[2] - '1' + DeviceValueTAG::TAG_HS1 - DeviceValueTAG::TAG_HC1 + 1; //20;
|
||||
command += 3;
|
||||
}
|
||||
|
||||
// remove separator
|
||||
if (command[0] == '/' || command[0] == '.' || command[0] == '_') {
|
||||
command++;
|
||||
}
|
||||
|
||||
free(lowerCmd);
|
||||
|
||||
// return null for empty command
|
||||
if (command[0] == '\0') {
|
||||
return nullptr;
|
||||
@@ -273,9 +304,7 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
|
||||
// except for system commands as this is a special device without any queryable entities (device values)
|
||||
if ((device_type > EMSdevice::DeviceType::SYSTEM) && (!value || !strlen(value))) {
|
||||
if (!cf || !cf->cmdfunction_json_) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] Calling %s command '%s' to retrieve attributes", dname, cmd);
|
||||
#endif
|
||||
LOG_DEBUG("Calling %s command '%s' to retrieve attributes", dname, cmd);
|
||||
return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd
|
||||
}
|
||||
}
|
||||
@@ -350,7 +379,8 @@ void Command::add(const uint8_t device_type, const uint8_t device_id, const char
|
||||
cmdfunctions_.emplace_back(device_type, device_id, flags, cmd, cb, nullptr, description); // callback for json is nullptr
|
||||
}
|
||||
|
||||
// same for system/dallas/analog devices with device_id 0
|
||||
// add a command with no json output
|
||||
// system/temperature/analog devices uses device_id 0
|
||||
void Command::add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
|
||||
add(device_type, 0, cmd, cb, description, flags);
|
||||
}
|
||||
@@ -415,7 +445,9 @@ bool Command::list(const uint8_t device_type, JsonObject & output) {
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == std::string(cf.cmd_))) {
|
||||
if (cf.has_flags(CommandFlag::MQTT_SUB_FLAG_WW)) {
|
||||
output[cl] = EMSdevice::tag_to_string(DeviceValueTAG::TAG_DEVICE_DATA_WW) + " " + Helpers::translated_word(cf.description_);
|
||||
char s[100];
|
||||
snprintf(s, sizeof(s), "%s %s", EMSdevice::tag_to_string(DeviceValueTAG::TAG_DEVICE_DATA_WW), Helpers::translated_word(cf.description_));
|
||||
output[cl] = s;
|
||||
} else {
|
||||
output[cl] = Helpers::translated_word(cf.description_);
|
||||
}
|
||||
@@ -503,8 +535,16 @@ bool Command::device_has_commands(const uint8_t device_type) {
|
||||
return true; // we always have System
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::DALLASSENSOR) {
|
||||
return (EMSESP::dallassensor_.have_sensors());
|
||||
if (device_type == EMSdevice::DeviceType::SCHEDULER) {
|
||||
return EMSESP::webSchedulerService.has_commands();
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::CUSTOM) {
|
||||
return (EMSESP::webEntityService.count_entities() != 0);
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::TEMPERATURESENSOR) {
|
||||
return (EMSESP::temperaturesensor_.have_sensors());
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::ANALOGSENSOR) {
|
||||
@@ -529,8 +569,11 @@ bool Command::device_has_commands(const uint8_t device_type) {
|
||||
void Command::show_devices(uuid::console::Shell & shell) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM));
|
||||
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR));
|
||||
if (EMSESP::webSchedulerService.has_commands()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SCHEDULER));
|
||||
}
|
||||
if (EMSESP::temperaturesensor_.have_sensors()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::TEMPERATURESENSOR));
|
||||
}
|
||||
if (EMSESP::analogsensor_.have_sensors()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR));
|
||||
@@ -559,13 +602,39 @@ void Command::show_all(uuid::console::Shell & shell) {
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM, true);
|
||||
|
||||
// show sensors
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
// show Custom
|
||||
if (EMSESP::webEntityService.has_commands()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR));
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::CUSTOM));
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" info: %slists all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_RED);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" commands: %slists all commands %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_RED);
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::DALLASSENSOR, true);
|
||||
show(shell, EMSdevice::DeviceType::CUSTOM, true);
|
||||
}
|
||||
|
||||
// show scheduler
|
||||
if (EMSESP::webSchedulerService.has_commands()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SCHEDULER));
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" info: %slists all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_RED);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" commands: %slists all commands %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_RED);
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::SCHEDULER, true);
|
||||
}
|
||||
|
||||
// show sensors
|
||||
if (EMSESP::temperaturesensor_.have_sensors()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::TEMPERATURESENSOR));
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::TEMPERATURESENSOR, true);
|
||||
}
|
||||
if (EMSESP::analogsensor_.have_sensors()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -56,7 +56,7 @@ class Command {
|
||||
struct CmdFunction {
|
||||
uint8_t device_type_; // DeviceType::
|
||||
uint8_t device_id_;
|
||||
uint8_t flags_; // mqtt flags for command subscriptions
|
||||
uint8_t flags_; // mqtt flags for command subscriptions
|
||||
const char * cmd_;
|
||||
cmd_function_p cmdfunction_;
|
||||
cmd_json_function_p cmdfunction_json_;
|
||||
@@ -107,7 +107,7 @@ class Command {
|
||||
const char * const * description,
|
||||
uint8_t flags = CommandFlag::MQTT_SUB_FLAG_DEFAULT);
|
||||
|
||||
// same for system/dallas/analog devices
|
||||
// same for system/temperature/analog devices
|
||||
static void add(const uint8_t device_type,
|
||||
const char * cmd,
|
||||
const cmd_function_p cb,
|
||||
|
||||
35
src/common.h
35
src/common.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,11 +19,16 @@
|
||||
#ifndef EMSESP_COMMON_H
|
||||
#define EMSESP_COMMON_H
|
||||
|
||||
// logging
|
||||
#include <uuid/log.h>
|
||||
|
||||
using uuid::log::Level;
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#define LOG_DEBUG(...) logger_.debug(__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#define LOG_INFO(...) logger_.info(__VA_ARGS__)
|
||||
#define LOG_TRACE(...) logger_.trace(__VA_ARGS__)
|
||||
#define LOG_NOTICE(...) logger_.notice(__VA_ARGS__)
|
||||
@@ -41,13 +46,31 @@ using string_vector = std::vector<const char *>;
|
||||
// clang-format off
|
||||
|
||||
#define FPSTR(pstr_pointer) pstr_pointer
|
||||
#define MAKE_PSTR(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
|
||||
#define MAKE_PSTR_WORD(string_name) MAKE_PSTR(string_name, #string_name)
|
||||
#define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
|
||||
#define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name)
|
||||
|
||||
#define F_(string_name) (__pstr__##string_name)
|
||||
#define FL_(list_name) (__pstr__L_##list_name)
|
||||
#define MAKE_PSTR_LIST(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
#define MAKE_PSTR_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
#if defined(EMSESP_TEST) || defined(EMSESP_EN_ONLY)
|
||||
// In testing just take one language (en) to save on Flash space
|
||||
#define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr};
|
||||
#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr};
|
||||
#elif defined(EMSESP_DE_ONLY)
|
||||
#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {de, nullptr};
|
||||
#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, de, nullptr};
|
||||
#else
|
||||
#define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
#endif
|
||||
|
||||
#define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// fixed strings, no translations
|
||||
#define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// with translations
|
||||
#define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
860
src/console.cpp
860
src/console.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,85 +26,47 @@
|
||||
#include "system.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
using uuid::console::Commands;
|
||||
using uuid::console::Shell;
|
||||
|
||||
static constexpr uint32_t INVALID_PASSWORD_DELAY_MS = 2000;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using LogLevel = ::uuid::log::Level;
|
||||
using LogFacility = ::uuid::log::Facility;
|
||||
|
||||
#ifdef LOCAL
|
||||
#undef LOCAL
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum CommandFlags : uint8_t { USER = 0, ADMIN = (1 << 0), LOCAL = (1 << 1) };
|
||||
|
||||
enum ShellContext : uint8_t {
|
||||
MAIN = 0,
|
||||
SYSTEM,
|
||||
};
|
||||
enum ShellContext : uint8_t { MAIN = 0, SYSTEM, END };
|
||||
|
||||
class EMSESPShell : virtual public uuid::console::Shell {
|
||||
class EMSESP;
|
||||
|
||||
class EMSESPShell : public uuid::console::Shell {
|
||||
public:
|
||||
~EMSESPShell() override = default;
|
||||
|
||||
virtual std::string console_name() = 0;
|
||||
|
||||
static std::shared_ptr<uuid::console::Commands> commands;
|
||||
static std::shared_ptr<EMSESPShell> shell;
|
||||
static void main_help_function(Shell & shell, const std::vector<std::string> & arguments);
|
||||
static void main_exit_function(Shell & shell, const std::vector<std::string> & arguments);
|
||||
|
||||
EMSESP & emsesp_;
|
||||
|
||||
protected:
|
||||
EMSESPShell();
|
||||
EMSESPShell(EMSESP & emsesp, Stream & stream, unsigned int context, unsigned int flags);
|
||||
|
||||
static std::shared_ptr<uuid::console::Commands> commands_;
|
||||
|
||||
// our custom functions for Shell
|
||||
void started() override;
|
||||
void stopped() override;
|
||||
void display_banner() override;
|
||||
std::string hostname_text() override;
|
||||
std::string context_text() override;
|
||||
std::string prompt_suffix() override;
|
||||
void end_of_transmission() override;
|
||||
|
||||
|
||||
private:
|
||||
void add_console_commands();
|
||||
bool console_commands_loaded_ = false; // set to true when the initial commands are loaded
|
||||
std::string console_hostname_;
|
||||
};
|
||||
|
||||
class EMSESPStreamConsole : public uuid::console::StreamConsole, public EMSESPShell {
|
||||
public:
|
||||
EMSESPStreamConsole(Stream & stream, bool local);
|
||||
EMSESPStreamConsole(Stream & stream, const IPAddress & addr, uint16_t port);
|
||||
~EMSESPStreamConsole() override;
|
||||
|
||||
std::string console_name() override;
|
||||
|
||||
private:
|
||||
static std::vector<bool> ptys_;
|
||||
|
||||
std::string name_;
|
||||
size_t pty_;
|
||||
IPAddress addr_;
|
||||
uint16_t port_;
|
||||
};
|
||||
|
||||
class Console {
|
||||
public:
|
||||
void loop();
|
||||
void start_serial();
|
||||
void start_telnet();
|
||||
|
||||
uuid::log::Level log_level();
|
||||
|
||||
static void load_standard_commands(unsigned int context);
|
||||
static void load_system_commands(unsigned int context);
|
||||
|
||||
private:
|
||||
bool telnet_enabled_ = false; // telnet is default off
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
50
src/console_stream.h
Normal file
50
src/console_stream.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* mcu-app - Microcontroller application framework
|
||||
* Copyright 2022 Simon Arlott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <uuid/console.h>
|
||||
|
||||
#include "console.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSESPConsole : public EMSESPShell {
|
||||
public:
|
||||
EMSESPConsole(EMSESP & emsesp, Stream & stream, bool local);
|
||||
#ifndef ENV_NATIVE
|
||||
EMSESPConsole(EMSESP & emsesp, Stream & stream, const IPAddress & addr, uint16_t port);
|
||||
#endif
|
||||
~EMSESPConsole() override;
|
||||
|
||||
std::string console_name();
|
||||
|
||||
private:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static std::vector<bool> ptys_;
|
||||
#endif
|
||||
|
||||
std::string name_;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
size_t pty_;
|
||||
IPAddress addr_;
|
||||
uint16_t port_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -61,10 +61,6 @@
|
||||
#define EMSESP_DEFAULT_TRACELOG_RAW false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOILER_HEATINGOFF
|
||||
#define EMSESP_DEFAULT_BOILER_HEATINGOFF false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SHOWER_TIMER
|
||||
#define EMSESP_DEFAULT_SHOWER_TIMER false
|
||||
#endif
|
||||
@@ -173,6 +169,10 @@
|
||||
#define EMSESP_DEFAULT_DISCOVERY_PREFIX "homeassistant"
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DISCOVERY_TYPE
|
||||
#define EMSESP_DEFAULT_DISCOVERY_TYPE 0 // HA
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_SINGLE
|
||||
#define EMSESP_DEFAULT_PUBLISH_SINGLE false
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,7 +32,7 @@
|
||||
{115, DeviceType::BOILER, "Topline/GB162", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{121, DeviceType::BOILER, "Cascade MCM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{122, DeviceType::BOILER, "Proline", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{123, DeviceType::BOILER, "GBx72/Trendline/Cerapur/Greenstar Si/27i-30i", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{123, DeviceType::BOILER, "GBx72/Trendline/Cerapur/Greenstar Si", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{131, DeviceType::BOILER, "GB212", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{132, DeviceType::BOILER, "GC7000F", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{133, DeviceType::BOILER, "Logano GB125/KB195i/Logamatic MC110", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
@@ -48,6 +48,7 @@
|
||||
{208, DeviceType::BOILER, "Logamax Plus/GB192/Condens GC9000/Greenstar ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{210, DeviceType::BOILER, "Cascade MC400", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{211, DeviceType::BOILER, "EasyControl Adapter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{219, DeviceType::BOILER, "Greenstar HIU", DeviceFlags::EMS_DEVICE_FLAG_HIU},
|
||||
{234, DeviceType::BOILER, "Logamax Plus GB122/Condense 2300", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Controllers - 0x09 / 0x10 / 0x50
|
||||
@@ -96,9 +97,10 @@
|
||||
{158, DeviceType::THERMOSTAT, "RC300/RC310/Moduline 3000/1010H/CW400/Sense II/HPC410", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{165, DeviceType::THERMOSTAT, "RC100/Moduline 1000/1010", DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
|
||||
{172, DeviceType::THERMOSTAT, "Rego 2000/3000", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{215, DeviceType::THERMOSTAT, "Comfort RF", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||
{216, DeviceType::THERMOSTAT, "CRF200S", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||
{246, DeviceType::THERMOSTAT, "Comfort+2RF", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||
{253, DeviceType::THERMOSTAT, "Rego 3000", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{253, DeviceType::THERMOSTAT, "Rego 3000/UI800", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
|
||||
// Thermostat - Sieger - 0x10 / 0x17
|
||||
{ 66, DeviceType::THERMOSTAT, "ES72/RC20", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
|
||||
@@ -121,7 +123,7 @@
|
||||
// Thermostat remote - 0x38
|
||||
{ 3, DeviceType::THERMOSTAT, "RT800", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
|
||||
{200, DeviceType::THERMOSTAT, "RC100H", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
|
||||
{249, DeviceType::THERMOSTAT, "TR120RF", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
|
||||
{249, DeviceType::THERMOSTAT, "TR120RF/CR20RF", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
|
||||
|
||||
// Solar Modules - 0x30 (for solar), 0x2A, 0x41 (for ww)
|
||||
{ 73, DeviceType::SOLAR, "SM10", DeviceFlags::EMS_DEVICE_FLAG_SM10},
|
||||
@@ -148,6 +150,9 @@
|
||||
// Heat Pumps - 0x53
|
||||
{248, DeviceType::HEATPUMP, "Hybrid Manager HM200", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Ventilation - 0x51
|
||||
{231, DeviceType::VENTILATION, "Logavent HRV176", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Heatsource - 0x60
|
||||
{228, DeviceType::HEATSOURCE, "AM200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // alternative heatsource
|
||||
|
||||
@@ -175,4 +180,4 @@
|
||||
// Generic - 0x40 or other with no product-id and no version
|
||||
{0, DeviceType::GENERIC, "unknown", DeviceFlags::EMS_DEVICE_FLAG_NONE}
|
||||
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -27,7 +27,6 @@ uuid::log::Logger Boiler::logger_{F_(boiler), uuid::log::Facility::CONSOLE};
|
||||
Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// register values for master boiler/cascade module
|
||||
// reserve_telegram_functions(25); // reserve some space for the telegram registries, to avoid memory fragmentation
|
||||
|
||||
// the telegram handlers...
|
||||
// common for all boilers
|
||||
@@ -76,8 +75,8 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0x4A2, "HpInput", true, MAKE_PF_CB(process_HpInput));
|
||||
register_telegram_type(0x485, "HpCooling", true, MAKE_PF_CB(process_HpCooling));
|
||||
register_telegram_type(0x486, "HpInConfig", true, MAKE_PF_CB(process_HpInConfig));
|
||||
register_telegram_type(0x492, "HpHeaterConfig", true, MAKE_PF_CB(process_HpHeaterConfig));
|
||||
|
||||
register_telegram_type(0x492, "HpHeaterConfig", true, MAKE_PF_CB(process_HpHeaterConfig));
|
||||
register_telegram_type(0x488, "HPValve", true, MAKE_PF_CB(process_HpValve));
|
||||
register_telegram_type(0x484, "HPSilentMode", true, MAKE_PF_CB(process_HpSilentMode));
|
||||
register_telegram_type(0x48B, "HPPumps", true, MAKE_PF_CB(process_HpPumps));
|
||||
@@ -87,6 +86,26 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0x49D, "HPSettings3", true, MAKE_PF_CB(process_HpSettings3));
|
||||
}
|
||||
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_HIU) {
|
||||
register_telegram_type(0x772, "HIUSettings", false, MAKE_PF_CB(process_HIUSettings));
|
||||
register_telegram_type(0x779, "HIUMonitor", false, MAKE_PF_CB(process_HIUMonitor));
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&keepWarmTemp_,
|
||||
DeviceValueType::UINT,
|
||||
FL_(keepWarmTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_keepWarmTemp));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&setReturnTemp_,
|
||||
DeviceValueType::UINT,
|
||||
FL_(setReturnTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_returnTemp));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cwFlowRate_, DeviceValueType::USHORT, FL_(cwFlowRate), DeviceValueUOM::LMIN);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &netFlowTemp_, DeviceValueType::USHORT, FL_(netFlowTemp), DeviceValueUOM::DEGREES);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hybrid heatpump with telegram 0xBB is readable and writeable in boiler and thermostat
|
||||
* thermostat always overwrites settings in boiler
|
||||
@@ -100,12 +119,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
// reset is a command uses a dummy variable which is always zero, shown as blank, but provides command enum options
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &reset_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset));
|
||||
has_update(reset_, 0);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&forceHeatingOff_,
|
||||
DeviceValueType::BOOL,
|
||||
FL_(forceHeatingOff),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_forceHeatingOff));
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingActive_, DeviceValueType::BOOL, FL_(heatingActive), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &tapwaterActive_, DeviceValueType::BOOL, FL_(tapwaterActive), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &selFlowTemp_, DeviceValueType::UINT, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
|
||||
@@ -576,7 +590,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
FL_(tempDiffHeat),
|
||||
DeviceValueUOM::K,
|
||||
MAKE_CF_CB(set_tempDiffHeat),
|
||||
3,
|
||||
2,
|
||||
10);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&tempDiffCool_,
|
||||
@@ -585,7 +599,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
FL_(tempDiffCool),
|
||||
DeviceValueUOM::K,
|
||||
MAKE_CF_CB(set_tempDiffCool),
|
||||
3,
|
||||
2,
|
||||
10);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &vp_cooling_, DeviceValueType::BOOL, FL_(vp_cooling), DeviceValueUOM::NONE, MAKE_CF_CB(set_vp_cooling));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatCable_, DeviceValueType::BOOL, FL_(heatCable), DeviceValueUOM::NONE, MAKE_CF_CB(set_heatCable));
|
||||
@@ -839,7 +853,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
|
||||
// Check if hot tap water or heating is active
|
||||
// Values will always be posted first time as heatingActive_ and tapwaterActive_ will have values EMS_VALUE_BOOL_NOTSET
|
||||
void Boiler::check_active(const bool force) {
|
||||
void Boiler::check_active() {
|
||||
if (!Helpers::hasValue(boilerState_)) {
|
||||
return;
|
||||
}
|
||||
@@ -850,10 +864,10 @@ void Boiler::check_active(const bool force) {
|
||||
// check if heating is active, bits 2 and 4 must be set
|
||||
b = ((boilerState_ & 0x09) == 0x09);
|
||||
val = b ? EMS_VALUE_BOOL_ON : EMS_VALUE_BOOL_OFF;
|
||||
if (heatingActive_ != val || force) {
|
||||
if (heatingActive_ != val) {
|
||||
heatingActive_ = val;
|
||||
char s[12];
|
||||
Mqtt::publish(F_(heating_active), Helpers::render_boolean(s, b));
|
||||
Mqtt::queue_publish(F_(heating_active), Helpers::render_boolean(s, b));
|
||||
}
|
||||
|
||||
// check if we can use tapactivated in flow systems
|
||||
@@ -874,17 +888,12 @@ void Boiler::check_active(const bool force) {
|
||||
}
|
||||
|
||||
val = b ? EMS_VALUE_BOOL_ON : EMS_VALUE_BOOL_OFF;
|
||||
if (tapwaterActive_ != val || force) {
|
||||
if (tapwaterActive_ != val) {
|
||||
tapwaterActive_ = val;
|
||||
char s[12];
|
||||
Mqtt::publish(F_(tapwater_active), Helpers::render_boolean(s, b));
|
||||
Mqtt::queue_publish(F_(tapwater_active), Helpers::render_boolean(s, b));
|
||||
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
|
||||
}
|
||||
|
||||
if (!Helpers::hasValue(forceHeatingOff_, EMS_VALUE_BOOL)) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { forceHeatingOff_ = (settings.boiler_heatingoff || selFlowTemp_ == 0) ? 1 : 0; });
|
||||
has_update(&forceHeatingOff_);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x18
|
||||
@@ -971,13 +980,13 @@ void Boiler::process_UBASettingsWW(std::shared_ptr<const Telegram> telegram) {
|
||||
// Boiler(0x08) -> Me(0x0B), UBAParameterWW(0x33), data: 08 FF 30 FB FF 28 FF 07 46 00 00
|
||||
void Boiler::process_UBAParameterWW(std::shared_ptr<const Telegram> telegram) {
|
||||
// has_bitupdate(telegram, wwEquipt_,0,3); // 8=boiler has ww
|
||||
has_update(telegram, wwActivated_, 1); // 0xFF means on
|
||||
has_update(telegram, wwActivated_, 1); // 0xFF means on
|
||||
has_update(telegram, wwSelTemp_, 2);
|
||||
has_update(telegram, wwHystOn_, 3); // Hyst on (default -5)
|
||||
has_update(telegram, wwHystOff_, 4); // Hyst off (default -1)
|
||||
has_update(telegram, wwFlowTempOffset_, 5); // default 40
|
||||
has_update(telegram, wwCircPump_, 6); // 0xFF means on
|
||||
has_update(telegram, wwCircMode_, 7); // 0=off, 1=1x3min 6=6x3min 7=continuous
|
||||
has_update(telegram, wwHystOn_, 3); // Hyst on (default -5)
|
||||
has_update(telegram, wwHystOff_, 4); // Hyst off (default -1)
|
||||
has_update(telegram, wwFlowTempOffset_, 5); // default 40
|
||||
has_update(telegram, wwCircPump_, 6); // 0xFF means on
|
||||
has_update(telegram, wwCircMode_, 7); // 0=off, 1=1x3min 6=6x3min 7=continuous
|
||||
has_update(telegram, wwDisinfectionTemp_, 8);
|
||||
has_bitupdate(telegram, wwChargeType_, 10, 0); // 0 = charge pump, 0xff = 3-way valve
|
||||
|
||||
@@ -1043,6 +1052,8 @@ void Boiler::process_UBAMonitorFastPlus(std::shared_ptr<const Telegram> telegram
|
||||
//has_update(telegram, temperatur_, 13); // unknown temperature
|
||||
//has_update(telegram, temperatur_, 27); // unknown temperature
|
||||
|
||||
has_update(telegram, exhaustTemp_, 31);
|
||||
|
||||
// read 3 char service code / installation status as appears on the display
|
||||
if ((telegram->message_length > 3) && (telegram->offset == 0)) {
|
||||
char serviceCode[4] = {0};
|
||||
@@ -1078,18 +1089,13 @@ void Boiler::process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, outdoorTemp_, 0);
|
||||
has_update(telegram, boilTemp_, 2);
|
||||
has_update(telegram, exhaustTemp_, 4);
|
||||
has_update(telegram, switchTemp_, 25); // only if there is a mixer module present
|
||||
has_update(telegram, switchTemp_, 25); // only if there is a mixer module present
|
||||
has_update(telegram, heatingPumpMod_, 9);
|
||||
has_update(telegram, burnStarts_, 10, 3); // force to 3 bytes
|
||||
has_update(telegram, burnWorkMin_, 13, 3); // force to 3 bytes
|
||||
has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes
|
||||
has_update(telegram, heatWorkMin_, 19, 3); // force to 3 bytes
|
||||
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
|
||||
|
||||
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
|
||||
uint8_t data[] = {0, 0, 0, 0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1112,7 +1118,7 @@ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram
|
||||
has_bitupdate(telegram, ignWork_, 2, 3);
|
||||
has_bitupdate(telegram, heatingPump_, 2, 5);
|
||||
has_bitupdate(telegram, wwCirc_, 2, 7);
|
||||
has_update(telegram, exhaustTemp_, 6);
|
||||
// has_update(telegram, exhaustTemp_, 6); // Disabled until verified as valid location, see #1147.
|
||||
has_update(telegram, burnStarts_, 10, 3); // force to 3 bytes
|
||||
has_update(telegram, burnWorkMin_, 13, 3); // force to 3 bytes
|
||||
has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes
|
||||
@@ -1120,11 +1126,6 @@ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram
|
||||
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
|
||||
has_update(telegram, heatingPumpMod_, 25);
|
||||
// temperature measurements at 4, see #620
|
||||
|
||||
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
|
||||
uint8_t data[] = {0, 0, 0, 0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1310,28 +1311,36 @@ void Boiler::process_HpInput(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, hpInput[3].state, 5);
|
||||
}
|
||||
|
||||
// Heatpump inputs settings- type 0x486
|
||||
// Heatpump inputs settings- type 0x486 (https://github.com/emsesp/EMS-ESP32/issues/600)
|
||||
// Boiler(0x08) -> All(0x00), ?(0x0486), data: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
// Boiler(0x08) -> All(0x00), ?(0x0486), data: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 00 00 00 00 00 (offset 25)
|
||||
// Boiler(0x08) -> All(0x00), ?(0x0486), data: 00 00 (offset 51)
|
||||
void Boiler::process_HpInConfig(std::shared_ptr<const Telegram> telegram) {
|
||||
char option[12];
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
for (uint8_t j = 0; j < 11; j++) {
|
||||
char option[16];
|
||||
// inputs 1,2,3 <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv><prot><pres><mod>]
|
||||
uint8_t index[] = {0, 3, 6, 9, 12, 15, 18, 21, 24, 39, 36, 30, 27};
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
for (uint8_t j = 0; j < 12; j++) {
|
||||
option[j] = hpInput[i].option[j] - '0';
|
||||
telegram->read_value(option[j], j * 4 + i);
|
||||
option[j] = option[j] ? '1' : '0';
|
||||
telegram->read_value(option[j], index[j] + i);
|
||||
option[j] = option[j] == 1 ? '1' : '0';
|
||||
}
|
||||
option[11] = '\0'; // terminate string
|
||||
has_update(hpInput[i].option, option, 12);
|
||||
option[12] = atoi(&hpInput[i].option[12]);
|
||||
telegram->read_value(option[12], 27 + i); // modulation
|
||||
Helpers::smallitoa(&option[12], (uint16_t)option[12]);
|
||||
has_update(hpInput[i].option, option, 16);
|
||||
}
|
||||
// input 4 <inv>[<comp><aux><cool><heat><dhw><pv><prot><pres><mod>]
|
||||
uint8_t index4[] = {42, 43, 44, 45, 46, 47, 52, 50, 49, 48};
|
||||
for (uint8_t j = 0; j < 9; j++) {
|
||||
option[j] = hpInput[3].option[j] - '0';
|
||||
telegram->read_value(option[j], 42 + j);
|
||||
option[j] = option[j] ? '1' : '0';
|
||||
telegram->read_value(option[j], index4[j]);
|
||||
option[j] = option[j] == 1 ? '1' : '0';
|
||||
}
|
||||
option[9] = '\0'; // terminate string
|
||||
has_update(hpInput[3].option, option, 12);
|
||||
option[9] = atoi(&hpInput[3].option[9]);
|
||||
telegram->read_value(option[9], 48); // modulation
|
||||
Helpers::smallitoa(&option[9], (uint16_t)option[9]);
|
||||
has_update(hpInput[3].option, option, 13);
|
||||
}
|
||||
|
||||
// Boiler(0x08) -W-> Me(0x0B), HpHeaterConfig(0x0485)
|
||||
@@ -1405,7 +1414,7 @@ void Boiler::process_UBAErrorMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// data: displaycode(2), errornumber(2), year, month, hour, day, minute, duration(2), src-addr
|
||||
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] = {0};
|
||||
uint16_t codeNo = EMS_VALUE_SHORT_NOTSET;
|
||||
code[0] = telegram->message_data[0];
|
||||
@@ -1476,7 +1485,8 @@ void Boiler::process_UBAErrorMessage2(std::shared_ptr<const Telegram> telegram)
|
||||
snprintf(&code[3], sizeof(code) - 3, "(%d) %02d.%02d.%04d %02d:%02d - now", codeNo, start_day, start_month, start_year, start_hour, start_min);
|
||||
}
|
||||
} else { // no clock, the uptime is stored https://github.com/emsesp/EMS-ESP32/issues/121
|
||||
uint32_t starttime, endtime;
|
||||
uint32_t starttime = 0;
|
||||
uint32_t endtime = 0;
|
||||
telegram->read_value(starttime, 11, 3);
|
||||
telegram->read_value(endtime, 16, 3);
|
||||
snprintf(&code[3], sizeof(code) - 3, "(%d) @uptime %d - %d min", codeNo, starttime, endtime);
|
||||
@@ -1520,12 +1530,12 @@ void Boiler::process_HpSilentMode(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, wwAltOpPrioWw_, 3); // range 30-120 minutes on Buderus WSW196i
|
||||
has_update(telegram, silentMode_, 10); // enum off-auto-on
|
||||
has_update(telegram, minTempSilent_, 11);
|
||||
has_update(telegram, hpHystHeat_, 37); // is / 5
|
||||
has_update(telegram, hpHystCool_, 35); // is / 5, maybe offset swapped with pool
|
||||
has_update(telegram, hpHystPool_, 33); // is / 5
|
||||
has_update(telegram, hpHystHeat_, 37); // is / 5
|
||||
has_update(telegram, hpHystCool_, 35); // is / 5, maybe offset swapped with pool
|
||||
has_update(telegram, hpHystPool_, 33); // is / 5
|
||||
has_update(telegram, hpCircPumpWw_, 46);
|
||||
has_update(telegram, silentFrom_, 52); // in steps of 15 min
|
||||
has_update(telegram, silentTo_, 53); // in steps of 15 min
|
||||
has_update(telegram, silentFrom_, 52); // in steps of 15 min
|
||||
has_update(telegram, silentTo_, 53); // in steps of 15 min
|
||||
}
|
||||
|
||||
// Boiler(0x08) -B-> All(0x00), ?(0x0488), data: 8E 00 00 00 00 00 01 03
|
||||
@@ -1546,7 +1556,7 @@ void Boiler::process_HpAdditionalHeater(std::shared_ptr<const Telegram> telegram
|
||||
has_update(telegram, manDefrost_, 0); // off/on
|
||||
has_update(telegram, auxHeaterOnly_, 1);
|
||||
has_update(telegram, auxHeaterOff_, 2);
|
||||
has_update(telegram, auxHeatMode_, 4); // eco/comfort
|
||||
has_update(telegram, auxHeatMode_, 4); // eco/comfort
|
||||
has_update(telegram, tempParMode_, 5);
|
||||
has_update(telegram, auxMaxLimit_, 14); // is * 10
|
||||
has_update(telegram, auxLimitStart_, 15); // is * 10
|
||||
@@ -1580,6 +1590,39 @@ void Boiler::process_HpSettings3(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, elHeatStep3_, 9);
|
||||
}
|
||||
|
||||
// HIU unit
|
||||
|
||||
// boiler(0x08) -B-> All(0x00), ?(0x0779), data: 06 05 01 01 AD 02 EF FF FF 00 00 7F FF
|
||||
void Boiler::process_HIUMonitor(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, netFlowTemp_, 5); // is * 10
|
||||
has_update(telegram, cwFlowRate_, 9); // is * 10
|
||||
}
|
||||
|
||||
// Boiler(0x08) -W-> ME(0x0x), ?(0x0772), data: 00 00 00 00 00
|
||||
void Boiler::process_HIUSettings(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, keepWarmTemp_, 1);
|
||||
has_update(telegram, setReturnTemp_, 2);
|
||||
}
|
||||
|
||||
// HIU Settings
|
||||
bool Boiler::set_keepWarmTemp(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x772, 1, v, 0x772);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Boiler::set_returnTemp(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x772, 2, v, 0x772);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hybrid heatpump with telegram 0xBB is readable and writeable in boiler and thermostat
|
||||
* thermostat always overwrites settings in boiler
|
||||
@@ -1731,9 +1774,8 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
// no write/verify if there is no change, see https://github.com/emsesp/EMS-ESP32/issues/654
|
||||
// put it to end of tx-queue
|
||||
if (v == selFlowTemp_) {
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, (uint8_t *) &v, 1, 0, false);
|
||||
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, (uint8_t *)&v, 1, 0, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2322,38 +2364,41 @@ bool Boiler::set_emergency_ops(const char * value, const int8_t id) {
|
||||
}
|
||||
|
||||
bool Boiler::set_HpInLogic(const char * value, const int8_t id) {
|
||||
if (id == 0 || id > 4) {
|
||||
if (id == 0 || id > 4 || strlen(value) > 15) {
|
||||
return false;
|
||||
}
|
||||
bool v;
|
||||
if (Helpers::value2bool(value, v)) {
|
||||
if (strlen(value) == 1 && Helpers::value2bool(value, v)) {
|
||||
write_command(0x486, id == 4 ? 42 : id - 1, v ? 1 : 0, 0x486);
|
||||
return true;
|
||||
}
|
||||
if (strlen(value) == 11 && id != 4) {
|
||||
uint8_t v[11];
|
||||
for (uint8_t i = 0; i < 11; i++) {
|
||||
v[i] = value[i] - '0';
|
||||
if (v[i] > 1) {
|
||||
return false;
|
||||
char option[] = {"xxxxxxxxxxxxxxx"};
|
||||
strncpy(option, value, strlen(value)); // copy without termination
|
||||
// inputs 1,2,3 <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv><prot><pres><mod>]
|
||||
if (id < 4) {
|
||||
uint8_t index[] = {0, 3, 6, 9, 12, 15, 18, 21, 24, 39, 36, 30};
|
||||
for (uint8_t i = 0; i < 12; i++) {
|
||||
if (option[i] == '0' || option[i] == '1') {
|
||||
write_command(0x486, index[i] + id - 1, option[i] - '0');
|
||||
}
|
||||
write_command(0x486, i * 3 + id - 1, v[i]);
|
||||
}
|
||||
if (option[12] >= '0' && option[12] <= '9') {
|
||||
write_command(0x486, 27, (uint8_t)atoi(&option[12]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// input 4
|
||||
if (strlen(value) == 8 && id == 4) {
|
||||
uint8_t v[11];
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
v[i] = value[i] - '0';
|
||||
if (v[i] > 1) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x486, 42 + i, v[i]);
|
||||
|
||||
// input 4: <inv>[<comp><aux><cool><heat><dhw><pv><prot><pres><mod>]
|
||||
uint8_t index4[] = {42, 43, 44, 45, 46, 47, 52, 50, 49};
|
||||
for (uint8_t i = 0; i < 9; i++) {
|
||||
if (option[i] == '0' || option[i] == '1') {
|
||||
write_command(0x486, index4[i], option[i] - '0');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (option[9] >= '0' && option[9] <= '9') {
|
||||
write_command(0x486, 48, (uint8_t)atoi(&option[9]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Boiler::set_maxHeat(const char * value, const int8_t id) {
|
||||
@@ -2596,17 +2641,4 @@ bool Boiler::set_wwAltOpPrio(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Boiler::set_forceHeatingOff(const char * value, const int8_t id) {
|
||||
bool v;
|
||||
if (Helpers::value2bool(value, v)) {
|
||||
has_update(forceHeatingOff_, v);
|
||||
if (!v && Helpers::hasValue(heatingTemp_)) {
|
||||
uint8_t data[] = {heatingTemp_, (Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100), (Helpers::hasValue(pumpModMax_) ? pumpModMax_ : (uint8_t)0), 0};
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -35,7 +35,7 @@ class Boiler : public EMSdevice {
|
||||
return (flags() & 0x0F);
|
||||
}
|
||||
|
||||
void check_active(const bool force = false);
|
||||
void check_active();
|
||||
|
||||
uint8_t boilerState_ = EMS_VALUE_UINT_NOTSET; // Boiler state flag - FOR INTERNAL USE
|
||||
|
||||
@@ -87,17 +87,13 @@ class Boiler : public EMSdevice {
|
||||
uint32_t wwWorkM_; // DHW minutes
|
||||
int8_t wwHystOn_;
|
||||
int8_t wwHystOff_;
|
||||
uint16_t wwMixerTemp_; // mixing temperature
|
||||
uint16_t wwCylMiddleTemp_; // Cyl middle temperature (TS3)
|
||||
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
|
||||
uint16_t wwMixerTemp_; // mixing temperature
|
||||
uint16_t wwCylMiddleTemp_; // Cyl middle temperature (TS3)
|
||||
uint16_t wwSolarTemp_;
|
||||
uint8_t wwAlternatingOper_; // alternating operation on/off
|
||||
uint8_t wwAltOpPrioHeat_; // alternating operation, prioritise heat time
|
||||
uint8_t wwAltOpPrioWw_; // alternating operation, prioritise dhw time
|
||||
|
||||
// special function
|
||||
uint8_t forceHeatingOff_;
|
||||
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
|
||||
|
||||
uint8_t wwAltOpPrioHeat_; // alternating operation, prioritize heat time
|
||||
uint8_t wwAltOpPrioWw_; // alternating operation, prioritize dhw time
|
||||
|
||||
// main
|
||||
uint8_t reset_; // for reset command
|
||||
@@ -212,7 +208,7 @@ class Boiler : public EMSdevice {
|
||||
// Inputs
|
||||
struct {
|
||||
uint8_t state;
|
||||
char option[12]; // logic, block_comp, block_dhw, block_heat, block_cool, overheat_protect, evu_blocktime1,2,3, block_heater, Solar
|
||||
char option[16]; // logic, block_comp, block_dhw, block_heat, block_cool, overheat_protect, evu_blocktime1,2,3, block_heater, Solar, brine lowpressure, brine pump modulation
|
||||
} hpInput[4];
|
||||
|
||||
// Heater limits
|
||||
@@ -256,6 +252,12 @@ class Boiler : public EMSdevice {
|
||||
uint8_t elHeatStep2_;
|
||||
uint8_t elHeatStep3_;
|
||||
|
||||
// HIU
|
||||
uint16_t cwFlowRate_; // cold water flow rate *10
|
||||
uint16_t netFlowTemp_; // heat network flow temperature *10
|
||||
uint8_t keepWarmTemp_;
|
||||
uint8_t setReturnTemp_;
|
||||
|
||||
/*
|
||||
// Hybrid heatpump with telegram 0xBB is readable and writeable in boiler and thermostat
|
||||
// thermostat always overwrites settings in boiler
|
||||
@@ -310,6 +312,12 @@ class Boiler : public EMSdevice {
|
||||
void process_HpDhwSettings(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpSettings2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpSettings3(std::shared_ptr<const Telegram> telegram);
|
||||
// HIU
|
||||
void process_HIUSettings(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HIUMonitor(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
bool set_keepWarmTemp(const char * value, const int8_t id);
|
||||
bool set_returnTemp(const char * value, const int8_t id);
|
||||
|
||||
// commands - none of these use the additional id parameter
|
||||
bool set_ww_mode(const char * value, const int8_t id);
|
||||
@@ -451,8 +459,6 @@ class Boiler : public EMSdevice {
|
||||
inline bool set_wwAltOpPrioWw(const char * value, const int8_t id) {
|
||||
return set_wwAltOpPrio(value, 3);
|
||||
}
|
||||
bool set_forceHeatingOff(const char * value, const int8_t id);
|
||||
|
||||
/*
|
||||
bool set_hybridStrategy(const char * value, const int8_t id);
|
||||
bool set_switchOverTemp(const char * value, const int8_t id);
|
||||
@@ -466,4 +472,4 @@ class Boiler : public EMSdevice {
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -212,10 +212,11 @@ void Heatpump::process_HPSettings(std::shared_ptr<const Telegram> telegram) {
|
||||
// Broadcast (0x099C), data: 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 76 00 00
|
||||
// data: 00 2B 00 03 04 13 00 00 00 00 00 02 02 02 (offset 24)
|
||||
void Heatpump::process_HPComp(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, hpCompSpd_, 51);
|
||||
has_update(telegram, hpCompSpd_, 15);
|
||||
}
|
||||
|
||||
// 0x999 HPFunctionTest
|
||||
// HPFunctionTest(0x0999), data: 00 00 00 32 00 00 00 00 00 00 00
|
||||
void Heatpump::process_HPFunctionTest(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, airPurgeMode_, 0);
|
||||
has_update(telegram, heatPumpOutput_, 2);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -52,7 +52,7 @@ class Heatpump : public EMSdevice {
|
||||
uint8_t heatDrainPan_;
|
||||
uint8_t heatCable_;
|
||||
|
||||
// HM200 temperature
|
||||
// HM200 temperatures
|
||||
int16_t flowTemp_; // TH1
|
||||
int16_t retTemp_; // TH2
|
||||
int16_t sysRetTemp_; // TH3
|
||||
@@ -84,7 +84,6 @@ class Heatpump : public EMSdevice {
|
||||
bool set_energyPriceEl(const char * value, const int8_t id);
|
||||
bool set_energyPricePV(const char * value, const int8_t id);
|
||||
bool set_switchOverTemp(const char * value, const int8_t id);
|
||||
|
||||
bool set_airPurgeMode(const char * value, const int8_t id);
|
||||
bool set_heatPumpOutput(const char * value, const int8_t id);
|
||||
bool set_coolingCircuit(const char * value, const int8_t id);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,155 +24,53 @@ REGISTER_FACTORY(Heatsource, EMSdevice::DeviceType::HEATSOURCE);
|
||||
|
||||
Heatsource::Heatsource(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
if (device_id >= EMSdevice::EMS_DEVICE_ID_AHS1 && device_id < EMSdevice::EMS_DEVICE_ID_HS1) {
|
||||
uint8_t ahs = device_id - EMSdevice::EMS_DEVICE_ID_AHS1; // heating source id, count from 0
|
||||
// AM200 alternative heatsource
|
||||
if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER || (device_id >= EMSdevice::EMS_DEVICE_ID_AHS1 && device_id < EMSdevice::EMS_DEVICE_ID_HS1)) {
|
||||
uint8_t tag = device_id == EMSdevice::EMS_DEVICE_ID_BOILER
|
||||
? DeviceValueTAG::TAG_DEVICE_DATA
|
||||
: DeviceValueTAG::TAG_AHS1 + device_id - EMSdevice::EMS_DEVICE_ID_AHS1; // heating source id, count from 0
|
||||
register_telegram_type(0x54D, "AmTemperatures", false, MAKE_PF_CB(process_amTempMessage));
|
||||
register_telegram_type(0x54E, "AmStatus", false, MAKE_PF_CB(process_amStatusMessage));
|
||||
register_telegram_type(0x54F, "AmCommand", false, MAKE_PF_CB(process_amCommandMessage)); // not broadcasted, but actually not used
|
||||
register_telegram_type(0x550, "AmExtra", false, MAKE_PF_CB(process_amExtraMessage));
|
||||
register_telegram_type(0x54C, "AmSettings", true, MAKE_PF_CB(process_amSettingMessage)); // not broadcasted
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&curFlowTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(sysFlowTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&retTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(sysRetTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&aFlowTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(aFlowTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&aRetTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(aRetTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&cylTopTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(aCylTopTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&cylCenterTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(aCylCenterTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&cylBottomTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(aCylBottomTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&flueGasTemp_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(flueGasTemp),
|
||||
DeviceValueUOM::DEGREES);
|
||||
// register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &valveByPass_, DeviceValueType::BOOL, nullptr, FL_(valveByPass), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &valveBuffer_, DeviceValueType::UINT, FL_(valveBuffer), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &valveReturn_, DeviceValueType::UINT, FL_(valveReturn), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &aPumpMod_, DeviceValueType::UINT, FL_(aPumpMod), DeviceValueUOM::PERCENT);
|
||||
// register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &heatSource_, DeviceValueType::BOOL, nullptr, FL_(heatSource), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &curFlowTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(sysFlowTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &retTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(sysRetTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &aFlowTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(aFlowTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &aRetTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(aRetTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &cylTopTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(aCylTopTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &cylCenterTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(aCylCenterTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &cylBottomTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(aCylBottomTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &flueGasTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(flueGasTemp), DeviceValueUOM::DEGREES);
|
||||
// register_device_value(tag, &valveByPass_, DeviceValueType::BOOL, nullptr, FL_(valveByPass), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &valveBuffer_, DeviceValueType::UINT, FL_(valveBuffer), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &valveReturn_, DeviceValueType::UINT, FL_(valveReturn), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &aPumpMod_, DeviceValueType::UINT, FL_(aPumpMod), DeviceValueUOM::PERCENT);
|
||||
// register_device_value(tag, &heatSource_, DeviceValueType::BOOL, nullptr, FL_(heatSource), DeviceValueUOM::NONE);
|
||||
// Settings:
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&vr2Config_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_vr2Config),
|
||||
FL_(vr2Config),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_vr2Config));
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&ahsActivated_,
|
||||
DeviceValueType::BOOL,
|
||||
FL_(ahsActivated),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_ahsActivated));
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&aPumpConfig_,
|
||||
DeviceValueType::BOOL,
|
||||
FL_(aPumpConfig),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_aPumpConfig));
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&aPumpSignal_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_aPumpSignal),
|
||||
FL_(aPumpSignal),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_aPumpSignal));
|
||||
register_device_value(
|
||||
DeviceValueTAG::TAG_AHS1 + ahs, &aPumpMin_, DeviceValueType::UINT, FL_(aPumpMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_aPumpMin), 12, 50);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &tempRise_, DeviceValueType::BOOL, FL_(tempRise), DeviceValueUOM::NONE, MAKE_CF_CB(set_tempRise));
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&setReturnTemp_,
|
||||
DeviceValueType::UINT,
|
||||
FL_(setReturnTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_setReturnTemp),
|
||||
40,
|
||||
75);
|
||||
register_device_value(
|
||||
DeviceValueTAG::TAG_AHS1 + ahs, &mixRuntime_, DeviceValueType::USHORT, FL_(mixRuntime), DeviceValueUOM::SECONDS, MAKE_CF_CB(set_mixRuntime), 0, 600);
|
||||
register_device_value(
|
||||
DeviceValueTAG::TAG_AHS1 + ahs, &setFlowTemp_, DeviceValueType::UINT, FL_(setFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_setFlowTemp), 40, 75);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&bufBypass_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_bufBypass),
|
||||
FL_(bufBypass),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_bufBypass));
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&bufMixRuntime_,
|
||||
DeviceValueType::USHORT,
|
||||
FL_(bufMixRuntime),
|
||||
DeviceValueUOM::SECONDS,
|
||||
MAKE_CF_CB(set_bufMixRuntime),
|
||||
0,
|
||||
600);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&bufConfig_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_bufConfig),
|
||||
FL_(bufConfig),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_bufConfig));
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&blockMode_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_blockMode),
|
||||
FL_(blockMode),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_blockMode));
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs,
|
||||
&blockTerm_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_blockTerm),
|
||||
FL_(blockTerm),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_blockTerm));
|
||||
register_device_value(
|
||||
DeviceValueTAG::TAG_AHS1 + ahs, &blockHyst_, DeviceValueType::INT, FL_(blockHyst), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_blockHyst), 0, 50);
|
||||
register_device_value(
|
||||
DeviceValueTAG::TAG_AHS1 + ahs, &releaseWait_, DeviceValueType::UINT, FL_(releaseWait), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_releaseWait), 0, 240);
|
||||
register_device_value(tag, &vr2Config_, DeviceValueType::ENUM, FL_(enum_vr2Config), FL_(vr2Config), DeviceValueUOM::NONE, MAKE_CF_CB(set_vr2Config));
|
||||
register_device_value(tag, &ahsActivated_, DeviceValueType::BOOL, FL_(ahsActivated), DeviceValueUOM::NONE, MAKE_CF_CB(set_ahsActivated));
|
||||
register_device_value(tag, &aPumpConfig_, DeviceValueType::BOOL, FL_(aPumpConfig), DeviceValueUOM::NONE, MAKE_CF_CB(set_aPumpConfig));
|
||||
register_device_value(tag, &aPumpSignal_, DeviceValueType::ENUM, FL_(enum_aPumpSignal), FL_(aPumpSignal), DeviceValueUOM::NONE, MAKE_CF_CB(set_aPumpSignal));
|
||||
register_device_value(tag, &aPumpMin_, DeviceValueType::UINT, FL_(aPumpMin), DeviceValueUOM::PERCENT, MAKE_CF_CB(set_aPumpMin), 12, 50);
|
||||
register_device_value(tag, &tempRise_, DeviceValueType::BOOL, FL_(tempRise), DeviceValueUOM::NONE, MAKE_CF_CB(set_tempRise));
|
||||
register_device_value(tag, &setReturnTemp_, DeviceValueType::UINT, FL_(setReturnTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_setReturnTemp), 40, 75);
|
||||
register_device_value(tag, &mixRuntime_, DeviceValueType::USHORT, FL_(mixRuntime), DeviceValueUOM::SECONDS, MAKE_CF_CB(set_mixRuntime), 0, 600);
|
||||
register_device_value(tag, &setFlowTemp_, DeviceValueType::UINT, FL_(setFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_setFlowTemp), 40, 75);
|
||||
register_device_value(tag, &bufBypass_, DeviceValueType::ENUM, FL_(enum_bufBypass), FL_(bufBypass), DeviceValueUOM::NONE, MAKE_CF_CB(set_bufBypass));
|
||||
register_device_value(tag, &bufMixRuntime_, DeviceValueType::USHORT, FL_(bufMixRuntime), DeviceValueUOM::SECONDS, MAKE_CF_CB(set_bufMixRuntime), 0, 600);
|
||||
register_device_value(tag, &bufConfig_, DeviceValueType::ENUM, FL_(enum_bufConfig), FL_(bufConfig), DeviceValueUOM::NONE, MAKE_CF_CB(set_bufConfig));
|
||||
register_device_value(tag, &blockMode_, DeviceValueType::ENUM, FL_(enum_blockMode), FL_(blockMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_blockMode));
|
||||
register_device_value(tag, &blockTerm_, DeviceValueType::ENUM, FL_(enum_blockTerm), FL_(blockTerm), DeviceValueUOM::NONE, MAKE_CF_CB(set_blockTerm));
|
||||
register_device_value(tag, &blockHyst_, DeviceValueType::INT, FL_(blockHyst), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_blockHyst), 0, 50);
|
||||
register_device_value(tag, &releaseWait_, DeviceValueType::UINT, FL_(releaseWait), DeviceValueUOM::MINUTES, MAKE_CF_CB(set_releaseWait), 0, 240);
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &burner_, DeviceValueType::BOOL, FL_(burner), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &aPump_, DeviceValueType::BOOL, FL_(aPump), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &heatRequest_, DeviceValueType::UINT, FL_(heatRequest), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &blockRemain_, DeviceValueType::UINT, FL_(blockRemain), DeviceValueUOM::MINUTES);
|
||||
register_device_value(DeviceValueTAG::TAG_AHS1 + ahs, &blockRemainWw_, DeviceValueType::UINT, FL_(blockRemainWw), DeviceValueUOM::MINUTES);
|
||||
register_device_value(tag, &burner_, DeviceValueType::BOOL, FL_(burner), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &aPump_, DeviceValueType::BOOL, FL_(aPump), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &heatRequest_, DeviceValueType::UINT, FL_(heatRequest), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &blockRemain_, DeviceValueType::UINT, FL_(blockRemain), DeviceValueUOM::MINUTES);
|
||||
register_device_value(tag, &blockRemainWw_, DeviceValueType::UINT, FL_(blockRemainWw), DeviceValueUOM::MINUTES);
|
||||
}
|
||||
|
||||
// cascaded heating sources, only some values per individual heatsource (hs)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -170,7 +170,7 @@ void Mixer::process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> teleg
|
||||
void Mixer::process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, flowTempHc_, 0); // is * 10
|
||||
has_bitupdate(telegram, pumpStatus_, 2, 0);
|
||||
has_update(telegram, status_, 11); // temp status
|
||||
has_update(telegram, status_, 11); // temp status
|
||||
}
|
||||
|
||||
// Mixer IPM - 0x010C
|
||||
@@ -220,7 +220,7 @@ void Mixer::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, flowTempHc_, 1); // is * 10
|
||||
has_bitupdate(telegram, pumpStatus_, 3, 2); // is 0 or 0x64 (100%), check only bit 2
|
||||
has_update(telegram, flowSetTemp_, 0);
|
||||
has_update(telegram, status_, 4); // valve status -100 to 100
|
||||
has_update(telegram, status_, 4); // valve status -100 to 100
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -347,7 +347,9 @@ bool Mixer::set_pump(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
write_command(0xAC, 1, b ? 0x64 : 0, 0xAB);
|
||||
// AC telegram can only be written with offset 0
|
||||
uint8_t dat[2] = {flowSetTemp_, b ? (uint8_t)0x64 : (uint8_t)0};
|
||||
write_command(0xAC, 0, dat, sizeof(dat), 0xAB);
|
||||
return true;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_IPM) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -685,9 +685,9 @@ void Solar::process_SM100Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, cylBottomTemp2_, 16); // is *10 - TS5: Temperature sensor 2 cylinder, bottom, or swimming pool
|
||||
has_update(telegram, heatExchangerTemp_, 20); // is *10 - TS6: Heat exchanger temperature sensor
|
||||
|
||||
has_update(telegram, collector2Temp_, 6); // is *10 - TS7: Temperature sensor for collector array 2
|
||||
has_update(telegram, cylMiddleTemp_, 8); // is *10 - TS14: cylinder middle temperature
|
||||
has_update(telegram, retHeatAssist_, 10); // is *10 - TS15: return temperature heating assistance
|
||||
has_update(telegram, collector2Temp_, 6); // is *10 - TS7: Temperature sensor for collector array 2
|
||||
has_update(telegram, cylMiddleTemp_, 8); // is *10 - TS14: cylinder middle temperature
|
||||
has_update(telegram, retHeatAssist_, 10); // is *10 - TS15: return temperature heating assistance
|
||||
}
|
||||
|
||||
// SM100wwTemperature - 0x07D6
|
||||
@@ -749,8 +749,8 @@ void Solar::process_SM100Monitor2(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(heatCntFlowTemp_, 0)); // is *10
|
||||
has_update(telegram->read_value(heatCntRetTemp_, 2)); // is *10
|
||||
has_update(telegram->read_value(heatCnt_, 12));
|
||||
has_update(telegram->read_value(swapRetTemp_, 6)); // is *10
|
||||
has_update(telegram->read_value(swapFlowTemp_, 8)); // is *10
|
||||
has_update(telegram->read_value(swapRetTemp_, 6)); // is *10
|
||||
has_update(telegram->read_value(swapFlowTemp_, 8)); // is *10
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
@@ -879,7 +879,7 @@ void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram)
|
||||
has_update(telegram, collectorTemp_, 4); // Collector Temperature
|
||||
has_update(telegram, cylBottomTemp_, 6); // Temperature Bottom of Solar Boiler cyl
|
||||
uint16_t Wh = energyLastHour_ / 10;
|
||||
telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10
|
||||
telegram->read_value(Wh, 2); // Solar Energy produced in last hour only ushort, is not * 10
|
||||
if (energyLastHour_ != Wh * 10) {
|
||||
energyLastHour_ = Wh * 10;
|
||||
has_update(&energyLastHour_);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -58,9 +58,9 @@ class Solar : public EMSdevice {
|
||||
uint32_t energyLastHour_;
|
||||
uint32_t energyToday_;
|
||||
uint32_t energyTotal_;
|
||||
uint32_t pumpWorkTime_; // Total solar pump operating time
|
||||
uint32_t pump2WorkTime_; // Total solar pump 2 operating time
|
||||
uint32_t m1WorkTime_; // differential control work time
|
||||
uint32_t pumpWorkTime_; // Total solar pump operating time
|
||||
uint32_t pump2WorkTime_; // Total solar pump 2 operating time
|
||||
uint32_t m1WorkTime_; // differential control work time
|
||||
uint8_t cylHeated_;
|
||||
uint8_t collectorShutdown_; // Collector shutdown on/off
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -136,6 +136,8 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
summer_typeids = {0x02AF, 0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6};
|
||||
curve_typeids = {0x029B, 0x029C, 0x029D, 0x029E, 0x029F, 0x02A0, 0x02A1, 0x02A2};
|
||||
summer2_typeids = {0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0476, 0x0477, 0x0478};
|
||||
hp_typeids = {0x0467, 0x0468, 0x0469, 0x046A};
|
||||
hpmode_typeids = {0x0291, 0x0292, 0x0293, 0x0294};
|
||||
for (uint8_t i = 0; i < monitor_typeids.size(); i++) {
|
||||
register_telegram_type(monitor_typeids[i], "RC300Monitor", false, MAKE_PF_CB(process_RC300Monitor));
|
||||
register_telegram_type(set_typeids[i], "RC300Set", false, MAKE_PF_CB(process_RC300Set));
|
||||
@@ -145,6 +147,8 @@ Thermostat::Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_i
|
||||
}
|
||||
for (uint8_t i = 0; i < set2_typeids.size(); i++) {
|
||||
register_telegram_type(set2_typeids[i], "RC300Set2", false, MAKE_PF_CB(process_RC300Set2));
|
||||
register_telegram_type(hp_typeids[i], "HPSet", false, MAKE_PF_CB(process_HPSet));
|
||||
register_telegram_type(hpmode_typeids[i], "HPMode", true, MAKE_PF_CB(process_HPMode));
|
||||
}
|
||||
register_telegram_type(0x2F5, "RC300WWmode", true, MAKE_PF_CB(process_RC300WWmode));
|
||||
register_telegram_type(0x31B, "RC300WWtemp", true, MAKE_PF_CB(process_RC300WWtemp));
|
||||
@@ -216,9 +220,7 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(const ui
|
||||
return heating_circuit;
|
||||
}
|
||||
}
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("Heating circuit not fond on device 0x%02X", device_id());
|
||||
#endif
|
||||
return nullptr; // not found
|
||||
}
|
||||
|
||||
@@ -308,6 +310,25 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
||||
}
|
||||
}
|
||||
|
||||
// not found, search heatpump message types
|
||||
if (hc_num == 0) {
|
||||
for (uint8_t i = 0; i < hp_typeids.size(); i++) {
|
||||
if (hp_typeids[i] == telegram->type_id) {
|
||||
hc_num = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hc_num == 0) {
|
||||
for (uint8_t i = 0; i < hpmode_typeids.size(); i++) {
|
||||
if (hpmode_typeids[i] == telegram->type_id) {
|
||||
hc_num = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not found, search device-id types for remote thermostats
|
||||
if (hc_num == 0 && telegram->src >= 0x18 && telegram->src <= 0x1F) {
|
||||
hc_num = telegram->src - 0x17;
|
||||
@@ -385,6 +406,9 @@ std::shared_ptr<Thermostat::HeatingCircuit> Thermostat::heating_circuit(std::sha
|
||||
if (summer2_typeids.size()) {
|
||||
toggle_fetch(summer2_typeids[hc_num - 1], toggle_);
|
||||
}
|
||||
if (hp_typeids.size()) {
|
||||
toggle_fetch(hp_typeids[hc_num - 1], toggle_);
|
||||
}
|
||||
|
||||
return new_hc; // return back point to new HC object
|
||||
}
|
||||
@@ -398,9 +422,9 @@ void Thermostat::add_ha_climate(std::shared_ptr<HeatingCircuit> hc) const {
|
||||
|
||||
if (Helpers::hasValue(hc->selTemp) && is_readable(&hc->selTemp)) {
|
||||
if (Helpers::hasValue(hc->roomTemp) && is_readable(&hc->roomTemp)) {
|
||||
hc->climate = 1;
|
||||
hc->climate = 1; // use roomTemp as we have a sensor
|
||||
} else {
|
||||
hc->climate = 0;
|
||||
hc->climate = 0; // use selTemp, as there is no sensor present in the thermostat
|
||||
}
|
||||
} else {
|
||||
hc->climate = EMS_VALUE_UINT_NOTSET;
|
||||
@@ -637,8 +661,8 @@ void Thermostat::process_RC20Set_2(std::shared_ptr<const Telegram> telegram) {
|
||||
return;
|
||||
}
|
||||
has_update(telegram, hc->heatingtype, 0);
|
||||
has_update(telegram, hc->nighttemp, 1); // is * 2,
|
||||
has_update(telegram, hc->daytemp, 2); // is * 2,
|
||||
has_update(telegram, hc->nighttemp, 1); // is * 2,
|
||||
has_update(telegram, hc->daytemp, 2); // is * 2,
|
||||
has_update(telegram, hc->mode, 3);
|
||||
has_enumupdate(telegram, hc->program, 11, 1); // 1 .. 9 predefined programs
|
||||
has_update(telegram, hc->tempautotemp, 13);
|
||||
@@ -774,8 +798,8 @@ void Thermostat::process_RC35wwSettings(std::shared_ptr<const Telegram> telegram
|
||||
has_update(telegram, wwDisinfecting_, 4); // 0-off, 0xFF on
|
||||
has_update(telegram, wwDisinfectDay_, 5); // 0-6 Day of week, 7 every day
|
||||
has_update(telegram, wwDisinfectHour_, 6);
|
||||
has_update(telegram, wwMaxTemp_, 8); // Limiter 60 degrees
|
||||
has_update(telegram, wwOneTimeKey_, 9); // 0-off, 0xFF on
|
||||
has_update(telegram, wwMaxTemp_, 8); // Limiter 60 degrees
|
||||
has_update(telegram, wwOneTimeKey_, 9); // 0-off, 0xFF on
|
||||
}
|
||||
|
||||
// Settings WW 0x3A - RC30
|
||||
@@ -927,7 +951,7 @@ void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram)
|
||||
return;
|
||||
}
|
||||
|
||||
has_update(telegram, hc->roomTemp, 0); // is * 10
|
||||
has_update(telegram, hc->roomTemp, 0); // is * 10
|
||||
has_bitupdate(telegram, hc->modetype, 10, 1);
|
||||
has_bitupdate(telegram, hc->mode, 10, 0); // bit 1, mode (auto=1 or manual=0)
|
||||
|
||||
@@ -952,6 +976,7 @@ void Thermostat::process_RC300Monitor(std::shared_ptr<const Telegram> telegram)
|
||||
}
|
||||
has_update(telegram, hc->targetflowtemp, 4);
|
||||
has_update(telegram, hc->curroominfl, 27);
|
||||
has_update(telegram, hc->coolingon, 32);
|
||||
|
||||
add_ha_climate(hc);
|
||||
}
|
||||
@@ -988,13 +1013,14 @@ void Thermostat::process_RC300Set(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
has_update(hc->tempautotemp, tat);
|
||||
|
||||
has_update(telegram, hc->manualtemp, 10); // is * 2
|
||||
has_enumupdate(telegram, hc->program, 11, 1); // timer program 1 or 2
|
||||
has_update(telegram, hc->manualtemp, 10); // is * 2
|
||||
has_enumupdate(telegram, hc->program, 11, 1); // timer program 1 or 2
|
||||
|
||||
has_enumupdate(telegram, hc->reducemode, 5, 1); // 1-outdoor temp threshold, 2-room temp threshold, 3-reduced mode
|
||||
has_update(telegram, hc->reducetemp, 9);
|
||||
has_update(telegram, hc->noreducetemp, 12);
|
||||
has_update(telegram, hc->remoteseltemp, 17); // see https://github.com/emsesp/EMS-ESP32/issues/590
|
||||
has_update(telegram, hc->cooling, 28);
|
||||
}
|
||||
|
||||
// types 0x2AF ff
|
||||
@@ -1071,7 +1097,7 @@ void Thermostat::process_RC300WWtemp(std::shared_ptr<const Telegram> telegram) {
|
||||
// RC300WWmode(0x2F5), data: 01 FF 04 00 00 00 08 05 00 08 04 00 00 00 00 00 00 00 00 00 01
|
||||
void Thermostat::process_RC300WWmode(std::shared_ptr<const Telegram> telegram) {
|
||||
// circulation pump see: https://github.com/Th3M3/buderus_ems-wiki/blob/master/Einstellungen%20der%20Bedieneinheit%20RC310.md
|
||||
has_update(telegram, wwCircPump_, 1); // FF=off, 0=on ?
|
||||
has_update(telegram, wwCircPump_, 1); // FF=off, 0=on ?
|
||||
|
||||
has_update(telegram, wwMode_, 2); // 0=off, 1=low, 2=high, 3=auto, 4=own prog
|
||||
has_update(telegram, wwCircMode_, 3); // 0=off, 1=on, 2=auto, 4=own?
|
||||
@@ -1114,8 +1140,8 @@ void Thermostat::process_RC300Settings(std::shared_ptr<const Telegram> telegram)
|
||||
// 0x2CC - e.g. wwprio for RC310 hcx parameter
|
||||
void Thermostat::process_RC300Set2(std::shared_ptr<const Telegram> telegram) {
|
||||
// typeids are not in a raw. hc:0x2CC, hc2: 0x2CE for RC310
|
||||
// telegram is either offset 3 with data lenght of 1 and values 0/1 (radiators) - 10 0B FF 03 01 CC 01 F6
|
||||
// or offset 0 with data lenght of 6 bytes - offset 3 values are 0x00 or 0xFF - 10 0B FF 00 01 CE FF 13 0A FF 1E 00 20
|
||||
// telegram is either offset 3 with data length of 1 and values 0/1 (radiators) - 10 0B FF 03 01 CC 01 F6
|
||||
// or offset 0 with data length of 6 bytes - offset 3 values are 0x00 or 0xFF - 10 0B FF 00 01 CE FF 13 0A FF 1E 00 20
|
||||
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||
if (hc == nullptr) {
|
||||
@@ -1130,6 +1156,26 @@ void Thermostat::process_RC300Floordry(std::shared_ptr<const Telegram> telegram)
|
||||
has_update(telegram, floordrytemp_, 1);
|
||||
}
|
||||
|
||||
// 0x291 ff. HP mode
|
||||
void Thermostat::process_HPMode(std::shared_ptr<const Telegram> telegram) {
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||
if (hc == nullptr) {
|
||||
return;
|
||||
}
|
||||
has_update(telegram, hc->hpmode, 5);
|
||||
}
|
||||
|
||||
// 0x467 ff HP settings
|
||||
void Thermostat::process_HPSet(std::shared_ptr<const Telegram> telegram) {
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(telegram);
|
||||
if (hc == nullptr) {
|
||||
return;
|
||||
}
|
||||
has_update(telegram, hc->dewoffset, 4); // 7-35°C
|
||||
has_update(telegram, hc->roomtempdiff, 3); // 1-10K
|
||||
has_update(telegram, hc->hpminflowtemp, 0); // 2-10K
|
||||
}
|
||||
|
||||
// type 0x41 - data from the RC30 thermostat(0x10) - 14 bytes long
|
||||
// RC30Monitor(0x41), data: 80 20 00 AC 00 00 00 02 00 05 09 00 AC 00
|
||||
void Thermostat::process_RC30Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||
@@ -1161,11 +1207,11 @@ void Thermostat::process_RC30Set(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, mixingvalves_, 17); // Number of Mixing Valves: (0x00=0, 0x01=1, 0x02=2)
|
||||
has_update(telegram, brightness_, 18); // Screen brightness 0F=dark F1=light
|
||||
has_update(telegram, hc->mode, 23);
|
||||
has_update(telegram, hc->nofrosttemp, 24); // Set Temperature when mode is Off / 10 (e.g.: 0x0F = 7.5 degrees Celsius)
|
||||
has_update(telegram, heatingpid_, 25); // PID setting 00=1 01=2 02=3
|
||||
has_update(telegram, preheating_, 26); // Preheating in the clock program: (0x00 = off, 0xFF = on)
|
||||
has_update(telegram, hc->tempautotemp, 28); // is * 2
|
||||
has_update(telegram, hc->manualtemp, 29); // manualtemp is * 2
|
||||
has_update(telegram, hc->nofrosttemp, 24); // Set Temperature when mode is Off / 10 (e.g.: 0x0F = 7.5 degrees Celsius)
|
||||
has_update(telegram, heatingpid_, 25); // PID setting 00=1 01=2 02=3
|
||||
has_update(telegram, preheating_, 26); // Preheating in the clock program: (0x00 = off, 0xFF = on)
|
||||
has_update(telegram, hc->tempautotemp, 28); // is * 2
|
||||
has_update(telegram, hc->manualtemp, 29); // manualtemp is * 2
|
||||
}
|
||||
|
||||
// type 0x40 (HC1) - for reading the operating mode from the RC30 thermostat (0x10)
|
||||
@@ -1193,6 +1239,7 @@ void Thermostat::process_RC30Temp(std::shared_ptr<const Telegram> telegram) {
|
||||
// type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - data from the RC35 thermostat (0x10) - 16 bytes
|
||||
void Thermostat::process_RC35Monitor(std::shared_ptr<const Telegram> telegram) {
|
||||
// Check if heatingciruit is active, see https://github.com/emsesp/EMS-ESP32/issues/786
|
||||
// roomtemp is measured value or 7D00 on active hc's, zero on inactive
|
||||
uint16_t active = 0;
|
||||
if (!telegram->read_value(active, 3)) {
|
||||
return;
|
||||
@@ -1227,13 +1274,13 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
|
||||
return;
|
||||
}
|
||||
|
||||
has_update(telegram, hc->heatingtype, 0); // 0- off, 1-radiator, 2-convector, 3-floor
|
||||
has_update(telegram, hc->nighttemp, 1); // is * 2
|
||||
has_update(telegram, hc->daytemp, 2); // is * 2
|
||||
has_update(telegram, hc->holidaytemp, 3); // is * 2
|
||||
has_update(telegram, hc->roominfluence, 4); // is * 1
|
||||
has_update(telegram, hc->offsettemp, 6); // is * 2
|
||||
has_update(telegram, hc->mode, 7); // night, day, auto
|
||||
has_update(telegram, hc->heatingtype, 0); // 0- off, 1-radiator, 2-convector, 3-floor
|
||||
has_update(telegram, hc->nighttemp, 1); // is * 2
|
||||
has_update(telegram, hc->daytemp, 2); // is * 2
|
||||
has_update(telegram, hc->holidaytemp, 3); // is * 2
|
||||
has_update(telegram, hc->roominfluence, 4); // is * 1
|
||||
has_update(telegram, hc->offsettemp, 6); // is * 2
|
||||
has_update(telegram, hc->mode, 7); // night, day, auto
|
||||
|
||||
has_update(telegram, hc->wwprio, 21); // 0xFF for on
|
||||
has_update(telegram, hc->summertemp, 22); // is * 1
|
||||
@@ -1244,10 +1291,10 @@ void Thermostat::process_RC35Set(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, hc->control, 26); // 0-off, 1-RC20 (remote), 2-RC35
|
||||
has_update(telegram, hc->controlmode, 33); // 0-outdoortemp, 1-roomtemp
|
||||
has_update(telegram, hc->tempautotemp, 37);
|
||||
has_update(telegram, hc->noreducetemp, 38); // outdoor temperature for no reduce
|
||||
has_update(telegram, hc->reducetemp, 39); // temperature for off/reduce
|
||||
has_update(telegram, hc->vacreducetemp, 40); // temperature for off/reduce in vacations
|
||||
has_update(telegram, hc->vacreducemode, 41); // vacations reduce mode
|
||||
has_update(telegram, hc->noreducetemp, 38); // outdoor temperature for no reduce
|
||||
has_update(telegram, hc->reducetemp, 39); // temperature for off/reduce
|
||||
has_update(telegram, hc->vacreducetemp, 40); // temperature for off/reduce in vacations
|
||||
has_update(telegram, hc->vacreducemode, 41); // vacations reduce mode
|
||||
has_update(telegram, hc->minflowtemp, 16);
|
||||
|
||||
// RC35 stores values for floorheating in different position
|
||||
@@ -1369,7 +1416,7 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
bool ivtclock = (telegram->message_data[0] & 0x80) == 0x80; // dont sync ivt-clock, #439
|
||||
bool junkersclock = model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS;
|
||||
time_t ttime = mktime(tm_); // thermostat time
|
||||
time_t ttime = mktime(tm_); // thermostat time
|
||||
// correct thermostat clock if we have valid ntp time, and could write the command
|
||||
if (!ivtclock && !junkersclock && tset_ && EMSESP::system_.ntp_connected() && !EMSESP::system_.readonly_mode() && has_command(&dateTime_)) {
|
||||
double difference = difftime(now, ttime);
|
||||
@@ -1379,7 +1426,7 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
}
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!tset_ && tm_->tm_year > 110) { // emsesp clock not set, but thermostat clock
|
||||
if (!tset_ && tm_->tm_year > 110) { // emsesp clock not set, but thermostat clock
|
||||
if (ivtclock) {
|
||||
tm_->tm_isdst = -1; // determine dst
|
||||
ttime = mktime(tm_); // thermostat time
|
||||
@@ -1448,6 +1495,62 @@ void Thermostat::process_RCErrorMessage(std::shared_ptr<const Telegram> telegram
|
||||
*
|
||||
*/
|
||||
|
||||
// hp mode RC300
|
||||
bool Thermostat::set_roomtempdiff(const char * value, const int8_t id) {
|
||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||
if (hc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int v;
|
||||
if (Helpers::value2number(value, v)) {
|
||||
write_command(hp_typeids[hc->hc()], 3, v, hp_typeids[hc->hc()]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Thermostat::set_dewoffset(const char * value, const int8_t id) {
|
||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||
if (hc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int v;
|
||||
if (Helpers::value2number(value, v)) {
|
||||
write_command(hp_typeids[hc->hc()], 4, v, hp_typeids[hc->hc()]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Thermostat::set_hpminflowtemp(const char * value, const int8_t id) {
|
||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||
if (hc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int v;
|
||||
if (Helpers::value2temperature(value, v)) {
|
||||
write_command(hp_typeids[hc->hc()], 0, v, hp_typeids[hc->hc()]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Thermostat::set_hpmode(const char * value, const int8_t id) {
|
||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||
if (hc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
uint8_t v;
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_hpmode))) {
|
||||
return false;
|
||||
}
|
||||
write_command(hpmode_typeids[hc->hc()], 5, v, hpmode_typeids[hc->hc()]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0xBB Hybrid pump
|
||||
bool Thermostat::set_hybridStrategy(const char * value, const int8_t id) {
|
||||
uint8_t v;
|
||||
@@ -1869,6 +1972,20 @@ bool Thermostat::set_wwprio(const char * value, const int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// set cooling
|
||||
bool Thermostat::set_cooling(const char * value, const int8_t id) {
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit((id == -1) ? AUTO_HEATING_CIRCUIT : id);
|
||||
if (hc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool b;
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
write_command(set_typeids[hc->hc()], 28, b ? 0x01 : 0x00, set_typeids[hc->hc()]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// sets the thermostat ww circulation working mode, where mode is a string
|
||||
bool Thermostat::set_wwcircmode(const char * value, const int8_t id) {
|
||||
@@ -2185,7 +2302,7 @@ bool Thermostat::set_datetime(const char * value, const int8_t id) {
|
||||
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
|
||||
data[7] = tm_->tm_isdst + 2; // set DST and flag for ext. clock
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
||||
data[6]++; // Junkers use 1-7;
|
||||
data[6]++; // Junkers use 1-7;
|
||||
data[7] = 0;
|
||||
}
|
||||
} else if (dt.length() == 23) {
|
||||
@@ -2383,7 +2500,7 @@ bool Thermostat::set_mode_n(const uint8_t mode, const uint8_t hc_num) {
|
||||
if (mode == HeatingCircuit::Mode::AUTO) {
|
||||
set_mode_value = 0xFF; // special value for auto
|
||||
} else {
|
||||
set_mode_value = 0; // everything else, like manual/day etc..
|
||||
set_mode_value = 0; // everything else, like manual/day etc..
|
||||
}
|
||||
break;
|
||||
case EMSdevice::EMS_DEVICE_FLAG_JUNKERS:
|
||||
@@ -2611,7 +2728,7 @@ bool Thermostat::set_controlmode(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// sets the thermostat time for nightmode for RC10, telegrm 0xB0
|
||||
// sets the thermostat time for nightmode for RC10, telegram 0xB0
|
||||
bool Thermostat::set_reducehours(const char * value, const int8_t id) {
|
||||
uint8_t hc_num = (id == -1) ? AUTO_HEATING_CIRCUIT : id;
|
||||
std::shared_ptr<Thermostat::HeatingCircuit> hc = heating_circuit(hc_num);
|
||||
@@ -3004,7 +3121,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
offset = 0x0A; // manual offset
|
||||
break;
|
||||
case HeatingCircuit::Mode::TEMPAUTO:
|
||||
offset = 0x08; // auto offset
|
||||
offset = 0x08; // auto offset
|
||||
if (temperature == -1) {
|
||||
factor = 1; // to write 0xFF
|
||||
}
|
||||
@@ -3294,9 +3411,7 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
write_command(set_typeid, offset, (uint8_t)(temperature * (float)factor), validate_typeid);
|
||||
return true;
|
||||
}
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("temperature mode %d not found", mode);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3339,7 +3454,6 @@ void Thermostat::register_device_values() {
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &errorCode_, DeviceValueType::STRING, FL_(errorCode), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &lastCode_, DeviceValueType::STRING, FL_(lastCode), DeviceValueUOM::NONE);
|
||||
|
||||
|
||||
switch (this->model()) {
|
||||
case EMS_DEVICE_FLAG_RC100:
|
||||
case EMS_DEVICE_FLAG_RC300:
|
||||
@@ -3995,7 +4109,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
||||
register_device_value(tag, &hc->selTemp, DeviceValueType::SHORT, seltemp_divider, FL_(selRoomTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_temp), 0, 30);
|
||||
}
|
||||
register_device_value(tag, &hc->roomTemp, DeviceValueType::SHORT, roomtemp_divider, FL_(roomTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &hc->climate, DeviceValueType::ENUM, FL_(enum_climate), FL_(climate), DeviceValueUOM::NONE, nullptr, 5, 30);
|
||||
register_device_value(tag, &hc->climate, DeviceValueType::ENUM, FL_(enum_climate), FL_(haclimate), DeviceValueUOM::NONE, nullptr, 5, 30);
|
||||
|
||||
switch (model) {
|
||||
case EMS_DEVICE_FLAG_RC10:
|
||||
@@ -4080,6 +4194,14 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
||||
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp));
|
||||
register_device_value(tag, &hc->reducetemp, DeviceValueType::INT, FL_(reducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_reducetemp));
|
||||
register_device_value(tag, &hc->wwprio, DeviceValueType::BOOL, FL_(wwprio), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwprio));
|
||||
register_device_value(tag, &hc->cooling, DeviceValueType::BOOL, FL_(hpcooling), DeviceValueUOM::NONE, MAKE_CF_CB(set_cooling));
|
||||
register_device_value(tag, &hc->coolingon, DeviceValueType::BOOL, FL_(coolingOn), DeviceValueUOM::NONE);
|
||||
|
||||
register_device_value(tag, &hc->hpmode, DeviceValueType::ENUM, FL_(enum_hpmode), FL_(hpmode), DeviceValueUOM::NONE, MAKE_CF_CB(set_hpmode));
|
||||
register_device_value(tag, &hc->dewoffset, DeviceValueType::UINT, FL_(dewoffset), DeviceValueUOM::K, MAKE_CF_CB(set_dewoffset), 2, 10);
|
||||
register_device_value(tag, &hc->roomtempdiff, DeviceValueType::UINT, FL_(roomtempdiff), DeviceValueUOM::K, MAKE_CF_CB(set_roomtempdiff));
|
||||
register_device_value(tag, &hc->hpminflowtemp, DeviceValueType::UINT, FL_(hpminflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_hpminflowtemp));
|
||||
|
||||
break;
|
||||
case EMS_DEVICE_FLAG_CRF:
|
||||
register_device_value(tag, &hc->mode, DeviceValueType::ENUM, FL_(enum_mode5), FL_(mode), DeviceValueUOM::NONE);
|
||||
@@ -4246,7 +4368,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
||||
MAKE_CF_CB(set_tempautotemp),
|
||||
0,
|
||||
30);
|
||||
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp), -30, 10);
|
||||
register_device_value(tag, &hc->noreducetemp, DeviceValueType::INT, FL_(noreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_noreducetemp), -31, 10);
|
||||
register_device_value(tag, &hc->reducetemp, DeviceValueType::INT, FL_(reducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_reducetemp), -20, 10);
|
||||
register_device_value(tag, &hc->vacreducetemp, DeviceValueType::INT, FL_(vacreducetemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_vacreducetemp), -20, 10);
|
||||
register_device_value(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -89,6 +89,13 @@ class Thermostat : public EMSdevice {
|
||||
uint16_t reduceminutes; // remaining minutes to night->day
|
||||
// FW100 temperature
|
||||
uint8_t roomsensor; // 1-intern, 2-extern, 3-autoselect the lower value
|
||||
// hp
|
||||
uint8_t dewoffset;
|
||||
uint8_t roomtempdiff;
|
||||
uint8_t hpminflowtemp;
|
||||
uint8_t hpmode;
|
||||
uint8_t cooling;
|
||||
uint8_t coolingon;
|
||||
|
||||
uint8_t hc_num() const {
|
||||
return hc_num_;
|
||||
@@ -170,6 +177,8 @@ class Thermostat : public EMSdevice {
|
||||
std::vector<uint16_t> summer_typeids;
|
||||
std::vector<uint16_t> summer2_typeids;
|
||||
std::vector<uint16_t> curve_typeids;
|
||||
std::vector<uint16_t> hp_typeids;
|
||||
std::vector<uint16_t> hpmode_typeids;
|
||||
|
||||
// standard for all thermostats
|
||||
char status_[20]; // online or offline
|
||||
@@ -247,9 +256,9 @@ class Thermostat : public EMSdevice {
|
||||
static constexpr uint16_t EMS_TYPE_RCOutdoorTemp = 0xA3; // is an automatic thermostat broadcast, outdoor external temp
|
||||
|
||||
// Type offsets
|
||||
static constexpr uint8_t EMS_OFFSET_RC10StatusMessage_setpoint = 1; // setpoint temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC10StatusMessage_curr = 2; // current temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC10Set_temp = 4; // position of thermostat setpoint temperature
|
||||
static constexpr uint8_t EMS_OFFSET_RC10StatusMessage_setpoint = 1; // setpoint temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC10StatusMessage_curr = 2; // current temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC10Set_temp = 4; // position of thermostat setpoint temperature
|
||||
|
||||
static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_setpoint = 1; // setpoint temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_curr = 2; // current temp
|
||||
@@ -258,9 +267,9 @@ class Thermostat : public EMSdevice {
|
||||
static constexpr uint8_t EMS_OFFSET_RC20Set_temp_auto = 28; // position of thermostat setpoint temperature
|
||||
static constexpr uint8_t EMS_OFFSET_RC20Set_temp_manual = 29; // position of thermostat setpoint temperature
|
||||
|
||||
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_mode = 3; // ES72 - see https://github.com/emsesp/EMS-ESP/issues/334
|
||||
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_temp_night = 1; // ES72
|
||||
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_temp_day = 2; // ES72
|
||||
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_mode = 3; // ES72 - see https://github.com/emsesp/EMS-ESP/issues/334
|
||||
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_temp_night = 1; // ES72
|
||||
static constexpr uint8_t EMS_OFFSET_RC20_2_Set_temp_day = 2; // ES72
|
||||
|
||||
static constexpr uint8_t EMS_OFFSET_RC30StatusMessage_setpoint = 1; // setpoint temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC30StatusMessage_curr = 2; // current temp
|
||||
@@ -299,8 +308,8 @@ class Thermostat : public EMSdevice {
|
||||
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_summer = 22;
|
||||
static constexpr uint8_t EMS_OFFSET_RC35Set_temp_nofrost = 23;
|
||||
|
||||
static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_setpoint = 10; // setpoint temp
|
||||
static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_curr = 8; // current temp
|
||||
static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_setpoint = 10; // setpoint temp
|
||||
static constexpr uint8_t EMS_OFFSET_EasyStatusMessage_curr = 8; // current temp
|
||||
|
||||
static constexpr uint8_t EMS_OFFSET_RCPLUSStatusMessage_mode = 10; // thermostat mode (auto, manual)
|
||||
static constexpr uint8_t EMS_OFFSET_RCPLUSStatusMessage_setpoint = 3; // setpoint temp
|
||||
@@ -392,6 +401,8 @@ class Thermostat : public EMSdevice {
|
||||
void process_RemoteTemp(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RemoteHumidity(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RemoteCorrection(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPSet(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPMode(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
// internal helper functions
|
||||
bool set_mode_n(const uint8_t mode, const uint8_t hc_num);
|
||||
@@ -551,8 +562,14 @@ class Thermostat : public EMSdevice {
|
||||
bool set_pvEnableWw(const char * value, const int8_t id);
|
||||
bool set_pvRaiseHeat(const char * value, const int8_t id);
|
||||
bool set_pvLowerCool(const char * value, const int8_t id);
|
||||
|
||||
bool set_roomtempdiff(const char * value, const int8_t id);
|
||||
bool set_dewoffset(const char * value, const int8_t id);
|
||||
bool set_hpminflowtemp(const char * value, const int8_t id);
|
||||
bool set_hpmode(const char * value, const int8_t id);
|
||||
bool set_cooling(const char * value, const int8_t id);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
#endif
|
||||
118
src/devices/ventilation.cpp
Normal file
118
src/devices/ventilation.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ventilation.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
REGISTER_FACTORY(Ventilation, EMSdevice::DeviceType::VENTILATION);
|
||||
|
||||
Ventilation::Ventilation(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// HRV176 module, device_id 0x51
|
||||
register_telegram_type(0x56B, "VentilationMode", true, MAKE_PF_CB(process_ModeMessage));
|
||||
register_telegram_type(0x585, "Blowerspeed", false, MAKE_PF_CB(process_BlowerMessage));
|
||||
register_telegram_type(0x583, "VentilationMonitor", false, MAKE_PF_CB(process_MonitorMessage));
|
||||
register_telegram_type(0x5D9, "Airquality", false, MAKE_PF_CB(process_VOCMessage));
|
||||
register_telegram_type(0x587, "Bypass", false, MAKE_PF_CB(process_BypassMessage));
|
||||
// register_telegram_type(0x5, "VentilationSet", true, MAKE_PF_CB(process_SetMessage));
|
||||
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&outFresh_,
|
||||
DeviceValueType::SHORT,
|
||||
DeviceValueNumOp::DV_NUMOP_DIV10,
|
||||
FL_(outFresh),
|
||||
DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &inFresh_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(inFresh), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &outEx_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(outEx), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &inEx_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(inEx), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &ventInSpeed_, DeviceValueType::UINT, FL_(ventInSpeed), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &ventOutSpeed_, DeviceValueType::UINT, FL_(ventOutSpeed), DeviceValueUOM::PERCENT);
|
||||
register_device_value(
|
||||
DeviceValueTAG::TAG_DEVICE_DATA, &mode_, DeviceValueType::ENUM, FL_(enum_ventMode), FL_(ventMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_ventMode));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &voc_, DeviceValueType::USHORT, FL_(airquality), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &bypass_, DeviceValueType::BOOL, FL_(bypass), DeviceValueUOM::NONE, MAKE_CF_CB(set_bypass));
|
||||
}
|
||||
|
||||
// message
|
||||
void Ventilation::process_SetMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
// message 583
|
||||
void Ventilation::process_MonitorMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, outEx_, 0); // Fortluft
|
||||
has_update(telegram, inEx_, 7); // Abluft
|
||||
has_update(telegram, outFresh_, 13); // Außenluft
|
||||
has_update(telegram, inFresh_, 15); // Zuluft
|
||||
}
|
||||
|
||||
// message 575 10 bytes
|
||||
// data: 02 02 46 46 00 00 FF 80 00 01
|
||||
// 0-level out, 1-level in, 2-mod out, 3-mod in, 9-mode:1-manual/2-auto/3-prog
|
||||
|
||||
// message 585 26 bytes long
|
||||
// Data: 46 46 00 00 00 77 00 03 F4 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
void Ventilation::process_BlowerMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, ventOutSpeed_, 0);
|
||||
has_update(telegram, ventInSpeed_, 1);
|
||||
}
|
||||
|
||||
// message 0x05D9, data: 03 9C FF
|
||||
void Ventilation::process_VOCMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, voc_, 0);
|
||||
}
|
||||
|
||||
// message 0x56B
|
||||
// level 0=0, 1=1, 2=2, 3=3, 4= 4, Auto 0xFF, demand 5, sleep 6, intense 7, bypass-8, party 9, fireplace 0A
|
||||
void Ventilation::process_ModeMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_enumupdate(telegram, mode_, 0, -1);
|
||||
}
|
||||
|
||||
// message 0x0587, data: 01 00
|
||||
void Ventilation::process_BypassMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, bypass_, 1);
|
||||
}
|
||||
|
||||
bool Ventilation::set_ventMode(const char * value, const int8_t id) {
|
||||
uint8_t v;
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_ventMode))) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x56B, 0, v - 1, 0x56B);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ventilation::set_filter(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
// write_command(0x5xx, 0, v, 0x5xx);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ventilation::set_bypass(const char * value, const int8_t id) {
|
||||
bool b;
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x55C, 1, b ? 1 : 0, 0x587);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
119
src/devices/ventilation.h
Normal file
119
src/devices/ventilation.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EMSESP_VENTILATION_H
|
||||
#define EMSESP_VENTILATION_H
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Ventilation : public EMSdevice {
|
||||
public:
|
||||
Ventilation(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
|
||||
|
||||
private:
|
||||
uint8_t mode_;
|
||||
int16_t outFresh_;
|
||||
int16_t inFresh_;
|
||||
int16_t inEx_;
|
||||
int16_t outEx_;
|
||||
uint16_t voc_;
|
||||
uint8_t bypass_;
|
||||
// uint16_t filterRemain_;
|
||||
uint8_t ventInSpeed_;
|
||||
uint8_t ventOutSpeed_;
|
||||
|
||||
// handlers: 0x056B 0x0575 0x0583 0x0585 0x0586 0x0587 0x0588 0x058D 0x058E 0x058F 0x0590 0x05CF 0x05D9 0x05E3
|
||||
void process_SetMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MonitorMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_ModeMessage(std::shared_ptr<const Telegram> telegram); // 0x56B
|
||||
void process_BlowerMessage(std::shared_ptr<const Telegram> telegram); // 0x56B
|
||||
void process_VOCMessage(std::shared_ptr<const Telegram> telegram); // 0x56B
|
||||
void process_BypassMessage(std::shared_ptr<const Telegram> telegram); // 0x56B
|
||||
|
||||
bool set_ventMode(const char * value, const int8_t id);
|
||||
bool set_bypass(const char * value, const int8_t id);
|
||||
bool set_filter(const char * value, const int8_t id);
|
||||
|
||||
|
||||
/* Sensors:
|
||||
outdoor air temp (außenluft)
|
||||
supply air temp (zuluft)
|
||||
extract air temp (abluft)
|
||||
away air temp (fortluft)
|
||||
supply blower (zuluftgebläse)
|
||||
supply blower mod (zuluftebläse drehzahl)
|
||||
away blower (abluftgebläse)
|
||||
away blower mod (abluftgebläse drehzahl)
|
||||
Anschlussvariante
|
||||
el. vorheizer
|
||||
ext. el. vorheizreg.
|
||||
nachheiz zulufttemp
|
||||
mischer öffnen
|
||||
mischer schließen
|
||||
mischerposition
|
||||
zuluft temp soll
|
||||
zuluft temp ist
|
||||
leistung nachheizreg.
|
||||
erdwärmetauscher klappe
|
||||
solekreispumpe
|
||||
abluftfeuchte
|
||||
abluftqualität
|
||||
raumluftfechte
|
||||
raumluftqualität
|
||||
luftfeuchte fernbed. 1..4
|
||||
*/
|
||||
/* Parameters:
|
||||
Gerätetyp,
|
||||
Nennvolumentstrom,
|
||||
Filterlaufzeit 1-6-12 m
|
||||
Filterwechsel confirm CMD
|
||||
Lüftungsfrostschutz: _el._preheat_, Disballance | Interval
|
||||
Ext. Frostschutz: on/_off_
|
||||
Bypass _on_, off
|
||||
min. outdoortemp 12 15 19 °C
|
||||
max. outdoortemp 21-24-30 C
|
||||
Enthalpie Wärmetauscher instaliert nein-ja
|
||||
Feuchteschutz AUs/ 1-24 h
|
||||
Lüfterstufe 1-4, Drehzahlanpassung
|
||||
ext. Luftfeuchtefühler inst.? _nein_, ja
|
||||
Abluftfeuchtefühler inst.? _nein_, ja
|
||||
Luftfeuchte Fernbed. _nein_, ja
|
||||
Luftfeuchte: trocken, _normal_, feucht
|
||||
Abluftqualitätsfühler inst. _ja_, nein
|
||||
ext. Luftqualfühl? _nein_, ja
|
||||
Lufqualität: ausreichend, _normal_, hoch
|
||||
el. Nachheizregister inst. _nein_, ja
|
||||
Nachheiz-Zuluft temp: 10-22-30 °C
|
||||
Erdwärmetauscher inst? _nein_, Luft, Sole
|
||||
Taster Funktion: nein, einschlafen, intensiv, bypass, party, kamin
|
||||
ext. Störung aktivieren: _nein_, ja, invertiert
|
||||
Dauer einschlafen: 15-60-120 min
|
||||
Dauer Intensiv: 5-15-60 min
|
||||
Dauer Bypass Abluft: 1-8-12 h
|
||||
Dauer Bypass: 1-8-12 h
|
||||
Dauer PArty 1-8-12 h
|
||||
Dauer Kamin: 5-10-15 min
|
||||
Volumenstromabgleich 90-100-110 %
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -52,16 +52,18 @@ bool EMSdevice::has_entities() const {
|
||||
return found;
|
||||
}
|
||||
|
||||
std::string EMSdevice::tag_to_string(uint8_t tag, const bool translate) {
|
||||
return (translate ? Helpers::translated_word(DeviceValue::DeviceValueTAG_s[tag]) : DeviceValue::DeviceValueTAG_s[tag][0]);
|
||||
// return translated tag name based on tag id
|
||||
const char * EMSdevice::tag_to_string(uint8_t tag, const bool translate) {
|
||||
uint8_t tag_n = tag > DeviceValue::NUM_TAGS ? 0 : tag;
|
||||
return (translate ? Helpers::translated_word(DeviceValue::DeviceValueTAG_s[tag_n]) : DeviceValue::DeviceValueTAG_s[tag_n][0]);
|
||||
}
|
||||
|
||||
std::string EMSdevice::tag_to_mqtt(uint8_t tag) {
|
||||
return (DeviceValue::DeviceValueTAG_mqtt[tag]);
|
||||
const char * EMSdevice::tag_to_mqtt(uint8_t tag) {
|
||||
return (DeviceValue::DeviceValueTAG_mqtt[tag > DeviceValue::NUM_TAGS ? 0 : tag]);
|
||||
}
|
||||
|
||||
// convert UOM to a string - translating only for hours/minutes/seconds
|
||||
std::string EMSdevice::uom_to_string(uint8_t uom) {
|
||||
// convert UOM to a char string - translating only for hours/minutes/seconds
|
||||
const char * EMSdevice::uom_to_string(uint8_t uom) {
|
||||
switch (uom) {
|
||||
case DeviceValueUOM::DEGREES:
|
||||
case DeviceValueUOM::DEGREES_R:
|
||||
@@ -77,24 +79,24 @@ std::string EMSdevice::uom_to_string(uint8_t uom) {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string EMSdevice::brand_to_string() {
|
||||
const char * EMSdevice::brand_to_char() {
|
||||
switch (brand_) {
|
||||
case EMSdevice::Brand::BOSCH:
|
||||
return "Bosch";
|
||||
return F_(bosch);
|
||||
case EMSdevice::Brand::JUNKERS:
|
||||
return "Junkers";
|
||||
return F_(junkers);
|
||||
case EMSdevice::Brand::BUDERUS:
|
||||
return "Buderus";
|
||||
return F_(buderus);
|
||||
case EMSdevice::Brand::NEFIT:
|
||||
return "Nefit";
|
||||
return F_(nefit);
|
||||
case EMSdevice::Brand::SIEGER:
|
||||
return "Sieger";
|
||||
return F_(sieger);
|
||||
case EMSdevice::Brand::WORCESTER:
|
||||
return "Worcester";
|
||||
return F_(worcester);
|
||||
case EMSdevice::Brand::IVT:
|
||||
return "IVT";
|
||||
return F_(ivt);
|
||||
default:
|
||||
return "";
|
||||
return F_(no_brand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +105,8 @@ const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
|
||||
switch (device_type) {
|
||||
case DeviceType::SYSTEM:
|
||||
return F_(system);
|
||||
case DeviceType::SCHEDULER:
|
||||
return F_(scheduler);
|
||||
case DeviceType::BOILER:
|
||||
return F_(boiler);
|
||||
case DeviceType::THERMOSTAT:
|
||||
@@ -115,8 +119,8 @@ const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
|
||||
return F_(connect);
|
||||
case DeviceType::MIXER:
|
||||
return F_(mixer);
|
||||
case DeviceType::DALLASSENSOR:
|
||||
return F_(dallassensor);
|
||||
case DeviceType::TEMPERATURESENSOR:
|
||||
return F_(temperaturesensor);
|
||||
case DeviceType::ANALOGSENSOR:
|
||||
return F_(analogsensor);
|
||||
case DeviceType::CONTROLLER:
|
||||
@@ -131,13 +135,17 @@ const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
|
||||
return F_(pump);
|
||||
case DeviceType::HEATSOURCE:
|
||||
return F_(heatsource);
|
||||
case DeviceType::CUSTOM:
|
||||
return F_(custom);
|
||||
case DeviceType::VENTILATION:
|
||||
return F_(ventilation);
|
||||
default:
|
||||
return Helpers::translated_word(FL_(unknown), true);
|
||||
}
|
||||
}
|
||||
|
||||
// returns the translated name of a specific EMS device
|
||||
// excludes dallassensor, analogsensor and system
|
||||
// excludes temperaturesensor, analogsensor and system
|
||||
const char * EMSdevice::device_type_2_device_name_translated() {
|
||||
switch (device_type_) {
|
||||
case DeviceType::BOILER:
|
||||
@@ -164,6 +172,8 @@ const char * EMSdevice::device_type_2_device_name_translated() {
|
||||
return Helpers::translated_word(FL_(pump_device));
|
||||
case DeviceType::HEATSOURCE:
|
||||
return Helpers::translated_word(FL_(heatsource_device));
|
||||
case DeviceType::VENTILATION:
|
||||
return Helpers::translated_word(FL_(ventilation_device));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -192,6 +202,9 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
if (!strcmp(lowtopic, F_(system))) {
|
||||
return DeviceType::SYSTEM;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(scheduler))) {
|
||||
return DeviceType::SCHEDULER;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(heatpump))) {
|
||||
return DeviceType::HEATPUMP;
|
||||
}
|
||||
@@ -201,8 +214,8 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
if (!strcmp(lowtopic, F_(mixer))) {
|
||||
return DeviceType::MIXER;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(dallassensor))) {
|
||||
return DeviceType::DALLASSENSOR;
|
||||
if (!strcmp(lowtopic, F_(temperaturesensor))) {
|
||||
return DeviceType::TEMPERATURESENSOR;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(analogsensor))) {
|
||||
return DeviceType::ANALOGSENSOR;
|
||||
@@ -222,6 +235,12 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
|
||||
if (!strcmp(lowtopic, F_(heatsource))) {
|
||||
return DeviceType::HEATSOURCE;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(custom))) {
|
||||
return DeviceType::CUSTOM;
|
||||
}
|
||||
if (!strcmp(lowtopic, F_(ventilation))) {
|
||||
return DeviceType::VENTILATION;
|
||||
}
|
||||
|
||||
return DeviceType::UNKNOWN;
|
||||
}
|
||||
@@ -264,8 +283,8 @@ const std::string EMSdevice::to_string() {
|
||||
return std::string(name_) + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
|
||||
}
|
||||
|
||||
return brand_to_string() + " " + name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_
|
||||
+ ")";
|
||||
return std::string(brand_to_char()) + " " + name_ + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_)
|
||||
+ ", Version:" + version_ + ")";
|
||||
}
|
||||
|
||||
// returns out brand + device name
|
||||
@@ -275,12 +294,14 @@ const std::string EMSdevice::to_string_short() {
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + name_;
|
||||
}
|
||||
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + brand_to_string() + " " + name_;
|
||||
return std::string(device_type_2_device_name_translated()) + ": " + brand_to_char() + " " + name_;
|
||||
}
|
||||
|
||||
// for each telegram that has the fetch value set (true) do a read request
|
||||
void EMSdevice::fetch_values() {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("Fetching values for deviceID 0x%02X", device_id());
|
||||
#endif
|
||||
|
||||
for (const auto & tf : telegram_functions_) {
|
||||
if (tf.fetch_) {
|
||||
@@ -291,7 +312,9 @@ void EMSdevice::fetch_values() {
|
||||
|
||||
// toggle on/off automatic fetch for a telegramID
|
||||
void EMSdevice::toggle_fetch(uint16_t telegram_id, bool toggle) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("Toggling fetch for deviceID 0x%02X, telegramID 0x%02X to %d", device_id(), telegram_id, toggle);
|
||||
#endif
|
||||
|
||||
for (auto & tf : telegram_functions_) {
|
||||
if (tf.telegram_type_id_ == telegram_id) {
|
||||
@@ -311,7 +334,7 @@ bool EMSdevice::is_fetch(uint16_t telegram_id) const {
|
||||
}
|
||||
|
||||
// check for a tag to create a nest
|
||||
bool EMSdevice::has_tag(const uint8_t tag) const {
|
||||
bool EMSdevice::has_tags(const uint8_t tag) const {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
if (dv.tag == tag && tag >= DeviceValueTAG::TAG_HC1) {
|
||||
return true;
|
||||
@@ -339,8 +362,9 @@ void EMSdevice::list_device_entries(JsonObject & output) const {
|
||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.type != DeviceValueType::CMD && !fullname.empty()) {
|
||||
// if we have a tag prefix it
|
||||
char key[50];
|
||||
if (!EMSdevice::tag_to_mqtt(dv.tag).empty()) {
|
||||
snprintf(key, sizeof(key), "%s.%s", EMSdevice::tag_to_mqtt(dv.tag).c_str(), dv.short_name);
|
||||
auto tag_s = EMSdevice::tag_to_mqtt(dv.tag);
|
||||
if (strlen(tag_s)) {
|
||||
snprintf(key, sizeof(key), "%s.%s", tag_s, dv.short_name);
|
||||
} else {
|
||||
snprintf(key, sizeof(key), "%s", dv.short_name);
|
||||
}
|
||||
@@ -351,7 +375,7 @@ void EMSdevice::list_device_entries(JsonObject & output) const {
|
||||
details.add(fullname);
|
||||
|
||||
// add uom
|
||||
if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") {
|
||||
if (dv.uom != DeviceValueUOM::NONE) {
|
||||
details.add(EMSdevice::uom_to_string(dv.uom));
|
||||
}
|
||||
}
|
||||
@@ -454,48 +478,37 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const ch
|
||||
}
|
||||
|
||||
// add to device value library, also know now as a "device entity"
|
||||
// arguments are:
|
||||
// tag: to be used to group mqtt together, either as separate topics as a nested object
|
||||
// value_p: pointer to the value from the .h file
|
||||
// type: one of DeviceValueType
|
||||
// options: options for enum, which are translated as a list of lists
|
||||
// options_single: list of names
|
||||
// numeric_operator: to divide or multiply, see DeviceValueNumOps::
|
||||
// short_name: used in MQTT as keys
|
||||
// fullname: used in Web and Console unless empty (nullptr) - can be translated
|
||||
// uom: unit of measure from DeviceValueUOM
|
||||
// has_cmd: true if this is an associated command
|
||||
// min: min allowed value
|
||||
// max: max allowed value
|
||||
void EMSdevice::add_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const char * const ** options,
|
||||
const char * const * options_single,
|
||||
int8_t numeric_operator,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
void EMSdevice::add_device_value(uint8_t tag, // to be used to group mqtt together, either as separate topics as a nested object
|
||||
void * value_p, // pointer to the value from the .h file
|
||||
uint8_t type, // one of DeviceValueType
|
||||
const char * const ** options, // options for enum, which are translated as a list of lists
|
||||
const char * const * options_single, // list of names
|
||||
int8_t numeric_operator, // to divide or multiply, see DeviceValueNumOps::
|
||||
const char * const * name, // list of names, including shortname and translations
|
||||
uint8_t uom, // unit of measure from DeviceValueUOM
|
||||
const cmd_function_p f, // command function pointer
|
||||
int16_t min, // min allowed value
|
||||
uint16_t max // max allowed value
|
||||
) {
|
||||
// initialize the device value depending on it's type
|
||||
// ignoring DeviceValueType::CMD and DeviceValueType::TIME
|
||||
|
||||
if (type == DeviceValueType::STRING) {
|
||||
*(char *)(value_p) = {'\0'}; // this is important for string functions like strlen() to work later
|
||||
} else if (type == DeviceValueType::INT) {
|
||||
*(int8_t *)(value_p) = EMS_VALUE_DEFAULT_INT;
|
||||
*(int8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_INT_DUMMY : EMS_VALUE_DEFAULT_INT;
|
||||
} else if (type == DeviceValueType::UINT) {
|
||||
*(uint8_t *)(value_p) = EMS_VALUE_DEFAULT_UINT;
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_UINT_DUMMY : EMS_VALUE_DEFAULT_UINT;
|
||||
} else if (type == DeviceValueType::SHORT) {
|
||||
*(int16_t *)(value_p) = EMS_VALUE_DEFAULT_SHORT;
|
||||
*(int16_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_SHORT_DUMMY : EMS_VALUE_DEFAULT_SHORT;
|
||||
} else if (type == DeviceValueType::USHORT) {
|
||||
*(uint16_t *)(value_p) = EMS_VALUE_DEFAULT_USHORT;
|
||||
*(uint16_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_USHORT_DUMMY : EMS_VALUE_DEFAULT_USHORT;
|
||||
} else if ((type == DeviceValueType::ULONG) || (type == DeviceValueType::TIME)) {
|
||||
*(uint32_t *)(value_p) = EMS_VALUE_DEFAULT_ULONG;
|
||||
*(uint32_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_ULONG_DUMMY : EMS_VALUE_DEFAULT_ULONG;
|
||||
} else if (type == DeviceValueType::BOOL) {
|
||||
*(int8_t *)(value_p) = EMS_VALUE_DEFAULT_BOOL; // bool is uint8_t, but other initial value
|
||||
*(int8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_BOOL_DUMMY : EMS_VALUE_DEFAULT_BOOL; // bool is uint8_t, but other initial value
|
||||
} else if (type == DeviceValueType::ENUM) {
|
||||
*(uint8_t *)(value_p) = EMS_VALUE_DEFAULT_ENUM; // enums behave as uint8_t
|
||||
*(uint8_t *)(value_p) = System::test_set_all_active() ? EMS_VALUE_DEFAULT_ENUM_DUMMY : EMS_VALUE_DEFAULT_ENUM; // enums behave as uint8_t
|
||||
}
|
||||
|
||||
uint8_t state = DeviceValueState::DV_DEFAULT; // determine state
|
||||
@@ -507,7 +520,7 @@ void EMSdevice::add_device_value(uint8_t tag,
|
||||
// get fullname, getting translation if it exists
|
||||
const char * const * fullname;
|
||||
if (Helpers::count_items(name) == 1) {
|
||||
fullname = nullptr; // no translations available, use empty to prevent crash
|
||||
fullname = nullptr; // no translations available, use empty
|
||||
} else {
|
||||
fullname = &name[1]; // translations start at index 1
|
||||
}
|
||||
@@ -516,7 +529,13 @@ void EMSdevice::add_device_value(uint8_t tag,
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||
if ((entityCustomization.product_id == product_id()) && (entityCustomization.device_id == device_id())) {
|
||||
std::string entity = tag < DeviceValueTAG::TAG_HC1 ? short_name : tag_to_mqtt(tag) + "/" + short_name;
|
||||
char entity[70];
|
||||
if (tag < DeviceValueTAG::TAG_HC1) {
|
||||
strncpy(entity, short_name, sizeof(entity));
|
||||
} else {
|
||||
snprintf(entity, sizeof(entity), "%s/%s", tag_to_mqtt(tag), short_name);
|
||||
}
|
||||
|
||||
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||
// if there is an appended custom name, strip it to get the true entity name
|
||||
// and extract the new custom name
|
||||
@@ -705,12 +724,12 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", device_type_2_device_name(device_type_), tag_to_mqtt(dv.tag).c_str(), dv.short_name);
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", device_type_2_device_name(device_type_), tag_to_mqtt(dv.tag), dv.short_name);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", device_type_2_device_name(device_type_), (dv.short_name));
|
||||
}
|
||||
} else if (Mqtt::is_nested() && dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), tag_to_mqtt(dv.tag).c_str(), dv.short_name);
|
||||
snprintf(topic, sizeof(topic), "%s/%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), tag_to_mqtt(dv.tag), dv.short_name);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), dv.short_name);
|
||||
}
|
||||
@@ -766,7 +785,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
}
|
||||
|
||||
if (payload[0] != '\0') {
|
||||
Mqtt::publish(topic, payload);
|
||||
Mqtt::queue_publish(topic, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,7 +799,7 @@ std::string EMSdevice::get_value_uom(const char * key) const {
|
||||
strlcpy(new_key, key, sizeof(new_key));
|
||||
char * key_p = new_key;
|
||||
|
||||
for (uint8_t i = 0; i < DeviceValue::tag_count; i++) {
|
||||
for (uint8_t i = 0; i < DeviceValue::NUM_TAGS; i++) {
|
||||
auto tag = Helpers::translated_word(DeviceValue::DeviceValueTAG_s[i]);
|
||||
if (tag) {
|
||||
std::string key2 = key; // copy string to a std::string so we can use the find function
|
||||
@@ -812,8 +831,9 @@ std::string EMSdevice::get_value_uom(const char * key) const {
|
||||
// except additional data is stored in the JSON document needed for the Web UI like the UOM and command
|
||||
// v=value, u=uom, n=name, c=cmd, h=help string, s=step, m=min, x=max
|
||||
void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
output["label"] = to_string_short();
|
||||
JsonArray data = output.createNestedArray("data");
|
||||
// output["label"] = to_string_short();
|
||||
// output["label"] = name_;
|
||||
JsonArray data = output.createNestedArray("data");
|
||||
|
||||
for (auto & dv : devicevalues_) {
|
||||
auto fullname = dv.get_fullname();
|
||||
@@ -826,7 +846,7 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value
|
||||
uint8_t fahrenheit = 0;
|
||||
|
||||
// handle Booleans (true, false), use strings, no native true/false)
|
||||
// handle Booleans (true, false), output as strings according to the user settings
|
||||
if (dv.type == DeviceValueType::BOOL) {
|
||||
auto value_b = (bool)*(uint8_t *)(dv.value_p);
|
||||
char s[12];
|
||||
@@ -871,17 +891,19 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
auto mask = Helpers::hextoa((uint8_t)(dv.state >> 4), false); // create mask to a 2-char string
|
||||
|
||||
// add name, prefixing the tag if it exists. This is the id used in the WebUI table and must be unique
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
obj["id"] = mask + fullname;
|
||||
} else {
|
||||
if (dv.has_tag()) {
|
||||
obj["id"] = mask + tag_to_string(dv.tag) + " " + fullname;
|
||||
} else {
|
||||
obj["id"] = mask + fullname;
|
||||
}
|
||||
|
||||
// add commands and options
|
||||
if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) {
|
||||
// add the name of the Command function
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
obj["c"] = tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
char c_s[50];
|
||||
snprintf(c_s, sizeof(c_s), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
||||
obj["c"] = c_s;
|
||||
} else {
|
||||
obj["c"] = dv.short_name;
|
||||
}
|
||||
@@ -908,20 +930,19 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
}
|
||||
}
|
||||
// handle INTs
|
||||
// add steps to numeric values with numeric_operator
|
||||
// add min and max values and steps, as integer values
|
||||
else {
|
||||
char s[10];
|
||||
if (dv.numeric_operator > 0) {
|
||||
obj["s"] = Helpers::render_value(s, (float)1 / dv.numeric_operator, 1);
|
||||
obj["s"] = (float)1 / dv.numeric_operator;
|
||||
} else if (dv.numeric_operator < 0) {
|
||||
obj["s"] = Helpers::render_value(s, (float)(-1) * dv.numeric_operator, 0);
|
||||
obj["s"] = (float)(-1) * dv.numeric_operator;
|
||||
}
|
||||
|
||||
int16_t dv_set_min;
|
||||
uint16_t dv_set_max;
|
||||
if (dv.get_min_max(dv_set_min, dv_set_max)) {
|
||||
obj["m"] = Helpers::render_value(s, dv_set_min, 0);
|
||||
obj["x"] = Helpers::render_value(s, dv_set_max, 0);
|
||||
obj["m"] = dv_set_min;
|
||||
obj["x"] = dv_set_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -976,7 +997,9 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
|
||||
// id holds the shortname and must always have a value for the WebUI table to work
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
obj["id"] = tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
char id_s[50];
|
||||
snprintf(id_s, sizeof(id_s), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
||||
obj["id"] = id_s;
|
||||
} else {
|
||||
obj["id"] = dv.short_name;
|
||||
}
|
||||
@@ -986,12 +1009,12 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
auto fullname = Helpers::translated_word(dv.fullname);
|
||||
if (dv.type != DeviceValueType::CMD) {
|
||||
if (fullname) {
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
obj["n"] = fullname;
|
||||
} else {
|
||||
if (dv.has_tag()) {
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), fullname);
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname);
|
||||
obj["n"] = name;
|
||||
} else {
|
||||
obj["n"] = fullname;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1012,15 +1035,15 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
int16_t dv_set_min;
|
||||
uint16_t dv_set_max;
|
||||
if (dv.get_min_max(dv_set_min, dv_set_max)) {
|
||||
char s[10];
|
||||
obj["mi"] = Helpers::render_value(s, dv_set_min, 0, fahrenheit);
|
||||
obj["ma"] = Helpers::render_value(s, dv_set_max, 0, fahrenheit);
|
||||
obj["mi"] = dv_set_min;
|
||||
obj["ma"] = dv_set_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
for (EntityCustomization entityCustomization : settings.entityCustomizations) {
|
||||
if ((entityCustomization.device_id == device_id())) {
|
||||
if (entityCustomization.device_id == device_id()) {
|
||||
for (std::string entity_id : entityCustomization.entity_ids) {
|
||||
uint8_t mask = Helpers::hextoint(entity_id.substr(0, 2).c_str());
|
||||
if (mask & 0x80) {
|
||||
@@ -1038,12 +1061,11 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
|
||||
void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
|
||||
for (auto & dv : devicevalues_) {
|
||||
if (dv.tag == tag && dv.short_name == FL_(climate[0])) {
|
||||
if (dv.tag == tag && (strcmp(dv.short_name, FL_(haclimate[0])) == 0)) {
|
||||
if (dv.min != min || dv.max != max) {
|
||||
dv.min = min;
|
||||
dv.max = max;
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
Mqtt::publish_ha_climate_config(dv.tag, false, true); // delete topic (remove = true)
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1054,7 +1076,12 @@ void EMSdevice::set_climate_minmax(uint8_t tag, int16_t min, uint16_t max) {
|
||||
// returns true if the entity has a mask set (not 0 the default)
|
||||
void EMSdevice::setCustomEntity(const std::string & entity_id) {
|
||||
for (auto & dv : devicevalues_) {
|
||||
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
char entity_name[70];
|
||||
if (dv.tag < DeviceValueTAG::TAG_HC1) {
|
||||
strncpy(entity_name, dv.short_name, sizeof(entity_name));
|
||||
} else {
|
||||
snprintf(entity_name, sizeof(entity_name), "%s/%s", tag_to_mqtt(dv.tag), dv.short_name);
|
||||
}
|
||||
|
||||
// extra shortname
|
||||
auto custom_name_pos = entity_id.find('|');
|
||||
@@ -1101,9 +1128,18 @@ void EMSdevice::setCustomEntity(const std::string & entity_id) {
|
||||
// populate a string vector with entities that have masks set or have a custom name
|
||||
void EMSdevice::getCustomEntities(std::vector<std::string> & entity_ids) {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
std::string entity_name = dv.tag < DeviceValueTAG::TAG_HC1 ? dv.short_name : tag_to_mqtt(dv.tag) + "/" + dv.short_name;
|
||||
uint8_t mask = dv.state >> 4;
|
||||
bool is_set = false;
|
||||
char name[100];
|
||||
name[0] = '\0';
|
||||
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
|
||||
// prefix tag
|
||||
strcpy(name, tag_to_mqtt(dv.tag));
|
||||
strcat(name, "/");
|
||||
}
|
||||
strcat(name, dv.short_name);
|
||||
std::string entity_name = name;
|
||||
|
||||
uint8_t mask = dv.state >> 4;
|
||||
bool is_set = false;
|
||||
for (auto & eid : entity_ids) {
|
||||
if (DeviceValue::get_name(eid) == entity_name) {
|
||||
is_set = true;
|
||||
@@ -1120,7 +1156,7 @@ void EMSdevice::getCustomEntities(std::vector<std::string> & entity_ids) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// dumps all entity values in native English
|
||||
// the code is intended to run only once standalone, outside the ESP32 so not optimized for memory efficiency
|
||||
// pipe symbols (|) are escaped so they can be converted to Markdown in the Wiki
|
||||
@@ -1228,7 +1264,6 @@ void EMSdevice::dump_value_info() {
|
||||
char entityid[500];
|
||||
char entity_name[100];
|
||||
|
||||
|
||||
for (uint8_t count = 0; count < 2; count++) {
|
||||
if (count) {
|
||||
// new name, comes as last
|
||||
@@ -1247,7 +1282,7 @@ void EMSdevice::dump_value_info() {
|
||||
sizeof(entity_with_tag),
|
||||
"%s_%s_%s",
|
||||
device_type_2_device_name(device_type_),
|
||||
EMSdevice::tag_to_mqtt(dv.tag).c_str(),
|
||||
EMSdevice::tag_to_mqtt(dv.tag),
|
||||
entity_name);
|
||||
} else {
|
||||
snprintf(entity_with_tag, sizeof(entity_with_tag), "%s_%s", device_type_2_device_name(device_type_), entity_name);
|
||||
@@ -1276,7 +1311,7 @@ void EMSdevice::dump_value_info() {
|
||||
if (dv.type == DeviceValueType::BOOL) {
|
||||
snprintf(entityid, sizeof(entityid), "binary_sensor.%s", entity_with_tag); // binary sensor (for booleans)
|
||||
} else {
|
||||
snprintf(entityid, sizeof(entityid), "sensor.%s", entity_with_tag); // normal HA sensor
|
||||
snprintf(entityid, sizeof(entityid), "sensor.%s", entity_with_tag); // normal HA sensor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1315,7 +1350,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
|
||||
// search device value with this tag
|
||||
for (auto & dv : devicevalues_) {
|
||||
if (!strcmp(command_s, dv.short_name) && (tag <= 0 || tag == dv.tag)) {
|
||||
if (Helpers::toLower(command_s) == Helpers::toLower(dv.short_name) && (tag <= 0 || tag == dv.tag)) {
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
|
||||
const char * type = "type";
|
||||
@@ -1325,14 +1360,16 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
|
||||
auto fullname = dv.get_fullname();
|
||||
if (!fullname.empty()) {
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
json["fullname"] = fullname;
|
||||
if (dv.has_tag()) {
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag), fullname.c_str());
|
||||
json["fullname"] = name;
|
||||
} else {
|
||||
json["fullname"] = tag_to_string(dv.tag) + " " + fullname;
|
||||
json["fullname"] = fullname;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tag_to_mqtt(dv.tag).empty()) {
|
||||
if (dv.tag != DeviceValueTAG::TAG_NONE) {
|
||||
json["circuit"] = tag_to_mqtt(dv.tag);
|
||||
}
|
||||
|
||||
@@ -1444,7 +1481,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
}
|
||||
|
||||
// add uom if it's not a " " (single space)
|
||||
if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") {
|
||||
if (dv.uom != DeviceValueUOM::NONE) {
|
||||
json["uom"] = uom_to_string(dv.uom);
|
||||
}
|
||||
|
||||
@@ -1460,7 +1497,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
// if we're filtering on an attribute, go find it
|
||||
if (attribute_s) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("[DEBUG] Attribute '%s'", attribute_s);
|
||||
EMSESP::logger().debug("Attribute '%s'", attribute_s);
|
||||
#endif
|
||||
if (json.containsKey(attribute_s)) {
|
||||
JsonVariant data = json[attribute_s];
|
||||
@@ -1480,6 +1517,9 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
}
|
||||
}
|
||||
|
||||
char error[100];
|
||||
snprintf(error, sizeof(error), "cannot find values for entity '%s'", cmd);
|
||||
json["message"] = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1492,7 +1532,7 @@ void EMSdevice::publish_all_values() {
|
||||
|
||||
// For each value in the device create the json object pair and add it to given json
|
||||
// return false if empty
|
||||
// this is used to create both the MQTT payloads, Console messages and Web API calls
|
||||
// this is used to create the MQTT payloads, Console messages and Web API calls
|
||||
bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, const bool nested, const uint8_t output_target) {
|
||||
bool has_values = false; // to see if we've added a value. it's faster than doing a json.size() at the end
|
||||
uint8_t old_tag = 255; // NAN
|
||||
@@ -1501,11 +1541,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
for (auto & dv : devicevalues_) {
|
||||
// check if it exists, there is a value for the entity. Set the flag to ACTIVE
|
||||
// not that this will override any previously removed states
|
||||
if (dv.hasValue()) {
|
||||
dv.add_state(DeviceValueState::DV_ACTIVE);
|
||||
} else {
|
||||
dv.remove_state(DeviceValueState::DV_ACTIVE);
|
||||
}
|
||||
(dv.hasValue()) ? dv.add_state(DeviceValueState::DV_ACTIVE) : dv.remove_state(DeviceValueState::DV_ACTIVE);
|
||||
|
||||
auto fullname = dv.get_fullname();
|
||||
|
||||
@@ -1519,16 +1555,22 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
has_values = true; // flagged if we actually have data
|
||||
|
||||
// we have a tag if it matches the filter given, and that the tag name is not empty/""
|
||||
bool have_tag = ((dv.tag != tag_filter) && !tag_to_string(dv.tag).empty());
|
||||
bool have_tag = ((dv.tag != tag_filter) && dv.has_tag());
|
||||
|
||||
// create the name for the JSON key
|
||||
char name[80];
|
||||
|
||||
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
|
||||
if (have_tag) {
|
||||
snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), fullname.c_str()); // prefix the tag
|
||||
char short_name[20];
|
||||
if (output_target == OUTPUT_TARGET::CONSOLE) {
|
||||
snprintf(short_name, sizeof(short_name), " (%s)", dv.short_name);
|
||||
} else {
|
||||
strlcpy(name, fullname.c_str(), sizeof(name)); // use full name
|
||||
strcpy(short_name, "");
|
||||
}
|
||||
if (have_tag) {
|
||||
snprintf(name, sizeof(name), "%s %s%s", tag_to_string(dv.tag), fullname.c_str(), short_name); // prefix the tag
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "%s%s", fullname.c_str(), short_name);
|
||||
}
|
||||
} else {
|
||||
strlcpy(name, (dv.short_name), sizeof(name)); // use short name
|
||||
@@ -1581,7 +1623,7 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
: (dv.uom == DeviceValueUOM::DEGREES) ? 2
|
||||
: (dv.uom == DeviceValueUOM::DEGREES_R) ? 1
|
||||
: 0;
|
||||
char val[10];
|
||||
char val[10] = {'\0'};
|
||||
if (dv.type == DeviceValueType::INT) {
|
||||
json[name] = serialized(Helpers::render_value(val, *(int8_t *)(dv.value_p), dv.numeric_operator, fahrenheit));
|
||||
} else if (dv.type == DeviceValueType::UINT) {
|
||||
@@ -1610,9 +1652,10 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
Helpers::translated_word(FL_(minutes)));
|
||||
json[name] = time_s;
|
||||
} else {
|
||||
json[name] = time_value;
|
||||
json[name] = serialized(Helpers::render_value(val, time_value, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// commenting out - we don't want Commands in MQTT or Console
|
||||
// else if (dv.type == DeviceValueType::CMD && output_target != EMSdevice::OUTPUT_TARGET::MQTT) {
|
||||
// json[name] = "";
|
||||
@@ -1621,18 +1664,16 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
// check for value outside min/max range and adapt the limits to avoid HA complains
|
||||
// Should this also check for api output?
|
||||
if ((output_target == OUTPUT_TARGET::MQTT) && (dv.min != 0 || dv.max != 0)) {
|
||||
if (json[name].is<float>() || json[name].is<int>()) {
|
||||
int v = json[name];
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
if (v < dv.min) {
|
||||
dv.min = v;
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
} else if (v > dv.max) {
|
||||
dv.max = v;
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
}
|
||||
int v = Helpers::atoint(val);
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
if (v < dv.min) {
|
||||
dv.min = v;
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
} else if (v > dv.max) {
|
||||
dv.max = v;
|
||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1651,27 +1692,34 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
// create climate if roomtemp is visible
|
||||
// create the discovery topic if if hasn't already been created, not a command (like reset) and is active and visible
|
||||
for (auto & dv : devicevalues_) {
|
||||
if ((dv.short_name == FL_(climate)[0]) && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE) && dv.has_state(DeviceValueState::DV_ACTIVE)) {
|
||||
if (!strcmp(dv.short_name, FL_(haclimate)[0]) && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE) && dv.has_state(DeviceValueState::DV_ACTIVE)) {
|
||||
if (*(int8_t *)(dv.value_p) == 1 && (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) {
|
||||
dv.remove_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max);
|
||||
if (Mqtt::publish_ha_climate_config(dv.tag, true, false, dv.min, dv.max)) { // roomTemp
|
||||
dv.remove_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
}
|
||||
} else if (*(int8_t *)(dv.value_p) == 0
|
||||
&& (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) || !dv.has_state(DeviceValueState::DV_HA_CLIMATE_NO_RT))) {
|
||||
dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
Mqtt::publish_ha_climate_config(dv.tag, false, false, dv.min, dv.max);
|
||||
if (Mqtt::publish_ha_climate_config(dv.tag, false, false, dv.min, dv.max)) { // no roomTemp
|
||||
dv.add_state(DeviceValueState::DV_HA_CLIMATE_NO_RT);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && (dv.type != DeviceValueType::CMD) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
||||
&& !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
||||
// create_device_config is only done once for the EMS device. It can added to any entity, so we take the first
|
||||
Mqtt::publish_ha_sensor_config(dv, name(), brand_to_string(), false, create_device_config);
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
create_device_config = false; // only create the main config once
|
||||
}
|
||||
if (ESP.getFreeHeap() < (65 * 1024)) {
|
||||
break;
|
||||
if (Mqtt::publish_ha_sensor_config(dv, name(), brand_to_char(), false, create_device_config)) {
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
create_device_config = false; // only create the main config once
|
||||
}
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// always create minimum one config
|
||||
if (ESP.getMaxAllocHeap() < (6 * 1024) || (!emsesp::EMSESP::system_.PSram() && ESP.getFreeHeap() < (65 * 1024))) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1697,7 +1745,7 @@ bool EMSdevice::has_telegram_id(uint16_t id) const {
|
||||
}
|
||||
|
||||
// return the name of the telegram type
|
||||
std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegram) const {
|
||||
const char * EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegram) {
|
||||
// see if it's one of the common ones, like Version
|
||||
if (telegram->type_id == EMS_TYPE_VERSION) {
|
||||
return "Version";
|
||||
@@ -1711,7 +1759,7 @@ std::string EMSdevice::telegram_type_name(std::shared_ptr<const Telegram> telegr
|
||||
}
|
||||
}
|
||||
|
||||
return std::string{};
|
||||
return "";
|
||||
}
|
||||
|
||||
// take a telegram_type_id and call the matching handler
|
||||
@@ -1722,7 +1770,9 @@ bool EMSdevice::handle_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
// if the data block is empty and we have not received data before, assume that this telegram
|
||||
// is not recognized by the bus master. So remove it from the automatic fetch list
|
||||
if (telegram->message_length == 0 && telegram->offset == 0 && !tf.received_) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("This telegram (%s) is not recognized by the EMS bus", tf.telegram_type_name_);
|
||||
#endif
|
||||
tf.fetch_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -47,15 +47,17 @@ class EMSdevice {
|
||||
// static functions, used outside the class like in console.cpp, command.cpp, emsesp.cpp, mqtt.cpp
|
||||
static const char * device_type_2_device_name(const uint8_t device_type);
|
||||
static uint8_t device_name_2_device_type(const char * topic);
|
||||
static std::string uom_to_string(uint8_t uom);
|
||||
static std::string tag_to_string(uint8_t tag, const bool translate = true);
|
||||
static std::string tag_to_mqtt(uint8_t tag);
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
|
||||
static const char * tag_to_string(uint8_t tag, const bool translate = true);
|
||||
static const char * uom_to_string(uint8_t uom);
|
||||
static const char * tag_to_mqtt(uint8_t tag);
|
||||
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
|
||||
const char * device_type_name(); // returns short non-translated device type name
|
||||
const char * device_type_2_device_name_translated(); // returns translated device type name
|
||||
|
||||
bool has_tag(const uint8_t tag) const;
|
||||
bool has_tags(const uint8_t tag) const;
|
||||
bool has_cmd(const char * cmd, const int8_t id) const;
|
||||
|
||||
inline uint8_t device_id() const {
|
||||
@@ -153,7 +155,7 @@ class EMSdevice {
|
||||
}
|
||||
}
|
||||
|
||||
inline void has_enumupdate(std::shared_ptr<const Telegram> telegram, uint8_t & value, const uint8_t index, uint8_t s = 0) {
|
||||
inline void has_enumupdate(std::shared_ptr<const Telegram> telegram, uint8_t & value, const uint8_t index, int8_t s = 0) {
|
||||
if (telegram->read_enumvalue(value, index, s)) {
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
@@ -176,7 +178,7 @@ class EMSdevice {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string brand_to_string();
|
||||
const char * brand_to_char();
|
||||
const std::string to_string();
|
||||
const std::string to_string_short();
|
||||
|
||||
@@ -290,7 +292,7 @@ class EMSdevice {
|
||||
|
||||
void mqtt_ha_entity_config_create();
|
||||
|
||||
std::string telegram_type_name(std::shared_ptr<const Telegram> telegram) const;
|
||||
const char * telegram_type_name(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void fetch_values();
|
||||
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
||||
@@ -317,9 +319,10 @@ class EMSdevice {
|
||||
};
|
||||
|
||||
enum DeviceType : uint8_t {
|
||||
SYSTEM = 0, // this is us (EMS-ESP)
|
||||
DALLASSENSOR, // for internal dallas sensors
|
||||
ANALOGSENSOR, // for internal analog sensors
|
||||
SYSTEM = 0, // this is us (EMS-ESP)
|
||||
TEMPERATURESENSOR, // for internal temperature sensors
|
||||
ANALOGSENSOR, // for internal analog sensors
|
||||
SCHEDULER,
|
||||
BOILER,
|
||||
THERMOSTAT,
|
||||
MIXER,
|
||||
@@ -333,6 +336,8 @@ class EMSdevice {
|
||||
PUMP,
|
||||
GENERIC,
|
||||
HEATSOURCE,
|
||||
CUSTOM,
|
||||
VENTILATION,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
@@ -376,6 +381,7 @@ class EMSdevice {
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HT3 = 3;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HEATPUMP = 4;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HYBRID = 5;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HIU = 6;
|
||||
|
||||
// Solar Module
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_SM10 = 1;
|
||||
@@ -408,7 +414,17 @@ class EMSdevice {
|
||||
uint8_t count_entities();
|
||||
bool has_entities() const;
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
/*
|
||||
void reserve_device_values(uint8_t elements) {
|
||||
devicevalues_.reserve(elements);
|
||||
}
|
||||
|
||||
void reserve_telegram_functions(uint8_t elements) {
|
||||
telegram_functions_.reserve(elements);
|
||||
}
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
void dump_value_info();
|
||||
#endif
|
||||
|
||||
@@ -422,15 +438,15 @@ class EMSdevice {
|
||||
uint8_t flags_ = 0;
|
||||
uint8_t brand_ = Brand::NO_BRAND;
|
||||
|
||||
bool ha_config_done_ = false;
|
||||
bool has_update_ = false;
|
||||
bool ha_config_done_ = false;
|
||||
bool has_update_ = false;
|
||||
|
||||
struct TelegramFunction {
|
||||
uint16_t telegram_type_id_; // it's type_id
|
||||
const char * telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
bool received_;
|
||||
process_function_p process_function_;
|
||||
const uint16_t telegram_type_id_; // it's type_id
|
||||
const char * telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
bool received_;
|
||||
const process_function_p process_function_;
|
||||
|
||||
TelegramFunction(uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, bool received, const process_function_p process_function)
|
||||
: telegram_type_id_(telegram_type_id)
|
||||
@@ -441,13 +457,9 @@ class EMSdevice {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
void debug_print_dv(const char * shortname);
|
||||
#endif
|
||||
|
||||
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||
|
||||
std::vector<DeviceValue> devicevalues_; // all the device values
|
||||
std::vector<DeviceValue> devicevalues_; // all the device values
|
||||
|
||||
std::vector<uint16_t> handlers_ignored_;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -107,14 +107,14 @@ DeviceValue::DeviceValue(uint8_t device_type,
|
||||
// must be an int of 4 bytes, 32bit aligned
|
||||
const char * DeviceValue::DeviceValueUOM_s[] = {
|
||||
|
||||
F_(uom_blank), F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0],
|
||||
F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv),
|
||||
F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_blank)
|
||||
F_(uom_blank), // 0
|
||||
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0], FL_(minutes)[0],
|
||||
F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0], F_(uom_dbm), F_(uom_fahrenheit),
|
||||
F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin), F_(uom_k), F_(uom_blank)
|
||||
|
||||
};
|
||||
|
||||
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevice.h
|
||||
// must be an int of 4 bytes, 32bit aligned
|
||||
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevicevalue.h
|
||||
const char * const * DeviceValue::DeviceValueTAG_s[] = {
|
||||
|
||||
FL_(tag_none), // ""
|
||||
@@ -160,7 +160,7 @@ const char * const * DeviceValue::DeviceValueTAG_s[] = {
|
||||
|
||||
};
|
||||
|
||||
// MQTT topics derived from tags
|
||||
// tags used in MQTT topic names. Macthes sequence from DeviceValueTAG_s
|
||||
const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
|
||||
|
||||
FL_(tag_none)[0], // ""
|
||||
@@ -207,7 +207,7 @@ const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
|
||||
};
|
||||
|
||||
// count #tags once at compile time
|
||||
size_t DeviceValue::tag_count = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(char * const *);
|
||||
uint8_t DeviceValue::NUM_TAGS = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(char * const *);
|
||||
|
||||
// checks whether the device value has an actual value
|
||||
// returns true if its valid
|
||||
@@ -252,6 +252,11 @@ bool DeviceValue::hasValue() const {
|
||||
return has_value;
|
||||
}
|
||||
|
||||
// See if the device value has a tag and it's not empty
|
||||
bool DeviceValue::has_tag() const {
|
||||
return ((tag < DeviceValue::NUM_TAGS) && (tag != TAG_NONE) && strlen(DeviceValueTAG_s[tag][0]));
|
||||
}
|
||||
|
||||
// set the min and max value for a device value
|
||||
// converts to signed int, which means rounding to an whole integer
|
||||
// returns false if there is no min/max needed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -145,12 +145,12 @@ class DeviceValue {
|
||||
DV_NUMOP_MUL15 = -15
|
||||
};
|
||||
|
||||
uint8_t device_type; // EMSdevice::DeviceType
|
||||
uint8_t tag; // DeviceValueTAG::*
|
||||
void * value_p; // pointer to variable of any type
|
||||
uint8_t type; // DeviceValueType::*
|
||||
const char * const ** options; // options as a flash char array
|
||||
const char * const * options_single; // options are not translated
|
||||
uint8_t device_type; // EMSdevice::DeviceType
|
||||
uint8_t tag; // DeviceValueTAG::*
|
||||
void * value_p; // pointer to variable of any type
|
||||
uint8_t type; // DeviceValueType::*
|
||||
const char * const ** options; // options as a flash char array
|
||||
const char * const * options_single; // options are not translated
|
||||
int8_t numeric_operator;
|
||||
uint8_t options_size; // number of options in the char array, calculated
|
||||
const char * const short_name; // used in MQTT and API
|
||||
@@ -179,6 +179,7 @@ class DeviceValue {
|
||||
uint8_t state);
|
||||
|
||||
bool hasValue() const;
|
||||
bool has_tag() const;
|
||||
bool get_min_max(int16_t & dv_set_min, uint16_t & dv_set_max);
|
||||
|
||||
void set_custom_minmax();
|
||||
@@ -205,7 +206,7 @@ class DeviceValue {
|
||||
static const char * DeviceValueUOM_s[];
|
||||
static const char * const * DeviceValueTAG_s[];
|
||||
static const char * const DeviceValueTAG_mqtt[];
|
||||
static size_t tag_count; // # tags
|
||||
static uint8_t NUM_TAGS; // # tags
|
||||
};
|
||||
|
||||
}; // namespace emsesp
|
||||
|
||||
446
src/emsesp.cpp
446
src/emsesp.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,6 +18,10 @@
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
static_assert(uuid::thread_safe, "uuid-common must be thread-safe");
|
||||
static_assert(uuid::log::thread_safe, "uuid-log must be thread-safe");
|
||||
static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe");
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
AsyncWebServer webServer(80);
|
||||
@@ -27,10 +31,14 @@ FS dummyFS;
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &dummyFS);
|
||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebEntityService EMSESP::webEntityService = WebEntityService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#else
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &LittleFS);
|
||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebSchedulerService EMSESP::webSchedulerService = WebSchedulerService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebEntityService EMSESP::webEntityService = WebEntityService(&webServer, &LittleFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#endif
|
||||
|
||||
WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
@@ -49,15 +57,18 @@ uuid::log::Logger EMSESP::logger() {
|
||||
return logger_;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::syslog::SyslogService System::syslog_;
|
||||
#endif
|
||||
|
||||
// The services
|
||||
RxService EMSESP::rxservice_; // incoming Telegram Rx handler
|
||||
TxService EMSESP::txservice_; // outgoing Telegram Tx handler
|
||||
Mqtt EMSESP::mqtt_; // mqtt handler
|
||||
System EMSESP::system_; // core system services
|
||||
Console EMSESP::console_; // telnet and serial console
|
||||
DallasSensor EMSESP::dallassensor_; // Dallas sensors
|
||||
AnalogSensor EMSESP::analogsensor_; // Analog sensors
|
||||
Shower EMSESP::shower_; // Shower logic
|
||||
RxService EMSESP::rxservice_; // incoming Telegram Rx handler
|
||||
TxService EMSESP::txservice_; // outgoing Telegram Tx handler
|
||||
Mqtt EMSESP::mqtt_; // mqtt handler
|
||||
System EMSESP::system_; // core system services
|
||||
TemperatureSensor EMSESP::temperaturesensor_; // Temperature sensors
|
||||
AnalogSensor EMSESP::analogsensor_; // Analog sensors
|
||||
Shower EMSESP::shower_; // Shower logic
|
||||
|
||||
// static/common variables
|
||||
uint16_t EMSESP::watch_id_ = WATCH_ID_NONE; // for when log is TRACE. 0 means no trace set
|
||||
@@ -65,6 +76,7 @@ uint8_t EMSESP::watch_ = 0; // trace off
|
||||
uint16_t EMSESP::read_id_ = WATCH_ID_NONE;
|
||||
bool EMSESP::read_next_ = false;
|
||||
uint16_t EMSESP::publish_id_ = 0;
|
||||
uint16_t EMSESP::response_id_ = 0;
|
||||
bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower()
|
||||
uint32_t EMSESP::last_fetch_ = 0;
|
||||
uint8_t EMSESP::publish_all_idx_ = 0;
|
||||
@@ -309,7 +321,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
|
||||
// Dump all entities to Serial out
|
||||
// this is intended to run within the OS with lots of available memory!
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
void EMSESP::dump_all_values(uuid::console::Shell & shell) {
|
||||
Serial.println("---- CSV START ----"); // marker use by py script
|
||||
// add header for CSV
|
||||
@@ -322,19 +334,27 @@ void EMSESP::dump_all_values(uuid::console::Shell & shell) {
|
||||
for (const auto & device : device_library_) {
|
||||
if (device_class.first == device.device_type) {
|
||||
uint8_t device_id = 0;
|
||||
// Mixer class looks at device_id to determine type, so fixing to 0x28 which will give all the settings except flowSetTemp
|
||||
if ((device.device_type == DeviceType::MIXER) && (device.flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS)) {
|
||||
// pick one as hc and the other as having wwc
|
||||
if (device.product_id == 160) { // MM100
|
||||
device_id = 0x28; // wwc
|
||||
// Mixer class looks at device_id to determine type and the tag
|
||||
// so fixing to 0x28 which will give all the settings except flowSetTemp
|
||||
if (device.device_type == DeviceType::MIXER) {
|
||||
if (device.flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
if (device.product_id == 160) { // MM100
|
||||
device_id = 0x28; // wwc
|
||||
} else {
|
||||
device_id = 0x20; // hc
|
||||
}
|
||||
} else {
|
||||
device_id = 0x20; // hc
|
||||
device_id = 0x20; // should cover all the other device types
|
||||
}
|
||||
}
|
||||
|
||||
// add the device and print out all the entities
|
||||
|
||||
// if (device.product_id == 69) { // only for testing mixer
|
||||
emsdevices.push_back(
|
||||
EMSFactory::add(device.device_type, device_id, device.product_id, "1.0", device.name, device.flags, EMSdevice::Brand::NO_BRAND));
|
||||
emsdevices.back()->dump_value_info(); // dump all the entity information
|
||||
emsdevices.back()->dump_value_info();
|
||||
// } // only for testing mixer
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -358,13 +378,12 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
// print header, with device type translated
|
||||
shell.printfln("%s: %s (%d)", emsdevice->device_type_2_device_name_translated(), emsdevice->to_string().c_str(), emsdevice->count_entities());
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN); // use max size
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE); // use max size
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::CONSOLE);
|
||||
|
||||
// print line
|
||||
uint8_t id = 0;
|
||||
for (JsonPair p : json) {
|
||||
const char * key = p.key().c_str();
|
||||
shell.printf(" %s: ", key);
|
||||
@@ -383,7 +402,6 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
|
||||
shell.print(COLOR_RESET);
|
||||
shell.println();
|
||||
id++;
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
@@ -391,15 +409,15 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
}
|
||||
}
|
||||
|
||||
// show Dallas temperature sensors and Analog sensors
|
||||
// show temperature sensors and Analog sensors
|
||||
void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
|
||||
if (dallassensor_.have_sensors()) {
|
||||
if (temperaturesensor_.have_sensors()) {
|
||||
shell.printfln("Temperature sensors:");
|
||||
char s[10];
|
||||
char s2[10];
|
||||
uint8_t fahrenheit = EMSESP::system_.fahrenheit() ? 2 : 0;
|
||||
|
||||
for (const auto & sensor : dallassensor_.sensors()) {
|
||||
for (const auto & sensor : temperaturesensor_.sensors()) {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
shell.printfln(" %s: %s%s °%c%s (offset %s, ID: %s)",
|
||||
sensor.name().c_str(),
|
||||
@@ -427,7 +445,7 @@ void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
|
||||
sensor.name().c_str(),
|
||||
COLOR_BRIGHT_GREEN,
|
||||
Helpers::render_value(s, sensor.value(), 2),
|
||||
EMSdevice::uom_to_string(sensor.uom()).c_str(),
|
||||
EMSdevice::uom_to_string(sensor.uom()),
|
||||
COLOR_RESET,
|
||||
Helpers::render_value(s2, sensor.factor(), 4),
|
||||
sensor.offset());
|
||||
@@ -461,20 +479,22 @@ void EMSESP::publish_all(bool force) {
|
||||
publish_device_values(EMSdevice::DeviceType::THERMOSTAT);
|
||||
publish_device_values(EMSdevice::DeviceType::SOLAR);
|
||||
publish_device_values(EMSdevice::DeviceType::MIXER);
|
||||
publish_other_values(); // switch and heat pump, ...
|
||||
publish_sensor_values(true); // includes dallas and analog sensors
|
||||
publish_other_values(); // switch and heat pump, ...
|
||||
webSchedulerService.publish();
|
||||
webEntityService.publish();
|
||||
publish_sensor_values(true); // includes temperature and analog sensors
|
||||
system_.send_heartbeat();
|
||||
}
|
||||
}
|
||||
|
||||
// on command "publish HA" loop and wait between devices for publishing all sensors
|
||||
// loop and wait between devices for publishing all values
|
||||
void EMSESP::publish_all_loop() {
|
||||
if (!Mqtt::connected() || !publish_all_idx_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// wait for free queue before sending next message, HA-messages are also queued
|
||||
if (!Mqtt::is_empty()) {
|
||||
if (Mqtt::publish_queued() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -493,6 +513,8 @@ void EMSESP::publish_all_loop() {
|
||||
break;
|
||||
case 5:
|
||||
publish_other_values(); // switch and heat pump
|
||||
webSchedulerService.publish(true);
|
||||
webEntityService.publish(true);
|
||||
break;
|
||||
case 6:
|
||||
publish_sensor_values(true, true);
|
||||
@@ -520,16 +542,16 @@ void EMSESP::reset_mqtt_ha() {
|
||||
emsdevice->ha_config_clear();
|
||||
}
|
||||
|
||||
// force the re-creating of the dallas and analog sensor topics (for HA)
|
||||
dallassensor_.reload();
|
||||
// force the re-creating of the temperature and analog sensor topics (for HA)
|
||||
temperaturesensor_.reload();
|
||||
analogsensor_.reload();
|
||||
}
|
||||
|
||||
// create json doc for the devices values and add to MQTT publish queue
|
||||
// this will also create the HA /config topic
|
||||
// this will also create the HA /config topic for each device value
|
||||
// generate_values_json is called to build the device value (dv) object array
|
||||
void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE);
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool need_publish = false;
|
||||
bool nested = (Mqtt::is_nested());
|
||||
@@ -540,7 +562,7 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
bool nest_created = false;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
if (nested && !nest_created && emsdevice->has_tag(tag)) {
|
||||
if (nested && !nest_created && emsdevice->has_tags(tag)) {
|
||||
json_hc = doc.createNestedObject(EMSdevice::tag_to_mqtt(tag));
|
||||
nest_created = true;
|
||||
}
|
||||
@@ -548,16 +570,17 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
}
|
||||
}
|
||||
if (need_publish && ((!nested && tag >= DeviceValueTAG::TAG_DEVICE_DATA_WW) || (tag == DeviceValueTAG::TAG_BOILER_DATA_WW))) {
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, tag), json);
|
||||
Mqtt::queue_publish(Mqtt::tag_to_topic(device_type, tag), json);
|
||||
json = doc.to<JsonObject>();
|
||||
need_publish = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_publish) {
|
||||
if (doc.overflowed()) {
|
||||
LOG_WARNING("MQTT buffer overflow, please use individual topics");
|
||||
}
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
Mqtt::queue_publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
}
|
||||
|
||||
// we want to create the /config topic after the data payload to prevent HA from throwing up a warning
|
||||
@@ -575,19 +598,21 @@ void EMSESP::publish_other_values() {
|
||||
publish_device_values(EMSdevice::DeviceType::SWITCH);
|
||||
publish_device_values(EMSdevice::DeviceType::HEATPUMP);
|
||||
publish_device_values(EMSdevice::DeviceType::HEATSOURCE);
|
||||
publish_device_values(EMSdevice::DeviceType::VENTILATION);
|
||||
// other devices without values yet
|
||||
// publish_device_values(EMSdevice::DeviceType::GATEWAY);
|
||||
// publish_device_values(EMSdevice::DeviceType::CONNECT);
|
||||
// publish_device_values(EMSdevice::DeviceType::ALERT);
|
||||
// publish_device_values(EMSdevice::DeviceType::PUMP);
|
||||
// publish_device_values(EMSdevice::DeviceType::GENERIC);
|
||||
webEntityService.publish();
|
||||
}
|
||||
|
||||
// publish both the dallas and analog sensor values
|
||||
// publish both the temperature and analog sensor values
|
||||
void EMSESP::publish_sensor_values(const bool time, const bool force) {
|
||||
if (dallas_enabled()) {
|
||||
if (dallassensor_.updated_values() || time || force) {
|
||||
dallassensor_.publish_values(force);
|
||||
if (sensor_enabled()) {
|
||||
if (temperaturesensor_.updated_values() || time || force) {
|
||||
temperaturesensor_.publish_values(force);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,28 +625,39 @@ void EMSESP::publish_sensor_values(const bool time, const bool force) {
|
||||
|
||||
// MQTT publish a telegram as raw data to the topic 'response'
|
||||
void EMSESP::publish_response(std::shared_ptr<const Telegram> telegram) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
static char * buffer = nullptr;
|
||||
static uint8_t offset;
|
||||
if (buffer == nullptr) {
|
||||
offset = telegram->offset; // store offset from first part
|
||||
buffer = new char[768]; // max 256 hex-codes, 255 spaces, 1 termination
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
strlcat(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str(), 768);
|
||||
if (response_id_ != 0) {
|
||||
strlcat(buffer, " ", 768);
|
||||
return;
|
||||
}
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_LARGE);
|
||||
char s[10];
|
||||
doc["src"] = Helpers::hextoa(s, telegram->src);
|
||||
doc["dest"] = Helpers::hextoa(s, telegram->dest);
|
||||
doc["type"] = Helpers::hextoa(s, telegram->type_id);
|
||||
doc["offset"] = Helpers::hextoa(s, offset);
|
||||
doc["data"] = buffer;
|
||||
|
||||
char buffer[100];
|
||||
doc["src"] = Helpers::hextoa(buffer, telegram->src);
|
||||
doc["dest"] = Helpers::hextoa(buffer, telegram->dest);
|
||||
doc["type"] = Helpers::hextoa(buffer, telegram->type_id);
|
||||
doc["offset"] = Helpers::hextoa(buffer, telegram->offset);
|
||||
strlcpy(buffer, Helpers::data_to_hex(telegram->message_data, telegram->message_length).c_str(), sizeof(buffer)); // telegram is without crc
|
||||
doc["data"] = buffer;
|
||||
|
||||
if (telegram->message_length <= 4) {
|
||||
if (telegram->message_length <= 4 && strlen(buffer) <= 11) {
|
||||
uint32_t value = 0;
|
||||
for (uint8_t i = 0; i < telegram->message_length; i++) {
|
||||
value = (value << 8) + telegram->message_data[i];
|
||||
}
|
||||
doc["value"] = value;
|
||||
}
|
||||
|
||||
Mqtt::publish("response", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("response", doc.as<JsonObject>());
|
||||
delete[] buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
|
||||
// builds json with the detail of each value, for a specific EMS device type or the dallas sensor
|
||||
// builds json with the detail of each value, for a specific EMS device type or the temperature sensor
|
||||
bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const int8_t id, const uint8_t devicetype) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice->device_type() == devicetype) {
|
||||
@@ -631,16 +667,24 @@ bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const in
|
||||
}
|
||||
}
|
||||
|
||||
// specific for the dallassensor
|
||||
if (devicetype == DeviceType::DALLASSENSOR) {
|
||||
EMSESP::dallassensor_.get_value_info(root, cmd, id);
|
||||
return true;
|
||||
// specific for the temperaturesensor
|
||||
if (devicetype == DeviceType::TEMPERATURESENSOR) {
|
||||
return EMSESP::temperaturesensor_.get_value_info(root, cmd, id);
|
||||
}
|
||||
|
||||
// analog sensor
|
||||
if (devicetype == DeviceType::ANALOGSENSOR) {
|
||||
EMSESP::analogsensor_.get_value_info(root, cmd, id);
|
||||
return true;
|
||||
return EMSESP::analogsensor_.get_value_info(root, cmd, id);
|
||||
}
|
||||
|
||||
// scheduler
|
||||
if (devicetype == DeviceType::SCHEDULER) {
|
||||
return EMSESP::webSchedulerService.get_value_info(root, cmd);
|
||||
}
|
||||
|
||||
// own entities
|
||||
if (devicetype == DeviceType::CUSTOM) {
|
||||
return EMSESP::webEntityService.get_value_info(root, cmd);
|
||||
}
|
||||
|
||||
char error[100];
|
||||
@@ -815,12 +859,19 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
|
||||
// returns false if there are none found
|
||||
bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
// if watching or reading...
|
||||
if ((telegram->type_id == read_id_) && (telegram->dest == txservice_.ems_bus_id())) {
|
||||
LOG_INFO("%s", pretty_telegram(telegram).c_str());
|
||||
if (Mqtt::send_response()) {
|
||||
if ((telegram->type_id == read_id_ || telegram->type_id == response_id_) && (telegram->dest == txservice_.ems_bus_id())) {
|
||||
if (telegram->type_id == response_id_) {
|
||||
if (!trace_raw_) {
|
||||
LOG_TRACE("%s", pretty_telegram(telegram).c_str());
|
||||
}
|
||||
if (!read_next_) {
|
||||
response_id_ = 0;
|
||||
}
|
||||
publish_response(telegram);
|
||||
} else {
|
||||
LOG_NOTICE("%s", pretty_telegram(telegram).c_str());
|
||||
}
|
||||
|
||||
// check if read is finished or gives more parts
|
||||
if (!read_next_) {
|
||||
read_id_ = WATCH_ID_NONE;
|
||||
}
|
||||
@@ -843,6 +894,9 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for custom entities reding this telegram
|
||||
webEntityService.get_value(telegram);
|
||||
|
||||
// check for common types, like the Version(0x02)
|
||||
if (telegram->type_id == EMSdevice::EMS_TYPE_VERSION) {
|
||||
process_version(telegram);
|
||||
@@ -872,7 +926,7 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
if (telegram->type_id == publish_id_) {
|
||||
publish_id_ = 0;
|
||||
}
|
||||
emsdevice->has_update(false); // reset flag
|
||||
emsdevice->has_update(false); // reset flag
|
||||
if (!Mqtt::publish_single()) {
|
||||
publish_device_values(emsdevice->device_type()); // publish to MQTT if we explicitly have too
|
||||
}
|
||||
@@ -916,7 +970,7 @@ bool EMSESP::device_exists(const uint8_t device_id) {
|
||||
// for each associated EMS device go and get its system information
|
||||
void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
if (emsdevices.empty()) {
|
||||
shell.printfln("No EMS devices detected. Try using 'scan devices' from the ems menu.");
|
||||
shell.printfln("No EMS devices detected");
|
||||
shell.println();
|
||||
return;
|
||||
}
|
||||
@@ -925,12 +979,14 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
shell.println();
|
||||
|
||||
// count the number of thermostats
|
||||
/*
|
||||
uint8_t num_thermostats = 0;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == DeviceType::THERMOSTAT)) {
|
||||
num_thermostats++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// for all device objects from emsdevice.h
|
||||
// so we keep a consistent order
|
||||
@@ -1040,6 +1096,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
name = "RF room temperature sensor";
|
||||
device_type = DeviceType::THERMOSTAT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_ROOMTHERMOSTAT || device_id == EMSdevice::EMS_DEVICE_ID_TADO_OLD) {
|
||||
// see https://github.com/emsesp/EMS-ESP32/issues/174
|
||||
name = "Generic thermostat";
|
||||
device_type = DeviceType::THERMOSTAT;
|
||||
flags = DeviceFlags::EMS_DEVICE_FLAG_RC10 | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE;
|
||||
@@ -1050,31 +1107,32 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
name = "Terminal";
|
||||
device_type = DeviceType::CONNECT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_SERVICEKEY) {
|
||||
name = "Service key";
|
||||
name = "Service Key";
|
||||
device_type = DeviceType::CONNECT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_CASCADE) {
|
||||
name = "Cascade";
|
||||
device_type = DeviceType::CONNECT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_EASYCOM) {
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_EASYCOM
|
||||
|| (device_id >= EMSdevice::EMS_DEVICE_ID_MODEM && device_id <= EMSdevice::EMS_DEVICE_ID_MODEM + 5)) {
|
||||
// see https://github.com/emsesp/EMS-ESP/issues/460#issuecomment-709553012
|
||||
name = "Modem";
|
||||
device_type = DeviceType::CONNECT;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_CONVERTER) {
|
||||
name = "Converter"; // generic
|
||||
name = "Converter"; // generic
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_CLOCK) {
|
||||
name = "Clock"; // generic
|
||||
device_type = DeviceType::CONTROLLER;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_CONTROLLER) {
|
||||
name = "Generic controller";
|
||||
name = "Generic Controller";
|
||||
device_type = DeviceType::CONTROLLER;
|
||||
} else if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER) {
|
||||
name = "Generic boiler";
|
||||
name = "Generic Boiler";
|
||||
device_type = DeviceType::BOILER;
|
||||
flags = DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP;
|
||||
LOG_WARNING("Unknown EMS boiler. Using generic profile. Please report on GitHub.");
|
||||
} else if (device_id >= 0x68 && device_id <= 0x6F) {
|
||||
// test for https://github.com/emsesp/EMS-ESP32/issues/882
|
||||
name = "Cascaded controller";
|
||||
name = "Cascaded Controller";
|
||||
device_type = DeviceType::CONTROLLER;
|
||||
} else {
|
||||
LOG_WARNING("Unrecognized EMS device (device ID 0x%02X, no product ID). Please report on GitHub.", device_id);
|
||||
@@ -1086,6 +1144,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
||||
emsdevices.push_back(EMSFactory::add(device_type, device_id, product_id, version, name, flags, brand));
|
||||
|
||||
// assign a unique ID. Note that this is not actual unique after a restart as it's dependent on the order that devices are found
|
||||
// can't be 0 otherwise web won't work
|
||||
emsdevices.back()->unique_id(++unique_id_count_);
|
||||
|
||||
// sort devices based on type
|
||||
@@ -1181,7 +1240,7 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & output, const int8_t
|
||||
bool nest_created = false;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
if (!nest_created && emsdevice->has_tag(tag)) {
|
||||
if (!nest_created && emsdevice->has_tags(tag)) {
|
||||
output_hc = output.createNestedObject(EMSdevice::tag_to_mqtt(tag));
|
||||
nest_created = true;
|
||||
}
|
||||
@@ -1193,8 +1252,8 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & output, const int8_t
|
||||
}
|
||||
|
||||
// send a read request, passing it into to the Tx Service, with optional offset and length
|
||||
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
|
||||
txservice_.read_request(type_id, dest, offset, length);
|
||||
void EMSESP::send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length, const bool front) {
|
||||
txservice_.read_request(type_id, dest, offset, length, front);
|
||||
}
|
||||
|
||||
// sends write request
|
||||
@@ -1257,9 +1316,10 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
} else if (first_value == TxService::TX_WRITE_FAIL) {
|
||||
LOG_ERROR("Last Tx write rejected by host");
|
||||
txservice_.send_poll(); // close the bus
|
||||
txservice_.reset_retry_count();
|
||||
tx_successful = true;
|
||||
}
|
||||
} else if (tx_state == Telegram::Operation::TX_READ && length == 1) {
|
||||
EMSbus::tx_state(Telegram::Operation::TX_READ); // reset Tx wait state
|
||||
return;
|
||||
} else if (tx_state == Telegram::Operation::TX_READ) {
|
||||
// got a telegram with data in it. See if the src/dest matches that from the last one we sent and continue to process it
|
||||
uint8_t src = data[0];
|
||||
@@ -1267,13 +1327,16 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
if (txservice_.is_last_tx(src, dest)) {
|
||||
LOG_DEBUG("Last Tx read successful");
|
||||
txservice_.increment_telegram_read_count();
|
||||
txservice_.send_poll(); // close the bus
|
||||
txservice_.reset_retry_count();
|
||||
tx_successful = true;
|
||||
|
||||
// if telegram is longer read next part with offset +25 for ems+ or +27 for ems1.0
|
||||
if ((length >= 31) && (txservice_.read_next_tx(data[3], length) == read_id_)) {
|
||||
// not for response to raw send commands without read_id set
|
||||
if ((response_id_ == 0 || read_id_ > 0) && (length >= 31) && (txservice_.read_next_tx(data[3], length) == read_id_)) {
|
||||
read_next_ = true;
|
||||
txservice_.send();
|
||||
} else {
|
||||
txservice_.send_poll(); // close the bus
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1327,93 +1390,10 @@ void EMSESP::incoming_telegram(uint8_t * data, const uint8_t length) {
|
||||
#endif
|
||||
Roomctrl::check((data[1] ^ 0x80 ^ rxservice_.ems_mask()), data); // check if there is a message for the roomcontroller
|
||||
|
||||
rxservice_.add(data, length); // add to RxQueue
|
||||
rxservice_.add(data, length); // add to RxQueue
|
||||
}
|
||||
}
|
||||
|
||||
// start all the core services
|
||||
// the services must be loaded in the correct order
|
||||
void EMSESP::start() {
|
||||
console_.start_serial();
|
||||
|
||||
// start the file system
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!LittleFS.begin(true)) {
|
||||
Serial.println("LittleFS Mount Failed. EMS-ESP stopped.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// do a quick scan of the filesystem to see if we have a /config folder
|
||||
// so we know if this is a new install or not
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File root = LittleFS.open("/config");
|
||||
bool factory_settings = !root;
|
||||
if (!root) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
Serial.println("No config found, assuming factory settings");
|
||||
#endif
|
||||
}
|
||||
root.close();
|
||||
#else
|
||||
bool factory_settings = false;
|
||||
#endif
|
||||
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
webLogService.begin(); // start web log service. now we can start capturing logs to the web log
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_NOTICE("System is running in Debug mode");
|
||||
#endif
|
||||
|
||||
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||
|
||||
// see if we're restoring a settings file
|
||||
if (system_.check_restore()) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
webSettingsService.begin(); // load EMS-ESP Application settings...
|
||||
|
||||
// do any system upgrades
|
||||
if (system_.check_upgrade(factory_settings)) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
system_.reload_settings(); // ... and store some of the settings locally
|
||||
|
||||
webCustomizationService.begin(); // load the customizations
|
||||
|
||||
// start telnet service if it's enabled
|
||||
if (system_.telnet_enabled()) {
|
||||
console_.start_telnet();
|
||||
}
|
||||
|
||||
// start all the EMS-ESP services
|
||||
mqtt_.start(); // mqtt init
|
||||
system_.start(); // starts commands, led, adc, button, network, syslog & uart
|
||||
LOG_INFO(("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str()); // welcome message
|
||||
|
||||
shower_.start(); // initialize shower timer and shower alert
|
||||
dallassensor_.start(); // Dallas external sensors
|
||||
analogsensor_.start(); // Analog external sensors
|
||||
webLogService.start(); // apply settings to weblog service
|
||||
|
||||
// Load our library of known devices into stack mem. Names are stored in Flash memory
|
||||
device_library_ = {
|
||||
#include "device_library.h"
|
||||
};
|
||||
LOG_INFO("Loaded EMS device library (%d records)", device_library_.size());
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Mqtt::on_connect(); // simulate an MQTT connection
|
||||
#endif
|
||||
|
||||
webServer.begin(); // start the web server
|
||||
}
|
||||
|
||||
// fetch devices one by one
|
||||
void EMSESP::scheduled_fetch_values() {
|
||||
static uint8_t no = 0;
|
||||
@@ -1432,34 +1412,156 @@ void EMSESP::scheduled_fetch_values() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
webEntityService.fetch();
|
||||
no = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EMSESP main class
|
||||
|
||||
EMSESP::EMSESP()
|
||||
#ifndef EMSESP_STANDALONE
|
||||
: telnet_([this](Stream & stream, const IPAddress & addr, uint16_t port) -> std::shared_ptr<uuid::console::Shell> {
|
||||
return std::make_shared<emsesp::EMSESPConsole>(*this, stream, addr, port);
|
||||
})
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// start all the core services
|
||||
// the services must be loaded in the correct order
|
||||
void EMSESP::start() {
|
||||
serial_console_.begin(SERIAL_CONSOLE_BAUD_RATE);
|
||||
|
||||
shell_ = std::make_shared<EMSESPConsole>(*this, serial_console_, true);
|
||||
shell_->maximum_log_messages(100);
|
||||
shell_->start();
|
||||
#if defined(EMSESP_DEBUG)
|
||||
shell_->log_level(uuid::log::Level::DEBUG);
|
||||
#else
|
||||
shell_->log_level(uuid::log::Level::TRACE);
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
shell_->add_flags(CommandFlags::ADMIN); // always start in su/admin mode when running tests
|
||||
#endif
|
||||
|
||||
// start the file system
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!LittleFS.begin(true)) {
|
||||
Serial.println("LittleFS Mount Failed. EMS-ESP stopped.");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// do a quick scan of the filesystem to see if we have a /config folder
|
||||
// so we know if this is a new install or not
|
||||
#ifndef EMSESP_STANDALONE
|
||||
File root = LittleFS.open("/config");
|
||||
bool factory_settings = !root;
|
||||
if (!root) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Serial.println("No config found, assuming factory settings");
|
||||
#endif
|
||||
}
|
||||
root.close();
|
||||
#else
|
||||
bool factory_settings = false;
|
||||
#endif
|
||||
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
webLogService.begin(); // start web log service. now we can start capturing logs to the web log
|
||||
|
||||
LOG_INFO("Starting EMS-ESP version %s", EMSESP_APP_VERSION); // welcome message
|
||||
LOG_DEBUG("System is running in Debug mode");
|
||||
LOG_INFO("Last system reset reason Core0: %s, Core1: %s", system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||
|
||||
// see if we're restoring a settings file
|
||||
if (system_.check_restore()) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
webSettingsService.begin(); // load EMS-ESP Application settings...
|
||||
|
||||
// do any system upgrades
|
||||
if (system_.check_upgrade(factory_settings)) {
|
||||
LOG_WARNING("System needs a restart to apply new settings. Please wait.");
|
||||
system_.system_restart();
|
||||
};
|
||||
|
||||
system_.reload_settings(); // ... and store some of the settings locally
|
||||
|
||||
webCustomizationService.begin(); // load the customizations
|
||||
webSchedulerService.begin(); // load the scheduler events
|
||||
webEntityService.begin(); // load the custom telegram reads
|
||||
|
||||
// start telnet service if it's enabled
|
||||
// default idle is 10 minutes, default write timeout is 0 (automatic)
|
||||
// note, this must be started after the network/wifi for ESP32 otherwise it'll crash
|
||||
if (system_.telnet_enabled()) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
telnet_.start();
|
||||
telnet_.initial_idle_timeout(3600); // in sec, one hour idle timeout
|
||||
telnet_.default_write_timeout(1000); // in ms, socket timeout 1 second
|
||||
#endif
|
||||
}
|
||||
|
||||
mqtt_.start(); // mqtt init
|
||||
system_.start(); // starts commands, led, adc, button, network (sets hostname), syslog & uart
|
||||
shower_.start(); // initialize shower timer and shower alert
|
||||
temperaturesensor_.start(); // Temperature external sensors
|
||||
analogsensor_.start(); // Analog external sensors
|
||||
webLogService.start(); // apply settings to weblog service
|
||||
|
||||
// Load our library of known devices into stack mem. Names are stored in Flash memory
|
||||
device_library_ = {
|
||||
#include "device_library.h"
|
||||
};
|
||||
LOG_INFO("Loaded EMS device library (%d records)", device_library_.size());
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Mqtt::on_connect(); // simulate an MQTT connection
|
||||
#endif
|
||||
|
||||
webServer.begin(); // start the web server
|
||||
}
|
||||
|
||||
// main loop calling all services
|
||||
void EMSESP::loop() {
|
||||
esp8266React.loop(); // web services
|
||||
system_.loop(); // does LED and checks system health, and syslog service
|
||||
|
||||
// if we're doing an OTA upload, skip MQTT and EMS
|
||||
// if we're doing an OTA upload, skip everything except from console refresh
|
||||
if (!system_.upload_status()) {
|
||||
webLogService.loop(); // log in Web UI
|
||||
rxservice_.loop(); // process any incoming Rx telegrams
|
||||
shower_.loop(); // check for shower on/off
|
||||
dallassensor_.loop(); // read dallas sensor temperatures
|
||||
analogsensor_.loop(); // read analog sensor values
|
||||
publish_all_loop(); // with HA messages in parts to avoid flooding the mqtt queue
|
||||
mqtt_.loop(); // sends out anything in the MQTT queue
|
||||
// service loops
|
||||
webLogService.loop(); // log in Web UI
|
||||
rxservice_.loop(); // process any incoming Rx telegrams
|
||||
shower_.loop(); // check for shower on/off
|
||||
temperaturesensor_.loop(); // read sensor temperatures
|
||||
analogsensor_.loop(); // read analog sensor values
|
||||
publish_all_loop(); // with HA messages in parts to avoid flooding the mqtt queue
|
||||
mqtt_.loop(); // sends out anything in the MQTT queue
|
||||
webSchedulerService.loop(); // handle any scheduled jobs
|
||||
|
||||
// force a query on the EMS devices to fetch latest data at a set interval (1 min)
|
||||
scheduled_fetch_values();
|
||||
}
|
||||
|
||||
console_.loop(); // telnet/serial console
|
||||
uuid::loop();
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/78#issuecomment-877599145
|
||||
// delay(1); // helps telnet catch up. don't think its needed in ESP32 >3.1.0?
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (system_.telnet_enabled()) {
|
||||
telnet_.loop();
|
||||
}
|
||||
#else
|
||||
if (!shell_->running()) {
|
||||
::exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
Shell::loop_all();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
96
src/emsesp.h
96
src/emsesp.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -43,8 +43,10 @@
|
||||
#include "web/WebDataService.h"
|
||||
#include "web/WebSettingsService.h"
|
||||
#include "web/WebCustomizationService.h"
|
||||
#include "web/WebSchedulerService.h"
|
||||
#include "web/WebAPIService.h"
|
||||
#include "web/WebLogService.h"
|
||||
#include "web/WebEntityService.h"
|
||||
|
||||
#include "emsdevicevalue.h"
|
||||
#include "emsdevice.h"
|
||||
@@ -52,9 +54,10 @@
|
||||
#include "telegram.h"
|
||||
#include "mqtt.h"
|
||||
#include "system.h"
|
||||
#include "dallassensor.h"
|
||||
#include "temperaturesensor.h"
|
||||
#include "analogsensor.h"
|
||||
#include "console.h"
|
||||
#include "console_stream.h"
|
||||
#include "shower.h"
|
||||
#include "roomcontrol.h"
|
||||
#include "command.h"
|
||||
@@ -62,25 +65,16 @@
|
||||
|
||||
#define WATCH_ID_NONE 0 // no watch id set
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#define EMSESP_JSON_SIZE_HA_CONFIG 1024 // for HA config payloads, using StaticJsonDocument
|
||||
#else
|
||||
#define EMSESP_JSON_SIZE_HA_CONFIG 2024 // for HA config payloads, using StaticJsonDocument
|
||||
#endif
|
||||
#define EMSESP_JSON_SIZE_SMALL 256 // for smaller json docs, using StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_MEDIUM 768 // for medium json docs from ems devices, using StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_LARGE 1024 // for large json docs from ems devices, like boiler or thermostat data, using StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_MEDIUM_DYN 1024 // for large json docs, using DynamicJsonDocument
|
||||
#define EMSESP_JSON_SIZE_LARGE_DYN 2048 // for very large json docs, using DynamicJsonDocument
|
||||
// uses StaticJsonDocument
|
||||
#define EMSESP_JSON_SIZE_SMALL 256
|
||||
#define EMSESP_JSON_SIZE_MEDIUM 768
|
||||
#define EMSESP_JSON_SIZE_LARGE 1024 // used in forming HA config payloads, also in *AsyncJsonResponse
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#define EMSESP_JSON_SIZE_XLARGE_DYN 4096 // for very very large json docs, using DynamicJsonDocument
|
||||
#else
|
||||
#define EMSESP_JSON_SIZE_XLARGE_DYN 16384 // for very very large json docs, using DynamicJsonDocument
|
||||
#endif
|
||||
|
||||
#define EMSESP_JSON_SIZE_XXLARGE_DYN 16384 // for extra very very large json docs, using DynamicJsonDocument
|
||||
#define EMSESP_JSON_SIZE_XXXLARGE_DYN 20480 // web output (maybe for 4 hc)
|
||||
// used in larger buffers like DynamicJsonDocument
|
||||
#define EMSESP_JSON_SIZE_XLARGE 2048
|
||||
#define EMSESP_JSON_SIZE_XXLARGE 4096
|
||||
#define EMSESP_JSON_SIZE_XXXLARGE 16384
|
||||
#define EMSESP_JSON_SIZE_XXXXLARGE 20480 // web output
|
||||
|
||||
// helpers for callback functions
|
||||
#define MAKE_PF_CB(__f) [&](std::shared_ptr<const Telegram> t) { __f(t); } // for Process Function callbacks to EMSDevice::process_function_p
|
||||
@@ -94,13 +88,18 @@ using DeviceValueState = emsesp::DeviceValue::DeviceValueState;
|
||||
using DeviceValueTAG = emsesp::DeviceValue::DeviceValueTAG;
|
||||
using DeviceValueNumOp = emsesp::DeviceValue::DeviceValueNumOp;
|
||||
|
||||
|
||||
class Shower; // forward declaration for compiler
|
||||
// forward declarations for compiler
|
||||
class EMSESPShell;
|
||||
class Shower;
|
||||
|
||||
class EMSESP {
|
||||
public:
|
||||
static void start();
|
||||
static void loop();
|
||||
EMSESP();
|
||||
~EMSESP() = default;
|
||||
virtual void start();
|
||||
virtual void loop();
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
|
||||
static void publish_device_values(uint8_t device_type);
|
||||
static void publish_other_values();
|
||||
@@ -118,7 +117,7 @@ class EMSESP {
|
||||
static bool process_telegram(std::shared_ptr<const Telegram> telegram);
|
||||
static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0);
|
||||
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0, const bool front = false);
|
||||
static void send_write_request(const uint16_t type_id,
|
||||
const uint8_t dest,
|
||||
const uint8_t offset,
|
||||
@@ -149,8 +148,8 @@ class EMSESP {
|
||||
|
||||
static void incoming_telegram(uint8_t * data, const uint8_t length);
|
||||
|
||||
static bool dallas_enabled() {
|
||||
return (dallassensor_.dallas_enabled());
|
||||
static bool sensor_enabled() {
|
||||
return (temperaturesensor_.sensor_enabled());
|
||||
}
|
||||
|
||||
static bool analog_enabled() {
|
||||
@@ -164,7 +163,7 @@ class EMSESP {
|
||||
}
|
||||
|
||||
static void watch(uint8_t watch) {
|
||||
watch_ = watch; // 0=off, 1=on, 2=raw
|
||||
watch_ = watch; // 0=off, 1=on, 2=raw
|
||||
if (watch == WATCH_OFF) {
|
||||
watch_id_ = 0; // reset watch id if watch is disabled
|
||||
}
|
||||
@@ -177,6 +176,10 @@ class EMSESP {
|
||||
read_id_ = id;
|
||||
}
|
||||
|
||||
static void set_response_id(uint16_t id) {
|
||||
response_id_ = id;
|
||||
}
|
||||
|
||||
static bool wait_validate() {
|
||||
return (wait_validate_ != 0);
|
||||
}
|
||||
@@ -215,14 +218,13 @@ class EMSESP {
|
||||
static std::vector<std::unique_ptr<EMSdevice>> emsdevices;
|
||||
|
||||
// services
|
||||
static Mqtt mqtt_;
|
||||
static System system_;
|
||||
static DallasSensor dallassensor_;
|
||||
static AnalogSensor analogsensor_;
|
||||
static Console console_;
|
||||
static Shower shower_;
|
||||
static RxService rxservice_;
|
||||
static TxService txservice_;
|
||||
static Mqtt mqtt_;
|
||||
static System system_;
|
||||
static TemperatureSensor temperaturesensor_;
|
||||
static AnalogSensor analogsensor_;
|
||||
static Shower shower_;
|
||||
static RxService rxservice_;
|
||||
static TxService txservice_;
|
||||
|
||||
// web controllers
|
||||
static ESP8266React esp8266React;
|
||||
@@ -232,14 +234,10 @@ class EMSESP {
|
||||
static WebAPIService webAPIService;
|
||||
static WebLogService webLogService;
|
||||
static WebCustomizationService webCustomizationService;
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
static WebSchedulerService webSchedulerService;
|
||||
static WebEntityService webEntityService;
|
||||
|
||||
private:
|
||||
EMSESP() = delete;
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static std::string device_tostring(const uint8_t device_id);
|
||||
static void process_UBADevices(std::shared_ptr<const Telegram> telegram);
|
||||
static void process_version(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -265,6 +263,7 @@ class EMSESP {
|
||||
static uint16_t read_id_;
|
||||
static bool read_next_;
|
||||
static uint16_t publish_id_;
|
||||
static uint16_t response_id_;
|
||||
static bool tap_water_active_;
|
||||
static uint8_t publish_all_idx_;
|
||||
static uint8_t unique_id_count_;
|
||||
@@ -272,6 +271,19 @@ class EMSESP {
|
||||
static uint16_t wait_validate_;
|
||||
static bool wait_km_;
|
||||
static uint32_t last_fetch_;
|
||||
|
||||
// UUID stuff
|
||||
static constexpr auto & serial_console_ = Serial;
|
||||
static constexpr unsigned long SERIAL_CONSOLE_BAUD_RATE = 115200;
|
||||
|
||||
std::shared_ptr<EMSESPShell> shell_;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::telnet::TelnetService telnet_;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// EMSESP();
|
||||
static uuid::log::Logger logger_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include "system.h"
|
||||
#include "mqtt.h"
|
||||
#include "dallassensor.h"
|
||||
#include "temperaturesensor.h"
|
||||
#include "version.h"
|
||||
#include "default_settings.h"
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
// forward declarators
|
||||
// forward declarator
|
||||
// used to bind EMS-ESP functions to external frameworks
|
||||
namespace emsesp {
|
||||
class EMSESP {
|
||||
public:
|
||||
static Mqtt mqtt_;
|
||||
static System system_;
|
||||
static DallasSensor dallassensor_;
|
||||
static Mqtt mqtt_;
|
||||
static System system_;
|
||||
static TemperatureSensor temperaturesensor_;
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
static ESP8266React esp8266React;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
106
src/helpers.cpp
106
src/helpers.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -277,7 +277,7 @@ char * Helpers::render_value(char * result, const double value, const int8_t for
|
||||
// format: 0=no division, other divide by the value given and render with a decimal point
|
||||
char * Helpers::render_value(char * result, const int32_t value, const int8_t format, const uint8_t fahrenheit) {
|
||||
int32_t new_value = fahrenheit ? format ? value * 1.8 + 32 * format * (fahrenheit - 1) : value * 1.8 + 32 * (fahrenheit - 1) : value;
|
||||
char s[10] = {0};
|
||||
char s[13] = {0};
|
||||
// just print it if no conversion required (format = 0)
|
||||
if (!format) {
|
||||
strlcpy(result, itoa(new_value, s, 10), sizeof(s)); // format is 0
|
||||
@@ -385,7 +385,7 @@ std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) {
|
||||
*p++ = buffer[1];
|
||||
*p++ = ' '; // space
|
||||
}
|
||||
*--p = '\0'; // null terminate just in case, loosing the trailing space
|
||||
*--p = '\0'; // null terminate just in case, loosing the trailing space
|
||||
|
||||
return std::string(str);
|
||||
}
|
||||
@@ -442,7 +442,6 @@ int Helpers::atoint(const char * value) {
|
||||
|
||||
// rounds a number to 2 decimal places
|
||||
// example: round2(3.14159) -> 3.14
|
||||
// From mvdp:
|
||||
// The conversion to Fahrenheit is different for absolute temperatures and relative temperatures like hysteresis.
|
||||
// fahrenheit=0 - off, no conversion
|
||||
// fahrenheit=1 - relative, 1.8t
|
||||
@@ -647,7 +646,7 @@ bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * co
|
||||
std::string s_on = Helpers::translated_word(FL_(on));
|
||||
std::string s_off = Helpers::translated_word(FL_(off));
|
||||
|
||||
// stops when a nullptr is found, which is the end delimeter of a MAKE_PSTR_LIST()
|
||||
// stops when a nullptr is found, which is the end delimeter of a MAKE_TRANSLATION()
|
||||
// could use count_items() to avoid buffer over-run but this works
|
||||
for (value_ui = 0; strs[value_ui]; value_ui++) {
|
||||
std::string enum_str = toLower((strs[value_ui]));
|
||||
@@ -682,39 +681,49 @@ std::string Helpers::toUpper(std::string const & s) {
|
||||
// capitalizes one UTF-8 character in char array
|
||||
// works with Latin1 (1 byte), Polish amd some other (2 bytes) characters
|
||||
// TODO add special characters that occur in other supported languages
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#endif
|
||||
void Helpers::CharToUpperUTF8(char * c) {
|
||||
auto p = (c + 1); // pointer to 2nd char of 2-byte unicode char
|
||||
char p_v = *p; // value of 2nd char in 2-byte unicode char
|
||||
|
||||
switch (*c) {
|
||||
case 0xC3:
|
||||
case (char)0xC3:
|
||||
// grave, acute, circumflex, diaeresis, etc.
|
||||
if ((*(c + 1) >= 0xA0) && (*(c + 1) <= 0xBE)) {
|
||||
*(c + 1) -= 0x20;
|
||||
if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) {
|
||||
*p -= 0x20;
|
||||
}
|
||||
break;
|
||||
case 0xC4:
|
||||
switch (*(c + 1)) {
|
||||
case 0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84)
|
||||
case 0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86)
|
||||
case 0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98)
|
||||
*(c + 1) -= 1;
|
||||
case (char)0xC4:
|
||||
switch (p_v) {
|
||||
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84)
|
||||
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86)
|
||||
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98)
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0xC5:
|
||||
switch (*(c + 1)) {
|
||||
case 0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81)
|
||||
case 0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83)
|
||||
case 0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A)
|
||||
case 0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9)
|
||||
case 0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB)
|
||||
*(c + 1) -= 1;
|
||||
case (char)0xC5:
|
||||
switch (p_v) {
|
||||
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81)
|
||||
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83)
|
||||
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A)
|
||||
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9)
|
||||
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB)
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*c = toupper(*c); //works on Latin1 letters
|
||||
*c = toupper(*c); // works on Latin1 letters
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// replace char in char string
|
||||
void Helpers::replace_char(char * str, char find, char replace) {
|
||||
@@ -774,4 +783,55 @@ const char * Helpers::translated_word(const char * const * strings, const bool f
|
||||
return strings[index];
|
||||
}
|
||||
|
||||
uint16_t Helpers::string2minutes(const std::string & str) {
|
||||
uint8_t i = 0;
|
||||
uint16_t res = 0;
|
||||
uint16_t tmp = 0;
|
||||
uint8_t state = 0;
|
||||
|
||||
while (str[i] != '\0') {
|
||||
// If we got a digit
|
||||
if (str[i] >= '0' && str[i] <= '9') {
|
||||
tmp = tmp * 10 + str[i] - '0';
|
||||
}
|
||||
// Or if we got a colon
|
||||
else if (str[i] == ':') {
|
||||
// If we were reading the hours
|
||||
if (state == 0) {
|
||||
res = 60 * tmp;
|
||||
}
|
||||
// Or if we were reading the minutes
|
||||
else if (state == 1) {
|
||||
if (tmp > 60) {
|
||||
return 0;
|
||||
}
|
||||
Serial.print("*");
|
||||
Serial.print(tmp);
|
||||
Serial.println("*");
|
||||
|
||||
res += tmp;
|
||||
}
|
||||
// Or we got an extra colon
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
state++;
|
||||
tmp = 0;
|
||||
}
|
||||
|
||||
// Or we got something wrong
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (state == 1 && tmp < 60) {
|
||||
return res + tmp;
|
||||
} else {
|
||||
return 0; // Or if we were not, something is wrong in the given string
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -49,6 +49,7 @@ class Helpers {
|
||||
static int atoint(const char * value);
|
||||
static bool check_abs(const int32_t i);
|
||||
static uint32_t abs(const int32_t i);
|
||||
static uint16_t string2minutes(const std::string & str);
|
||||
|
||||
static float transformNumFloat(float value, const int8_t numeric_operator, const uint8_t fahrenheit = 0);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -27,309 +27,330 @@
|
||||
*/
|
||||
|
||||
// common words
|
||||
MAKE_PSTR_WORD(debug)
|
||||
MAKE_PSTR_WORD(exit)
|
||||
MAKE_PSTR_WORD(help)
|
||||
MAKE_PSTR_WORD(log)
|
||||
MAKE_PSTR_WORD(logout)
|
||||
MAKE_PSTR_WORD(enabled)
|
||||
MAKE_PSTR_WORD(disabled)
|
||||
MAKE_PSTR_WORD(set)
|
||||
MAKE_PSTR_WORD(show)
|
||||
MAKE_PSTR_WORD(su)
|
||||
MAKE_PSTR_WORD(name)
|
||||
MAKE_PSTR_WORD(scan)
|
||||
MAKE_PSTR_WORD(password)
|
||||
MAKE_PSTR_WORD(read)
|
||||
MAKE_PSTR_WORD(version)
|
||||
MAKE_PSTR_WORD(values)
|
||||
MAKE_PSTR_WORD(system)
|
||||
MAKE_PSTR_WORD(fetch)
|
||||
MAKE_PSTR_WORD(restart)
|
||||
MAKE_PSTR_WORD(format)
|
||||
MAKE_PSTR_WORD(raw)
|
||||
MAKE_PSTR_WORD(watch)
|
||||
MAKE_PSTR_WORD(syslog)
|
||||
MAKE_PSTR_WORD(send)
|
||||
MAKE_PSTR_WORD(telegram)
|
||||
MAKE_PSTR_WORD(bus_id)
|
||||
MAKE_PSTR_WORD(tx_mode)
|
||||
MAKE_PSTR_WORD(ems)
|
||||
MAKE_PSTR_WORD(devices)
|
||||
MAKE_PSTR_WORD(shower)
|
||||
MAKE_PSTR_WORD(mqtt)
|
||||
MAKE_PSTR_WORD(emsesp)
|
||||
MAKE_PSTR_WORD(connected)
|
||||
MAKE_PSTR_WORD(disconnected)
|
||||
MAKE_PSTR_WORD(passwd)
|
||||
MAKE_PSTR_WORD(hostname)
|
||||
MAKE_PSTR_WORD(wifi)
|
||||
MAKE_PSTR_WORD(reconnect)
|
||||
MAKE_PSTR_WORD(ssid)
|
||||
MAKE_PSTR_WORD(heartbeat)
|
||||
MAKE_PSTR_WORD(users)
|
||||
MAKE_PSTR_WORD(publish)
|
||||
MAKE_PSTR_WORD(timeout)
|
||||
MAKE_PSTR_WORD(board_profile)
|
||||
MAKE_PSTR_WORD(setvalue)
|
||||
MAKE_WORD(exit)
|
||||
MAKE_WORD(help)
|
||||
MAKE_WORD(log)
|
||||
MAKE_WORD(set)
|
||||
MAKE_WORD(show)
|
||||
MAKE_WORD(su)
|
||||
MAKE_WORD(scan)
|
||||
MAKE_WORD(password)
|
||||
MAKE_WORD(read)
|
||||
MAKE_WORD(values)
|
||||
MAKE_WORD(system)
|
||||
MAKE_WORD(fetch)
|
||||
MAKE_WORD(restart)
|
||||
MAKE_WORD(format)
|
||||
MAKE_WORD(raw)
|
||||
MAKE_WORD(watch)
|
||||
MAKE_WORD(syslog)
|
||||
MAKE_WORD(send)
|
||||
MAKE_WORD(telegram)
|
||||
MAKE_WORD(bus_id)
|
||||
MAKE_WORD(tx_mode)
|
||||
MAKE_WORD(ems)
|
||||
MAKE_WORD(devices)
|
||||
MAKE_WORD(shower)
|
||||
MAKE_WORD(mqtt)
|
||||
MAKE_WORD(emsesp)
|
||||
MAKE_WORD(connected)
|
||||
MAKE_WORD(disconnected)
|
||||
MAKE_WORD(passwd)
|
||||
MAKE_WORD(hostname)
|
||||
MAKE_WORD(wifi)
|
||||
MAKE_WORD(reconnect)
|
||||
MAKE_WORD(ssid)
|
||||
MAKE_WORD(heartbeat)
|
||||
MAKE_WORD(users)
|
||||
MAKE_WORD(publish)
|
||||
MAKE_WORD(board_profile)
|
||||
MAKE_WORD(setvalue)
|
||||
|
||||
// for commands
|
||||
MAKE_PSTR_WORD(call)
|
||||
MAKE_PSTR_WORD(cmd)
|
||||
MAKE_PSTR_WORD(id)
|
||||
MAKE_PSTR_WORD(hc)
|
||||
MAKE_PSTR_WORD(wwc)
|
||||
MAKE_PSTR_WORD(device)
|
||||
MAKE_PSTR_WORD(data)
|
||||
MAKE_PSTR_WORD(command)
|
||||
MAKE_PSTR_WORD(commands)
|
||||
MAKE_PSTR_WORD(info)
|
||||
MAKE_PSTR_WORD(settings)
|
||||
MAKE_PSTR_WORD(customizations)
|
||||
MAKE_PSTR_WORD(value)
|
||||
MAKE_PSTR_WORD(entities)
|
||||
MAKE_WORD(call)
|
||||
MAKE_WORD(cmd)
|
||||
MAKE_WORD(id)
|
||||
MAKE_WORD(hc)
|
||||
MAKE_WORD(wwc)
|
||||
MAKE_WORD(device)
|
||||
MAKE_WORD(data)
|
||||
MAKE_WORD(command)
|
||||
MAKE_WORD(commands)
|
||||
MAKE_WORD(info)
|
||||
MAKE_WORD(settings)
|
||||
MAKE_WORD(value)
|
||||
MAKE_WORD(entities)
|
||||
|
||||
// device types - lowercase, used in MQTT
|
||||
MAKE_PSTR_WORD(boiler)
|
||||
MAKE_PSTR_WORD(thermostat)
|
||||
MAKE_PSTR_WORD(switch)
|
||||
MAKE_PSTR_WORD(solar)
|
||||
MAKE_PSTR_WORD(mixer)
|
||||
MAKE_PSTR_WORD(gateway)
|
||||
MAKE_PSTR_WORD(controller)
|
||||
MAKE_PSTR_WORD(connect)
|
||||
MAKE_PSTR_WORD(heatpump)
|
||||
MAKE_PSTR_WORD(generic)
|
||||
MAKE_PSTR_WORD(analogsensor)
|
||||
MAKE_PSTR_WORD(dallassensor)
|
||||
MAKE_PSTR_WORD(alert)
|
||||
MAKE_PSTR_WORD(pump)
|
||||
MAKE_PSTR_WORD(heatsource)
|
||||
MAKE_WORD(boiler)
|
||||
MAKE_WORD(thermostat)
|
||||
MAKE_WORD(switch)
|
||||
MAKE_WORD(solar)
|
||||
MAKE_WORD(mixer)
|
||||
MAKE_WORD(gateway)
|
||||
MAKE_WORD(controller)
|
||||
MAKE_WORD(connect)
|
||||
MAKE_WORD(heatpump)
|
||||
MAKE_WORD(generic)
|
||||
MAKE_WORD(analogsensor)
|
||||
MAKE_WORD(temperaturesensor)
|
||||
MAKE_WORD(alert)
|
||||
MAKE_WORD(pump)
|
||||
MAKE_WORD(heatsource)
|
||||
MAKE_WORD(scheduler)
|
||||
MAKE_WORD(custom)
|
||||
MAKE_WORD(ventilation)
|
||||
|
||||
MAKE_PSTR(number, "number")
|
||||
MAKE_PSTR(enum, "enum")
|
||||
MAKE_PSTR(text, "text")
|
||||
// brands
|
||||
MAKE_WORD_CUSTOM(bosch, "Bosch")
|
||||
MAKE_WORD_CUSTOM(junkers, "Junkers")
|
||||
MAKE_WORD_CUSTOM(buderus, "Buderus")
|
||||
MAKE_WORD_CUSTOM(nefit, "Nefit")
|
||||
MAKE_WORD_CUSTOM(sieger, "Sieger")
|
||||
MAKE_WORD_CUSTOM(worcester, "Worcester")
|
||||
MAKE_WORD_CUSTOM(ivt, "IVT")
|
||||
MAKE_WORD_CUSTOM(no_brand, "")
|
||||
|
||||
// types
|
||||
MAKE_WORD_CUSTOM(number, "number")
|
||||
MAKE_WORD_CUSTOM(enum, "enum")
|
||||
MAKE_WORD_CUSTOM(text, "text")
|
||||
|
||||
// Console
|
||||
MAKE_PSTR(EMSESP, "EMS-ESP")
|
||||
MAKE_PSTR(host_fmt, "Host: %s")
|
||||
MAKE_PSTR(port_fmt, "Port: %d")
|
||||
MAKE_PSTR(hostname_fmt, "Hostname: %s")
|
||||
MAKE_PSTR(board_profile_fmt, "Board Profile: %s")
|
||||
MAKE_PSTR(mark_interval_fmt, "Mark interval: %lus")
|
||||
MAKE_PSTR(wifi_ssid_fmt, "WiFi SSID: %s")
|
||||
MAKE_PSTR(wifi_password_fmt, "WiFi Password: %S")
|
||||
MAKE_PSTR(tx_mode_fmt, "Tx mode: %d")
|
||||
MAKE_PSTR(bus_id_fmt, "Bus ID: %02X")
|
||||
MAKE_PSTR(log_level_fmt, "Log level: %s")
|
||||
MAKE_PSTR(cmd_optional, "[cmd]")
|
||||
MAKE_PSTR(ha_optional, "[ha]")
|
||||
MAKE_PSTR(deep_optional, "[deep]")
|
||||
MAKE_PSTR(watchid_optional, "[ID]")
|
||||
MAKE_PSTR(watch_format_optional, "[off | on | raw | unknown]")
|
||||
MAKE_PSTR(invalid_watch, "Invalid watch type")
|
||||
MAKE_PSTR(data_mandatory, "\"XX XX ...\"")
|
||||
MAKE_PSTR(asterisks, "********")
|
||||
MAKE_PSTR(n_mandatory, "<n>")
|
||||
MAKE_PSTR(sensorid_optional, "[sensor ID]")
|
||||
MAKE_PSTR(id_optional, "[id|hc]")
|
||||
MAKE_PSTR(data_optional, "[data]")
|
||||
MAKE_PSTR(offset_optional, "[offset]")
|
||||
MAKE_PSTR(length_optional, "[length]")
|
||||
MAKE_PSTR(typeid_mandatory, "<type ID>")
|
||||
MAKE_PSTR(deviceid_mandatory, "<device ID>")
|
||||
MAKE_PSTR(device_type_optional, "[device]")
|
||||
MAKE_PSTR(invalid_log_level, "Invalid log level")
|
||||
MAKE_PSTR(log_level_optional, "[level]")
|
||||
MAKE_PSTR(name_mandatory, "<name>")
|
||||
MAKE_PSTR(name_optional, "[name]")
|
||||
MAKE_PSTR(new_password_prompt1, "Enter new password: ")
|
||||
MAKE_PSTR(new_password_prompt2, "Retype new password: ")
|
||||
MAKE_PSTR(password_prompt, "Password: ")
|
||||
MAKE_PSTR(unset, "<unset>")
|
||||
MAKE_WORD_CUSTOM(EMSESP, "EMS-ESP")
|
||||
MAKE_WORD_CUSTOM(host_fmt, "Host: %s")
|
||||
MAKE_WORD_CUSTOM(port_fmt, "Port: %d")
|
||||
MAKE_WORD_CUSTOM(hostname_fmt, "Hostname: %s")
|
||||
MAKE_WORD_CUSTOM(board_profile_fmt, "Board Profile: %s")
|
||||
MAKE_WORD_CUSTOM(mark_interval_fmt, "Mark interval: %lus")
|
||||
MAKE_WORD_CUSTOM(wifi_ssid_fmt, "WiFi SSID: %s")
|
||||
MAKE_WORD_CUSTOM(wifi_password_fmt, "WiFi Password: %S")
|
||||
MAKE_WORD_CUSTOM(tx_mode_fmt, "Tx mode: %d")
|
||||
MAKE_WORD_CUSTOM(bus_id_fmt, "Bus ID: %02X")
|
||||
MAKE_WORD_CUSTOM(log_level_fmt, "Log level: %s")
|
||||
MAKE_WORD_CUSTOM(cmd_optional, "[cmd]")
|
||||
MAKE_WORD_CUSTOM(ha_optional, "[ha]")
|
||||
MAKE_WORD_CUSTOM(deep_optional, "[deep]")
|
||||
MAKE_WORD_CUSTOM(watchid_optional, "[ID]")
|
||||
MAKE_WORD_CUSTOM(watch_format_optional, "[off | on | raw | unknown]")
|
||||
MAKE_WORD_CUSTOM(invalid_watch, "Invalid watch type")
|
||||
MAKE_WORD_CUSTOM(data_mandatory, "\"XX XX ...\"")
|
||||
MAKE_WORD_CUSTOM(asterisks, "********")
|
||||
MAKE_WORD_CUSTOM(n_mandatory, "<n>")
|
||||
MAKE_WORD_CUSTOM(sensorid_optional, "[sensor ID]")
|
||||
MAKE_WORD_CUSTOM(id_optional, "[id|hc]")
|
||||
MAKE_WORD_CUSTOM(data_optional, "[data]")
|
||||
MAKE_WORD_CUSTOM(offset_optional, "[offset]")
|
||||
MAKE_WORD_CUSTOM(length_optional, "[length]")
|
||||
MAKE_WORD_CUSTOM(typeid_mandatory, "<type ID>")
|
||||
MAKE_WORD_CUSTOM(deviceid_mandatory, "<device ID>")
|
||||
MAKE_WORD_CUSTOM(device_type_optional, "[device]")
|
||||
MAKE_WORD_CUSTOM(invalid_log_level, "Invalid log level")
|
||||
MAKE_WORD_CUSTOM(log_level_optional, "[level]")
|
||||
MAKE_WORD_CUSTOM(name_mandatory, "<name>")
|
||||
MAKE_WORD_CUSTOM(name_optional, "[name]")
|
||||
MAKE_WORD_CUSTOM(new_password_prompt1, "Enter new password: ")
|
||||
MAKE_WORD_CUSTOM(new_password_prompt2, "Retype new password: ")
|
||||
MAKE_WORD_CUSTOM(password_prompt, "Password: ")
|
||||
MAKE_WORD_CUSTOM(unset, "<unset>")
|
||||
|
||||
// more common names that don't need translations
|
||||
MAKE_PSTR_LIST(1x3min, "1x3min")
|
||||
MAKE_PSTR_LIST(2x3min, "2x3min")
|
||||
MAKE_PSTR_LIST(3x3min, "3x3min")
|
||||
MAKE_PSTR_LIST(4x3min, "4x3min")
|
||||
MAKE_PSTR_LIST(5x3min, "5x3min")
|
||||
MAKE_PSTR_LIST(6x3min, "6x3min")
|
||||
MAKE_PSTR_LIST(auto, "auto")
|
||||
MAKE_PSTR_LIST(rc3x, "RC3x")
|
||||
MAKE_PSTR_LIST(rc20, "RC20")
|
||||
MAKE_PSTR_LIST(fb10, "FB10")
|
||||
MAKE_PSTR_LIST(fb100, "FB100")
|
||||
MAKE_PSTR_LIST(dash, "-")
|
||||
MAKE_PSTR_LIST(BLANK, "")
|
||||
MAKE_PSTR_LIST(pwm, "pwm")
|
||||
MAKE_PSTR_LIST(pwm_invers, "pwm inverse")
|
||||
MAKE_PSTR_LIST(mpc, "mpc")
|
||||
MAKE_PSTR_LIST(tempauto, "temp auto")
|
||||
MAKE_PSTR_LIST(bypass, "bypass")
|
||||
MAKE_PSTR_LIST(mixer, "mixer")
|
||||
MAKE_PSTR_LIST(monovalent, "monovalent")
|
||||
MAKE_PSTR_LIST(bivalent, "bivalent")
|
||||
MAKE_PSTR_LIST(n_o, "n_o")
|
||||
MAKE_PSTR_LIST(n_c, "n_c")
|
||||
MAKE_PSTR_LIST(prog1, "prog 1")
|
||||
MAKE_PSTR_LIST(prog2, "prog 2")
|
||||
MAKE_PSTR_LIST(proga, "prog a")
|
||||
MAKE_PSTR_LIST(progb, "prog b")
|
||||
MAKE_PSTR_LIST(progc, "prog c")
|
||||
MAKE_PSTR_LIST(progd, "prog d")
|
||||
MAKE_PSTR_LIST(proge, "prog e")
|
||||
MAKE_PSTR_LIST(progf, "prog f")
|
||||
MAKE_PSTR_LIST(rc35, "RC35")
|
||||
MAKE_PSTR_LIST(0kW, "0 kW")
|
||||
MAKE_PSTR_LIST(2kW, "2 kW")
|
||||
MAKE_PSTR_LIST(3kW, "3 kW")
|
||||
MAKE_PSTR_LIST(4kW, "4 kW")
|
||||
MAKE_PSTR_LIST(6kW, "6 kW")
|
||||
MAKE_PSTR_LIST(9kW, "9 kW")
|
||||
MAKE_NOTRANSLATION(1x3min, "1x3min")
|
||||
MAKE_NOTRANSLATION(2x3min, "2x3min")
|
||||
MAKE_NOTRANSLATION(3x3min, "3x3min")
|
||||
MAKE_NOTRANSLATION(4x3min, "4x3min")
|
||||
MAKE_NOTRANSLATION(5x3min, "5x3min")
|
||||
MAKE_NOTRANSLATION(6x3min, "6x3min")
|
||||
MAKE_NOTRANSLATION(auto, "auto")
|
||||
MAKE_NOTRANSLATION(rc3x, "RC3x")
|
||||
MAKE_NOTRANSLATION(rc20, "RC20")
|
||||
MAKE_NOTRANSLATION(fb10, "FB10")
|
||||
MAKE_NOTRANSLATION(fb100, "FB100")
|
||||
MAKE_NOTRANSLATION(dash, "-")
|
||||
MAKE_NOTRANSLATION(BLANK, "")
|
||||
MAKE_NOTRANSLATION(pwm, "pwm")
|
||||
MAKE_NOTRANSLATION(pwm_invers, "pwm inverse")
|
||||
MAKE_NOTRANSLATION(mpc, "mpc")
|
||||
MAKE_NOTRANSLATION(tempauto, "temp auto")
|
||||
MAKE_NOTRANSLATION(bypass, "bypass")
|
||||
MAKE_NOTRANSLATION(mixer, "mixer")
|
||||
MAKE_NOTRANSLATION(monovalent, "monovalent")
|
||||
MAKE_NOTRANSLATION(bivalent, "bivalent")
|
||||
MAKE_NOTRANSLATION(n_o, "n_o")
|
||||
MAKE_NOTRANSLATION(n_c, "n_c")
|
||||
MAKE_NOTRANSLATION(prog1, "prog 1")
|
||||
MAKE_NOTRANSLATION(prog2, "prog 2")
|
||||
MAKE_NOTRANSLATION(proga, "prog a")
|
||||
MAKE_NOTRANSLATION(progb, "prog b")
|
||||
MAKE_NOTRANSLATION(progc, "prog c")
|
||||
MAKE_NOTRANSLATION(progd, "prog d")
|
||||
MAKE_NOTRANSLATION(proge, "prog e")
|
||||
MAKE_NOTRANSLATION(progf, "prog f")
|
||||
MAKE_NOTRANSLATION(rc35, "RC35")
|
||||
MAKE_NOTRANSLATION(0kW, "0 kW")
|
||||
MAKE_NOTRANSLATION(2kW, "2 kW")
|
||||
MAKE_NOTRANSLATION(3kW, "3 kW")
|
||||
MAKE_NOTRANSLATION(4kW, "4 kW")
|
||||
MAKE_NOTRANSLATION(6kW, "6 kW")
|
||||
MAKE_NOTRANSLATION(9kW, "9 kW")
|
||||
MAKE_NOTRANSLATION(L1, "L1")
|
||||
MAKE_NOTRANSLATION(L2, "L2")
|
||||
MAKE_NOTRANSLATION(L3, "L3")
|
||||
MAKE_NOTRANSLATION(L4, "L4")
|
||||
|
||||
// templates - this are not translated and will be saved under options_single
|
||||
MAKE_PSTR_LIST(tpl_datetime, "Format: < NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
|
||||
MAKE_PSTR_LIST(tpl_switchtime, "Format: <nn> [ not_set | day hh:mm on|off ]")
|
||||
MAKE_PSTR_LIST(tpl_switchtime1, "Format: <nn> [ not_set | day hh:mm Tn ]")
|
||||
MAKE_PSTR_LIST(tpl_holidays, "Format: < dd.mm.yyyy-dd.mm.yyyy >")
|
||||
MAKE_PSTR_LIST(tpl_date, "Format: < dd.mm.yyyy >")
|
||||
MAKE_PSTR_LIST(tpl_input, "Format: <inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
|
||||
MAKE_PSTR_LIST(tpl_input4, "Format: <inv>[<comp><aux><cool><heat><dhw><pv>]")
|
||||
MAKE_NOTRANSLATION(tpl_datetime, "< NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
|
||||
MAKE_NOTRANSLATION(tpl_switchtime, "<nn> [ not_set | day hh:mm on|off ]")
|
||||
MAKE_NOTRANSLATION(tpl_switchtime1, "<nn> [ not_set | day hh:mm Tn ]")
|
||||
MAKE_NOTRANSLATION(tpl_holidays, "< dd.mm.yyyy-dd.mm.yyyy >")
|
||||
MAKE_NOTRANSLATION(tpl_date, "< dd.mm.yyyy >")
|
||||
MAKE_NOTRANSLATION(tpl_input, "<inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
|
||||
MAKE_NOTRANSLATION(tpl_input4, "<inv>[<comp><aux><cool><heat><dhw><pv>]")
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
MAKE_NOTRANSLATION(test_cmd, "run a test")
|
||||
#endif
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||
// use empty string if want to suppress showing tags
|
||||
// mqtt tags must not have spaces
|
||||
MAKE_NOTRANSLATION(tag_none, "")
|
||||
MAKE_NOTRANSLATION(tag_heartbeat, "")
|
||||
MAKE_NOTRANSLATION(tag_device_data, "")
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// Translating hours/minute/seconds are done in emsdevice.cpp (uom_to_string())
|
||||
MAKE_PSTR(uom_blank, " ")
|
||||
MAKE_PSTR(uom_percent, "%")
|
||||
MAKE_PSTR(uom_degrees, "°C")
|
||||
MAKE_PSTR(uom_kwh, "kWh")
|
||||
MAKE_PSTR(uom_wh, "Wh")
|
||||
MAKE_PSTR(uom_bar, "bar")
|
||||
MAKE_PSTR(uom_ua, "µA")
|
||||
MAKE_PSTR(uom_lmin, "l/min")
|
||||
MAKE_PSTR(uom_kw, "kW")
|
||||
MAKE_PSTR(uom_w, "W")
|
||||
MAKE_PSTR(uom_kb, "KB")
|
||||
MAKE_PSTR(uom_dbm, "dBm")
|
||||
MAKE_PSTR(uom_fahrenheit, "°F")
|
||||
MAKE_PSTR(uom_mv, "mV")
|
||||
MAKE_PSTR(uom_sqm, "m²")
|
||||
MAKE_PSTR(uom_m3, "m³")
|
||||
MAKE_PSTR(uom_l, "l")
|
||||
MAKE_PSTR(uom_kmin, "K*min")
|
||||
MAKE_PSTR(uom_k, "K")
|
||||
MAKE_WORD_CUSTOM(uom_blank, " ")
|
||||
MAKE_WORD_CUSTOM(uom_percent, "%")
|
||||
MAKE_WORD_CUSTOM(uom_degrees, "°C")
|
||||
MAKE_WORD_CUSTOM(uom_kwh, "kWh")
|
||||
MAKE_WORD_CUSTOM(uom_wh, "Wh")
|
||||
MAKE_WORD_CUSTOM(uom_bar, "bar")
|
||||
MAKE_WORD_CUSTOM(uom_ua, "µA")
|
||||
MAKE_WORD_CUSTOM(uom_lmin, "l/min")
|
||||
MAKE_WORD_CUSTOM(uom_kw, "kW")
|
||||
MAKE_WORD_CUSTOM(uom_w, "W")
|
||||
MAKE_WORD_CUSTOM(uom_kb, "KB")
|
||||
MAKE_WORD_CUSTOM(uom_dbm, "dBm")
|
||||
MAKE_WORD_CUSTOM(uom_fahrenheit, "°F")
|
||||
MAKE_WORD_CUSTOM(uom_mv, "mV")
|
||||
MAKE_WORD_CUSTOM(uom_sqm, "m²")
|
||||
MAKE_WORD_CUSTOM(uom_m3, "m³")
|
||||
MAKE_WORD_CUSTOM(uom_l, "l")
|
||||
MAKE_WORD_CUSTOM(uom_kmin, "K*min")
|
||||
MAKE_WORD_CUSTOM(uom_k, "K")
|
||||
|
||||
// MQTT topics and prefixes
|
||||
MAKE_PSTR(heating_active, "heating_active")
|
||||
MAKE_PSTR(tapwater_active, "tapwater_active")
|
||||
MAKE_PSTR(response, "response")
|
||||
MAKE_PSTR(tag_boiler_data_ww_mqtt, "ww")
|
||||
MAKE_PSTR(tag_device_data_ww_mqtt, "")
|
||||
|
||||
// Home Assistant - this is special and has no translations
|
||||
MAKE_PSTR_LIST(climate, "HA climate config creation")
|
||||
MAKE_WORD_CUSTOM(heating_active, "heating_active")
|
||||
MAKE_WORD_CUSTOM(tapwater_active, "tapwater_active")
|
||||
MAKE_WORD_CUSTOM(response, "response")
|
||||
MAKE_WORD_CUSTOM(tag_boiler_data_ww_mqtt, "ww")
|
||||
MAKE_WORD_CUSTOM(tag_device_data_ww_mqtt, "")
|
||||
|
||||
// syslog
|
||||
MAKE_PSTR_LIST(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all")
|
||||
MAKE_ENUM_FIXED(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all")
|
||||
|
||||
// sensors
|
||||
MAKE_PSTR_LIST(counter, "counter")
|
||||
MAKE_PSTR_LIST(digital_out, "digital_out")
|
||||
MAKE_PSTR_LIST(list_sensortype, "none", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2")
|
||||
MAKE_ENUM_FIXED(counter, "counter")
|
||||
MAKE_ENUM_FIXED(digital_out, "digital_out")
|
||||
MAKE_ENUM_FIXED(list_sensortype, "none", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2")
|
||||
|
||||
// watch
|
||||
MAKE_PSTR_LIST(list_watch, "off", "on", "raw", "unknown")
|
||||
MAKE_ENUM_FIXED(list_watch, "off", "on", "raw", "unknown")
|
||||
|
||||
/*
|
||||
* The rest below are Enums and generated from translations lists
|
||||
*/
|
||||
|
||||
MAKE_PSTR_ENUM(enum_cylprio, FL_(cyl1), FL_(cyl2))
|
||||
MAKE_PSTR_ENUM(enum_progMode, FL_(prog1), FL_(prog2))
|
||||
MAKE_PSTR_ENUM(enum_progMode4, FL_(proga), FL_(progb), FL_(progc), FL_(progd), FL_(proge), FL_(progf))
|
||||
MAKE_PSTR_ENUM(enum_climate, FL_(seltemp), FL_(roomtemp))
|
||||
MAKE_PSTR_ENUM(enum_charge, FL_(chargepump), FL_(3wayvalve))
|
||||
MAKE_PSTR_ENUM(enum_freq, FL_(off), FL_(1x3min), FL_(2x3min), FL_(3x3min), FL_(4x3min), FL_(5x3min), FL_(6x3min), FL_(continuous))
|
||||
MAKE_PSTR_ENUM(enum_off_time_date_manual, FL_(off), FL_(time), FL_(date), FL_(manual))
|
||||
MAKE_PSTR_ENUM(enum_comfort, FL_(hot), FL_(eco), FL_(intelligent))
|
||||
MAKE_PSTR_ENUM(enum_comfort1, FL_(high_comfort), FL_(eco))
|
||||
MAKE_PSTR_ENUM(enum_comfort2, FL_(eco), FL_(high_comfort))
|
||||
MAKE_PSTR_ENUM(enum_flow, FL_(off), FL_(flow), FL_(bufferedflow), FL_(buffer), FL_(layeredbuffer))
|
||||
MAKE_PSTR_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error))
|
||||
MAKE_PSTR_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
|
||||
MAKE_PSTR_ENUM(enum_pumpMode, FL_(proportional), FL_(deltaP1), FL_(deltaP2), FL_(deltaP3), FL_(deltaP4))
|
||||
MAKE_ENUM(enum_cylprio, FL_(cyl1), FL_(cyl2))
|
||||
MAKE_ENUM(enum_progMode, FL_(prog1), FL_(prog2))
|
||||
MAKE_ENUM(enum_progMode4, FL_(proga), FL_(progb), FL_(progc), FL_(progd), FL_(proge), FL_(progf))
|
||||
MAKE_ENUM(enum_climate, FL_(seltemp), FL_(roomtemp))
|
||||
MAKE_ENUM(enum_charge, FL_(chargepump), FL_(3wayvalve))
|
||||
MAKE_ENUM(enum_freq, FL_(off), FL_(1x3min), FL_(2x3min), FL_(3x3min), FL_(4x3min), FL_(5x3min), FL_(6x3min), FL_(continuous))
|
||||
MAKE_ENUM(enum_off_time_date_manual, FL_(off), FL_(time), FL_(date), FL_(manual))
|
||||
MAKE_ENUM(enum_comfort, FL_(hot), FL_(eco), FL_(intelligent))
|
||||
MAKE_ENUM(enum_comfort1, FL_(high_comfort), FL_(eco))
|
||||
MAKE_ENUM(enum_comfort2, FL_(eco), FL_(high_comfort))
|
||||
MAKE_ENUM(enum_flow, FL_(off), FL_(flow), FL_(bufferedflow), FL_(buffer), FL_(layeredbuffer))
|
||||
MAKE_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error))
|
||||
MAKE_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
|
||||
MAKE_ENUM(enum_pumpMode, FL_(proportional), FL_(deltaP1), FL_(deltaP2), FL_(deltaP3), FL_(deltaP4))
|
||||
|
||||
// thermostat lists
|
||||
MAKE_PSTR_ENUM(enum_ibaMainDisplay, FL_(internal_temperature), FL_(internal_setpoint), FL_(external_temperature), FL_(burner_temperature), FL_(ww_temperature), FL_(functioning_mode), FL_(time), FL_(date), FL_(smoke_temperature))
|
||||
MAKE_PSTR_ENUM(enum_ibaLanguage, FL_(german), FL_(dutch), FL_(french), FL_(italian))
|
||||
MAKE_PSTR_ENUM(enum_ibaLanguage_RC30, FL_(german), FL_(dutch))
|
||||
MAKE_PSTR_ENUM(enum_floordrystatus, FL_(off), FL_(start), FL_(heat), FL_(hold), FL_(cool), FL_(end))
|
||||
MAKE_PSTR_ENUM(enum_ibaBuildingType, FL_(light), FL_(medium), FL_(heavy))
|
||||
MAKE_PSTR_ENUM(enum_PID, FL_(fast), FL_(medium), FL_(slow))
|
||||
MAKE_PSTR_ENUM(enum_wwMode, FL_(off), FL_(normal), FL_(comfort), FL_(auto), FL_(own_prog), FL_(eco))
|
||||
MAKE_PSTR_ENUM(enum_wwCircMode, FL_(off), FL_(on), FL_(auto), FL_(own_prog))
|
||||
MAKE_PSTR_ENUM(enum_wwMode2, FL_(off), FL_(on), FL_(auto))
|
||||
MAKE_PSTR_ENUM(enum_wwMode3, FL_(on), FL_(off), FL_(auto))
|
||||
MAKE_PSTR_ENUM(enum_heatingtype, FL_(off), FL_(radiator), FL_(convector), FL_(floor))
|
||||
MAKE_PSTR_ENUM(enum_summermode, FL_(summer), FL_(auto), FL_(winter))
|
||||
MAKE_PSTR_ENUM(enum_hpoperatingmode, FL_(off), FL_(auto), FL_(heating), FL_(cooling))
|
||||
MAKE_PSTR_ENUM(enum_summer, FL_(winter), FL_(summer))
|
||||
MAKE_PSTR_ENUM(enum_operatingstate, FL_(heating), FL_(off), FL_(cooling))
|
||||
MAKE_ENUM(enum_ibaMainDisplay, FL_(internal_temperature), FL_(internal_setpoint), FL_(external_temperature), FL_(burner_temperature), FL_(ww_temperature), FL_(functioning_mode), FL_(time), FL_(date), FL_(smoke_temperature))
|
||||
MAKE_ENUM(enum_ibaLanguage, FL_(german), FL_(dutch), FL_(french), FL_(italian))
|
||||
MAKE_ENUM(enum_ibaLanguage_RC30, FL_(german), FL_(dutch))
|
||||
MAKE_ENUM(enum_floordrystatus, FL_(off), FL_(start), FL_(heat), FL_(hold), FL_(cool), FL_(end))
|
||||
MAKE_ENUM(enum_ibaBuildingType, FL_(light), FL_(medium), FL_(heavy))
|
||||
MAKE_ENUM(enum_PID, FL_(fast), FL_(medium), FL_(slow))
|
||||
MAKE_ENUM(enum_wwMode, FL_(off), FL_(normal), FL_(comfort), FL_(auto), FL_(own_prog), FL_(eco))
|
||||
MAKE_ENUM(enum_wwCircMode, FL_(off), FL_(on), FL_(auto), FL_(own_prog))
|
||||
MAKE_ENUM(enum_wwMode2, FL_(off), FL_(on), FL_(auto))
|
||||
MAKE_ENUM(enum_wwMode3, FL_(on), FL_(off), FL_(auto))
|
||||
MAKE_ENUM(enum_heatingtype, FL_(off), FL_(radiator), FL_(convector), FL_(floor))
|
||||
MAKE_ENUM(enum_summermode, FL_(summer), FL_(auto), FL_(winter))
|
||||
MAKE_ENUM(enum_hpoperatingmode, FL_(off), FL_(auto), FL_(heating), FL_(cooling))
|
||||
MAKE_ENUM(enum_summer, FL_(winter), FL_(summer))
|
||||
MAKE_ENUM(enum_operatingstate, FL_(heating), FL_(off), FL_(cooling))
|
||||
MAKE_ENUM(enum_hpmode, FL_(heating), FL_(cooling), FL_(heatandcool))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_mode, FL_(manual), FL_(auto)) // RC100, RC300, RC310
|
||||
MAKE_PSTR_ENUM(enum_mode2, FL_(off), FL_(manual), FL_(auto)) // RC20, RC30
|
||||
MAKE_PSTR_ENUM(enum_mode3, FL_(night), FL_(day), FL_(auto)) // RC35, RC30_N, RC25, RC20_N
|
||||
MAKE_PSTR_ENUM(enum_mode4, FL_(nofrost), FL_(eco), FL_(heat), FL_(auto)) // JUNKERS
|
||||
MAKE_PSTR_ENUM(enum_mode5, FL_(auto), FL_(off)) // CRF
|
||||
MAKE_PSTR_ENUM(enum_mode6, FL_(nofrost), FL_(night), FL_(day)) // RC10
|
||||
MAKE_PSTR_ENUM(enum_mode_ha, FL_(off), FL_(heat), FL_(auto)) // HA climate
|
||||
MAKE_ENUM(enum_mode, FL_(manual), FL_(auto)) // RC100, RC300, RC310
|
||||
MAKE_ENUM(enum_mode2, FL_(off), FL_(manual), FL_(auto)) // RC20, RC30
|
||||
MAKE_ENUM(enum_mode3, FL_(night), FL_(day), FL_(auto)) // RC35, RC30_N, RC25, RC20_N
|
||||
MAKE_ENUM(enum_mode4, FL_(nofrost), FL_(eco), FL_(heat), FL_(auto)) // JUNKERS
|
||||
MAKE_ENUM(enum_mode5, FL_(auto), FL_(off)) // CRF
|
||||
MAKE_ENUM(enum_mode6, FL_(nofrost), FL_(night), FL_(day)) // RC10
|
||||
MAKE_ENUM(enum_mode_ha, FL_(off), FL_(heat), FL_(auto)) // HA climate
|
||||
|
||||
MAKE_PSTR_ENUM(enum_modetype, FL_(eco), FL_(comfort))
|
||||
MAKE_PSTR_ENUM(enum_modetype3, FL_(night), FL_(day))
|
||||
MAKE_PSTR_ENUM(enum_modetype4, FL_(nofrost), FL_(eco), FL_(heat))
|
||||
MAKE_PSTR_ENUM(enum_modetype5, FL_(off), FL_(on))
|
||||
MAKE_ENUM(enum_modetype, FL_(eco), FL_(comfort))
|
||||
MAKE_ENUM(enum_modetype3, FL_(night), FL_(day))
|
||||
MAKE_ENUM(enum_modetype4, FL_(nofrost), FL_(eco), FL_(heat))
|
||||
MAKE_ENUM(enum_modetype5, FL_(off), FL_(on))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_reducemode, FL_(nofrost), FL_(reduce), FL_(room), FL_(outdoor))
|
||||
MAKE_PSTR_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 values: 1-3
|
||||
MAKE_PSTR_ENUM(enum_nofrostmode, FL_(off), FL_(room), FL_(outdoor))
|
||||
MAKE_PSTR_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor))
|
||||
MAKE_ENUM(enum_reducemode, FL_(nofrost), FL_(reduce), FL_(room), FL_(outdoor))
|
||||
MAKE_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 values: 1-3
|
||||
MAKE_ENUM(enum_nofrostmode, FL_(off), FL_(outdoor), FL_(room))
|
||||
MAKE_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_controlmode, FL_(off), FL_(optimized), FL_(simple), FL_(mpc), FL_(room), FL_(power), FL_(constant))
|
||||
MAKE_PSTR_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoint), FL_(na), FL_(room), FL_(power), FL_(constant)) // RC310 1-4
|
||||
MAKE_PSTR_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
|
||||
MAKE_PSTR_ENUM(enum_control, FL_(off), FL_(rc20), FL_(rc3x))
|
||||
MAKE_PSTR_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
|
||||
MAKE_PSTR_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
|
||||
MAKE_ENUM(enum_controlmode, FL_(off), FL_(optimized), FL_(simple), FL_(mpc), FL_(room), FL_(power), FL_(constant))
|
||||
MAKE_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoint), FL_(na), FL_(room), FL_(power), FL_(constant)) // RC310 1-4
|
||||
MAKE_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
|
||||
MAKE_ENUM(enum_control, FL_(off), FL_(rc20), FL_(rc3x))
|
||||
MAKE_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
|
||||
MAKE_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))
|
||||
MAKE_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))
|
||||
|
||||
MAKE_PSTR_ENUM(enum_dayOfWeek, FL_(day_mo), FL_(day_tu), FL_(day_we), FL_(day_th), FL_(day_fr), FL_(day_sa), FL_(day_su), FL_(all))
|
||||
MAKE_PSTR_ENUM(enum_progMode2, FL_(own_1), FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors), FL_(new), FL_(own_2))
|
||||
MAKE_PSTR_ENUM(enum_progMode3, FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors))
|
||||
MAKE_PSTR_ENUM(enum_hybridStrategy, FL_(co2_optimized), FL_(cost_optimized), FL_(outside_temp_switched), FL_(co2_cost_mix))
|
||||
MAKE_PSTR_ENUM(enum_hybridStrategy1, FL_(cost_optimized), FL_(co2_optimized), FL_(outside_temp_alt), FL_(outside_temp_par), FL_(hp_prefered), FL_(boiler_only))
|
||||
MAKE_PSTR_ENUM(enum_lowNoiseMode, FL_(off), FL_(reduced_output), FL_(switchoff), FL_(perm))
|
||||
MAKE_ENUM(enum_dayOfWeek, FL_(day_mo), FL_(day_tu), FL_(day_we), FL_(day_th), FL_(day_fr), FL_(day_sa), FL_(day_su), FL_(all))
|
||||
MAKE_ENUM(enum_progMode2, FL_(own_1), FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors), FL_(new), FL_(own_2))
|
||||
MAKE_ENUM(enum_progMode3, FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors))
|
||||
MAKE_ENUM(enum_hybridStrategy, FL_(co2_optimized), FL_(cost_optimized), FL_(outside_temp_switched), FL_(co2_cost_mix))
|
||||
MAKE_ENUM(enum_hybridStrategy1, FL_(cost_optimized), FL_(co2_optimized), FL_(outside_temp_alt), FL_(outside_temp_par), FL_(hp_prefered), FL_(boiler_only))
|
||||
MAKE_ENUM(enum_lowNoiseMode, FL_(off), FL_(reduced_output), FL_(switchoff), FL_(perm))
|
||||
|
||||
// heat pump
|
||||
MAKE_PSTR_ENUM(enum_hpactivity, FL_(none), FL_(heating), FL_(cooling), FL_(hot_water), FL_(pool), FL_(unknown), FL_(defrost))
|
||||
MAKE_PSTR_ENUM(enum_silentMode, FL_(off), FL_(auto), FL_(on))
|
||||
MAKE_ENUM(enum_hpactivity, FL_(none), FL_(heating), FL_(cooling), FL_(hot_water), FL_(pool), FL_(unknown), FL_(defrost))
|
||||
MAKE_ENUM(enum_silentMode, FL_(off), FL_(auto), FL_(on))
|
||||
|
||||
// solar
|
||||
MAKE_PSTR_ENUM(enum_solarmode, FL_(constant), FL_(pwm), FL_(analog))
|
||||
MAKE_PSTR_ENUM(enum_collectortype, FL_(flat), FL_(vacuum))
|
||||
MAKE_PSTR_ENUM(enum_wwStatus2, FL_(BLANK), FL_(BLANK), FL_(BLANK), FL_(no_heat), FL_(BLANK), FL_(BLANK), FL_(heatrequest), FL_(BLANK), FL_(disinfecting), FL_(hold))
|
||||
MAKE_ENUM(enum_solarmode, FL_(constant), FL_(pwm), FL_(analog))
|
||||
MAKE_ENUM(enum_collectortype, FL_(flat), FL_(vacuum))
|
||||
MAKE_ENUM(enum_wwStatus2, FL_(BLANK), FL_(BLANK), FL_(BLANK), FL_(no_heat), FL_(BLANK), FL_(BLANK), FL_(heatrequest), FL_(BLANK), FL_(disinfecting), FL_(hold))
|
||||
|
||||
// mixer
|
||||
MAKE_PSTR_ENUM(enum_shunt, FL_(stopped), FL_(opening), FL_(closing), FL_(open), FL_(close))
|
||||
MAKE_PSTR_ENUM(enum_wwProgMode, FL_(std_prog), FL_(own_prog))
|
||||
MAKE_ENUM(enum_shunt, FL_(stopped), FL_(opening), FL_(closing), FL_(open), FL_(close))
|
||||
MAKE_ENUM(enum_wwProgMode, FL_(std_prog), FL_(own_prog))
|
||||
|
||||
// AM200 lists
|
||||
MAKE_PSTR_ENUM(enum_vr2Config, FL_(off), FL_(bypass))
|
||||
MAKE_PSTR_ENUM(enum_aPumpSignal, FL_(off), FL_(pwm), FL_(pwm_invers))
|
||||
MAKE_PSTR_ENUM(enum_bufBypass, FL_(no), FL_(mixer), FL_(valve))
|
||||
MAKE_PSTR_ENUM(enum_blockMode, FL_(off), FL_(auto), FL_(blocking))
|
||||
MAKE_PSTR_ENUM(enum_bufConfig, FL_(off), FL_(monovalent), FL_(bivalent))
|
||||
MAKE_PSTR_ENUM(enum_blockTerm, FL_(n_o), FL_(n_c))
|
||||
MAKE_ENUM(enum_vr2Config, FL_(off), FL_(bypass))
|
||||
MAKE_ENUM(enum_aPumpSignal, FL_(off), FL_(pwm), FL_(pwm_invers))
|
||||
MAKE_ENUM(enum_bufBypass, FL_(no), FL_(mixer), FL_(valve))
|
||||
MAKE_ENUM(enum_blockMode, FL_(off), FL_(auto), FL_(blocking))
|
||||
MAKE_ENUM(enum_bufConfig, FL_(off), FL_(monovalent), FL_(bivalent))
|
||||
MAKE_ENUM(enum_blockTerm, FL_(n_o), FL_(n_c))
|
||||
|
||||
// Ventilation
|
||||
MAKE_ENUM(enum_ventMode, FL_(auto), FL_(off), FL_(L1), FL_(L2), FL_(L3), FL_(L4), FL_(demand), FL_(sleep), FL_(intense), FL_(bypass), FL_(partymode), FL_(fireplace))
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,10 +18,12 @@
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
static emsesp::EMSESP application;
|
||||
|
||||
void setup() {
|
||||
emsesp::EMSESP::start();
|
||||
application.start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
emsesp::EMSESP::loop();
|
||||
application.loop();
|
||||
}
|
||||
|
||||
688
src/mqtt.cpp
688
src/mqtt.cpp
File diff suppressed because it is too large
Load Diff
123
src/mqtt.h
123
src/mqtt.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef EMSESP_MQTT_H_
|
||||
#define EMSESP_MQTT_H_
|
||||
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <espMqttClient.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "system.h"
|
||||
@@ -29,31 +29,15 @@
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
// size of queue
|
||||
static constexpr uint16_t MAX_MQTT_MESSAGES = 300;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using mqtt_sub_function_p = std::function<bool(const char * message)>;
|
||||
using cmdfunction_p = std::function<bool(const char * data, const int8_t id)>;
|
||||
|
||||
struct MqttMessage {
|
||||
const uint8_t operation;
|
||||
const std::string topic;
|
||||
const std::string payload;
|
||||
const bool retain;
|
||||
|
||||
MqttMessage(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain)
|
||||
: operation(operation)
|
||||
, topic(topic)
|
||||
, payload(payload)
|
||||
, retain(retain) {
|
||||
}
|
||||
~MqttMessage() = default;
|
||||
};
|
||||
|
||||
class Mqtt {
|
||||
public:
|
||||
enum discoveryType : uint8_t { HOMEASSISTANT, DOMOTICZ };
|
||||
enum entityFormat : uint8_t { SINGLE_LONG, SINGLE_SHORT, MULTI_SHORT };
|
||||
|
||||
void loop();
|
||||
void start();
|
||||
|
||||
@@ -74,25 +58,24 @@ class Mqtt {
|
||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore
|
||||
|
||||
static void on_connect();
|
||||
|
||||
static void on_disconnect(espMqttClientTypes::DisconnectReason reason);
|
||||
static void subscribe(const uint8_t device_type, const std::string & topic, mqtt_sub_function_p cb);
|
||||
static void subscribe(const std::string & topic);
|
||||
static void resubscribe();
|
||||
|
||||
static void publish(const std::string & topic, const std::string & payload);
|
||||
static void publish(const char * topic, const char * payload);
|
||||
static void publish(const std::string & topic, const JsonObject & payload);
|
||||
static void publish(const char * topic, const JsonObject & payload);
|
||||
static void publish(const char * topic, const std::string & payload);
|
||||
static void publish_retain(const std::string & topic, const JsonObject & payload, bool retain);
|
||||
static void publish_retain(const char * topic, const std::string & payload, bool retain);
|
||||
static void publish_retain(const char * topic, const JsonObject & payload, bool retain);
|
||||
static void publish_ha(const char * topic, const JsonObject & payload);
|
||||
static void publish_ha(const char * topic);
|
||||
static bool queue_publish(const std::string & topic, const std::string & payload);
|
||||
static bool queue_publish(const char * topic, const char * payload);
|
||||
static bool queue_publish(const std::string & topic, const JsonObject & payload);
|
||||
static bool queue_publish(const char * topic, const JsonObject & payload);
|
||||
static bool queue_publish(const char * topic, const std::string & payload);
|
||||
static bool queue_publish_retain(const std::string & topic, const JsonObject & payload, const bool retain);
|
||||
static bool queue_publish_retain(const char * topic, const std::string & payload, const bool retain);
|
||||
static bool queue_publish_retain(const char * topic, const JsonObject & payload, const bool retain);
|
||||
static bool queue_ha(const char * topic, const JsonObject & payload);
|
||||
static bool queue_remove_topic(const char * topic);
|
||||
|
||||
static void
|
||||
publish_ha_sensor_config(DeviceValue & dv, const std::string & model, const std::string & brand, const bool remove, const bool create_device_config = false);
|
||||
static void publish_ha_sensor_config(uint8_t type,
|
||||
static bool publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
|
||||
static bool publish_ha_sensor_config(uint8_t type,
|
||||
uint8_t tag,
|
||||
const char * const fullname,
|
||||
const char * const en_name,
|
||||
@@ -108,27 +91,23 @@ class Mqtt {
|
||||
const int8_t num_op,
|
||||
const JsonObject & dev_json);
|
||||
|
||||
static void publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
|
||||
static void publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint16_t max = 30);
|
||||
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
|
||||
static bool publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint16_t max = 30);
|
||||
|
||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
|
||||
static void show_mqtt(uuid::console::Shell & shell);
|
||||
|
||||
static void ha_status();
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
void incoming(const char * topic, const char * payload = ""); // for testing only
|
||||
#endif
|
||||
|
||||
static bool connected() {
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
return true;
|
||||
#else
|
||||
return mqttClient_->connected();
|
||||
#endif
|
||||
}
|
||||
|
||||
static AsyncMqttClient * client() {
|
||||
static MqttClient * client() {
|
||||
return mqttClient_;
|
||||
}
|
||||
|
||||
@@ -169,7 +148,7 @@ class Mqtt {
|
||||
}
|
||||
|
||||
static uint32_t publish_queued() {
|
||||
return mqtt_messages_.size();
|
||||
return queuecount_;
|
||||
}
|
||||
|
||||
static uint8_t connect_count() {
|
||||
@@ -186,6 +165,10 @@ class Mqtt {
|
||||
return entity_format_;
|
||||
}
|
||||
|
||||
static uint8_t discovery_type() {
|
||||
return discovery_type_;
|
||||
}
|
||||
|
||||
static void nested_format(uint8_t nested_format) {
|
||||
nested_format_ = nested_format;
|
||||
}
|
||||
@@ -218,12 +201,8 @@ class Mqtt {
|
||||
ha_climate_reset_ = reset;
|
||||
}
|
||||
|
||||
static bool send_response() {
|
||||
return send_response_;
|
||||
}
|
||||
|
||||
static void send_response(bool send_response) {
|
||||
send_response_ = send_response;
|
||||
static std::string get_response() {
|
||||
return lastresponse_;
|
||||
}
|
||||
|
||||
void set_qos(uint8_t mqtt_qos) const {
|
||||
@@ -234,47 +213,24 @@ class Mqtt {
|
||||
mqtt_retain_ = mqtt_retain;
|
||||
}
|
||||
|
||||
static bool is_empty() {
|
||||
return mqtt_messages_.empty();
|
||||
}
|
||||
|
||||
static std::string tag_to_topic(uint8_t device_type, uint8_t tag);
|
||||
|
||||
static void
|
||||
add_avty_to_doc(const char * state_t, const JsonObject & doc, const char * cond1 = nullptr, const char * cond2 = nullptr, const char * negcond = nullptr);
|
||||
|
||||
struct QueuedMqttMessage {
|
||||
const uint32_t id_;
|
||||
const std::shared_ptr<const MqttMessage> content_;
|
||||
uint8_t retry_count_ = 0;
|
||||
uint16_t packet_id_ = 0;
|
||||
|
||||
~QueuedMqttMessage() = default;
|
||||
QueuedMqttMessage(uint32_t id, std::shared_ptr<MqttMessage> && content)
|
||||
: id_(id)
|
||||
, content_(std::move(content)) {
|
||||
}
|
||||
};
|
||||
static std::deque<QueuedMqttMessage> mqtt_messages_;
|
||||
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static AsyncMqttClient * mqttClient_;
|
||||
static uint32_t mqtt_message_id_;
|
||||
static MqttClient * mqttClient_;
|
||||
static uint32_t mqtt_message_id_;
|
||||
|
||||
static constexpr uint32_t MQTT_PUBLISH_WAIT = 100; // delay between sending publishes, to account for large payloads
|
||||
static constexpr uint8_t MQTT_PUBLISH_MAX_RETRY = 3; // max retries for giving up on publishing
|
||||
|
||||
static std::shared_ptr<const MqttMessage> queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, bool retain);
|
||||
static std::shared_ptr<const MqttMessage> queue_publish_message(const std::string & topic, const std::string & payload, bool retain);
|
||||
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 bool queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain);
|
||||
static bool queue_publish_message(const std::string & topic, const std::string & payload, const bool retain);
|
||||
static void queue_subscribe_message(const std::string & topic);
|
||||
static void queue_unsubscribe_message(const std::string & topic);
|
||||
|
||||
void on_publish(uint16_t packetId) const;
|
||||
void on_message(const char * topic, const char * payload, size_t len) const;
|
||||
void process_queue();
|
||||
void on_message(const char * topic, const uint8_t * payload, size_t len) const;
|
||||
|
||||
// function handlers for MQTT subscriptions
|
||||
struct MQTTSubFunction {
|
||||
@@ -291,7 +247,7 @@ class Mqtt {
|
||||
|
||||
static std::vector<MQTTSubFunction> mqtt_subfunctions_; // list of mqtt subscribe callbacks for all devices
|
||||
|
||||
uint32_t last_mqtt_poll_ = 0;
|
||||
// uint32_t last_mqtt_poll_ = 0;
|
||||
uint32_t last_publish_boiler_ = 0;
|
||||
uint32_t last_publish_thermostat_ = 0;
|
||||
uint32_t last_publish_solar_ = 0;
|
||||
@@ -299,16 +255,18 @@ class Mqtt {
|
||||
uint32_t last_publish_other_ = 0;
|
||||
uint32_t last_publish_sensor_ = 0;
|
||||
uint32_t last_publish_heartbeat_ = 0;
|
||||
uint32_t last_publish_queue_ = 0;
|
||||
// uint32_t last_publish_queue_ = 0;
|
||||
|
||||
static bool connecting_;
|
||||
static bool initialized_;
|
||||
static uint32_t mqtt_publish_fails_;
|
||||
static uint16_t queuecount_;
|
||||
static uint8_t connectcount_;
|
||||
static bool ha_climate_reset_;
|
||||
|
||||
static std::string lasttopic_;
|
||||
static std::string lastpayload_;
|
||||
static std::string lastresponse_;
|
||||
|
||||
// settings, copied over
|
||||
static std::string mqtt_base_;
|
||||
@@ -328,6 +286,7 @@ class Mqtt {
|
||||
static uint8_t nested_format_;
|
||||
static uint8_t entity_format_;
|
||||
static std::string discovery_prefix_;
|
||||
static uint8_t discovery_type_;
|
||||
static bool publish_single_;
|
||||
static bool publish_single2cmd_;
|
||||
static bool send_response_;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,6 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EMSESP_ROOMCONTROL_H
|
||||
#define EMSESP_ROOMCONTROL_H
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -82,7 +82,7 @@ void Shower::loop() {
|
||||
char s[50];
|
||||
snprintf(s, 50, "%d minutes and %d seconds", (uint8_t)(duration_ / 60000), (uint8_t)((duration_ / 1000) % 60));
|
||||
doc["duration"] = s;
|
||||
Mqtt::publish("shower_data", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("shower_data", doc.as<JsonObject>());
|
||||
LOG_DEBUG("[Shower] finished with duration %d", duration_);
|
||||
}
|
||||
}
|
||||
@@ -144,18 +144,16 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
|
||||
// always publish as a string
|
||||
char s[12];
|
||||
Mqtt::publish("shower_active", Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
|
||||
Mqtt::queue_publish("shower_active", Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
|
||||
|
||||
// send out HA MQTT Discovery config topic
|
||||
if ((Mqtt::ha_enabled()) && (!ha_configdone_ || force)) {
|
||||
ha_configdone_ = true;
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_LARGE> doc;
|
||||
|
||||
doc["name"] = "Shower Active";
|
||||
|
||||
char str[70];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
|
||||
snprintf(str, sizeof(str), "%s_shower_active", Mqtt::basename().c_str());
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "shower_active"); // v3.4 compatible
|
||||
@@ -164,7 +162,7 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
doc["object_id"] = str;
|
||||
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str()); // use base path
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::basename().c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
@@ -181,14 +179,15 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
ids.add(Mqtt::basename());
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, doc.as<JsonObject>());
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -46,9 +46,9 @@ class Shower {
|
||||
uint32_t shower_alert_coldshot_; // default 10 seconds for cold water before turning back hot water
|
||||
bool ha_configdone_ = false; // for HA MQTT Discovery
|
||||
bool shower_state_;
|
||||
uint32_t timer_start_; // ms
|
||||
uint32_t timer_pause_; // ms
|
||||
uint32_t duration_; // ms
|
||||
uint32_t timer_start_; // ms
|
||||
uint32_t timer_pause_; // ms
|
||||
uint32_t duration_; // ms
|
||||
|
||||
// cold shot
|
||||
uint32_t alert_timer_start_; // ms
|
||||
|
||||
291
src/system.cpp
291
src/system.cpp
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,9 +19,13 @@
|
||||
#include "system.h"
|
||||
#include "emsesp.h" // for send_raw_telegram() command
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include "esp_ota_ops.h"
|
||||
#endif
|
||||
|
||||
#include <semver200.h>
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
#include "test/test.h"
|
||||
#endif
|
||||
|
||||
@@ -50,24 +54,38 @@ Adafruit_NeoPixel pixels(1, 7, NEO_GRB + NEO_KHZ800);
|
||||
namespace emsesp {
|
||||
|
||||
// Languages supported. Note: the order is important and must match locale_translations.h
|
||||
const char * const languages[] = {EMSESP_LOCALE_EN, EMSESP_LOCALE_DE, EMSESP_LOCALE_NL, EMSESP_LOCALE_SV, EMSESP_LOCALE_PL, EMSESP_LOCALE_NO, EMSESP_LOCALE_FR};
|
||||
#if defined(EMSESP_TEST) || defined(EMSESP_EN_ONLY)
|
||||
// in Debug mode use one language (en) to save flash memory needed for the tests
|
||||
const char * const languages[] = {EMSESP_LOCALE_EN};
|
||||
#elif defined(EMSESP_DE_ONLY)
|
||||
const char * const languages[] = {EMSESP_LOCALE_DE};
|
||||
#else
|
||||
const char * const languages[] = {EMSESP_LOCALE_EN,
|
||||
EMSESP_LOCALE_DE,
|
||||
EMSESP_LOCALE_NL,
|
||||
EMSESP_LOCALE_SV,
|
||||
EMSESP_LOCALE_PL,
|
||||
EMSESP_LOCALE_NO,
|
||||
EMSESP_LOCALE_FR,
|
||||
EMSESP_LOCALE_TR,
|
||||
EMSESP_LOCALE_IT};
|
||||
#endif
|
||||
|
||||
size_t num_languages = sizeof(languages) / sizeof(const char *);
|
||||
static constexpr uint8_t NUM_LANGUAGES = sizeof(languages) / sizeof(const char *);
|
||||
|
||||
uuid::log::Logger System::logger_{F_(system), uuid::log::Facility::KERN};
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::syslog::SyslogService System::syslog_;
|
||||
#endif
|
||||
|
||||
// init statics
|
||||
PButton System::myPButton_;
|
||||
bool System::restart_requested_ = false;
|
||||
PButton System::myPButton_;
|
||||
bool System::restart_requested_ = false;
|
||||
bool System::test_set_all_active_ = false;
|
||||
uint32_t System::max_alloc_mem_;
|
||||
uint32_t System::heap_mem_;
|
||||
|
||||
// find the index of the language
|
||||
// 0 = EN, 1 = DE, etc...
|
||||
uint8_t System::language_index() {
|
||||
for (uint8_t i = 0; i < num_languages; i++) {
|
||||
for (uint8_t i = 0; i < NUM_LANGUAGES; i++) {
|
||||
if (languages[i] == locale()) {
|
||||
return i;
|
||||
}
|
||||
@@ -80,6 +98,18 @@ bool System::command_send(const char * value, const int8_t id) {
|
||||
return EMSESP::txservice_.send_raw(value); // ignore id
|
||||
}
|
||||
|
||||
bool System::command_response(const char * value, const int8_t id, JsonObject & output) {
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_LARGE);
|
||||
if (DeserializationError::Ok == deserializeJson(doc, Mqtt::get_response())) {
|
||||
for (JsonPair p : doc.as<JsonObject>()) {
|
||||
output[p.key()] = p.value();
|
||||
}
|
||||
} else {
|
||||
output["response"] = Mqtt::get_response();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// fetch device values
|
||||
bool System::command_fetch(const char * value, const int8_t id) {
|
||||
std::string value_s;
|
||||
@@ -130,19 +160,21 @@ bool System::command_publish(const char * value, const int8_t id) {
|
||||
} else if (value_s == "other") {
|
||||
EMSESP::publish_other_values(); // switch and heat pump
|
||||
return true;
|
||||
} else if ((value_s == (F_(dallassensor))) || (value_s == (F_(analogsensor)))) {
|
||||
} else if ((value_s == (F_(temperaturesensor))) || (value_s == (F_(analogsensor)))) {
|
||||
EMSESP::publish_sensor_values(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::publish_all();
|
||||
LOG_INFO("Publishing all data to MQTT");
|
||||
EMSESP::publish_all();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// syslog level
|
||||
// commenting this out - don't see the point on having an API service to change the syslog level
|
||||
/*
|
||||
bool System::command_syslog_level(const char * value, const int8_t id) {
|
||||
uint8_t s = 0xff;
|
||||
if (Helpers::value2enum(value, s, FL_(list_syslog_level))) {
|
||||
@@ -163,6 +195,7 @@ bool System::command_syslog_level(const char * value, const int8_t id) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// watch
|
||||
bool System::command_watch(const char * value, const int8_t id) {
|
||||
@@ -174,9 +207,9 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
}
|
||||
if (Mqtt::publish_single() && w != EMSESP::watch()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::publish("system/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
Mqtt::queue_publish("system/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
} else {
|
||||
Mqtt::publish("system_data/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
Mqtt::queue_publish("system_data/watch", EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(w) : (FL_(list_watch)[w]));
|
||||
}
|
||||
}
|
||||
EMSESP::watch(w);
|
||||
@@ -184,9 +217,9 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
} else if (i) {
|
||||
if (Mqtt::publish_single() && i != EMSESP::watch_id()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::publish("system/watch", Helpers::hextoa(i));
|
||||
Mqtt::queue_publish("system/watch", Helpers::hextoa(i));
|
||||
} else {
|
||||
Mqtt::publish("system_data/watch", Helpers::hextoa(i));
|
||||
Mqtt::queue_publish("system_data/watch", Helpers::hextoa(i));
|
||||
}
|
||||
}
|
||||
EMSESP::watch_id(i);
|
||||
@@ -212,7 +245,6 @@ void System::system_restart() {
|
||||
void System::wifi_reconnect() {
|
||||
LOG_INFO("WiFi reconnecting...");
|
||||
Shell::loop_all();
|
||||
EMSESP::console_.loop();
|
||||
delay(1000); // wait a second
|
||||
EMSESP::webSettingsService.save(); // local settings
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers("local"); // in case we've changed ssid or password
|
||||
@@ -222,8 +254,6 @@ void System::wifi_reconnect() {
|
||||
void System::format(uuid::console::Shell & shell) {
|
||||
auto msg = ("Formatting file system. This will reset all settings to their defaults");
|
||||
shell.logger().warning(msg);
|
||||
// shell.flush();
|
||||
|
||||
EMSuart::stop();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -234,9 +264,6 @@ void System::format(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
void System::syslog_init() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bool was_enabled = syslog_enabled_;
|
||||
#endif
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
syslog_enabled_ = settings.syslog_enabled;
|
||||
syslog_level_ = settings.syslog_level;
|
||||
@@ -247,44 +274,42 @@ void System::syslog_init() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (syslog_enabled_) {
|
||||
// start & configure syslog
|
||||
if (!was_enabled) {
|
||||
syslog_.start();
|
||||
EMSESP::logger().info("Starting Syslog");
|
||||
}
|
||||
EMSESP::logger().info("Starting Syslog service");
|
||||
syslog_.start();
|
||||
|
||||
syslog_.log_level((uuid::log::Level)syslog_level_);
|
||||
syslog_.mark_interval(syslog_mark_interval_);
|
||||
syslog_.destination(syslog_host_.c_str(), syslog_port_);
|
||||
syslog_.hostname(hostname().c_str());
|
||||
|
||||
// register the command
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, FL_(changeloglevel_cmd), CommandFlag::ADMIN_ONLY);
|
||||
// removed in 3.6.0
|
||||
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, FL_(changeloglevel_cmd), CommandFlag::ADMIN_ONLY);
|
||||
|
||||
} else if (was_enabled) {
|
||||
} else if (syslog_.started()) {
|
||||
// in case service is still running, this flushes the queue
|
||||
// https://github.com/emsesp/EMS-ESP/issues/496
|
||||
EMSESP::logger().info("Stopping Syslog");
|
||||
syslog_.log_level((uuid::log::Level)-1);
|
||||
syslog_.log_level((uuid::log::Level)-1); // stop server
|
||||
syslog_.mark_interval(0);
|
||||
syslog_.destination("");
|
||||
}
|
||||
|
||||
if (Mqtt::publish_single()) {
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
Mqtt::publish("system/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
Mqtt::queue_publish("system/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
|
||||
Mqtt::publish("system/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
Mqtt::queue_publish("system/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
} else {
|
||||
Mqtt::publish("system/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
Mqtt::queue_publish("system/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
}
|
||||
|
||||
} else {
|
||||
Mqtt::publish("system_data/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
Mqtt::queue_publish("system_data/syslog", syslog_enabled_ ? (FL_(list_syslog_level)[syslog_level_ + 1]) : "off");
|
||||
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
|
||||
Mqtt::publish("system_data/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
Mqtt::queue_publish("system_data/watch",
|
||||
EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX ? Helpers::itoa(EMSESP::watch()) : (FL_(list_watch)[EMSESP::watch()]));
|
||||
} else {
|
||||
Mqtt::publish("system_data/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
Mqtt::queue_publish("system_data/watch", Helpers::hextoa(EMSESP::watch_id()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,9 +377,7 @@ void System::wifi_tweak() {
|
||||
bool s1 = WiFi.getSleep();
|
||||
WiFi.setSleep(false); // turn off sleep - WIFI_PS_NONE
|
||||
bool s2 = WiFi.getSleep();
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("[DEBUG] Adjusting WiFi - Tx power %d->%d, Sleep %d->%d", p1, p2, s1, s2);
|
||||
#endif
|
||||
LOG_DEBUG("Adjusting WiFi - Tx power %d->%d, Sleep %d->%d", p1, p2, s1, s2);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -391,10 +414,13 @@ void System::start() {
|
||||
setCpuFrequencyMhz(160);
|
||||
#endif
|
||||
}
|
||||
|
||||
// get current memory values
|
||||
fstotal_ = LittleFS.totalBytes() / 1024; // read only once, it takes 500 ms to read
|
||||
psram_ = ESP.getPsramSize() / 1024;
|
||||
appused_ = ESP.getSketchSize() / 1024;
|
||||
appfree_ = ESP.getFreeSketchSpace() / 1024 - appused_;
|
||||
appfree_ = esp_ota_get_running_partition()->size / 1024 - appused_;
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
#endif
|
||||
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
@@ -405,16 +431,15 @@ void System::start() {
|
||||
led_init(false); // init LED
|
||||
button_init(false); // the special button
|
||||
network_init(false); // network
|
||||
syslog_init(); // start Syslog
|
||||
|
||||
EMSESP::uart_init(); // start UART
|
||||
syslog_init(); // start syslog
|
||||
}
|
||||
|
||||
// button single click
|
||||
void System::button_OnClick(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - single click");
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_TEST)
|
||||
#ifndef EMSESP_STANDALONE
|
||||
Test::listDir(LittleFS, FS_CONFIG_DIRECTORY, 3);
|
||||
#endif
|
||||
@@ -437,7 +462,6 @@ void System::button_OnVLongPress(PButton & b) {
|
||||
LOG_DEBUG("Button pressed - very long press");
|
||||
#ifndef EMSESP_STANDALONE
|
||||
LOG_WARNING("Performing factory reset...");
|
||||
EMSESP::console_.loop();
|
||||
EMSESP::esp8266React.factoryReset();
|
||||
#endif
|
||||
}
|
||||
@@ -519,29 +543,15 @@ void System::loop() {
|
||||
|
||||
led_monitor(); // check status and report back using the LED
|
||||
system_check(); // check system health
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
/*
|
||||
static uint32_t last_memcheck_ = 0;
|
||||
if (currentMillis - last_memcheck_ > 10000) { // 10 seconds
|
||||
last_memcheck_ = currentMillis;
|
||||
show_mem("core");
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// send MQTT info topic appended with the version information as JSON, as a retained flag
|
||||
void System::send_info_mqtt(const char * event_str, bool send_ntp) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
|
||||
doc["event"] = event_str;
|
||||
doc["version"] = EMSESP_APP_VERSION;
|
||||
// use dynamic json because it is called from NTP-callback from lwip task with small stack
|
||||
DynamicJsonDocument doc = DynamicJsonDocument(EMSESP_JSON_SIZE_MEDIUM);
|
||||
doc["event"] = event_str;
|
||||
doc["version"] = EMSESP_APP_VERSION;
|
||||
|
||||
// if NTP is enabled send the boot_time in local time in ISO 8601 format (eg: 2022-11-15 20:46:38)
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/751
|
||||
@@ -554,15 +564,18 @@ void System::send_info_mqtt(const char * event_str, bool send_ntp) {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (EMSESP::system_.ethernet_connected()) {
|
||||
doc["network"] = "ethernet";
|
||||
doc["hostname"] = ETH.getHostname();
|
||||
doc["network"] = "ethernet";
|
||||
doc["hostname"] = ETH.getHostname();
|
||||
/*
|
||||
doc["MAC"] = ETH.macAddress();
|
||||
doc["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
||||
doc["IPv4 gateway"] = uuid::printable_to_string(ETH.gatewayIP());
|
||||
doc["IPv4 nameserver"] = uuid::printable_to_string(ETH.dnsIP());
|
||||
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") {
|
||||
doc["IPv6 address"] = uuid::printable_to_string(ETH.localIPv6());
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
} else if (WiFi.status() == WL_CONNECTED) {
|
||||
doc["network"] = "wifi";
|
||||
doc["hostname"] = WiFi.getHostname();
|
||||
@@ -578,7 +591,7 @@ void System::send_info_mqtt(const char * event_str, bool send_ntp) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Mqtt::publish_retain(F_(info), doc.as<JsonObject>(), true); // topic called "info" and it's Retained
|
||||
Mqtt::queue_publish_retain(F_(info), doc.as<JsonObject>(), true); // topic called "info" and it's Retained
|
||||
}
|
||||
|
||||
// create the json for heartbeat
|
||||
@@ -617,13 +630,14 @@ bool System::heartbeat_json(JsonObject & output) {
|
||||
output["apicalls"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
|
||||
output["apifails"] = WebAPIService::api_fails();
|
||||
|
||||
if (EMSESP::dallas_enabled() || EMSESP::analog_enabled()) {
|
||||
output["sensorreads"] = EMSESP::dallassensor_.reads() + EMSESP::analogsensor_.reads();
|
||||
output["sensorfails"] = EMSESP::dallassensor_.fails() + EMSESP::analogsensor_.fails();
|
||||
if (EMSESP::sensor_enabled() || EMSESP::analog_enabled()) {
|
||||
output["sensorreads"] = EMSESP::temperaturesensor_.reads() + EMSESP::analogsensor_.reads();
|
||||
output["sensorfails"] = EMSESP::temperaturesensor_.fails() + EMSESP::analogsensor_.fails();
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
output["freemem"] = ESP.getFreeHeap() / 1024; // kilobytes
|
||||
output["freemem"] = getHeapMem();
|
||||
output["max_alloc"] = getMaxAllocMem();
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -644,11 +658,13 @@ void System::send_heartbeat() {
|
||||
return;
|
||||
}
|
||||
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
if (heartbeat_json(json)) {
|
||||
Mqtt::publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
||||
Mqtt::queue_publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,11 +675,14 @@ void System::network_init(bool refresh) {
|
||||
}
|
||||
|
||||
last_system_check_ = 0; // force the LED to go from fast flash to pulse
|
||||
send_heartbeat();
|
||||
|
||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE) {
|
||||
bool disableEth;
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) { disableEth = settings.ssid.length() > 0; });
|
||||
|
||||
// no ethernet present or disabled
|
||||
if (phy_type_ == PHY_type::PHY_TYPE_NONE || disableEth) {
|
||||
return;
|
||||
}
|
||||
} // no ethernet present
|
||||
|
||||
// configure Ethernet
|
||||
int mdc = 23; // Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
|
||||
@@ -671,14 +690,14 @@ void System::network_init(bool refresh) {
|
||||
uint8_t phy_addr = eth_phy_addr_; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
||||
int8_t power = eth_power_; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
|
||||
eth_phy_type_t type = (phy_type_ == PHY_type::PHY_TYPE_LAN8720) ? ETH_PHY_LAN8720 : ETH_PHY_TLK110; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
||||
// clock mode
|
||||
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
||||
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
||||
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
||||
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
||||
// clock mode:
|
||||
// ETH_CLOCK_GPIO0_IN = 0 RMII clock input to GPIO0
|
||||
// ETH_CLOCK_GPIO0_OUT = 1 RMII clock output from GPIO0
|
||||
// ETH_CLOCK_GPIO16_OUT = 2 RMII clock output from GPIO16
|
||||
// ETH_CLOCK_GPIO17_OUT = 3 RMII clock output from GPIO17, for 50hz inverted clock
|
||||
auto clock_mode = (eth_clock_mode_t)eth_clock_mode_;
|
||||
|
||||
ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
|
||||
eth_present_ = ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
|
||||
}
|
||||
|
||||
// check health of system, done every 5 seconds
|
||||
@@ -715,7 +734,6 @@ void System::system_check() {
|
||||
digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
|
||||
#endif
|
||||
}
|
||||
send_heartbeat();
|
||||
} else {
|
||||
// turn off LED so we're ready to the flashes
|
||||
if (led_gpio_) {
|
||||
@@ -732,23 +750,22 @@ void System::system_check() {
|
||||
}
|
||||
|
||||
// commands - takes static function pointers
|
||||
// can be called via Console using 'call system <cmd>'
|
||||
void System::commands_init() {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, FL_(send_cmd), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, FL_(fetch_cmd), CommandFlag::ADMIN_ONLY);
|
||||
|
||||
// restart and watch (and test) are also exposed as Console commands
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, FL_(restart_cmd), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, FL_(watch_cmd));
|
||||
|
||||
if (Mqtt::enabled()) {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, FL_(publish_cmd));
|
||||
}
|
||||
#if defined(EMSESP_TEST)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, ("test"), System::command_test, FL_(test_cmd));
|
||||
#endif
|
||||
|
||||
// these commands will return data in JSON format
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, FL_(system_info_cmd));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, FL_(commands_cmd));
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, ("test"), System::command_test, FL_(test_cmd));
|
||||
#endif
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("response"), System::command_response, FL_(commands_response));
|
||||
|
||||
// MQTT subscribe "ems-esp/system/#"
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::SYSTEM, "system/#", nullptr); // use empty function callback
|
||||
@@ -830,7 +847,7 @@ void System::led_monitor() {
|
||||
}
|
||||
|
||||
if (led_on_) {
|
||||
digitalWrite(led_gpio_, LED_ON); // LED off
|
||||
digitalWrite(led_gpio_, LED_ON); // LED off
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
@@ -880,19 +897,27 @@ void System::show_users(uuid::console::Shell & shell) {
|
||||
}
|
||||
|
||||
void System::show_system(uuid::console::Shell & shell) {
|
||||
refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
shell.println("System:");
|
||||
shell.printfln(" Board profile: %s", board_profile().c_str());
|
||||
shell.printfln(" Uptime: %s", uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3).c_str());
|
||||
#ifndef EMSESP_STANDALONE
|
||||
shell.printfln(" SDK version: %s", ESP.getSdkVersion());
|
||||
shell.printfln(" CPU frequency: %lu MHz", ESP.getCpuFreqMHz());
|
||||
shell.printfln(" Free heap: %lu KB", (uint32_t)ESP.getFreeHeap() / 1024);
|
||||
shell.printfln(" Free heap/Max alloc: %lu KB / %lu KB", getHeapMem(), getMaxAllocMem());
|
||||
shell.printfln(" App used/free: %lu KB / %lu KB", appUsed(), appFree());
|
||||
uint32_t FSused = LittleFS.usedBytes() / 1024;
|
||||
shell.printfln(" FS used/free: %lu KB / %lu KB", FSused, FStotal() - FSused);
|
||||
shell.println();
|
||||
|
||||
shell.println("Network:");
|
||||
|
||||
// show ethernet mac address if we have an eth controller present
|
||||
if (eth_present_) {
|
||||
shell.printfln(" Ethernet MAC address: %s", ETH.macAddress().c_str());
|
||||
}
|
||||
|
||||
switch (WiFi.status()) {
|
||||
case WL_IDLE_STATUS:
|
||||
shell.printfln(" Status: Idle");
|
||||
@@ -907,7 +932,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
break;
|
||||
|
||||
case WL_CONNECTED:
|
||||
shell.printfln(" Status: connected");
|
||||
shell.printfln(" Status: WiFi connected");
|
||||
shell.printfln(" SSID: %s", WiFi.SSID().c_str());
|
||||
shell.printfln(" BSSID: %s", WiFi.BSSIDstr().c_str());
|
||||
shell.printfln(" RSSI: %d dBm (%d %%)", WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
|
||||
@@ -935,7 +960,8 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
|
||||
case WL_NO_SHIELD:
|
||||
default:
|
||||
shell.printfln(" WiFi Network: Unknown");
|
||||
shell.printfln(" WiFi MAC address: %s", WiFi.macAddress().c_str());
|
||||
shell.printfln(" WiFi Network: not connected");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -943,7 +969,7 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
if (ethernet_connected_) {
|
||||
shell.println();
|
||||
shell.printfln(" Status: Ethernet connected");
|
||||
shell.printfln(" MAC address: %s", ETH.macAddress().c_str());
|
||||
shell.printfln(" Ethernet MAC address: %s", ETH.macAddress().c_str());
|
||||
shell.printfln(" Hostname: %s", ETH.getHostname());
|
||||
shell.printfln(" IPv4 address: %s/%s", uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
||||
shell.printfln(" IPv4 gateway: %s", uuid::printable_to_string(ETH.gatewayIP()).c_str());
|
||||
@@ -996,9 +1022,16 @@ bool System::check_restore() {
|
||||
reboot_required |= saveSettings(NTP_SETTINGS_FILE, "NTP", input);
|
||||
reboot_required |= saveSettings(SECURITY_SETTINGS_FILE, "Security", input);
|
||||
reboot_required |= saveSettings(EMSESP_SETTINGS_FILE, "Settings", input);
|
||||
reboot_required |= saveSettings(OTA_SETTINGS_FILE, "OTA", input);
|
||||
} else if (settings_type == "customizations") {
|
||||
// it's a customization file, just replace it and there's no need to reboot
|
||||
saveSettings(EMSESP_CUSTOMIZATION_FILE, "Customizations", input);
|
||||
} else if (settings_type == "schedule") {
|
||||
// it's a schedule file, just replace it and there's no need to reboot
|
||||
saveSettings(EMSESP_SCHEDULER_FILE, "Schedule", input);
|
||||
} else if (settings_type == "entities") {
|
||||
// it's a entity file, just replace it and there's no need to reboot
|
||||
saveSettings(EMSESP_ENTITY_FILE, "Entities", input);
|
||||
} else {
|
||||
LOG_ERROR("Unrecognized file uploaded");
|
||||
}
|
||||
@@ -1029,15 +1062,14 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
// see if we're missing a version, will be < 3.5.0b13 from Dec 23 2022
|
||||
missing_version = (settingsVersion.empty() || (settingsVersion.length() < 5));
|
||||
if (missing_version) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("No version information found (%s)", settingsVersion.c_str());
|
||||
#endif
|
||||
settingsVersion = "3.4.4"; // this was the last stable version
|
||||
settingsVersion = "3.5.0"; // this was the last stable version
|
||||
}
|
||||
}
|
||||
|
||||
version::Semver200_version settings_version(settingsVersion);
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
if (!missing_version) {
|
||||
LOG_INFO("Current version from settings is %d.%d.%d-%s",
|
||||
settings_version.major(),
|
||||
@@ -1045,6 +1077,7 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
settings_version.patch(),
|
||||
settings_version.prerelease().c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// always save the new version to the settings
|
||||
EMSESP::webSettingsService.update(
|
||||
@@ -1067,9 +1100,7 @@ bool System::check_upgrade(bool factory_settings) {
|
||||
|
||||
// if we're coming from 3.4.4 or 3.5.0b14 then we need to apply new settings
|
||||
if (missing_version) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("Setting MQTT ID Entity to v3.4 format");
|
||||
#endif
|
||||
LOG_DEBUG("Setting MQTT Entity ID format to v3.4 format");
|
||||
EMSESP::esp8266React.getMqttSettingsService()->update(
|
||||
[&](MqttSettings & mqttSettings) {
|
||||
mqttSettings.entity_format = 0; // use old Entity ID format from v3.4
|
||||
@@ -1119,7 +1150,7 @@ bool System::saveSettings(const char * filename, const char * section, JsonObjec
|
||||
if (section_json) {
|
||||
File section_file = LittleFS.open(filename, "w");
|
||||
if (section_file) {
|
||||
LOG_INFO("Applying new %s settings", section);
|
||||
LOG_INFO("Applying new %s", section);
|
||||
serializeJson(section_json, section_file);
|
||||
section_file.close();
|
||||
return true; // reboot required
|
||||
@@ -1141,9 +1172,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
node["uptime (seconds)"] = uuid::get_uptime_sec();
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["free mem"] = ESP.getFreeHeap() / 1024; // kilobytes
|
||||
node["max alloc"] = ESP.getMaxAllocHeap() / 1024; // kilobytes
|
||||
node["free app"] = EMSESP::system_.appFree(); // kilobytes
|
||||
node["free mem"] = getHeapMem();
|
||||
node["max alloc"] = getMaxAllocMem();
|
||||
node["free app"] = EMSESP::system_.appFree(); // kilobytes
|
||||
#endif
|
||||
node["reset reason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1);
|
||||
|
||||
@@ -1151,9 +1182,9 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
// Network Status
|
||||
node = output.createNestedObject("Network Info");
|
||||
if (EMSESP::system_.ethernet_connected()) {
|
||||
node["network"] = "Ethernet";
|
||||
node["hostname"] = ETH.getHostname();
|
||||
node["MAC"] = ETH.macAddress();
|
||||
node["network"] = "Ethernet";
|
||||
node["hostname"] = ETH.getHostname();
|
||||
// node["MAC"] = ETH.macAddress();
|
||||
node["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
||||
node["IPv4 gateway"] = uuid::printable_to_string(ETH.gatewayIP());
|
||||
node["IPv4 nameserver"] = uuid::printable_to_string(ETH.dnsIP());
|
||||
@@ -1163,9 +1194,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
} else if (WiFi.status() == WL_CONNECTED) {
|
||||
node["network"] = "WiFi";
|
||||
node["hostname"] = WiFi.getHostname();
|
||||
// node["SSID"] = WiFi.SSID();
|
||||
// node["BSSID"] = WiFi.BSSIDstr();
|
||||
node["RSSI"] = WiFi.RSSI();
|
||||
node["RSSI"] = WiFi.RSSI();
|
||||
// node["MAC"] = WiFi.macAddress();
|
||||
node["IPv4 address"] = uuid::printable_to_string(WiFi.localIP()) + "/" + uuid::printable_to_string(WiFi.subnetMask());
|
||||
node["IPv4 gateway"] = uuid::printable_to_string(WiFi.gatewayIP());
|
||||
@@ -1231,6 +1260,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["entity format"] = settings.entity_format;
|
||||
node["base"] = settings.base;
|
||||
node["discovery prefix"] = settings.discovery_prefix;
|
||||
node["discovery type"] = settings.discovery_type;
|
||||
node["nested format"] = settings.nested_format;
|
||||
node["ha enabled"] = settings.ha_enabled;
|
||||
node["mqtt qos"] = settings.mqtt_qos;
|
||||
@@ -1261,10 +1291,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
|
||||
// Sensor Status
|
||||
node = output.createNestedObject("Sensor Info");
|
||||
if (EMSESP::dallas_enabled()) {
|
||||
node["temperature sensors"] = EMSESP::dallassensor_.no_sensors();
|
||||
node["temperature sensor reads"] = EMSESP::dallassensor_.reads();
|
||||
node["temperature sensor fails"] = EMSESP::dallassensor_.fails();
|
||||
if (EMSESP::sensor_enabled()) {
|
||||
node["temperature sensors"] = EMSESP::temperaturesensor_.no_sensors();
|
||||
node["temperature sensor reads"] = EMSESP::temperaturesensor_.reads();
|
||||
node["temperature sensor fails"] = EMSESP::temperaturesensor_.fails();
|
||||
}
|
||||
if (EMSESP::analog_enabled()) {
|
||||
node["analog sensors"] = EMSESP::analogsensor_.no_sensors();
|
||||
@@ -1383,11 +1413,10 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_TEST)
|
||||
// run a test, e.g. http://ems-esp/api?device=system&cmd=test&data=boiler
|
||||
bool System::command_test(const char * value, const int8_t id) {
|
||||
Test::run_test(value, id);
|
||||
return true;
|
||||
return Test::run_test(value, id);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1403,25 +1432,27 @@ bool System::command_test(const char * value, const int8_t id) {
|
||||
// 3 = RMII clock output from GPIO17, for 50hz inverted clock
|
||||
bool System::load_board_profile(std::vector<int8_t> & data, const std::string & board_profile) {
|
||||
if (board_profile == "S32") {
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // BBQKees Gateway S32
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // BBQKees Gateway S32
|
||||
} else if (board_profile == "E32") {
|
||||
data = {2, 4, 5, 17, 33, PHY_type::PHY_TYPE_LAN8720, 16, 1, 0}; // BBQKees Gateway E32
|
||||
} else if (board_profile == "MH-ET") {
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // MH-ET Live D1 Mini
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // MH-ET Live D1 Mini
|
||||
} else if (board_profile == "NODEMCU") {
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // NodeMCU 32S
|
||||
data = {2, 18, 23, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // NodeMCU 32S
|
||||
} else if (board_profile == "LOLIN") {
|
||||
data = {2, 18, 17, 16, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Lolin D32
|
||||
data = {2, 18, 17, 16, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Lolin D32
|
||||
} else if (board_profile == "OLIMEX") {
|
||||
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, -1, 0, 0}; // Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Dallas)
|
||||
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, -1, 0, 0}; // Olimex ESP32-EVB (uses U1TXD/U1RXD/BUTTON, no LED or Temperature sensor)
|
||||
} else if (board_profile == "OLIMEXPOE") {
|
||||
data = {0, 0, 36, 4, 34, PHY_type::PHY_TYPE_LAN8720, 12, 0, 3}; // Olimex ESP32-POE
|
||||
} else if (board_profile == "C3MINI") {
|
||||
data = {7, 1, 4, 5, 9, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Lolin C3 Mini
|
||||
data = {7, 1, 4, 5, 9, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Lolin C3 Mini
|
||||
} else if (board_profile == "S2MINI") {
|
||||
data = {15, 7, 11, 12, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Lolin S2 Mini
|
||||
data = {15, 7, 11, 12, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Lolin S2 Mini
|
||||
} else if (board_profile == "S3MINI") {
|
||||
data = {17, 18, 8, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Liligo S3
|
||||
data = {17, 18, 8, 5, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // Liligo S3
|
||||
} else if (board_profile == "S32S3") {
|
||||
data = {2, 18, 5, 17, 0, PHY_type::PHY_TYPE_NONE, 0, 0, 0}; // BBQKees Gateway S3
|
||||
} else if (board_profile == "CUSTOM") {
|
||||
// send back current values
|
||||
data = {(int8_t)EMSESP::system_.led_gpio_,
|
||||
@@ -1488,7 +1519,7 @@ std::string System::reset_reason(uint8_t cpu) const {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return ("Unkonwn");
|
||||
return ("Unknown");
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
||||
41
src/system.h
41
src/system.h
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -55,14 +55,14 @@ class System {
|
||||
static bool command_publish(const char * value, const int8_t id);
|
||||
static bool command_fetch(const char * value, const int8_t id);
|
||||
static bool command_restart(const char * value, const int8_t id);
|
||||
#if defined(EMSESP_DEBUG)
|
||||
static bool command_test(const char * value, const int8_t id);
|
||||
#endif
|
||||
static bool command_syslog_level(const char * value, const int8_t id);
|
||||
static bool command_watch(const char * value, const int8_t id);
|
||||
|
||||
static bool command_info(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_response(const char * value, const int8_t id, JsonObject & output);
|
||||
#if defined(EMSESP_TEST)
|
||||
static bool command_test(const char * value, const int8_t id);
|
||||
#endif
|
||||
|
||||
std::string reset_reason(uint8_t cpu) const;
|
||||
|
||||
@@ -230,9 +230,38 @@ class System {
|
||||
return appused_;
|
||||
}
|
||||
|
||||
// memory in kb
|
||||
static uint32_t getMaxAllocMem() {
|
||||
return max_alloc_mem_;
|
||||
}
|
||||
static uint32_t getHeapMem() {
|
||||
return heap_mem_;
|
||||
}
|
||||
static void refreshHeapMem() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
max_alloc_mem_ = ESP.getMaxAllocHeap() / 1024;
|
||||
heap_mem_ = ESP.getFreeHeap() / 1024;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool test_set_all_active() {
|
||||
return test_set_all_active_;
|
||||
}
|
||||
static void test_set_all_active(bool n) {
|
||||
#if defined(EMSESP_TEST)
|
||||
if (n) {
|
||||
logger_.debug("Using dummy entity values");
|
||||
}
|
||||
#endif
|
||||
test_set_all_active_ = n;
|
||||
}
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
static bool restart_requested_;
|
||||
static bool test_set_all_active_; // force all entities in a device to have a value
|
||||
static uint32_t max_alloc_mem_;
|
||||
static uint32_t heap_mem_;
|
||||
|
||||
// button
|
||||
static PButton myPButton_; // PButton instance
|
||||
@@ -271,6 +300,8 @@ class System {
|
||||
bool ntp_connected_ = false;
|
||||
uint32_t ntp_last_check_ = 0;
|
||||
|
||||
bool eth_present_ = false;
|
||||
|
||||
// EMS-ESP settings
|
||||
// copies from WebSettings class in WebSettingsService.h and loaded with reload_settings()
|
||||
std::string hostname_;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -148,11 +148,11 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
// validate the CRC. if it fails then increment the number of corrupt/incomplete telegrams and only report to console/syslog
|
||||
uint8_t crc = calculate_crc(data, length - 1);
|
||||
if (data[length - 1] != crc) {
|
||||
if ((data[0] & 0x7F) != ems_bus_id()) { // do not count echos as errors
|
||||
if ((data[0] & 0x7F) != ems_bus_id()) { // do not count echos as errors
|
||||
telegram_error_count_++;
|
||||
LOG_WARNING("Incomplete Rx: %s", Helpers::data_to_hex(data, length - 1).c_str()); // exclude CRC
|
||||
} else {
|
||||
LOG_TRACE("Incomplete Rx: %s", Helpers::data_to_hex(data, length - 1).c_str()); // exclude CRC
|
||||
LOG_TRACE("Incomplete Rx: %s", Helpers::data_to_hex(data, length - 1).c_str()); // exclude CRC
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -211,9 +211,7 @@ void RxService::add(uint8_t * data, uint8_t length) {
|
||||
LOG_TRACE("Rx: %s", Helpers::data_to_hex(data, length).c_str());
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] New Rx telegram, message length %d", message_length);
|
||||
#endif
|
||||
LOG_DEBUG("New Rx telegram, message length %d", message_length);
|
||||
|
||||
// if we don't have a type_id exit,
|
||||
// do not exit on empty message, it is checked for toggle fetch
|
||||
@@ -349,7 +347,7 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
telegram_raw[5] = (telegram->type_id >> 8) - 1; // type, 1st byte, high-byte, subtract 0x100
|
||||
telegram_raw[6] = telegram->type_id & 0xFF; // type, 2nd byte, low-byte
|
||||
message_p = 7;
|
||||
copy_data = false; // there are no more data values after the type_id when reading on EMS+
|
||||
copy_data = false; // there are no more data values after the type_id when reading on EMS+
|
||||
}
|
||||
} else {
|
||||
// EMS 1.0
|
||||
@@ -398,11 +396,11 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
if (status == EMS_TX_STATUS_ERR) {
|
||||
LOG_ERROR("Failed to transmit Tx via UART.");
|
||||
if (telegram->operation == Telegram::Operation::TX_READ) {
|
||||
increment_telegram_read_fail_count(); // another Tx fail
|
||||
increment_telegram_read_fail_count(); // another Tx fail
|
||||
} else {
|
||||
increment_telegram_write_fail_count(); // another Tx fail
|
||||
}
|
||||
tx_state(Telegram::Operation::NONE); // nothing send, tx not in wait state
|
||||
tx_state(Telegram::Operation::NONE); // nothing send, tx not in wait state
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -443,9 +441,7 @@ void TxService::add(const uint8_t operation,
|
||||
const bool front) {
|
||||
auto telegram = std::make_shared<Telegram>(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
#endif
|
||||
LOG_DEBUG("New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
|
||||
// if the queue is full, make room by removing the last one
|
||||
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
|
||||
@@ -460,7 +456,7 @@ void TxService::add(const uint8_t operation,
|
||||
if (front) {
|
||||
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram), false, validateid); // add to front of queue
|
||||
} else {
|
||||
tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validateid); // add to back of queue
|
||||
tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validateid); // add to back of queue
|
||||
}
|
||||
if (validateid != 0) {
|
||||
EMSESP::wait_validate(validateid);
|
||||
@@ -507,9 +503,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
|
||||
// if we don't have a type_id or empty data block, exit
|
||||
if ((type_id == 0) || (message_length == 0)) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] Tx telegram type %d failed, length %d", type_id, message_length);
|
||||
#endif
|
||||
LOG_DEBUG("Tx telegram type %d failed, length %d", type_id, message_length);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -518,11 +512,16 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
operation = Telegram::Operation::NONE; // do not check reply/ack for other ids
|
||||
} else if (dest & 0x80) {
|
||||
operation = Telegram::Operation::TX_READ;
|
||||
EMSESP::set_response_id(type_id);
|
||||
// trigger read of all parts of telegram if requested length is more than 32
|
||||
// compatibility to earlier versions
|
||||
if (message_data[0] >= 32) {
|
||||
EMSESP::set_read_id(type_id);
|
||||
}
|
||||
} else {
|
||||
operation = Telegram::Operation::TX_WRITE;
|
||||
validate_id = type_id;
|
||||
}
|
||||
EMSESP::set_read_id(type_id);
|
||||
}
|
||||
|
||||
auto telegram = std::make_shared<Telegram>(operation, src, dest, type_id, offset, message_data, message_length); // operation is TX_WRITE or TX_READ
|
||||
@@ -537,9 +536,7 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
tx_telegrams_.pop_front();
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
#endif
|
||||
LOG_DEBUG("New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
|
||||
if (front) {
|
||||
// tx_telegrams_.push_front(qtxt); // add to front of queue
|
||||
@@ -554,16 +551,14 @@ void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t lengt
|
||||
}
|
||||
|
||||
// send a Tx telegram to request data from an EMS device
|
||||
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length) {
|
||||
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length, const bool front) {
|
||||
LOG_DEBUG("Tx read request to device 0x%02X for type ID 0x%02X", dest, type_id);
|
||||
|
||||
uint8_t message_data = (type_id > 0xFF) ? (EMS_MAX_TELEGRAM_MESSAGE_LENGTH - 2) : EMS_MAX_TELEGRAM_MESSAGE_LENGTH;
|
||||
// if length set, publish result and set telegram to front
|
||||
if (length) {
|
||||
if (length > 0 && length < message_data) {
|
||||
message_data = length;
|
||||
EMSESP::set_read_id(type_id);
|
||||
}
|
||||
add(Telegram::Operation::TX_READ, dest, type_id, offset, &message_data, 1, 0, length != 0);
|
||||
add(Telegram::Operation::TX_READ, dest, type_id, offset, &message_data, 1, 0, front);
|
||||
}
|
||||
|
||||
// Send a raw telegram to the bus, telegram is a text string of hex values
|
||||
@@ -574,32 +569,16 @@ bool TxService::send_raw(const char * telegram_data) {
|
||||
|
||||
// since the telegram data is a const, make a copy. add 1 to grab the \0 EOS
|
||||
char telegram[EMS_MAX_TELEGRAM_LENGTH * 3];
|
||||
for (uint8_t i = 0; i < strlen(telegram_data); i++) {
|
||||
telegram[i] = telegram_data[i];
|
||||
}
|
||||
telegram[strlen(telegram_data)] = '\0'; // make sure its terminated
|
||||
strlcpy(telegram, telegram_data, sizeof(telegram));
|
||||
|
||||
uint8_t count = 0;
|
||||
char * p;
|
||||
char value[10] = {0};
|
||||
|
||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||
|
||||
// get first value, which should be the src
|
||||
if ((p = strtok(telegram, " ,"))) { // delimiter
|
||||
strlcpy(value, p, sizeof(value));
|
||||
data[0] = (uint8_t)strtol(value, 0, 16);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// and iterate until end
|
||||
while (p != 0) {
|
||||
if ((p = strtok(nullptr, " ,"))) {
|
||||
strlcpy(value, p, sizeof(value));
|
||||
auto val = (uint8_t)strtol(value, 0, 16);
|
||||
data[++count] = val;
|
||||
}
|
||||
// get values
|
||||
char * p = strtok(telegram, " ,"); // delimiter
|
||||
while (p != nullptr) {
|
||||
data[count++] = (uint8_t)strtol(p, 0, 16);
|
||||
p = strtok(nullptr, " ,");
|
||||
}
|
||||
|
||||
// check valid length
|
||||
@@ -607,7 +586,7 @@ bool TxService::send_raw(const char * telegram_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
add(Telegram::Operation::TX_RAW, data, count + 1, 0, true); // add to top/front of Tx queue
|
||||
add(Telegram::Operation::TX_RAW, data, count, 0, true); // add to top/front of Tx queue
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -616,14 +595,14 @@ bool TxService::send_raw(const char * telegram_data) {
|
||||
void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length) {
|
||||
// have we reached the limit? if so, reset count and give up
|
||||
if (++retry_count_ > MAXIMUM_TX_RETRIES) {
|
||||
reset_retry_count(); // give up
|
||||
EMSESP::wait_validate(0); // do not wait for validation
|
||||
reset_retry_count(); // give up
|
||||
EMSESP::wait_validate(0); // do not wait for validation
|
||||
if (operation == Telegram::Operation::TX_READ) {
|
||||
if (telegram_last_->offset > 0) { // ignore errors for higher offsets
|
||||
LOG_DEBUG("Last Tx Read operation failed after %d retries. Ignoring request: %s", MAXIMUM_TX_RETRIES, telegram_last_->to_string().c_str());
|
||||
return;
|
||||
}
|
||||
increment_telegram_read_fail_count(); // another Tx fail
|
||||
increment_telegram_read_fail_count(); // another Tx fail
|
||||
} else {
|
||||
increment_telegram_write_fail_count(); // another Tx fail
|
||||
}
|
||||
@@ -639,13 +618,11 @@ void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const ui
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("[DEBUG] Last Tx %s operation failed. Retry #%d. sent message: %s, received: %s",
|
||||
LOG_DEBUG("Last Tx %s operation failed. Retry #%d. sent message: %s, received: %s",
|
||||
(operation == Telegram::Operation::TX_WRITE) ? ("Write") : ("Read"),
|
||||
retry_count_,
|
||||
telegram_last_->to_string().c_str(),
|
||||
Helpers::data_to_hex(data, length - 1).c_str());
|
||||
#endif
|
||||
|
||||
// add to the top of the queue
|
||||
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -37,30 +37,20 @@
|
||||
#define MAX_TX_TELEGRAMS 50 // size of Tx queue
|
||||
|
||||
// default values for null values
|
||||
static constexpr uint8_t EMS_VALUE_BOOL = 0xFF; // used to mark that something is a boolean
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_OFF = 0x00; // boolean false
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_ON = 0x01; // boolean true. True can be 0x01 or 0xFF sometimes
|
||||
|
||||
static constexpr uint8_t EMS_VALUE_BOOL = 0xFF; // used to mark that something is a boolean
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_OFF = 0x00; // boolean false
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_ON = 0x01; // boolean true. True can be 0x01 or 0xFF sometimes
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_NOTSET = 0xFE; // random number for booleans, that's not 0, 1 or FF
|
||||
static constexpr uint8_t EMS_VALUE_UINT_NOTSET = 0xFF; // for 8-bit unsigned ints/bytes
|
||||
static constexpr int8_t EMS_VALUE_INT_NOTSET = 0x7F; // for signed 8-bit ints/bytes
|
||||
static constexpr uint16_t EMS_VALUE_USHORT_NOTSET = 0x7D00; // 32000: for 2-byte unsigned shorts
|
||||
static constexpr int16_t EMS_VALUE_SHORT_NOTSET = 0x7D00; // 32000: for 2-byte signed shorts
|
||||
static constexpr uint16_t EMS_VALUE_USHORT_NOTSET = 0x7D00; // 32000: for 2-byte unsigned shorts
|
||||
static constexpr int16_t EMS_VALUE_SHORT_NOTSET = 0x7D00; // 32000: for 2-byte signed shorts
|
||||
static constexpr uint32_t EMS_VALUE_ULONG_NOTSET = 0x00FFFFFF; // for 3-byte longs
|
||||
static constexpr uint32_t EMS_VALUE_ULLONG_NOTSET = 0xFFFFFFFF; // for 4-byte longs
|
||||
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_LENGTH = 32; // max length of a complete EMS telegram
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = 27; // max length of message block, assuming EMS1.0
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_LENGTH = 32; // max length of a complete EMS telegram
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = 27; // max length of message block, assuming EMS1.0
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
#define EMS_VALUE_DEFAULT_INT 11
|
||||
#define EMS_VALUE_DEFAULT_UINT -12
|
||||
#define EMS_VALUE_DEFAULT_SHORT -1234
|
||||
#define EMS_VALUE_DEFAULT_USHORT 1234
|
||||
#define EMS_VALUE_DEFAULT_ULONG 12356
|
||||
#define EMS_VALUE_DEFAULT_BOOL 1
|
||||
#define EMS_VALUE_DEFAULT_ENUM 1
|
||||
#else
|
||||
#define EMS_VALUE_DEFAULT_INT EMS_VALUE_INT_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_UINT EMS_VALUE_UINT_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_SHORT EMS_VALUE_SHORT_NOTSET
|
||||
@@ -68,7 +58,15 @@ static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = 27; // max length of
|
||||
#define EMS_VALUE_DEFAULT_ULONG EMS_VALUE_ULONG_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_BOOL EMS_VALUE_BOOL_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_ENUM EMS_VALUE_UINT_NOTSET
|
||||
#endif
|
||||
|
||||
// used when System::test_set_all_active() is set
|
||||
#define EMS_VALUE_DEFAULT_INT_DUMMY 11
|
||||
#define EMS_VALUE_DEFAULT_UINT_DUMMY -12
|
||||
#define EMS_VALUE_DEFAULT_SHORT_DUMMY -1234
|
||||
#define EMS_VALUE_DEFAULT_USHORT_DUMMY 1235
|
||||
#define EMS_VALUE_DEFAULT_ULONG_DUMMY 12456
|
||||
#define EMS_VALUE_DEFAULT_BOOL_DUMMY 1
|
||||
#define EMS_VALUE_DEFAULT_ENUM_DUMMY 1
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -139,7 +137,7 @@ class Telegram {
|
||||
return (val != value);
|
||||
}
|
||||
|
||||
bool read_enumvalue(uint8_t & value, const uint8_t index, uint8_t start = 0) const {
|
||||
bool read_enumvalue(uint8_t & value, const uint8_t index, int8_t start = 0) const {
|
||||
if ((index < this->offset) || ((index - this->offset) >= this->message_length)) {
|
||||
return false;
|
||||
}
|
||||
@@ -234,13 +232,13 @@ class EMSbus {
|
||||
private:
|
||||
static constexpr uint32_t EMS_BUS_TIMEOUT = 30000; // timeout in ms before recognizing the ems bus is offline (30 seconds)
|
||||
|
||||
static uint32_t last_bus_activity_; // timestamp of last time a valid Rx came in
|
||||
static uint32_t bus_uptime_start_; // timestamp of first time we connected to the bus
|
||||
static bool bus_connected_; // start assuming the bus hasn't been connected
|
||||
static uint8_t ems_mask_; // unset=0xFF, buderus=0x00, junkers/ht3=0x80
|
||||
static uint8_t ems_bus_id_; // the bus id, which configurable and stored in settings
|
||||
static uint8_t tx_mode_; // local copy of the tx mode
|
||||
static uint8_t tx_state_; // state of the Tx line (NONE or waiting on a TX_READ or TX_WRITE)
|
||||
static uint32_t last_bus_activity_; // timestamp of last time a valid Rx came in
|
||||
static uint32_t bus_uptime_start_; // timestamp of first time we connected to the bus
|
||||
static bool bus_connected_; // start assuming the bus hasn't been connected
|
||||
static uint8_t ems_mask_; // unset=0xFF, buderus=0x00, junkers/ht3=0x80
|
||||
static uint8_t ems_bus_id_; // the bus id, which configurable and stored in settings
|
||||
static uint8_t tx_mode_; // local copy of the tx mode
|
||||
static uint8_t tx_state_; // state of the Tx line (NONE or waiting on a TX_READ or TX_WRITE)
|
||||
};
|
||||
|
||||
class RxService : public EMSbus {
|
||||
@@ -319,7 +317,7 @@ class TxService : public EMSbus {
|
||||
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, const bool readId = false);
|
||||
bool send_raw(const char * telegram_data);
|
||||
void send_poll() const;
|
||||
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);
|
||||
@@ -435,17 +433,17 @@ class TxService : public EMSbus {
|
||||
private:
|
||||
std::deque<QueuedTxTelegram> tx_telegrams_; // the Tx queue
|
||||
|
||||
uint32_t telegram_read_count_ = 0; // # Tx successful reads
|
||||
uint32_t telegram_write_count_ = 0; // # Tx successful writes
|
||||
uint32_t telegram_read_fail_count_ = 0; // # Tx unsuccessful transmits
|
||||
uint32_t telegram_write_fail_count_ = 0; // # Tx unsuccessful transmits
|
||||
uint32_t telegram_read_count_ = 0; // # Tx successful reads
|
||||
uint32_t telegram_write_count_ = 0; // # Tx successful writes
|
||||
uint32_t telegram_read_fail_count_ = 0; // # Tx unsuccessful transmits
|
||||
uint32_t telegram_write_fail_count_ = 0; // # Tx unsuccessful transmits
|
||||
|
||||
std::shared_ptr<Telegram> telegram_last_;
|
||||
uint16_t telegram_last_post_send_query_; // which type ID to query after a successful send, to read back the values just written
|
||||
uint8_t retry_count_ = 0; // count for # Tx retries
|
||||
uint32_t delayed_send_ = 0; // manage delay for post send query
|
||||
|
||||
uint8_t tx_telegram_id_ = 0; // queue counter
|
||||
uint8_t tx_telegram_id_ = 0; // queue counter
|
||||
|
||||
void send_telegram(const QueuedTxTelegram & tx_telegram);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
// code originally written by nomis - https://github.com/nomis
|
||||
|
||||
#include "dallassensor.h"
|
||||
#include "temperaturesensor.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
#ifdef ESP32
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger DallasSensor::logger_{F_(dallassensor), uuid::log::Facility::DAEMON};
|
||||
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
|
||||
|
||||
// start the 1-wire
|
||||
void DallasSensor::start() {
|
||||
void TemperatureSensor::start() {
|
||||
reload();
|
||||
|
||||
if (!dallas_gpio_) {
|
||||
@@ -42,32 +42,32 @@ void DallasSensor::start() {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bus_.begin(dallas_gpio_);
|
||||
LOG_INFO("Starting Dallas sensor service");
|
||||
LOG_INFO("Starting Temperature sensor service");
|
||||
#endif
|
||||
|
||||
// Add API calls
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
EMSdevice::DeviceType::TEMPERATURESENSOR,
|
||||
F_(info),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
|
||||
FL_(info_cmd));
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
EMSdevice::DeviceType::TEMPERATURESENSOR,
|
||||
F_(values),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, 0, output); },
|
||||
nullptr,
|
||||
CommandFlag::HIDDEN); // this command is hidden
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
EMSdevice::DeviceType::TEMPERATURESENSOR,
|
||||
F_(commands),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
|
||||
FL_(commands_cmd));
|
||||
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::DALLASSENSOR, "dallasssensor/#", nullptr); // use empty function callback
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::TEMPERATURESENSOR, "temperaturesensor/#", nullptr); // use empty function callback
|
||||
}
|
||||
|
||||
// load settings
|
||||
void DallasSensor::reload() {
|
||||
void TemperatureSensor::reload() {
|
||||
// load the service settings
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
dallas_gpio_ = settings.dallas_gpio;
|
||||
@@ -80,7 +80,7 @@ void DallasSensor::reload() {
|
||||
}
|
||||
}
|
||||
|
||||
void DallasSensor::loop() {
|
||||
void TemperatureSensor::loop() {
|
||||
if (!dallas_gpio_) {
|
||||
return; // dallas gpio is 0 (disabled)
|
||||
}
|
||||
@@ -91,7 +91,7 @@ void DallasSensor::loop() {
|
||||
if (state_ == State::IDLE) {
|
||||
if (time_now - last_activity_ >= READ_INTERVAL_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_DEBUG("[DEBUG] Read sensor temperature");
|
||||
LOG_DEBUG("Read sensor temperature");
|
||||
#endif
|
||||
if (bus_.reset() || parasite_) {
|
||||
YIELD;
|
||||
@@ -119,13 +119,13 @@ void DallasSensor::loop() {
|
||||
} else if (state_ == State::READING) {
|
||||
if (temperature_convert_complete() && (time_now - last_activity_ > CONVERSION_MS)) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_DEBUG("Scanning for sensors");
|
||||
LOG_DEBUG("Scanning for temperature sensors");
|
||||
#endif
|
||||
bus_.reset_search();
|
||||
state_ = State::SCANNING;
|
||||
} else if (time_now - last_activity_ > READ_TIMEOUT_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_WARNING("Dallas sensor read timeout");
|
||||
LOG_WARNING("Sensor read timeout");
|
||||
#endif
|
||||
state_ = State::IDLE;
|
||||
sensorfails_++;
|
||||
@@ -133,7 +133,7 @@ void DallasSensor::loop() {
|
||||
} else if (state_ == State::SCANNING) {
|
||||
if (time_now - last_activity_ > SCAN_TIMEOUT_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_ERROR("Dallas sensor scan timeout");
|
||||
LOG_ERROR("Sensor scan timeout");
|
||||
#endif
|
||||
state_ = State::IDLE;
|
||||
sensorfails_++;
|
||||
@@ -188,12 +188,12 @@ void DallasSensor::loop() {
|
||||
|
||||
default:
|
||||
sensorfails_++;
|
||||
LOG_ERROR("Unknown dallas sensor %s", Sensor(addr).id().c_str());
|
||||
LOG_ERROR("Unknown sensor %s", Sensor(addr).id().c_str());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sensorfails_++;
|
||||
LOG_ERROR("Invalid dallas sensor %s", Sensor(addr).id().c_str());
|
||||
LOG_ERROR("Invalid sensor %s", Sensor(addr).id().c_str());
|
||||
}
|
||||
} else {
|
||||
if (!parasite_) {
|
||||
@@ -211,10 +211,10 @@ void DallasSensor::loop() {
|
||||
scancnt_ = 0;
|
||||
} else if (scancnt_ == SCAN_START + 1) { // startup
|
||||
firstscan_ = sensors_.size();
|
||||
// LOG_DEBUG("Adding %d dallas sensor(s) from first scan", firstscan_);
|
||||
// LOG_DEBUG("Adding %d sensor(s) from first scan", firstscan_);
|
||||
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
|
||||
scancnt_ = SCAN_START;
|
||||
sensors_.clear(); // restart scaning and clear to get correct numbering
|
||||
sensors_.clear(); // restart scanning and clear to get correct numbering
|
||||
}
|
||||
state_ = State::IDLE;
|
||||
}
|
||||
@@ -223,7 +223,7 @@ void DallasSensor::loop() {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DallasSensor::temperature_convert_complete() {
|
||||
bool TemperatureSensor::temperature_convert_complete() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (parasite_) {
|
||||
return true; // don't care, use the minimum time in loop
|
||||
@@ -234,7 +234,7 @@ bool DallasSensor::temperature_convert_complete() {
|
||||
#endif
|
||||
}
|
||||
|
||||
int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
int16_t TemperatureSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!bus_.reset()) {
|
||||
LOG_ERROR("Bus reset failed before reading scratchpad from %s", Sensor(addr).id().c_str());
|
||||
@@ -297,8 +297,8 @@ int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// update dallas information name and offset
|
||||
bool DallasSensor::update(const std::string & id, const std::string & name, int16_t offset) {
|
||||
// update temperature sensor information name and offset
|
||||
bool TemperatureSensor::update(const std::string & id, const std::string & name, int16_t offset) {
|
||||
// find the sensor
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.id() == id) {
|
||||
@@ -346,7 +346,7 @@ bool DallasSensor::update(const std::string & id, const std::string & name, int1
|
||||
}
|
||||
|
||||
// check to see if values have been updated
|
||||
bool DallasSensor::updated_values() {
|
||||
bool TemperatureSensor::updated_values() {
|
||||
if (changed_) {
|
||||
changed_ = false;
|
||||
return true;
|
||||
@@ -355,13 +355,13 @@ bool DallasSensor::updated_values() {
|
||||
}
|
||||
|
||||
// list commands
|
||||
bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObject & output) {
|
||||
return Command::list(EMSdevice::DeviceType::DALLASSENSOR, output);
|
||||
bool TemperatureSensor::command_commands(const char * value, const int8_t id, JsonObject & output) {
|
||||
return Command::list(EMSdevice::DeviceType::TEMPERATURESENSOR, output);
|
||||
}
|
||||
|
||||
// creates JSON doc from values
|
||||
// returns false if there are no sensors
|
||||
bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & output) {
|
||||
bool TemperatureSensor::command_info(const char * value, const int8_t id, JsonObject & output) {
|
||||
if (sensors_.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -387,7 +387,7 @@ bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject
|
||||
}
|
||||
|
||||
// called from emsesp.cpp, similar to the emsdevice->get_value_info
|
||||
bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) {
|
||||
bool TemperatureSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) {
|
||||
if (sensors_.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -404,7 +404,7 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
|
||||
}
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (strcmp(command_s, sensor.name().c_str()) == 0 || strcmp(command_s, sensor.id().c_str()) == 0) {
|
||||
if (Helpers::toLower(command_s) == Helpers::toLower(sensor.name().c_str()) || Helpers::toLower(command_s) == Helpers::toLower(sensor.id().c_str())) {
|
||||
output["id"] = sensor.id();
|
||||
output["name"] = sensor.name();
|
||||
char val[10];
|
||||
@@ -438,37 +438,39 @@ bool DallasSensor::get_value_info(JsonObject & output, const char * cmd, const i
|
||||
}
|
||||
|
||||
// publish a single sensor to MQTT
|
||||
void DallasSensor::publish_sensor(const Sensor & sensor) {
|
||||
if (Mqtt::publish_single()) {
|
||||
void TemperatureSensor::publish_sensor(const Sensor & sensor) {
|
||||
if (Mqtt::enabled() && Mqtt::publish_single()) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", (F_(dallassensor)), sensor.name().c_str());
|
||||
snprintf(topic, sizeof(topic), "%s/%s", (F_(temperaturesensor)), sensor.name().c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", (F_(dallassensor)), "_data", sensor.name().c_str());
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", (F_(temperaturesensor)), "_data", sensor.name().c_str());
|
||||
}
|
||||
char payload[10];
|
||||
Mqtt::publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
// send empty config topic to remove the entry from HA
|
||||
void DallasSensor::remove_ha_topic(const std::string & id) {
|
||||
void TemperatureSensor::remove_ha_topic(const std::string & id) {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG("Removing HA config for temperature sensor ID %s", id.c_str());
|
||||
#endif
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string sensorid = id;
|
||||
std::replace(sensorid.begin(), sensorid.end(), '-', '_');
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::basename().c_str(), sensorid.c_str());
|
||||
Mqtt::publish_ha(topic);
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/temperaturesensor_%s/config", Mqtt::basename().c_str(), sensorid.c_str());
|
||||
Mqtt::queue_remove_topic(topic);
|
||||
}
|
||||
|
||||
// send all dallas sensor values as a JSON package to MQTT
|
||||
void DallasSensor::publish_values(const bool force) {
|
||||
// send all temperature sensor values as a JSON package to MQTT
|
||||
void TemperatureSensor::publish_values(const bool force) {
|
||||
if (!Mqtt::enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t num_sensors = sensors_.size();
|
||||
|
||||
if (num_sensors == 0) {
|
||||
@@ -497,7 +499,7 @@ void DallasSensor::publish_values(const bool force) {
|
||||
}
|
||||
|
||||
// create the HA MQTT config
|
||||
// to e.g. homeassistant/sensor/ems-esp/dallassensor_28-233D-9497-0C03/config
|
||||
// to e.g. homeassistant/sensor/ems-esp/temperaturesensor_28-233D-9497-0C03/config
|
||||
if (Mqtt::ha_enabled()) {
|
||||
if (!has_value && sensor.ha_registered) {
|
||||
remove_ha_topic(sensor.id());
|
||||
@@ -509,31 +511,31 @@ void DallasSensor::publish_values(const bool force) {
|
||||
config["dev_cla"] = "temperature";
|
||||
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str()); // use base path
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/temperaturesensor_data", Mqtt::basename().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
|
||||
char val_obj[50];
|
||||
char val_cond[65];
|
||||
char val_cond[95];
|
||||
if (Mqtt::is_nested()) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s'].temp", sensor.id().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%s'] is defined", sensor.id().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%s'] is defined and %s is defined", sensor.id().c_str(), val_obj);
|
||||
} else {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else -55}}";
|
||||
|
||||
char uniq_s[70];
|
||||
if (Mqtt::entity_format() == 2) {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "%s_dallassensor_%s", Mqtt::basename().c_str(), sensor.id().c_str());
|
||||
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "%s_temperaturesensor_%s", Mqtt::basename().c_str(), sensor.id().c_str());
|
||||
} else {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "dallassensor_%s", sensor.id().c_str());
|
||||
snprintf(uniq_s, sizeof(uniq_s), "temperaturesensor_%s", sensor.id().c_str());
|
||||
}
|
||||
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id/obj_id
|
||||
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s", sensor.name().c_str());
|
||||
@@ -541,7 +543,7 @@ void DallasSensor::publish_values(const bool force) {
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
ids.add(Mqtt::basename());
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
@@ -551,21 +553,19 @@ void DallasSensor::publish_values(const bool force) {
|
||||
std::string sensorid = sensor.id();
|
||||
std::replace(sensorid.begin(), sensorid.end(), '-', '_');
|
||||
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::basename().c_str(), sensorid.c_str());
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/temperaturesensor_%s/config", Mqtt::basename().c_str(), sensorid.c_str());
|
||||
|
||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||
|
||||
sensor.ha_registered = true;
|
||||
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::publish("dallassensor_data", doc.as<JsonObject>());
|
||||
Mqtt::queue_publish("temperaturesensor_data", doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
|
||||
// skip crc from id
|
||||
DallasSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
TemperatureSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
: internal_id_(((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16)
|
||||
| ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6])) {
|
||||
// create ID string
|
||||
@@ -582,14 +582,14 @@ DallasSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
offset_ = 0; // 0 degrees offset
|
||||
}
|
||||
|
||||
uint64_t DallasSensor::get_id(const uint8_t addr[]) {
|
||||
uint64_t TemperatureSensor::get_id(const uint8_t addr[]) {
|
||||
return (((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16)
|
||||
| ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6]));
|
||||
}
|
||||
|
||||
// find the name from the customization service
|
||||
// if empty, return the ID as a string
|
||||
std::string DallasSensor::Sensor::name() const {
|
||||
std::string TemperatureSensor::Sensor::name() const {
|
||||
if (name_.empty()) {
|
||||
return id_;
|
||||
}
|
||||
@@ -598,14 +598,12 @@ std::string DallasSensor::Sensor::name() const {
|
||||
|
||||
// look up in customization service for a specific sensor
|
||||
// and set the name and offset from that entry if it exists
|
||||
bool DallasSensor::Sensor::apply_customization() {
|
||||
bool TemperatureSensor::Sensor::apply_customization() {
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
auto sensors = settings.sensorCustomizations;
|
||||
if (!sensors.empty()) {
|
||||
for (const auto & sensor : sensors) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG("Loading customization for dallas sensor %s", sensor.id.c_str());
|
||||
#endif
|
||||
LOG_DEBUG("Loading customization for temperature sensor %s", sensor.id.c_str());
|
||||
if (id_ == sensor.id) {
|
||||
set_name(sensor.name);
|
||||
set_offset(sensor.offset);
|
||||
@@ -620,9 +618,9 @@ bool DallasSensor::Sensor::apply_customization() {
|
||||
}
|
||||
|
||||
// hard coded tests
|
||||
#ifdef EMSESP_DEBUG
|
||||
void DallasSensor::test() {
|
||||
// add 2 dallas sensors
|
||||
#if defined(EMSESP_TEST)
|
||||
void TemperatureSensor::test() {
|
||||
// add 2 temperature sensors
|
||||
uint8_t addr[ADDR_LEN] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
sensors_.emplace_back(addr);
|
||||
sensors_.back().temperature_c = 123;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
// code originally written by nomis - https://github.com/nomis
|
||||
|
||||
#ifndef EMSESP_DALLASSENSOR_H
|
||||
#define EMSESP_DALLASSENSOR_H
|
||||
#ifndef EMSESP_TEMPERATURESENSOR_H
|
||||
#define EMSESP_TEMPERATURESENSOR_H
|
||||
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class DallasSensor {
|
||||
class TemperatureSensor {
|
||||
public:
|
||||
class Sensor {
|
||||
public:
|
||||
@@ -73,8 +73,8 @@ class DallasSensor {
|
||||
int16_t offset_;
|
||||
};
|
||||
|
||||
DallasSensor() = default;
|
||||
~DallasSensor() = default;
|
||||
TemperatureSensor() = default;
|
||||
~TemperatureSensor() = default;
|
||||
|
||||
void start();
|
||||
void loop();
|
||||
@@ -97,7 +97,7 @@ class DallasSensor {
|
||||
return sensorfails_;
|
||||
}
|
||||
|
||||
bool dallas_enabled() {
|
||||
bool sensor_enabled() {
|
||||
return (dallas_gpio_ != 0);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class DallasSensor {
|
||||
|
||||
bool update(const std::string & id, const std::string & name, int16_t offset);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
#if defined(EMSESP_TEST)
|
||||
void test();
|
||||
#endif
|
||||
|
||||
@@ -128,11 +128,11 @@ class DallasSensor {
|
||||
static constexpr size_t SCRATCHPAD_CONFIG = 4;
|
||||
static constexpr size_t SCRATCHPAD_CNT_REM = 6;
|
||||
|
||||
// dallas chips
|
||||
// dallas chip types
|
||||
static constexpr uint8_t TYPE_DS18B20 = 0x28;
|
||||
static constexpr uint8_t TYPE_DS18S20 = 0x10;
|
||||
static constexpr uint8_t TYPE_DS1822 = 0x22;
|
||||
static constexpr uint8_t TYPE_DS1825 = 0x3B; // also DS1826
|
||||
static constexpr uint8_t TYPE_DS1825 = 0x3B; // also DS1826
|
||||
|
||||
static constexpr uint32_t READ_INTERVAL_MS = 5000; // 5 seconds
|
||||
static constexpr uint32_t CONVERSION_MS = 1000; // 1 seconds
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,18 +17,33 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||
|
||||
#include "test.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// no shell
|
||||
// no shell, called via the API or 'call system test' command
|
||||
// or http://ems-esp/api?device=system&cmd=test&data=boiler
|
||||
bool Test::run_test(const char * command, int8_t id) {
|
||||
if ((command == nullptr) || (strlen(command) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(command, "memory") == 0) {
|
||||
EMSESP::logger().notice("Testing memory by adding lots of devices and entities...");
|
||||
|
||||
System::test_set_all_active(true); // include all entities and give them fake values
|
||||
|
||||
// simulate HansRemmerswaal's setup - see https://github.com/emsesp/EMS-ESP32/issues/859
|
||||
add_device(0x08, 172); // 176 entities - boiler: Enviline/Compress 6000AW/Hybrid 3000-7000iAW/SupraEco/Geo 5xx/WLW196i
|
||||
|
||||
// add_device(0x10, 158); // 62 entities - thermostat: RC300/RC310/Moduline 3000/1010H/CW400/Sense II/HPC410
|
||||
// add_device(0x38, 200); // 4 entities - thermostat: RC100H
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(command, "general") == 0) {
|
||||
EMSESP::logger().info("Testing general. Adding a Boiler and Thermostat");
|
||||
|
||||
@@ -53,7 +68,11 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_DEBUG_LIMITED
|
||||
//
|
||||
// the tests take a lot of memory when built for the ESP32
|
||||
// so only including the full set in standalone, otherwise a limited selection of basic tests
|
||||
//
|
||||
#ifdef EMSESP_STANDALONE
|
||||
|
||||
if (strcmp(command, "2thermostats") == 0) {
|
||||
EMSESP::logger().info("Testing with multiple thermostats...");
|
||||
@@ -223,41 +242,79 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// These next tests are run from the Console
|
||||
// using the test command
|
||||
// These next tests are run from the Consol via the test command, so inherit the Shell
|
||||
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & data) {
|
||||
// switch to su
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
shell.add_flags(CommandFlags::ADMIN); // switch to su
|
||||
|
||||
// init stuff
|
||||
Mqtt::ha_enabled(true);
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw
|
||||
|
||||
// EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw mode
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_ON); // verbose mode
|
||||
|
||||
std::string command(20, '\0');
|
||||
|
||||
#ifndef EMSESP_DEBUG_LIMITED
|
||||
|
||||
if ((cmd.empty()) || (cmd == "default")) {
|
||||
command = EMSESP_DEBUG_DEFAULT;
|
||||
} else {
|
||||
command = cmd;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE_DUMP)
|
||||
if (command == "dump") {
|
||||
shell.printfln("Adding all devices and entities...");
|
||||
EMSESP::dump_all_values(shell);
|
||||
}
|
||||
#endif
|
||||
bool ok = false;
|
||||
|
||||
if (command == "general") {
|
||||
shell.printfln("Testing adding a general boiler & thermostat...");
|
||||
run_test("general");
|
||||
shell.invoke_command("show devices");
|
||||
shell.invoke_command("show");
|
||||
// shell.invoke_command("show devices");
|
||||
// shell.invoke_command("show values");
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
// shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/869
|
||||
if (command == "memory") {
|
||||
shell.printfln("Testing memory by adding lots of devices and entities...");
|
||||
run_test("memory");
|
||||
shell.invoke_command("show values");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "string2minutes") {
|
||||
shell.printfln("Testing string2minutes()...");
|
||||
std::string time_s = "12:00";
|
||||
shell.printfln("Testing %s is %d", time_s.c_str(), Helpers::string2minutes(time_s));
|
||||
std::string time_s2 = "12:12";
|
||||
shell.printfln("Testing %s is %d", time_s2.c_str(), Helpers::string2minutes(time_s2));
|
||||
std::string time_s3 = "00:50";
|
||||
shell.printfln("Testing %s is %d", time_s3.c_str(), Helpers::string2minutes(time_s3));
|
||||
std::string time_s4 = "03:49";
|
||||
shell.printfln("Testing %s is %d", time_s4.c_str(), Helpers::string2minutes(time_s4));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// THESE ONLY WORK WITH AN ESP32, not in standalone mode
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (command == "ls") {
|
||||
listDir(LittleFS, "/", 3);
|
||||
Serial.println();
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// the tests take a lot of memory when built for the ESP32
|
||||
// so only including the full set in standalone, otherwise a limited selection of basic tests
|
||||
//
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// all tests with EMSESP_STANDALONE
|
||||
|
||||
if (command == "entity_dump") {
|
||||
shell.printfln("Adding all devices and entities...");
|
||||
System::test_set_all_active(true);
|
||||
EMSESP::dump_all_values(shell);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "modes") {
|
||||
@@ -266,6 +323,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call thermostat mode auto");
|
||||
shell.invoke_command("call thermostat mode Manuell"); // DE
|
||||
shell.invoke_command("call thermostat mode 1");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "render") {
|
||||
@@ -334,6 +392,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
temp = 0x63;
|
||||
doub = Helpers::transformNumFloat(temp, 2); // divide by 2
|
||||
shell.printfln("Round test div2 from x%02X to %d to %f", temp, temp, doub);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "devices") {
|
||||
@@ -341,6 +400,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// A fake response - UBADevices(0x07)
|
||||
rx_telegram({0x08, 0x00, 0x07, 0x00, 0x0B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// check for boiler and controller on same product_id
|
||||
@@ -352,6 +412,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// UBAuptime
|
||||
uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "620") {
|
||||
@@ -362,6 +423,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// Version Boiler
|
||||
uart_telegram({0x08, 0x0B, 0x02, 0x00, 0x5F, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// unknown device
|
||||
@@ -380,6 +442,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("show devices");
|
||||
shell.invoke_command("call system report");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "unknown2") {
|
||||
@@ -387,11 +450,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// simulate getting version information back from an unknown device
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x5A, 0x01, 0x02}); // productID is 90 which doesn't exist
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "gateway") {
|
||||
shell.printfln("Testing Gateway...");
|
||||
run_test("gateway");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "310") {
|
||||
@@ -401,6 +466,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "2thermostats") {
|
||||
@@ -408,6 +474,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("2thermostats");
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("show devices");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "web") {
|
||||
@@ -419,8 +486,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
|
||||
DynamicJsonDocument doc(8000); // some absurd high number
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
@@ -455,8 +520,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
Serial.println(" **");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "board_profile") {
|
||||
@@ -466,6 +530,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("set board_profile wemos");
|
||||
shell.invoke_command("exit");
|
||||
shell.invoke_command("call system settings");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "boiler") {
|
||||
@@ -494,6 +559,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler", "{\"cmd\":\"heatingactivated\",\"data\":1}");
|
||||
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "shower_alert") {
|
||||
@@ -503,6 +569,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// device type, command, data
|
||||
Command::call(EMSdevice::DeviceType::BOILER, "wwtapactivated", "false");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "fr120") {
|
||||
@@ -515,6 +582,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("show devices");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "ha") {
|
||||
@@ -537,6 +605,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// shell.invoke_command("call thermostat seltemp"); // sensor.thermostat_hc1_selected_room_temperature
|
||||
// shell.invoke_command("call thermostat entities");
|
||||
// shell.invoke_command("call boiler entities");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "lastcode") {
|
||||
@@ -544,7 +613,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
Mqtt::ha_enabled(false);
|
||||
Mqtt::nested_format(1);
|
||||
Mqtt::send_response(false);
|
||||
// Mqtt::send_response(false);
|
||||
|
||||
run_test("boiler");
|
||||
// run_test("thermostat");
|
||||
@@ -555,6 +624,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
{0x08, 0x0B, 0xC2, 0, 0x08, 0xAC, 00, 0x10, 0x31, 0x48, 0x30, 0x31, 0x15, 0x80, 0x95, 0x0B, 0x0E, 0x10, 0x38, 00, 0x7F, 0xFF, 0xFF, 0xFF});
|
||||
|
||||
// shell.invoke_command("show");
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dv") {
|
||||
@@ -562,34 +633,37 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::nested_format(1);
|
||||
Mqtt::send_response(false);
|
||||
// Mqtt::send_response(false);
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dallas") {
|
||||
shell.printfln("Testing adding Dallas sensor");
|
||||
emsesp::EMSESP::dallassensor_.test();
|
||||
if (command == "temperature") {
|
||||
shell.printfln("Testing adding Temperature sensor");
|
||||
emsesp::EMSESP::temperaturesensor_.test();
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dallas_full") {
|
||||
shell.printfln("Testing adding and changing Dallas sensor");
|
||||
if (command == "temperature_full") {
|
||||
shell.printfln("Testing adding and changing Temperature sensor");
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::nested_format(1);
|
||||
// Mqtt::nested_format(0);
|
||||
|
||||
emsesp::EMSESP::dallassensor_.test();
|
||||
emsesp::EMSESP::temperaturesensor_.test();
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
|
||||
// rename
|
||||
EMSESP::dallassensor_.update("01-0203-0405-0607", "testdallas", 2);
|
||||
EMSESP::temperaturesensor_.update("01-0203-0405-0607", "testtemperature", 2);
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "analog") {
|
||||
@@ -609,6 +683,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::analogsensor_.update(36, "analogtest", 2, 0.7, 17, 1);
|
||||
shell.invoke_command("show");
|
||||
// shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "healthcheck") {
|
||||
@@ -621,13 +696,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// n=2 = EMSESP::system_.HEALTHCHECK_NO_NETWORK
|
||||
shell.printfln("Testing healthcheck with %d", n);
|
||||
EMSESP::system_.healthcheck(n);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "custom") {
|
||||
shell.printfln(F("Testing custom entities"));
|
||||
shell.printfln("Testing custom entities");
|
||||
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::send_response(false);
|
||||
// Mqtt::send_response(false);
|
||||
|
||||
run_test("thermostat");
|
||||
|
||||
@@ -650,14 +726,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call thermostat seltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "masked") {
|
||||
shell.printfln("Testing masked entities");
|
||||
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::send_response(false);
|
||||
// Mqtt::send_response(false);
|
||||
|
||||
run_test("boiler");
|
||||
|
||||
@@ -675,13 +752,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "dv2") {
|
||||
shell.printfln("Testing device value lost");
|
||||
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::send_response(false);
|
||||
// Mqtt::send_response(false);
|
||||
|
||||
run_test("boiler");
|
||||
|
||||
@@ -695,14 +773,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "api_values") {
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
shell.printfln("Testing API getting values");
|
||||
Mqtt::ha_enabled(false);
|
||||
Mqtt::nested_format(1);
|
||||
Mqtt::send_response(false);
|
||||
// Mqtt::send_response(false);
|
||||
// EMSESP::bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR
|
||||
EMSESP::system_.bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
|
||||
|
||||
@@ -728,14 +806,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
request.url("/api/boiler/flamecurr/bad");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
#endif
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt_post") {
|
||||
shell.printfln("Testing MQTT incoming changes");
|
||||
Mqtt::ha_enabled(false);
|
||||
Mqtt::nested_format(1);
|
||||
Mqtt::send_response(false);
|
||||
// Mqtt::send_response(false);
|
||||
EMSESP::system_.bool_format(BOOL_FORMAT_10); // BOOL_FORMAT_10_STR
|
||||
// EMSESP::bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
|
||||
|
||||
@@ -743,9 +821,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("thermostat");
|
||||
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/541
|
||||
if (command == "api_wwmode") {
|
||||
shell.printfln("Testing API wwmode");
|
||||
@@ -763,8 +841,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/wwmode");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
ok = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (command == "api") {
|
||||
shell.printfln("Testing API with MQTT and REST, standalone");
|
||||
@@ -773,13 +851,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// Mqtt::ha_enabled(false);
|
||||
|
||||
Mqtt::nested_format(1);
|
||||
Mqtt::send_response(true);
|
||||
// Mqtt::send_response(true);
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
|
||||
AsyncWebServerRequest requestX;
|
||||
DynamicJsonDocument docX(2000);
|
||||
JsonVariant jsonX;
|
||||
@@ -799,7 +875,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/thermostat/mode/auto");
|
||||
requestX.url("/api/thermostat/mode/auto");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
return;
|
||||
*/
|
||||
@@ -847,10 +923,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
*/
|
||||
|
||||
/*
|
||||
requestX.url("/api/dallassensor/xxxx");
|
||||
requestX.url("/api/temperaturesensor/xxxx");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
emsesp::EMSESP::logger().notice("****");
|
||||
requestX.url("/api/dallassensor/info");
|
||||
requestX.url("/api/temperaturesensor/info");
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
return;
|
||||
*/
|
||||
@@ -871,7 +947,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
return;
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
// char dataX[] = "{\"value\":\"0B 88 19 19 02\"}";
|
||||
char dataX[] = "{\"name\":\"temp\",\"value\":11}";
|
||||
@@ -917,8 +992,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
ncmd = Command::parse_command_string(command_s, id_n);
|
||||
shell.printfln("test cmd parse cmd=%s id=%d", ncmd, id_n);
|
||||
|
||||
#endif
|
||||
|
||||
// Console tests
|
||||
shell.invoke_command("call thermostat entities");
|
||||
shell.invoke_command("call thermostat mode auto");
|
||||
@@ -928,14 +1001,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/hc2/mode", "auto");
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/wwc3/mode", "auto");
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/wwcircpump", "off");
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/seltemp"); // empty payload, sends reponse
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/seltemp"); // empty payload
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc1", "22"); // HA only
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc1", "off"); // HA only
|
||||
EMSESP::mqtt_.incoming("ems-esp/system/send", "11 12 13");
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/syspress"); // empty payload, sends reponse
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode"); // empty payload, sends reponse
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/syspress"); // empty payload
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/mode"); // empty payload
|
||||
EMSESP::mqtt_.incoming("ems-esp/system/publish");
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/seltemp"); // empty payload, sends reponse
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat/seltemp"); // empty payload
|
||||
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59");
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp");
|
||||
@@ -949,9 +1022,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// check extended MQTT base
|
||||
Mqtt::base("home/cellar/heating");
|
||||
EMSESP::mqtt_.incoming("home/cellar/heating/thermostat/mode"); // empty payload, sends reponse
|
||||
EMSESP::mqtt_.incoming("home/cellar/heating/thermostat/mode"); // empty payload
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// Web API TESTS
|
||||
AsyncWebServerRequest request;
|
||||
|
||||
@@ -1019,15 +1091,15 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
char data6[] = "{\"id\":2,\"devicevalue\":{\"v\":\"44\",\"u\":1,\"n\":\"hc2 selected room temperature\",\"c\":\"hc2/seltemp\"}";
|
||||
deserializeJson(doc, data6);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/rest/writeValue");
|
||||
EMSESP::webDataService.write_value(&request, json);
|
||||
request.url("/rest/writeDeviceValue");
|
||||
EMSESP::webDataService.write_device_value(&request, json);
|
||||
|
||||
// write value from web - testing hc9/seltemp - should fail!
|
||||
char data7[] = "{\"id\":2,\"devicevalue\":{\"v\":\"55\",\"u\":1,\"n\":\"hc2 selected room temperature\",\"c\":\"hc9/seltemp\"}";
|
||||
deserializeJson(doc, data7);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/rest/writeValue");
|
||||
EMSESP::webDataService.write_value(&request, json);
|
||||
request.url("/rest/writeDeviceValue");
|
||||
EMSESP::webDataService.write_device_value(&request, json);
|
||||
|
||||
// emsesp::EMSESP::logger().notice("*");
|
||||
|
||||
@@ -1037,8 +1109,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/mode/auto");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
|
||||
#endif
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt_nested") {
|
||||
@@ -1059,6 +1130,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
Mqtt::nested_format(2);
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "thermostat") {
|
||||
@@ -1075,6 +1147,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat_hc3", "{\"cmd\":\"temp\",\"data\":-3}");
|
||||
|
||||
shell.invoke_command("show mqtt");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "tc100") {
|
||||
@@ -1086,6 +1159,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// 0x0A
|
||||
uart_telegram({0x98, 0x0B, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "solar") {
|
||||
@@ -1099,6 +1173,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call system publish");
|
||||
|
||||
// EMSESP::txservice_.send_raw("B0 00 FF 18 02 62 80 00 B8");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "heatpump") {
|
||||
@@ -1106,6 +1181,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
run_test("heatpump");
|
||||
shell.invoke_command("call");
|
||||
shell.invoke_command("call heatpump info");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "solar200") {
|
||||
@@ -1128,9 +1204,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
uart_telegram("30 00 FF 0A 02 6A 04"); // SM100 pump on 1
|
||||
uart_telegram("30 00 FF 00 02 64 00 00 00 04 00 00 FF 00 00 1E 0B 09 64 00 00 00 00"); // SM100 modulation
|
||||
|
||||
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
|
||||
uart_telegram("30 00 FF 0A 02 6A 03"); // SM100 pump off 0
|
||||
|
||||
shell.invoke_command("show");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "km") {
|
||||
@@ -1189,6 +1266,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
EMSESP::show_ems(shell);
|
||||
EMSESP::show_device_values(shell);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "cr100") {
|
||||
@@ -1214,12 +1292,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.loop_all();
|
||||
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "rx2") {
|
||||
shell.printfln("Testing Rx2...");
|
||||
for (uint8_t i = 0; i < 30; i++) {
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1278,6 +1358,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// test 0x2A - DHWStatus3
|
||||
uart_telegram({0x88, 00, 0x2A, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xD2, 00, 00, 0x80, 00, 00, 01, 0x9D, 0x80, 0x00, 0x02, 0x79, 00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "tx") {
|
||||
@@ -1315,6 +1396,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
for (uint8_t i = 0; i < 10; i++) {
|
||||
EMSESP::txservice_.send(); // send it to UART
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "poll") {
|
||||
@@ -1340,6 +1423,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
uint8_t t2[] = {0x21, 0x22};
|
||||
EMSESP::send_write_request(0x91, 0x17, 0x00, t2, sizeof(t2), 0);
|
||||
EMSESP::show_ems(shell);
|
||||
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "cmd") {
|
||||
@@ -1361,7 +1446,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call");
|
||||
shell.invoke_command("call system info");
|
||||
|
||||
EMSESP::mqtt_.incoming("ems-esp/system", "{\"cmd\":\"info\"}"); // this should fail
|
||||
EMSESP::mqtt_.incoming("ems-esp/system", "{\"cmd\":\"info\"}"); // this should fail
|
||||
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat", "{\"cmd\":\"temp\",\"data\":23.45}"); // this should work just fine
|
||||
EMSESP::mqtt_.incoming("ems-esp/thermostat", "{\"cmd\":\"TeMP\",\"data\":23.45}"); // test mix cased cmd
|
||||
@@ -1371,18 +1456,20 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call thermostat wwmode"); // should do nothing
|
||||
shell.invoke_command("call thermostat mode auto 2"); // should error, no hc2
|
||||
shell.invoke_command("call thermostat temp 22.56");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "pin") {
|
||||
shell.printfln("Testing pin...");
|
||||
shell.invoke_command("call system pin");
|
||||
shell.invoke_command("call system pin 1 true");
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt2") {
|
||||
shell.printfln("Testing MQTT large payloads...");
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXXLARGE);
|
||||
|
||||
char key[8];
|
||||
char value[8];
|
||||
@@ -1398,8 +1485,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.printfln("Size of JSON payload = %d", jo.memoryUsage());
|
||||
shell.printfln("Length of JSON payload = %d", measureJson(jo));
|
||||
|
||||
Mqtt::publish("test", jo);
|
||||
Mqtt::queue_publish("test", jo);
|
||||
Mqtt::show_mqtt(shell); // show queue
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mqtt") {
|
||||
@@ -1448,7 +1536,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
strlcpy(system_topic, "ems-esp/system", sizeof(system_topic));
|
||||
|
||||
// test publishing
|
||||
EMSESP::mqtt_.publish(boiler_topic, "test me");
|
||||
EMSESP::mqtt_.queue_publish(boiler_topic, "test me");
|
||||
|
||||
// test receiving
|
||||
EMSESP::mqtt_.incoming(boiler_topic, ""); // test if ignore empty payloads, should return values
|
||||
@@ -1471,7 +1559,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
// EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"pin\",\"id\":12,\"data\":\"1\"}");
|
||||
|
||||
EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"wwmode\",\"data\":\"auto\"}");
|
||||
EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"typo\",\"id\":2}"); // invalid mode
|
||||
EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"typo\",\"id\":2}"); // invalid mode
|
||||
EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"auto\",\"id\":2}");
|
||||
EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"mode\",\"data\":\"auto\",\"hc\":2}"); // hc as number
|
||||
EMSESP::mqtt_.incoming(thermostat_topic, "{\"cmd\":\"seltemp\",\"data\":19.5,\"hc\":1}"); // data as number
|
||||
@@ -1494,6 +1582,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
Mqtt::resubscribe();
|
||||
Mqtt::show_mqtt(shell); // show queue
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "poll2") {
|
||||
@@ -1505,6 +1594,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::incoming_telegram(poll, 1);
|
||||
|
||||
EMSESP::show_ems(shell);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "rx2") {
|
||||
@@ -1512,6 +1602,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
uart_telegram({0x1B, 0x5B, 0xFD, 0x2D, 0x9E, 0x3A, 0xB6, 0xE5, 0x02, 0x20, 0x33, 0x30, 0x32, 0x3A, 0x20, 0x5B,
|
||||
0x73, 0xFF, 0xFF, 0xCB, 0xDF, 0xB7, 0xA7, 0xB5, 0x67, 0x77, 0x77, 0xE4, 0xFF, 0xFD, 0x77, 0xFF});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// https://github.com/emsesp/EMS-ESP/issues/380#issuecomment-633663007
|
||||
@@ -1519,6 +1610,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.printfln("Testing rx3...");
|
||||
|
||||
uart_telegram({0x21, 0x0B, 0xFF, 0x00});
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// testing the UART tx command, without a queue
|
||||
@@ -1527,6 +1619,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
uint8_t t[] = {0x0B, 0x88, 0x18, 0x00, 0x20, 0xD4}; // including CRC
|
||||
EMSuart::transmit(t, sizeof(t));
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// send read request with offset
|
||||
@@ -1535,6 +1628,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
|
||||
// send_read_request(0x18, 0x08);
|
||||
EMSESP::txservice_.read_request(0x18, 0x08, 27); // no offset
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "mixer") {
|
||||
@@ -1550,11 +1644,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
|
||||
// shell.invoke_command("call mixer wwc1 info");
|
||||
// shell.invoke_command("call mixer wwc2 info");
|
||||
// shell.invoke_command("call mixer wwc1 info");
|
||||
// shell.invoke_command("call mixer wwc2 info");
|
||||
|
||||
// test API
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// test API
|
||||
AsyncWebServerRequest request;
|
||||
request.url("/api/mixer");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
@@ -1562,30 +1655,35 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
request.url("/api/mixer/wwc2/pumpstatus");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
#endif
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (command == "crash") {
|
||||
shell.printfln("Forcing a crash...");
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdiv-by-zero"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
uint8_t a = 2 / 0;
|
||||
shell.printfln("Testing %s", a);
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
ok = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (command == "limited") {
|
||||
shell.printfln("Run a limited memory test...");
|
||||
|
||||
run_test("general");
|
||||
if (!ok) {
|
||||
shell.printfln("Unknown test command: %s", command.c_str());
|
||||
EMSESP::logger().notice("Unknown test command: %s", command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// loop console. simulates what EMSESP::loop() does
|
||||
void Test::refresh() {
|
||||
uuid::loop();
|
||||
EMSESP::rxservice_.loop();
|
||||
EMSESP::mqtt_.loop();
|
||||
Shell::loop_all();
|
||||
}
|
||||
|
||||
// simulates a telegram in the Rx queue, but without the CRC which is added automatically
|
||||
void Test::rx_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
uint8_t len = rx_data.size();
|
||||
@@ -1598,9 +1696,7 @@ void Test::rx_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::rxservice_.add(data, len + 1);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// simulates a telegram straight from UART, but without the CRC which is added automatically
|
||||
@@ -1615,9 +1711,7 @@ void Test::uart_telegram(const std::vector<uint8_t> & rx_data) {
|
||||
data[i] = EMSESP::rxservice_.calculate_crc(data, i);
|
||||
EMSESP::incoming_telegram(data, i + 1);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// takes raw string, assuming it contains the CRC. This is what is output from 'watch raw'
|
||||
@@ -1656,9 +1750,7 @@ void Test::uart_telegram_withCRC(const char * rx_data) {
|
||||
|
||||
EMSESP::incoming_telegram(data, count + 1);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// takes raw string, adds CRC to end
|
||||
@@ -1699,17 +1791,14 @@ void Test::uart_telegram(const char * rx_data) {
|
||||
|
||||
EMSESP::incoming_telegram(data, count + 2);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESP::loop();
|
||||
#endif
|
||||
refresh();
|
||||
}
|
||||
|
||||
// Sends version telegram. Version is hardcoded to 1.0
|
||||
void Test::add_device(uint8_t device_id, uint8_t product_id) {
|
||||
// Send version: 09 0B 02 00 PP V1 V2
|
||||
uart_telegram({device_id, EMSESP_DEFAULT_EMS_BUS_ID, EMSdevice::EMS_TYPE_VERSION, 0, product_id, 1, 0});
|
||||
}
|
||||
|
||||
#ifdef EMSESP_TEST
|
||||
#ifndef EMSESP_STANDALONE
|
||||
void Test::listDir(fs::FS & fs, const char * dirname, uint8_t levels) {
|
||||
Serial.println();
|
||||
@@ -1744,24 +1833,7 @@ void Test::listDir(fs::FS & fs, const char * dirname, uint8_t levels) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Test::debug(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
// shell.add_flags(CommandFlags::ADMIN); // switch to su
|
||||
|
||||
std::string command(20, '\0');
|
||||
if ((cmd.empty()) || (cmd == "default")) {
|
||||
command = "ls";
|
||||
} else {
|
||||
command = cmd;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (command == "ls") {
|
||||
listDir(LittleFS, "/", 3);
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||
|
||||
#ifndef EMSESP_TEST_H
|
||||
#define EMSESP_TEST_H
|
||||
@@ -28,10 +28,9 @@ namespace emsesp {
|
||||
|
||||
// #define EMSESP_DEBUG_DEFAULT "thermostat"
|
||||
// #define EMSESP_DEBUG_DEFAULT "solar"
|
||||
#define EMSESP_DEBUG_DEFAULT "mixer"
|
||||
// #define EMSESP_DEBUG_DEFAULT "web"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt"
|
||||
// #define EMSESP_DEBUG_DEFAULT "general"
|
||||
#define EMSESP_DEBUG_DEFAULT "general"
|
||||
// #define EMSESP_DEBUG_DEFAULT "boiler"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt2"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt_nested"
|
||||
@@ -46,13 +45,14 @@ namespace emsesp {
|
||||
// #define EMSESP_DEBUG_DEFAULT "dv"
|
||||
// #define EMSESP_DEBUG_DEFAULT "lastcode"
|
||||
// #define EMSESP_DEBUG_DEFAULT "2thermostats"
|
||||
// #define EMSESP_DEBUG_DEFAULT "dallas"
|
||||
// #define EMSESP_DEBUG_DEFAULT "temperature"
|
||||
// #define EMSESP_DEBUG_DEFAULT "analog"
|
||||
// #define EMSESP_DEBUG_DEFAULT "api_values"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt_post"
|
||||
// #define EMSESP_DEBUG_DEFAULT "api_wwmode"
|
||||
// #define EMSESP_DEBUG_DEFAULT "custom"
|
||||
// #define EMSESP_DEBUG_DEFAULT "dump"
|
||||
// #define EMSESP_DEBUG_DEFAULT "entity_dump"
|
||||
// #define EMSESP_DEBUG_DEFAULT "memory"
|
||||
|
||||
class Test {
|
||||
public:
|
||||
@@ -64,10 +64,8 @@ class Test {
|
||||
static void uart_telegram(const char * rx_data);
|
||||
static void uart_telegram_withCRC(const char * rx_data);
|
||||
static void add_device(uint8_t device_id, uint8_t product_id);
|
||||
static void debug(uuid::console::Shell & shell, const std::string & command);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static void refresh();
|
||||
static void listDir(fs::FS & fs, const char * dirname, uint8_t levels);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -86,10 +86,10 @@ void EMSuart::start(const uint8_t tx_mode, const uint8_t rx_gpio, const uint8_t
|
||||
uart_set_pin(EMSUART_NUM, tx_gpio, rx_gpio, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
uart_driver_install(EMSUART_NUM, 129, 0, (EMS_MAXBUFFERSIZE + 1) * 2, &uart_queue, 0); // buffer must be > fifo
|
||||
uart_set_rx_full_threshold(EMSUART_NUM, 1);
|
||||
uart_set_rx_timeout(EMSUART_NUM, 0); // disable
|
||||
uart_set_rx_timeout(EMSUART_NUM, 0); // disable
|
||||
|
||||
// note setting the static max buffer to 1024 causes OTA to fail
|
||||
xTaskCreate(uart_event_task, "uart_event_task", 2048, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
// note esp32s3 crashes with 2k stacksize, stack overflow here sometimes wipes settingsfiles.
|
||||
xTaskCreate(uart_event_task, "uart_event_task", 2560, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
}
|
||||
tx_mode_ = tx_mode;
|
||||
uart_enable_intr_mask(EMSUART_NUM, UART_BRK_DET_INT_ENA | UART_RXFIFO_FULL_INT_ENA);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,7 +24,7 @@
|
||||
#ifndef EMSESP_EMSUART_H
|
||||
#define EMSESP_EMSUART_H
|
||||
|
||||
#define EMS_MAXBUFFERSIZE 33 // max size of the buffer. EMS packets are max 32 bytes, plus extra for BRK
|
||||
#define EMS_MAXBUFFERSIZE 33 // max size of the buffer. EMS packets are max 32 bytes, plus extra for BRK
|
||||
|
||||
#define EMSUART_NUM UART_NUM_1 // on C3 and S2 there is no UART2, use UART1 for all
|
||||
#define EMSUART_BAUD 9600 // uart baud rate for the EMS circuit
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.5.1"
|
||||
#define EMSESP_APP_VERSION "3.6.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -36,6 +36,8 @@ WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * security
|
||||
server->on(GET_CUSTOMIZATIONS_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebAPIService::getCustomizations, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(GET_SCHEDULE_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getSchedule, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(GET_ENTITIES_PATH, HTTP_GET, securityManager->wrapRequest(std::bind(&WebAPIService::getEntities, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
// HTTP GET
|
||||
@@ -100,8 +102,11 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
}
|
||||
}
|
||||
|
||||
// capture current heap memory before allocating the large return buffer
|
||||
emsesp::EMSESP::system_.refreshHeapMem();
|
||||
|
||||
// output json buffer
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXLARGE_DYN;
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXLARGE;
|
||||
auto * response = new PrettyAsyncJsonResponse(false, buffer);
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
@@ -123,7 +128,6 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
emsesp::EMSESP::logger().err(error);
|
||||
api_fails_++;
|
||||
} else {
|
||||
// emsesp::EMSESP::logger().debug("API command called successfully");
|
||||
// if there was no json output from the call, default to the output message 'OK'.
|
||||
if (!output.size()) {
|
||||
output["message"] = "OK";
|
||||
@@ -194,4 +198,28 @@ void WebAPIService::getCustomizations(AsyncWebServerRequest * request) {
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
void WebAPIService::getSchedule(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, FS_BUFFER_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["type"] = "schedule";
|
||||
|
||||
System::extractSettings(EMSESP_SCHEDULER_FILE, "Schedule", root);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
void WebAPIService::getEntities(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, FS_BUFFER_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["type"] = "entities";
|
||||
|
||||
System::extractSettings(EMSESP_ENTITY_FILE, "Entities", root);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,6 +22,8 @@
|
||||
#define EMSESP_API_SERVICE_PATH "/api"
|
||||
#define GET_SETTINGS_PATH "/rest/getSettings"
|
||||
#define GET_CUSTOMIZATIONS_PATH "/rest/getCustomizations"
|
||||
#define GET_SCHEDULE_PATH "/rest/getSchedule"
|
||||
#define GET_ENTITIES_PATH "/rest/getEntities"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -51,6 +53,8 @@ class WebAPIService {
|
||||
|
||||
void getSettings(AsyncWebServerRequest * request);
|
||||
void getCustomizations(AsyncWebServerRequest * request);
|
||||
void getSchedule(AsyncWebServerRequest * request);
|
||||
void getEntities(AsyncWebServerRequest * request);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -33,10 +33,12 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f
|
||||
, _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
|
||||
, _masked_entities_handler(CUSTOM_ENTITIES_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebCustomizationService::custom_entities, this, _1, _2),
|
||||
AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _device_entities_handler(DEVICE_ENTITIES_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebCustomizationService::device_entities, this, _1, _2),
|
||||
AuthenticationPredicates::IS_AUTHENTICATED)) {
|
||||
server->on(DEVICE_ENTITIES_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebCustomizationService::device_entities, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
|
||||
server->on(DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebCustomizationService::devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
@@ -49,25 +51,21 @@ WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * f
|
||||
_masked_entities_handler.setMaxContentLength(2048);
|
||||
_masked_entities_handler.setMaxJsonBufferSize(2048);
|
||||
server->addHandler(&_masked_entities_handler);
|
||||
|
||||
_device_entities_handler.setMethod(HTTP_POST);
|
||||
_device_entities_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_entities_handler);
|
||||
}
|
||||
|
||||
// this creates the customization file, saving it to the FS
|
||||
void WebCustomization::read(WebCustomization & settings, JsonObject & root) {
|
||||
// Dallas Sensor customization
|
||||
JsonArray sensorsJson = root.createNestedArray("sensors");
|
||||
// Temperature Sensor customization
|
||||
JsonArray sensorsJson = root.createNestedArray("ts");
|
||||
for (const SensorCustomization & sensor : settings.sensorCustomizations) {
|
||||
JsonObject sensorJson = sensorsJson.createNestedObject();
|
||||
sensorJson["id"] = sensor.id; // is
|
||||
sensorJson["id"] = sensor.id; // ID of chip
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
}
|
||||
|
||||
// Analog Sensor customization
|
||||
JsonArray analogJson = root.createNestedArray("analogs");
|
||||
JsonArray analogJson = root.createNestedArray("as");
|
||||
for (const AnalogCustomization & sensor : settings.analogCustomizations) {
|
||||
JsonObject sensorJson = analogJson.createNestedObject();
|
||||
sensorJson["gpio"] = sensor.gpio; // g
|
||||
@@ -98,7 +96,7 @@ void WebCustomization::read(WebCustomization & settings, JsonObject & root) {
|
||||
StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & settings) {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// invoke some fake data for testing
|
||||
const char * json = "{\"sensors\":[],\"analogs\":[],\"masked_entities\":[{\"product_id\":123,\"device_id\":8,\"entity_ids\":[\"08heatingactive|my custom "
|
||||
const char * json = "{\"ts\":[],\"as\":[],\"masked_entities\":[{\"product_id\":123,\"device_id\":8,\"entity_ids\":[\"08heatingactive|my custom "
|
||||
"name for heating active\",\"08tapwateractive\"]}]}";
|
||||
|
||||
StaticJsonDocument<500> doc;
|
||||
@@ -111,10 +109,10 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
|
||||
Serial.println(COLOR_RESET);
|
||||
#endif
|
||||
|
||||
// Dallas Sensor customization
|
||||
// Temperature Sensor customization
|
||||
settings.sensorCustomizations.clear();
|
||||
if (root["sensors"].is<JsonArray>()) {
|
||||
for (const JsonObject sensorJson : root["sensors"].as<JsonArray>()) {
|
||||
if (root["ts"].is<JsonArray>()) {
|
||||
for (const JsonObject sensorJson : root["ts"].as<JsonArray>()) {
|
||||
// create each of the sensor, overwriting any previous settings
|
||||
auto sensor = SensorCustomization();
|
||||
sensor.id = sensorJson["id"].as<std::string>();
|
||||
@@ -126,8 +124,8 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
|
||||
|
||||
// Analog Sensor customization
|
||||
settings.analogCustomizations.clear();
|
||||
if (root["analogs"].is<JsonArray>()) {
|
||||
for (const JsonObject analogJson : root["analogs"].as<JsonArray>()) {
|
||||
if (root["as"].is<JsonArray>()) {
|
||||
for (const JsonObject analogJson : root["as"].as<JsonArray>()) {
|
||||
// create each of the sensor, overwriting any previous settings
|
||||
auto sensor = AnalogCustomization();
|
||||
sensor.gpio = analogJson["gpio"];
|
||||
@@ -165,20 +163,20 @@ StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization &
|
||||
void WebCustomizationService::reset_customization(AsyncWebServerRequest * request) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (LittleFS.remove(EMSESP_CUSTOMIZATION_FILE)) {
|
||||
AsyncWebServerResponse * response = request->beginResponse(200); // OK
|
||||
AsyncWebServerResponse * response = request->beginResponse(205); // restart needed
|
||||
request->send(response);
|
||||
EMSESP::system_.restart_requested(true);
|
||||
return;
|
||||
}
|
||||
// failed
|
||||
AsyncWebServerResponse * response = request->beginResponse(204); // no content error
|
||||
AsyncWebServerResponse * response = request->beginResponse(400); // bad request
|
||||
request->send(response);
|
||||
#endif
|
||||
}
|
||||
|
||||
// send back a list of devices used in the customization web page
|
||||
void WebCustomizationService::devices(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
@@ -199,17 +197,21 @@ void WebCustomizationService::devices(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
// send back list of device entities
|
||||
void WebCustomizationService::device_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXLARGE_DYN;
|
||||
void WebCustomizationService::device_entities(AsyncWebServerRequest * request) {
|
||||
uint8_t id;
|
||||
if (request->hasParam(F_(id))) {
|
||||
id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
|
||||
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
|
||||
auto * response = new MsgpackAsyncJsonResponse(true, buffer);
|
||||
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
buffer -= 1024;
|
||||
response = new MsgpackAsyncJsonResponse(true, buffer);
|
||||
}
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->unique_id() == json["id"]) {
|
||||
if (emsdevice->unique_id() == id) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
JsonArray output = response->getRoot();
|
||||
emsdevice->generate_values_web_customization(output);
|
||||
@@ -319,7 +321,7 @@ void WebCustomizationService::custom_entities(AsyncWebServerRequest * request, J
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(need_reboot ? 201 : 200); // OK
|
||||
AsyncWebServerResponse * response = request->beginResponse(need_reboot ? 205 : 200); // reboot or just OK
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
@@ -328,4 +330,4 @@ void WebCustomizationService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,15 +24,15 @@
|
||||
// GET
|
||||
#define DEVICES_SERVICE_PATH "/rest/devices"
|
||||
#define EMSESP_CUSTOMIZATION_SERVICE_PATH "/rest/customization"
|
||||
#define DEVICE_ENTITIES_PATH "/rest/deviceEntities"
|
||||
|
||||
// POST
|
||||
#define DEVICE_ENTITIES_PATH "/rest/deviceEntities"
|
||||
#define CUSTOM_ENTITIES_PATH "/rest/customEntities"
|
||||
#define RESET_CUSTOMIZATION_SERVICE_PATH "/rest/resetCustomizations"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// Customization for dallas sensor
|
||||
// Customization for temperature sensor
|
||||
class SensorCustomization {
|
||||
public:
|
||||
std::string id;
|
||||
@@ -91,13 +91,13 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
|
||||
|
||||
// GET
|
||||
void devices(AsyncWebServerRequest * request);
|
||||
void device_entities(AsyncWebServerRequest * request);
|
||||
|
||||
// POST
|
||||
void custom_entities(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void device_entities(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void reset_customization(AsyncWebServerRequest * request);
|
||||
void reset_customization(AsyncWebServerRequest * request); // command
|
||||
|
||||
AsyncCallbackJsonWebHandler _masked_entities_handler, _device_entities_handler;
|
||||
AsyncCallbackJsonWebHandler _masked_entities_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
@@ -23,14 +23,18 @@ namespace emsesp {
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _device_data_handler(DEVICE_DATA_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _write_value_handler(WRITE_VALUE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_sensor_handler(WRITE_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_analog_handler(WRITE_ANALOG_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_analog, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
: _write_value_handler(WRITE_DEVICE_VALUE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_device_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_temperature_handler(WRITE_TEMPERATURE_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_temperature_sensor, this, _1, _2),
|
||||
AuthenticationPredicates::IS_ADMIN))
|
||||
, _write_analog_handler(WRITE_ANALOG_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_analog_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
// GET's
|
||||
server->on(DEVICE_DATA_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::device_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
server->on(CORE_DATA_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::core_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
@@ -39,21 +43,18 @@ WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securi
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::sensor_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
// POST's
|
||||
server->on(SCAN_DEVICES_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
_device_data_handler.setMethod(HTTP_POST);
|
||||
_device_data_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_data_handler);
|
||||
|
||||
_write_value_handler.setMethod(HTTP_POST);
|
||||
_write_value_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_value_handler);
|
||||
|
||||
_write_sensor_handler.setMethod(HTTP_POST);
|
||||
_write_sensor_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_sensor_handler);
|
||||
_write_temperature_handler.setMethod(HTTP_POST);
|
||||
_write_temperature_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_temperature_handler);
|
||||
|
||||
_write_analog_handler.setMethod(HTTP_POST);
|
||||
_write_analog_handler.setMaxContentLength(256);
|
||||
@@ -70,33 +71,40 @@ void WebDataService::scan_devices(AsyncWebServerRequest * request) {
|
||||
// this is used in the dashboard and contains all ems device information
|
||||
// /coreData endpoint
|
||||
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
char buffer[3];
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
// ignore controller
|
||||
if (emsdevice && (emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER || emsdevice->count_entities() > 0)) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
obj["id"] = Helpers::smallitoa(buffer, emsdevice->unique_id()); // a unique id as a string
|
||||
obj["tn"] = emsdevice->device_type_2_device_name_translated(); // translated device type name
|
||||
obj["t"] = emsdevice->device_type(); // device type number
|
||||
obj["b"] = emsdevice->brand_to_string(); // brand
|
||||
obj["n"] = emsdevice->name(); // name
|
||||
obj["d"] = emsdevice->device_id(); // deviceid
|
||||
obj["p"] = emsdevice->product_id(); // productid
|
||||
obj["v"] = emsdevice->version(); // version
|
||||
obj["e"] = emsdevice->count_entities(); // number of entities (device values)
|
||||
obj["id"] = emsdevice->unique_id(); // a unique id
|
||||
obj["tn"] = emsdevice->device_type_2_device_name_translated(); // translated device type name
|
||||
obj["t"] = emsdevice->device_type(); // device type number
|
||||
obj["b"] = emsdevice->brand_to_char(); // brand
|
||||
obj["n"] = emsdevice->name(); // name
|
||||
obj["d"] = emsdevice->device_id(); // deviceid
|
||||
obj["p"] = emsdevice->product_id(); // productid
|
||||
obj["v"] = emsdevice->version(); // version
|
||||
}
|
||||
}
|
||||
|
||||
// sensors stuff
|
||||
root["s_n"] = Helpers::translated_word(FL_(sensors_device));
|
||||
root["active_sensors"] = EMSESP::dallassensor_.no_sensors() + (EMSESP::analogsensor_.analog_enabled() ? EMSESP::analogsensor_.no_sensors() : 0);
|
||||
root["analog_enabled"] = EMSESP::analogsensor_.analog_enabled();
|
||||
root["connected"] = EMSESP::bus_status() != 2;
|
||||
// add any custom entities
|
||||
if (EMSESP::webEntityService.count_entities()) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
obj["id"] = 99; // the last unique id
|
||||
obj["tn"] = Helpers::translated_word(FL_(custom_device)); // translated device type name
|
||||
obj["t"] = EMSdevice::DeviceType::CUSTOM; // device type number
|
||||
obj["b"] = Helpers::translated_word(FL_(na)); // brand
|
||||
obj["n"] = Helpers::translated_word(FL_(custom_device_name)); // name
|
||||
obj["d"] = 0; // deviceid
|
||||
obj["p"] = 0; // productid
|
||||
obj["v"] = 0; // version
|
||||
}
|
||||
|
||||
root["connected"] = EMSESP::bus_status() != 2;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
@@ -104,15 +112,14 @@ void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
|
||||
// sensor data - sends back to web
|
||||
// /sensorData endpoint
|
||||
// the "sensors" and "analogs" are arrays and must exist
|
||||
void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// dallas sensors
|
||||
JsonArray sensors = root.createNestedArray("sensors");
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
for (const auto & sensor : EMSESP::dallassensor_.sensors()) {
|
||||
// temperature sensors
|
||||
JsonArray sensors = root.createNestedArray("ts");
|
||||
if (EMSESP::temperaturesensor_.have_sensors()) {
|
||||
for (const auto & sensor : EMSESP::temperaturesensor_.sensors()) {
|
||||
JsonObject obj = sensors.createNestedObject();
|
||||
obj["id"] = sensor.id(); // id as string
|
||||
obj["n"] = sensor.name(); // name
|
||||
@@ -133,49 +140,52 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
// analog sensors
|
||||
JsonArray analogs = root.createNestedArray("analogs");
|
||||
JsonArray analogs = root.createNestedArray("as");
|
||||
if (EMSESP::analog_enabled() && EMSESP::analogsensor_.have_sensors()) {
|
||||
uint8_t count = 0;
|
||||
char buffer[3];
|
||||
for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
|
||||
// don't send if it's marked for removal
|
||||
if (sensor.type() != AnalogSensor::AnalogType::MARK_DELETED) {
|
||||
count++;
|
||||
JsonObject obj = analogs.createNestedObject();
|
||||
obj["id"] = Helpers::smallitoa(buffer, count); // needed for sorting table
|
||||
obj["g"] = sensor.gpio();
|
||||
obj["n"] = sensor.name();
|
||||
obj["u"] = sensor.uom();
|
||||
obj["o"] = sensor.offset();
|
||||
obj["f"] = sensor.factor();
|
||||
obj["t"] = sensor.type();
|
||||
JsonObject obj = analogs.createNestedObject();
|
||||
obj["id"] = ++count; // needed for sorting table
|
||||
obj["g"] = sensor.gpio();
|
||||
obj["n"] = sensor.name();
|
||||
obj["u"] = sensor.uom();
|
||||
obj["o"] = sensor.offset();
|
||||
obj["f"] = sensor.factor();
|
||||
obj["t"] = sensor.type();
|
||||
|
||||
if (sensor.type() != AnalogSensor::AnalogType::NOTUSED) {
|
||||
obj["v"] = Helpers::transformNumFloat(sensor.value(), 0); // is optional and is a float
|
||||
} else {
|
||||
obj["v"] = 0; // must have a value for web sorting to work
|
||||
}
|
||||
if (sensor.type() != AnalogSensor::AnalogType::NOTUSED) {
|
||||
obj["v"] = Helpers::transformNumFloat(sensor.value(), 0); // is optional and is a float
|
||||
} else {
|
||||
obj["v"] = 0; // must have a value for web sorting to work
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root["analog_enabled"] = EMSESP::analogsensor_.analog_enabled();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// The unique_id is the unique record ID from the Web table to identify which device to load
|
||||
// Compresses the JSON using MsgPack https://msgpack.org/index.html
|
||||
void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXLARGE_DYN;
|
||||
void WebDataService::device_data(AsyncWebServerRequest * request) {
|
||||
uint8_t id;
|
||||
if (request->hasParam(F_(id))) {
|
||||
id = Helpers::atoint(request->getParam(F_(id))->value().c_str()); // get id from url
|
||||
|
||||
size_t buffer = EMSESP_JSON_SIZE_XXXXLARGE;
|
||||
auto * response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
|
||||
// check size
|
||||
while (!response->getSize()) {
|
||||
delete response;
|
||||
buffer -= 1024;
|
||||
response = new MsgpackAsyncJsonResponse(false, buffer);
|
||||
}
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->unique_id() == json["id"]) {
|
||||
if (emsdevice->unique_id() == id) {
|
||||
// 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++) {
|
||||
delay(1);
|
||||
@@ -186,12 +196,6 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
||||
emsdevice->generate_values_web(output);
|
||||
#endif
|
||||
|
||||
// #ifdef EMSESP_USE_SERIAL
|
||||
// #ifdef EMSESP_DEBUG
|
||||
// serializeJson(output, Serial);
|
||||
// #endif
|
||||
// #endif
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
size_t length = response->setLength();
|
||||
EMSESP::logger().debug("Dashboard buffer used: %d", length);
|
||||
@@ -202,17 +206,25 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (id == 99) {
|
||||
JsonObject output = response->getRoot();
|
||||
EMSESP::webEntityService.generate_value_web(output);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// invalid but send ok
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
// invalid
|
||||
AsyncWebServerResponse * response = request->beginResponse(400);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
// takes a command and its data value from a specific EMS Device, from the Web
|
||||
// assumes the service has been checked for admin authentication
|
||||
void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
void WebDataService::write_device_value(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject dv = json["devicevalue"];
|
||||
uint8_t unique_id = json["id"];
|
||||
@@ -233,7 +245,7 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
|
||||
// the data could be in any format, but we need string
|
||||
// authenticated is always true
|
||||
JsonVariant data = dv["v"]; // the value in any format
|
||||
uint8_t return_code = CommandRet::OK;
|
||||
uint8_t return_code = CommandRet::NOT_FOUND;
|
||||
uint8_t device_type = emsdevice->device_type();
|
||||
if (data.is<const char *>()) {
|
||||
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
||||
@@ -251,24 +263,57 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
|
||||
if (return_code != CommandRet::OK) {
|
||||
EMSESP::logger().err("Write command failed %s (%s)", (const char *)output["message"], Command::return_code_string(return_code).c_str());
|
||||
} else {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("Write command successful");
|
||||
#endif
|
||||
}
|
||||
|
||||
response->setCode((return_code == CommandRet::OK) ? 200 : 204);
|
||||
response->setCode((return_code == CommandRet::OK) ? 200 : 400); // bad request
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (unique_id == 99) {
|
||||
// parse the command as it could have a hc or wwc prefixed, e.g. hc2/seltemp
|
||||
const char * cmd = dv["c"];
|
||||
int8_t id = -1;
|
||||
cmd = Command::parse_command_string(cmd, id);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_SMALL);
|
||||
JsonObject output = response->getRoot();
|
||||
JsonVariant data = dv["v"]; // the value in any format
|
||||
uint8_t return_code = CommandRet::NOT_FOUND;
|
||||
uint8_t device_type = EMSdevice::DeviceType::CUSTOM;
|
||||
if (data.is<const char *>()) {
|
||||
return_code = Command::call(device_type, cmd, data.as<const char *>(), true, id, output);
|
||||
} else if (data.is<int>()) {
|
||||
char s[10];
|
||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<int16_t>(), 0), true, id, output);
|
||||
} else if (data.is<float>()) {
|
||||
char s[10];
|
||||
return_code = Command::call(device_type, cmd, Helpers::render_value(s, data.as<float>(), 1), true, id, output);
|
||||
}
|
||||
if (return_code != CommandRet::OK) {
|
||||
EMSESP::logger().err("Write command failed %s (%s)", (const char *)output["message"], Command::return_code_string(return_code).c_str());
|
||||
} else {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
EMSESP::logger().debug("Write command successful");
|
||||
#endif
|
||||
}
|
||||
response->setCode((return_code == CommandRet::OK) ? 200 : 400); // bad request
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(204); // Write command failed
|
||||
AsyncWebServerResponse * response = request->beginResponse(400); // bad request
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// takes a dallas sensor name and optional offset from the WebUI and update the customization settings
|
||||
// via the Dallas service
|
||||
void WebDataService::write_sensor(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
// takes a temperaturesensor name and optional offset from the WebUI and update the customization settings
|
||||
// via the temperaturesensor service
|
||||
void WebDataService::write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
bool ok = false;
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject sensor = json;
|
||||
@@ -282,29 +327,31 @@ void WebDataService::write_sensor(AsyncWebServerRequest * request, JsonVariant &
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
offset10 = offset / 0.18;
|
||||
}
|
||||
ok = EMSESP::dallassensor_.update(id, name, offset10);
|
||||
|
||||
ok = EMSESP::temperaturesensor_.update(id, name, offset10);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 400); // bad request
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// update the analog record, or create a new one
|
||||
void WebDataService::write_analog(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
void WebDataService::write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
bool ok = false;
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject analog = json;
|
||||
|
||||
uint8_t gpio = analog["gpio"]; // this is the unique key, the GPIO
|
||||
std::string name = analog["name"];
|
||||
double factor = analog["factor"];
|
||||
double offset = analog["offset"];
|
||||
uint8_t uom = analog["uom"];
|
||||
int8_t type = analog["type"];
|
||||
ok = EMSESP::analogsensor_.update(gpio, name, offset, factor, uom, type);
|
||||
uint8_t gpio = analog["gpio"];
|
||||
std::string name = analog["name"];
|
||||
double factor = analog["factor"];
|
||||
double offset = analog["offset"];
|
||||
uint8_t uom = analog["uom"];
|
||||
int8_t type = analog["type"];
|
||||
bool deleted = analog["deleted"];
|
||||
ok = EMSESP::analogsensor_.update(gpio, name, offset, factor, uom, type, deleted);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 400); // bad request
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
@@ -21,14 +21,14 @@
|
||||
|
||||
// GET
|
||||
#define CORE_DATA_SERVICE_PATH "/rest/coreData"
|
||||
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
||||
#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData"
|
||||
#define SENSOR_DATA_SERVICE_PATH "/rest/sensorData"
|
||||
|
||||
// POST
|
||||
#define WRITE_VALUE_SERVICE_PATH "/rest/writeValue"
|
||||
#define WRITE_SENSOR_SERVICE_PATH "/rest/writeSensor"
|
||||
#define WRITE_ANALOG_SERVICE_PATH "/rest/writeAnalog"
|
||||
#define WRITE_DEVICE_VALUE_SERVICE_PATH "/rest/writeDeviceValue"
|
||||
#define WRITE_TEMPERATURE_SENSOR_SERVICE_PATH "/rest/writeTemperatureSensor"
|
||||
#define WRITE_ANALOG_SENSOR_SERVICE_PATH "/rest/writeAnalogSensor"
|
||||
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -44,15 +44,15 @@ class WebDataService {
|
||||
// GET
|
||||
void core_data(AsyncWebServerRequest * request);
|
||||
void sensor_data(AsyncWebServerRequest * request);
|
||||
void device_data(AsyncWebServerRequest * request);
|
||||
|
||||
// POST
|
||||
void device_data(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_value(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_sensor(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_analog(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void scan_devices(AsyncWebServerRequest * request);
|
||||
void write_device_value(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_temperature_sensor(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void write_analog_sensor(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void scan_devices(AsyncWebServerRequest * request); // command
|
||||
|
||||
AsyncCallbackJsonWebHandler _device_data_handler, _write_value_handler, _write_sensor_handler, _write_analog_handler;
|
||||
AsyncCallbackJsonWebHandler _write_value_handler, _write_temperature_handler, _write_analog_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
549
src/web/WebEntityService.cpp
Normal file
549
src/web/WebEntityService.cpp
Normal file
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebEntityService::WebEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebEntity::read, WebEntity::update, this, server, EMSESP_ENTITY_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED)
|
||||
, _fsPersistence(WebEntity::read, WebEntity::update, this, fs, EMSESP_ENTITY_FILE, FS_BUFFER_SIZE) {
|
||||
}
|
||||
|
||||
// load the settings when the service starts
|
||||
void WebEntityService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
EMSESP::logger().info("Starting Custom entity service");
|
||||
}
|
||||
|
||||
// this creates the entity file, saving it to the FS
|
||||
// and also calls when the Entity web page is refreshed
|
||||
void WebEntity::read(WebEntity & webEntity, JsonObject & root) {
|
||||
JsonArray entity = root.createNestedArray("entities");
|
||||
uint8_t counter = 0;
|
||||
for (const EntityItem & entityItem : webEntity.entityItems) {
|
||||
JsonObject ei = entity.createNestedObject();
|
||||
ei["id"] = counter++; // id is only used to render the table and must be unique
|
||||
ei["device_id"] = entityItem.device_id;
|
||||
ei["type_id"] = entityItem.type_id;
|
||||
ei["offset"] = entityItem.offset;
|
||||
ei["factor"] = entityItem.factor;
|
||||
ei["name"] = entityItem.name;
|
||||
ei["uom"] = entityItem.uom;
|
||||
ei["value_type"] = entityItem.value_type;
|
||||
ei["writeable"] = entityItem.writeable;
|
||||
EMSESP::webEntityService.render_value(ei, entityItem, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// call on initialization and also when the Entity web page is updated
|
||||
// this loads the data into the internal class
|
||||
StateUpdateResult WebEntity::update(JsonObject & root, WebEntity & webEntity) {
|
||||
for (EntityItem & entityItem : webEntity.entityItems) {
|
||||
Command::erase_command(EMSdevice::DeviceType::CUSTOM, entityItem.name.c_str());
|
||||
}
|
||||
webEntity.entityItems.clear();
|
||||
|
||||
if (root["entities"].is<JsonArray>()) {
|
||||
for (const JsonObject ei : root["entities"].as<JsonArray>()) {
|
||||
auto entityItem = EntityItem();
|
||||
entityItem.device_id = ei["device_id"]; // send as numeric, will be converted to string in web
|
||||
entityItem.type_id = ei["type_id"];
|
||||
entityItem.offset = ei["offset"];
|
||||
entityItem.factor = ei["factor"];
|
||||
entityItem.name = ei["name"].as<std::string>();
|
||||
entityItem.uom = ei["uom"];
|
||||
entityItem.value_type = ei["value_type"];
|
||||
entityItem.writeable = ei["writeable"];
|
||||
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_BOOL;
|
||||
} else if (entityItem.value_type == DeviceValueType::INT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_INT;
|
||||
} else if (entityItem.value_type == DeviceValueType::UINT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_UINT;
|
||||
} else if (entityItem.value_type == DeviceValueType::SHORT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_SHORT;
|
||||
} else if (entityItem.value_type == DeviceValueType::USHORT) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_USHORT;
|
||||
} else { // if (entityItem.value_type == DeviceValueType::ULONG || entityItem.value_type == DeviceValueType::TIME) {
|
||||
entityItem.value = EMS_VALUE_DEFAULT_ULONG;
|
||||
}
|
||||
if (entityItem.factor == 0) {
|
||||
entityItem.factor = 1;
|
||||
}
|
||||
webEntity.entityItems.push_back(entityItem); // add to list
|
||||
if (entityItem.writeable) {
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::CUSTOM,
|
||||
webEntity.entityItems.back().name.c_str(),
|
||||
[webEntity](const char * value, const int8_t id) {
|
||||
return EMSESP::webEntityService.command_setvalue(value, webEntity.entityItems.back().name);
|
||||
},
|
||||
FL_(entity_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
// set value by api command
|
||||
bool WebEntityService::command_setvalue(const char * value, const std::string name) {
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
for (EntityItem & entityItem : *entityItems) {
|
||||
if (Helpers::toLower(entityItem.name) == Helpers::toLower(name)) {
|
||||
if (entityItem.value_type == DeviceValueType::STRING) {
|
||||
char telegram[84];
|
||||
strlcpy(telegram, value, sizeof(telegram));
|
||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||
uint8_t count = 0;
|
||||
char * p = strtok(telegram, " ,"); // delimiter
|
||||
while (p != nullptr) {
|
||||
data[count++] = (uint8_t)strtol(p, 0, 16);
|
||||
p = strtok(nullptr, " ,");
|
||||
}
|
||||
if (count == 0) {
|
||||
return false;
|
||||
}
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, data, count, 0);
|
||||
} else if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v ? 0xFF : 0, 0);
|
||||
} else {
|
||||
float f;
|
||||
if (!Helpers::value2float(value, f)) {
|
||||
return false;
|
||||
}
|
||||
int v = f / entityItem.factor;
|
||||
if (entityItem.value_type == DeviceValueType::UINT || entityItem.value_type == DeviceValueType::INT) {
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v, 0);
|
||||
} else if (entityItem.value_type == DeviceValueType::USHORT || entityItem.value_type == DeviceValueType::SHORT) {
|
||||
uint8_t v1[2] = {(uint8_t)(v >> 8), (uint8_t)(v & 0xFF)};
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v1, 2, 0);
|
||||
} else {
|
||||
uint8_t v1[3] = {(uint8_t)(v >> 16), (uint8_t)((v & 0xFF00) >> 8), (uint8_t)(v & 0xFF)};
|
||||
EMSESP::send_write_request(entityItem.type_id, entityItem.device_id, entityItem.offset, v1, 3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
publish_single(entityItem);
|
||||
if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
publish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// output of a single value
|
||||
void WebEntityService::render_value(JsonObject & output, EntityItem entity, const bool useVal, const bool web) {
|
||||
char payload[12];
|
||||
std::string name = useVal ? "value" : entity.name;
|
||||
switch (entity.value_type) {
|
||||
case DeviceValueType::BOOL:
|
||||
if ((uint8_t)entity.value != EMS_VALUE_BOOL_NOTSET) {
|
||||
if (web) {
|
||||
output[name] = Helpers::render_boolean(payload, (uint8_t)entity.value, true);
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
output[name] = (uint8_t)entity.value ? true : false;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
output[name] = (uint8_t)entity.value ? 1 : 0;
|
||||
} else {
|
||||
output[name] = Helpers::render_boolean(payload, (uint8_t)entity.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::INT:
|
||||
if ((int8_t)entity.value != EMS_VALUE_INT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (int8_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::UINT:
|
||||
if ((uint8_t)entity.value != EMS_VALUE_UINT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (uint8_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::SHORT:
|
||||
if ((int16_t)entity.value != EMS_VALUE_SHORT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (int16_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::USHORT:
|
||||
if ((uint16_t)entity.value != EMS_VALUE_USHORT_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * (uint16_t)entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::ULONG:
|
||||
case DeviceValueType::TIME:
|
||||
if (entity.value != EMS_VALUE_ULONG_NOTSET) {
|
||||
output[name] = serialized(Helpers::render_value(payload, entity.factor * entity.value, 2));
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::STRING:
|
||||
if (entity.data.length() > 0) {
|
||||
output[name] = entity.data;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// EMSESP::logger().warning("unknown value type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// process json output for info/commands and value_info
|
||||
bool WebEntityService::get_value_info(JsonObject & output, const char * cmd) {
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
if (entityItems->size() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (Helpers::toLower(cmd) == "commands") {
|
||||
output["info"] = "lists all values";
|
||||
output["commands"] = "lists all commands";
|
||||
for (const auto & entity : *entityItems) {
|
||||
output[entity.name] = "custom entitiy";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (strlen(cmd) == 0 || Helpers::toLower(cmd) == "values" || Helpers::toLower(cmd) == "info") {
|
||||
// list all names
|
||||
for (const EntityItem & entity : *entityItems) {
|
||||
render_value(output, entity);
|
||||
}
|
||||
return (output.size() != 0);
|
||||
}
|
||||
char command_s[30];
|
||||
strlcpy(command_s, cmd, sizeof(command_s));
|
||||
char * attribute_s = nullptr;
|
||||
// check specific attribute to fetch instead of the complete record
|
||||
char * breakp = strchr(command_s, '/');
|
||||
if (breakp) {
|
||||
*breakp = '\0';
|
||||
attribute_s = breakp + 1;
|
||||
}
|
||||
for (const auto & entity : *entityItems) {
|
||||
if (Helpers::toLower(entity.name) == Helpers::toLower(command_s)) {
|
||||
output["name"] = entity.name;
|
||||
if (entity.uom > 0) {
|
||||
output["uom"] = EMSdevice::uom_to_string(entity.uom);
|
||||
}
|
||||
output["type"] = entity.value_type == DeviceValueType::BOOL ? "boolean" : entity.value_type == DeviceValueType::STRING ? "string" : F_(number);
|
||||
output["readable"] = true;
|
||||
output["writeable"] = entity.writeable;
|
||||
output["visible"] = true;
|
||||
output["device_id"] = Helpers::hextoa(entity.device_id);
|
||||
output["type_id"] = Helpers::hextoa(entity.type_id);
|
||||
output["offset"] = entity.offset;
|
||||
if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) {
|
||||
output["factor"] = entity.factor;
|
||||
} else if (entity.value_type == DeviceValueType::STRING) {
|
||||
output["bytes"] = (uint8_t)entity.factor;
|
||||
}
|
||||
render_value(output, entity, true);
|
||||
if (attribute_s) {
|
||||
if (output.containsKey(attribute_s)) {
|
||||
JsonVariant data = output[attribute_s];
|
||||
output.clear();
|
||||
output["api_data"] = data;
|
||||
} else {
|
||||
char error[100];
|
||||
snprintf(error, sizeof(error), "cannot find attribute %s in entity %s", attribute_s, command_s);
|
||||
output.clear();
|
||||
output["message"] = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (output.size()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
output["message"] = "unknown command";
|
||||
return false;
|
||||
}
|
||||
|
||||
// publish single value
|
||||
void WebEntityService::publish_single(const EntityItem & entity) {
|
||||
if (!Mqtt::enabled() || !Mqtt::publish_single()) {
|
||||
return;
|
||||
}
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", "custom", entity.name.c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", "custom_data", entity.name.c_str());
|
||||
}
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
render_value(output, entity, true);
|
||||
Mqtt::queue_publish(topic, output["value"].as<std::string>());
|
||||
}
|
||||
|
||||
// publish to Mqtt
|
||||
void WebEntityService::publish(const bool force) {
|
||||
if (force) {
|
||||
ha_registered_ = false;
|
||||
}
|
||||
if (!Mqtt::enabled()) {
|
||||
return;
|
||||
}
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
if (entityItems->size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (Mqtt::publish_single() && force) {
|
||||
for (const EntityItem & entityItem : *entityItems) {
|
||||
publish_single(entityItem);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
for (const EntityItem & entityItem : *entityItems) {
|
||||
render_value(output, entityItem);
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled() && !ha_registered_) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/custom_data", Mqtt::basename().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
char val_obj[50];
|
||||
char val_cond[65];
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", entityItem.name.c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
||||
|
||||
char uniq_s[70];
|
||||
snprintf(uniq_s, sizeof(uniq_s), "custom_%s", entityItem.name.c_str());
|
||||
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
config["name"] = entityItem.name.c_str();
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (entityItem.writeable) {
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
snprintf(topic, sizeof(topic), "switch/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
} else if (entityItem.value_type == DeviceValueType::STRING) {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
} else if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||
snprintf(topic, sizeof(topic), "number/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
}
|
||||
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/custom/%s", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
} else {
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/custom_%s/config", Mqtt::basename().c_str(), entityItem.name.c_str());
|
||||
}
|
||||
}
|
||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
config["pl_on"] = true;
|
||||
config["pl_off"] = false;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
config["pl_on"] = 1;
|
||||
config["pl_off"] = 0;
|
||||
} else {
|
||||
char result[12];
|
||||
config["pl_on"] = Helpers::render_boolean(result, true);
|
||||
config["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
}
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(Mqtt::basename());
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
if (Mqtt::queue_ha(topic, config.as<JsonObject>())) {
|
||||
ha_registered_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (output.size() > 0) {
|
||||
Mqtt::queue_publish("custom_data", output);
|
||||
}
|
||||
// EMSESP::logger().debug("publish %d custom entities", output.size());
|
||||
}
|
||||
|
||||
// count only entities with valid value or command to show in dashboard
|
||||
uint8_t WebEntityService::count_entities() {
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
if (entityItems->size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
JsonObject output = doc.to<JsonObject>();
|
||||
uint8_t count = 0;
|
||||
for (const EntityItem & entity : *entityItems) {
|
||||
render_value(output, entity);
|
||||
count += (output.containsKey(entity.name) || entity.writeable) ? 1 : 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t WebEntityService::has_commands() {
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
uint8_t count = 0;
|
||||
for (const EntityItem & entity : *entityItems) {
|
||||
count += entity.writeable ? 1 : 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// send to dashboard, msgpack don't like serialized, use number
|
||||
void WebEntityService::generate_value_web(JsonObject & output) {
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
|
||||
output["label"] = (std::string) "Custom Entities";
|
||||
JsonArray data = output.createNestedArray("data");
|
||||
uint8_t index = 0;
|
||||
for (const EntityItem & entity : *entityItems) {
|
||||
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value
|
||||
obj["id"] = "00" + entity.name;
|
||||
obj["u"] = entity.uom;
|
||||
if (entity.writeable) {
|
||||
obj["c"] = entity.name;
|
||||
if (entity.value_type != DeviceValueType::BOOL && entity.value_type != DeviceValueType::STRING) {
|
||||
char s[10];
|
||||
obj["s"] = Helpers::render_value(s, entity.factor, 1);
|
||||
}
|
||||
}
|
||||
|
||||
switch (entity.value_type) {
|
||||
case DeviceValueType::BOOL: {
|
||||
char s[12];
|
||||
obj["v"] = Helpers::render_boolean(s, (uint8_t)entity.value, true);
|
||||
JsonArray l = obj.createNestedArray("l");
|
||||
l.add(Helpers::render_boolean(s, false, true));
|
||||
l.add(Helpers::render_boolean(s, true, true));
|
||||
break;
|
||||
}
|
||||
case DeviceValueType::INT:
|
||||
if ((int8_t)entity.value != EMS_VALUE_INT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int8_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::UINT:
|
||||
if ((uint8_t)entity.value != EMS_VALUE_UINT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint8_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::SHORT:
|
||||
if ((int16_t)entity.value != EMS_VALUE_SHORT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (int16_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::USHORT:
|
||||
if ((uint16_t)entity.value != EMS_VALUE_USHORT_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * (uint16_t)entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::ULONG:
|
||||
case DeviceValueType::TIME:
|
||||
if (entity.value != EMS_VALUE_ULONG_NOTSET) {
|
||||
obj["v"] = Helpers::transformNumFloat(entity.factor * entity.value, 0);
|
||||
}
|
||||
break;
|
||||
case DeviceValueType::STRING:
|
||||
if (entity.data.length() > 0) {
|
||||
obj["v"] = entity.data;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// show only entities with value or command
|
||||
if (!obj.containsKey("v") && !obj.containsKey("c")) {
|
||||
data.remove(index);
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fetch telegram, called from emsesp::fetch
|
||||
void WebEntityService::fetch() {
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
|
||||
for (auto & entity : *entityItems) {
|
||||
EMSESP::send_read_request(entity.type_id,
|
||||
entity.device_id,
|
||||
entity.offset,
|
||||
entity.value_type == DeviceValueType::STRING ? (uint8_t)entity.factor : len[entity.value_type]);
|
||||
}
|
||||
// EMSESP::logger().debug("fetch custom entities");
|
||||
}
|
||||
|
||||
// called on process telegram, read from telegram
|
||||
bool WebEntityService::get_value(std::shared_ptr<const Telegram> telegram) {
|
||||
bool has_change = false;
|
||||
EMSESP::webEntityService.read([&](WebEntity & webEntity) { entityItems = &webEntity.entityItems; });
|
||||
// read-length of BOOL, INT, UINT, SHORT, USHORT, ULONG, TIME
|
||||
const uint8_t len[] = {1, 1, 1, 2, 2, 3, 3};
|
||||
for (auto & entity : *entityItems) {
|
||||
if (entity.value_type == DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
|
||||
&& telegram->offset == entity.offset) {
|
||||
auto data = Helpers::data_to_hex(telegram->message_data, telegram->message_length);
|
||||
if (entity.data != data) {
|
||||
entity.data = data;
|
||||
if (Mqtt::publish_single()) {
|
||||
publish_single(entity);
|
||||
} else if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
has_change = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (entity.value_type != DeviceValueType::STRING && telegram->type_id == entity.type_id && telegram->src == entity.device_id
|
||||
&& telegram->offset <= entity.offset && (telegram->offset + telegram->message_length) >= (entity.offset + len[entity.value_type])) {
|
||||
uint32_t value = 0;
|
||||
for (uint8_t i = 0; i < len[entity.value_type]; i++) {
|
||||
value = (value << 8) + telegram->message_data[i + entity.offset - telegram->offset];
|
||||
}
|
||||
if (value != entity.value) {
|
||||
entity.value = value;
|
||||
if (Mqtt::publish_single()) {
|
||||
publish_single(entity);
|
||||
} else if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
has_change = true;
|
||||
}
|
||||
}
|
||||
// EMSESP::logger().debug("custom entity %s received with value %d", entity.name.c_str(), (int)entity.val);
|
||||
}
|
||||
}
|
||||
if (has_change) {
|
||||
publish();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
78
src/web/WebEntityService.h
Normal file
78
src/web/WebEntityService.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "../telegram.h"
|
||||
|
||||
#ifndef WebEntityService_h
|
||||
#define WebEntityService_h
|
||||
|
||||
#define EMSESP_ENTITY_FILE "/config/emsespEntity.json"
|
||||
#define EMSESP_ENTITY_SERVICE_PATH "/rest/entities" // GET and POST
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EntityItem {
|
||||
public:
|
||||
uint8_t id;
|
||||
uint8_t device_id;
|
||||
uint16_t type_id;
|
||||
uint8_t offset;
|
||||
int8_t value_type;
|
||||
uint8_t uom;
|
||||
std::string name;
|
||||
double factor;
|
||||
bool writeable;
|
||||
uint32_t value;
|
||||
std::string data;
|
||||
};
|
||||
|
||||
class WebEntity {
|
||||
public:
|
||||
std::list<EntityItem> entityItems;
|
||||
|
||||
static void read(WebEntity & webEntity, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, WebEntity & webEntity);
|
||||
};
|
||||
|
||||
class WebEntityService : public StatefulService<WebEntity> {
|
||||
public:
|
||||
WebEntityService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void publish_single(const EntityItem & entity);
|
||||
void publish(const bool force = false);
|
||||
bool command_setvalue(const char * value, const std::string name);
|
||||
bool get_value_info(JsonObject & output, const char * cmd);
|
||||
bool get_value(std::shared_ptr<const Telegram> telegram);
|
||||
void fetch();
|
||||
void render_value(JsonObject & output, EntityItem entity, const bool useVal = false, const bool web = false);
|
||||
uint8_t count_entities();
|
||||
uint8_t has_commands();
|
||||
void generate_value_web(JsonObject & output);
|
||||
|
||||
|
||||
private:
|
||||
HttpEndpoint<WebEntity> _httpEndpoint;
|
||||
FSPersistence<WebEntity> _fsPersistence;
|
||||
|
||||
std::list<EntityItem> * entityItems; // pointer to the list of entity items
|
||||
bool ha_registered_ = false;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
*
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
@@ -24,25 +24,16 @@ namespace emsesp {
|
||||
|
||||
WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: events_(EVENT_SOURCE_LOG_PATH)
|
||||
, setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2), 256) { // for POSTS
|
||||
|
||||
, setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2), 256) {
|
||||
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
server->addHandler(&events_);
|
||||
server->on(EVENT_SOURCE_LOG_PATH, HTTP_GET, std::bind(&WebLogService::forbidden, this, _1));
|
||||
server->on(LOG_SETTINGS_PATH, HTTP_GET, std::bind(&WebLogService::getValues, this, _1)); // get settings
|
||||
|
||||
// for bring back the whole log
|
||||
server->on(FETCH_LOG_PATH, HTTP_GET, std::bind(&WebLogService::fetchLog, this, _1));
|
||||
// for bring back the whole log - is a command, hence a POST
|
||||
server->on(FETCH_LOG_PATH, HTTP_POST, std::bind(&WebLogService::fetchLog, this, _1));
|
||||
|
||||
// get when page is loaded
|
||||
server->on(LOG_SETTINGS_PATH, HTTP_GET, std::bind(&WebLogService::getValues, this, _1));
|
||||
|
||||
// for setting a level
|
||||
server->addHandler(&setValues_);
|
||||
}
|
||||
|
||||
void WebLogService::forbidden(AsyncWebServerRequest * request) {
|
||||
request->send(403);
|
||||
server->addHandler(&events_);
|
||||
}
|
||||
|
||||
// start the log service with INFO level
|
||||
@@ -211,12 +202,12 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
}
|
||||
|
||||
// send the complete log buffer to the API, not filtering on log level
|
||||
// done by resetting the pointer
|
||||
void WebLogService::fetchLog(AsyncWebServerRequest * request) {
|
||||
log_message_id_tail_ = 0;
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
|
||||
// sets the values like level after a POST
|
||||
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (!json.is<JsonObject>()) {
|
||||
@@ -248,4 +239,4 @@ void WebLogService::getValues(AsyncWebServerRequest * request) {
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -58,7 +58,6 @@ class WebLogService : public uuid::log::Handler {
|
||||
const std::shared_ptr<const uuid::log::Message> content_; // Log message content
|
||||
};
|
||||
|
||||
void forbidden(AsyncWebServerRequest * request);
|
||||
void transmit(const QueuedLogMessage & message);
|
||||
void fetchLog(AsyncWebServerRequest * request);
|
||||
void getValues(AsyncWebServerRequest * request);
|
||||
@@ -67,7 +66,7 @@ class WebLogService : public uuid::log::Handler {
|
||||
|
||||
void setValues(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
|
||||
AsyncCallbackJsonWebHandler setValues_; // for POSTs
|
||||
AsyncCallbackJsonWebHandler setValues_; // for POSTs
|
||||
|
||||
uint64_t last_transmit_ = 0; // Last transmit time
|
||||
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; // Maximum number of log messages to buffer before they are output
|
||||
|
||||
405
src/web/WebSchedulerService.cpp
Normal file
405
src/web/WebSchedulerService.cpp
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebSchedulerService::WebSchedulerService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebScheduler::read, WebScheduler::update, this, server, EMSESP_SCHEDULER_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_AUTHENTICATED)
|
||||
, _fsPersistence(WebScheduler::read, WebScheduler::update, this, fs, EMSESP_SCHEDULER_FILE) {
|
||||
}
|
||||
|
||||
// load the settings when the service starts
|
||||
void WebSchedulerService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
EMSESP::logger().info("Starting Scheduler service");
|
||||
}
|
||||
|
||||
// this creates the scheduler file, saving it to the FS
|
||||
// and also calls when the Scheduler web page is refreshed
|
||||
void WebScheduler::read(WebScheduler & webScheduler, JsonObject & root) {
|
||||
JsonArray schedule = root.createNestedArray("schedule");
|
||||
uint8_t counter = 0;
|
||||
for (const ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||
JsonObject si = schedule.createNestedObject();
|
||||
si["id"] = counter++; // id is only used to render the table and must be unique
|
||||
si["active"] = scheduleItem.active;
|
||||
si["flags"] = scheduleItem.flags;
|
||||
si["time"] = scheduleItem.time;
|
||||
si["cmd"] = scheduleItem.cmd;
|
||||
si["value"] = scheduleItem.value;
|
||||
si["name"] = scheduleItem.name;
|
||||
}
|
||||
}
|
||||
|
||||
// call on initialization and also when the Schedule web page is saved
|
||||
// this loads the data into the internal class
|
||||
StateUpdateResult WebScheduler::update(JsonObject & root, WebScheduler & webScheduler) {
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// invoke some fake data for testing
|
||||
const char * json =
|
||||
"{[{\"id\":1,\"active\":true,\"flags\":31,\"time\": \"07:30\",\"cmd\": \"hc1/mode\",\"value\": \"day\",\"name\": \"turn on central heating\"}]}";
|
||||
StaticJsonDocument<500> doc;
|
||||
deserializeJson(doc, json);
|
||||
root = doc.as<JsonObject>();
|
||||
Serial.println(COLOR_BRIGHT_MAGENTA);
|
||||
Serial.print("Using custom file: ");
|
||||
serializeJson(root, Serial);
|
||||
Serial.println(COLOR_RESET);
|
||||
#endif
|
||||
|
||||
for (ScheduleItem & scheduleItem : webScheduler.scheduleItems) {
|
||||
Command::erase_command(EMSdevice::DeviceType::SCHEDULER, scheduleItem.name.c_str());
|
||||
}
|
||||
webScheduler.scheduleItems.clear();
|
||||
|
||||
if (root["schedule"].is<JsonArray>()) {
|
||||
for (const JsonObject schedule : root["schedule"].as<JsonArray>()) {
|
||||
// create each schedule item, overwriting any previous settings
|
||||
// ignore the id (as this is only used in the web for table rendering)
|
||||
auto si = ScheduleItem();
|
||||
si.active = schedule["active"];
|
||||
si.flags = schedule["flags"];
|
||||
si.time = schedule["time"].as<std::string>();
|
||||
si.cmd = schedule["cmd"].as<std::string>();
|
||||
si.value = schedule["value"].as<std::string>();
|
||||
si.name = schedule["name"].as<std::string>();
|
||||
|
||||
// calculated elapsed minutes
|
||||
si.elapsed_min = Helpers::string2minutes(si.time);
|
||||
si.retry_cnt = 0xFF; // no startup retries
|
||||
|
||||
webScheduler.scheduleItems.push_back(si); // add to list
|
||||
if (!webScheduler.scheduleItems.back().name.empty()) {
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::SCHEDULER,
|
||||
webScheduler.scheduleItems.back().name.c_str(),
|
||||
[webScheduler](const char * value, const int8_t id) {
|
||||
return EMSESP::webSchedulerService.command_setvalue(value, webScheduler.scheduleItems.back().name);
|
||||
},
|
||||
FL_(schedule_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
}
|
||||
EMSESP::webSchedulerService.publish(true);
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
// set active by api command
|
||||
bool WebSchedulerService::command_setvalue(const char * value, const std::string name) {
|
||||
bool v;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
for (ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (scheduleItem.name == name) {
|
||||
if (scheduleItem.active == v) {
|
||||
return true;
|
||||
}
|
||||
scheduleItem.active = v;
|
||||
publish_single(name.c_str(), v);
|
||||
if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
publish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// process json output for info/commands and value_info
|
||||
bool WebSchedulerService::get_value_info(JsonObject & output, const char * cmd) {
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (Helpers::toLower(cmd) == "commands") {
|
||||
output["info"] = "lists all values";
|
||||
output["commands"] = "lists all commands";
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.name.empty()) {
|
||||
output[scheduleItem.name] = "activate schedule";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (strlen(cmd) == 0 || Helpers::toLower(cmd) == "values" || Helpers::toLower(cmd) == "info") {
|
||||
// list all names
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.name.empty()) {
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
output[scheduleItem.name] = scheduleItem.active;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
output[scheduleItem.name] = scheduleItem.active ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
output[scheduleItem.name] = Helpers::render_boolean(result, scheduleItem.active);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (output.size() > 0);
|
||||
}
|
||||
char command_s[30];
|
||||
strlcpy(command_s, cmd, sizeof(command_s));
|
||||
char * attribute_s = nullptr;
|
||||
|
||||
// check specific attribute to fetch instead of the complete record
|
||||
char * breakp = strchr(command_s, '/');
|
||||
if (breakp) {
|
||||
*breakp = '\0';
|
||||
attribute_s = breakp + 1;
|
||||
}
|
||||
JsonVariant data;
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (Helpers::toLower(scheduleItem.name) == Helpers::toLower(command_s)) {
|
||||
output["name"] = scheduleItem.name;
|
||||
output["type"] = "boolean";
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
output["value"] = scheduleItem.active;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
output["value"] = scheduleItem.active ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
output["value"] = Helpers::render_boolean(result, scheduleItem.active);
|
||||
}
|
||||
output["command"] = scheduleItem.cmd;
|
||||
output["cmd_data"] = scheduleItem.value;
|
||||
output["readable"] = true;
|
||||
output["writeable"] = true;
|
||||
output["visible"] = true;
|
||||
}
|
||||
}
|
||||
if (attribute_s && output.containsKey(attribute_s)) {
|
||||
data = output[attribute_s];
|
||||
output.clear();
|
||||
output["api_data"] = data;
|
||||
}
|
||||
if (output.size()) {
|
||||
return true;
|
||||
}
|
||||
output["message"] = "unknown command";
|
||||
return false;
|
||||
}
|
||||
|
||||
// publish single value
|
||||
void WebSchedulerService::publish_single(const char * name, const bool state) {
|
||||
if (!Mqtt::publish_single() || name == nullptr || name[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", F_(scheduler), name);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(scheduler), "_data", name);
|
||||
}
|
||||
char payload[12];
|
||||
Mqtt::queue_publish(topic, Helpers::render_boolean(payload, state));
|
||||
}
|
||||
|
||||
// publish to Mqtt
|
||||
void WebSchedulerService::publish(const bool force) {
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (Mqtt::publish_single() && force) {
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
publish_single(scheduleItem.name.c_str(), scheduleItem.active);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE);
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.name.empty() && !doc.containsKey(scheduleItem.name)) {
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
doc[scheduleItem.name] = scheduleItem.active;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
doc[scheduleItem.name] = scheduleItem.active ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
doc[scheduleItem.name] = Helpers::render_boolean(result, scheduleItem.active);
|
||||
}
|
||||
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled() && force) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/scheduler_data", Mqtt::basename().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
char val_obj[50];
|
||||
char val_cond[65];
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", scheduleItem.name.c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
|
||||
|
||||
char uniq_s[70];
|
||||
snprintf(uniq_s, sizeof(uniq_s), "scheduler_%s", scheduleItem.name.c_str());
|
||||
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
config["name"] = scheduleItem.name.c_str();
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "switch/%s/scheduler_%s/config", Mqtt::basename().c_str(), scheduleItem.name.c_str());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/scheduler/%s", Mqtt::basename().c_str(), scheduleItem.name.c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
config["pl_on"] = true;
|
||||
config["pl_off"] = false;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
config["pl_on"] = 1;
|
||||
config["pl_off"] = 0;
|
||||
} else {
|
||||
char result[12];
|
||||
config["pl_on"] = Helpers::render_boolean(result, true);
|
||||
config["pl_off"] = Helpers::render_boolean(result, false);
|
||||
}
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add(Mqtt::basename());
|
||||
|
||||
// add "availability" section
|
||||
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
|
||||
Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doc.size() > 0) {
|
||||
Mqtt::queue_publish("scheduler_data", doc.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
|
||||
bool WebSchedulerService::has_commands() {
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return false;
|
||||
}
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (!scheduleItem.name.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// execute scheduled command
|
||||
bool WebSchedulerService::command(const char * cmd, const char * data) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc_input;
|
||||
JsonObject input = doc_input.to<JsonObject>();
|
||||
if (strlen(data)) { // empty data queries a value
|
||||
input["data"] = data;
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc_output; // only for commands without output
|
||||
JsonObject output = doc_output.to<JsonObject>();
|
||||
|
||||
// prefix "api/" to command string
|
||||
char command_str[100];
|
||||
snprintf(command_str, sizeof(command_str), "/api/%s", cmd);
|
||||
uint8_t return_code = Command::process(command_str, true, input, output); // admin set
|
||||
|
||||
if (return_code == CommandRet::OK) {
|
||||
EMSESP::logger().debug("Scheduled command %s with data %s successfully", cmd, data);
|
||||
if (strlen(data) == 0 && output.size()) {
|
||||
Mqtt::queue_publish("response", output);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char error[100];
|
||||
if (output.size()) {
|
||||
snprintf(error,
|
||||
sizeof(error),
|
||||
"Scheduled command %s failed with error: %s (%s)",
|
||||
cmd,
|
||||
(const char *)output["message"],
|
||||
Command::return_code_string(return_code).c_str());
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Scheduled command %s failed with error code (%s)", cmd, Command::return_code_string(return_code).c_str());
|
||||
}
|
||||
emsesp::EMSESP::logger().err(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// process any scheduled jobs
|
||||
// checks on the minute and at startup
|
||||
void WebSchedulerService::loop() {
|
||||
// initialize static value on startup
|
||||
static int8_t last_tm_min = -1; // invalid value also used for startup commands
|
||||
static uint32_t last_uptime_min = 0;
|
||||
|
||||
// get list of scheduler events and exit if it's empty
|
||||
EMSESP::webSchedulerService.read([&](WebScheduler & webScheduler) { scheduleItems = &webScheduler.scheduleItems; });
|
||||
if (scheduleItems->size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check startup commands
|
||||
if (last_tm_min == -1) {
|
||||
for (ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0) {
|
||||
scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str()) ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
last_tm_min = 0; // startup done, now use for RTC
|
||||
}
|
||||
|
||||
// check timer every minute, sync to EMS-ESP clock
|
||||
uint32_t uptime_min = uuid::get_uptime_sec() / 60;
|
||||
if (last_uptime_min != uptime_min) {
|
||||
for (ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
// retry startup commands not yet executed
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min == 0
|
||||
&& scheduleItem.retry_cnt < MAX_STARTUP_RETRIES) {
|
||||
scheduleItem.retry_cnt = command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str()) ? 0xFF : scheduleItem.retry_cnt + 1;
|
||||
}
|
||||
// scheduled timer commands
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_TIMER && scheduleItem.elapsed_min > 0
|
||||
&& (uptime_min % scheduleItem.elapsed_min == 0)) {
|
||||
command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str());
|
||||
}
|
||||
}
|
||||
last_uptime_min = uptime_min;
|
||||
}
|
||||
|
||||
// check calender, sync to RTC, only execute if year is valid
|
||||
time_t now = time(nullptr);
|
||||
tm * tm = localtime(&now);
|
||||
if (tm->tm_min != last_tm_min && tm->tm_year > 120) {
|
||||
// find the real dow and minute from RTC
|
||||
uint8_t real_dow = 1 << tm->tm_wday; // 1 is Sunday
|
||||
uint16_t real_min = tm->tm_hour * 60 + tm->tm_min;
|
||||
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems) {
|
||||
if (scheduleItem.active && (real_dow & scheduleItem.flags) && real_min == scheduleItem.elapsed_min) {
|
||||
command(scheduleItem.cmd.c_str(), scheduleItem.value.c_str());
|
||||
}
|
||||
}
|
||||
last_tm_min = tm->tm_min;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
76
src/web/WebSchedulerService.h
Normal file
76
src/web/WebSchedulerService.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WebSchedulerService_h
|
||||
#define WebSchedulerService_h
|
||||
|
||||
#define EMSESP_SCHEDULER_FILE "/config/emsespScheduler.json"
|
||||
#define EMSESP_SCHEDULER_SERVICE_PATH "/rest/schedule" // GET and POST
|
||||
|
||||
#define SCHEDULEFLAG_SCHEDULE_TIMER 0x80 // 7th bit for Timer
|
||||
#define MAX_STARTUP_RETRIES 3 // retry the start-up commands x times
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class ScheduleItem {
|
||||
public:
|
||||
boolean active;
|
||||
uint8_t flags;
|
||||
uint16_t elapsed_min; // total mins from 00:00
|
||||
std::string time; // HH:MM
|
||||
std::string cmd;
|
||||
std::string value;
|
||||
std::string name;
|
||||
uint8_t retry_cnt;
|
||||
};
|
||||
|
||||
class WebScheduler {
|
||||
public:
|
||||
std::list<ScheduleItem> scheduleItems;
|
||||
|
||||
static void read(WebScheduler & webScheduler, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, WebScheduler & webScheduler);
|
||||
};
|
||||
|
||||
class WebSchedulerService : public StatefulService<WebScheduler> {
|
||||
public:
|
||||
WebSchedulerService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
void publish_single(const char * name, const bool state);
|
||||
void publish(const bool force = false);
|
||||
bool has_commands();
|
||||
bool command_setvalue(const char * value, const std::string name);
|
||||
bool get_value_info(JsonObject & output, const char * cmd);
|
||||
|
||||
// make all functions public so we can test in the debug and standalone mode
|
||||
#ifndef EMSESP_STANDALONE
|
||||
private:
|
||||
#endif
|
||||
bool command(const char * cmd, const char * data);
|
||||
|
||||
HttpEndpoint<WebScheduler> _httpEndpoint;
|
||||
FSPersistence<WebScheduler> _fsPersistence;
|
||||
|
||||
std::list<ScheduleItem> * scheduleItems; // pointer to the list of schedule events
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -26,12 +26,11 @@ using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebSettingsService::WebSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebSettings::read, WebSettings::update, this, server, EMSESP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(WebSettings::read, WebSettings::update, this, fs, EMSESP_SETTINGS_FILE)
|
||||
, _boardProfileHandler(EMSESP_BOARD_PROFILE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebSettingsService::board_profile, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
_boardProfileHandler.setMethod(HTTP_POST);
|
||||
_boardProfileHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_boardProfileHandler);
|
||||
, _fsPersistence(WebSettings::read, WebSettings::update, this, fs, EMSESP_SETTINGS_FILE) {
|
||||
// GET
|
||||
server->on(EMSESP_BOARD_PROFILE_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebSettingsService::board_profile, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
addUpdateHandler([&](const String & originId) { onUpdate(); }, false);
|
||||
}
|
||||
@@ -47,7 +46,6 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
root["syslog_mark_interval"] = settings.syslog_mark_interval;
|
||||
root["syslog_host"] = settings.syslog_host;
|
||||
root["syslog_port"] = settings.syslog_port;
|
||||
root["boiler_heatingoff"] = settings.boiler_heatingoff;
|
||||
root["shower_timer"] = settings.shower_timer;
|
||||
root["shower_alert"] = settings.shower_alert;
|
||||
root["shower_alert_coldshot"] = settings.shower_alert_coldshot;
|
||||
@@ -93,7 +91,8 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
settings.board_profile = root["board_profile"] | "S2MINI";
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
settings.board_profile = root["board_profile"] | "S3MINI";
|
||||
// settings.board_profile = root["board_profile"] | "S3MINI";
|
||||
settings.board_profile = root["board_profile"] | "S32S3"; // BBQKees Gateway S3
|
||||
#elif CONFIG_IDF_TARGET_ESP32
|
||||
settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE;
|
||||
#endif
|
||||
@@ -167,7 +166,7 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
#ifndef EMSESP_STANDALONE
|
||||
String old_syslog_host = settings.syslog_host;
|
||||
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
|
||||
if (!old_syslog_host.equals(settings.syslog_host)) {
|
||||
if (old_syslog_host != settings.syslog_host) {
|
||||
add_flags(ChangeFlags::SYSLOG);
|
||||
}
|
||||
#endif
|
||||
@@ -177,13 +176,13 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.pbutton_gpio = root["pbutton_gpio"] | default_pbutton_gpio;
|
||||
check_flag(prev, settings.pbutton_gpio, ChangeFlags::BUTTON);
|
||||
|
||||
// dallas
|
||||
// temperaturesensor
|
||||
prev = settings.dallas_gpio;
|
||||
settings.dallas_gpio = root["dallas_gpio"] | default_dallas_gpio;
|
||||
check_flag(prev, settings.dallas_gpio, ChangeFlags::DALLAS);
|
||||
check_flag(prev, settings.dallas_gpio, ChangeFlags::SENSOR);
|
||||
prev = settings.dallas_parasite;
|
||||
settings.dallas_parasite = root["dallas_parasite"] | EMSESP_DEFAULT_DALLAS_PARASITE;
|
||||
check_flag(prev, settings.dallas_parasite, ChangeFlags::DALLAS);
|
||||
check_flag(prev, settings.dallas_parasite, ChangeFlags::SENSOR);
|
||||
|
||||
// shower
|
||||
prev = settings.shower_timer;
|
||||
@@ -238,7 +237,7 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
check_flag(prev, settings.ems_bus_id, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.low_clock;
|
||||
settings.low_clock = root["low_clock"] | false;
|
||||
settings.low_clock = root["low_clock"];
|
||||
check_flag(prev, settings.low_clock, ChangeFlags::RESTART);
|
||||
|
||||
//
|
||||
@@ -247,32 +246,38 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
prev = settings.bool_format;
|
||||
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
||||
EMSESP::system_.bool_format(settings.bool_format);
|
||||
if (Mqtt::ha_enabled())
|
||||
if (Mqtt::ha_enabled()) {
|
||||
check_flag(prev, settings.bool_format, ChangeFlags::MQTT);
|
||||
}
|
||||
|
||||
prev = settings.enum_format;
|
||||
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
|
||||
EMSESP::system_.enum_format(settings.enum_format);
|
||||
if (Mqtt::ha_enabled())
|
||||
if (Mqtt::ha_enabled()) {
|
||||
check_flag(prev, settings.enum_format, ChangeFlags::MQTT);
|
||||
}
|
||||
|
||||
String old_locale = settings.locale;
|
||||
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
|
||||
EMSESP::system_.locale(settings.locale);
|
||||
if (Mqtt::ha_enabled() && old_locale != settings.locale) {
|
||||
add_flags(ChangeFlags::MQTT);
|
||||
}
|
||||
|
||||
//
|
||||
// without checks or necessary restarts...
|
||||
//
|
||||
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
|
||||
EMSESP::system_.locale(settings.locale);
|
||||
|
||||
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
|
||||
EMSESP::trace_raw(settings.trace_raw);
|
||||
|
||||
settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
|
||||
settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
|
||||
settings.boiler_heatingoff = root["boiler_heatingoff"] | EMSESP_DEFAULT_BOILER_HEATINGOFF;
|
||||
settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
|
||||
settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
|
||||
|
||||
settings.fahrenheit = root["fahrenheit"] | false;
|
||||
settings.fahrenheit = root["fahrenheit"];
|
||||
EMSESP::system_.fahrenheit(settings.fahrenheit);
|
||||
|
||||
settings.readonly_mode = root["readonly_mode"] | false;
|
||||
settings.readonly_mode = root["readonly_mode"];
|
||||
EMSESP::system_.readonly_mode(settings.readonly_mode);
|
||||
|
||||
settings.bool_dashboard = root["bool_dashboard"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
||||
@@ -296,8 +301,8 @@ void WebSettingsService::onUpdate() {
|
||||
EMSESP::shower_.start();
|
||||
}
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::DALLAS)) {
|
||||
EMSESP::dallassensor_.start();
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::SENSOR)) {
|
||||
EMSESP::temperaturesensor_.start();
|
||||
}
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::UART)) {
|
||||
@@ -337,29 +342,29 @@ void WebSettingsService::save() {
|
||||
}
|
||||
|
||||
// build the json profile to send back
|
||||
void WebSettingsService::board_profile(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
void WebSettingsService::board_profile(AsyncWebServerRequest * request) {
|
||||
if (request->hasParam("boardProfile")) {
|
||||
std::string board_profile = request->getParam("boardProfile")->value().c_str();
|
||||
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
if (json.containsKey("board_profile")) {
|
||||
String board_profile = json["board_profile"];
|
||||
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
(void)System::load_board_profile(data, board_profile.c_str());
|
||||
root["led_gpio"] = data[0];
|
||||
root["dallas_gpio"] = data[1];
|
||||
root["rx_gpio"] = data[2];
|
||||
root["tx_gpio"] = data[3];
|
||||
root["pbutton_gpio"] = data[4];
|
||||
root["phy_type"] = data[5];
|
||||
root["eth_power"] = data[6];
|
||||
root["eth_phy_addr"] = data[7];
|
||||
root["eth_clock_mode"] = data[8];
|
||||
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
(void)System::load_board_profile(data, board_profile);
|
||||
root["board_profile"] = board_profile;
|
||||
root["led_gpio"] = data[0];
|
||||
root["dallas_gpio"] = data[1];
|
||||
root["rx_gpio"] = data[2];
|
||||
root["tx_gpio"] = data[3];
|
||||
root["pbutton_gpio"] = data[4];
|
||||
root["phy_type"] = data[5];
|
||||
root["eth_power"] = data[6];
|
||||
root["eth_phy_addr"] = data[7];
|
||||
root["eth_clock_mode"] = data[8];
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -33,7 +33,6 @@ class WebSettings {
|
||||
String locale;
|
||||
uint8_t tx_mode;
|
||||
uint8_t ems_bus_id;
|
||||
bool boiler_heatingoff;
|
||||
bool shower_timer;
|
||||
bool shower_alert;
|
||||
uint8_t shower_alert_trigger;
|
||||
@@ -80,7 +79,7 @@ class WebSettings {
|
||||
UART = (1 << 0), // 1
|
||||
SYSLOG = (1 << 1), // 2
|
||||
ADC = (1 << 2), // 4 - analog
|
||||
DALLAS = (1 << 3), // 8
|
||||
SENSOR = (1 << 3), // 8
|
||||
SHOWER = (1 << 4), // 16
|
||||
LED = (1 << 5), // 32
|
||||
BUTTON = (1 << 6), // 64
|
||||
@@ -123,11 +122,10 @@ class WebSettingsService : public StatefulService<WebSettings> {
|
||||
void save();
|
||||
|
||||
private:
|
||||
HttpEndpoint<WebSettings> _httpEndpoint;
|
||||
FSPersistence<WebSettings> _fsPersistence;
|
||||
AsyncCallbackJsonWebHandler _boardProfileHandler;
|
||||
HttpEndpoint<WebSettings> _httpEndpoint;
|
||||
FSPersistence<WebSettings> _fsPersistence;
|
||||
|
||||
void board_profile(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void board_profile(AsyncWebServerRequest * request);
|
||||
|
||||
void onUpdate();
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,6 +32,8 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se
|
||||
|
||||
// handles both WiFI and Ethernet
|
||||
void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
EMSESP::logger().warning("WiFi disconnected. Reason code=%s", disconnectReason(info.wifi_sta_disconnected.reason)); // IDF 4.0
|
||||
@@ -39,10 +41,8 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
break;
|
||||
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info("WiFi connected with IP=%s, hostname=%s", WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
#endif
|
||||
EMSESP::system_.syslog_init();
|
||||
// EMSESP::system_.syslog_init();
|
||||
mDNS_start();
|
||||
EMSESP::system_.send_info_mqtt("connected");
|
||||
break;
|
||||
@@ -63,11 +63,9 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
case ARDUINO_EVENT_ETH_GOT_IP:
|
||||
// prevent double calls
|
||||
if (!EMSESP::system_.ethernet_connected()) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info("Ethernet connected with IP=%s, speed %d Mbps", ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
||||
#endif
|
||||
// EMSESP::system_.send_heartbeat();
|
||||
EMSESP::system_.syslog_init();
|
||||
// EMSESP::system_.syslog_init();
|
||||
EMSESP::system_.ethernet_connected(true);
|
||||
mDNS_start();
|
||||
EMSESP::system_.send_info_mqtt("connected");
|
||||
@@ -84,7 +82,6 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
EMSESP::system_.ethernet_connected(false);
|
||||
break;
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
if (networkSettings.enableIPv6) {
|
||||
@@ -108,73 +105,74 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
} else {
|
||||
EMSESP::logger().info("WiFi connected with IPv6=%s, hostname=%s", WiFi.localIPv6().toString().c_str(), WiFi.getHostname());
|
||||
}
|
||||
EMSESP::system_.syslog_init();
|
||||
// EMSESP::system_.syslog_init();
|
||||
mDNS_start();
|
||||
EMSESP::system_.send_info_mqtt("connected");
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM_DYN);
|
||||
auto * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
root["tx_mode"] = EMSESP::txservice_.tx_mode();
|
||||
root["uptime"] = EMSbus::bus_uptime();
|
||||
root["num_devices"] = EMSESP::count_devices(); // excluding Controller
|
||||
root["num_sensors"] = EMSESP::dallassensor_.no_sensors();
|
||||
root["num_sensors"] = EMSESP::temperaturesensor_.no_sensors();
|
||||
root["num_analogs"] = EMSESP::analogsensor_.no_sensors();
|
||||
|
||||
JsonArray statsJson = root.createNestedArray("stats");
|
||||
JsonObject statJson;
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "0";
|
||||
statJson["id"] = 0;
|
||||
statJson["s"] = EMSESP::rxservice_.telegram_count();
|
||||
statJson["f"] = EMSESP::rxservice_.telegram_error_count();
|
||||
statJson["q"] = EMSESP::rxservice_.quality();
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "1";
|
||||
statJson["id"] = 1;
|
||||
statJson["s"] = EMSESP::txservice_.telegram_read_count();
|
||||
statJson["f"] = EMSESP::txservice_.telegram_read_fail_count();
|
||||
statJson["q"] = EMSESP::txservice_.read_quality();
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "2";
|
||||
statJson["id"] = 2;
|
||||
statJson["s"] = EMSESP::txservice_.telegram_write_count();
|
||||
statJson["f"] = EMSESP::txservice_.telegram_write_fail_count();
|
||||
statJson["q"] = EMSESP::txservice_.write_quality();
|
||||
|
||||
if (EMSESP::dallassensor_.dallas_enabled()) {
|
||||
if (EMSESP::temperaturesensor_.sensor_enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "3";
|
||||
statJson["s"] = EMSESP::dallassensor_.reads();
|
||||
statJson["f"] = EMSESP::dallassensor_.fails();
|
||||
statJson["q"] = EMSESP::dallassensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::dallassensor_.fails()) / EMSESP::dallassensor_.reads());
|
||||
statJson["id"] = 3;
|
||||
statJson["s"] = EMSESP::temperaturesensor_.reads();
|
||||
statJson["f"] = EMSESP::temperaturesensor_.fails();
|
||||
statJson["q"] =
|
||||
EMSESP::temperaturesensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::temperaturesensor_.fails()) / EMSESP::temperaturesensor_.reads());
|
||||
}
|
||||
if (EMSESP::analog_enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "4";
|
||||
statJson["id"] = 4;
|
||||
statJson["s"] = EMSESP::analogsensor_.reads();
|
||||
statJson["f"] = EMSESP::analogsensor_.fails();
|
||||
statJson["q"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads());
|
||||
}
|
||||
if (Mqtt::enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "5";
|
||||
statJson["id"] = 5;
|
||||
statJson["s"] = Mqtt::publish_count();
|
||||
statJson["f"] = Mqtt::publish_fails();
|
||||
statJson["q"] = Mqtt::publish_count() == 0 ? 100 : 100 - (uint8_t)((100 * Mqtt::publish_fails()) / (Mqtt::publish_count() + Mqtt::publish_fails()));
|
||||
}
|
||||
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "6";
|
||||
statJson["id"] = 6;
|
||||
statJson["s"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
|
||||
statJson["f"] = WebAPIService::api_fails();
|
||||
statJson["q"] =
|
||||
@@ -183,7 +181,7 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (EMSESP::system_.syslog_enabled()) {
|
||||
statJson = statsJson.createNestedObject();
|
||||
statJson["id"] = "7";
|
||||
statJson["id"] = 7;
|
||||
statJson["s"] = EMSESP::system_.syslog_count();
|
||||
statJson["f"] = EMSESP::system_.syslog_fails();
|
||||
statJson["q"] = EMSESP::system_.syslog_count() == 0
|
||||
@@ -230,61 +228,61 @@ void WebStatusService::mDNS_start() const {
|
||||
const char * WebStatusService::disconnectReason(uint8_t code) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
switch (code) {
|
||||
case WIFI_REASON_UNSPECIFIED: // = 1,
|
||||
return "unspecifiied";
|
||||
case WIFI_REASON_AUTH_EXPIRE: // = 2,
|
||||
case WIFI_REASON_UNSPECIFIED: // = 1,
|
||||
return "unspecified";
|
||||
case WIFI_REASON_AUTH_EXPIRE: // = 2,
|
||||
return "auth expire";
|
||||
case WIFI_REASON_AUTH_LEAVE: // = 3,
|
||||
case WIFI_REASON_AUTH_LEAVE: // = 3,
|
||||
return "auth leave";
|
||||
case WIFI_REASON_ASSOC_EXPIRE: // = 4,
|
||||
case WIFI_REASON_ASSOC_EXPIRE: // = 4,
|
||||
return "assoc expired";
|
||||
case WIFI_REASON_ASSOC_TOOMANY: // = 5,
|
||||
case WIFI_REASON_ASSOC_TOOMANY: // = 5,
|
||||
return "assoc too many";
|
||||
case WIFI_REASON_NOT_AUTHED: // = 6,
|
||||
return "not authed";
|
||||
case WIFI_REASON_NOT_ASSOCED: // = 7,
|
||||
return "not assoced";
|
||||
case WIFI_REASON_ASSOC_LEAVE: // = 8,
|
||||
case WIFI_REASON_NOT_AUTHED: // = 6,
|
||||
return "not authenticated";
|
||||
case WIFI_REASON_NOT_ASSOCED: // = 7,
|
||||
return "not assoc";
|
||||
case WIFI_REASON_ASSOC_LEAVE: // = 8,
|
||||
return "assoc leave";
|
||||
case WIFI_REASON_ASSOC_NOT_AUTHED: // = 9,
|
||||
case WIFI_REASON_ASSOC_NOT_AUTHED: // = 9,
|
||||
return "assoc not authed";
|
||||
case WIFI_REASON_DISASSOC_PWRCAP_BAD: // = 10,
|
||||
case WIFI_REASON_DISASSOC_PWRCAP_BAD: // = 10,
|
||||
return "disassoc powerCAP bad";
|
||||
case WIFI_REASON_DISASSOC_SUPCHAN_BAD: // = 11,
|
||||
case WIFI_REASON_DISASSOC_SUPCHAN_BAD: // = 11,
|
||||
return "disassoc supchan bad";
|
||||
case WIFI_REASON_IE_INVALID: // = 13,
|
||||
case WIFI_REASON_IE_INVALID: // = 13,
|
||||
return "IE invalid";
|
||||
case WIFI_REASON_MIC_FAILURE: // = 14,
|
||||
case WIFI_REASON_MIC_FAILURE: // = 14,
|
||||
return "MIC failure";
|
||||
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // = 15,
|
||||
case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: // = 15,
|
||||
return "4way handshake timeout";
|
||||
case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: // = 16,
|
||||
return "group key-update timeout";
|
||||
case WIFI_REASON_IE_IN_4WAY_DIFFERS: // = 17,
|
||||
case WIFI_REASON_IE_IN_4WAY_DIFFERS: // = 17,
|
||||
return "IE in 4way differs";
|
||||
case WIFI_REASON_GROUP_CIPHER_INVALID: // = 18,
|
||||
case WIFI_REASON_GROUP_CIPHER_INVALID: // = 18,
|
||||
return "group cipher invalid";
|
||||
case WIFI_REASON_PAIRWISE_CIPHER_INVALID: // = 19,
|
||||
case WIFI_REASON_PAIRWISE_CIPHER_INVALID: // = 19,
|
||||
return "pairwise cipher invalid";
|
||||
case WIFI_REASON_AKMP_INVALID: // = 20,
|
||||
case WIFI_REASON_AKMP_INVALID: // = 20,
|
||||
return "AKMP invalid";
|
||||
case WIFI_REASON_UNSUPP_RSN_IE_VERSION: // = 21,
|
||||
case WIFI_REASON_UNSUPP_RSN_IE_VERSION: // = 21,
|
||||
return "unsupported RSN_IE version";
|
||||
case WIFI_REASON_INVALID_RSN_IE_CAP: // = 22,
|
||||
case WIFI_REASON_INVALID_RSN_IE_CAP: // = 22,
|
||||
return "invalid RSN_IE_CAP";
|
||||
case WIFI_REASON_802_1X_AUTH_FAILED: // = 23,
|
||||
case WIFI_REASON_802_1X_AUTH_FAILED: // = 23,
|
||||
return "802 X1 auth failed";
|
||||
case WIFI_REASON_CIPHER_SUITE_REJECTED: // = 24,
|
||||
case WIFI_REASON_CIPHER_SUITE_REJECTED: // = 24,
|
||||
return "cipher suite rejected";
|
||||
case WIFI_REASON_BEACON_TIMEOUT: // = 200,
|
||||
case WIFI_REASON_BEACON_TIMEOUT: // = 200,
|
||||
return "beacon timeout";
|
||||
case WIFI_REASON_NO_AP_FOUND: // = 201,
|
||||
case WIFI_REASON_NO_AP_FOUND: // = 201,
|
||||
return "no AP found";
|
||||
case WIFI_REASON_AUTH_FAIL: // = 202,
|
||||
case WIFI_REASON_AUTH_FAIL: // = 202,
|
||||
return "auth fail";
|
||||
case WIFI_REASON_ASSOC_FAIL: // = 203,
|
||||
case WIFI_REASON_ASSOC_FAIL: // = 203,
|
||||
return "assoc fail";
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT: // = 204,
|
||||
case WIFI_REASON_HANDSHAKE_TIMEOUT: // = 204,
|
||||
return "handshake timeout";
|
||||
default:
|
||||
return "unknown";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 Paul Derbyshire
|
||||
* Copyright 2020-2023 Paul Derbyshire
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
||||
Reference in New Issue
Block a user