mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-01-28 17:49:08 +03:00
Merge remote-tracking branch 'origin/v3.4' into dev
This commit is contained in:
413
src/analogsensor.cpp
Normal file
413
src/analogsensor.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 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 "analogsensor.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger AnalogSensor::logger_{F_(analogsensor), uuid::log::Facility::DAEMON};
|
||||
|
||||
void AnalogSensor::start() {
|
||||
reload(); // fetch the list of sensors from our customization service
|
||||
|
||||
if (analog_enabled_) {
|
||||
analogSetAttenuation(ADC_2_5db); // for all channels
|
||||
}
|
||||
|
||||
LOG_INFO(F("Starting Analog sensor service"));
|
||||
|
||||
// Add API call for /info
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::ANALOGSENSOR,
|
||||
F_(info),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
|
||||
F_(info_cmd));
|
||||
}
|
||||
|
||||
// load settings from the customization file, sorts them and initializes the GPIOs
|
||||
void AnalogSensor::reload() {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { analog_enabled_ = settings.analog_enabled; });
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
analog_enabled_ = true; // for local offline testing
|
||||
#endif
|
||||
|
||||
// load the list of analog sensors from the customization service
|
||||
// and store them locally and then activate them
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
auto sensors = settings.analogCustomizations;
|
||||
sensors_.clear(); // start with an empty list
|
||||
if (sensors.size() != 0) {
|
||||
for (auto & sensor : sensors) {
|
||||
sensors_.emplace_back(sensor.id, sensor.name, sensor.offset, sensor.factor, sensor.uom, sensor.type);
|
||||
sensors_.back().ha_registered = false; // this will trigger recrate of the HA config
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// sort the list based on GPIO (id)
|
||||
std::sort(sensors_.begin(), sensors_.end(), [](const Sensor & a, const Sensor & b) { return a.id() < b.id(); });
|
||||
|
||||
// activate each sensor
|
||||
for (auto & sensor : sensors_) {
|
||||
sensor.ha_registered = false; // force HA configs to be re-created
|
||||
if (sensor.type() == AnalogType::ADC) {
|
||||
LOG_DEBUG(F("Adding analog ADC sensor on GPIO%d"), sensor.id());
|
||||
// analogSetPinAttenuation does not work with analogReadMilliVolts
|
||||
sensor.analog_ = 0; // initialize
|
||||
sensor.last_reading_ = 0;
|
||||
} else if (sensor.type() == AnalogType::COUNTER) {
|
||||
LOG_DEBUG(F("Adding analog I/O Counter sensor on GPIO%d"), sensor.id());
|
||||
pinMode(sensor.id(), INPUT_PULLUP);
|
||||
sensor.set_value(0); // reset count
|
||||
sensor.set_uom(0); // no uom, just for safe measures
|
||||
sensor.polltime_ = 0;
|
||||
sensor.poll_ = digitalRead(sensor.id());
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
LOG_DEBUG(F("Adding analog Read sensor on GPIO%d"), sensor.id());
|
||||
pinMode(sensor.id(), INPUT_PULLUP);
|
||||
sensor.set_value(digitalRead(sensor.id())); // initial value
|
||||
sensor.set_uom(0); // no uom, just for safe measures
|
||||
sensor.polltime_ = 0;
|
||||
sensor.poll_ = digitalRead(sensor.id());
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// measure and moving average adc
|
||||
void AnalogSensor::measure() {
|
||||
static uint32_t measure_last_ = 0;
|
||||
|
||||
// measure interval 500ms for analog sensors
|
||||
if (!measure_last_ || (uuid::get_uptime() - measure_last_) >= MEASURE_ANALOG_INTERVAL) {
|
||||
measure_last_ = uuid::get_uptime();
|
||||
// go through the list of ADC sensors
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() == AnalogType::ADC) {
|
||||
uint16_t a = analogReadMilliVolts(sensor.id()); // e.g. ADC1_CHANNEL_0_GPIO_NUM
|
||||
if (!sensor.analog_) { // init first time
|
||||
sensor.analog_ = a;
|
||||
sensor.sum_ = a * 512;
|
||||
} else { // simple moving average filter
|
||||
sensor.sum_ = (sensor.sum_ * 511) / 512 + a;
|
||||
sensor.analog_ = sensor.sum_ / 512;
|
||||
}
|
||||
// detect change with little hysteresis on raw mV value
|
||||
if (sensor.last_reading_ + 1 < sensor.analog_ || sensor.last_reading_ > sensor.analog_ + 1) {
|
||||
sensor.set_value(((int32_t)sensor.analog_ - sensor.offset()) * sensor.factor());
|
||||
sensor.last_reading_ = sensor.analog_;
|
||||
sensorreads_++;
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// poll digital io every time
|
||||
// go through the list of digital sensors
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER) {
|
||||
auto old_value = sensor.value(); // remember current value before reading
|
||||
auto current_reading = digitalRead(sensor.id());
|
||||
if (sensor.poll_ != current_reading) { // check for pinchange
|
||||
sensor.polltime_ = uuid::get_uptime();
|
||||
sensor.poll_ = current_reading;
|
||||
}
|
||||
if (uuid::get_uptime() - sensor.polltime_ >= 15) { // debounce
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
sensor.set_value(sensor.poll_);
|
||||
} else if (sensor.type() == AnalogType::COUNTER) {
|
||||
// capture reading and compare with the last one to see if there is high/low change
|
||||
if (sensor.poll_ != sensor.last_reading_) {
|
||||
sensor.last_reading_ = sensor.poll_;
|
||||
if (!sensor.poll_) {
|
||||
sensor.set_value(old_value + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// see if there is a change and increment # reads
|
||||
if (old_value != sensor.value()) {
|
||||
sensorreads_++;
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogSensor::loop() {
|
||||
if (!analog_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
measure(); // take the measurements
|
||||
}
|
||||
|
||||
// update analog information name and offset
|
||||
bool AnalogSensor::update(uint8_t id, const std::string & name, uint16_t offset, float factor, uint8_t uom, int8_t type) {
|
||||
boolean found_sensor = false; // see if we can find the sensor in our customization list
|
||||
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
for (auto & AnalogCustomization : settings.analogCustomizations) {
|
||||
if (AnalogCustomization.id == id) {
|
||||
found_sensor = true; // found the record
|
||||
// see if it's marked for deletion
|
||||
if (type == AnalogType::MARK_DELETED) {
|
||||
LOG_DEBUG(F("Removing analog sensor ID %d"), id);
|
||||
settings.analogCustomizations.remove(AnalogCustomization);
|
||||
} else {
|
||||
// update existing record
|
||||
AnalogCustomization.name = name;
|
||||
AnalogCustomization.offset = offset;
|
||||
AnalogCustomization.factor = factor;
|
||||
AnalogCustomization.uom = uom;
|
||||
AnalogCustomization.type = type;
|
||||
LOG_DEBUG(F("Customizing existing analog sensor ID %d"), id);
|
||||
}
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
}
|
||||
}
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
},
|
||||
"local");
|
||||
|
||||
// if the sensor exists and we're using HA, delete the old HA record
|
||||
if (found_sensor && Mqtt::ha_enabled()) {
|
||||
remove_ha_topic(id); // id is the GPIO
|
||||
}
|
||||
|
||||
// we didn't find it, it's new, so create and store it
|
||||
if (!found_sensor) {
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
AnalogCustomization newSensor = AnalogCustomization();
|
||||
newSensor.id = id;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
newSensor.factor = factor;
|
||||
newSensor.uom = uom;
|
||||
newSensor.type = type;
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG(F("Adding new customization for analog sensor ID %d"), id);
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
},
|
||||
"local");
|
||||
}
|
||||
|
||||
// reloads the sensors in the customizations file into the sensors list
|
||||
reload();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// check to see if values have been updated
|
||||
bool AnalogSensor::updated_values() {
|
||||
if (changed_) {
|
||||
changed_ = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// publish a single sensor to MQTT
|
||||
void AnalogSensor::publish_sensor(Sensor sensor) {
|
||||
if (Mqtt::publish_single()) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(analogsensor)).c_str(), sensor.name().c_str());
|
||||
char payload[10];
|
||||
Mqtt::publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as floats
|
||||
}
|
||||
}
|
||||
|
||||
// send empty config topic to remove the entry from HA
|
||||
void AnalogSensor::remove_ha_topic(const uint8_t id) {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG(F("Removing HA config for analog sensor ID %d"), id);
|
||||
#endif
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%d/config", Mqtt::base().c_str(), id);
|
||||
Mqtt::publish_ha(topic);
|
||||
}
|
||||
|
||||
// send all sensor values as a JSON package to MQTT
|
||||
void AnalogSensor::publish_values(const bool force) {
|
||||
uint8_t num_sensors = sensors_.size();
|
||||
|
||||
if (num_sensors == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force && Mqtt::publish_single()) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
// return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(120 * num_sensors);
|
||||
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() != AnalogType::NOTUSED) {
|
||||
if (Mqtt::is_nested() || Mqtt::ha_enabled()) {
|
||||
// nested
|
||||
char s[10];
|
||||
JsonObject dataSensor = doc.createNestedObject(Helpers::smallitoa(s, sensor.id()));
|
||||
dataSensor["name"] = sensor.name();
|
||||
switch (sensor.type()) {
|
||||
case AnalogType::COUNTER:
|
||||
dataSensor["count"] = (uint16_t)sensor.value(); // convert to integer
|
||||
break;
|
||||
case AnalogType::ADC:
|
||||
dataSensor["value"] = (float)sensor.value(); // float
|
||||
break;
|
||||
case AnalogType::DIGITAL_IN:
|
||||
default:
|
||||
dataSensor["value"] = (uint8_t)sensor.value(); // convert to char for 1 or 0
|
||||
break;
|
||||
}
|
||||
|
||||
// create HA config
|
||||
if (Mqtt::ha_enabled()) {
|
||||
if (!sensor.ha_registered || force) {
|
||||
LOG_DEBUG(F("Recreating HA config for analog sensor ID %d"), sensor.id());
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
char str[50];
|
||||
snprintf(str, sizeof(str), "{{value_json['%d'].value}}", sensor.id());
|
||||
config["val_tpl"] = str;
|
||||
|
||||
snprintf(str, sizeof(str), "Analog Sensor %s", sensor.name().c_str());
|
||||
config["name"] = str;
|
||||
|
||||
snprintf(str, sizeof(str), "analogsensor_%d", sensor.id());
|
||||
config["uniq_id"] = str;
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%d/config", Mqtt::base().c_str(), sensor.id());
|
||||
|
||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||
|
||||
sensor.ha_registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// not nested
|
||||
doc[sensor.name()] = sensor.value();
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::publish(F("analogsensor_data"), doc.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
|
||||
// called from emsesp.cpp, similar to the emsdevice->get_value_info
|
||||
// searches by name
|
||||
bool AnalogSensor::get_value_info(JsonObject & output, const char * cmd, const int8_t id) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (strcmp(cmd, sensor.name().c_str()) == 0) {
|
||||
output["id"] = sensor.id();
|
||||
output["name"] = sensor.name();
|
||||
output["type"] = sensor.type();
|
||||
output["uom"] = sensor.uom();
|
||||
output["offset"] = sensor.offset();
|
||||
output["factor"] = sensor.factor();
|
||||
output["value"] = sensor.value();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// creates JSON doc from values
|
||||
// returns false if there are no sensors
|
||||
bool AnalogSensor::command_info(const char * value, const int8_t id, JsonObject & output) {
|
||||
if (sensors_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (id == -1) { // show number and id
|
||||
JsonObject dataSensor = output.createNestedObject(sensor.name());
|
||||
dataSensor["id"] = sensor.id();
|
||||
dataSensor["name"] = sensor.name();
|
||||
dataSensor["type"] = sensor.type();
|
||||
dataSensor["uom"] = sensor.uom();
|
||||
dataSensor["offset"] = sensor.offset();
|
||||
dataSensor["factor"] = sensor.factor();
|
||||
dataSensor["value"] = sensor.value();
|
||||
} else {
|
||||
output[sensor.name()] = sensor.value();
|
||||
}
|
||||
}
|
||||
|
||||
return (output.size() > 0);
|
||||
}
|
||||
|
||||
// this creates the sensor, initializing everything
|
||||
AnalogSensor::Sensor::Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type)
|
||||
: id_(id)
|
||||
, name_(name)
|
||||
, offset_(offset)
|
||||
, factor_(factor)
|
||||
, uom_(uom)
|
||||
, type_(type) {
|
||||
value_ = 0; // init value to 0 always
|
||||
}
|
||||
|
||||
// returns name of the analog sensor or creates one if its empty
|
||||
std::string AnalogSensor::Sensor::name() const {
|
||||
if (name_.empty()) {
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "Analog Sensor GPIO%d", id_);
|
||||
return name;
|
||||
}
|
||||
return name_;
|
||||
}
|
||||
|
||||
// hard coded tests
|
||||
#ifdef EMSESP_DEBUG
|
||||
void AnalogSensor::test() {
|
||||
// Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type);
|
||||
sensors_.emplace_back(36, "test12", 0, 0.1, 17, AnalogType::ADC);
|
||||
sensors_.back().set_value(12.4);
|
||||
|
||||
sensors_.emplace_back(37, "test13", 0, 0, 0, AnalogType::DIGITAL_IN);
|
||||
sensors_.back().set_value(13);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
180
src/analogsensor.h
Normal file
180
src/analogsensor.h
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 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_ANALOGSENSOR_H
|
||||
#define EMSESP_ANALOGSENSOR_H
|
||||
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
#include "console.h"
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include "driver/adc.h"
|
||||
#include <esp_bt.h>
|
||||
#endif
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class AnalogSensor {
|
||||
public:
|
||||
class Sensor {
|
||||
public:
|
||||
Sensor(const uint8_t id, const std::string & name, const uint16_t offset, const float factor, const uint8_t uom, const int8_t type);
|
||||
~Sensor() = default;
|
||||
|
||||
void set_offset(const uint16_t offset) {
|
||||
offset_ = offset;
|
||||
}
|
||||
|
||||
std::string name() const;
|
||||
void set_name(const std::string & name) {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
uint8_t id() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
float value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void set_value(float value) {
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
float factor() const {
|
||||
return factor_;
|
||||
}
|
||||
|
||||
void set_factor(float factor) {
|
||||
factor_ = factor;
|
||||
}
|
||||
|
||||
uint16_t offset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
void set_uom(const uint8_t uom) {
|
||||
uom_ = uom;
|
||||
}
|
||||
|
||||
uint8_t uom() const {
|
||||
return uom_;
|
||||
}
|
||||
|
||||
int8_t type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
void set_type(int8_t type) {
|
||||
type_ = type;
|
||||
}
|
||||
|
||||
bool ha_registered = false;
|
||||
|
||||
uint16_t analog_ = 0; // ADC - average value
|
||||
uint32_t sum_ = 0; // ADC - rolling sum
|
||||
uint16_t last_reading_ = 0; // IO COUNTER & ADC - last reading
|
||||
uint32_t polltime_ = 0; // digital IO & COUNTER debounce time
|
||||
int poll_ = 0;
|
||||
|
||||
private:
|
||||
uint8_t id_;
|
||||
std::string name_;
|
||||
uint16_t offset_;
|
||||
float factor_;
|
||||
uint8_t uom_;
|
||||
float value_; // float because of the factor is a float
|
||||
int8_t type_;
|
||||
};
|
||||
|
||||
AnalogSensor() = default;
|
||||
~AnalogSensor() = default;
|
||||
|
||||
enum AnalogType : int8_t {
|
||||
MARK_DELETED = -1, // mark for deletion
|
||||
NOTUSED, // 0 - disabled
|
||||
DIGITAL_IN, // 1
|
||||
COUNTER, // 2
|
||||
ADC // 3
|
||||
};
|
||||
|
||||
void start();
|
||||
void loop();
|
||||
void publish_sensor(Sensor sensor);
|
||||
void publish_values(const bool force);
|
||||
void reload();
|
||||
bool updated_values();
|
||||
|
||||
// return back reference to the sensor list, used by other classes
|
||||
const std::vector<Sensor> sensors() const {
|
||||
return sensors_;
|
||||
}
|
||||
|
||||
uint32_t reads() {
|
||||
return sensorreads_;
|
||||
}
|
||||
|
||||
uint32_t fails() {
|
||||
return sensorfails_;
|
||||
}
|
||||
|
||||
bool analog_enabled() {
|
||||
return (analog_enabled_);
|
||||
}
|
||||
|
||||
bool have_sensors() {
|
||||
return (sensors_.size() > 0);
|
||||
}
|
||||
|
||||
size_t no_sensors() {
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
bool update(uint8_t id, const std::string & name, uint16_t offset, float factor, uint8_t uom, int8_t type);
|
||||
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
void test();
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint8_t MAX_SENSORS = 20;
|
||||
static constexpr uint32_t MEASURE_ANALOG_INTERVAL = 500;
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void remove_ha_topic(const uint8_t id);
|
||||
bool command_counter(const char * value, const int8_t id);
|
||||
void measure();
|
||||
bool command_info(const char * value, const int8_t id, JsonObject & output);
|
||||
|
||||
std::vector<Sensor> sensors_; // our list of sensors
|
||||
|
||||
bool analog_enabled_;
|
||||
bool changed_ = false;
|
||||
uint32_t sensorfails_ = 0;
|
||||
uint32_t sensorreads_ = 0;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -52,7 +52,9 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(EMSESP_USE_SERIAL)
|
||||
// Serial.println(p.path().c_str()); // dump paths, for debugging
|
||||
#endif
|
||||
|
||||
// re-calculate new path
|
||||
// if there is only a path (URL) and no body then error!
|
||||
@@ -107,7 +109,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
command_p = parse_command_string(command_p, id_n);
|
||||
if (command_p == nullptr) {
|
||||
// handle dead endpoints like api/system or api/boiler
|
||||
// default to 'info' for SYSTEM and DALLASENSOR, the other devices to 'values' for shortname version
|
||||
// default to 'info' for SYSTEM, DALLASENSOR and ANALOGSENSOR, the other devices to 'values' for shortname version
|
||||
if (num_paths < 3) {
|
||||
if (device_type < EMSdevice::DeviceType::BOILER) {
|
||||
command_p = "info";
|
||||
@@ -126,7 +128,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
id_n = input["hc"];
|
||||
} else if (input.containsKey("wwc")) {
|
||||
id_n = input["wwc"];
|
||||
id_n += 7; // wwc1 has id 8
|
||||
id_n += 8; // wwc1 has id 9
|
||||
} else if (input.containsKey("id")) {
|
||||
id_n = input["id"];
|
||||
}
|
||||
@@ -146,7 +148,7 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
|
||||
return_code = Command::call(device_type, command_p, data.as<const char *>(), is_admin, id_n, output);
|
||||
} else if (data.is<int>()) {
|
||||
char data_str[10];
|
||||
return_code = Command::call(device_type, command_p, Helpers::itoa(data_str, (int16_t)data.as<int>()), is_admin, id_n, output);
|
||||
return_code = Command::call(device_type, command_p, Helpers::itoa((int16_t)data.as<int>(), data_str), is_admin, id_n, output);
|
||||
} else if (data.is<float>()) {
|
||||
char data_str[10];
|
||||
return_code = Command::call(device_type, command_p, Helpers::render_value(data_str, (float)data.as<float>(), 2), is_admin, id_n, output);
|
||||
@@ -211,7 +213,7 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
if (!strncmp(command, "hc", 2) && start_pos == 4) {
|
||||
id = command[start_pos - 2] - '0';
|
||||
} else if (!strncmp(command, "wwc", 3) && start_pos == 5) {
|
||||
id = command[start_pos - 2] - '0' + 7; // wwc1 has id 8
|
||||
id = command[start_pos - 2] - '0' + 8; // wwc1 has id 9
|
||||
} else {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG(F("[DEBUG] Command parse error, unknown hc/wwc in %s"), command_s);
|
||||
@@ -245,42 +247,48 @@ uint8_t Command::call(const uint8_t device_type, const char * cmd, const char *
|
||||
|
||||
// check if its a call to and end-point to a device, i.e. has no value
|
||||
// except for system commands as this is a special device without any queryable entities (device values)
|
||||
// exclude SYSTEM and DALLASSENSOR
|
||||
|
||||
if ((device_type >= EMSdevice::DeviceType::BOILER) && (!value || !strlen(value))) {
|
||||
// exclude SYSTEM
|
||||
if ((device_type > EMSdevice::DeviceType::SYSTEM) && (!value || !strlen(value))) {
|
||||
if (!cf || !cf->cmdfunction_json_) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_INFO(F("[DEBUG] Calling %s command '%s' to retrieve values"), dname.c_str(), cmd);
|
||||
LOG_DEBUG(F("[DEBUG] Calling %s command '%s' to retrieve values"), dname.c_str(), cmd);
|
||||
#endif
|
||||
return EMSESP::get_device_value_info(output, cmd, id, device_type) ? CommandRet::OK : CommandRet::ERROR; // entity = cmd
|
||||
}
|
||||
}
|
||||
|
||||
// check if we have a matching command
|
||||
if (cf) {
|
||||
// we have a matching command
|
||||
if ((value == nullptr) || !strlen(value)) {
|
||||
LOG_INFO(F("Calling %s command '%s'"), dname.c_str(), cmd);
|
||||
} else if (id == -1) {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is default"), dname.c_str(), cmd, value);
|
||||
} else {
|
||||
LOG_INFO(F("Calling %s command '%s', value %s, id is %d"), dname.c_str(), cmd, value, id);
|
||||
}
|
||||
|
||||
// check permissions
|
||||
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !is_admin) {
|
||||
output["message"] = "authentication failed";
|
||||
return CommandRet::NOT_ALLOWED; // command not allowed
|
||||
}
|
||||
|
||||
// call the function
|
||||
if (value == nullptr) {
|
||||
if (EMSESP::system_.readonly_mode()) {
|
||||
LOG_INFO(F("[readonly] Calling command '%s/%s' (%s)"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str());
|
||||
} else {
|
||||
LOG_DEBUG(F("Calling command '%s/%s' (%s)"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str());
|
||||
}
|
||||
} else {
|
||||
if (EMSESP::system_.readonly_mode()) {
|
||||
LOG_INFO(F("[readonly] Calling command '%s/%s' (%s) with value %s"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str(), value);
|
||||
} else {
|
||||
LOG_DEBUG(F("Calling command '%s/%s' (%s) with value %s"), dname.c_str(), cmd, read_flash_string(cf->description_).c_str(), value);
|
||||
}
|
||||
}
|
||||
|
||||
// call the function baesed on type
|
||||
if (cf->cmdfunction_json_) {
|
||||
return_code = ((cf->cmdfunction_json_)(value, id, output)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
|
||||
if (cf->cmdfunction_) {
|
||||
return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
|
||||
// report error if call failed
|
||||
// report back
|
||||
if (return_code != CommandRet::OK) {
|
||||
return message(return_code, "callback function failed", output);
|
||||
}
|
||||
@@ -417,7 +425,7 @@ void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbo
|
||||
}
|
||||
shell.print(COLOR_BRIGHT_CYAN);
|
||||
if (cf.has_flags(MQTT_SUB_FLAG_WW)) {
|
||||
shell.print(EMSdevice::tag_to_string(TAG_DEVICE_DATA_WW));
|
||||
shell.print(EMSdevice::tag_to_string(DeviceValueTAG::TAG_DEVICE_DATA_WW));
|
||||
shell.print(' ');
|
||||
}
|
||||
shell.print(read_flash_string(cf.description_));
|
||||
@@ -447,7 +455,11 @@ bool Command::device_has_commands(const uint8_t device_type) {
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::DALLASSENSOR) {
|
||||
return (EMSESP::sensor_devices().size() != 0);
|
||||
return (EMSESP::dallassensor_.have_sensors());
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::ANALOGSENSOR) {
|
||||
return (EMSESP::analogsensor_.have_sensors());
|
||||
}
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
@@ -464,12 +476,16 @@ bool Command::device_has_commands(const uint8_t device_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// list sensors and EMS devices
|
||||
void Command::show_devices(uuid::console::Shell & shell) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM).c_str());
|
||||
|
||||
if (EMSESP::have_sensors()) {
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR).c_str());
|
||||
}
|
||||
if (EMSESP::analogsensor_.have_sensors()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR).c_str());
|
||||
}
|
||||
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
@@ -494,14 +510,21 @@ void Command::show_all(uuid::console::Shell & shell) {
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM, true);
|
||||
|
||||
// show sensor
|
||||
if (EMSESP::have_sensors()) {
|
||||
// show sensors
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR).c_str());
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::DALLASSENSOR, true);
|
||||
}
|
||||
if (EMSESP::analogsensor_.have_sensors()) {
|
||||
shell.print(COLOR_BOLD_ON);
|
||||
shell.print(COLOR_YELLOW);
|
||||
shell.printf(" %s: ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR).c_str());
|
||||
shell.print(COLOR_RESET);
|
||||
show(shell, EMSdevice::DeviceType::ANALOGSENSOR, true);
|
||||
}
|
||||
|
||||
// do this in the order of factory classes to keep a consistent order when displaying
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
|
||||
@@ -19,18 +19,10 @@
|
||||
#ifndef EMSESP_COMMAND_H_
|
||||
#define EMSESP_COMMAND_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "console.h"
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
namespace emsesp {
|
||||
@@ -38,7 +30,7 @@ namespace emsesp {
|
||||
// mqtt flags for command subscriptions
|
||||
enum CommandFlag : uint8_t {
|
||||
MQTT_SUB_FLAG_DEFAULT = 0, // 0 no flags set, always subscribe to MQTT
|
||||
MQTT_SUB_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC4
|
||||
MQTT_SUB_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC8
|
||||
MQTT_SUB_FLAG_WWC = (1 << 1), // 2 TAG_WWC1 - TAG_WWC4
|
||||
MQTT_SUB_FLAG_NOSUB = (1 << 2), // 4
|
||||
MQTT_SUB_FLAG_WW = (1 << 3), // 8 TAG_DEVICE_DATA_WW
|
||||
|
||||
141
src/console.cpp
141
src/console.cpp
@@ -76,8 +76,9 @@ void EMSESPShell::display_banner() {
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { console_hostname_ = networkSettings.hostname.c_str(); });
|
||||
|
||||
if (console_hostname_.empty()) {
|
||||
console_hostname_.resize(16, '\0');
|
||||
snprintf(&console_hostname_[0], console_hostname_.capacity() + 1, "ems-esp");
|
||||
console_hostname_ = "ems-esp";
|
||||
// console_hostname_.resize(16, '\0');
|
||||
// snprintf(&console_hostname_[0], console_hostname_.capacity() + 1, "ems-esp");
|
||||
}
|
||||
|
||||
// load the list of commands
|
||||
@@ -249,7 +250,7 @@ void EMSESPShell::add_console_commands() {
|
||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||
|
||||
if (!EMSESP::valid_device(device_id)) {
|
||||
shell.printfln(F("Invalid device ID"));
|
||||
shell.printfln(F("Invalid deviceID"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -355,7 +356,7 @@ void EMSESPShell::add_console_commands() {
|
||||
if (watch_id > 0x80) {
|
||||
shell.printfln(F("Filtering only telegrams that match a telegram type of 0x%02X"), watch_id);
|
||||
} else if (watch_id != WATCH_ID_NONE) {
|
||||
shell.printfln(F("Filtering only telegrams that match a device ID or telegram type of 0x%02X"), watch_id);
|
||||
shell.printfln(F("Filtering only telegrams that match a deviceID or telegram type of 0x%02X"), watch_id);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -433,6 +434,7 @@ void EMSESPShell::add_console_commands() {
|
||||
std::vector<std::string> devices_list;
|
||||
devices_list.emplace_back(EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SYSTEM));
|
||||
devices_list.emplace_back(EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::DALLASSENSOR));
|
||||
devices_list.emplace_back(EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR));
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
if (Command::device_has_commands(device_class.first)) {
|
||||
devices_list.emplace_back(EMSdevice::device_type_2_device_name(device_class.first));
|
||||
@@ -491,20 +493,21 @@ void Console::enter_custom_context(Shell & shell, unsigned int context) {
|
||||
// each custom context has the common commands like log, help, exit, su etc
|
||||
void Console::load_standard_commands(unsigned int context) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
// create commands test and t
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::USER,
|
||||
flash_string_vector{F("test")},
|
||||
flash_string_vector{F_(name_optional)},
|
||||
flash_string_vector{F_(name_optional), F_(data_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
Test::run_test(shell, "default");
|
||||
} else {
|
||||
} else if (arguments.size() == 1) {
|
||||
Test::run_test(shell, arguments.front());
|
||||
} else {
|
||||
Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str());
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
EMSESPShell::commands->add_command(context, CommandFlags::USER, flash_string_vector{F("t")}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Test::run_test(shell, "default");
|
||||
});
|
||||
@@ -696,6 +699,16 @@ void Console::load_system_commands(unsigned int context) {
|
||||
shell.println("Use `wifi reconnect` to save and apply the new settings");
|
||||
});
|
||||
|
||||
// added by mvdp
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F("mqtt"), F("subscribe")},
|
||||
flash_string_vector{F("<topic>")},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Mqtt::subscribe(arguments.front());
|
||||
shell.println("subscribing");
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(wifi), F_(password)},
|
||||
@@ -723,72 +736,33 @@ void Console::load_system_commands(unsigned int context) {
|
||||
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(sensorname)},
|
||||
flash_string_vector{F_(sensorid_optional), F_(name_optional), F_(offset_optional)},
|
||||
flash_string_vector{F_(set), F_(board_profile)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size() == 0) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (!settings.sensor[i].id.isEmpty()) {
|
||||
shell.print(settings.sensor[i].id);
|
||||
shell.print(" : ");
|
||||
shell.print(settings.sensor[i].name);
|
||||
shell.print(" : ");
|
||||
char buf[10];
|
||||
shell.println(Helpers::render_value(buf, settings.sensor[i].offset, 10));
|
||||
}
|
||||
}
|
||||
});
|
||||
std::vector<int8_t> data; // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, OLIMEXPOE, CUSTOM)"));
|
||||
return;
|
||||
}
|
||||
if (arguments.size() == 1) {
|
||||
EMSESP::dallassensor_.update(arguments.front().c_str(), "", 0);
|
||||
// shell.println(EMSESP::dallassensor_.get_name(arguments.front().c_str()));
|
||||
return;
|
||||
}
|
||||
int16_t offset = 0;
|
||||
float val;
|
||||
if (arguments.size() == 2) {
|
||||
if (Helpers::value2float(arguments.back().c_str(), val)) {
|
||||
offset = (10 * val);
|
||||
EMSESP::dallassensor_.update(arguments.front().c_str(), "", offset);
|
||||
return;
|
||||
}
|
||||
} else if (arguments.size() == 3) {
|
||||
if (Helpers::value2float(arguments.back().c_str(), val)) {
|
||||
offset = (10 * val);
|
||||
}
|
||||
}
|
||||
EMSESP::dallassensor_.update(arguments.front().c_str(), arguments[1].c_str(), offset);
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
settings.phy_type = data[5];
|
||||
settings.eth_power = data[6]; // can be -1
|
||||
settings.eth_phy_addr = data[7];
|
||||
settings.eth_clock_mode = data[8];
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.printfln("Loaded board profile %s", board_profile.c_str());
|
||||
EMSESP::system_.network_init(true);
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(
|
||||
context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(set), F_(board_profile)},
|
||||
flash_string_vector{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||
std::string board_profile = Helpers::toUpper(arguments.front());
|
||||
if (!EMSESP::system_.load_board_profile(data, board_profile)) {
|
||||
shell.println(F("Invalid board profile (S32, E32, MH-ET, NODEMCU, OLIMEX, CUSTOM)"));
|
||||
return;
|
||||
}
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
settings.board_profile = board_profile.c_str();
|
||||
settings.led_gpio = data[0];
|
||||
settings.dallas_gpio = data[1];
|
||||
settings.rx_gpio = data[2];
|
||||
settings.tx_gpio = data[3];
|
||||
settings.pbutton_gpio = data[4];
|
||||
settings.phy_type = data[5];
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
shell.printfln("Loaded board profile %s (%d,%d,%d,%d,%d,%d)", board_profile.c_str(), data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
EMSESP::system_.network_init(true);
|
||||
});
|
||||
EMSESPShell::commands->add_command(context,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(show), F_(users)},
|
||||
@@ -853,7 +827,7 @@ EMSESPStreamConsole::EMSESPStreamConsole(Stream & stream, const IPAddress & addr
|
||||
ptys_[pty_] = true;
|
||||
}
|
||||
|
||||
snprintf(text.data(), text.size(), "pty%u", pty_);
|
||||
snprintf(text.data(), text.size(), "pty%u", (uint16_t)pty_);
|
||||
name_ = text.data();
|
||||
#ifndef EMSESP_STANDALONE
|
||||
logger().info(F("Allocated console %s for connection from [%s]:%u"), name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
|
||||
@@ -876,9 +850,12 @@ std::string EMSESPStreamConsole::console_name() {
|
||||
|
||||
// Start up telnet and logging
|
||||
// Log order is off, err, warning, notice, info, debug, trace, all
|
||||
void Console::start() {
|
||||
void Console::start(bool telnet_enabled) {
|
||||
telnet_enabled_ = telnet_enabled;
|
||||
|
||||
// Serial Console
|
||||
shell = std::make_shared<EMSESPStreamConsole>(Serial, true);
|
||||
shell->maximum_log_messages(100); // default was 50
|
||||
shell->maximum_log_messages(100);
|
||||
shell->start();
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
@@ -889,13 +866,15 @@ void Console::start() {
|
||||
shell->add_flags(CommandFlags::ADMIN); // always start in su/admin mode when running tests
|
||||
#endif
|
||||
|
||||
// start the telnet service
|
||||
// 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
|
||||
// start the telnet service
|
||||
// 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
|
||||
#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
|
||||
if (telnet_enabled) {
|
||||
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
|
||||
|
||||
// turn watch off in case it was still set in the last session
|
||||
@@ -907,7 +886,9 @@ void Console::loop() {
|
||||
uuid::loop();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
telnet_.loop();
|
||||
if (telnet_enabled_) {
|
||||
telnet_.loop();
|
||||
}
|
||||
#endif
|
||||
|
||||
Shell::loop_all();
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
#ifndef EMSESP_CONSOLE_H
|
||||
#define EMSESP_CONSOLE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <uuid/console.h>
|
||||
#include <uuid/log.h>
|
||||
|
||||
@@ -101,9 +96,10 @@ class EMSESPShell : virtual public uuid::console::Shell {
|
||||
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;
|
||||
|
||||
// std::string context_text() override;
|
||||
// bool exit_context() override;
|
||||
|
||||
private:
|
||||
@@ -132,13 +128,16 @@ class EMSESPStreamConsole : public uuid::console::StreamConsole, public EMSESPSh
|
||||
class Console {
|
||||
public:
|
||||
void loop();
|
||||
void start();
|
||||
void start(bool telnet_enabled = true);
|
||||
|
||||
uuid::log::Level log_level();
|
||||
|
||||
// static void enter_custom_context(Shell & shell, unsigned int context);
|
||||
static void load_standard_commands(unsigned int context);
|
||||
static void load_system_commands(unsigned int context);
|
||||
|
||||
private:
|
||||
bool telnet_enabled_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -35,36 +35,38 @@ uuid::log::Logger DallasSensor::logger_{F_(dallassensor), uuid::log::Facility::D
|
||||
void DallasSensor::start() {
|
||||
reload();
|
||||
|
||||
// disabled if dallas gpio is 0
|
||||
if (dallas_gpio_) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bus_.begin(dallas_gpio_);
|
||||
#endif
|
||||
// API calls
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(info),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
|
||||
F_(info_cmd));
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(commands),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
|
||||
F_(commands_cmd));
|
||||
if (!dallas_gpio_) {
|
||||
return; // disabled if dallas gpio is 0
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bus_.begin(dallas_gpio_);
|
||||
LOG_INFO(F("Starting Dallas sensor service"));
|
||||
#endif
|
||||
|
||||
// Add API calls
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(info),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_info(value, id, output); },
|
||||
F_(info_cmd));
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::DALLASSENSOR,
|
||||
F_(commands),
|
||||
[&](const char * value, const int8_t id, JsonObject & output) { return command_commands(value, id, output); },
|
||||
F_(commands_cmd));
|
||||
}
|
||||
|
||||
// load the MQTT settings
|
||||
// load settings
|
||||
void DallasSensor::reload() {
|
||||
// load the service settings
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
dallas_gpio_ = settings.dallas_gpio;
|
||||
parasite_ = settings.dallas_parasite;
|
||||
dallas_format_ = settings.dallas_format;
|
||||
dallas_gpio_ = settings.dallas_gpio;
|
||||
parasite_ = settings.dallas_parasite;
|
||||
});
|
||||
|
||||
if (Mqtt::ha_enabled()) {
|
||||
for (uint8_t i = 0; i < MAX_SENSORS; registered_ha_[i++] = false)
|
||||
;
|
||||
for (auto & sensor : sensors_) {
|
||||
sensor.ha_registered = false; // force HA configs to be re-created
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +95,9 @@ void DallasSensor::loop() {
|
||||
sensorfails_++;
|
||||
if (++scanretry_ > SCAN_MAX) { // every 30 sec
|
||||
scanretry_ = 0;
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_ERROR(F("Bus reset failed"));
|
||||
#endif
|
||||
for (auto & sensor : sensors_) {
|
||||
sensor.temperature_c = EMS_VALUE_SHORT_NOTSET;
|
||||
}
|
||||
@@ -110,13 +114,17 @@ void DallasSensor::loop() {
|
||||
bus_.reset_search();
|
||||
state_ = State::SCANNING;
|
||||
} else if (time_now - last_activity_ > READ_TIMEOUT_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_WARNING(F("Dallas sensor read timeout"));
|
||||
#endif
|
||||
state_ = State::IDLE;
|
||||
sensorfails_++;
|
||||
}
|
||||
} else if (state_ == State::SCANNING) {
|
||||
if (time_now - last_activity_ > SCAN_TIMEOUT_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_ERROR(F("Dallas sensor scan timeout"));
|
||||
#endif
|
||||
state_ = State::IDLE;
|
||||
sensorfails_++;
|
||||
} else {
|
||||
@@ -136,12 +144,13 @@ void DallasSensor::loop() {
|
||||
t = get_temperature_c(addr);
|
||||
if ((t >= -550) && (t <= 1250)) {
|
||||
sensorreads_++;
|
||||
// check if we have this sensor already
|
||||
// check if we already have this sensor
|
||||
bool found = false;
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.id() == get_id(addr)) {
|
||||
t += sensor.offset();
|
||||
if (t != sensor.temperature_c) {
|
||||
publish_sensor(sensor);
|
||||
changed_ |= true;
|
||||
}
|
||||
sensor.temperature_c = t;
|
||||
@@ -150,12 +159,17 @@ void DallasSensor::loop() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// add new sensor
|
||||
// add new sensor. this will create the id string, empty name and offset
|
||||
if (!found && (sensors_.size() < (MAX_SENSORS - 1))) {
|
||||
sensors_.emplace_back(addr);
|
||||
sensors_.back().temperature_c = t + sensors_.back().offset();
|
||||
sensors_.back().read = true;
|
||||
changed_ = true;
|
||||
// look in the customization service for an optional alias or offset for that particular sensor
|
||||
sensors_.back().apply_customization();
|
||||
publish_sensor(sensors_.back()); // call publish single
|
||||
// sort the sensors based on name
|
||||
std::sort(sensors_.begin(), sensors_.end(), [](const Sensor & a, const Sensor & b) { return a.name() < b.name(); });
|
||||
}
|
||||
} else {
|
||||
sensorfails_++;
|
||||
@@ -164,12 +178,12 @@ void DallasSensor::loop() {
|
||||
|
||||
default:
|
||||
sensorfails_++;
|
||||
LOG_ERROR(F("Unknown dallas sensor %s"), Sensor(addr).to_string().c_str());
|
||||
LOG_ERROR(F("Unknown dallas sensor %s"), Sensor(addr).id_str().c_str());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sensorfails_++;
|
||||
LOG_ERROR(F("Invalid dallas sensor %s"), Sensor(addr).to_string().c_str());
|
||||
LOG_ERROR(F("Invalid dallas sensor %s"), Sensor(addr).id_str().c_str());
|
||||
}
|
||||
} else {
|
||||
if (!parasite_) {
|
||||
@@ -187,7 +201,7 @@ void DallasSensor::loop() {
|
||||
scancnt_ = 0;
|
||||
} else if (scancnt_ == SCAN_START + 1) { // startup
|
||||
firstscan_ = sensors_.size();
|
||||
LOG_DEBUG(F("Adding %d dallas sensor(s) from first scan"), firstscan_);
|
||||
// LOG_DEBUG(F("Adding %d dallas sensor(s) from first scan"), firstscan_);
|
||||
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
|
||||
scancnt_ = SCAN_START;
|
||||
sensors_.clear(); // restart scaning and clear to get correct numbering
|
||||
@@ -213,7 +227,7 @@ bool DallasSensor::temperature_convert_complete() {
|
||||
int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!bus_.reset()) {
|
||||
LOG_ERROR(F("Bus reset failed before reading scratchpad from %s"), Sensor(addr).to_string().c_str());
|
||||
LOG_ERROR(F("Bus reset failed before reading scratchpad from %s"), Sensor(addr).id_str().c_str());
|
||||
return EMS_VALUE_SHORT_NOTSET;
|
||||
}
|
||||
YIELD;
|
||||
@@ -225,7 +239,7 @@ int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
YIELD;
|
||||
|
||||
if (!bus_.reset()) {
|
||||
LOG_ERROR(F("Bus reset failed after reading scratchpad from %s"), Sensor(addr).to_string().c_str());
|
||||
LOG_ERROR(F("Bus reset failed after reading scratchpad from %s"), Sensor(addr).id_str().c_str());
|
||||
return EMS_VALUE_SHORT_NOTSET;
|
||||
}
|
||||
YIELD;
|
||||
@@ -241,7 +255,7 @@ int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
scratchpad[6],
|
||||
scratchpad[7],
|
||||
scratchpad[8],
|
||||
Sensor(addr).to_string().c_str());
|
||||
Sensor(addr).id_str().c_str());
|
||||
return EMS_VALUE_SHORT_NOTSET;
|
||||
}
|
||||
|
||||
@@ -273,162 +287,52 @@ int16_t DallasSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::vector<DallasSensor::Sensor> DallasSensor::sensors() const {
|
||||
return sensors_;
|
||||
}
|
||||
// update dallas information name and offset
|
||||
bool DallasSensor::update(const std::string & id_str, const std::string & name, int16_t offset) {
|
||||
// find the sensor
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.id_str() == id_str) {
|
||||
// found a match, update the sensor object
|
||||
|
||||
// skip crc from id.
|
||||
DallasSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
: 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])) {
|
||||
}
|
||||
|
||||
uint64_t DallasSensor::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]));
|
||||
}
|
||||
|
||||
uint64_t DallasSensor::Sensor::id() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
std::string DallasSensor::Sensor::id_string() const {
|
||||
std::string str(20, '\0');
|
||||
snprintf(&str[0],
|
||||
str.capacity() + 1,
|
||||
"%02X-%04X-%04X-%04X",
|
||||
(unsigned int)(id_ >> 48) & 0xFF,
|
||||
(unsigned int)(id_ >> 32) & 0xFFFF,
|
||||
(unsigned int)(id_ >> 16) & 0xFFFF,
|
||||
(unsigned int)(id_)&0xFFFF);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string DallasSensor::Sensor::to_string(const bool name) const {
|
||||
std::string str = id_string();
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
if (settings.dallas_format == Dallas_Format::NAME || name) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) {
|
||||
str = settings.sensor[i].name.c_str();
|
||||
}
|
||||
// if HA is enabled then delete the old record
|
||||
if (Mqtt::ha_enabled()) {
|
||||
remove_ha_topic(id_str);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return str;
|
||||
}
|
||||
sensor.set_name(name);
|
||||
sensor.set_offset(offset);
|
||||
|
||||
int16_t DallasSensor::Sensor::offset() const {
|
||||
std::string str = id_string();
|
||||
int16_t offset = 0; // default value
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (strcmp(settings.sensor[i].id.c_str(), str.c_str()) == 0) {
|
||||
offset = settings.sensor[i].offset;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// if HA enabled with MQTT Discovery, delete the old config entry by sending an empty topic
|
||||
// if we're using the name in the MQTT topic name (Dallas format = NAME)
|
||||
void DallasSensor::delete_ha_config(uint8_t index, const char * name) {
|
||||
if (Mqtt::ha_enabled() && (dallas_format_ == Dallas_Format::NAME)) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string topicname = name;
|
||||
std::replace(topicname.begin(), topicname.end(), '-', '_');
|
||||
|
||||
snprintf(topic, sizeof(topic), "homeassistant/sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), topicname.c_str());
|
||||
Mqtt::publish(topic);
|
||||
registered_ha_[index] = false; // forces a recreate of the HA config topic
|
||||
}
|
||||
}
|
||||
|
||||
// update dallas information like name and offset
|
||||
bool DallasSensor::update(const char * idstr, const char * name, int16_t offset) {
|
||||
bool ok = false;
|
||||
char id[20];
|
||||
strlcpy(id, idstr, sizeof(id));
|
||||
|
||||
// check for number and convert to id
|
||||
if (strlen(id) > 0 && strlen(id) <= 2 && id[0] >= '1' && id[0] <= '9') {
|
||||
uint8_t no = atoi(idstr) - 1;
|
||||
if (no < sensors_.size()) {
|
||||
strlcpy(id, sensors_[no].id_string().c_str(), sizeof(id));
|
||||
}
|
||||
}
|
||||
|
||||
// check valid id
|
||||
if (strlen(id) != 17 || id[2] != '-' || id[7] != '-' || id[12] != '-') {
|
||||
LOG_WARNING(F("Invalid sensor id: %s"), id);
|
||||
return ok;
|
||||
}
|
||||
|
||||
EMSESP::webSettingsService.update(
|
||||
[&](WebSettings & settings) {
|
||||
// check for new name of stored id
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (strcmp(id, settings.sensor[i].id.c_str()) == 0) {
|
||||
if (strlen(name) == 0 && offset == 0) { // delete entry if name and offset is empty
|
||||
LOG_INFO(F("Deleting entry for sensor %s"), id);
|
||||
delete_ha_config(i, settings.sensor[i].name.c_str());
|
||||
settings.sensor[i].id = "";
|
||||
settings.sensor[i].name = "";
|
||||
settings.sensor[i].offset = 0;
|
||||
} else {
|
||||
char result[10];
|
||||
LOG_INFO(F("Renaming sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10));
|
||||
delete_ha_config(i, settings.sensor[i].name.c_str()); // remove old name in HA
|
||||
settings.sensor[i].name = (strlen(name) == 0) ? id : name;
|
||||
settings.sensor[i].offset = offset;
|
||||
// store the new name and offset in our configuration
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
// look it up to see if it exists
|
||||
bool found = false;
|
||||
for (auto & SensorCustomization : settings.sensorCustomizations) {
|
||||
if (SensorCustomization.id_str == id_str) {
|
||||
SensorCustomization.name = name;
|
||||
SensorCustomization.offset = offset;
|
||||
found = true;
|
||||
LOG_DEBUG(F("Customizing existing sensor ID %s"), id_str.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok = true;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
// check for free place
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
if (settings.sensor[i].id.isEmpty()) {
|
||||
settings.sensor[i].id = id;
|
||||
settings.sensor[i].name = (strlen(name) == 0) ? id : name;
|
||||
settings.sensor[i].offset = offset;
|
||||
char result[10];
|
||||
LOG_INFO(F("Adding sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10));
|
||||
ok = true;
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
// check if there is a unused id and overwrite it
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
bool found = false;
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (strcmp(sensor.id_string().c_str(), settings.sensor[i].id.c_str()) == 0) {
|
||||
found = true;
|
||||
if (!found) {
|
||||
SensorCustomization newSensor = SensorCustomization();
|
||||
newSensor.id_str = id_str;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
settings.sensorCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG(F("Adding new customization for sensor ID %s"), id_str.c_str());
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
char result[10];
|
||||
LOG_INFO(F("Renaming sensor ID %s to %s with offset %s"), id, name, Helpers::render_value(result, offset, 10));
|
||||
delete_ha_config(i, settings.sensor[i].name.c_str()); // remove old name in HA
|
||||
settings.sensor[i].id = id;
|
||||
settings.sensor[i].name = (strlen(name) == 0) ? id : name;
|
||||
settings.sensor[i].offset = offset;
|
||||
ok = true;
|
||||
sensor.ha_registered = false; // it's changed so we may need to recreate the HA config
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
}
|
||||
},
|
||||
"local");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR(F("No more empty sensor slots, remove one first"));
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
},
|
||||
"local");
|
||||
return ok;
|
||||
return true; // not found, nothing updated
|
||||
}
|
||||
|
||||
// check to see if values have been updated
|
||||
@@ -446,35 +350,73 @@ bool DallasSensor::command_commands(const char * value, const int8_t id, JsonObj
|
||||
}
|
||||
|
||||
// creates JSON doc from values
|
||||
// returns false if empty
|
||||
// e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03-5F","temp":23.30},"sensor2":{"id":"28-233D-9497-0C03-8B","temp":24.0}}
|
||||
// returns false if there are no sensors
|
||||
bool DallasSensor::command_info(const char * value, const int8_t id, JsonObject & output) {
|
||||
if (sensors_.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t i = 1; // sensor count
|
||||
for (const auto & sensor : sensors_) {
|
||||
char sensorID[10]; // sensor{1-n}
|
||||
snprintf(sensorID, 10, "sensor%d", i++);
|
||||
if (id == -1) { // show number and id
|
||||
JsonObject dataSensor = output.createNestedObject(sensorID);
|
||||
dataSensor["id"] = sensor.to_string();
|
||||
JsonObject dataSensor = output.createNestedObject(sensor.name());
|
||||
dataSensor["id_str"] = sensor.id_str();
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
} else { // show according to format
|
||||
if (dallas_format_ == Dallas_Format::NUMBER && Helpers::hasValue(sensor.temperature_c)) {
|
||||
output[sensorID] = (float)(sensor.temperature_c) / 10;
|
||||
} else if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
output[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
} else if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
output[sensor.name()] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
}
|
||||
|
||||
return (output.size() > 0);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (strcmp(cmd, sensor.name().c_str()) == 0) {
|
||||
output["id_str"] = sensor.id_str();
|
||||
output["name"] = sensor.name();
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
output["value"] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
output["type"] = F_(number);
|
||||
output["min"] = -55;
|
||||
output["max"] = 125;
|
||||
output["unit"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
output["writeable"] = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// publish a single sensor to MQTT
|
||||
void DallasSensor::publish_sensor(const Sensor & sensor) {
|
||||
if (Mqtt::publish_single()) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "%s/%s", read_flash_string(F_(dallassensor)).c_str(), sensor.name().c_str());
|
||||
char payload[10];
|
||||
Mqtt::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_str) {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_DEBUG(F("Removing HA config for sensor ID %s"), id_str.c_str());
|
||||
#endif
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string sensorid = id_str;
|
||||
std::replace(sensorid.begin(), sensorid.end(), '-', '_');
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), sensorid.c_str());
|
||||
Mqtt::publish_ha(topic);
|
||||
}
|
||||
|
||||
// send all dallas sensor values as a JSON package to MQTT
|
||||
void DallasSensor::publish_values(const bool force) {
|
||||
uint8_t num_sensors = sensors_.size();
|
||||
@@ -483,27 +425,33 @@ void DallasSensor::publish_values(const bool force) {
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(100 * num_sensors);
|
||||
uint8_t sensor_no = 1;
|
||||
if (force && Mqtt::publish_single()) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
// return;
|
||||
}
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
char sensorID[10]; // sensor{1-n}
|
||||
snprintf(sensorID, 10, "sensor%d", sensor_no);
|
||||
if (dallas_format_ == Dallas_Format::NUMBER) {
|
||||
// e.g. dallassensor_data = {"sensor1":{"id":"28-EA41-9497-0E03","temp":23.3},"sensor2":{"id":"28-233D-9497-0C03","temp":24.0}}
|
||||
JsonObject dataSensor = doc.createNestedObject(sensorID);
|
||||
dataSensor["id"] = sensor.to_string();
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
DynamicJsonDocument doc(120 * num_sensors);
|
||||
|
||||
for (auto & sensor : sensors_) {
|
||||
bool has_value = Helpers::hasValue(sensor.temperature_c);
|
||||
if (Mqtt::is_nested() || Mqtt::ha_enabled()) {
|
||||
JsonObject dataSensor = doc.createNestedObject(sensor.id_str());
|
||||
dataSensor["name"] = sensor.name();
|
||||
if (has_value) {
|
||||
dataSensor["temp"] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
} else if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
doc[sensor.to_string()] = (float)(sensor.temperature_c) / 10;
|
||||
} else if (has_value) {
|
||||
doc[sensor.name()] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
|
||||
// create the HA MQTT config
|
||||
// to e.g. homeassistant/sensor/ems-esp/dallassensor_28-233D-9497-0C03/config
|
||||
if (Mqtt::ha_enabled()) {
|
||||
if (!(registered_ha_[sensor_no - 1]) || force) {
|
||||
if (!sensor.ha_registered || force) {
|
||||
LOG_DEBUG(F("Recreating HA config for sensor ID %s"), sensor.id_str().c_str());
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> config;
|
||||
config["dev_cla"] = FJSON("temperature");
|
||||
|
||||
@@ -511,25 +459,20 @@ void DallasSensor::publish_values(const bool force) {
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/dallassensor_data", Mqtt::base().c_str());
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
config["unit_of_meas"] = FJSON("°C");
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
config["unit_of_meas"] = FJSON("°F");
|
||||
} else {
|
||||
config["unit_of_meas"] = FJSON("°C");
|
||||
}
|
||||
|
||||
char str[50];
|
||||
if (dallas_format_ != Dallas_Format::NUMBER) {
|
||||
snprintf(str, sizeof(str), "{{value_json['%s']}}", sensor.to_string().c_str());
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "{{value_json.sensor%d.temp}}", sensor_no);
|
||||
}
|
||||
snprintf(str, sizeof(str), "{{value_json['%s'].temp}}", sensor.id_str().c_str());
|
||||
config["val_tpl"] = str;
|
||||
|
||||
// name as sensor number not the long unique ID
|
||||
if (dallas_format_ != Dallas_Format::NUMBER) {
|
||||
snprintf(str, sizeof(str), "Dallas Sensor %s", sensor.to_string().c_str());
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "Dallas Sensor %d", sensor_no);
|
||||
}
|
||||
snprintf(str, sizeof(str), "Temperature Sensor %s", sensor.name().c_str());
|
||||
config["name"] = str;
|
||||
|
||||
snprintf(str, sizeof(str), "dallasensor_%s", sensor.to_string().c_str());
|
||||
snprintf(str, sizeof(str), "dallasensor_%s", sensor.id_str().c_str());
|
||||
config["uniq_id"] = str;
|
||||
|
||||
JsonObject dev = config.createNestedObject("dev");
|
||||
@@ -537,23 +480,94 @@ void DallasSensor::publish_values(const bool force) {
|
||||
ids.add("ems-esp");
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (dallas_format_ == Dallas_Format::NUMBER) {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%d/config", Mqtt::base().c_str(), sensor_no);
|
||||
} else {
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string topicname = sensor.to_string();
|
||||
std::replace(topicname.begin(), topicname.end(), '-', '_');
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), topicname.c_str());
|
||||
}
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string sensorid = sensor.id_str();
|
||||
std::replace(sensorid.begin(), sensorid.end(), '-', '_');
|
||||
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/dallassensor_%s/config", Mqtt::base().c_str(), sensorid.c_str());
|
||||
|
||||
Mqtt::publish_ha(topic, config.as<JsonObject>());
|
||||
|
||||
registered_ha_[sensor_no - 1] = true;
|
||||
sensor.ha_registered = true;
|
||||
}
|
||||
}
|
||||
sensor_no++; // increment sensor count
|
||||
}
|
||||
|
||||
Mqtt::publish(F("dallassensor_data"), doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
|
||||
// skip crc from id
|
||||
DallasSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
: 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
|
||||
char id[20];
|
||||
snprintf(id,
|
||||
sizeof(id),
|
||||
"%02X-%04X-%04X-%04X",
|
||||
(unsigned int)(id_ >> 48) & 0xFF,
|
||||
(unsigned int)(id_ >> 32) & 0xFFFF,
|
||||
(unsigned int)(id_ >> 16) & 0xFFFF,
|
||||
(unsigned int)(id_)&0xFFFF);
|
||||
id_str_ = std::string(id);
|
||||
name_ = std::string{}; // name (alias) is empty
|
||||
offset_ = 0; // 0 degrees offset
|
||||
}
|
||||
|
||||
uint64_t DallasSensor::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 {
|
||||
if (name_.empty()) {
|
||||
return id_str_;
|
||||
}
|
||||
return name_;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
auto sensors = settings.sensorCustomizations;
|
||||
if (sensors.size() != 0) {
|
||||
for (auto & sensor : sensors) {
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG(F("Loading customization for dallas sensor %s"), sensor.id_str.c_str());
|
||||
#endif
|
||||
if (id_str_ == sensor.id_str) {
|
||||
set_name(sensor.name);
|
||||
set_offset(sensor.offset);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
return false; // not found, will use default ID as name and 0 for offset
|
||||
}
|
||||
|
||||
// hard coded tests
|
||||
#ifdef EMSESP_DEBUG
|
||||
void DallasSensor::test() {
|
||||
// add 2 dallas sensors
|
||||
uint8_t addr[ADDR_LEN] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
sensors_.emplace_back(addr);
|
||||
sensors_.back().temperature_c = 123;
|
||||
sensors_.back().read = true;
|
||||
sensors_.back().apply_customization();
|
||||
|
||||
uint8_t addr2[ADDR_LEN] = {11, 12, 13, 14, 15, 16, 17, 18};
|
||||
sensors_.emplace_back(addr2);
|
||||
sensors_.back().temperature_c = 456;
|
||||
sensors_.back().read = true;
|
||||
sensors_.back().apply_customization();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
#ifndef EMSESP_DALLASSENSOR_H
|
||||
#define EMSESP_DALLASSENSOR_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
#include "console.h"
|
||||
@@ -36,8 +33,6 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum Dallas_Format : uint8_t { SENSORID = 1, NUMBER, NAME };
|
||||
|
||||
class DallasSensor {
|
||||
public:
|
||||
class Sensor {
|
||||
@@ -45,16 +40,37 @@ class DallasSensor {
|
||||
Sensor(const uint8_t addr[]);
|
||||
~Sensor() = default;
|
||||
|
||||
uint64_t id() const;
|
||||
std::string id_string() const;
|
||||
std::string to_string(const bool name = false) const;
|
||||
int16_t offset() const;
|
||||
uint64_t id() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
std::string id_str() const {
|
||||
return id_str_;
|
||||
}
|
||||
|
||||
int16_t offset() const {
|
||||
return offset_;
|
||||
}
|
||||
void set_offset(const int16_t offset) {
|
||||
offset_ = offset;
|
||||
}
|
||||
|
||||
std::string name() const;
|
||||
void set_name(const std::string & name) {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
bool apply_customization();
|
||||
|
||||
int16_t temperature_c = EMS_VALUE_SHORT_NOTSET;
|
||||
bool read = false;
|
||||
bool ha_registered = false;
|
||||
|
||||
private:
|
||||
const uint64_t id_;
|
||||
uint64_t id_;
|
||||
std::string id_str_;
|
||||
std::string name_;
|
||||
int16_t offset_;
|
||||
};
|
||||
|
||||
DallasSensor() = default;
|
||||
@@ -62,11 +78,16 @@ class DallasSensor {
|
||||
|
||||
void start();
|
||||
void loop();
|
||||
void publish_sensor(const Sensor & sensor);
|
||||
void publish_values(const bool force);
|
||||
void reload();
|
||||
bool updated_values();
|
||||
bool get_value_info(JsonObject & output, const char * cmd, const int8_t id);
|
||||
|
||||
const std::vector<Sensor> sensors() const;
|
||||
// return back reference to the sensor list, used by other classes
|
||||
const std::vector<Sensor> sensors() const {
|
||||
return sensors_;
|
||||
}
|
||||
|
||||
uint32_t reads() {
|
||||
return sensorreads_;
|
||||
@@ -80,15 +101,19 @@ class DallasSensor {
|
||||
return (dallas_gpio_ != 0);
|
||||
}
|
||||
|
||||
uint8_t dallas_format() {
|
||||
return dallas_format_;
|
||||
bool have_sensors() {
|
||||
return (sensors_.size() > 0);
|
||||
}
|
||||
|
||||
void dallas_format(uint8_t dallas_format) {
|
||||
dallas_format_ = dallas_format;
|
||||
size_t no_sensors() {
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
bool update(const char * idstr, const char * name, int16_t offset);
|
||||
bool update(const std::string & id_str, const std::string & name, int16_t offset);
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
void test();
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint8_t MAX_SENSORS = 20;
|
||||
@@ -129,27 +154,24 @@ class DallasSensor {
|
||||
bool temperature_convert_complete();
|
||||
int16_t get_temperature_c(const uint8_t addr[]);
|
||||
uint64_t get_id(const uint8_t addr[]);
|
||||
void remove_ha_topic(const std::string & id_str);
|
||||
|
||||
bool command_info(const char * value, const int8_t id, JsonObject & output);
|
||||
bool command_commands(const char * value, const int8_t id, JsonObject & output);
|
||||
|
||||
void delete_ha_config(uint8_t index, const char * name);
|
||||
uint32_t last_activity_ = uuid::get_uptime();
|
||||
State state_ = State::IDLE;
|
||||
|
||||
uint32_t last_activity_ = uuid::get_uptime();
|
||||
State state_ = State::IDLE;
|
||||
std::vector<Sensor> sensors_;
|
||||
std::vector<Sensor> sensors_; // our list of active sensors
|
||||
|
||||
bool registered_ha_[MAX_SENSORS];
|
||||
|
||||
int8_t scancnt_ = SCAN_START;
|
||||
uint8_t firstscan_ = 0;
|
||||
uint8_t dallas_gpio_ = 0;
|
||||
bool parasite_ = false;
|
||||
bool changed_ = false;
|
||||
uint32_t sensorfails_ = 0;
|
||||
uint32_t sensorreads_ = 0;
|
||||
int8_t scanretry_ = 0;
|
||||
uint8_t dallas_format_ = 0;
|
||||
int8_t scancnt_ = SCAN_START;
|
||||
uint8_t firstscan_ = 0;
|
||||
uint8_t dallas_gpio_ = 0;
|
||||
bool parasite_ = false;
|
||||
bool changed_ = false;
|
||||
uint32_t sensorfails_ = 0;
|
||||
uint32_t sensorreads_ = 0;
|
||||
int8_t scanretry_ = 0;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -88,6 +88,10 @@
|
||||
#define EMSESP_DEFAULT_ANALOG_ENABLED false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TELNET_ENABLED
|
||||
#define EMSESP_DEFAULT_TELNET_ENABLED false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
|
||||
#define EMSESP_DEFAULT_BOARD_PROFILE "S32" // Gateway S32
|
||||
#endif
|
||||
@@ -124,10 +128,6 @@
|
||||
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DALLAS_FORMAT
|
||||
#define EMSESP_DEFAULT_DALLAS_FORMAT 1 // sensorid
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_HA_CLIMATE_FORMAT
|
||||
#define EMSESP_DEFAULT_HA_CLIMATE_FORMAT 1 // current temp
|
||||
#endif
|
||||
@@ -152,6 +152,14 @@
|
||||
#define EMSESP_DEFAULT_NESTED_FORMAT 1
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DISCOVERY_PREFIX
|
||||
#define EMSESP_DEFAULT_DISCOVERY_PREFIX "homeassistant"
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_SINGLE
|
||||
#define EMSESP_DEFAULT_PUBLISH_SINGLE false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SEND_RESPONSE
|
||||
#define EMSESP_DEFAULT_SEND_RESPONSE false
|
||||
#endif
|
||||
@@ -164,6 +172,14 @@
|
||||
#define EMSESP_DEFAULT_SENSOR_NAME ""
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_ANALOG_NAME
|
||||
#define EMSESP_DEFAULT_ANALOG_NAME ""
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_ANALOG_UOM
|
||||
#define EMSESP_DEFAULT_ANALOG_UOM "mV"
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_WEBLOG_LEVEL
|
||||
#define EMSESP_DEFAULT_WEBLOG_LEVEL 6 // INFO
|
||||
#endif
|
||||
@@ -176,4 +192,24 @@
|
||||
#define EMSESP_DEFAULT_WEBLOG_COMPACT true
|
||||
#endif
|
||||
|
||||
// matches Web UI settings
|
||||
enum {
|
||||
|
||||
BOOL_FORMAT_ONOFF_STR = 1,
|
||||
BOOL_FORMAT_ONOFF_STR_CAP,
|
||||
BOOL_FORMAT_TRUEFALSE_STR,
|
||||
BOOL_FORMAT_TRUEFALSE,
|
||||
BOOL_FORMAT_10_STR,
|
||||
BOOL_FORMAT_10
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
ENUM_FORMAT_VALUE = 1,
|
||||
ENUM_FORMAT_INDEX // 2
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,12 +26,14 @@
|
||||
// Boilers - 0x08
|
||||
{ 64, DeviceType::BOILER, F("BK13/BK15/Smartline/GB1x2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 72, DeviceType::BOILER, F("GB125/MC10"), DeviceFlags::EMS_DEVICE_FLAG_EMS},
|
||||
{ 81, DeviceType::BOILER, F("Cascade CM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 84, DeviceType::BOILER, F("Logamax Plus GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 95, DeviceType::BOILER, F("Condens 2500/Logamax/Logomatic/Cerapur Top/Greenstar/Generic HT3"), DeviceFlags::EMS_DEVICE_FLAG_HT3},
|
||||
{115, DeviceType::BOILER, F("Topline/GB162"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{122, DeviceType::BOILER, F("Proline"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{123, DeviceType::BOILER, F("GBx72/Trendline/Cerapur/Greenstar Si/27i"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{131, DeviceType::BOILER, F("GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{132, DeviceType::BOILER, F("GC7000F"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{133, DeviceType::BOILER, F("Logano GB125/KB195i/Logamatic MC110"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{167, DeviceType::BOILER, F("Cerapur Aero"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{170, DeviceType::BOILER, F("Logano GB212"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
@@ -42,11 +44,12 @@
|
||||
{208, DeviceType::BOILER, F("Logamax Plus/GB192/Condens GC9000"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{210, DeviceType::BOILER, F("Cascade MC400"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{211, DeviceType::BOILER, F("EasyControl Adapter"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{234, DeviceType::BOILER, F("Logamax Plus GB122"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{234, DeviceType::BOILER, F("Logamax Plus GB122/Condense 2300"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{206, DeviceType::BOILER, F("Ecomline Excellent"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Controllers - 0x09 / 0x10 / 0x50
|
||||
{ 68, DeviceType::CONTROLLER, F("BC10/RFM20"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 81, DeviceType::CONTROLLER, F("CM10"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 84, DeviceType::CONTROLLER, F("GB022"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 89, DeviceType::CONTROLLER, F("BC10 GB142"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 95, DeviceType::CONTROLLER, F("HT3"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
@@ -61,6 +64,7 @@
|
||||
{209, DeviceType::CONTROLLER, F("ErP"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{218, DeviceType::CONTROLLER, F("M200/RFM200"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x50
|
||||
{224, DeviceType::CONTROLLER, F("9000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{229, DeviceType::CONTROLLER, F("8700i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{230, DeviceType::CONTROLLER, F("BC Base"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{240, DeviceType::CONTROLLER, F("Rego 3000"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{241, DeviceType::CONTROLLER, F("Condens 5000i"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
@@ -80,7 +84,7 @@
|
||||
{ 90, DeviceType::THERMOSTAT, F("RC10/Moduline 100"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||
{ 93, DeviceType::THERMOSTAT, F("RC20RF"), DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
|
||||
{ 94, DeviceType::THERMOSTAT, F("RFM20 Remote"), DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
||||
{151, DeviceType::THERMOSTAT, F("RC25"), DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||
{151, DeviceType::THERMOSTAT, F("RC25"), DeviceFlags::EMS_DEVICE_FLAG_RC25}, // 0x17
|
||||
{157, DeviceType::THERMOSTAT, F("RC200/CW100"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18
|
||||
{158, DeviceType::THERMOSTAT, F("RC300/RC310/Moduline 3000/1010H/CW400/Sense II"), DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{165, DeviceType::THERMOSTAT, F("RC100/Moduline 1000/1010"), DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
|
||||
@@ -109,13 +113,13 @@
|
||||
{163, DeviceType::SOLAR, F("SM100/MS100"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{164, DeviceType::SOLAR, F("SM200/MS200"), DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
|
||||
// Mixer Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC
|
||||
// Mixer Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC and 0x11 for the MP100
|
||||
{ 69, DeviceType::MIXER, F("MM10"), DeviceFlags::EMS_DEVICE_FLAG_MM10},
|
||||
{102, DeviceType::MIXER, F("IPM"), DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||
{159, DeviceType::MIXER, F("MM50"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{160, DeviceType::MIXER, F("MM100"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{161, DeviceType::MIXER, F("MM200"), DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{204, DeviceType::MIXER, F("MP100"), DeviceFlags::EMS_DEVICE_FLAG_MP},
|
||||
{204, DeviceType::MIXER, F("MP100"), DeviceFlags::EMS_DEVICE_FLAG_MP}, // pool
|
||||
|
||||
// Heat Pumps - 0x38
|
||||
{200, DeviceType::HEATPUMP, F("HP Module"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
@@ -126,7 +130,7 @@
|
||||
{205, DeviceType::CONNECT, F("Moduline Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{206, DeviceType::CONNECT, F("Easy Connect"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// wireless sensor base- 0x50
|
||||
// Wireless sensor base - 0x50
|
||||
{236, DeviceType::CONNECT, F("Wireless sensor base"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Switches - 0x11
|
||||
@@ -135,7 +139,7 @@
|
||||
// Gateways - 0x48
|
||||
{189, DeviceType::GATEWAY, F("KM200/MB LAN 2"), DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// generic - 0x40 or other with no product-id and no version
|
||||
// Generic - 0x40 or other with no product-id and no version
|
||||
{0, DeviceType::GENERIC, F("unknown"), DeviceFlags::EMS_DEVICE_FLAG_NONE}
|
||||
|
||||
// clang-format on
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,9 +25,7 @@ namespace emsesp {
|
||||
|
||||
class Boiler : public EMSdevice {
|
||||
public:
|
||||
Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
@@ -52,50 +50,46 @@ class Boiler : public EMSdevice {
|
||||
static constexpr uint16_t EMS_TYPE_UBAInformation = 0x495;
|
||||
static constexpr uint16_t EMS_TYPE_UBAEnergySupplied = 0x494;
|
||||
|
||||
static constexpr uint8_t EMS_BOILER_SELFLOWTEMP_HEATING = 20; // was originally 70, changed to 30 for issue #193, then to 20 with issue #344
|
||||
|
||||
// ww
|
||||
uint8_t wwSetTemp_; // Warm Water set temperature
|
||||
uint8_t wwSelTemp_; // Warm Water selected temperature
|
||||
uint8_t wwSelTempLow_; // Warm Water lower selected temperature
|
||||
uint8_t wwSelTempOff_; // Warm Water selected temperature for off position
|
||||
uint8_t wwSelTempSingle_; // Warm Water single charge temperature
|
||||
uint8_t wwSetTemp_; // DHW set temperature
|
||||
uint8_t wwSelTemp_; // DHW selected temperature
|
||||
uint8_t wwSelTempLow_; // DHW lower selected temperature
|
||||
uint8_t wwSelTempOff_; // DHW selected temperature for off position
|
||||
uint8_t wwSelTempSingle_; // DHW single charge temperature
|
||||
uint8_t wwType_; // 0-off, 1-flow, 2-flowbuffer, 3-buffer, 4-layered buffer
|
||||
uint8_t wwComfort_; // WW comfort mode
|
||||
uint8_t wwCircPump_; // Warm Water circulation pump available
|
||||
uint8_t wwChargeType_; // Warm Water charge type (pump or 3-way-valve)
|
||||
uint8_t wwDisinfectionTemp_; // Warm Water disinfection temperature to prevent infection
|
||||
uint8_t wwCircMode_; // Warm Water circulation pump mode
|
||||
uint8_t wwCircPump_; // DHW circulation pump available
|
||||
uint8_t wwChargeType_; // DHW charge type (pump or 3-way-valve)
|
||||
uint8_t wwDisinfectionTemp_; // DHW disinfection temperature to prevent infection
|
||||
uint8_t wwCircMode_; // DHW circulation pump mode
|
||||
uint8_t wwCirc_; // Circulation on/off
|
||||
uint16_t wwCurTemp_; // Warm Water current temperature
|
||||
uint16_t wwCurTemp2_; // Warm Water current temperature storage
|
||||
uint8_t wwCurFlow_; // Warm Water current flow temp in l/min
|
||||
uint16_t wwStorageTemp1_; // warm water storage temp 1
|
||||
uint16_t wwStorageTemp2_; // warm water storage temp 2
|
||||
uint8_t wwActivated_; // Warm Water activated
|
||||
uint8_t wwOneTime_; // Warm Water one time function on/off
|
||||
uint8_t wwDisinfect_; // Warm Water disinfection on/off
|
||||
uint8_t wwCharging_; // Warm Water charging on/off
|
||||
uint8_t wwRecharging_; // Warm Water recharge on/off
|
||||
uint8_t wwTempOK_; // Warm Water temperature ok on/off
|
||||
uint16_t wwCurTemp_; // DHW current temperature
|
||||
uint16_t wwCurTemp2_; // DHW current temperature storage
|
||||
uint8_t wwCurFlow_; // DHW current flow temp in l/min
|
||||
uint16_t wwStorageTemp1_; // DHW storage temp 1
|
||||
uint16_t wwStorageTemp2_; // DHW storage temp 2
|
||||
uint8_t wwActivated_; // DHW activated
|
||||
uint8_t wwOneTime_; // DHW one time function on/off
|
||||
uint8_t wwDisinfect_; // DHW disinfection on/off
|
||||
uint8_t wwCharging_; // DHW charging on/off
|
||||
uint8_t wwRecharging_; // DHW recharge on/off
|
||||
uint8_t wwTempOK_; // DHW temperature ok on/off
|
||||
uint8_t wwActive_; //
|
||||
uint8_t wwHeat_; // 3-way valve on WW
|
||||
uint8_t ww3wayValve_; // 3-way valve on WW
|
||||
uint8_t wwSetPumpPower_; // ww pump speed/power?
|
||||
uint8_t wwFlowTempOffset_; // Boiler offset for ww heating
|
||||
uint8_t wwMaxPower_; // Warm Water maximum power
|
||||
uint32_t wwStarts_; // Warm Water starts
|
||||
uint32_t wwStarts2_; // Warm water control starts
|
||||
uint32_t wwWorkM_; // Warm Water minutes
|
||||
uint8_t wwMaxPower_; // DHW maximum power
|
||||
uint32_t wwStarts_; // DHW starts
|
||||
uint32_t wwStarts2_; // DHW control starts
|
||||
uint32_t wwWorkM_; // DHW minutes
|
||||
int8_t wwHystOn_;
|
||||
int8_t wwHystOff_;
|
||||
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
|
||||
uint16_t wwMixerTemp_; // mixing temperature
|
||||
uint16_t wwTankMiddleTemp_; // Tank middle temperature (TS3)
|
||||
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
|
||||
uint16_t wwMixerTemp_; // mixing temperature
|
||||
uint16_t wwCylMiddleTemp_; // Cyl middle temperature (TS3)
|
||||
|
||||
// main
|
||||
uint8_t id_; // product id
|
||||
uint8_t dummy8u_; // for commands with no output
|
||||
uint8_t dummybool_; // for commands with no output
|
||||
uint8_t reset_; // for reset command
|
||||
uint8_t heatingActive_; // Central heating is on/off
|
||||
uint8_t tapwaterActive_; // Hot tap water is on/off
|
||||
uint8_t selFlowTemp_; // Selected flow temperature
|
||||
@@ -110,10 +104,12 @@ class Boiler : public EMSdevice {
|
||||
uint16_t boilTemp_; // Boiler temperature
|
||||
uint16_t exhaustTemp_; // Exhaust temperature
|
||||
uint8_t burnGas_; // Gas on/off
|
||||
uint8_t burnGas2_; // Gas stage 2 on/off
|
||||
uint16_t flameCurr_; // Flame current in micro amps
|
||||
uint8_t heatingPump_; // Boiler heating pump on/off
|
||||
uint8_t fanWork_; // Fan on/off
|
||||
uint8_t ignWork_; // Ignition on/off
|
||||
uint8_t oilPreHeat_; // oil preheating on
|
||||
uint8_t heatingActivated_; // Heating activated on the boiler
|
||||
uint8_t heatingTemp_; // Heating temperature setting on the boiler
|
||||
uint8_t pumpModMax_; // Boiler circuit pump modulation max. power %
|
||||
@@ -131,7 +127,7 @@ class Boiler : public EMSdevice {
|
||||
uint32_t burnWorkMin_; // Total burner operating time
|
||||
uint32_t heatWorkMin_; // Total heat operating time
|
||||
uint32_t UBAuptime_; // Total UBA working hours
|
||||
char lastCode_[60]; // last error code
|
||||
char lastCode_[75]; // last error code
|
||||
char serviceCode_[4]; // 3 character status/service code
|
||||
uint16_t serviceCodeNumber_; // error/service code
|
||||
|
||||
@@ -139,21 +135,21 @@ class Boiler : public EMSdevice {
|
||||
uint32_t upTimeControl_; // Operating time control
|
||||
uint32_t upTimeCompHeating_; // Operating time compressor heating
|
||||
uint32_t upTimeCompCooling_; // Operating time compressor cooling
|
||||
uint32_t upTimeCompWw_; // Operating time compressor warm water
|
||||
uint32_t upTimeCompWw_; // Operating time compressor DHW
|
||||
uint32_t upTimeCompPool_; // Operating time compressor pool
|
||||
uint32_t totalCompStarts_; // Total Commpressor control starts
|
||||
uint32_t heatingStarts_; // Heating control starts
|
||||
uint32_t coolingStarts_; // Cooling control starts
|
||||
uint32_t poolStarts_; // Warm water control starts
|
||||
uint32_t poolStarts_; // DHW control starts
|
||||
uint32_t nrgConsTotal_; // Energy consumption total
|
||||
uint32_t nrgConsCompTotal_; // Energy consumption compressor total
|
||||
uint32_t nrgConsCompHeating_; // Energy consumption compressor heating
|
||||
uint32_t nrgConsCompWw_; // Energy consumption compressor warm water
|
||||
uint32_t nrgConsCompWw_; // Energy consumption compressor DHW
|
||||
uint32_t nrgConsCompCooling_; // Energy consumption compressor cooling
|
||||
uint32_t nrgConsCompPool_; // Energy consumption compressor pool
|
||||
uint32_t nrgSuppTotal_; // Energy supplied total
|
||||
uint32_t nrgSuppHeating_; // Energy supplied heating
|
||||
uint32_t nrgSuppWw_; // Energy supplied warm water
|
||||
uint32_t nrgSuppWw_; // Energy supplied DHW
|
||||
uint32_t nrgSuppCooling_; // Energy supplied cooling
|
||||
uint32_t nrgSuppPool_; // Energy supplied pool
|
||||
uint32_t auxElecHeatNrgConsTotal_; // Auxiliary electrical heater energy consumption total
|
||||
|
||||
@@ -22,15 +22,8 @@ namespace emsesp {
|
||||
|
||||
REGISTER_FACTORY(Connect, EMSdevice::DeviceType::CONNECT);
|
||||
|
||||
uuid::log::Logger Connect::logger_{F_(connect), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Connect::publish_ha_device_config() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -25,12 +25,7 @@ namespace emsesp {
|
||||
|
||||
class Connect : public EMSdevice {
|
||||
public:
|
||||
Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -22,15 +22,8 @@ namespace emsesp {
|
||||
|
||||
REGISTER_FACTORY(Controller, EMSdevice::DeviceType::CONTROLLER);
|
||||
|
||||
uuid::log::Logger Controller::logger_{F_(controller), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Controller::publish_ha_device_config() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -25,12 +25,7 @@ namespace emsesp {
|
||||
|
||||
class Controller : public EMSdevice {
|
||||
public:
|
||||
Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
Controller(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -22,15 +22,8 @@ namespace emsesp {
|
||||
|
||||
REGISTER_FACTORY(Gateway, EMSdevice::DeviceType::GATEWAY);
|
||||
|
||||
uuid::log::Logger Gateway::logger_{F_(gateway), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Gateway::publish_ha_device_config() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
@@ -25,12 +25,7 @@ namespace emsesp {
|
||||
|
||||
class Gateway : public EMSdevice {
|
||||
public:
|
||||
Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -24,23 +24,18 @@ REGISTER_FACTORY(Generic, EMSdevice::DeviceType::GENERIC);
|
||||
|
||||
uuid::log::Logger Generic::logger_{F_(generic), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Generic::Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
Generic::Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// RF-Sensor 0x40 sending temperature in telegram 0x435, see https://github.com/emsesp/EMS-ESP32/issues/103
|
||||
if (device_id == 0x40) {
|
||||
register_telegram_type(0x435, F("RFSensorMessage"), false, MAKE_PF_CB(process_RFSensorMessage));
|
||||
register_device_value(TAG_NONE, &rfTemp_, DeviceValueType::SHORT, FL_(div10), FL_(RFTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &rfTemp_, DeviceValueType::SHORT, FL_(div10), FL_(RFTemp), DeviceValueUOM::DEGREES);
|
||||
}
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Generic::publish_ha_device_config() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// type 0x435 rf remote sensor
|
||||
void Generic::process_RFSensorMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(rfTemp_, 0)); // is * 10
|
||||
has_update(telegram, rfTemp_, 0); // is * 10
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -25,9 +25,7 @@ namespace emsesp {
|
||||
|
||||
class Generic : public EMSdevice {
|
||||
public:
|
||||
Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
Generic(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
@@ -22,50 +22,15 @@ namespace emsesp {
|
||||
|
||||
REGISTER_FACTORY(Heatpump, EMSdevice::DeviceType::HEATPUMP);
|
||||
|
||||
uuid::log::Logger Heatpump::logger_{F_(heatpump), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// telegram handlers
|
||||
register_telegram_type(0x042B, F("HP1"), true, MAKE_PF_CB(process_HPMonitor1));
|
||||
register_telegram_type(0x047B, F("HP2"), true, MAKE_PF_CB(process_HPMonitor2));
|
||||
|
||||
// device values
|
||||
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &airHumidity_, DeviceValueType::UINT, FL_(div2), FL_(airHumidity), DeviceValueUOM::PERCENT);
|
||||
register_device_value(TAG_NONE, &dewTemperature_, DeviceValueType::UINT, nullptr, FL_(dewTemperature), DeviceValueUOM::DEGREES);
|
||||
|
||||
id_ = product_id;
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Heatpump::publish_ha_device_config() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["uniq_id"] = F_(heatpump);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/%s", Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
char name_s[40];
|
||||
snprintf(name_s, sizeof(name_s), FSTR_(productid_fmt), device_type_name().c_str());
|
||||
doc["name"] = name_s;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Heat Pump");
|
||||
dev["sw"] = EMSESP_APP_VERSION;
|
||||
dev["mf"] = brand_to_string();
|
||||
dev["mdl"] = this->name();
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp-heatpump");
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/heatpump/config", Mqtt::base().c_str());
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
return true;
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &airHumidity_, DeviceValueType::UINT, FL_(div2), FL_(airHumidity), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &dewTemperature_, DeviceValueType::UINT, nullptr, FL_(dewTemperature), DeviceValueUOM::DEGREES);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -73,8 +38,8 @@ bool Heatpump::publish_ha_device_config() {
|
||||
* e.g. "38 10 FF 00 03 7B 08 24 00 4B"
|
||||
*/
|
||||
void Heatpump::process_HPMonitor2(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(dewTemperature_, 0));
|
||||
has_update(telegram->read_value(airHumidity_, 1));
|
||||
has_update(telegram, dewTemperature_, 0);
|
||||
has_update(telegram, airHumidity_, 1);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
@@ -90,4 +55,4 @@ void Heatpump::process_HPMonitor1(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -25,16 +25,11 @@ namespace emsesp {
|
||||
|
||||
class Heatpump : public EMSdevice {
|
||||
public:
|
||||
Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
uint8_t airHumidity_;
|
||||
uint8_t dewTemperature_;
|
||||
uint8_t id_;
|
||||
|
||||
void process_HPMonitor1(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HPMonitor2(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
@@ -24,41 +24,59 @@ REGISTER_FACTORY(Mixer, EMSdevice::DeviceType::MIXER);
|
||||
|
||||
uuid::log::Logger Mixer::logger_{F_(mixer), uuid::log::Facility::CONSOLE};
|
||||
|
||||
Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
// Pool module
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_MP) {
|
||||
register_telegram_type(0x5BA, F("HpPoolStatus"), true, MAKE_PF_CB(process_HpPoolStatus));
|
||||
type_ = Type::MP;
|
||||
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &poolTemp_, DeviceValueType::SHORT, FL_(div10), FL_(poolTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &poolShuntStatus_, DeviceValueType::ENUM, FL_(enum_shunt), FL_(poolShuntStatus), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &poolShunt_, DeviceValueType::UINT, nullptr, FL_(poolShunt), DeviceValueUOM::PERCENT);
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &poolTemp_, DeviceValueType::SHORT, FL_(div10), FL_(poolTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &poolShuntStatus_, DeviceValueType::ENUM, FL_(enum_shunt), FL_(poolShuntStatus), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &poolShunt_, DeviceValueType::UINT, nullptr, FL_(poolShunt), DeviceValueUOM::PERCENT);
|
||||
}
|
||||
|
||||
// EMS+
|
||||
if (flags == EMSdevice::EMS_DEVICE_FLAG_MMPLUS) {
|
||||
if (device_id >= 0x20 && device_id <= 0x27) {
|
||||
register_telegram_type(device_id - 0x20 + 0x02D7, F("MMPLUSStatusMessage_HC"), true, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
|
||||
register_telegram_type(device_id - 0x20 + 0x02D7, F("MMPLUSStatusMessage_HC"), false, MAKE_PF_CB(process_MMPLUSStatusMessage_HC));
|
||||
// register_telegram_type(device_id - 0x20 + 0x02E1, F("MMPLUSStetMessage_HC"), true, MAKE_PF_CB(process_MMPLUSSetMessage_HC));
|
||||
type_ = Type::HC;
|
||||
hc_ = device_id - 0x20 + 1;
|
||||
uint8_t tag = TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::NONE, MAKE_CF_CB(set_pump));
|
||||
} else if (device_id >= 0x28 && device_id <= 0x29) {
|
||||
register_telegram_type(device_id - 0x28 + 0x0331, F("MMPLUSStatusMessage_WWC"), true, MAKE_PF_CB(process_MMPLUSStatusMessage_WWC));
|
||||
register_telegram_type(device_id - 0x28 + 0x0331, F("MMPLUSStatusMessage_WWC"), false, MAKE_PF_CB(process_MMPLUSStatusMessage_WWC));
|
||||
register_telegram_type(device_id - 0x28 + 0x0313, F("MMPLUSConfigMessage_WWC"), true, MAKE_PF_CB(process_MMPLUSConfigMessage_WWC));
|
||||
// register_telegram_type(device_id - 0x28 + 0x033B, F("MMPLUSSetMessage_WWC"), true, MAKE_PF_CB(process_MMPLUSSetMessage_WWC));
|
||||
type_ = Type::WWC;
|
||||
hc_ = device_id - 0x28 + 1;
|
||||
uint8_t tag = TAG_WWC1 + hc_ - 1;
|
||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
uint8_t tag = DeviceValueTAG::TAG_WWC1 + hc_ - 1;
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(wwTemp), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(wwPumpStatus), DeviceValueUOM::NONE);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(wwTempStatus), DeviceValueUOM::NONE);
|
||||
|
||||
register_device_value(tag, &wwMaxTemp_, DeviceValueType::UINT, nullptr, FL_(wwMaxTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwMaxTemp));
|
||||
register_device_value(tag, &wwDiffTemp_, DeviceValueType::INT, nullptr, FL_(wwDiffTemp), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_wwDiffTemp));
|
||||
register_device_value(tag,
|
||||
&wwDisinfectionTemp_,
|
||||
DeviceValueType::UINT,
|
||||
nullptr,
|
||||
FL_(wwDisinfectionTemp),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_wwDisinfectionTemp));
|
||||
register_device_value(tag, &wwReducedTemp_, DeviceValueType::UINT, nullptr, FL_(wwRedTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwReducedTemp));
|
||||
register_device_value(tag, &wwRequiredTemp_, DeviceValueType::UINT, nullptr, FL_(wwRequiredTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_wwRequiredTemp));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA_WW,
|
||||
&wwCircPump_,
|
||||
DeviceValueType::BOOL,
|
||||
nullptr,
|
||||
FL_(wwCircPump),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_wwCircPump));
|
||||
register_device_value(tag, &wwCircMode_, DeviceValueType::ENUM, FL_(enum_wwCircMode), FL_(wwCircMode), DeviceValueUOM::NONE, MAKE_CF_CB(set_wwCircMode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,17 +85,16 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
register_telegram_type(0x00AA, F("MMConfigMessage"), true, MAKE_PF_CB(process_MMConfigMessage));
|
||||
register_telegram_type(0x00AB, F("MMStatusMessage"), false, MAKE_PF_CB(process_MMStatusMessage));
|
||||
register_telegram_type(0x00AC, F("MMSetMessage"), false, MAKE_PF_CB(process_MMSetMessage));
|
||||
// EMSESP::send_read_request(0xAA, device_id);
|
||||
type_ = Type::HC;
|
||||
hc_ = device_id - 0x20 + 1;
|
||||
uint8_t tag = TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::NONE, MAKE_CF_CB(set_pump));
|
||||
register_device_value(tag, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE, MAKE_CF_CB(set_activated));
|
||||
register_device_value(tag, &setValveTime_, DeviceValueType::UINT, FL_(mul10), FL_(mixerSetTime), DeviceValueUOM::SECONDS, MAKE_CF_CB(set_setValveTime), 1, 12);
|
||||
register_device_value(
|
||||
tag, &setValveTime_, DeviceValueType::UINT, FL_(mul10), FL_(mixerSetTime), DeviceValueUOM::SECONDS, MAKE_CF_CB(set_setValveTime), 10, 120);
|
||||
}
|
||||
|
||||
// HT3
|
||||
@@ -87,99 +104,32 @@ Mixer::Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s
|
||||
// register_telegram_type(0x0123, F("IPMSetMessage"), false, MAKE_PF_CB(process_IPMSetMessage));
|
||||
type_ = Type::HC;
|
||||
hc_ = device_id - 0x20 + 1;
|
||||
uint8_t tag = TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
uint8_t tag = DeviceValueTAG::TAG_HC1 + hc_ - 1;
|
||||
register_device_value(tag, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(tag, &status_, DeviceValueType::INT, nullptr, FL_(mixerStatus), DeviceValueUOM::PERCENT);
|
||||
register_device_value(tag, &flowSetTemp_, DeviceValueType::UINT, nullptr, FL_(flowSetTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flowSetTemp));
|
||||
register_device_value(tag, &pumpStatus_, DeviceValueType::BOOL, nullptr, FL_(pumpStatus), DeviceValueUOM::NONE, MAKE_CF_CB(set_pump));
|
||||
register_device_value(tag, &flowTempVf_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempVf), DeviceValueUOM::DEGREES);
|
||||
}
|
||||
|
||||
id_ = product_id;
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Mixer::publish_ha_device_config() {
|
||||
// if we don't have valid values for this HC don't add it ever again
|
||||
if (!Helpers::hasValue(pumpStatus_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
|
||||
char uniq_id[20];
|
||||
if (type_ == Type::MP) {
|
||||
snprintf(uniq_id, sizeof(uniq_id), "MixerMP");
|
||||
} else {
|
||||
snprintf(uniq_id, sizeof(uniq_id), "Mixer%02X", device_id() - 0x20 + 1);
|
||||
}
|
||||
doc["uniq_id"] = uniq_id;
|
||||
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/%s", Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
char name[20];
|
||||
if (type_ == Type::MP) {
|
||||
snprintf(name, sizeof(name), "Mixer MP");
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "Mixer %02X", device_id() - 0x20 + 1);
|
||||
}
|
||||
doc["name"] = name;
|
||||
|
||||
char tpl[30];
|
||||
if (type_ == Type::HC) {
|
||||
snprintf(tpl, sizeof(tpl), "{{value_json.hc%d.id}}", device_id() - 0x20 + 1);
|
||||
} else if (type_ == Type::WWC) {
|
||||
snprintf(tpl, sizeof(tpl), "{{value_json.wwc%d.id}}", device_id() - 0x28 + 1);
|
||||
} else {
|
||||
snprintf(tpl, sizeof(tpl), "{{value_json.id}}");
|
||||
}
|
||||
doc["val_tpl"] = tpl;
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Mixer");
|
||||
dev["sw"] = EMSESP_APP_VERSION;
|
||||
dev["mf"] = brand_to_string();
|
||||
dev["mdl"] = this->name();
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp-mixer");
|
||||
|
||||
// determine the topic, if its HC and WWC. This is determined by the incoming telegram types.
|
||||
std::string topic(Mqtt::MQTT_TOPIC_MAX_SIZE, '\0');
|
||||
if (type_ == Type::HC) {
|
||||
snprintf(&topic[0], topic.capacity() + 1, "sensor/%s/mixer_hc%d/config", Mqtt::base().c_str(), hc_);
|
||||
} else if (type_ == Type::WWC) {
|
||||
snprintf(&topic[0], topic.capacity() + 1, "sensor/%s/mixer_wwc%d/config", Mqtt::base().c_str(), hc_); // WWC
|
||||
} else if (type_ == Type::MP) {
|
||||
snprintf(&topic[0], topic.capacity() + 1, "sensor/%s/mixer_mp/config", Mqtt::base().c_str());
|
||||
}
|
||||
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// heating circuits 0x02D7, 0x02D8 etc...
|
||||
// e.g. A0 00 FF 00 01 D7 00 00 00 80 00 00 00 00 03 C5
|
||||
// A0 0B FF 00 01 D7 00 00 00 80 00 00 00 00 03 80
|
||||
void Mixer::process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(flowTempHc_, 3)); // is * 10
|
||||
has_update(telegram->read_value(flowSetTemp_, 5));
|
||||
has_update(telegram->read_bitvalue(pumpStatus_, 0, 0));
|
||||
has_update(telegram->read_value(status_, 2)); // valve status
|
||||
has_update(telegram, flowTempHc_, 3); // is * 10
|
||||
has_update(telegram, flowSetTemp_, 5);
|
||||
has_bitupdate(telegram, pumpStatus_, 0, 0);
|
||||
has_update(telegram, status_, 2); // valve status
|
||||
}
|
||||
|
||||
// Mixer warm water loading/DHW - 0x0331, 0x0332
|
||||
// e.g. A9 00 FF 00 02 32 02 6C 00 3C 00 3C 3C 46 02 03 03 00 3C // on 0x28
|
||||
// A8 00 FF 00 02 31 02 35 00 3C 00 3C 3C 46 02 03 03 00 3C // in 0x29
|
||||
void Mixer::process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(flowTempHc_, 0)); // is * 10
|
||||
has_update(telegram->read_bitvalue(pumpStatus_, 2, 0));
|
||||
has_update(telegram->read_value(status_, 11)); // temp status
|
||||
has_update(telegram, flowTempHc_, 0); // is * 10
|
||||
has_bitupdate(telegram, pumpStatus_, 2, 0);
|
||||
has_update(telegram, status_, 11); // temp status
|
||||
}
|
||||
|
||||
// Mixer IPM - 0x010C
|
||||
@@ -195,18 +145,27 @@ void Mixer::process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// do we have a mixed circuit
|
||||
if (ismixed == 2) {
|
||||
has_update(telegram->read_value(flowTempHc_, 3)); // is * 10
|
||||
has_update(telegram->read_value(status_, 2)); // valve status
|
||||
has_update(telegram, flowTempHc_, 3); // is * 10
|
||||
has_update(telegram, status_, 2); // valve status
|
||||
}
|
||||
|
||||
has_update(telegram->read_bitvalue(pumpStatus_, 1, 0)); // pump is also in unmixed circuits
|
||||
has_update(telegram->read_value(flowSetTemp_, 5)); // flowSettemp is also in unmixed circuits, see #711
|
||||
has_bitupdate(telegram, pumpStatus_, 1, 0); // pump is also in unmixed circuits
|
||||
has_update(telegram, flowSetTemp_, 5); // flowSettemp is also in unmixed circuits, see #711
|
||||
}
|
||||
|
||||
// Mixer IPM - 0x001E Temperature Message in unmixed circuits
|
||||
// in unmixed circuits FlowTemp in 10C is zero, this is the measured flowtemp in header
|
||||
void Mixer::process_IPMTempMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(flowTempVf_, 0)); // TC1, is * 10
|
||||
has_update(telegram, flowTempVf_, 0); // TC1, is * 10
|
||||
}
|
||||
|
||||
// Mixer MP100 for pools - 0x5BA
|
||||
void Mixer::process_HpPoolStatus(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, poolTemp_, 0);
|
||||
has_update(telegram, poolShunt_, 3); // 0-100% how much is the shunt open?
|
||||
telegram->read_value(poolShuntStatus__, 2);
|
||||
uint8_t pss = poolShunt_ == 100 ? 3 : (poolShunt_ == 0 ? 4 : poolShuntStatus__);
|
||||
has_update(poolShuntStatus_, pss);
|
||||
}
|
||||
|
||||
// Mixer on a MM10 - 0xAB
|
||||
@@ -217,41 +176,67 @@ void Mixer::process_MMStatusMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// 0x21 is position 2. 0x20 is typically reserved for the WM10 switch module
|
||||
// see https://github.com/emsesp/EMS-ESP/issues/270 and https://github.com/emsesp/EMS-ESP/issues/386#issuecomment-629610918
|
||||
|
||||
has_update(telegram->read_value(flowTempHc_, 1)); // is * 10
|
||||
has_update(telegram->read_bitvalue(pumpStatus_, 3, 2)); // is 0 or 0x64 (100%), check only bit 2
|
||||
has_update(telegram->read_value(flowSetTemp_, 0));
|
||||
has_update(telegram->read_value(status_, 4)); // valve status -100 to 100
|
||||
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
|
||||
}
|
||||
|
||||
// Pool mixer MP100, - 0x5BA
|
||||
void Mixer::process_HpPoolStatus(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(poolTemp_, 0));
|
||||
has_update(telegram->read_value(poolShuntStatus__, 2));
|
||||
has_update(telegram->read_value(poolShunt_, 3)); // 0-100% how much is the shunt open?
|
||||
poolShuntStatus_ = poolShunt_ == 100 ? 3 : (poolShunt_ == 0 ? 4 : poolShuntStatus__);
|
||||
}
|
||||
/*
|
||||
* The set-messages are not broadcasted and send from thermostat to mixer,
|
||||
* we have to fetch for processing
|
||||
*/
|
||||
|
||||
// Mixer on a MM10 - 0xAA
|
||||
// e.g. Thermostat -> Mixer Module, type 0xAA, telegram: 10 21 AA 00 FF 0C 0A 11 0A 32 xx
|
||||
void Mixer::process_MMConfigMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(activated_, 0)); // on = 0xFF
|
||||
has_update(telegram->read_value(setValveTime_, 1)); // valve runtime in 10 sec, max 120 s
|
||||
has_update(telegram, activated_, 0); // on = 0xFF
|
||||
has_update(telegram, setValveTime_, 1); // valve runtime in 10 sec, max 120 s
|
||||
}
|
||||
|
||||
// Config message 0x313, has to be fetched
|
||||
void Mixer::process_MMPLUSConfigMessage_WWC(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, wwRequiredTemp_, 4);
|
||||
has_update(telegram, wwReducedTemp_, 5);
|
||||
has_update(telegram, wwDiffTemp_, 7);
|
||||
has_update(telegram, wwDisinfectionTemp_, 9);
|
||||
has_update(telegram, wwMaxTemp_, 10);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
// Thermostat(0x10) -> Mixer(0x20), ?(0x2E1), data: 01 1C 64 00 01
|
||||
// Thermostat(0x10) -> Mixing Module(0x20), (0x2E1), data: 01 00 00 00 01
|
||||
// Thermostat(0x10) -> Mixing Module(0x20), (0x2EB), data: 00
|
||||
void Mixer::process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram) {
|
||||
// pos 1: setpoint
|
||||
// pos2: pump
|
||||
}
|
||||
|
||||
// unknown, 2 examples from older threads
|
||||
// Thermostat(0x10) -> Mixer(0x28), ?(0x33B), data: 01 01 00
|
||||
// Thermostat -> Mixing Module, type 0x023B, telegram: 90 28 FF 00 02 3B 00 02 00 (CRC=68)
|
||||
void Mixer::process_MMPLUSSetMessage_WWC(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
// MMPLUS telegram 0x345 unknown
|
||||
// Solar Module -> Mixing Module, type 0x0245, telegram: B0 28 FF 00 02 45 64 01 01 (CRC=36)
|
||||
// ?
|
||||
|
||||
// Mixer on a MM10 - 0xAC
|
||||
// e.g. Thermostat -> Mixer Module, type 0xAC, telegram: 10 21 AC 00 1E 64 01 AB
|
||||
void Mixer::process_MMSetMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// pos 0: flowtemp setpoint 1E = 30°C
|
||||
// pos 1: position in %
|
||||
// pos 1: pump in %
|
||||
// pos 2 flags (mostly 01)
|
||||
// LOG_INFO("MM10 SetMessage received");
|
||||
}
|
||||
|
||||
// Thermostat(0x10) -> Mixer(0x21), ?(0x23), data: 1A 64 00 90 21 23 00 1A 64 00 89
|
||||
void Mixer::process_IPMSetMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
// pos 0: flowtemp setpoint 1A = 26°C
|
||||
// pos 1: position in %?
|
||||
// pos 1: pump in %?
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
@@ -261,7 +246,6 @@ bool Mixer::set_flowSetTemp(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2number(value, v)) {
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(F("Setting mixer flow set temperature to %d"), v);
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
write_command(0xAC, 0, v, 0xAB);
|
||||
return true;
|
||||
@@ -283,7 +267,6 @@ bool Mixer::set_pump(const char * value, const int8_t id) {
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(F("Setting mixer pump %s"), b ? "on" : "off");
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
write_command(0xAC, 1, b ? 0x64 : 0, 0xAB);
|
||||
return true;
|
||||
@@ -306,7 +289,6 @@ bool Mixer::set_activated(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
LOG_INFO(F("Setting mixer %s"), value);
|
||||
write_command(0xAA, 0, b ? 0xFF : 0, 0xAA);
|
||||
return true;
|
||||
}
|
||||
@@ -320,11 +302,81 @@ bool Mixer::set_setValveTime(const char * value, const int8_t id) {
|
||||
}
|
||||
if (flags() == EMSdevice::EMS_DEVICE_FLAG_MM10) {
|
||||
v = (v + 5) / 10;
|
||||
LOG_INFO(F("Setting mixer valve time to %ds"), v * 10);
|
||||
write_command(0xAA, 1, v, 0xAA);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mixer::set_wwMaxTemp(const char * value, const int8_t id) {
|
||||
uint8_t wwc = device_id() - 0x28;
|
||||
float v = 0;
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x313 + wwc, 10, (uint8_t)v, 0x313 + wwc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mixer::set_wwDiffTemp(const char * value, const int8_t id) {
|
||||
uint8_t wwc = device_id() - 0x28;
|
||||
float v = 0;
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x313 + wwc, 7, (int8_t)(v * 10), 0x313 + wwc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mixer::set_wwReducedTemp(const char * value, const int8_t id) {
|
||||
uint8_t wwc = device_id() - 0x28;
|
||||
float v = 0;
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x313 + wwc, 5, (uint8_t)v, 0x313 + wwc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mixer::set_wwRequiredTemp(const char * value, const int8_t id) {
|
||||
uint8_t wwc = device_id() - 0x28;
|
||||
float v = 0;
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x313 + wwc, 4, (uint8_t)v, 0x313 + wwc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mixer::set_wwDisinfectionTemp(const char * value, const int8_t id) {
|
||||
uint8_t wwc = device_id() - 0x28;
|
||||
float v = 0;
|
||||
if (!Helpers::value2temperature(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x313 + wwc, 9, (uint8_t)v, 0x313 + wwc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mixer::set_wwCircPump(const char * value, const int8_t id) {
|
||||
uint8_t wwc = device_id() - 0x28;
|
||||
bool v = false;
|
||||
if (!Helpers::value2bool(value, v)) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x33B + wwc, 0, v ? 0x01 : 0x00, 0x33B + wwc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mixer::set_wwCircMode(const char * value, const int8_t id) {
|
||||
uint8_t wwc = device_id() - 0x28;
|
||||
uint8_t n;
|
||||
if (!Helpers::value2enum(value, n, FL_(enum_wwCircMode))) {
|
||||
return false;
|
||||
}
|
||||
write_command(0x313 + wwc, 0, n, 0x313 + wwc);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -25,15 +25,16 @@ namespace emsesp {
|
||||
|
||||
class Mixer : public EMSdevice {
|
||||
public:
|
||||
Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
Mixer(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void process_MMPLUSStatusMessage_HC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MMPLUSSetMessage_HC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MMPLUSStatusMessage_WWC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MMPLUSSetMessage_WWC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_MMPLUSConfigMessage_WWC(std::shared_ptr<const Telegram> telegram);
|
||||
void process_IPMStatusMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_IPMTempMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_IPMSetMessage(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -47,12 +48,20 @@ class Mixer : public EMSdevice {
|
||||
bool set_activated(const char * value, const int8_t id);
|
||||
bool set_setValveTime(const char * value, const int8_t id);
|
||||
|
||||
bool set_wwMaxTemp(const char * value, const int8_t id);
|
||||
bool set_wwDiffTemp(const char * value, const int8_t id);
|
||||
bool set_wwReducedTemp(const char * value, const int8_t id);
|
||||
bool set_wwRequiredTemp(const char * value, const int8_t id);
|
||||
bool set_wwDisinfectionTemp(const char * value, const int8_t id);
|
||||
bool set_wwCircPump(const char * value, const int8_t id);
|
||||
bool set_wwCircMode(const char * value, const int8_t id);
|
||||
|
||||
|
||||
enum class Type {
|
||||
NONE,
|
||||
HC, // heating circuit
|
||||
WWC, // warm water circuit
|
||||
MP // pool
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -64,14 +73,23 @@ class Mixer : public EMSdevice {
|
||||
uint8_t activated_;
|
||||
uint8_t setValveTime_;
|
||||
|
||||
// MM100wwParam - 0x0313, 0x033B
|
||||
uint8_t wwMaxTemp_;
|
||||
uint8_t wwRequiredTemp_;
|
||||
uint8_t wwReducedTemp_;
|
||||
uint8_t wwDiffTemp_;
|
||||
uint8_t wwDisinfectionTemp_;
|
||||
uint8_t wwCircPump_;
|
||||
uint8_t wwCircMode_;
|
||||
|
||||
// MP100 pool
|
||||
int16_t poolTemp_;
|
||||
int8_t poolShuntStatus_;
|
||||
int8_t poolShunt_;
|
||||
uint8_t poolShuntStatus_;
|
||||
uint8_t poolShunt_;
|
||||
|
||||
Type type_ = Type::NONE;
|
||||
uint16_t hc_ = EMS_VALUE_USHORT_NOTSET;
|
||||
int8_t poolShuntStatus__ = EMS_VALUE_INT_NOTSET; // temp value
|
||||
uint8_t id_;
|
||||
uint8_t poolShuntStatus__ = EMS_VALUE_UINT_NOTSET; // temp value
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,26 +25,42 @@ namespace emsesp {
|
||||
|
||||
class Solar : public EMSdevice {
|
||||
public:
|
||||
Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
int16_t collectorTemp_; // TS1: Temperature sensor for collector array 1
|
||||
int16_t tankBottomTemp_; // TS2: Temperature sensor 1 cylinder, bottom tank (solar thermal system)
|
||||
int16_t tankBottomTemp2_; // TS5: Temperature sensor 2 cylinder, bottom tank, or swimming pool (solar thermal system)
|
||||
int16_t heatExchangerTemp_; // TS6: Heat exchanger temperature sensor
|
||||
uint8_t solarPumpModulation_; // PS1: modulation solar pump
|
||||
uint8_t cylinderPumpModulation_; // PS5: modulation cylinder pump
|
||||
uint8_t solarPump_; // PS1: solar pump active
|
||||
uint8_t valveStatus_; // VS2: status 3-way valve for cylinder 2 (solar thermal system) with valve
|
||||
int16_t collectorTemp_; // TS1: Temperature sensor for collector array 1
|
||||
int16_t cylBottomTemp_; // TS2: Temperature sensor 1 cylinder, bottom cyl (solar thermal system)
|
||||
int16_t cylBottomTemp2_; // TS5: Temperature sensor 2 cylinder, bottom cyl, or swimming pool (solar thermal system)
|
||||
int16_t heatExchangerTemp_; // TS6: Heat exchanger temperature sensor
|
||||
int16_t collector2Temp_; // TS7: Temperature sensor for collector array 2
|
||||
int16_t cylMiddleTemp_; // TS14: Cylinder middle temp
|
||||
int16_t retHeatAssist_; // TS15: return temperature heating assistance
|
||||
uint8_t solarPumpMod_; // PS1: modulation solar pump
|
||||
uint8_t cylPumpMod_; // PS5: modulation cylinder pump
|
||||
uint8_t solarPump_; // PS1: solar pump active
|
||||
uint8_t valveStatus_; // VS2: status 3-way valve for cylinder 2 (solar thermal system) with valve
|
||||
uint8_t solarPump2_; // PS4: solar pump 2 active
|
||||
uint8_t solarPump2Mod_; // PS4: modulation solar pump
|
||||
uint8_t m1Valve_; // M1: heat assistance valve
|
||||
uint8_t m1Power_; // M1: heat assistance valve
|
||||
|
||||
// 0x363 heat counter
|
||||
uint16_t heatCntFlowTemp_;
|
||||
uint16_t heatCntRetTemp_;
|
||||
uint8_t heatCnt_;
|
||||
uint16_t swapFlowTemp_;
|
||||
uint16_t swapRetTemp_;
|
||||
|
||||
// 0x38E
|
||||
uint32_t energyLastHour_;
|
||||
uint32_t energyToday_;
|
||||
uint32_t energyTotal_;
|
||||
uint32_t pumpWorkTime_; // Total solar pump operating time
|
||||
uint8_t tankHeated_;
|
||||
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
|
||||
|
||||
uint8_t availabilityFlag_;
|
||||
@@ -53,57 +69,104 @@ class Solar : public EMSdevice {
|
||||
|
||||
// telegram 0x0358
|
||||
uint8_t heatTransferSystem_; // Umladesystem, 00=no
|
||||
uint8_t externalTank_; // Heat exchanger, 00=no
|
||||
uint8_t externalCyl_; // Heat exchanger, 00=no
|
||||
uint8_t thermalDisinfect_; // Daily heatup for disinfection, 00=no
|
||||
uint8_t heatMetering_; // Wärmemengenzählung, 00=no
|
||||
uint8_t heatMetering_; // Heat quantity metering, 00=no
|
||||
uint8_t solarIsEnabled_; // System enable, 00=no
|
||||
|
||||
// telegram 0x035A
|
||||
uint8_t collectorMaxTemp_; // maximum allowed collectorTemp array 1
|
||||
uint8_t tankMaxTemp_; // Current value for max tank temp
|
||||
uint8_t collectorMaxTemp_; // maximum allowed collectorTemp array 1
|
||||
uint8_t cylMaxTemp_; // Current value for max cyl temp
|
||||
// uint8_t cyl2MaxTemp_; // Current value for max cyl temp
|
||||
uint8_t collectorMinTemp_; // minimum allowed collectorTemp array 1
|
||||
uint8_t solarPumpMode_; // 00=off, 01=PWM, 02=10V
|
||||
uint8_t solarPumpMinMod_; // minimum modulation setting, *5 %
|
||||
uint8_t solarPumpTurnoffDiff_; // solar pump turnoff collector/tank diff
|
||||
uint8_t solarPumpTurnonDiff_; // solar pump turnon collector/tank diff
|
||||
uint8_t solarPumpMinMod_; // minimum modulation setting
|
||||
uint8_t solarPumpTurnoffDiff_; // solar pump turnoff collector/cyl diff
|
||||
uint8_t solarPumpTurnonDiff_; // solar pump turnon collector/cyl diff
|
||||
uint8_t solarPumpKick_; // pump kick for vacuum collector, 00=off
|
||||
uint8_t plainWaterMode_; // system does not use antifreeze, 00=off
|
||||
uint8_t doubleMatchFlow_; // double Match Flow, 00=off
|
||||
|
||||
// telegram 0x035D
|
||||
uint8_t collector2MaxTemp_; // maximum allowed collectorTemp array 1
|
||||
uint8_t collector2MinTemp_; // minimum allowed collectorTemp array 1
|
||||
uint8_t solarPump2MinMod_; // minimum modulation setting
|
||||
uint8_t solarPump2TurnoffDiff_; // solar pump turnoff collector/cyl diff
|
||||
uint8_t solarPump2TurnonDiff_; // solar pump turnon collector/cyl diff
|
||||
uint8_t solarPump2Kick_; // pump kick for vacuum collector, 00=off
|
||||
uint8_t solarPump2Mode_; // 00=off, 01=PWM, 02=10V
|
||||
|
||||
// telegram 0x35C Heat assistance
|
||||
uint8_t solarHeatAssist_; // is *10
|
||||
|
||||
// telegram 0x035F
|
||||
uint8_t cylPriority_; // 0 or 1
|
||||
|
||||
// telegram 0x361 Differential control
|
||||
uint8_t diffControl_; // is *10
|
||||
|
||||
|
||||
// telegram 0x380
|
||||
uint8_t climateZone_; // climate zone identifier
|
||||
uint16_t collector1Area_; // Area of collector field 1
|
||||
uint8_t collector1Type_; // Type of collector field 1, 01=flat, 02=vacuum
|
||||
uint16_t collector2Area_; // Area of collector field 2
|
||||
uint8_t collector2Type_; // Type of collector field 2, 01=flat, 02=vacuum
|
||||
|
||||
// SM100wwTemperature - 0x07D6
|
||||
uint8_t wwTemp_1_;
|
||||
uint8_t wwTemp_3_;
|
||||
uint8_t wwTemp_4_;
|
||||
uint8_t wwTemp_5_;
|
||||
uint8_t wwTemp_7_;
|
||||
uint16_t wwTemp_1_;
|
||||
uint16_t wwTemp_3_;
|
||||
uint16_t wwTemp_4_;
|
||||
uint16_t wwTemp_5_;
|
||||
uint16_t wwTemp_7_;
|
||||
|
||||
// SM100wwStatus - 0x07AA
|
||||
uint8_t wwPump_;
|
||||
|
||||
// SM100wwParam - 0x07A6
|
||||
uint8_t wwMaxTemp_;
|
||||
uint8_t wwTemp_;
|
||||
uint8_t wwRedTemp_;
|
||||
uint8_t wwDailyTemp_;
|
||||
uint8_t wwDisinfectionTemp_;
|
||||
|
||||
// SM100wwKeepWarm - 0x07AE
|
||||
uint8_t wwKeepWarm_;
|
||||
|
||||
// SM100wwCirc - 0x07A5
|
||||
uint8_t wwCirc_;
|
||||
uint8_t wwCircMode_;
|
||||
|
||||
// SM100wwStatus2 - 0x07E0
|
||||
uint8_t wwFlow_;
|
||||
uint8_t wwPumpMod_;
|
||||
uint8_t wwStatus2_;
|
||||
|
||||
// SM10Config - 0x96
|
||||
uint8_t wwMinTemp_;
|
||||
uint8_t maxFlow_; // set this to calculate power
|
||||
uint32_t solarPower_; // calculated from maxFlow
|
||||
uint8_t wwMinTemp_;
|
||||
uint8_t maxFlow_; // set this to calculate power
|
||||
int16_t solarPower_; // calculated from maxFlow
|
||||
|
||||
std::deque<uint16_t> energy;
|
||||
uint8_t data0_;
|
||||
uint8_t data1_;
|
||||
uint8_t data11_;
|
||||
uint8_t data12_;
|
||||
uint8_t setting3_;
|
||||
uint8_t setting4_;
|
||||
|
||||
char type_[20]; // Solar of WWC
|
||||
uint8_t id_;
|
||||
std::deque<int16_t> energy;
|
||||
|
||||
void process_SM10Monitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM10Config(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100SystemConfig(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100SolarCircuitConfig(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100CircuitConfig(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Circuit2Config(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100ParamCfg(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Monitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Monitor2(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_SM100Config(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Config1(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_SM100Status(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Status2(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -111,9 +174,16 @@ class Solar : public EMSdevice {
|
||||
void process_SM100Energy(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Time(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_SM100HeatAssist(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100Differential(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_SM100wwTemperature(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100wwStatus(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100wwStatus2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100wwCommand(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100wwCirc(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100wwParam(std::shared_ptr<const Telegram> telegram);
|
||||
void process_SM100wwKeepWarm(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_ISM1Set(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -121,26 +191,47 @@ class Solar : public EMSdevice {
|
||||
|
||||
bool set_CollectorMaxTemp(const char * value, const int8_t id);
|
||||
bool set_CollectorMinTemp(const char * value, const int8_t id);
|
||||
bool set_TankMaxTemp(const char * value, const int8_t id);
|
||||
bool set_cylMaxTemp(const char * value, const int8_t id);
|
||||
bool set_PumpMinMod(const char * value, const int8_t id);
|
||||
bool set_wwMinTemp(const char * value, const int8_t id);
|
||||
bool set_TurnonDiff(const char * value, const int8_t id);
|
||||
bool set_TurnoffDiff(const char * value, const int8_t id);
|
||||
|
||||
bool set_Collector2MaxTemp(const char * value, const int8_t id);
|
||||
bool set_Collector2MinTemp(const char * value, const int8_t id);
|
||||
bool set_Pump2MinMod(const char * value, const int8_t id);
|
||||
bool set_TurnonDiff2(const char * value, const int8_t id);
|
||||
bool set_TurnoffDiff2(const char * value, const int8_t id);
|
||||
|
||||
bool set_SM10MaxFlow(const char * value, const int8_t id);
|
||||
// SM100
|
||||
bool set_heatTransferSystem(const char * value, const int8_t id);
|
||||
bool set_externalTank(const char * value, const int8_t id);
|
||||
bool set_externalCyl(const char * value, const int8_t id);
|
||||
bool set_thermalDisinfect(const char * value, const int8_t id);
|
||||
bool set_heatMetering(const char * value, const int8_t id);
|
||||
bool set_solarEnabled(const char * value, const int8_t id);
|
||||
bool set_solarMode(const char * value, const int8_t id);
|
||||
bool set_solarPumpKick(const char * value, const int8_t id);
|
||||
bool set_solarPump2Kick(const char * value, const int8_t id);
|
||||
bool set_plainWaterMode(const char * value, const int8_t id);
|
||||
bool set_doubleMatchFlow(const char * value, const int8_t id);
|
||||
bool set_climateZone(const char * value, const int8_t id);
|
||||
bool set_collector1Area(const char * value, const int8_t id);
|
||||
bool set_collector1Type(const char * value, const int8_t id);
|
||||
bool set_collector2Area(const char * value, const int8_t id);
|
||||
bool set_collector2Type(const char * value, const int8_t id);
|
||||
bool set_cylPriority(const char * value, const int8_t id);
|
||||
bool set_heatAssist(const char * value, const int8_t id);
|
||||
bool set_diffControl(const char * value, const int8_t id);
|
||||
|
||||
bool set_wwTemp(const char * value, const int8_t id);
|
||||
bool set_wwMaxTemp(const char * value, const int8_t id);
|
||||
bool set_wwRedTemp(const char * value, const int8_t id);
|
||||
bool set_wwCirc(const char * value, const int8_t id);
|
||||
bool set_wwCircMode(const char * value, const int8_t id);
|
||||
bool set_wwKeepWarm(const char * value, const int8_t id);
|
||||
bool set_wwDisinfectionTemp(const char * value, const int8_t id);
|
||||
bool set_wwDailyTemp(const char * value, const int8_t id);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -22,76 +22,35 @@ namespace emsesp {
|
||||
|
||||
REGISTER_FACTORY(Switch, EMSdevice::DeviceType::SWITCH);
|
||||
|
||||
uuid::log::Logger Switch::logger_ {
|
||||
F_(switch), uuid::log::Facility::CONSOLE
|
||||
};
|
||||
|
||||
Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
|
||||
register_telegram_type(0x9C, F("WM10MonitorMessage"), false, MAKE_PF_CB(process_WM10MonitorMessage));
|
||||
register_telegram_type(0x9D, F("WM10SetMessage"), false, MAKE_PF_CB(process_WM10SetMessage));
|
||||
register_telegram_type(0x1E, F("WM10TempMessage"), false, MAKE_PF_CB(process_WM10TempMessage));
|
||||
|
||||
register_device_value(TAG_NONE, &id_, DeviceValueType::UINT, nullptr, FL_(ID), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE);
|
||||
register_device_value(TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(TAG_NONE, &status_, DeviceValueType::INT, nullptr, FL_(status), DeviceValueUOM::NONE);
|
||||
id_ = product_id;
|
||||
}
|
||||
|
||||
// publish HA config
|
||||
bool Switch::publish_ha_device_config() {
|
||||
// if we don't have valid values don't add it ever again
|
||||
if (!Helpers::hasValue(flowTempHc_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["uniq_id"] = F_(switch);
|
||||
doc["ic"] = F_(icondevice);
|
||||
|
||||
char stat_t[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/%s", Mqtt::base().c_str(), Mqtt::tag_to_topic(device_type(), DeviceValueTAG::TAG_NONE).c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
char name_s[40];
|
||||
snprintf(name_s, sizeof(name_s), FSTR_(productid_fmt), device_type_name().c_str());
|
||||
doc["name"] = name_s;
|
||||
|
||||
doc["val_tpl"] = FJSON("{{value_json.id}}");
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = FJSON("EMS-ESP Switch");
|
||||
dev["sw"] = EMSESP_APP_VERSION;
|
||||
dev["mf"] = brand_to_string();
|
||||
dev["mdl"] = this->name();
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp-switch");
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/switch/config", Mqtt::base().c_str());
|
||||
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
return true;
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &activated_, DeviceValueType::BOOL, nullptr, FL_(activated), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &flowTempHc_, DeviceValueType::USHORT, FL_(div10), FL_(flowTempHc), DeviceValueUOM::DEGREES);
|
||||
register_device_value(DeviceValueTAG::TAG_NONE, &status_, DeviceValueType::INT, nullptr, FL_(status), DeviceValueUOM::NONE);
|
||||
}
|
||||
|
||||
// message 0x9D switch on/off
|
||||
// Thermostat(0x10) -> Switch(0x11), ?(0x9D), data: 00
|
||||
void Switch::process_WM10SetMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(activated_, 0));
|
||||
has_update(telegram, activated_, 0);
|
||||
}
|
||||
|
||||
// message 0x9C holds flowtemp and unknown status value
|
||||
// Switch(0x11) -> All(0x00), ?(0x9C), data: 01 BA 00 01 00
|
||||
void Switch::process_WM10MonitorMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(flowTempHc_, 0)); // is * 10
|
||||
has_update(telegram->read_value(status_, 2));
|
||||
// has_update(telegram->read_value(status2_, 3)); // unknown
|
||||
has_update(telegram, flowTempHc_, 0); // is * 10
|
||||
has_update(telegram, status_, 2);
|
||||
// has_update(telegram, status2_, 3)); // unknown
|
||||
}
|
||||
|
||||
// message 0x1E flow temperature, same as in 9C, published often, republished also by boiler UBAFast 0x18
|
||||
// Switch(0x11) -> Boiler(0x08), ?(0x1E), data: 01 BA
|
||||
void Switch::process_WM10TempMessage(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram->read_value(flowTempHc_, 0)); // is * 10
|
||||
has_update(telegram, flowTempHc_, 0); // is * 10
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -25,13 +25,9 @@ namespace emsesp {
|
||||
|
||||
class Switch : public EMSdevice {
|
||||
public:
|
||||
Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
void process_WM10SetMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_WM10MonitorMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_WM10TempMessage(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -39,7 +35,6 @@ class Switch : public EMSdevice {
|
||||
uint16_t flowTempHc_;
|
||||
uint8_t status_;
|
||||
uint8_t activated_;
|
||||
uint8_t id_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ namespace emsesp {
|
||||
|
||||
class Thermostat : public EMSdevice {
|
||||
public:
|
||||
Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
Thermostat(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand);
|
||||
class HeatingCircuit {
|
||||
public:
|
||||
HeatingCircuit(const uint8_t hc_num, const uint8_t model)
|
||||
@@ -34,12 +34,11 @@ class Thermostat : public EMSdevice {
|
||||
}
|
||||
~HeatingCircuit() = default;
|
||||
|
||||
int16_t setpoint_roomTemp;
|
||||
int16_t curr_roomTemp;
|
||||
int16_t selTemp;
|
||||
int16_t roomTemp;
|
||||
int16_t remotetemp; // for readback
|
||||
uint8_t tempautotemp;
|
||||
uint8_t mode;
|
||||
uint8_t hamode; // special mode for HA. See https://github.com/emsesp/EMS-ESP32/issues/66
|
||||
uint8_t modetype;
|
||||
uint8_t summermode;
|
||||
uint8_t holidaymode;
|
||||
@@ -68,8 +67,11 @@ class Thermostat : public EMSdevice {
|
||||
int8_t noreducetemp; // signed -20°C to +10°C
|
||||
uint8_t wwprio;
|
||||
uint8_t fastHeatup;
|
||||
char holiday[22];
|
||||
char vacation[22];
|
||||
char holiday[26];
|
||||
char vacation[26];
|
||||
char switchtime1[16];
|
||||
char switchtime2[16];
|
||||
|
||||
// RC 10
|
||||
uint8_t reducehours; // night reduce duration
|
||||
uint16_t reduceminutes; // remaining minutes to night->day
|
||||
@@ -78,13 +80,25 @@ class Thermostat : public EMSdevice {
|
||||
return hc_num_;
|
||||
}
|
||||
|
||||
uint8_t hc() const {
|
||||
return hc_num_ - 1;
|
||||
}
|
||||
|
||||
uint8_t get_model() const {
|
||||
return model_;
|
||||
}
|
||||
|
||||
// determines if the heating circuit is actually present and has data
|
||||
bool is_active() {
|
||||
return Helpers::hasValue(setpoint_roomTemp);
|
||||
return Helpers::hasValue(selTemp);
|
||||
}
|
||||
|
||||
bool ha_climate_created() {
|
||||
return ha_climate_created_;
|
||||
}
|
||||
|
||||
void ha_climate_created(bool ha_climate_created) {
|
||||
ha_climate_created_ = ha_climate_created;
|
||||
}
|
||||
|
||||
uint8_t get_mode() const;
|
||||
@@ -116,19 +130,18 @@ class Thermostat : public EMSdevice {
|
||||
};
|
||||
|
||||
// for sorting based on hc number
|
||||
friend inline bool operator<(const std::shared_ptr<HeatingCircuit> & lhs, const std::shared_ptr<HeatingCircuit> & rhs) {
|
||||
return (lhs->hc_num_ < rhs->hc_num_);
|
||||
friend inline bool operator<(const std::shared_ptr<HeatingCircuit> & a, const std::shared_ptr<HeatingCircuit> & b) {
|
||||
return (a->hc_num_ < b->hc_num_);
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t hc_num_; // heating circuit number 1..10
|
||||
uint8_t model_; // the model type
|
||||
uint8_t hc_num_; // heating circuit number 1..10
|
||||
uint8_t model_; // the model type
|
||||
bool ha_climate_created_; // if we need to create the HA climate control
|
||||
};
|
||||
|
||||
static std::string mode_tostring(uint8_t mode);
|
||||
|
||||
virtual bool publish_ha_device_config();
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
@@ -144,18 +157,17 @@ class Thermostat : public EMSdevice {
|
||||
std::vector<uint16_t> monitor_typeids;
|
||||
std::vector<uint16_t> set_typeids;
|
||||
std::vector<uint16_t> timer_typeids;
|
||||
std::vector<uint16_t> timer2_typeids;
|
||||
std::vector<uint16_t> summer_typeids;
|
||||
std::vector<uint16_t> summer2_typeids;
|
||||
std::vector<uint16_t> curve_typeids;
|
||||
|
||||
// standard for all thermostats
|
||||
uint8_t id_; // product id
|
||||
char status_[20]; // online or offline
|
||||
char dateTime_[25]; // date and time stamp
|
||||
char errorCode_[15]; // code from 0xA2 as string i.e. "A22(816)"
|
||||
uint16_t errorNumber_; // used internally to build error code
|
||||
char lastCode_[30]; // error log
|
||||
uint8_t dummy_; // for commands with no output
|
||||
char lastCode_[50]; // error log
|
||||
|
||||
// Installation parameters
|
||||
uint8_t ibaMainDisplay_; // display on Thermostat: 0 int temp, 1 int setpoint, 2 ext temp, 3 burner temp, 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
|
||||
@@ -184,13 +196,17 @@ class Thermostat : public EMSdevice {
|
||||
uint8_t wwSetTempLow_;
|
||||
uint8_t wwCharge_;
|
||||
uint8_t wwChargeDuration_;
|
||||
uint8_t wwDisinfect_;
|
||||
uint8_t wwDisinfecting_;
|
||||
uint8_t wwDisinfectDay_;
|
||||
uint8_t wwDisinfectHour_;
|
||||
uint8_t wwMaxTemp_;
|
||||
uint8_t wwOneTimeKey_;
|
||||
uint8_t wwProgMode_;
|
||||
uint8_t wwCircProg_;
|
||||
char wwSwitchTime_[16];
|
||||
char wwCircSwitchTime_[16];
|
||||
uint8_t wwDailyHeating_;
|
||||
uint8_t wwDailyHeatTime_;
|
||||
|
||||
std::vector<std::shared_ptr<HeatingCircuit>> heating_circuits_; // each thermostat can have multiple heating circuits
|
||||
|
||||
@@ -208,7 +224,8 @@ class Thermostat : public EMSdevice {
|
||||
static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_setpoint = 1; // setpoint temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC20StatusMessage_curr = 2; // current temp
|
||||
static constexpr uint8_t EMS_OFFSET_RC20Set_mode = 23; // position of thermostat mode
|
||||
static constexpr uint8_t EMS_OFFSET_RC20Set_temp = 28; // position of thermostat setpoint temperature
|
||||
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
|
||||
@@ -280,6 +297,7 @@ class Thermostat : public EMSdevice {
|
||||
void register_device_values_hc(std::shared_ptr<Thermostat::HeatingCircuit> hc);
|
||||
|
||||
bool thermostat_ha_cmd(const char * message, uint8_t hc_num);
|
||||
void add_ha_climate(std::shared_ptr<HeatingCircuit> hc);
|
||||
|
||||
void process_RCOutdoorTemp(std::shared_ptr<const Telegram> telegram);
|
||||
void process_IBASettings(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -287,6 +305,7 @@ class Thermostat : public EMSdevice {
|
||||
void process_RCError(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RCErrorMessage(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC35wwSettings(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC35wwTimer(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC35Monitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC35Set(std::shared_ptr<const Telegram> telegram);
|
||||
void process_RC35Timer(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -316,12 +335,14 @@ class Thermostat : public EMSdevice {
|
||||
void process_JunkersSet(std::shared_ptr<const Telegram> telegram);
|
||||
void process_JunkersSet2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_EasyMonitor(std::shared_ptr<const Telegram> telegram);
|
||||
void process_JunkersRemoteMonitor(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
// internal helper functions
|
||||
bool set_mode_n(const uint8_t mode, const uint8_t hc_num);
|
||||
|
||||
bool set_temperature_value(const char * value, const int8_t id, const uint8_t mode);
|
||||
bool set_temperature_value(const char * value, const int8_t id, const uint8_t mode, bool relative = false);
|
||||
bool set_temperature(const float temperature, const uint8_t mode, const uint8_t hc_num);
|
||||
bool set_switchtime(const char * value, const uint16_t type_id, char * out, size_t len);
|
||||
|
||||
// set functions - these use the id/hc
|
||||
bool set_mode(const char * value, const int8_t id);
|
||||
@@ -354,7 +375,8 @@ class Thermostat : public EMSdevice {
|
||||
bool set_minflowtemp(const char * value, const int8_t id);
|
||||
bool set_maxflowtemp(const char * value, const int8_t id);
|
||||
bool set_reducemode(const char * value, const int8_t id);
|
||||
bool set_switchtime(const char * value, const int8_t id);
|
||||
bool set_switchtime1(const char * value, const int8_t id);
|
||||
bool set_switchtime2(const char * value, const int8_t id);
|
||||
bool set_program(const char * value, const int8_t id);
|
||||
bool set_controlmode(const char * value, const int8_t id);
|
||||
bool set_wwprio(const char * value, const int8_t id);
|
||||
@@ -374,6 +396,10 @@ class Thermostat : public EMSdevice {
|
||||
bool set_wwOneTimeKey(const char * value, const int8_t id);
|
||||
bool set_wwProgMode(const char * value, const int8_t id);
|
||||
bool set_wwCircProg(const char * value, const int8_t id);
|
||||
bool set_wwSwitchTime(const char * value, const int8_t id);
|
||||
bool set_wwCircSwitchTime(const char * value, const int8_t id);
|
||||
bool set_wwDailyHeating(const char * value, const int8_t id);
|
||||
bool set_wwDailyHeatTime(const char * value, const int8_t id);
|
||||
|
||||
bool set_datetime(const char * value, const int8_t id);
|
||||
bool set_minexttemp(const char * value, const int8_t id);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
311
src/emsdevice.h
311
src/emsdevice.h
@@ -19,140 +19,35 @@
|
||||
#ifndef EMSESP_EMSDEVICE_H_
|
||||
#define EMSESP_EMSDEVICE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "emsfactory.h"
|
||||
#include "telegram.h"
|
||||
#include "mqtt.h"
|
||||
#include "helpers.h"
|
||||
#include "emsdevicevalue.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum DeviceValueType : uint8_t {
|
||||
BOOL,
|
||||
INT,
|
||||
UINT,
|
||||
SHORT,
|
||||
USHORT,
|
||||
ULONG,
|
||||
TIME, // same as ULONG (32 bits)
|
||||
ENUM,
|
||||
STRING,
|
||||
CMD // special for commands only
|
||||
|
||||
};
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp. Sequence is important!!
|
||||
// also used with HA as uom
|
||||
enum DeviceValueUOM : uint8_t {
|
||||
|
||||
NONE = 0, // 0
|
||||
DEGREES, // 1
|
||||
PERCENT, // 2
|
||||
LMIN, // 3
|
||||
KWH, // 4
|
||||
WH, // 5
|
||||
HOURS, // 6
|
||||
MINUTES, // 7
|
||||
UA, // 8
|
||||
BAR, // 9
|
||||
KW, // 10
|
||||
W, // 11
|
||||
KB, // 12
|
||||
SECONDS, // 13
|
||||
DBM, // 14
|
||||
MV, // 15
|
||||
TIMES, // 16
|
||||
OCLOCK // 17
|
||||
|
||||
};
|
||||
|
||||
// Home Assistant icons (https://materialdesignicons.com)
|
||||
// the following are used with the UOMs (unit of measurements)
|
||||
MAKE_PSTR(icondegrees, "mdi:coolant-temperature") // DeviceValueUOM::DEGREES
|
||||
MAKE_PSTR(iconpercent, "mdi:percent-outline") // DeviceValueUOM::PERCENT
|
||||
MAKE_PSTR(icontime, "mdi:clock-outline") // DeviceValueUOM::SECONDS MINUTES & HOURS
|
||||
MAKE_PSTR(iconkb, "mdi:memory") // DeviceValueUOM::KB
|
||||
MAKE_PSTR(iconlmin, "mdi:water-boiler") // DeviceValueUOM::LMIN
|
||||
MAKE_PSTR(iconkwh, "mdi:transmission-tower") // DeviceValueUOM::KWH & WH
|
||||
MAKE_PSTR(iconua, "mdi:lightning-bolt-circle") // DeviceValueUOM::UA
|
||||
MAKE_PSTR(iconbar, "mdi:gauge") // DeviceValueUOM::BAR
|
||||
MAKE_PSTR(iconkw, "mdi:omega") // DeviceValueUOM::KW & W
|
||||
MAKE_PSTR(icondbm, "mdi:wifi-strength-2") // DeviceValueUOM::DBM
|
||||
MAKE_PSTR(iconnum, "mdi:counter") // DeviceValueUOM::NONE
|
||||
|
||||
MAKE_PSTR(icondevice, "mdi:home-automation") // for devices in HA
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||
enum DeviceValueTAG : uint8_t {
|
||||
TAG_NONE = 0, // wild card
|
||||
TAG_HEARTBEAT,
|
||||
TAG_BOILER_DATA,
|
||||
TAG_DEVICE_DATA_WW,
|
||||
TAG_THERMOSTAT_DATA,
|
||||
TAG_HC1,
|
||||
TAG_HC2,
|
||||
TAG_HC3,
|
||||
TAG_HC4,
|
||||
TAG_WWC1,
|
||||
TAG_WWC2,
|
||||
TAG_WWC3,
|
||||
TAG_WWC4,
|
||||
TAG_HS1,
|
||||
TAG_HS2,
|
||||
TAG_HS3,
|
||||
TAG_HS4,
|
||||
TAG_HS5,
|
||||
TAG_HS6,
|
||||
TAG_HS7,
|
||||
TAG_HS8,
|
||||
TAG_HS9,
|
||||
TAG_HS10,
|
||||
TAG_HS11,
|
||||
TAG_HS12,
|
||||
TAG_HS13,
|
||||
TAG_HS14,
|
||||
TAG_HS15,
|
||||
TAG_HS16
|
||||
|
||||
};
|
||||
|
||||
// states of a device value
|
||||
enum DeviceValueState : uint8_t {
|
||||
|
||||
DV_DEFAULT = 0, // 0 - does not yet have a value
|
||||
DV_ACTIVE = (1 << 0), // 1 - has a value
|
||||
DV_VISIBLE = (1 << 1), // 2 - shown on web and console
|
||||
DV_HA_CONFIG_CREATED = (1 << 2) // 4 - set if the HA config has been created
|
||||
|
||||
};
|
||||
|
||||
class EMSdevice {
|
||||
public:
|
||||
virtual ~EMSdevice() = default; // destructor of base class must always be virtual because it's a polymorphic class
|
||||
|
||||
static constexpr uint8_t EMS_DEVICES_MAX_TELEGRAMS = 20;
|
||||
|
||||
// virtual functions overrules by derived classes
|
||||
virtual bool publish_ha_device_config() = 0;
|
||||
|
||||
// device_type defines which derived class to use, e.g. BOILER, THERMOSTAT etc..
|
||||
EMSdevice(uint8_t device_type, uint8_t device_id, uint8_t product_id, const std::string & version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
EMSdevice(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const std::string & name, uint8_t flags, uint8_t brand)
|
||||
: device_type_(device_type)
|
||||
, device_id_(device_id)
|
||||
, product_id_(product_id)
|
||||
, version_(version)
|
||||
, name_(name)
|
||||
, flags_(flags)
|
||||
, brand_(brand) {
|
||||
strlcpy(version_, version, sizeof(version_));
|
||||
}
|
||||
|
||||
const std::string device_type_name() const;
|
||||
const std::string device_type_name() const;
|
||||
|
||||
static const std::string device_type_2_device_name(const uint8_t device_type);
|
||||
static uint8_t device_name_2_device_type(const char * topic);
|
||||
|
||||
static const std::string uom_to_string(uint8_t uom);
|
||||
static const std::string tag_to_string(uint8_t tag);
|
||||
static const std::string tag_to_mqtt(uint8_t tag);
|
||||
@@ -191,11 +86,11 @@ class EMSdevice {
|
||||
return device_type_; // see enum DeviceType below
|
||||
}
|
||||
|
||||
inline void version(std::string & version) {
|
||||
version_ = version;
|
||||
inline void version(const char * version) {
|
||||
strlcpy(version_, version, sizeof(version_));
|
||||
}
|
||||
|
||||
inline std::string version() const {
|
||||
inline const char * version() {
|
||||
return version_;
|
||||
}
|
||||
|
||||
@@ -215,6 +110,7 @@ class EMSdevice {
|
||||
return name_;
|
||||
}
|
||||
|
||||
// unique id of a device
|
||||
inline uint8_t unique_id() const {
|
||||
return unique_id_;
|
||||
}
|
||||
@@ -227,8 +123,52 @@ class EMSdevice {
|
||||
return has_update_;
|
||||
}
|
||||
|
||||
inline void has_update(bool has_update) {
|
||||
has_update_ |= has_update;
|
||||
inline void has_update(bool flag) {
|
||||
has_update_ = flag;
|
||||
}
|
||||
|
||||
inline void has_update(void * value) {
|
||||
has_update_ = true;
|
||||
publish_value(value);
|
||||
}
|
||||
|
||||
inline void has_update(char * value, char * newvalue, size_t len) {
|
||||
if (strcmp(value, newvalue) != 0) {
|
||||
strlcpy(value, newvalue, len);
|
||||
has_update_ = true;
|
||||
publish_value(value);
|
||||
}
|
||||
}
|
||||
|
||||
inline void has_update(uint8_t & value, uint8_t newvalue) {
|
||||
if (value != newvalue) {
|
||||
value = newvalue;
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
inline void has_enumupdate(std::shared_ptr<const Telegram> telegram, uint8_t & value, const uint8_t index, uint8_t s = 0) {
|
||||
if (telegram->read_enumvalue(value, index, s)) {
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
inline void has_update(std::shared_ptr<const Telegram> telegram, Value & value, const uint8_t index, uint8_t s = 0) {
|
||||
if (telegram->read_value(value, index, s)) {
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BitValue>
|
||||
inline void has_bitupdate(std::shared_ptr<const Telegram> telegram, BitValue & value, const uint8_t index, uint8_t b) {
|
||||
if (telegram->read_bitvalue(value, index, b)) {
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string brand_to_string() const;
|
||||
@@ -237,10 +177,13 @@ class EMSdevice {
|
||||
const std::string to_string() const;
|
||||
const std::string to_string_short() const;
|
||||
|
||||
enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING };
|
||||
|
||||
void show_telegram_handlers(uuid::console::Shell & shell);
|
||||
char * show_telegram_handlers(char * result);
|
||||
char * show_telegram_handlers(char * result, uint8_t handlers);
|
||||
void show_mqtt_handlers(uuid::console::Shell & shell);
|
||||
void list_device_entries(JsonObject & output);
|
||||
void exclude_entity(uint8_t entity_id);
|
||||
|
||||
using process_function_p = std::function<void(std::shared_ptr<const Telegram>)>;
|
||||
|
||||
@@ -249,10 +192,12 @@ class EMSdevice {
|
||||
|
||||
const std::string get_value_uom(const char * key);
|
||||
bool get_value_info(JsonObject & root, const char * cmd, const int8_t id);
|
||||
void get_dv_info(JsonObject & json);
|
||||
|
||||
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT };
|
||||
bool generate_values_json(JsonObject & output, const uint8_t tag_filter, const bool nested, const uint8_t output_target);
|
||||
void generate_values_json_web(JsonObject & output);
|
||||
bool generate_values(JsonObject & output, const uint8_t tag_filter, const bool nested, const uint8_t output_target);
|
||||
void generate_values_web(JsonObject & output);
|
||||
void generate_values_web_all(JsonArray & output);
|
||||
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
@@ -262,8 +207,9 @@ class EMSdevice {
|
||||
const __FlashStringHelper * full_name,
|
||||
uint8_t uom,
|
||||
bool has_cmd,
|
||||
int32_t min,
|
||||
uint32_t max);
|
||||
int16_t min,
|
||||
uint16_t max);
|
||||
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
@@ -271,8 +217,9 @@ class EMSdevice {
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int32_t min,
|
||||
uint32_t max);
|
||||
int16_t min,
|
||||
uint16_t max);
|
||||
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
@@ -280,6 +227,7 @@ class EMSdevice {
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f);
|
||||
|
||||
void register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
@@ -293,6 +241,9 @@ class EMSdevice {
|
||||
|
||||
void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0);
|
||||
|
||||
void publish_value(void * value);
|
||||
void publish_all_values();
|
||||
|
||||
void publish_mqtt_ha_entity_config();
|
||||
|
||||
const std::string telegram_type_name(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -300,16 +251,22 @@ class EMSdevice {
|
||||
void fetch_values();
|
||||
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
||||
bool is_fetch(uint16_t telegram_id);
|
||||
bool has_telegram_id(uint16_t id);
|
||||
void ha_config_clear();
|
||||
|
||||
bool ha_config_done() const {
|
||||
return ha_config_done_;
|
||||
}
|
||||
|
||||
void ha_config_done(const bool v) {
|
||||
ha_config_done_ = v;
|
||||
}
|
||||
|
||||
void ha_config_clear();
|
||||
bool ha_config_firstrun() const {
|
||||
return ha_config_firstrun_;
|
||||
}
|
||||
void ha_config_firstrun(const bool v) {
|
||||
ha_config_firstrun_ = v;
|
||||
}
|
||||
|
||||
enum Brand : uint8_t {
|
||||
NO_BRAND = 0, // 0
|
||||
@@ -325,6 +282,7 @@ class EMSdevice {
|
||||
enum DeviceType : uint8_t {
|
||||
SYSTEM = 0, // this is us (EMS-ESP)
|
||||
DALLASSENSOR, // for internal dallas sensors
|
||||
ANALOGSENSOR, // for internal analog sensors
|
||||
BOILER,
|
||||
THERMOSTAT,
|
||||
MIXER,
|
||||
@@ -374,34 +332,31 @@ class EMSdevice {
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC10 = 2;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC20 = 3;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC20_N = 4; // Variation on RC20, Older, like ES72
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC30_N = 5; // variation on RC30, Newer models
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC30 = 6;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC35 = 7;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC300 = 8;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC100 = 9;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS = 10;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_CRF = 11; // CRF200 only monitor
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC25 = 5;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC30_N = 6; // variation on RC30, Newer models
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC30 = 7;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC35 = 8;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC300 = 9;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC100 = 10;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS = 11;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_CRF = 12; // CRF200 only monitor
|
||||
|
||||
void reserve_device_values(uint8_t elements) {
|
||||
devicevalues_.reserve(elements);
|
||||
}
|
||||
|
||||
void reserve_telgram_functions(uint8_t elements) {
|
||||
telegram_functions_.reserve(elements);
|
||||
}
|
||||
uint8_t count_entities();
|
||||
bool has_entities();
|
||||
|
||||
private:
|
||||
uint8_t unique_id_;
|
||||
uint8_t device_type_ = DeviceType::SYSTEM;
|
||||
uint8_t device_id_ = 0;
|
||||
uint8_t product_id_ = 0;
|
||||
std::string version_;
|
||||
char version_[6];
|
||||
std::string name_; // the long name for the EMS model
|
||||
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;
|
||||
bool ha_config_firstrun_ = true; // this means a first setup of HA is needed after a restart
|
||||
|
||||
struct TelegramFunction {
|
||||
uint16_t telegram_type_id_; // it's type_id
|
||||
@@ -419,78 +374,14 @@ class EMSdevice {
|
||||
}
|
||||
};
|
||||
|
||||
// DeviceValue holds all the attributes for a device value (also a device parameter)
|
||||
struct DeviceValue {
|
||||
uint8_t device_type; // EMSdevice::DeviceType
|
||||
uint8_t tag; // DeviceValueTAG::*
|
||||
void * value_p; // pointer to variable of any type
|
||||
uint8_t type; // DeviceValueType::*
|
||||
const __FlashStringHelper * const * options; // options as a flash char array
|
||||
uint8_t options_size; // number of options in the char array, calculated
|
||||
const __FlashStringHelper * short_name; // used in MQTT
|
||||
const __FlashStringHelper * full_name; // used in Web and Console
|
||||
uint8_t uom; // DeviceValueUOM::*
|
||||
uint8_t ha; // DevcieValueHA::
|
||||
bool has_cmd; // true if there is a Console/MQTT command which matches the short_name
|
||||
int32_t min;
|
||||
uint32_t max;
|
||||
uint8_t state; // DeviceValueState::*
|
||||
|
||||
DeviceValue(uint8_t device_type,
|
||||
uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
uint8_t options_size,
|
||||
const __FlashStringHelper * short_name,
|
||||
const __FlashStringHelper * full_name,
|
||||
uint8_t uom,
|
||||
uint8_t ha,
|
||||
bool has_cmd,
|
||||
int32_t min,
|
||||
uint32_t max,
|
||||
uint8_t state)
|
||||
: device_type(device_type)
|
||||
, tag(tag)
|
||||
, value_p(value_p)
|
||||
, type(type)
|
||||
, options(options)
|
||||
, options_size(options_size)
|
||||
, short_name(short_name)
|
||||
, full_name(full_name)
|
||||
, uom(uom)
|
||||
, ha(ha)
|
||||
, has_cmd(has_cmd)
|
||||
, min(min)
|
||||
, max(max)
|
||||
, state(state) {
|
||||
}
|
||||
|
||||
// state flags
|
||||
inline void add_state(uint8_t s) {
|
||||
state |= s;
|
||||
}
|
||||
inline bool has_state(uint8_t s) const {
|
||||
return (state & s) == s;
|
||||
}
|
||||
inline void remove_state(uint8_t s) {
|
||||
state &= ~s;
|
||||
}
|
||||
inline uint8_t get_state() const {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
const std::vector<DeviceValue> devicevalues() const;
|
||||
|
||||
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||
std::vector<DeviceValue> devicevalues_;
|
||||
|
||||
const std::string device_entity_ha(DeviceValue const & dv);
|
||||
// device values
|
||||
std::vector<DeviceValue> devicevalues_;
|
||||
|
||||
bool check_dv_hasvalue(const DeviceValue & dv);
|
||||
|
||||
void init_devicevalues(uint8_t size) {
|
||||
devicevalues_.reserve(size);
|
||||
uint8_t dv_index_ = 0; // unique counter for each added device value
|
||||
uint8_t get_next_dv_id() {
|
||||
return (dv_index_++);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
250
src/emsdevicevalue.cpp
Normal file
250
src/emsdevicevalue.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 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 "emsdevicevalue.h"
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// mapping of UOM, to match order in DeviceValueUOM enum emsdevice.h
|
||||
// also maps to DeviceValueUOM in interface/src/project/types.ts for the Web UI
|
||||
// must be an int of 4 bytes, 32bit aligned
|
||||
const __FlashStringHelper * DeviceValue::DeviceValueUOM_s[] __attribute__((__aligned__(sizeof(uint32_t)))) PROGMEM = {
|
||||
|
||||
F_(blank),
|
||||
F_(degrees),
|
||||
F_(degrees),
|
||||
F_(percent),
|
||||
F_(lmin),
|
||||
F_(kwh),
|
||||
F_(wh),
|
||||
F_(hours),
|
||||
F_(minutes),
|
||||
F_(ua),
|
||||
F_(bar),
|
||||
F_(kw),
|
||||
F_(w),
|
||||
F_(kb),
|
||||
F_(seconds),
|
||||
F_(dbm),
|
||||
F_(fahrenheit),
|
||||
F_(mv),
|
||||
F_(sqm)
|
||||
|
||||
};
|
||||
|
||||
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevice.h
|
||||
// must be an int of 4 bytes, 32bit aligned
|
||||
const __FlashStringHelper * const DeviceValue::DeviceValueTAG_s[] PROGMEM = {
|
||||
|
||||
F_(tag_none), // ""
|
||||
F_(tag_heartbeat), // ""
|
||||
F_(tag_boiler_data), // ""
|
||||
F_(tag_device_data_ww), // "ww"
|
||||
F_(tag_thermostat_data), // ""
|
||||
F_(tag_hc1), // "hc1"
|
||||
F_(tag_hc2), // "hc2"
|
||||
F_(tag_hc3), // "hc3"
|
||||
F_(tag_hc4), // "hc4"
|
||||
F_(tag_hc5), // "hc5"
|
||||
F_(tag_hc6), // "hc6"
|
||||
F_(tag_hc7), // "hc7"
|
||||
F_(tag_hc8), // "hc8"
|
||||
F_(tag_wwc1), // "wwc1"
|
||||
F_(tag_wwc2), // "Wwc2"
|
||||
F_(tag_wwc3), // "wwc3"
|
||||
F_(tag_wwc4), // "wwc4"
|
||||
F_(tag_hs1), // "hs1"
|
||||
F_(tag_hs2), // "hs2"
|
||||
F_(tag_hs3), // "hs3"
|
||||
F_(tag_hs4), // "hs4"
|
||||
F_(tag_hs5), // "hs5"
|
||||
F_(tag_hs6), // "hs6"
|
||||
F_(tag_hs7), // "hs7"
|
||||
F_(tag_hs8), // "hs8"
|
||||
F_(tag_hs9), // "hs9"
|
||||
F_(tag_hs10), // "hs10"
|
||||
F_(tag_hs11), // "hs11"
|
||||
F_(tag_hs12), // "hs12"
|
||||
F_(tag_hs13), // "hs13"
|
||||
F_(tag_hs14), // "hs14"
|
||||
F_(tag_hs15), // "hs15"
|
||||
F_(tag_hs16) // "hs16"
|
||||
|
||||
};
|
||||
|
||||
// MQTT topics derived from tags
|
||||
const __FlashStringHelper * const DeviceValue::DeviceValueTAG_mqtt[] PROGMEM = {
|
||||
|
||||
F_(tag_none), // ""
|
||||
F_(heartbeat), // "heartbeat"
|
||||
F_(tag_boiler_data_mqtt), // ""
|
||||
F_(tag_device_data_ww_mqtt), // "ww"
|
||||
F_(tag_thermostat_data), // ""
|
||||
F_(tag_hc1), // "hc1"
|
||||
F_(tag_hc2), // "hc2"
|
||||
F_(tag_hc3), // "hc3"
|
||||
F_(tag_hc4), // "hc4"
|
||||
F_(tag_hc5), // "hc5"
|
||||
F_(tag_hc6), // "hc6"
|
||||
F_(tag_hc7), // "hc7"
|
||||
F_(tag_hc8), // "hc8"
|
||||
F_(tag_wwc1), // "wwc1"
|
||||
F_(tag_wwc2), // "Wwc2"
|
||||
F_(tag_wwc3), // "wwc3"
|
||||
F_(tag_wwc4), // "wwc4"
|
||||
F_(tag_hs1), // "hs1"
|
||||
F_(tag_hs2), // "hs2"
|
||||
F_(tag_hs3), // "hs3"
|
||||
F_(tag_hs4), // "hs4"
|
||||
F_(tag_hs5), // "hs5"
|
||||
F_(tag_hs6), // "hs6"
|
||||
F_(tag_hs7), // "hs7"
|
||||
F_(tag_hs8), // "hs8"
|
||||
F_(tag_hs9), // "hs9"
|
||||
F_(tag_hs10), // "hs10"
|
||||
F_(tag_hs11), // "hs11"
|
||||
F_(tag_hs12), // "hs12"
|
||||
F_(tag_hs13), // "hs13"
|
||||
F_(tag_hs14), // "hs14"
|
||||
F_(tag_hs15), // "hs15"
|
||||
F_(tag_hs16) // "hs16"
|
||||
|
||||
};
|
||||
|
||||
// count #tags once at compile time
|
||||
size_t DeviceValue::tag_count = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(__FlashStringHelper *);
|
||||
|
||||
// checks whether the device value has an actual value
|
||||
// returns true if its valid
|
||||
// state is stored in the dv object
|
||||
bool DeviceValue::hasValue() {
|
||||
bool has_value = false;
|
||||
switch (type) {
|
||||
case DeviceValueType::BOOL:
|
||||
has_value = Helpers::hasValue(*(uint8_t *)(value_p), EMS_VALUE_BOOL);
|
||||
break;
|
||||
case DeviceValueType::STRING:
|
||||
has_value = Helpers::hasValue((char *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::ENUM:
|
||||
has_value = Helpers::hasValue(*(uint8_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::INT:
|
||||
has_value = Helpers::hasValue(*(int8_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::UINT:
|
||||
has_value = Helpers::hasValue(*(uint8_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::SHORT:
|
||||
has_value = Helpers::hasValue(*(int16_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::USHORT:
|
||||
has_value = Helpers::hasValue(*(uint16_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::ULONG:
|
||||
has_value = Helpers::hasValue(*(uint32_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::TIME:
|
||||
has_value = Helpers::hasValue(*(uint32_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::CMD:
|
||||
has_value = false; // commands don't have values!
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/196
|
||||
// if (has_state(DeviceValueState::DV_ACTIVE) && !has_value) {
|
||||
// emsesp::EMSESP::logger().warning(F("[DEBUG] Lost device value %s"), short_name);
|
||||
// }
|
||||
#endif
|
||||
|
||||
return has_value;
|
||||
}
|
||||
|
||||
// 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
|
||||
// Types BOOL, ENUM, STRING and CMD are not used
|
||||
bool DeviceValue::get_min_max(int16_t & dv_set_min, int16_t & dv_set_max) {
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
|
||||
// if we have individual limits set already, just do the conversion
|
||||
// limits are not scaled with divider and temperatures are °C
|
||||
if (min != 0 || max != 0) {
|
||||
dv_set_min = Helpers::round2(min, 0, fahrenheit);
|
||||
dv_set_max = Helpers::round2(max, 0, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
// init default values to 0 and 0
|
||||
dv_set_min = 0;
|
||||
dv_set_max = 0;
|
||||
|
||||
int8_t divider = (options_size == 1) ? Helpers::atoint(uuid::read_flash_string(options[0]).c_str()) : 0;
|
||||
|
||||
if (type == DeviceValueType::USHORT) {
|
||||
dv_set_min = Helpers::round2(0, divider, fahrenheit);
|
||||
dv_set_max = Helpers::round2(EMS_VALUE_USHORT_NOTSET, divider, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::SHORT) {
|
||||
dv_set_min = Helpers::round2(-EMS_VALUE_SHORT_NOTSET, divider, fahrenheit);
|
||||
dv_set_max = Helpers::round2(EMS_VALUE_SHORT_NOTSET, divider, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::UINT) {
|
||||
if (uom == DeviceValueUOM::PERCENT) {
|
||||
dv_set_max = 100;
|
||||
} else {
|
||||
dv_set_max = Helpers::round2(EMS_VALUE_UINT_NOTSET, divider, fahrenheit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::INT) {
|
||||
if (uom == DeviceValueUOM::PERCENT) {
|
||||
dv_set_min = -100;
|
||||
dv_set_max = 100;
|
||||
} else {
|
||||
dv_set_min = Helpers::round2(-EMS_VALUE_INT_NOTSET, divider, fahrenheit);
|
||||
dv_set_max = Helpers::round2(EMS_VALUE_INT_NOTSET, divider, fahrenheit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::ULONG) {
|
||||
dv_set_max = Helpers::round2(EMS_VALUE_ULONG_NOTSET, divider);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::TIME) {
|
||||
dv_set_max = Helpers::round2(EMS_VALUE_ULONG_NOTSET, divider);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // nothing changed, not supported
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
190
src/emsdevicevalue.h
Normal file
190
src/emsdevicevalue.h
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 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_EMSDEVICEVALUE_H_
|
||||
#define EMSESP_EMSDEVICEVALUE_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "helpers.h" // for conversions
|
||||
#include "default_settings.h" // for enum types
|
||||
#include <uuid/common.h> // for read_flash_string
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// DeviceValue holds the information for a device entity
|
||||
class DeviceValue {
|
||||
public:
|
||||
enum DeviceValueType : uint8_t {
|
||||
BOOL,
|
||||
INT,
|
||||
UINT,
|
||||
SHORT,
|
||||
USHORT,
|
||||
ULONG,
|
||||
TIME, // same as ULONG (32 bits)
|
||||
ENUM,
|
||||
STRING,
|
||||
CMD // special for commands only
|
||||
};
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp. Sequence is important!!
|
||||
// also used with HA as uom
|
||||
enum DeviceValueUOM : uint8_t {
|
||||
NONE = 0, // 0
|
||||
DEGREES, // 1
|
||||
DEGREES_R, // 2
|
||||
PERCENT, // 3
|
||||
LMIN, // 4
|
||||
KWH, // 5
|
||||
WH, // 6
|
||||
HOURS, // 7
|
||||
MINUTES, // 8
|
||||
UA, // 9
|
||||
BAR, // 10
|
||||
KW, // 11
|
||||
W, // 12
|
||||
KB, // 13
|
||||
SECONDS, // 14
|
||||
DBM, // 15
|
||||
FAHRENHEIT, // 16
|
||||
MV, // 17
|
||||
SQM // 18
|
||||
};
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||
enum DeviceValueTAG : uint8_t {
|
||||
TAG_NONE = 0, // wild card
|
||||
TAG_HEARTBEAT,
|
||||
TAG_BOILER_DATA,
|
||||
TAG_DEVICE_DATA_WW,
|
||||
TAG_THERMOSTAT_DATA,
|
||||
TAG_HC1,
|
||||
TAG_HC2,
|
||||
TAG_HC3,
|
||||
TAG_HC4,
|
||||
TAG_HC5,
|
||||
TAG_HC6,
|
||||
TAG_HC7,
|
||||
TAG_HC8,
|
||||
TAG_WWC1,
|
||||
TAG_WWC2,
|
||||
TAG_WWC3,
|
||||
TAG_WWC4,
|
||||
TAG_HS1,
|
||||
TAG_HS2,
|
||||
TAG_HS3,
|
||||
TAG_HS4,
|
||||
TAG_HS5,
|
||||
TAG_HS6,
|
||||
TAG_HS7,
|
||||
TAG_HS8,
|
||||
TAG_HS9,
|
||||
TAG_HS10,
|
||||
TAG_HS11,
|
||||
TAG_HS12,
|
||||
TAG_HS13,
|
||||
TAG_HS14,
|
||||
TAG_HS15,
|
||||
TAG_HS16
|
||||
};
|
||||
|
||||
// states of a device value
|
||||
enum DeviceValueState : uint8_t {
|
||||
DV_DEFAULT = 0, // 0 - does not yet have a value
|
||||
DV_ACTIVE = (1 << 0), // 1 - has a valid value
|
||||
DV_VISIBLE = (1 << 1), // 2 - shown on web and console, otherwise hidden
|
||||
DV_HA_CONFIG_CREATED = (1 << 2) // 4 - set if the HA config has been created
|
||||
};
|
||||
|
||||
uint8_t device_type; // EMSdevice::DeviceType
|
||||
uint8_t tag; // DeviceValueTAG::*
|
||||
void * value_p; // pointer to variable of any type
|
||||
uint8_t type; // DeviceValueType::*
|
||||
const __FlashStringHelper * const * options; // options as a flash char array
|
||||
uint8_t options_size; // number of options in the char array, calculated
|
||||
const __FlashStringHelper * short_name; // used in MQTT
|
||||
const __FlashStringHelper * full_name; // used in Web and Console
|
||||
uint8_t uom; // DeviceValueUOM::*
|
||||
uint8_t ha; // DevcieValueHA::
|
||||
bool has_cmd; // true if there is a Console/MQTT command which matches the short_name
|
||||
int16_t min; // min range
|
||||
uint16_t max; // max range
|
||||
uint8_t state; // DeviceValueState::*
|
||||
uint8_t id; // internal unique counter
|
||||
|
||||
DeviceValue(uint8_t device_type,
|
||||
uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
uint8_t options_size,
|
||||
const __FlashStringHelper * short_name,
|
||||
const __FlashStringHelper * full_name,
|
||||
uint8_t uom,
|
||||
uint8_t ha,
|
||||
bool has_cmd,
|
||||
int16_t min,
|
||||
uint16_t max,
|
||||
uint8_t state,
|
||||
uint8_t id)
|
||||
: device_type(device_type)
|
||||
, tag(tag)
|
||||
, value_p(value_p)
|
||||
, type(type)
|
||||
, options(options)
|
||||
, options_size(options_size)
|
||||
, short_name(short_name)
|
||||
, full_name(full_name)
|
||||
, uom(uom)
|
||||
, ha(ha)
|
||||
, has_cmd(has_cmd)
|
||||
, min(min)
|
||||
, max(max)
|
||||
, state(state)
|
||||
, id(id) {
|
||||
}
|
||||
|
||||
bool hasValue();
|
||||
bool get_min_max(int16_t & dv_set_min, int16_t & dv_set_max);
|
||||
|
||||
// state flags
|
||||
inline void add_state(uint8_t s) {
|
||||
state |= s;
|
||||
}
|
||||
inline bool has_state(uint8_t s) const {
|
||||
return (state & s) == s;
|
||||
}
|
||||
inline void remove_state(uint8_t s) {
|
||||
state &= ~s;
|
||||
}
|
||||
inline uint8_t get_state() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
static const __FlashStringHelper * DeviceValueUOM_s[];
|
||||
static const __FlashStringHelper * const DeviceValueTAG_s[];
|
||||
static const __FlashStringHelper * const DeviceValueTAG_mqtt[];
|
||||
static size_t tag_count; // # tags
|
||||
};
|
||||
|
||||
}; // namespace emsesp
|
||||
|
||||
#endif
|
||||
401
src/emsesp.cpp
401
src/emsesp.cpp
@@ -23,18 +23,20 @@ namespace emsesp {
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
uint32_t heap_start = 0;
|
||||
#else
|
||||
uint32_t heap_start = ESP.getFreeHeap(); // get initial available heap memory
|
||||
uint32_t heap_start = ESP.getFreeHeap(); // get initial available heap memory
|
||||
#endif
|
||||
|
||||
AsyncWebServer webServer(80);
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
FS dummyFS;
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &dummyFS);
|
||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager());
|
||||
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());
|
||||
#else
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &LITTLEFS);
|
||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &LITTLEFS, EMSESP::esp8266React.getSecurityManager());
|
||||
ESP8266React EMSESP::esp8266React(&webServer, &LITTLEFS);
|
||||
WebSettingsService EMSESP::webSettingsService = WebSettingsService(&webServer, &LITTLEFS, EMSESP::esp8266React.getSecurityManager());
|
||||
WebCustomizationService EMSESP::webCustomizationService = WebCustomizationService(&webServer, &LITTLEFS, EMSESP::esp8266React.getSecurityManager());
|
||||
#endif
|
||||
|
||||
WebStatusService EMSESP::webStatusService = WebStatusService(&webServer, EMSESP::esp8266React.getSecurityManager());
|
||||
@@ -60,6 +62,7 @@ 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
|
||||
|
||||
// static/common variables
|
||||
@@ -74,8 +77,6 @@ uint32_t EMSESP::last_fetch_ = 0;
|
||||
uint8_t EMSESP::publish_all_idx_ = 0;
|
||||
uint8_t EMSESP::unique_id_count_ = 0;
|
||||
bool EMSESP::trace_raw_ = false;
|
||||
uint8_t EMSESP::bool_format_ = 1;
|
||||
uint8_t EMSESP::enum_format_ = 1;
|
||||
uint16_t EMSESP::wait_validate_ = 0;
|
||||
bool EMSESP::wait_km_ = true;
|
||||
|
||||
@@ -94,7 +95,7 @@ void EMSESP::fetch_device_values(const uint8_t device_id) {
|
||||
}
|
||||
}
|
||||
|
||||
// see if the device ID exists
|
||||
// see if the deviceID exists
|
||||
bool EMSESP::valid_device(const uint8_t device_id) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
@@ -117,7 +118,7 @@ void EMSESP::fetch_device_values_type(const uint8_t device_type) {
|
||||
|
||||
// clears list of recognized devices
|
||||
void EMSESP::clear_all_devices() {
|
||||
// temporary removed: clearing the list causes a crash, the associated commands and mqtt should also be removed.
|
||||
// temporarily removed: clearing the list causes a crash, the associated commands and mqtt should also be removed.
|
||||
// emsdevices.clear(); // remove entries, but doesn't delete actual devices
|
||||
}
|
||||
|
||||
@@ -132,6 +133,36 @@ uint8_t EMSESP::count_devices(const uint8_t device_type) {
|
||||
return count;
|
||||
}
|
||||
|
||||
// return total number of devices excluding the Controller
|
||||
uint8_t EMSESP::count_devices() {
|
||||
uint8_t count = 0;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
count += (emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// returns the index of a device if there are more of the same type
|
||||
// or 0 if there is only one or none
|
||||
uint8_t EMSESP::device_index(const uint8_t device_type, const uint8_t unique_id) {
|
||||
if (count_devices(device_type) <= 1) {
|
||||
return 0; // none or only 1 device exists
|
||||
}
|
||||
uint8_t index = 1;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice->device_type() == device_type) {
|
||||
// did we find it?
|
||||
if (emsdevice->unique_id() == unique_id) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return 0; // didn't find it
|
||||
}
|
||||
|
||||
// scans for new devices
|
||||
void EMSESP::scan_devices() {
|
||||
EMSESP::clear_all_devices();
|
||||
@@ -139,19 +170,20 @@ void EMSESP::scan_devices() {
|
||||
}
|
||||
|
||||
/**
|
||||
* if thermostat master is 0x18 it handles only ww and hc1, hc2..hc4 handled by devices 0x19..0x1B
|
||||
* if thermostat master is 0x18 it handles only ww and hc1, hc2..hc8 handled by devices 0x19..0x1F
|
||||
* we send to right device and match all reads to 0x18
|
||||
*/
|
||||
uint8_t EMSESP::check_master_device(const uint8_t device_id, const uint16_t type_id, const bool read) {
|
||||
if (actual_master_thermostat_ == 0x18) {
|
||||
uint16_t mon_ids[4] = {0x02A5, 0x02A6, 0x02A7, 0x02A8};
|
||||
uint16_t set_ids[4] = {0x02B9, 0x02BA, 0x02BB, 0x02BC};
|
||||
uint16_t summer_ids[4] = {0x02AF, 0x02B0, 0x02B1, 0x02B2};
|
||||
uint16_t curve_ids[4] = {0x029B, 0x029C, 0x029D, 0x029E};
|
||||
uint16_t mon_ids[] = {0x02A5, 0x02A6, 0x02A7, 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC};
|
||||
uint16_t set_ids[] = {0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, 0x02C0};
|
||||
uint16_t summer_ids[] = {0x02AF, 0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6};
|
||||
uint16_t curve_ids[] = {0x029B, 0x029C, 0x029D, 0x029E, 0x029F, 0x02A0, 0x02A1, 0x02A2};
|
||||
uint16_t summer2_ids[] = {0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0476, 0x0477, 0x0478};
|
||||
uint16_t master_ids[] = {0x02F5, 0x031B, 0x031D, 0x031E, 0x023A, 0x0267, 0x0240};
|
||||
// look for heating circuits
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (type_id == mon_ids[i] || type_id == set_ids[i] || type_id == summer_ids[i] || type_id == curve_ids[i]) {
|
||||
for (uint8_t i = 0; i < sizeof(mon_ids) / 2; i++) {
|
||||
if (type_id == mon_ids[i] || type_id == set_ids[i] || type_id == summer_ids[i] || type_id == curve_ids[i] || type_id == summer2_ids[i]) {
|
||||
if (read) {
|
||||
// receiving telegrams and map all to master thermostat at 0x18 (src manipulated)
|
||||
return 0x18;
|
||||
@@ -180,7 +212,7 @@ uint8_t EMSESP::actual_master_thermostat() {
|
||||
return actual_master_thermostat_;
|
||||
}
|
||||
|
||||
// to watch both type IDs and device IDs
|
||||
// to watch both type IDs and deviceIDs
|
||||
void EMSESP::watch_id(uint16_t watch_id) {
|
||||
watch_id_ = watch_id;
|
||||
}
|
||||
@@ -222,20 +254,21 @@ uint8_t EMSESP::bus_status() {
|
||||
|
||||
// check if we have Tx issues.
|
||||
uint32_t total_sent = txservice_.telegram_read_count() + txservice_.telegram_write_count();
|
||||
uint32_t total_fail = txservice_.telegram_read_fail_count() + txservice_.telegram_write_fail_count();
|
||||
|
||||
// nothing sent and also no errors - must be ok
|
||||
if ((total_sent == 0) && (txservice_.telegram_fail_count() == 0)) {
|
||||
if ((total_sent == 0) && (total_fail == 0)) {
|
||||
return BUS_STATUS_CONNECTED;
|
||||
}
|
||||
|
||||
// nothing sent, but have Tx errors
|
||||
if ((total_sent == 0) && (txservice_.telegram_fail_count() != 0)) {
|
||||
if ((total_sent == 0) && (total_fail != 0)) {
|
||||
return BUS_STATUS_TX_ERRORS;
|
||||
}
|
||||
|
||||
// Tx Failure rate > 10%
|
||||
if (txservice_.telegram_fail_count() < total_sent) {
|
||||
if (((txservice_.telegram_fail_count() * 100) / total_sent) > EMSbus::EMS_TX_ERROR_LIMIT) {
|
||||
if (total_fail < total_sent) {
|
||||
if (((total_fail * 100) / total_sent) > EMSbus::EMS_TX_ERROR_LIMIT) {
|
||||
return BUS_STATUS_TX_ERRORS;
|
||||
}
|
||||
}
|
||||
@@ -265,13 +298,15 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
shell.printfln(F("EMS Bus info:"));
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { shell.printfln(F(" Tx mode: %d"), settings.tx_mode); });
|
||||
shell.printfln(F(" Bus protocol: %s"), EMSbus::is_ht3() ? F("HT3") : F("Buderus"));
|
||||
shell.printfln(F(" #recognized EMS devices: %d"), (EMSESP::emsdevices).size());
|
||||
shell.printfln(F(" #telegrams received: %d"), rxservice_.telegram_count());
|
||||
shell.printfln(F(" #read requests sent: %d"), txservice_.telegram_read_count());
|
||||
shell.printfln(F(" #write requests sent: %d"), txservice_.telegram_write_count());
|
||||
shell.printfln(F(" #incomplete telegrams: %d"), rxservice_.telegram_error_count());
|
||||
shell.printfln(F(" #tx fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_fail_count());
|
||||
shell.printfln(F(" #read fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_read_fail_count());
|
||||
shell.printfln(F(" #write fails (after %d retries): %d"), TxService::MAXIMUM_TX_RETRIES, txservice_.telegram_write_fail_count());
|
||||
shell.printfln(F(" Rx line quality: %d%%"), rxservice_.quality());
|
||||
shell.printfln(F(" Tx line quality: %d%%"), txservice_.quality());
|
||||
shell.printfln(F(" Tx line quality: %d%%"), (txservice_.read_quality() + txservice_.read_quality()) / 2);
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -315,7 +350,7 @@ void EMSESP::show_ems(uuid::console::Shell & shell) {
|
||||
// generate_values_json is called in verbose mode
|
||||
void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
if (emsdevices.empty()) {
|
||||
shell.printfln(F("No EMS devices detected. Try using 'scan devices' from the ems menu."));
|
||||
shell.printfln(F("No EMS devices detected."));
|
||||
shell.println();
|
||||
return;
|
||||
}
|
||||
@@ -325,11 +360,12 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
|
||||
// print header
|
||||
shell.printfln(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
|
||||
shell.printfln(F("%s: %s (%d)"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str(), emsdevice->count_entities());
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XLARGE_DYN); // use max size
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN); // use max size
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
emsdevice->generate_values_json(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::API_VERBOSE); // verbose mode and nested
|
||||
|
||||
emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::API_VERBOSE); // verbose mode and nested
|
||||
|
||||
// print line
|
||||
uint8_t id = 0;
|
||||
@@ -351,6 +387,9 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
|
||||
// if there is a uom print it
|
||||
std::string uom = emsdevice->get_value_uom(key);
|
||||
if (uom == "°C" && EMSESP::system_.fahrenheit()) {
|
||||
uom = "°F";
|
||||
}
|
||||
if (!uom.empty()) {
|
||||
shell.print(' ');
|
||||
shell.print(uom);
|
||||
@@ -366,24 +405,64 @@ void EMSESP::show_device_values(uuid::console::Shell & shell) {
|
||||
}
|
||||
}
|
||||
|
||||
// show Dallas temperature sensors
|
||||
// show Dallas temperature sensors and Analog sensors
|
||||
void EMSESP::show_sensor_values(uuid::console::Shell & shell) {
|
||||
if (!have_sensors()) {
|
||||
return;
|
||||
if (dallassensor_.have_sensors()) {
|
||||
shell.printfln(F("Temperature sensors:"));
|
||||
char s[10];
|
||||
char s2[10];
|
||||
uint8_t fahrenheit = EMSESP::system_.fahrenheit() ? 2 : 0;
|
||||
|
||||
for (const auto & sensor : dallassensor_.sensors()) {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
shell.printfln(F(" %s: %s%s °%c%s (offset %s, ID: %s)"),
|
||||
sensor.name().c_str(),
|
||||
COLOR_BRIGHT_GREEN,
|
||||
Helpers::render_value(s, sensor.temperature_c, 10, fahrenheit),
|
||||
(fahrenheit == 0) ? 'C' : 'F',
|
||||
COLOR_RESET,
|
||||
Helpers::render_value(s2, sensor.offset(), 10, fahrenheit),
|
||||
sensor.id_str().c_str());
|
||||
} else {
|
||||
shell.printfln(F(" %s (offset %s, ID: %s)"),
|
||||
sensor.name().c_str(),
|
||||
Helpers::render_value(s, sensor.offset(), 10, fahrenheit),
|
||||
sensor.id_str().c_str());
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
|
||||
shell.printfln(F("Dallas temperature sensors:"));
|
||||
uint8_t i = 1;
|
||||
char s[7];
|
||||
char s2[7];
|
||||
for (const auto & device : sensor_devices()) {
|
||||
shell.printfln(F(" Sensor %d, ID: %s, Temperature: %s °C (offset %s)"),
|
||||
i++,
|
||||
device.to_string().c_str(),
|
||||
Helpers::render_value(s, device.temperature_c, 10),
|
||||
Helpers::render_value(s2, device.offset(), 10));
|
||||
if (analogsensor_.have_sensors()) {
|
||||
char s[10];
|
||||
char s2[10];
|
||||
shell.printfln(F("Analog sensors:"));
|
||||
for (const auto & sensor : analogsensor_.sensors()) {
|
||||
switch (sensor.type()) {
|
||||
case AnalogSensor::AnalogType::ADC:
|
||||
shell.printfln(F(" %s: %s%s %s%s (Type: ADC, Factor: %s, Offset: %d)"),
|
||||
sensor.name().c_str(),
|
||||
COLOR_BRIGHT_GREEN,
|
||||
Helpers::render_value(s, sensor.value(), 2),
|
||||
EMSdevice::uom_to_string(sensor.uom()).c_str(),
|
||||
COLOR_RESET,
|
||||
Helpers::render_value(s2, sensor.factor(), 4),
|
||||
sensor.offset());
|
||||
break;
|
||||
default:
|
||||
case AnalogSensor::AnalogType::DIGITAL_IN:
|
||||
case AnalogSensor::AnalogType::COUNTER:
|
||||
shell.printfln(F(" %s: %s%d%s (Type: %s)"),
|
||||
sensor.name().c_str(),
|
||||
COLOR_BRIGHT_GREEN,
|
||||
(uint16_t)sensor.value(), // as int
|
||||
COLOR_RESET,
|
||||
sensor.type() == AnalogSensor::AnalogType::COUNTER ? "Counter" : "Digital in");
|
||||
break;
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// MQTT publish everything, immediately
|
||||
@@ -393,13 +472,14 @@ void EMSESP::publish_all(bool force) {
|
||||
reset_mqtt_ha();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mqtt::connected()) {
|
||||
publish_device_values(EMSdevice::DeviceType::BOILER);
|
||||
publish_device_values(EMSdevice::DeviceType::THERMOSTAT);
|
||||
publish_device_values(EMSdevice::DeviceType::SOLAR);
|
||||
publish_device_values(EMSdevice::DeviceType::MIXER);
|
||||
publish_other_values();
|
||||
publish_sensor_values(true);
|
||||
publish_other_values(); // switch and heat pump
|
||||
publish_sensor_values(true); // includes dallas and analog sensors
|
||||
system_.send_heartbeat();
|
||||
}
|
||||
}
|
||||
@@ -409,10 +489,12 @@ void EMSESP::publish_all_loop() {
|
||||
if (!Mqtt::connected() || !publish_all_idx_) {
|
||||
return;
|
||||
}
|
||||
// wait for free queue before sending next message, v3 queues HA-messages
|
||||
|
||||
// wait for free queue before sending next message, HA-messages are also queued
|
||||
if (!Mqtt::is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (publish_all_idx_++) {
|
||||
case 1:
|
||||
publish_device_values(EMSdevice::DeviceType::BOILER);
|
||||
@@ -427,7 +509,7 @@ void EMSESP::publish_all_loop() {
|
||||
publish_device_values(EMSdevice::DeviceType::MIXER);
|
||||
break;
|
||||
case 5:
|
||||
publish_other_values();
|
||||
publish_other_values(); // switch and heat pump
|
||||
break;
|
||||
case 6:
|
||||
publish_sensor_values(true, true);
|
||||
@@ -444,7 +526,8 @@ void EMSESP::publish_all_loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// force HA to re-create all the devices
|
||||
// force HA to re-create all the devices next time they are detected
|
||||
// also removes the old HA topics
|
||||
void EMSESP::reset_mqtt_ha() {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
@@ -454,27 +537,38 @@ void EMSESP::reset_mqtt_ha() {
|
||||
emsdevice->ha_config_clear();
|
||||
}
|
||||
dallassensor_.reload();
|
||||
analogsensor_.reload();
|
||||
}
|
||||
|
||||
// create json doc for the devices values and add to MQTT publish queue
|
||||
// this will also create the HA /config topic
|
||||
// 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); // use max size
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool need_publish = false;
|
||||
|
||||
bool nested = (Mqtt::nested_format() == 1); // 1 is nested, 2 is single
|
||||
bool nested = (Mqtt::is_nested());
|
||||
|
||||
// group by device type
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
// specially for HA
|
||||
// we may have some RETAINED /config topics that reference fields in the data payloads that no longer exist
|
||||
// remove them immediately to prevent HA from complaining
|
||||
// we need to do this first before the data payload is published, and only done once!
|
||||
if (Mqtt::ha_enabled() && emsdevice->ha_config_firstrun()) {
|
||||
emsdevice->ha_config_clear();
|
||||
emsdevice->ha_config_firstrun(false);
|
||||
}
|
||||
|
||||
// if its a boiler, generate json for each group and publish it directly. not nested
|
||||
if (device_type == DeviceType::BOILER) {
|
||||
if (emsdevice->generate_values_json(json, DeviceValueTAG::TAG_BOILER_DATA, false, EMSdevice::OUTPUT_TARGET::MQTT)) {
|
||||
if (emsdevice->generate_values(json, DeviceValueTAG::TAG_BOILER_DATA, false, EMSdevice::OUTPUT_TARGET::MQTT)) {
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_BOILER_DATA), json);
|
||||
}
|
||||
doc.clear();
|
||||
if (emsdevice->generate_values_json(json, DeviceValueTAG::TAG_DEVICE_DATA_WW, false, EMSdevice::OUTPUT_TARGET::MQTT)) {
|
||||
if (emsdevice->generate_values(json, DeviceValueTAG::TAG_DEVICE_DATA_WW, false, EMSdevice::OUTPUT_TARGET::MQTT)) {
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_DEVICE_DATA_WW), json);
|
||||
}
|
||||
need_publish = false;
|
||||
@@ -485,15 +579,14 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
// only publish the single master thermostat
|
||||
if (emsdevice->device_id() == EMSESP::actual_master_thermostat()) {
|
||||
if (nested) {
|
||||
need_publish |= emsdevice->generate_values_json(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::MQTT); // nested
|
||||
need_publish |= emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::MQTT); // nested
|
||||
} else {
|
||||
if (emsdevice->generate_values_json(json, DeviceValueTAG::TAG_THERMOSTAT_DATA, false, EMSdevice::OUTPUT_TARGET::MQTT)) { // not nested
|
||||
if (emsdevice->generate_values(json, DeviceValueTAG::TAG_THERMOSTAT_DATA, false, EMSdevice::OUTPUT_TARGET::MQTT)) { // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, DeviceValueTAG::TAG_NONE), json);
|
||||
}
|
||||
doc.clear();
|
||||
|
||||
for (uint8_t hc_tag = TAG_HC1; hc_tag <= DeviceValueTAG::TAG_HC4; hc_tag++) {
|
||||
if (emsdevice->generate_values_json(json, hc_tag, false, EMSdevice::OUTPUT_TARGET::MQTT)) { // not nested
|
||||
for (uint8_t hc_tag = DeviceValueTAG::TAG_HC1; hc_tag <= DeviceValueTAG::TAG_HC8; hc_tag++) {
|
||||
if (emsdevice->generate_values(json, hc_tag, false, EMSdevice::OUTPUT_TARGET::MQTT)) { // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, hc_tag), json);
|
||||
}
|
||||
doc.clear();
|
||||
@@ -506,10 +599,10 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
// Mixer
|
||||
else if (device_type == DeviceType::MIXER) {
|
||||
if (nested) {
|
||||
need_publish |= emsdevice->generate_values_json(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::MQTT); // nested
|
||||
need_publish |= emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::MQTT); // nested
|
||||
} else {
|
||||
for (uint8_t hc_tag = TAG_HC1; hc_tag <= DeviceValueTAG::TAG_WWC4; hc_tag++) {
|
||||
if (emsdevice->generate_values_json(json, hc_tag, false, EMSdevice::OUTPUT_TARGET::MQTT)) { // not nested
|
||||
for (uint8_t hc_tag = DeviceValueTAG::TAG_HC1; hc_tag <= DeviceValueTAG::TAG_WWC4; hc_tag++) {
|
||||
if (emsdevice->generate_values(json, hc_tag, false, EMSdevice::OUTPUT_TARGET::MQTT)) { // not nested
|
||||
Mqtt::publish(Mqtt::tag_to_topic(device_type, hc_tag), json);
|
||||
}
|
||||
doc.clear();
|
||||
@@ -519,18 +612,21 @@ void EMSESP::publish_device_values(uint8_t device_type) {
|
||||
|
||||
} else {
|
||||
// for all other devices add the values to the json
|
||||
need_publish |= emsdevice->generate_values_json(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::MQTT); // nested
|
||||
need_publish |= emsdevice->generate_values(json, DeviceValueTAG::TAG_NONE, true, EMSdevice::OUTPUT_TARGET::MQTT); // nested
|
||||
}
|
||||
}
|
||||
|
||||
// if we're using HA, done is checked for each sensor in devices
|
||||
// we want to create the /config topic after the data payload to prevent HA from throwing up a warning
|
||||
if (Mqtt::ha_enabled()) {
|
||||
emsdevice->publish_mqtt_ha_entity_config(); // create the configs for each value as a sensor
|
||||
emsdevice->publish_mqtt_ha_entity_config();
|
||||
}
|
||||
}
|
||||
|
||||
// publish it under a single topic, only if we have data to publish
|
||||
if (need_publish) {
|
||||
if (doc.overflowed()) {
|
||||
LOG_WARNING(F("MQTT buffer overflow, please use individual topics"));
|
||||
}
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "%s_data", EMSdevice::device_type_2_device_name(device_type).c_str());
|
||||
Mqtt::publish(topic, json);
|
||||
@@ -543,13 +639,18 @@ void EMSESP::publish_other_values() {
|
||||
publish_device_values(EMSdevice::DeviceType::HEATPUMP);
|
||||
}
|
||||
|
||||
// publish both the dallas and analog sensor values
|
||||
void EMSESP::publish_sensor_values(const bool time, const bool force) {
|
||||
if (!dallas_enabled()) {
|
||||
return;
|
||||
if (dallas_enabled()) {
|
||||
if (dallassensor_.updated_values() || time || force) {
|
||||
dallassensor_.publish_values(force);
|
||||
}
|
||||
}
|
||||
|
||||
if (dallassensor_.updated_values() || time || force) {
|
||||
dallassensor_.publish_values(force);
|
||||
if (analog_enabled()) {
|
||||
if (analogsensor_.updated_values() || time || force) {
|
||||
analogsensor_.publish_values(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,23 +687,14 @@ bool EMSESP::get_device_value_info(JsonObject & root, const char * cmd, const in
|
||||
|
||||
// specific for the dallassensor
|
||||
if (devicetype == DeviceType::DALLASSENSOR) {
|
||||
uint8_t i = 1;
|
||||
for (const auto & sensor : EMSESP::sensor_devices()) {
|
||||
char sensorID[10];
|
||||
snprintf(sensorID, 10, "sensor%d", i++);
|
||||
if ((strcmp(cmd, sensorID) == 0) || (strcmp(cmd, Helpers::toLower(sensor.to_string()).c_str()) == 0)) {
|
||||
root["name"] = sensor.to_string();
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
root["value"] = (float)(sensor.temperature_c) / 10;
|
||||
}
|
||||
root["type"] = F_(number);
|
||||
root["min"] = -55;
|
||||
root["max"] = 125;
|
||||
root["unit"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
root["writeable"] = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
EMSESP::dallassensor_.get_value_info(root, cmd, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// analog sensor
|
||||
if (devicetype == DeviceType::ANALOGSENSOR) {
|
||||
EMSESP::analogsensor_.get_value_info(root, cmd, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -628,6 +720,7 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
uint8_t offset = telegram->offset;
|
||||
|
||||
// find name for src and dest by looking up known devices
|
||||
|
||||
std::string src_name("");
|
||||
std::string dest_name("");
|
||||
std::string type_name("");
|
||||
@@ -674,32 +767,13 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
direction = read_flash_string(F("->"));
|
||||
}
|
||||
|
||||
std::string str(200, '\0');
|
||||
std::string str;
|
||||
str.reserve(200);
|
||||
str = src_name + "(" + Helpers::hextoa(src) + ") " + direction + " " + dest_name + "(" + Helpers::hextoa(dest) + "), " + type_name + "("
|
||||
+ Helpers::hextoa(telegram->type_id) + "), data: " + telegram->to_string_message();
|
||||
|
||||
if (offset) {
|
||||
snprintf(&str[0],
|
||||
str.capacity() + 1,
|
||||
"%s(0x%02X) %s %s(0x%02X), %s(0x%02X), data: %s (offset %d)",
|
||||
src_name.c_str(),
|
||||
src,
|
||||
direction.c_str(),
|
||||
dest_name.c_str(),
|
||||
dest,
|
||||
type_name.c_str(),
|
||||
telegram->type_id,
|
||||
telegram->to_string_message().c_str(),
|
||||
offset);
|
||||
} else {
|
||||
snprintf(&str[0],
|
||||
str.capacity() + 1,
|
||||
"%s(0x%02X) %s %s(0x%02X), %s(0x%02X), data: %s",
|
||||
src_name.c_str(),
|
||||
src,
|
||||
direction.c_str(),
|
||||
dest_name.c_str(),
|
||||
dest,
|
||||
type_name.c_str(),
|
||||
telegram->type_id,
|
||||
telegram->to_string_message().c_str());
|
||||
str += " (offset " + Helpers::itoa(offset) + ")";
|
||||
}
|
||||
|
||||
return str;
|
||||
@@ -711,8 +785,8 @@ std::string EMSESP::pretty_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
* Junkers has 15 bytes of data
|
||||
* each byte is a bitmask for which devices are active
|
||||
* byte 1 = 0x08 - 0x0F, byte 2 = 0x10 - 0x17, etc...
|
||||
* e.g. in example above 1st byte = x0B = b1011 so we have device ids 0x08, 0x09, 0x011
|
||||
* and 2nd byte = x80 = b1000 b0000 = device id 0x17
|
||||
* e.g. in example above 1st byte = x0B = b1011 so we have deviceIDs 0x08, 0x09, 0x011
|
||||
* and 2nd byte = x80 = b1000 b0000 = deviceID 0x17
|
||||
*/
|
||||
void EMSESP::process_UBADevices(std::shared_ptr<const Telegram> telegram) {
|
||||
// exit it length is incorrect (must be 13 or 15 bytes long)
|
||||
@@ -748,8 +822,7 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
|
||||
if (telegram->message_length < 3) {
|
||||
// for empty telegram add device with empty product, version and brand
|
||||
if (!telegram->message_length) {
|
||||
std::string version = "00.00";
|
||||
(void)add_device(telegram->src, 0, version, 0);
|
||||
(void)add_device(telegram->src, 0, "00.00", 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -766,12 +839,12 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
// extra details from the telegram
|
||||
uint8_t device_id = telegram->src; // device ID
|
||||
uint8_t product_id = telegram->message_data[offset]; // product ID
|
||||
uint8_t device_id = telegram->src; // deviceID
|
||||
uint8_t product_id = telegram->message_data[offset]; // productID
|
||||
|
||||
// get version as XX.XX
|
||||
std::string version(6, '\0');
|
||||
snprintf(&version[0], version.capacity() + 1, "%02d.%02d", telegram->message_data[offset + 1], telegram->message_data[offset + 2]);
|
||||
char version[8];
|
||||
snprintf(version, sizeof(version), "%02d.%02d", telegram->message_data[offset + 1], telegram->message_data[offset + 2]);
|
||||
|
||||
// some devices store the protocol type (HT3, Buderus) in the last byte
|
||||
uint8_t brand;
|
||||
@@ -785,14 +858,14 @@ void EMSESP::process_version(std::shared_ptr<const Telegram> telegram) {
|
||||
(void)add_device(device_id, product_id, version, brand);
|
||||
}
|
||||
|
||||
// find the device object that matches the device ID and see if it has a matching telegram type handler
|
||||
// find the device object that matches the deviceID and see if it has a matching telegram type handler
|
||||
// but only process if the telegram is sent to us or it's a broadcast (dest=0x00=all)
|
||||
// We also check for common telgram types, like the Version(0x02)
|
||||
// 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_NOTICE(F("%s"), pretty_telegram(telegram).c_str());
|
||||
LOG_INFO(F("%s"), pretty_telegram(telegram).c_str());
|
||||
if (Mqtt::send_response()) {
|
||||
publish_response(telegram);
|
||||
}
|
||||
@@ -813,7 +886,9 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
|
||||
// only process broadcast telegrams or ones sent to us on request
|
||||
if ((telegram->dest != 0x00) && (telegram->dest != rxservice_.ems_bus_id())) {
|
||||
// if ((telegram->dest != 0x00) && (telegram->dest != rxservice_.ems_bus_id())) {
|
||||
if (telegram->operation == Telegram::Operation::RX_READ) {
|
||||
// LOG_DEBUG(F("read telegram received, not processing"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -837,18 +912,23 @@ bool EMSESP::process_telegram(std::shared_ptr<const Telegram> telegram) {
|
||||
bool knowndevice = false;
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->is_device_id(telegram->src)) {
|
||||
if (emsdevice->is_device_id(telegram->src) || emsdevice->is_device_id(telegram->dest)) {
|
||||
knowndevice = true;
|
||||
found = emsdevice->handle_telegram(telegram);
|
||||
// if we correctly processes the telegram follow up with sending it via MQTT if needed
|
||||
if (found && emsdevice->is_device_id(telegram->dest)) {
|
||||
LOG_DEBUG(F("Process setting 0x%02X for device 0x%02X"), telegram->type_id, telegram->dest);
|
||||
}
|
||||
// if we correctly processed the telegram then follow up with sending it via MQTT (if enabled)
|
||||
if (found && Mqtt::connected()) {
|
||||
if ((mqtt_.get_publish_onchange(emsdevice->device_type()) && emsdevice->has_update())
|
||||
|| (telegram->type_id == publish_id_ && telegram->dest == txservice_.ems_bus_id())) {
|
||||
if (telegram->type_id == publish_id_) {
|
||||
publish_id_ = 0;
|
||||
}
|
||||
emsdevice->has_update(false); // reset flag
|
||||
publish_device_values(emsdevice->device_type()); // publish to MQTT if we explicitly have too
|
||||
emsdevice->has_update(false); // reset flag
|
||||
if (!Mqtt::publish_single()) {
|
||||
publish_device_values(emsdevice->device_type()); // publish to MQTT if we explicitly have too
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wait_validate_ == telegram->type_id) {
|
||||
@@ -909,7 +989,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if ((emsdevice) && (emsdevice->device_type() == device_class.first)) {
|
||||
shell.printf(F("(%d) %s: %s"), emsdevice->unique_id(), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
|
||||
shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str());
|
||||
if ((num_thermostats > 1) && (emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT)
|
||||
&& (emsdevice->device_id() == actual_master_thermostat())) {
|
||||
shell.printf(F(" **master device**"));
|
||||
@@ -928,7 +1008,7 @@ void EMSESP::show_devices(uuid::console::Shell & shell) {
|
||||
|
||||
// add a new or update existing EMS device to our list of active EMS devices
|
||||
// if its not in our database, we don't add it
|
||||
bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand) {
|
||||
bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand) {
|
||||
// don't add ourselves!
|
||||
if (device_id == rxservice_.ems_bus_id()) {
|
||||
return false;
|
||||
@@ -938,7 +1018,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->is_device_id(device_id)) {
|
||||
LOG_DEBUG(F("Updating details for already active device ID 0x%02X"), device_id);
|
||||
LOG_DEBUG(F("Updating details for already active deviceID 0x%02X"), device_id);
|
||||
emsdevice->product_id(product_id);
|
||||
emsdevice->version(version);
|
||||
// only set brand if it doesn't already exist
|
||||
@@ -962,7 +1042,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
Device_record * device_p = nullptr;
|
||||
for (auto & device : device_library_) {
|
||||
if (device.product_id == product_id) {
|
||||
// sometimes boilers share the same product id as controllers
|
||||
// sometimes boilers share the same productID as controllers
|
||||
// so only add boilers if the device_id is 0x08, which is fixed for EMS
|
||||
if (device.device_type == DeviceType::BOILER) {
|
||||
if (device_id == EMSdevice::EMS_DEVICE_ID_BOILER
|
||||
@@ -978,9 +1058,9 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't recognize the product ID report it and add as a generic device
|
||||
// if we don't recognize the productID report it and add as a generic device
|
||||
if (device_p == nullptr) {
|
||||
LOG_NOTICE(F("Unrecognized EMS device (device ID 0x%02X, product ID %d). Please report on GitHub."), device_id, product_id);
|
||||
LOG_NOTICE(F("Unrecognized EMS device (deviceID 0x%02X, productID %d). Please report on GitHub."), device_id, product_id);
|
||||
std::string name("unknown");
|
||||
emsdevices.push_back(
|
||||
EMSFactory::add(DeviceType::GENERIC, device_id, product_id, version, name, DeviceFlags::EMS_DEVICE_FLAG_NONE, EMSdevice::Brand::NO_BRAND));
|
||||
@@ -1019,16 +1099,30 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
device_type = DeviceType::CONNECT;
|
||||
} else if (device_id == 0x0E) {
|
||||
name = "converter"; // generic
|
||||
} else if (device_id == 0x0F) {
|
||||
name = "clock"; // generic
|
||||
} else if (device_id == 0x08) {
|
||||
name = "generic boiler";
|
||||
device_type = DeviceType::BOILER;
|
||||
flags = DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP;
|
||||
LOG_WARNING(F("Unknown EMS boiler. Using generic profile. Please report on GitHub."));
|
||||
} else {
|
||||
LOG_WARNING(F("Unrecognized EMS device (device ID 0x%02X, no product ID). Please report on GitHub."), device_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG(F("Adding new device %s (device ID 0x%02X, product ID %d, version %s)"), name.c_str(), device_id, product_id, version.c_str());
|
||||
LOG_DEBUG(F("Adding new device %s (deviceID 0x%02X, productID %d, version %s)"), name.c_str(), device_id, product_id, version);
|
||||
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
|
||||
emsdevices.back()->unique_id(++unique_id_count_);
|
||||
|
||||
// sort devices based on type
|
||||
std::sort(emsdevices.begin(), emsdevices.end(), [](const std::unique_ptr<emsesp::EMSdevice> & a, const std::unique_ptr<emsesp::EMSdevice> & b) {
|
||||
return a->device_type() < b->device_type();
|
||||
});
|
||||
|
||||
fetch_device_values(device_id); // go and fetch its data
|
||||
|
||||
// add command commands for all devices, except for connect, controller and gateway
|
||||
@@ -1066,7 +1160,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std::
|
||||
Mqtt::subscribe(device_type, EMSdevice::device_type_2_device_name(device_type) + "/#", nullptr);
|
||||
|
||||
// Print to LOG showing we've added a new device
|
||||
LOG_INFO(F("Recognized new %s with device ID 0x%02X"), EMSdevice::device_type_2_device_name(device_type).c_str(), device_id);
|
||||
LOG_INFO(F("Recognized new %s with deviceID 0x%02X"), EMSdevice::device_type_2_device_name(device_type).c_str(), device_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1094,9 +1188,9 @@ bool EMSESP::command_commands(uint8_t device_type, JsonObject & output, const in
|
||||
bool EMSESP::command_info(uint8_t device_type, JsonObject & output, const int8_t id, const uint8_t output_target) {
|
||||
bool has_value = false;
|
||||
uint8_t tag;
|
||||
if (id >= 1 && id <= 4) {
|
||||
if (id >= 1 && id <= 8) {
|
||||
tag = DeviceValueTAG::TAG_HC1 + id - 1;
|
||||
} else if (id >= 9 && id <= 10) {
|
||||
} else if (id >= 9 && id <= 12) {
|
||||
tag = DeviceValueTAG::TAG_WWC1 + id - 9;
|
||||
} else if (id == -1 || id == 0) {
|
||||
tag = DeviceValueTAG::TAG_NONE;
|
||||
@@ -1107,7 +1201,7 @@ bool EMSESP::command_info(uint8_t device_type, JsonObject & output, const int8_t
|
||||
for (const auto & emsdevice : emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)
|
||||
&& ((device_type != DeviceType::THERMOSTAT) || (emsdevice->device_id() == EMSESP::actual_master_thermostat()))) {
|
||||
has_value |= emsdevice->generate_values_json(output, tag, (id < 1), output_target); // use nested for id -1 and 0
|
||||
has_value |= emsdevice->generate_values(output, tag, (id < 1), output_target); // use nested for id -1 and 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1262,7 +1356,6 @@ void EMSESP::send_raw_telegram(const char * data) {
|
||||
// the services must be loaded in the correct order
|
||||
void EMSESP::start() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
|
||||
// start the file system
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -1272,29 +1365,34 @@ void EMSESP::start() {
|
||||
}
|
||||
#endif
|
||||
|
||||
esp8266React.begin(); // loads system settings (network, mqtt, etc)
|
||||
esp8266React.begin(); // loads core system services settings (network, mqtt, ap, ntp etc)
|
||||
system_.check_upgrade(); // do any system upgrades
|
||||
webSettingsService.begin(); // load EMS-ESP Application settings...
|
||||
system_.get_settings(); // ...and store some of the settings locally for future reference
|
||||
console_.start(system_.telnet_enabled()); // telnet and serial console, from here we can start logging events
|
||||
webLogService.start(); // start web log service
|
||||
webCustomizationService.begin(); // load the customizations
|
||||
|
||||
system_.check_upgrade(); // do any system upgrades
|
||||
// welcome message
|
||||
LOG_INFO(F("Starting EMS-ESP version %s (hostname: %s)"), EMSESP_APP_VERSION, system_.hostname().c_str());
|
||||
LOG_INFO(F("Configuring for interface board profile %s"), system_.board_profile().c_str());
|
||||
|
||||
// start all the EMS-ESP services
|
||||
mqtt_.start(); // mqtt init
|
||||
system_.start(heap_start); // starts commands, led, adc, button, network, syslog & uart
|
||||
shower_.start(); // initialize shower timer and shower alert
|
||||
dallassensor_.start(); // Dallas external sensors
|
||||
analogsensor_.start(); // Analog external sensors
|
||||
webServer.begin(); // start the web server
|
||||
// emsdevices.reserve(5); // reserve space for initially 5 devices to avoid mem frag issues
|
||||
|
||||
LOG_INFO(F("Last system reset reason Core0: %s, Core1: %s"), system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||
|
||||
// Load our library of known devices into stack mem. Names are stored in Flash memory (takes up about 1kb)
|
||||
device_library_ = {
|
||||
#include "device_library.h"
|
||||
};
|
||||
|
||||
console_.start(); // telnet and serial console
|
||||
|
||||
webSettingsService.begin(); // load EMS-ESP specific settings, like GPIO configurations
|
||||
mqtt_.start(); // mqtt init
|
||||
system_.start(heap_start); // starts commands, led, adc, button, network, syslog & uart
|
||||
shower_.start(); // initialize shower timer and shower alert
|
||||
dallassensor_.start(); // dallas external sensors
|
||||
webServer.begin(); // start web server
|
||||
webLogService.start(); // start web log service
|
||||
|
||||
emsdevices.reserve(5); // reserve space for initially 5 devices to avoid mem frag issues
|
||||
|
||||
LOG_INFO(F("Last system reset reason Core0: %s, Core1: %s"), system_.reset_reason(0).c_str(), system_.reset_reason(1).c_str());
|
||||
LOG_INFO(F("EMS Device library loaded with %d records"), device_library_.size());
|
||||
LOG_INFO(F("EMS device library loaded with %d records"), device_library_.size());
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Mqtt::on_connect(); // simulate an MQTT connection
|
||||
@@ -1312,6 +1410,7 @@ void EMSESP::loop() {
|
||||
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
|
||||
|
||||
|
||||
95
src/emsesp.h
95
src/emsesp.h
@@ -22,6 +22,10 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
@@ -38,15 +42,18 @@
|
||||
#include "web/WebStatusService.h"
|
||||
#include "web/WebDataService.h"
|
||||
#include "web/WebSettingsService.h"
|
||||
#include "web/WebCustomizationService.h"
|
||||
#include "web/WebAPIService.h"
|
||||
#include "web/WebLogService.h"
|
||||
|
||||
#include "emsdevicevalue.h"
|
||||
#include "emsdevice.h"
|
||||
#include "emsfactory.h"
|
||||
#include "telegram.h"
|
||||
#include "mqtt.h"
|
||||
#include "system.h"
|
||||
#include "dallassensor.h"
|
||||
#include "analogsensor.h"
|
||||
#include "console.h"
|
||||
#include "shower.h"
|
||||
#include "roomcontrol.h"
|
||||
@@ -55,7 +62,11 @@
|
||||
|
||||
#define WATCH_ID_NONE 0 // no watch id set
|
||||
|
||||
#define EMSESP_JSON_SIZE_HA_CONFIG 768 // for HA config payloads, using StaticJsonDocument
|
||||
#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
|
||||
@@ -68,7 +79,8 @@
|
||||
#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_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)
|
||||
|
||||
// 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
|
||||
@@ -76,6 +88,11 @@
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using DeviceValueUOM = emsesp::DeviceValue::DeviceValueUOM;
|
||||
using DeviceValueType = emsesp::DeviceValue::DeviceValueType;
|
||||
using DeviceValueState = emsesp::DeviceValue::DeviceValueState;
|
||||
using DeviceValueTAG = emsesp::DeviceValue::DeviceValueTAG;
|
||||
|
||||
class Shower; // forward declaration for compiler
|
||||
|
||||
class EMSESP {
|
||||
@@ -113,6 +130,8 @@ class EMSESP {
|
||||
static bool device_exists(const uint8_t device_id);
|
||||
|
||||
static uint8_t count_devices(const uint8_t device_type);
|
||||
static uint8_t count_devices();
|
||||
static uint8_t device_index(const uint8_t device_type, const uint8_t unique_id);
|
||||
|
||||
static uint8_t actual_master_thermostat();
|
||||
static void actual_master_thermostat(const uint8_t device_id);
|
||||
@@ -130,40 +149,12 @@ class EMSESP {
|
||||
|
||||
static void incoming_telegram(uint8_t * data, const uint8_t length);
|
||||
|
||||
static const std::vector<DallasSensor::Sensor> sensor_devices() {
|
||||
return dallassensor_.sensors();
|
||||
}
|
||||
|
||||
static bool have_sensors() {
|
||||
return (!(dallassensor_.sensors().empty()));
|
||||
}
|
||||
|
||||
static uint32_t sensor_reads() {
|
||||
return dallassensor_.reads();
|
||||
}
|
||||
|
||||
static uint32_t sensor_fails() {
|
||||
return dallassensor_.fails();
|
||||
}
|
||||
|
||||
static bool dallas_enabled() {
|
||||
return (dallassensor_.dallas_enabled());
|
||||
}
|
||||
|
||||
static uint8_t bool_format() {
|
||||
return bool_format_;
|
||||
}
|
||||
|
||||
static void bool_format(uint8_t format) {
|
||||
bool_format_ = format;
|
||||
}
|
||||
|
||||
static uint8_t enum_format() {
|
||||
return enum_format_;
|
||||
}
|
||||
|
||||
static void enum_format(uint8_t format) {
|
||||
enum_format_ = format;
|
||||
static bool analog_enabled() {
|
||||
return (analogsensor_.analog_enabled());
|
||||
}
|
||||
|
||||
enum Watch : uint8_t { WATCH_OFF, WATCH_ON, WATCH_RAW, WATCH_UNKNOWN };
|
||||
@@ -178,12 +169,14 @@ class EMSESP {
|
||||
watch_id_ = 0; // reset watch id if watch is disabled
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t watch() {
|
||||
return watch_;
|
||||
}
|
||||
static void set_read_id(uint16_t id) {
|
||||
read_id_ = id;
|
||||
}
|
||||
|
||||
static bool wait_validate() {
|
||||
return (wait_validate_ != 0);
|
||||
}
|
||||
@@ -214,7 +207,7 @@ class EMSESP {
|
||||
static void fetch_device_values_type(const uint8_t device_type);
|
||||
static bool valid_device(const uint8_t device_id);
|
||||
|
||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, std::string & version, const uint8_t brand);
|
||||
static bool add_device(const uint8_t device_id, const uint8_t product_id, const char * version, const uint8_t brand);
|
||||
static void scan_devices();
|
||||
static void clear_all_devices();
|
||||
|
||||
@@ -224,18 +217,20 @@ class EMSESP {
|
||||
static Mqtt mqtt_;
|
||||
static System system_;
|
||||
static DallasSensor dallassensor_;
|
||||
static AnalogSensor analogsensor_;
|
||||
static Console console_;
|
||||
static Shower shower_;
|
||||
static RxService rxservice_;
|
||||
static TxService txservice_;
|
||||
|
||||
// web controllers
|
||||
static ESP8266React esp8266React;
|
||||
static WebSettingsService webSettingsService;
|
||||
static WebStatusService webStatusService;
|
||||
static WebDataService webDataService;
|
||||
static WebAPIService webAPIService;
|
||||
static WebLogService webLogService;
|
||||
static ESP8266React esp8266React;
|
||||
static WebSettingsService webSettingsService;
|
||||
static WebStatusService webStatusService;
|
||||
static WebDataService webDataService;
|
||||
static WebAPIService webAPIService;
|
||||
static WebLogService webLogService;
|
||||
static WebCustomizationService webCustomizationService;
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
|
||||
@@ -245,17 +240,16 @@ class EMSESP {
|
||||
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);
|
||||
static void publish_response(std::shared_ptr<const Telegram> telegram);
|
||||
static void publish_all_loop();
|
||||
static bool command_info(uint8_t device_type, JsonObject & output, const int8_t id, const uint8_t output_target);
|
||||
static bool command_commands(uint8_t device_type, JsonObject & output, const int8_t id);
|
||||
static bool command_entities(uint8_t device_type, JsonObject & output, const int8_t id);
|
||||
static void process_UBADevices(std::shared_ptr<const Telegram> telegram);
|
||||
static void process_version(std::shared_ptr<const Telegram> telegram);
|
||||
static void publish_response(std::shared_ptr<const Telegram> telegram);
|
||||
static void publish_all_loop();
|
||||
static bool command_info(uint8_t device_type, JsonObject & output, const int8_t id, const uint8_t output_target);
|
||||
static bool command_commands(uint8_t device_type, JsonObject & output, const int8_t id);
|
||||
static bool command_entities(uint8_t device_type, JsonObject & output, const int8_t id);
|
||||
|
||||
static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute
|
||||
static uint32_t last_fetch_;
|
||||
static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute
|
||||
|
||||
struct Device_record {
|
||||
uint8_t product_id;
|
||||
@@ -275,12 +269,9 @@ class EMSESP {
|
||||
static uint8_t publish_all_idx_;
|
||||
static uint8_t unique_id_count_;
|
||||
static bool trace_raw_;
|
||||
static uint8_t bool_format_;
|
||||
static uint8_t enum_format_;
|
||||
static uint16_t wait_validate_;
|
||||
static bool wait_km_;
|
||||
|
||||
static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute
|
||||
static uint32_t last_fetch_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "dallassensor.h"
|
||||
#include "version.h"
|
||||
#include "default_settings.h"
|
||||
|
||||
#include <ESP8266React.h>
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
@@ -55,12 +55,12 @@ class EMSFactory {
|
||||
}
|
||||
|
||||
// Construct derived class returning an unique ptr
|
||||
static auto add(const uint8_t device_type, uint8_t device_id, uint8_t product_id, std::string & version, std::string & name, uint8_t flags, uint8_t brand)
|
||||
static auto add(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand)
|
||||
-> std::unique_ptr<EMSdevice> {
|
||||
return std::unique_ptr<EMSdevice>(EMSFactory::makeRaw(device_type, device_id, product_id, version, name, flags, brand));
|
||||
}
|
||||
|
||||
virtual auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, std::string & version, std::string & name, uint8_t flags, uint8_t brand) const
|
||||
virtual auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand) const
|
||||
-> EMSdevice * = 0;
|
||||
|
||||
private:
|
||||
@@ -72,7 +72,7 @@ class EMSFactory {
|
||||
|
||||
// Construct derived class returning a raw pointer
|
||||
// find which EMS device it is and use that class
|
||||
static auto makeRaw(const uint8_t device_type, uint8_t device_id, uint8_t product_id, std::string & version, std::string & name, uint8_t flags, uint8_t brand)
|
||||
static auto makeRaw(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand)
|
||||
-> EMSdevice * {
|
||||
auto it = EMSFactory::getRegister().find(device_type);
|
||||
if (it != EMSFactory::getRegister().end()) {
|
||||
@@ -90,7 +90,7 @@ class ConcreteEMSFactory : EMSFactory {
|
||||
EMSFactory::registerFactory(device_type, this);
|
||||
}
|
||||
|
||||
auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, std::string & version, std::string & name, uint8_t flags, uint8_t brand) const
|
||||
auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, std::string & name, uint8_t flags, uint8_t brand) const
|
||||
-> EMSdevice * {
|
||||
return new DerivedClass(device_type, device_id, product_id, version, name, flags, brand);
|
||||
}
|
||||
|
||||
286
src/helpers.cpp
286
src/helpers.cpp
@@ -33,6 +33,35 @@ char * Helpers::hextoa(char * result, const uint8_t value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// same as above but to a hex string
|
||||
std::string Helpers::hextoa(const uint8_t value, bool prefix) {
|
||||
char buf[3];
|
||||
if (prefix) {
|
||||
return std::string("0x") + hextoa(buf, value);
|
||||
}
|
||||
return std::string(hextoa(buf, value));
|
||||
}
|
||||
|
||||
// same for 16 bit values
|
||||
char * Helpers::hextoa(char * result, const uint16_t value) {
|
||||
if (value <= 0xFF) {
|
||||
return hextoa(result, (uint8_t)value);
|
||||
}
|
||||
hextoa(result, (uint8_t)(value >> 8));
|
||||
hextoa(&result[2], (uint8_t)(value & 0xFF));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// same as above but to a hex string
|
||||
std::string Helpers::hextoa(const uint16_t value, bool prefix) {
|
||||
char buf[5];
|
||||
if (prefix) {
|
||||
return std::string("0x") + hextoa(buf, value);
|
||||
}
|
||||
return std::string(hextoa(buf, value));
|
||||
}
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// special function to work outside of ESP's libraries
|
||||
char * Helpers::ultostr(char * ptr, uint32_t value, const uint8_t base) {
|
||||
@@ -70,11 +99,34 @@ char * Helpers::ultostr(char * ptr, uint32_t value, const uint8_t base) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fast atoi returning a std::string
|
||||
* http://www.strudel.org.uk/itoa/
|
||||
*
|
||||
*/
|
||||
std::string Helpers::itoa(int16_t value) {
|
||||
std::string buf;
|
||||
buf.reserve(25); // Pre-allocate enough space.
|
||||
int quotient = value;
|
||||
|
||||
do {
|
||||
buf += "0123456789abcdef"[std::abs(quotient % 10)];
|
||||
quotient /= 10;
|
||||
} while (quotient);
|
||||
|
||||
// Append the negative sign
|
||||
if (value < 0)
|
||||
buf += '-';
|
||||
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* itoa for 2 byte signed (short) integers
|
||||
* fast itoa and optimized for ESP32
|
||||
* written by Lukás Chmela, Released under GPLv3. http://www.strudel.org.uk/itoa/ version 0.4
|
||||
*/
|
||||
char * Helpers::itoa(char * result, int32_t value, const uint8_t base) {
|
||||
char * Helpers::itoa(int32_t value, char * result, const uint8_t base) {
|
||||
// check that the base if valid
|
||||
if (base < 2 || base > 36) {
|
||||
*result = '\0';
|
||||
@@ -82,7 +134,7 @@ char * Helpers::itoa(char * result, int32_t value, const uint8_t base) {
|
||||
}
|
||||
|
||||
char * ptr = result, *ptr1 = result;
|
||||
int16_t tmp_value;
|
||||
int32_t tmp_value;
|
||||
|
||||
do {
|
||||
tmp_value = value;
|
||||
@@ -123,31 +175,30 @@ char * Helpers::smallitoa(char * result, const uint16_t value) {
|
||||
}
|
||||
|
||||
// work out how to display booleans
|
||||
// for strings only
|
||||
char * Helpers::render_boolean(char * result, bool value) {
|
||||
uint8_t bool_format_ = EMSESP::bool_format();
|
||||
if (bool_format_ == BOOL_FORMAT_ONOFF) {
|
||||
uint8_t bool_format_ = EMSESP::system_.bool_format();
|
||||
if (bool_format_ == BOOL_FORMAT_ONOFF_STR) {
|
||||
strlcpy(result, value ? read_flash_string(F_(on)).c_str() : read_flash_string(F_(off)).c_str(), 5);
|
||||
} else if (bool_format_ == BOOL_FORMAT_ONOFF_CAP) {
|
||||
} else if (bool_format_ == BOOL_FORMAT_ONOFF_STR_CAP) {
|
||||
strlcpy(result, value ? read_flash_string(F_(ON)).c_str() : read_flash_string(F_(OFF)).c_str(), 5);
|
||||
} else if (bool_format_ == BOOL_FORMAT_TRUEFALSE) {
|
||||
strlcpy(result, value ? "true" : "false", 7);
|
||||
} else {
|
||||
} else if ((bool_format_ == BOOL_FORMAT_10) || (bool_format_ == BOOL_FORMAT_10_STR)) {
|
||||
strlcpy(result, value ? "1" : "0", 2);
|
||||
} else {
|
||||
strlcpy(result, value ? "true" : "false", 7); // default
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// render for native char strings
|
||||
// format is not used
|
||||
char * Helpers::render_value(char * result, const char * value, uint8_t format __attribute__((unused))) {
|
||||
char * Helpers::render_value(char * result, const char * value, const int8_t format __attribute__((unused))) {
|
||||
strcpy(result, value);
|
||||
return result;
|
||||
}
|
||||
|
||||
// convert unsigned int (single byte) to text value and returns it
|
||||
// format: 255(0xFF)=boolean, 0=no formatting, otherwise divide by format
|
||||
char * Helpers::render_value(char * result, uint8_t value, uint8_t format) {
|
||||
char * Helpers::render_value(char * result, uint8_t value, int8_t format, const uint8_t fahrenheit) {
|
||||
// special check if its a boolean
|
||||
if (format == EMS_VALUE_BOOL) {
|
||||
if (value == EMS_VALUE_BOOL_OFF) {
|
||||
@@ -164,8 +215,10 @@ char * Helpers::render_value(char * result, uint8_t value, uint8_t format) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int16_t new_value = fahrenheit ? format ? value * 1.8 + 32 * format * (fahrenheit - 1) : value * 1.8 + 32 * (fahrenheit - 1) : value;
|
||||
|
||||
if (!format) {
|
||||
itoa(result, value, 10); // format = 0
|
||||
itoa(new_value, result, 10); // format = 0
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -173,22 +226,31 @@ char * Helpers::render_value(char * result, uint8_t value, uint8_t format) {
|
||||
|
||||
// special case for / 2
|
||||
if (format == 2) {
|
||||
strlcpy(result, itoa(s2, value >> 1, 10), 5);
|
||||
strlcpy(result, itoa(new_value >> 1, s2, 10), 5);
|
||||
strlcat(result, ".", 5);
|
||||
strlcat(result, ((value & 0x01) ? "5" : "0"), 5);
|
||||
strlcat(result, ((new_value & 0x01) ? "5" : "0"), 7);
|
||||
return result;
|
||||
} else if (format == 4) {
|
||||
strlcpy(result, itoa(new_value >> 2, s2, 10), 5);
|
||||
strlcat(result, ".", 5);
|
||||
new_value = (new_value & 0x03) * 25;
|
||||
strlcat(result, itoa(new_value, s2, 10), 7);
|
||||
return result;
|
||||
}
|
||||
|
||||
strlcpy(result, itoa(s2, value / format, 10), 5);
|
||||
strlcat(result, ".", 5);
|
||||
strlcat(result, itoa(s2, value % format, 10), 5);
|
||||
} else if (format > 0) {
|
||||
strlcpy(result, itoa(new_value / format, s2, 10), 5);
|
||||
strlcat(result, ".", 5);
|
||||
strlcat(result, itoa(new_value % format, s2, 10), 7);
|
||||
} else {
|
||||
strlcpy(result, itoa(new_value * format * -1, s2, 10), 5);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// float: convert float to char
|
||||
// format is the precision, 0 to 8
|
||||
char * Helpers::render_value(char * result, const float value, const uint8_t format) {
|
||||
char * Helpers::render_value(char * result, const float value, const int8_t format) {
|
||||
if (format > 8) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -198,7 +260,7 @@ char * Helpers::render_value(char * result, const float value, const uint8_t for
|
||||
char * ret = result;
|
||||
int32_t whole = (int32_t)value;
|
||||
|
||||
itoa(result, whole, 10);
|
||||
itoa(whole, result, 10);
|
||||
|
||||
while (*result != '\0') {
|
||||
result++;
|
||||
@@ -206,97 +268,103 @@ char * Helpers::render_value(char * result, const float value, const uint8_t for
|
||||
|
||||
*result++ = '.';
|
||||
int32_t decimal = abs((int32_t)((value - whole) * p[format]));
|
||||
itoa(result, decimal, 10);
|
||||
itoa(decimal, result, 10);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// int16: convert short (two bytes) to text string and returns string
|
||||
// int32: convert signed 32bit to text string and returns string
|
||||
// format: 0=no division, other divide by the value given and render with a decimal point
|
||||
char * Helpers::render_value(char * result, const int16_t value, const uint8_t format) {
|
||||
if (!hasValue(value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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};
|
||||
// just print it if no conversion required (format = 0)
|
||||
if (!format) {
|
||||
itoa(result, value, 10);
|
||||
strlcpy(result, itoa(new_value, s, 10), sizeof(s)); // format is 0
|
||||
return result;
|
||||
}
|
||||
|
||||
int16_t new_value = value;
|
||||
result[0] = '\0';
|
||||
result[0] = '\0';
|
||||
|
||||
// check for negative values
|
||||
if (new_value < 0) {
|
||||
strlcpy(result, "-", 10);
|
||||
strlcpy(result, "-", sizeof(s));
|
||||
new_value *= -1; // convert to positive
|
||||
} else {
|
||||
strlcpy(result, "", 10);
|
||||
strlcpy(result, "", sizeof(s));
|
||||
}
|
||||
|
||||
// do floating point
|
||||
char s2[10] = {0};
|
||||
if (format == 2) {
|
||||
// divide by 2
|
||||
strlcat(result, itoa(s2, new_value / 2, 10), 10);
|
||||
strlcat(result, ".", 10);
|
||||
strlcat(result, ((new_value & 0x01) ? "5" : "0"), 10);
|
||||
strlcat(result, itoa(new_value / 2, s, 10), sizeof(s));
|
||||
strlcat(result, ".", sizeof(s));
|
||||
strlcat(result, ((new_value & 0x01) ? "5" : "0"), sizeof(s));
|
||||
} else if (format > 0) {
|
||||
strlcat(result, itoa(new_value / format, s, 10), sizeof(s));
|
||||
strlcat(result, ".", sizeof(s));
|
||||
strlcat(result, itoa(new_value % format, s, 10), sizeof(s));
|
||||
} else {
|
||||
strlcat(result, itoa(s2, new_value / format, 10), 10);
|
||||
strlcat(result, ".", 10);
|
||||
strlcat(result, itoa(s2, new_value % format, 10), 10);
|
||||
strlcat(result, itoa(new_value * format * -1, s, 10), sizeof(s));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// uint16: convert unsigned short (two bytes) to text string and prints it
|
||||
// format: 0=no division, other divide by the value given and render with a decimal point
|
||||
char * Helpers::render_value(char * result, const uint16_t value, const uint8_t format) {
|
||||
// int16: convert short (two bytes) to text string and prints it
|
||||
char * Helpers::render_value(char * result, const int16_t value, const int8_t format, const uint8_t fahrenheit) {
|
||||
if (!hasValue(value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (render_value(result, (int16_t)value, format)); // use same code, force it to a signed int
|
||||
return (render_value(result, (int32_t)value, format, fahrenheit)); // use same code, force it to a signed int
|
||||
}
|
||||
|
||||
// uint16: convert unsigned short (two bytes) to text string and prints it
|
||||
char * Helpers::render_value(char * result, const uint16_t value, const int8_t format, const uint8_t fahrenheit) {
|
||||
if (!hasValue(value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (render_value(result, (int32_t)value, format, fahrenheit)); // use same code, force it to a signed int
|
||||
}
|
||||
|
||||
// int8: convert signed byte to text string and prints it
|
||||
// format: 0=no division, other divide by the value given and render with a decimal point
|
||||
char * Helpers::render_value(char * result, const int8_t value, const uint8_t format) {
|
||||
char * Helpers::render_value(char * result, const int8_t value, const int8_t format, const uint8_t fahrenheit) {
|
||||
if (!hasValue(value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (render_value(result, (int16_t)value, format)); // use same code, force it to a signed int
|
||||
return (render_value(result, (int32_t)value, format, fahrenheit)); // use same code, force it to a signed int
|
||||
}
|
||||
|
||||
// uint32: render long (4 byte) unsigned values
|
||||
// format: 0=no division, other divide by the value given and render with a decimal point
|
||||
char * Helpers::render_value(char * result, const uint32_t value, const uint8_t format) {
|
||||
char * Helpers::render_value(char * result, const uint32_t value, const int8_t format, const uint8_t fahrenheit) {
|
||||
if (!hasValue(value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result[0] = '\0';
|
||||
char s[20];
|
||||
result[0] = '\0';
|
||||
int32_t new_value = fahrenheit ? format ? value * 1.8 + 32 * format * (fahrenheit - 1) : value * 1.8 + 32 * (fahrenheit - 1) : value;
|
||||
char s[10] = {0};
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!format) {
|
||||
strlcpy(result, ltoa(value, s, 10), 20); // format is 0
|
||||
strlcpy(result, ltoa(new_value, s, 10), sizeof(s)); // format is 0
|
||||
} else if (format > 0) {
|
||||
strlcpy(result, ltoa(new_value / format, s, 10), sizeof(s));
|
||||
strlcat(result, ".", sizeof(s));
|
||||
strlcat(result, ltoa(new_value % format, s, 10), sizeof(s));
|
||||
} else {
|
||||
strlcpy(result, ltoa(value / format, s, 10), 20);
|
||||
strlcat(result, ".", 20);
|
||||
strlcat(result, ltoa(value % format, s, 10), 20);
|
||||
strlcpy(result, ltoa(new_value * format * -1, s, 10), sizeof(s));
|
||||
}
|
||||
|
||||
#else
|
||||
if (!format) {
|
||||
strlcpy(result, ultostr(s, value, 10), 20); // format is 0
|
||||
strlcpy(result, ultostr(s, new_value, 10), sizeof(s)); // format is 0
|
||||
} else {
|
||||
strncpy(result, ultostr(s, value / format, 10), 20);
|
||||
strlcat(result, ".", 20);
|
||||
strncat(result, ultostr(s, value % format, 10), 20);
|
||||
strncpy(result, ultostr(s, new_value / format, 10), sizeof(s));
|
||||
strlcat(result, ".", sizeof(s));
|
||||
strncat(result, ultostr(s, new_value % format, 10), sizeof(s));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -306,12 +374,12 @@ char * Helpers::render_value(char * result, const uint32_t value, const uint8_t
|
||||
// creates string of hex values from an arrray of bytes
|
||||
std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) {
|
||||
if (length == 0) {
|
||||
return read_flash_string(F("<empty>"));
|
||||
return "<empty>";
|
||||
}
|
||||
|
||||
std::string str(160, '\0');
|
||||
char buffer[4];
|
||||
char * p = &str[0];
|
||||
char str[160] = {0};
|
||||
char buffer[4];
|
||||
char * p = &str[0];
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
Helpers::hextoa(buffer, data[i]);
|
||||
*p++ = buffer[0];
|
||||
@@ -320,17 +388,23 @@ std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) {
|
||||
}
|
||||
*--p = '\0'; // null terminate just in case, loosing the trailing space
|
||||
|
||||
return str;
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
// takes a hex string and convert it to an unsigned 32bit number (max 8 hex digits)
|
||||
// works with only positive numbers
|
||||
uint32_t Helpers::hextoint(const char * hex) {
|
||||
if (hex == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t val = 0;
|
||||
|
||||
// skip leading '0x'
|
||||
if (hex[0] == '0' && hex[1] == 'x') {
|
||||
hex += 2;
|
||||
}
|
||||
|
||||
while (*hex) {
|
||||
// get current character then increment
|
||||
char byte = *hex++;
|
||||
@@ -346,29 +420,49 @@ uint32_t Helpers::hextoint(const char * hex) {
|
||||
// shift 4 to make space for new digit, and add the 4 bits of the new digit
|
||||
val = (val << 4) | (byte & 0xF);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
// quick char to long
|
||||
uint16_t Helpers::atoint(const char * value) {
|
||||
unsigned int x = 0;
|
||||
int Helpers::atoint(const char * value) {
|
||||
int x = 0;
|
||||
char s = value[0];
|
||||
if (s == '-') {
|
||||
++value;
|
||||
}
|
||||
while (*value >= '0' && *value <= '9') {
|
||||
x = (x * 10) + (*value - '0');
|
||||
++value;
|
||||
}
|
||||
if (s == '-') {
|
||||
return (-x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// rounds a number to 2 decimal places
|
||||
// example: round2(3.14159) -> 3.14
|
||||
float Helpers::round2(float value, const uint8_t divider) {
|
||||
uint8_t div = (divider ? divider : 1); // prevent div-by-zero
|
||||
|
||||
if (value >= 0) {
|
||||
return (int)((value / div) * 100 + 0.5) / 100.0;
|
||||
// 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
|
||||
// fahrenheit=2 - absolute, 1.8t + 32(fahrenheit-1).
|
||||
float Helpers::round2(float value, const int8_t divider, const uint8_t fahrenheit) {
|
||||
float val = (value * 100 + 0.5);
|
||||
if (divider > 0) {
|
||||
val = ((value / divider) * 100 + 0.5);
|
||||
} else if (divider < 0) {
|
||||
val = value * -100 * divider;
|
||||
}
|
||||
if (value < 0) { // negative rounding
|
||||
val = val - 1;
|
||||
}
|
||||
if (fahrenheit) {
|
||||
val = val * 1.8 + 3200 * (fahrenheit - 1);
|
||||
}
|
||||
|
||||
return (int)((value / div) * 100 - 0.5) / 100.0; // negative values
|
||||
return ((int32_t)val) / 100.0;
|
||||
}
|
||||
|
||||
// abs of a signed 32-bit integer
|
||||
@@ -410,13 +504,16 @@ bool Helpers::hasValue(const uint32_t & v) {
|
||||
}
|
||||
|
||||
// checks if we can convert a char string to an int value
|
||||
bool Helpers::value2number(const char * v, int & value) {
|
||||
bool Helpers::value2number(const char * v, int & value, const int min, const int max) {
|
||||
if ((v == nullptr) || (strlen(v) == 0)) {
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
value = atoi((char *)v);
|
||||
return true;
|
||||
if (value >= min && value <= max) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if we can convert a char string to a float value
|
||||
@@ -432,6 +529,26 @@ bool Helpers::value2float(const char * v, float & value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Helpers::value2temperature(const char * v, float & value, bool relative) {
|
||||
if (value2float(v, value)) {
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
value = relative ? (value / 1.8) : (value - 32) / 1.8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Helpers::value2temperature(const char * v, int & value, const bool relative, const int min, const int max) {
|
||||
if (value2number(v, value, min, max)) {
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
value = relative ? (value / 1.8) : (value - 32) / 1.8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/313970/how-to-convert-stdstring-to-lower-case
|
||||
std::string Helpers::toLower(std::string const & s) {
|
||||
std::string lc = s;
|
||||
@@ -485,13 +602,26 @@ bool Helpers::value2enum(const char * v, uint8_t & value, const __FlashStringHel
|
||||
std::string str = toLower(v);
|
||||
for (value = 0; strs[value]; value++) {
|
||||
std::string str1 = toLower(read_flash_string(strs[value]));
|
||||
if ((str1 == read_flash_string(F_(off)) && str == "false") || (str1 == read_flash_string(F_(on)) && str == "true") || (str == str1)
|
||||
|| (v[0] == ('0' + value) && v[1] == '\0')) {
|
||||
if ((str1 != "")
|
||||
&& ((str1 == read_flash_string(F_(off)) && str == "false") || (str1 == read_flash_string(F_(on)) && str == "true") || (str == str1)
|
||||
|| (v[0] == ('0' + value) && v[1] == '\0'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// replace char in char string
|
||||
void Helpers::replace_char(char * str, char find, char replace) {
|
||||
int i = 0;
|
||||
|
||||
while (str[i] != '\0') {
|
||||
/*Replace the matched character...*/
|
||||
if (str[i] == find)
|
||||
str[i] = replace;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#ifndef EMSESP_HELPERS_H
|
||||
#define EMSESP_HELPERS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "telegram.h" // for EMS_VALUE_* settings
|
||||
|
||||
#define FJSON(x) x
|
||||
@@ -32,27 +30,33 @@ using flash_string_vector = std::vector<const __FlashStringHelper *>;
|
||||
|
||||
class Helpers {
|
||||
public:
|
||||
static char * render_value(char * result, const float value, const uint8_t format); // format is the precision
|
||||
static char * render_value(char * result, const uint8_t value, const uint8_t format);
|
||||
static char * render_value(char * result, const int8_t value, const uint8_t format);
|
||||
static char * render_value(char * result, const uint16_t value, const uint8_t format);
|
||||
static char * render_value(char * result, const uint32_t value, const uint8_t format);
|
||||
static char * render_value(char * result, const int16_t value, const uint8_t format);
|
||||
static char * render_value(char * result, const char * value, uint8_t format);
|
||||
static char * render_value(char * result, const float value, const int8_t format); // format is the precision
|
||||
static char * render_value(char * result, const uint8_t value, const int8_t format, const uint8_t fahrenheit = 0);
|
||||
static char * render_value(char * result, const int8_t value, const int8_t format, const uint8_t fahrenheit = 0);
|
||||
static char * render_value(char * result, const uint16_t value, const int8_t format, const uint8_t fahrenheit = 0);
|
||||
static char * render_value(char * result, const uint32_t value, const int8_t format, const uint8_t fahrenheit = 0);
|
||||
static char * render_value(char * result, const int16_t value, const int8_t format, const uint8_t fahrenheit = 0);
|
||||
static char * render_value(char * result, const int32_t value, const int8_t format, const uint8_t fahrenheit = 0);
|
||||
static char * render_value(char * result, const char * value, const int8_t format);
|
||||
static char * render_boolean(char * result, bool value);
|
||||
|
||||
static char * hextoa(char * result, const uint8_t value);
|
||||
static char * hextoa(char * result, const uint16_t value);
|
||||
static std::string hextoa(const uint8_t value, bool prefix = true); // default prefix with 0x
|
||||
static std::string hextoa(const uint16_t value, bool prefix = true); // default prefix with 0x
|
||||
static std::string data_to_hex(const uint8_t * data, const uint8_t length);
|
||||
static char * smallitoa(char * result, const uint8_t value);
|
||||
static char * smallitoa(char * result, const uint16_t value);
|
||||
static char * itoa(char * result, int32_t value, const uint8_t base = 10);
|
||||
static char * itoa(int32_t value, char * result, const uint8_t base = 10);
|
||||
static std::string itoa(int16_t value);
|
||||
static uint32_t hextoint(const char * hex);
|
||||
static uint16_t atoint(const char * value);
|
||||
static int atoint(const char * value);
|
||||
static bool check_abs(const int32_t i);
|
||||
static uint32_t abs(const int32_t i);
|
||||
static float round2(float value, const uint8_t divider);
|
||||
static float round2(float value, const int8_t divider, const uint8_t fahrenheit = 0);
|
||||
static std::string toLower(std::string const & s);
|
||||
static std::string toUpper(std::string const & s);
|
||||
static void replace_char(char * str, char find, char replace);
|
||||
|
||||
static bool hasValue(const uint8_t & v, const uint8_t isBool = 0);
|
||||
static bool hasValue(const int8_t & v);
|
||||
@@ -61,11 +65,13 @@ class Helpers {
|
||||
static bool hasValue(const uint32_t & v);
|
||||
static bool hasValue(char * v);
|
||||
|
||||
static bool value2number(const char * v, int & value);
|
||||
static bool value2number(const char * v, int & value, const int min = -2147483648, const int max = 2147483647);
|
||||
static bool value2float(const char * v, float & value);
|
||||
static bool value2bool(const char * v, bool & value);
|
||||
static bool value2string(const char * v, std::string & value);
|
||||
static bool value2enum(const char * v, uint8_t & value, const __FlashStringHelper * const * strs);
|
||||
static bool value2temperature(const char * v, float & value, bool relative = false);
|
||||
static bool value2temperature(const char * v, int & value, const bool relative = false, const int min = -2147483648, const int max = 2147483647);
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
static char * ultostr(char * ptr, uint32_t value, const uint8_t base);
|
||||
|
||||
639
src/locale_DE.h
Normal file
639
src/locale_DE.h
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// 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(on)
|
||||
MAKE_PSTR_WORD(off)
|
||||
MAKE_PSTR_WORD(ON)
|
||||
MAKE_PSTR_WORD(OFF)
|
||||
MAKE_PSTR_WORD(su)
|
||||
MAKE_PSTR_WORD(name)
|
||||
MAKE_PSTR_WORD(auto)
|
||||
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(master)
|
||||
MAKE_PSTR_WORD(pin)
|
||||
MAKE_PSTR_WORD(publish)
|
||||
MAKE_PSTR_WORD(timeout)
|
||||
MAKE_PSTR_WORD(board_profile)
|
||||
MAKE_PSTR_WORD(counter)
|
||||
MAKE_PSTR_WORD(sensorname)
|
||||
|
||||
// for commands
|
||||
MAKE_PSTR_WORD(call)
|
||||
MAKE_PSTR_WORD(cmd)
|
||||
MAKE_PSTR_WORD(id)
|
||||
MAKE_PSTR_WORD(device)
|
||||
MAKE_PSTR_WORD(data)
|
||||
MAKE_PSTR_WORD(command)
|
||||
MAKE_PSTR_WORD(commands)
|
||||
MAKE_PSTR_WORD(info)
|
||||
// MAKE_PSTR_WORD(info_short)
|
||||
MAKE_PSTR_WORD(settings)
|
||||
MAKE_PSTR_WORD(value)
|
||||
MAKE_PSTR_WORD(error)
|
||||
|
||||
// devices
|
||||
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(sensor)
|
||||
MAKE_PSTR_WORD(unknown)
|
||||
MAKE_PSTR_WORD(Sensor)
|
||||
MAKE_PSTR_WORD(other)
|
||||
|
||||
// format strings
|
||||
MAKE_PSTR(master_thermostat_fmt, "Master Thermostat Device ID: %s")
|
||||
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(ethernet_option_fmt, "Ethernet option: %d")
|
||||
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_STR(productid_fmt, "%s EMS Product ID")
|
||||
|
||||
MAKE_PSTR_LIST(enum_syslog_level, F_(off), F("emerg"), F("alert"), F("crit"), F_(error), F("warn"), F("notice"), F_(info), F_(debug), F("trace"), F("all"))
|
||||
MAKE_PSTR_LIST(enum_watch, F_(off), F_(on), F_(raw), F_(unknown))
|
||||
|
||||
// strings
|
||||
MAKE_PSTR(show_optional, "[devices | users | ems | mqtt | system | commands]")
|
||||
MAKE_PSTR(EMSESP, "EMS-ESP")
|
||||
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(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>")
|
||||
|
||||
// command descriptions
|
||||
MAKE_PSTR(info_cmd, "list all values")
|
||||
MAKE_PSTR(commands_cmd, "list all commands")
|
||||
|
||||
MAKE_PSTR_WORD(number)
|
||||
MAKE_PSTR_WORD(enum)
|
||||
MAKE_PSTR_WORD(text)
|
||||
|
||||
MAKE_PSTR_WORD(2)
|
||||
MAKE_PSTR_WORD(4)
|
||||
MAKE_PSTR_WORD(10)
|
||||
MAKE_PSTR_WORD(100)
|
||||
MAKE_PSTR_WORD(60)
|
||||
|
||||
MAKE_PSTR_LIST(div2, F_(2))
|
||||
MAKE_PSTR_LIST(div4, F_(4))
|
||||
MAKE_PSTR_LIST(div10, F_(10))
|
||||
MAKE_PSTR_LIST(div100, F_(100))
|
||||
MAKE_PSTR_LIST(div60, F_(60))
|
||||
MAKE_PSTR_LIST(mul15, F("-15"))
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// uom - also used with HA see https://github.com/home-assistant/core/blob/d7ac4bd65379e11461c7ce0893d3533d8d8b8cbf/homeassistant/const.py#L384
|
||||
MAKE_PSTR(blank, " ")
|
||||
MAKE_PSTR(percent, "%")
|
||||
MAKE_PSTR(degrees, "°C")
|
||||
MAKE_PSTR(kwh, "kWh")
|
||||
MAKE_PSTR(wh, "Wh")
|
||||
MAKE_PSTR(bar, "bar")
|
||||
MAKE_PSTR(minutes, "Minuten")
|
||||
MAKE_PSTR(hours, "Stunden")
|
||||
MAKE_PSTR(ua, "uA")
|
||||
MAKE_PSTR(lmin, "l/min")
|
||||
MAKE_PSTR(kw, "kW")
|
||||
MAKE_PSTR(w, "W")
|
||||
MAKE_PSTR(kb, "KB")
|
||||
MAKE_PSTR(seconds, "seconds")
|
||||
MAKE_PSTR(dbm, "dBm")
|
||||
MAKE_PSTR(fahrenheit, "°F")
|
||||
MAKE_PSTR(mv, "mV")
|
||||
MAKE_PSTR(sqm, "sqm")
|
||||
|
||||
// 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_PSTR(tag_none, "")
|
||||
MAKE_PSTR(tag_heartbeat, "")
|
||||
MAKE_PSTR(tag_boiler_data, "")
|
||||
MAKE_PSTR(tag_device_data_ww, "Warmwasser")
|
||||
MAKE_PSTR(tag_thermostat_data, "")
|
||||
MAKE_PSTR(tag_hc1, "hc1")
|
||||
MAKE_PSTR(tag_hc2, "hc2")
|
||||
MAKE_PSTR(tag_hc3, "hc3")
|
||||
MAKE_PSTR(tag_hc4, "hc4")
|
||||
MAKE_PSTR(tag_wwc1, "wwc1")
|
||||
MAKE_PSTR(tag_wwc2, "wwc2")
|
||||
MAKE_PSTR(tag_wwc3, "wwc3")
|
||||
MAKE_PSTR(tag_wwc4, "wwc4")
|
||||
MAKE_PSTR(tag_hs1, "hs1")
|
||||
MAKE_PSTR(tag_hs2, "hs2")
|
||||
MAKE_PSTR(tag_hs3, "hs3")
|
||||
MAKE_PSTR(tag_hs4, "hs4")
|
||||
MAKE_PSTR(tag_hs5, "hs5")
|
||||
MAKE_PSTR(tag_hs6, "hs6")
|
||||
MAKE_PSTR(tag_hs7, "hs7")
|
||||
MAKE_PSTR(tag_hs8, "hs8")
|
||||
MAKE_PSTR(tag_hs9, "hs9")
|
||||
MAKE_PSTR(tag_hs10, "hs10")
|
||||
MAKE_PSTR(tag_hs11, "hs11")
|
||||
MAKE_PSTR(tag_hs12, "hs12")
|
||||
MAKE_PSTR(tag_hs13, "hs13")
|
||||
MAKE_PSTR(tag_hs14, "hs14")
|
||||
MAKE_PSTR(tag_hs15, "hs15")
|
||||
MAKE_PSTR(tag_hs16, "hs16")
|
||||
|
||||
// MQTT topic names
|
||||
// MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
|
||||
MAKE_PSTR(tag_boiler_data_mqtt, "")
|
||||
MAKE_PSTR(tag_device_data_ww_mqtt, "ww")
|
||||
|
||||
// boiler
|
||||
MAKE_PSTR(time, "Zeit")
|
||||
MAKE_PSTR(date, "Datum")
|
||||
MAKE_PSTR_WORD(1x3min)
|
||||
MAKE_PSTR_WORD(2x3min)
|
||||
MAKE_PSTR_WORD(3x3min)
|
||||
MAKE_PSTR_WORD(4x3min)
|
||||
MAKE_PSTR_WORD(5x3min)
|
||||
MAKE_PSTR_WORD(6x3min)
|
||||
MAKE_PSTR_(continuos, "kontinuierlich")
|
||||
MAKE_PSTR(3wayvalve, "3-wege Ventil")
|
||||
MAKE_PSTR(chargepump, "Ladepumpe")
|
||||
MAKE_PSTR(hot, "Heiss")
|
||||
MAKE_PSTR_WORD(eco)
|
||||
MAKE_PSTR_WORD(intelligent)
|
||||
MAKE_PSTR(manual, "Manuell")
|
||||
MAKE_PSTR_(flow, "Fluss")
|
||||
MAKE_PSTR_(buffer, "Speicher")
|
||||
MAKE_PSTR(bufferedflow, "Durchlaufspeicher")
|
||||
MAKE_PSTR(layeredbuffer, "Schichtspeicher")
|
||||
MAKE_PSTR_WORD(maintenance)
|
||||
|
||||
// boiler lists
|
||||
MAKE_PSTR_LIST(tpl_date, F("Format: < dd.mm.yyyy >")) // template for text input
|
||||
MAKE_PSTR_LIST(enum_off_time_date_manual, F_(off), F_(time), F_(date), F_(manual))
|
||||
MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min), F_(5x3min), F_(6x3min), F_(continuos))
|
||||
MAKE_PSTR_LIST(enum_charge, F_(chargepump), F_(3wayvalve))
|
||||
MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent))
|
||||
MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer))
|
||||
MAKE_PSTR_LIST(enum_reset, F_(maintenance), F_(error))
|
||||
// MAKE_PSTR_LIST(enum_bool, F_(Aus), F_(Ein))
|
||||
|
||||
// thermostat
|
||||
MAKE_PSTR(light, "Leicht")
|
||||
MAKE_PSTR(medium, "Mittel")
|
||||
MAKE_PSTR(heavy, "Schwer")
|
||||
MAKE_PSTR(own_prog, "Eigenprog")
|
||||
MAKE_PSTR_WORD(start)
|
||||
MAKE_PSTR(heat, "Heizen")
|
||||
MAKE_PSTR(hold, "Halten")
|
||||
MAKE_PSTR(cool, "Kühl")
|
||||
MAKE_PSTR(end, "Ende")
|
||||
MAKE_PSTR(german, "Deutsch")
|
||||
MAKE_PSTR(dutch, "Niederländisch")
|
||||
MAKE_PSTR(french, "Französisch")
|
||||
MAKE_PSTR(italian, "Italienisch")
|
||||
MAKE_PSTR(high, "hoch")
|
||||
MAKE_PSTR(low, "niedrig")
|
||||
MAKE_PSTR(radiator, "Heizkörper")
|
||||
MAKE_PSTR(convector, "Konvektor")
|
||||
MAKE_PSTR(floor, "Fussboden")
|
||||
MAKE_PSTR(summer, "Sommer")
|
||||
MAKE_PSTR_WORD(winter)
|
||||
MAKE_PSTR(outdoor, "Aussentemperatur")
|
||||
MAKE_PSTR_WORD(mpc)
|
||||
MAKE_PSTR(room, "Raum")
|
||||
MAKE_PSTR(power, "Leistung")
|
||||
MAKE_PSTR(constant, "konstant")
|
||||
MAKE_PSTR(simple, "einfach")
|
||||
MAKE_PSTR(optimized, "optimiert")
|
||||
MAKE_PSTR(nofrost, "Frostschutz")
|
||||
MAKE_PSTR(comfort, "Komfort")
|
||||
MAKE_PSTR(night, "Nacht")
|
||||
MAKE_PSTR(day, "Tag")
|
||||
MAKE_PSTR(holiday, "Urlaub")
|
||||
MAKE_PSTR(reduce, "reduziert")
|
||||
MAKE_PSTR(noreduce, "unreduziert")
|
||||
MAKE_PSTR(offset, "Anhebung")
|
||||
MAKE_PSTR(design, "Auslegung")
|
||||
MAKE_PSTR_WORD(tempauto)
|
||||
MAKE_PSTR(minflow, "minfluss")
|
||||
MAKE_PSTR(maxflow, "maxfluss")
|
||||
|
||||
MAKE_PSTR_WORD(rc3x)
|
||||
MAKE_PSTR_WORD(rc20)
|
||||
MAKE_PSTR(internal_temperature, "interne Temperatur")
|
||||
MAKE_PSTR(internal_setpoint, "interner Sollwert")
|
||||
MAKE_PSTR(external_temperature, "externe Temperatur")
|
||||
MAKE_PSTR(burner_temperature, "Kesseltemperatur")
|
||||
MAKE_PSTR(ww_temperature, "Wassertemperatur")
|
||||
MAKE_PSTR(functioning_mode, "functioning mode")
|
||||
MAKE_PSTR(smoke_temperature, "Abgastemperatur")
|
||||
|
||||
// thermostat lists
|
||||
MAKE_PSTR_LIST(tpl_datetime, F("Format: < hh:mm:ss dd/mm/yyyy-dw-dst | NTP >"))
|
||||
MAKE_PSTR_LIST(tpl_switchtime, F("Format: < nn.d.o.hh:mm >"))
|
||||
MAKE_PSTR_LIST(enum_ibaMainDisplay,
|
||||
F_(internal_temperature),
|
||||
F_(internal_setpoint),
|
||||
F_(external_temperature),
|
||||
F_(burner_temperature),
|
||||
F_(ww_temperature),
|
||||
F_(functioning_mode),
|
||||
F_(time),
|
||||
F_(date),
|
||||
F_(smoke_temperature))
|
||||
MAKE_PSTR_LIST(enum_ibaLanguage, F_(german), F_(dutch), F_(french), F_(italian))
|
||||
MAKE_PSTR_LIST(enum_floordrystatus, F_(off), F_(start), F_(heat), F_(hold), F_(cool), F_(end))
|
||||
MAKE_PSTR_LIST(enum_ibaBuildingType, F_(light), F_(medium), F_(heavy)) // RC300
|
||||
MAKE_PSTR_LIST(enum_wwMode, F_(off), F_(low), F_(high), F_(auto), F_(own_prog))
|
||||
MAKE_PSTR_LIST(enum_wwCircMode, F_(off), F_(on), F_(auto), F_(own_prog))
|
||||
MAKE_PSTR_LIST(enum_wwMode2, F_(off), F_(on), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_wwCircMode2, F_(off), F_(on), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor))
|
||||
MAKE_PSTR_LIST(enum_summermode, F_(summer), F_(auto), F_(winter))
|
||||
|
||||
MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_mode4, F_(nofrost), F_(eco), F_(heat), F_(auto)) // JUNKERS
|
||||
MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off))
|
||||
|
||||
MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort))
|
||||
MAKE_PSTR_LIST(enum_modetype2, F_(day))
|
||||
MAKE_PSTR_LIST(enum_modetype3, F_(night), F_(day))
|
||||
MAKE_PSTR_LIST(enum_modetype4, F_(nofrost), F_(eco), F_(heat))
|
||||
MAKE_PSTR_LIST(enum_modetype5, F_(off), F_(on))
|
||||
|
||||
MAKE_PSTR_LIST(enum_reducemode, F_(nofrost), F_(reduce), F_(room), F_(outdoor))
|
||||
|
||||
MAKE_PSTR_LIST(enum_controlmode, F_(off), F_(optimized), F_(simple), F_(mpc), F_(room), F_(power), F_(constant))
|
||||
MAKE_PSTR_LIST(enum_controlmode2, F_(outdoor), F_(room))
|
||||
MAKE_PSTR_LIST(enum_controlmode3, F_(off), F_(room), F_(outdoor), F("room+outdoor"))
|
||||
MAKE_PSTR_LIST(enum_control, F_(off), F_(rc20), F_(rc3x))
|
||||
|
||||
MAKE_PSTR_LIST(enum_wwProgMode, F("std prog"), F_(own_prog))
|
||||
MAKE_PSTR_LIST(enum_wwDisinfectDay, F("Mo"), F("Di"), F("Mi"), F("Do"), F("Fr"), F("Sa"), F("So"), F("t<EFBFBD>glich"))
|
||||
MAKE_PSTR_LIST(enum_wwChargeDuration, F_(off), F("15min"), F("30min"), F("45min"), F("60min"), F("75min"), F("90min"), F("105min"), F("120min"))
|
||||
|
||||
// solar list
|
||||
MAKE_PSTR_LIST(enum_solarmode, F_(constant), F("pwm"), F("analog"))
|
||||
MAKE_PSTR_LIST(enum_collectortype, F("flat"), F("vacuum"))
|
||||
|
||||
// id used to store the device ID, goes into MQTT payload
|
||||
// empty full name to prevent being shown in web or console
|
||||
MAKE_PSTR_LIST(ID, F_(id))
|
||||
|
||||
// Boiler
|
||||
// extra commands
|
||||
MAKE_PSTR_LIST(wwtapactivated, F("wwtapactivated"), F("Aktiviere Warmwasser im Wartungsmodus"))
|
||||
MAKE_PSTR_LIST(reset, F("reset"), F("Sende 'RESET'"))
|
||||
|
||||
// single mqtt topics
|
||||
MAKE_PSTR_WORD(heating_active)
|
||||
MAKE_PSTR_WORD(tapwater_active)
|
||||
MAKE_PSTR_WORD(response)
|
||||
|
||||
// mqtt, commands and text
|
||||
MAKE_PSTR_LIST(heatingActive, F("heatingactive"), F("Heizung aktiv"))
|
||||
MAKE_PSTR_LIST(tapwaterActive, F("tapwateractive"), F("Warmwasser aktiv"))
|
||||
MAKE_PSTR_LIST(selFlowTemp, F("selflowtemp"), F("Sollwert Flusstemperatur"))
|
||||
MAKE_PSTR_LIST(selBurnPow, F("selburnpow"), F("Sollwert Brennerleistung"))
|
||||
MAKE_PSTR_LIST(heatingPumpMod, F("heatingpumpmod"), F("Heizungspumpe 1 Modulation"))
|
||||
MAKE_PSTR_LIST(heatingPump2Mod, F("heatingpump2mod"), F("Heizungspumpe 2 Modulation"))
|
||||
MAKE_PSTR_LIST(outdoorTemp, F("outdoortemp"), F("Aussentemperatur"))
|
||||
MAKE_PSTR_LIST(curFlowTemp, F("curflowtemp"), F("aktuelle Flusstemperatur"))
|
||||
MAKE_PSTR_LIST(retTemp, F("rettemp"), F("Rücklauftemperatur"))
|
||||
MAKE_PSTR_LIST(switchTemp, F("switchtemp"), F("Mischer Schalttemperatur"))
|
||||
MAKE_PSTR_LIST(sysPress, F("syspress"), F("Systemdruck"))
|
||||
MAKE_PSTR_LIST(boilTemp, F("boiltemp"), F("Kesseltemperatur"))
|
||||
MAKE_PSTR_LIST(exhaustTemp, F("exhausttemp"), F("Auslasstemperatur"))
|
||||
MAKE_PSTR_LIST(burnGas, F("burngas"), F("Gas"))
|
||||
MAKE_PSTR_LIST(flameCurr, F("flamecurr"), F("Flammstrom"))
|
||||
MAKE_PSTR_LIST(heatingPump, F("heatingpump"), F("Heizungspumpe"))
|
||||
MAKE_PSTR_LIST(fanWork, F("fanwork"), F("Gebläse"))
|
||||
MAKE_PSTR_LIST(ignWork, F("ignwork"), F("Zündung"))
|
||||
MAKE_PSTR_LIST(heatingActivated, F("heatingactivated"), F("Heizen aktiviert"))
|
||||
MAKE_PSTR_LIST(heatingTemp, F("heatingtemp"), F("Kesseltemperatur"))
|
||||
MAKE_PSTR_LIST(pumpModMax, F("pumpmodmax"), F("Kesselpumpen Maximalleistung"))
|
||||
MAKE_PSTR_LIST(pumpModMin, F("pumpmodmin"), F("Kesselpumpen Minmalleistung"))
|
||||
MAKE_PSTR_LIST(pumpDelay, F("pumpdelay"), F("Pumpennachlauf"))
|
||||
MAKE_PSTR_LIST(burnMinPeriod, F("burnminperiod"), F("Antipendelzeit"))
|
||||
MAKE_PSTR_LIST(burnMinPower, F("burnminpower"), F("minimale Brennerleistung"))
|
||||
MAKE_PSTR_LIST(burnMaxPower, F("burnmaxpower"), F("maximale Brennerleistung"))
|
||||
MAKE_PSTR_LIST(boilHystOn, F("boilhyston"), F("Hysterese ein temperatur"))
|
||||
MAKE_PSTR_LIST(boilHystOff, F("boilhystoff"), F("Hysterese aus temperatur"))
|
||||
MAKE_PSTR_LIST(setFlowTemp, F("setflowtemp"), F("Sollwert Flusstemperatur"))
|
||||
MAKE_PSTR_LIST(setBurnPow, F("setburnpow"), F("Sollwert Brennerleistung"))
|
||||
MAKE_PSTR_LIST(curBurnPow, F("curburnpow"), F("Brennerleistung"))
|
||||
MAKE_PSTR_LIST(burnStarts, F("burnstarts"), F("Brenner # starts"))
|
||||
MAKE_PSTR_LIST(burnWorkMin, F("burnworkmin"), F("Brenner Laufzeit"))
|
||||
MAKE_PSTR_LIST(heatWorkMin, F("heatworkmin"), F("Heizung Laufzeit"))
|
||||
MAKE_PSTR_LIST(UBAuptime, F("ubauptime"), F("gesamte Laufzeit"))
|
||||
MAKE_PSTR_LIST(lastCode, F("lastcode"), F("Fehlerspeicher"))
|
||||
MAKE_PSTR_LIST(serviceCode, F("servicecode"), F("Statusmeldung"))
|
||||
MAKE_PSTR_LIST(serviceCodeNumber, F("servicecodenumber"), F("Statusmeldungsnummer"))
|
||||
MAKE_PSTR_LIST(maintenanceMessage, F("maintenancemessage"), F("Wartungsmeldung"))
|
||||
MAKE_PSTR_LIST(maintenanceDate, F("maintenancedate"), F("Wartungsdatum"))
|
||||
MAKE_PSTR_LIST(maintenanceType, F_(maintenance), F("Wartungsplan"))
|
||||
MAKE_PSTR_LIST(maintenanceTime, F("maintenancetime"), F("Wartung in"))
|
||||
|
||||
MAKE_PSTR_LIST(upTimeControl, F("uptimecontrol"), F("Betriebszeit total heizen"))
|
||||
MAKE_PSTR_LIST(upTimeCompHeating, F("uptimecompheating"), F("Betriebszeit Kompressor heizen"))
|
||||
MAKE_PSTR_LIST(upTimeCompCooling, F("uptimecompcooling"), F("Betriebszeit Kompressor kühlen"))
|
||||
MAKE_PSTR_LIST(upTimeCompWw, F("uptimecompww"), F("Betriebszeit Kompressor"))
|
||||
MAKE_PSTR_LIST(heatingStarts, F("heatingstarts"), F("Heizen Starts"))
|
||||
MAKE_PSTR_LIST(coolingStarts, F("coolingstarts"), F("Kühlen Starts"))
|
||||
MAKE_PSTR_LIST(nrgConsTotal, F("nrgconstotal"), F("totaler Energieverbrauch"))
|
||||
MAKE_PSTR_LIST(nrgConsCompTotal, F("nrgconscomptotal"), F("Energieverbrauch Kompressor total"))
|
||||
MAKE_PSTR_LIST(nrgConsCompHeating, F("nrgconscompheating"), F("Energieverbrauch Kompressor heizen"))
|
||||
MAKE_PSTR_LIST(nrgConsCompWw, F("nrgconscompww"), F("Energieverbrauch Kompressor"))
|
||||
MAKE_PSTR_LIST(nrgConsCompCooling, F("nrgconscompcooling"), F("Energieverbrauch Kompressor kühlen"))
|
||||
MAKE_PSTR_LIST(nrgSuppTotal, F("nrgsupptotal"), F("gesamte Energieabgabe"))
|
||||
MAKE_PSTR_LIST(nrgSuppHeating, F("nrgsuppheating"), F("gesamte Energieabgabe heizen"))
|
||||
MAKE_PSTR_LIST(nrgSuppWw, F("nrgsuppww"), F("gesamte Energieabgabe"))
|
||||
MAKE_PSTR_LIST(nrgSuppCooling, F("nrgsuppcooling"), F("gesamte Energieabgabe kühlen"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsTotal, F("auxelecheatnrgconstotal"), F("Energieverbrauch el. Zusatzheizung"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsHeating, F("auxelecheatnrgconsheating"), F("Energieverbrauch el. Zusatzheizung heizen"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsWW, F("auxelecheatnrgconsww"), F("Energieverbrauch el. Zusatzheizung"))
|
||||
|
||||
MAKE_PSTR_LIST(hpPower, F("hppower"), F("Leistung Wärmepumpe"))
|
||||
MAKE_PSTR_LIST(hpTc0, F("hptc0"), F("Wärmeträgerflüssigkeit Eingang (TC0)"))
|
||||
MAKE_PSTR_LIST(hpTc1, F("hptc1"), F("Wärmeträgerflüssigkeit Ausgang (TC1)"))
|
||||
MAKE_PSTR_LIST(hpTc3, F("hptc3"), F("Verflüssigertemperatur (TC3)"))
|
||||
MAKE_PSTR_LIST(hpTr3, F("hptr3"), F(" Temperaturfühler Kältemittel (Flüssigkeit) (TR3)"))
|
||||
MAKE_PSTR_LIST(hpTr4, F("hptr4"), F("Verdampfer Eintritt (TR4)"))
|
||||
MAKE_PSTR_LIST(hpTr5, F("hptr5"), F("Temperaturfühler Kompessoransaugleitung (TR5)"))
|
||||
MAKE_PSTR_LIST(hpTr6, F("hptr6"), F("Temperaturfühler Kompressorausgangsleitung (TR6)"))
|
||||
MAKE_PSTR_LIST(hpTr7, F("hptr7"), F("Temperaturfühler Kältemittel (Gas) (TR7)"))
|
||||
MAKE_PSTR_LIST(hpTl2, F("hptl2"), F("Außenlufttemperaturfühler (TL2)"))
|
||||
MAKE_PSTR_LIST(hpPl1, F("hppl1"), F("Niedrigdruckfühler (PL1)"))
|
||||
MAKE_PSTR_LIST(hpPh1, F("hpph1"), F("Hochdruckfühler (PH1)"))
|
||||
|
||||
MAKE_PSTR_LIST(wWSelTemp, F("wwseltemp"), F("gewählte Temperatur"))
|
||||
MAKE_PSTR_LIST(wWSetTemp, F("wwsettemp"), F("Solltemperatur"))
|
||||
MAKE_PSTR_LIST(wWType, F("wwtype"), F("Typ"))
|
||||
MAKE_PSTR_LIST(wWComfort, F("wwcomfort"), F("Komfort"))
|
||||
MAKE_PSTR_LIST(wWFlowTempOffset, F("wwflowtempoffset"), F("Flusstemperaturanhebung"))
|
||||
MAKE_PSTR_LIST(wWMaxPower, F("wwmaxpower"), F("max Leistung"))
|
||||
MAKE_PSTR_LIST(wWCircPump, F("wwcircpump"), F("Zirkulationspumpe vorhanden"))
|
||||
MAKE_PSTR_LIST(wWChargeType, F("wwchargetype"), F("Ladungstyp"))
|
||||
MAKE_PSTR_LIST(wWDisinfectionTemp, F("wwdisinfectiontemp"), F("Desinfectionstemperatur"))
|
||||
MAKE_PSTR_LIST(wWCircMode, F("wwcircmode"), F("Zirkulationspumpenfrequenz"))
|
||||
MAKE_PSTR_LIST(wWCirc, F("wwcirc"), F("Zirkulation aktiv"))
|
||||
MAKE_PSTR_LIST(wWCurTemp, F("wwcurtemp"), F("aktuelle Warmwasser Temperatur intern"))
|
||||
MAKE_PSTR_LIST(wWCurTemp2, F("wwcurtemp2"), F("aktuelle Warmwaser Temperatur extern"))
|
||||
MAKE_PSTR_LIST(wWCurFlow, F("wwcurflow"), F("aktueller Durchfluss"))
|
||||
MAKE_PSTR_LIST(wWStorageTemp1, F("wwstoragetemp1"), F("interne Speichertemperature"))
|
||||
MAKE_PSTR_LIST(wWStorageTemp2, F("wwstoragetemp2"), F("externer Speichertemperatur"))
|
||||
MAKE_PSTR_LIST(wWActivated, F("wwactivated"), F("aktiviert"))
|
||||
MAKE_PSTR_LIST(wWOneTime, F("wwonetime"), F("Einmalladung"))
|
||||
MAKE_PSTR_LIST(wWDisinfect, F("wwdisinfect"), F("Desinfizieren"))
|
||||
MAKE_PSTR_LIST(wWCharging, F("wwcharging"), F("Laden"))
|
||||
MAKE_PSTR_LIST(wWRecharging, F("wwrecharging"), F("Nachladen"))
|
||||
MAKE_PSTR_LIST(wWTempOK, F("wwtempok"), F("Temperatur ok"))
|
||||
MAKE_PSTR_LIST(wWActive, F("wwactive"), F("aktiv"))
|
||||
MAKE_PSTR_LIST(wWHeat, F("wwheat"), F("heizen"))
|
||||
MAKE_PSTR_LIST(wWSetPumpPower, F("wwsetpumppower"), F("Soll Pumpenleistung"))
|
||||
MAKE_PSTR_LIST(mixerTemp, F("mixertemp"), F("Mischertemperatur"))
|
||||
MAKE_PSTR_LIST(tankMiddleTemp, F("tankmiddletemp"), F("Speicher mittel temperatur (TS3)"))
|
||||
MAKE_PSTR_LIST(wWStarts, F("wwstarts"), F("Anzahl starts"))
|
||||
MAKE_PSTR_LIST(wWStarts2, F("wwstarts2"), F("Kreis 2 Anzahl Starts"))
|
||||
MAKE_PSTR_LIST(wWWorkM, F("wwworkm"), F("aktive Zeit"))
|
||||
MAKE_PSTR_LIST(wWHystOn, F("wwhyston"), F("Hysterese Einschalttemperatur"))
|
||||
MAKE_PSTR_LIST(wWHystOff, F("wwhystoff"), F("Hysterese Ausschalttemperatur"))
|
||||
MAKE_PSTR_LIST(wwStarts2, F("wwstarts2"), F("Starts"))
|
||||
MAKE_PSTR_LIST(wwWorkM, F("wwworkm"), F("aktive Zeit"))
|
||||
MAKE_PSTR_LIST(wwHystOn, F("wwhyston"), F("Hysterese an"))
|
||||
MAKE_PSTR_LIST(wwHystOff, F("wwhystoff"), F("Hysterese aus"))
|
||||
MAKE_PSTR_LIST(wwProgMode, F("wwprogmode"), F("Programmmodus"))
|
||||
MAKE_PSTR_LIST(wwCircProg, F("wwcircprog"), F("Zirkulationsprogramm"))
|
||||
// MAKE_PSTR_LIST(wwDisinfect, F("wwdisinfect"), F("Desinfizieren")) // same as in boiler
|
||||
MAKE_PSTR_LIST(wwDisinfectDay, F("wwdisinfectday"), F("Desinfizierung Tag"))
|
||||
MAKE_PSTR_LIST(wwDisinfectHour, F("wwdisinfecthour"), F("Desinfizierung Stunde"))
|
||||
MAKE_PSTR_LIST(wwMaxTemp, F("wwmaxtemp"), F("Maximale Temperatur"))
|
||||
MAKE_PSTR_LIST(wwOneTimeKey, F("wwonetimekey"), F("Einmalladungstaste"))
|
||||
MAKE_PSTR_LIST(switchtime, F("switchtime"), F("einzelne Programmschaltzeit"))
|
||||
|
||||
// mqtt values / commands
|
||||
MAKE_PSTR_LIST(dateTime, F("datetime"), F("Datum/Zeit"))
|
||||
MAKE_PSTR_LIST(errorCode, F("errorcode"), F("Fehlermeldung"))
|
||||
|
||||
MAKE_PSTR_LIST(ibaMainDisplay, F("display"), F("Anzeige"))
|
||||
MAKE_PSTR_LIST(ibaLanguage, F("language"), F("Sprache"))
|
||||
MAKE_PSTR_LIST(ibaClockOffset, F("clockoffset"), F("Uhrkorrektur"))
|
||||
MAKE_PSTR_LIST(ibaBuildingType, F("building"), F("Gebäude"))
|
||||
MAKE_PSTR_LIST(ibaCalIntTemperature, F("intoffset"), F("Korrektur interner Temperatur"))
|
||||
MAKE_PSTR_LIST(ibaMinExtTemperature, F("minexttemp"), F("min Aussentemperatur"))
|
||||
MAKE_PSTR_LIST(damping, F("damping"), F("Dämpfung der Außentemperatur"))
|
||||
|
||||
MAKE_PSTR_LIST(tempsensor1, F("inttemp1"), F("Temperatursensor 1"))
|
||||
MAKE_PSTR_LIST(tempsensor2, F("inttemp2"), F("Temperatursensor 2"))
|
||||
MAKE_PSTR_LIST(dampedoutdoortemp, F("dampedoutdoortemp"), F("gedämpfte Aussentemperatur"))
|
||||
MAKE_PSTR_LIST(floordrystatus, F("floordry"), F("Estrichtrocknung"))
|
||||
MAKE_PSTR_LIST(floordrytemp, F("floordrytemp"), F("Estrichtrocknungs Temperatur"))
|
||||
MAKE_PSTR_LIST(wwMode, F("wwmode"), F("modus"))
|
||||
MAKE_PSTR_LIST(wwSetTempLow, F("wwsettemplow"), F("untere Solltemperatur"))
|
||||
MAKE_PSTR_LIST(wwChargeDuration, F("wwchargeduration"), F("charge duration"))
|
||||
MAKE_PSTR_LIST(wwExtra1, F("wwextra1"), F("Kreis 1 Extra"))
|
||||
MAKE_PSTR_LIST(wwExtra2, F("wwextra2"), F("Kreis 2 Extra"))
|
||||
MAKE_PSTR_LIST(selRoomTemp, F("seltemp"), F("Sollwert Raumtemperatur"))
|
||||
MAKE_PSTR_LIST(roomTemp, F("currtemp"), F("aktuelle Raumtemperatur"))
|
||||
MAKE_PSTR_LIST(mode, F("mode"), F("modus"))
|
||||
MAKE_PSTR_LIST(modetype, F("modetype"), F("modus Typ"))
|
||||
|
||||
MAKE_PSTR_LIST(daytemp, F("daytemp"), F("Tagestemperatur"))
|
||||
MAKE_PSTR_LIST(heattemp, F("heattemp"), F("Heizen Temperatur"))
|
||||
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("Nachttemperatur"))
|
||||
MAKE_PSTR_LIST(ecotemp, F("ecotemp"), F("eco Temperatur"))
|
||||
MAKE_PSTR_LIST(manualtemp, F("manualtemp"), F("manuelle Temperatur"))
|
||||
MAKE_PSTR_LIST(tempautotemp, F("tempautotemp"), F("zwischenzeitliche Solltemperatur"))
|
||||
MAKE_PSTR_LIST(comforttemp, F("comforttemp"), F("Komforttemperatur"))
|
||||
MAKE_PSTR_LIST(summertemp, F("summertemp"), F("Sommertemperatur"))
|
||||
MAKE_PSTR_LIST(designtemp, F("designtemp"), F("design-Temperatur"))
|
||||
MAKE_PSTR_LIST(offsettemp, F("offsettemp"), F("Temperaturanhebung"))
|
||||
MAKE_PSTR_LIST(minflowtemp, F("minflowtemp"), F("min Flusstemperatur"))
|
||||
MAKE_PSTR_LIST(maxflowtemp, F("maxflowtemp"), F("max Flusstemperatur"))
|
||||
MAKE_PSTR_LIST(roominfluence, F("roominfluence"), F("Raumeinfluss"))
|
||||
MAKE_PSTR_LIST(nofrosttemp, F("nofrosttemp"), F("Frostschutztemperatur"))
|
||||
MAKE_PSTR_LIST(targetflowtemp, F("targetflowtemp"), F("berechnete Flusstemperatur"))
|
||||
MAKE_PSTR_LIST(heatingtype, F("heatingtype"), F("Heizungstyp"))
|
||||
MAKE_PSTR_LIST(summersetmode, F("summersetmode"), F("Einstellung Sommerbetrieb"))
|
||||
MAKE_PSTR_LIST(controlmode, F("controlmode"), F("Kontrollmodus"))
|
||||
MAKE_PSTR_LIST(control, F("control"), F("Fernsteuerung"))
|
||||
MAKE_PSTR_LIST(program, F("program"), F("Programm"))
|
||||
MAKE_PSTR_LIST(pause, F("pause"), F("Pausenzeit"))
|
||||
MAKE_PSTR_LIST(party, F("party"), F("Partyzeit"))
|
||||
MAKE_PSTR_LIST(wwprio, F("wwprio"), F("dhw priority"))
|
||||
|
||||
MAKE_PSTR_LIST(holidaytemp, F("holidaytemp"), F("Urlaubstemperatur"))
|
||||
MAKE_PSTR_LIST(summermode, F("summermode"), F("Sommerbetrieb"))
|
||||
MAKE_PSTR_LIST(holidaymode, F("holidaymode"), F("Urlaubsbetrieb"))
|
||||
MAKE_PSTR_LIST(flowtempoffset, F("flowtempoffset"), F("Flusstemperaturanhebung"))
|
||||
MAKE_PSTR_LIST(reducemode, F("reducemode"), F("Absenkmodus"))
|
||||
MAKE_PSTR_LIST(noreducetemp, F("noreducetemp"), F("Absenkung unterbrechen unter Temperatur"))
|
||||
MAKE_PSTR_LIST(remotetemp, F("remotetemp"), F("Raumtemperatur der Fernsteuerung"))
|
||||
|
||||
// heatpump
|
||||
MAKE_PSTR_LIST(airHumidity, F("airhumidity"), F("relative Luftfeuchte"))
|
||||
MAKE_PSTR_LIST(dewTemperature, F("dewtemperature"), F("Taupunkttemperatur"))
|
||||
|
||||
// mixer
|
||||
MAKE_PSTR_LIST(flowSetTemp, F("flowsettemp"), F("Sollwert Flusstemperatur"))
|
||||
MAKE_PSTR_LIST(flowTempHc, F("flowtemphc"), F("Flusstemperatur des hk (TC1)"))
|
||||
MAKE_PSTR_LIST(pumpStatus, F("pumpstatus"), F("Pumpenstatus des hk (PC1)"))
|
||||
MAKE_PSTR_LIST(mixerStatus, F("valvestatus"), F("Mischerventil Position (VC1)"))
|
||||
MAKE_PSTR_LIST(flowTempVf, F("flowtempvf"), F("Flusstemperatur am Kessel (T0/Vf)"))
|
||||
|
||||
MAKE_PSTR_LIST(wwPumpStatus, F("pumpstatus"), F("Pumpenstatus des wwk (PC1)"))
|
||||
MAKE_PSTR_LIST(wwTempStatus, F("tempstatus"), F("Temperaturschalter des wwk (MC1)"))
|
||||
MAKE_PSTR_LIST(wwTemp, F("wwtemp"), F("aktuelle Temperatur"))
|
||||
|
||||
// solar
|
||||
MAKE_PSTR_LIST(type, F("type"), F("type"))
|
||||
MAKE_PSTR_LIST(collectorTemp, F("collectortemp"), F("Kollektortemperatur (TS1)"))
|
||||
MAKE_PSTR_LIST(tankBottomTemp, F("tankbottomtemp"), F("Speicher Bodentemperatur (TS2)"))
|
||||
MAKE_PSTR_LIST(tank2BottomTemp, F("tank2bottomtemp"), F("2. Speicher Bodentemperatur (TS5)"))
|
||||
MAKE_PSTR_LIST(heatExchangerTemp, F("heatexchangertemp"), F("wärmetauscher Temperatur (TS6)"))
|
||||
|
||||
MAKE_PSTR_LIST(collectorMaxTemp, F("collectormaxtemp"), F("maximale Kollektortemperatur"))
|
||||
MAKE_PSTR_LIST(collectorMinTemp, F("collectormintemp"), F("minimale Kollektortemperatur"))
|
||||
MAKE_PSTR_LIST(tankMaxTemp, F("tankmaxtemp"), F("maximale Speichertemperatur"))
|
||||
MAKE_PSTR_LIST(solarPumpModulation, F("solarpumpmodulation"), F("Pumpenmodulation (PS1)"))
|
||||
MAKE_PSTR_LIST(cylinderPumpModulation, F("cylinderpumpmodulation"), F("Speicherpumpenmodulation (PS5)"))
|
||||
|
||||
MAKE_PSTR_LIST(solarPump, F("solarpump"), F("Pumpe (PS1)"))
|
||||
MAKE_PSTR_LIST(valveStatus, F("valvestatus"), F("ventilstatus"))
|
||||
MAKE_PSTR_LIST(tankHeated, F("tankheated"), F("Speichertemperatur erreicht"))
|
||||
MAKE_PSTR_LIST(collectorShutdown, F("collectorshutdown"), F("Kollektorabschaltung"))
|
||||
|
||||
MAKE_PSTR_LIST(pumpWorkTime, F("pumpworktime"), F("Pumpenlaufzeit"))
|
||||
|
||||
MAKE_PSTR_LIST(energyLastHour, F("energylasthour"), F("Energie letzte Std"))
|
||||
MAKE_PSTR_LIST(energyTotal, F("energytotal"), F("Gesamtenergie"))
|
||||
MAKE_PSTR_LIST(energyToday, F("energytoday"), F("Energie heute"))
|
||||
MAKE_PSTR_LIST(wwTemp1, F("wwtemp1"), F("Temperatur 1"))
|
||||
MAKE_PSTR_LIST(wwTemp3, F("wwtemp3"), F("Temperatur 3"))
|
||||
MAKE_PSTR_LIST(wwTemp4, F("wwtemp4"), F("Temperatur 4"))
|
||||
MAKE_PSTR_LIST(wwTemp5, F("wwtemp5"), F("Temperatur 5"))
|
||||
MAKE_PSTR_LIST(wwTemp7, F("wwtemp7"), F("Temperatur 7"))
|
||||
MAKE_PSTR_LIST(wwPump, F("wwpump"), F("Pumpe"))
|
||||
MAKE_PSTR_LIST(wwMinTemp, F("wwmintemp"), F("minimale Temperatur"))
|
||||
MAKE_PSTR_LIST(pumpMinMod, F("minpumpmod"), F("minimale Pumpenmodulation"))
|
||||
MAKE_PSTR_LIST(maxFlow, F("maxflow"), F("maximaler Durchfluss"))
|
||||
MAKE_PSTR_LIST(solarPower, F("solarpower"), F("aktuelle Leistung"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnonDiff, F("turnondiff"), F("Einschaltdifferenz"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnoffDiff, F("turnoffdiff"), F("Ausschaltdifferenz"))
|
||||
|
||||
//SM100
|
||||
MAKE_PSTR_LIST(heatTransferSystem, F("heattransfersystem"), F("heattransfer system"))
|
||||
MAKE_PSTR_LIST(externalTank, F("externaltank"), F("external tank"))
|
||||
MAKE_PSTR_LIST(thermalDisinfect, F("thermaldisinfect"), F("thermal disinfection"))
|
||||
MAKE_PSTR_LIST(heatMetering, F("heatmetering"), F("heatmetering"))
|
||||
MAKE_PSTR_LIST(solarIsEnabled, F("solarenabled"), F("Solarmodul aktiviert"))
|
||||
|
||||
// telegram 0x035A
|
||||
MAKE_PSTR_LIST(solarPumpMode, F("solarpumpmode"), F("solar pump mode"))
|
||||
MAKE_PSTR_LIST(solarPumpKick, F("pumpkick"), F("pumpkick"))
|
||||
MAKE_PSTR_LIST(plainWaterMode, F("plainwatermode"), F("plain water mode"))
|
||||
MAKE_PSTR_LIST(doubleMatchFlow, F("doublematchflow"), F("doublematchflow"))
|
||||
|
||||
// telegram 0x380
|
||||
MAKE_PSTR_LIST(climateZone, F("climatezone"), F("climate zone"))
|
||||
MAKE_PSTR_LIST(collector1Area, F("collector1area"), F("collector 1 area"))
|
||||
MAKE_PSTR_LIST(collector1Type, F("collector1type"), F("collector 1 type"))
|
||||
|
||||
// switch
|
||||
MAKE_PSTR_LIST(activated, F("activated"), F("aktiviert"))
|
||||
MAKE_PSTR_LIST(status, F("status"), F("Status"))
|
||||
|
||||
MAKE_PSTR_LIST(data11, F("data11"), F("unknown datafield 11"))
|
||||
MAKE_PSTR_LIST(data12, F("data12"), F("unknown datafield 12"))
|
||||
MAKE_PSTR_LIST(data8, F("data8"), F("unknown datafield 8"))
|
||||
MAKE_PSTR_LIST(data0, F("data0"), F("unknown datafield 0"))
|
||||
MAKE_PSTR_LIST(data1, F("data1"), F("unknown datafield 1"))
|
||||
MAKE_PSTR_LIST(setting3, F("setting3"), F("unknown setting 3"))
|
||||
MAKE_PSTR_LIST(setting4, F("setting4"), F("unknown setting 4"))
|
||||
246
src/locale_EN.h
246
src/locale_EN.h
@@ -46,7 +46,7 @@ MAKE_PSTR_WORD(restart)
|
||||
MAKE_PSTR_WORD(format)
|
||||
MAKE_PSTR_WORD(raw)
|
||||
MAKE_PSTR_WORD(watch)
|
||||
MAKE_PSTR_WORD(syslog_level)
|
||||
MAKE_PSTR_WORD(syslog)
|
||||
MAKE_PSTR_WORD(send)
|
||||
MAKE_PSTR_WORD(telegram)
|
||||
MAKE_PSTR_WORD(bus_id)
|
||||
@@ -70,18 +70,22 @@ MAKE_PSTR_WORD(pin)
|
||||
MAKE_PSTR_WORD(publish)
|
||||
MAKE_PSTR_WORD(timeout)
|
||||
MAKE_PSTR_WORD(board_profile)
|
||||
MAKE_PSTR_WORD(counter)
|
||||
MAKE_PSTR_WORD(sensorname)
|
||||
|
||||
// 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(error)
|
||||
MAKE_PSTR_WORD(entities)
|
||||
@@ -97,9 +101,9 @@ MAKE_PSTR_WORD(controller)
|
||||
MAKE_PSTR_WORD(connect)
|
||||
MAKE_PSTR_WORD(heatpump)
|
||||
MAKE_PSTR_WORD(generic)
|
||||
MAKE_PSTR_WORD(dallassensor)
|
||||
MAKE_PSTR_WORD(analogsensor)
|
||||
MAKE_PSTR_WORD(unknown)
|
||||
MAKE_PSTR_WORD(Dallassensor)
|
||||
MAKE_PSTR_WORD(dallassensor)
|
||||
|
||||
// format strings
|
||||
MAKE_PSTR(master_thermostat_fmt, "Master Thermostat device ID: %s")
|
||||
@@ -110,16 +114,18 @@ 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(ethernet_option_fmt, "Ethernet option: %d")
|
||||
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_STR(productid_fmt, "%s EMS Product ID")
|
||||
MAKE_STR(productid_fmt, "%s EMS ProductID")
|
||||
|
||||
MAKE_PSTR_LIST(enum_syslog_level, F_(off), F("emerg"), F("alert"), F("crit"), F_(error), F("warn"), F("notice"), F_(info), F_(debug), F("trace"), F("all"))
|
||||
MAKE_PSTR_LIST(enum_watch, F_(off), F_(on), F_(raw), F_(unknown))
|
||||
MAKE_PSTR_LIST(enum_sensortype, F("none"), F("digital in"), F("counter"), F("adc"))
|
||||
|
||||
// strings
|
||||
MAKE_PSTR(show_optional, "[devices | users | ems | mqtt | system | commands]")
|
||||
MAKE_PSTR(EMSESP, "EMS-ESP")
|
||||
MAKE_PSTR(cmd_optional, "[cmd]")
|
||||
MAKE_PSTR(ha_optional, "[ha]")
|
||||
@@ -134,6 +140,7 @@ 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]")
|
||||
@@ -156,19 +163,23 @@ MAKE_PSTR_WORD(enum)
|
||||
MAKE_PSTR_WORD(text)
|
||||
|
||||
MAKE_PSTR_WORD(2)
|
||||
MAKE_PSTR_WORD(4)
|
||||
MAKE_PSTR_WORD(10)
|
||||
MAKE_PSTR_WORD(100)
|
||||
MAKE_PSTR_WORD(60)
|
||||
|
||||
MAKE_PSTR_LIST(div2, F_(2))
|
||||
MAKE_PSTR_LIST(div4, F_(4))
|
||||
MAKE_PSTR_LIST(div10, F_(10))
|
||||
MAKE_PSTR_LIST(div100, F_(100))
|
||||
MAKE_PSTR_LIST(div60, F_(60))
|
||||
MAKE_PSTR_LIST(mul10, F("*10"))
|
||||
MAKE_PSTR_LIST(mul15, F("*15"))
|
||||
MAKE_PSTR_LIST(div100, F_(100))
|
||||
MAKE_PSTR_LIST(mul5, F("-5"))
|
||||
MAKE_PSTR_LIST(mul10, F("-10"))
|
||||
MAKE_PSTR_LIST(mul15, F("-15"))
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// uom - also used with HA see https://github.com/home-assistant/core/blob/d7ac4bd65379e11461c7ce0893d3533d8d8b8cbf/homeassistant/const.py#L384
|
||||
MAKE_PSTR(blank, " ")
|
||||
MAKE_PSTR(percent, "%")
|
||||
MAKE_PSTR(degrees, "°C")
|
||||
MAKE_PSTR(kwh, "kWh")
|
||||
@@ -176,6 +187,7 @@ MAKE_PSTR(wh, "Wh")
|
||||
MAKE_PSTR(bar, "bar")
|
||||
MAKE_PSTR(minutes, "minutes")
|
||||
MAKE_PSTR(hours, "hours")
|
||||
MAKE_PSTR(days, "days")
|
||||
MAKE_PSTR(ua, "uA")
|
||||
MAKE_PSTR(lmin, "l/min")
|
||||
MAKE_PSTR(kw, "kW")
|
||||
@@ -183,24 +195,28 @@ MAKE_PSTR(w, "W")
|
||||
MAKE_PSTR(kb, "KB")
|
||||
MAKE_PSTR(seconds, "seconds")
|
||||
MAKE_PSTR(dbm, "dBm")
|
||||
MAKE_PSTR(fahrenheit, "°F")
|
||||
MAKE_PSTR(mv, "mV")
|
||||
MAKE_PSTR(sqm, "sqm")
|
||||
MAKE_PSTR(times, "times")
|
||||
MAKE_PSTR(oclock, "o'clock")
|
||||
|
||||
MAKE_PSTR(days, "days")
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevice.cpp
|
||||
// use empty string if want to suppress showing tags
|
||||
// tags must not have spaces
|
||||
// mqtt tags must not have spaces
|
||||
MAKE_PSTR(tag_none, "")
|
||||
MAKE_PSTR(tag_heartbeat, "")
|
||||
MAKE_PSTR(tag_boiler_data, "")
|
||||
MAKE_PSTR(tag_device_data_ww, "ww")
|
||||
MAKE_PSTR(tag_device_data_ww, "dhw")
|
||||
MAKE_PSTR(tag_thermostat_data, "")
|
||||
MAKE_PSTR(tag_hc1, "hc1")
|
||||
MAKE_PSTR(tag_hc2, "hc2")
|
||||
MAKE_PSTR(tag_hc3, "hc3")
|
||||
MAKE_PSTR(tag_hc4, "hc4")
|
||||
MAKE_PSTR(tag_hc5, "hc5")
|
||||
MAKE_PSTR(tag_hc6, "hc6")
|
||||
MAKE_PSTR(tag_hc7, "hc7")
|
||||
MAKE_PSTR(tag_hc8, "hc8")
|
||||
MAKE_PSTR(tag_wwc1, "wwc1")
|
||||
MAKE_PSTR(tag_wwc2, "wwc2")
|
||||
MAKE_PSTR(tag_wwc3, "wwc3")
|
||||
@@ -223,6 +239,7 @@ MAKE_PSTR(tag_hs15, "hs15")
|
||||
MAKE_PSTR(tag_hs16, "hs16")
|
||||
|
||||
// MQTT topic names
|
||||
// MAKE_PSTR(tag_heartbeat_mqtt, "heartbeat")
|
||||
MAKE_PSTR(tag_boiler_data_mqtt, "")
|
||||
MAKE_PSTR(tag_device_data_ww_mqtt, "ww")
|
||||
|
||||
@@ -242,22 +259,24 @@ MAKE_PSTR_WORD(hot)
|
||||
MAKE_PSTR_WORD(eco)
|
||||
MAKE_PSTR_WORD(intelligent)
|
||||
MAKE_PSTR_WORD(flow)
|
||||
MAKE_PSTR_WORD(manual)
|
||||
MAKE_PSTR_WORD(buffer)
|
||||
MAKE_PSTR(bufferedflow, "buffered flow")
|
||||
MAKE_PSTR(layeredbuffer, "layered buffer")
|
||||
MAKE_PSTR_WORD(maintenance)
|
||||
|
||||
// boiler lists
|
||||
MAKE_PSTR_LIST(enum_off_time_date, F_(off), F_(time), F_(date))
|
||||
MAKE_PSTR_LIST(tpl_date, F("Format: < dd.mm.yyyy >")) // template for text input
|
||||
MAKE_PSTR_LIST(enum_off_time_date_manual, F_(off), F_(time), F_(date), F_(manual))
|
||||
MAKE_PSTR_LIST(enum_freq, F_(off), F_(1x3min), F_(2x3min), F_(3x3min), F_(4x3min), F_(5x3min), F_(6x3min), F_(continuous))
|
||||
MAKE_PSTR_LIST(enum_charge, F_(chargepump), F_(3wayvalve))
|
||||
MAKE_PSTR_LIST(enum_comfort, F_(hot), F_(eco), F_(intelligent))
|
||||
MAKE_PSTR_LIST(enum_flow, F_(off), F_(flow), F_(bufferedflow), F_(buffer), F_(layeredbuffer))
|
||||
MAKE_PSTR_LIST(enum_reset, F_(maintenance), F_(error))
|
||||
MAKE_PSTR_LIST(enum_bool, F_(off), F_(on))
|
||||
MAKE_PSTR_LIST(enum_reset, F("-"), F_(maintenance), F_(error))
|
||||
// MAKE_PSTR_LIST(enum_bool, F_(off), F_(on))
|
||||
|
||||
//heatpump
|
||||
MAKE_PSTR_LIST(enum_hpactivity, F("none"), F("heating"), F("cooling"), F("warm water"), F("pool"))
|
||||
MAKE_PSTR_LIST(enum_hpactivity, F("none"), F("heating"), F("cooling"), F("hot water"), F("pool"))
|
||||
|
||||
// mixer
|
||||
MAKE_PSTR_LIST(enum_shunt, F("stopped"), F("opening"), F("closing"), F("open"), F("close"))
|
||||
@@ -292,7 +311,6 @@ MAKE_PSTR_WORD(simple)
|
||||
MAKE_PSTR_WORD(optimized)
|
||||
MAKE_PSTR_WORD(nofrost)
|
||||
MAKE_PSTR_WORD(comfort)
|
||||
MAKE_PSTR_WORD(manual)
|
||||
MAKE_PSTR_WORD(night)
|
||||
MAKE_PSTR_WORD(day)
|
||||
MAKE_PSTR_WORD(holiday)
|
||||
@@ -315,6 +333,10 @@ MAKE_PSTR(functioning_mode, "functioning mode")
|
||||
MAKE_PSTR(smoke_temperature, "smoke temperature")
|
||||
|
||||
// thermostat lists
|
||||
MAKE_PSTR_LIST(tpl_datetime, F("Format: < NTP | hh:mm:ss dd.mm.yyyy-dw-dst >"))
|
||||
// MAKE_PSTR_LIST(tpl_switchtime, F("Format: < p:nn.d.o.hh:mm > prog, no, day, on, time"))
|
||||
MAKE_PSTR_LIST(tpl_switchtime, F("Format: <nn> [ not_set | day hh:mm on|off ]"))
|
||||
MAKE_PSTR_LIST(tpl_holidays, F("format: < dd.mm.yyyy-dd.mm.yyyy >"))
|
||||
MAKE_PSTR_LIST(enum_ibaMainDisplay,
|
||||
F_(internal_temperature),
|
||||
F_(internal_setpoint),
|
||||
@@ -335,15 +357,14 @@ MAKE_PSTR_LIST(enum_wwMode2, F_(off), F_(on), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_wwMode3, F_(on), F_(off), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_heatingtype, F_(off), F_(radiator), F_(convector), F_(floor))
|
||||
MAKE_PSTR_LIST(enum_summermode, F_(summer), F_(auto), F_(winter))
|
||||
MAKE_PSTR_LIST(enum_summer, F_(winter), F_(summer))
|
||||
|
||||
MAKE_PSTR_LIST(enum_mode, F_(manual), F_(auto)) // RC100, RC300, RC310
|
||||
MAKE_PSTR_LIST(enum_mode2, F_(off), F_(manual), F_(auto)) // RC20
|
||||
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto)) // RC35, RC30
|
||||
MAKE_PSTR_LIST(enum_mode3, F_(night), F_(day), F_(auto)) // RC35, RC30, RC25
|
||||
MAKE_PSTR_LIST(enum_mode4, F_(nofrost), F_(eco), F_(heat), F_(auto)) // JUNKERS
|
||||
MAKE_PSTR_LIST(enum_mode5, F_(auto), F_(off)) // CRF
|
||||
MAKE_PSTR_LIST(enum_mode6, F_(off), F_(night), F_(day)) // RC10
|
||||
|
||||
MAKE_PSTR_LIST(enum_hamode, F_(off), F_(heat), F_(auto), F_(heat), F_(off), F_(heat), F_(auto), F_(auto), F_(auto), F_(auto))
|
||||
MAKE_PSTR_LIST(enum_mode6, F_(nofrost), F_(night), F_(day)) // RC10
|
||||
|
||||
MAKE_PSTR_LIST(enum_modetype, F_(eco), F_(comfort))
|
||||
MAKE_PSTR_LIST(enum_modetype2, F_(day))
|
||||
@@ -366,20 +387,20 @@ MAKE_PSTR_LIST(enum_progMode2, F("own_1"), F("family"), F("morning"), F("evening
|
||||
MAKE_PSTR_LIST(enum_progMode3, F("family"), F("morning"), F("evening"), F("am"), F("pm"), F("midday"), F("singles"), F("seniors"))
|
||||
MAKE_PSTR_LIST(enum_progMode4, F("prog_a"), F("prog_b"), F("prog_c"), F("prog_d"), F("prog_e"), F("prog_f"))
|
||||
|
||||
MAKE_PSTR_LIST(enum_switchmode, F_(off), F_(eco), F_(comfort), F_(heat))
|
||||
|
||||
// solar list
|
||||
MAKE_PSTR_LIST(enum_solarmode, F_(constant), F("pwm"), F("analog"))
|
||||
MAKE_PSTR_LIST(enum_collectortype, F("flat"), F("vacuum"))
|
||||
|
||||
// MQTT topic for homeassistant. Must include /
|
||||
MAKE_PSTR(homeassistant, "homeassistant/")
|
||||
MAKE_PSTR_LIST(enum_cylprio, F("cyl_1"), F("cyl_2"))
|
||||
|
||||
// id used to store the device ID. empty full name so only gets displayed in the MQTT payload
|
||||
MAKE_PSTR_LIST(ID, F_(id))
|
||||
|
||||
// Boiler
|
||||
// extra commands, with no json output
|
||||
MAKE_PSTR_LIST(wwtapactivated, F("wwtapactivated"), F("turn on/off DHW by going into maintenance mode"))
|
||||
MAKE_PSTR_LIST(reset, F("reset"), F("reset messages"))
|
||||
MAKE_PSTR_LIST(wwtapactivated, F("wwtapactivated"), F("turn on/off"))
|
||||
MAKE_PSTR_LIST(reset, F("reset"), F("reset"))
|
||||
|
||||
// single mqtt topics
|
||||
MAKE_PSTR_WORD(heating_active)
|
||||
@@ -388,7 +409,7 @@ MAKE_PSTR_WORD(response)
|
||||
|
||||
// mqtt, commands and text
|
||||
MAKE_PSTR_LIST(heatingActive, F("heatingactive"), F("heating active"))
|
||||
MAKE_PSTR_LIST(tapwaterActive, F("tapwateractive"), F("warm water active"))
|
||||
MAKE_PSTR_LIST(tapwaterActive, F("tapwateractive"), F("tapwater active"))
|
||||
MAKE_PSTR_LIST(selFlowTemp, F("selflowtemp"), F("selected flow temperature"))
|
||||
MAKE_PSTR_LIST(selBurnPow, F("selburnpow"), F("burner selected max power"))
|
||||
MAKE_PSTR_LIST(heatingPumpMod, F("heatingpumpmod"), F("heating pump modulation"))
|
||||
@@ -401,10 +422,12 @@ MAKE_PSTR_LIST(sysPress, F("syspress"), F("system pressure"))
|
||||
MAKE_PSTR_LIST(boilTemp, F("boiltemp"), F("actual boiler temperature"))
|
||||
MAKE_PSTR_LIST(exhaustTemp, F("exhausttemp"), F("exhaust temperature"))
|
||||
MAKE_PSTR_LIST(burnGas, F("burngas"), F("gas"))
|
||||
MAKE_PSTR_LIST(burnGas2, F("burngas2"), F("gas stage 2"))
|
||||
MAKE_PSTR_LIST(flameCurr, F("flamecurr"), F("flame current"))
|
||||
MAKE_PSTR_LIST(heatingPump, F("heatingpump"), F("heating pump"))
|
||||
MAKE_PSTR_LIST(fanWork, F("fanwork"), F("fan"))
|
||||
MAKE_PSTR_LIST(ignWork, F("ignwork"), F("ignition"))
|
||||
MAKE_PSTR_LIST(oilPreHeat, F("oilpreheat"), F("oil preheating"))
|
||||
MAKE_PSTR_LIST(heatingActivated, F("heatingactivated"), F("heating activated"))
|
||||
MAKE_PSTR_LIST(heatingTemp, F("heatingtemp"), F("heating temperature"))
|
||||
MAKE_PSTR_LIST(pumpModMax, F("pumpmodmax"), F("burner pump max power"))
|
||||
@@ -426,40 +449,41 @@ MAKE_PSTR_LIST(lastCode, F("lastcode"), F("last error code"))
|
||||
MAKE_PSTR_LIST(serviceCode, F("servicecode"), F("service code"))
|
||||
MAKE_PSTR_LIST(serviceCodeNumber, F("servicecodenumber"), F("service code number"))
|
||||
MAKE_PSTR_LIST(maintenanceMessage, F("maintenancemessage"), F("maintenance message"))
|
||||
MAKE_PSTR_LIST(maintenanceDate, F("maintenancedate"), F("maintenance set date"))
|
||||
MAKE_PSTR_LIST(maintenanceDate, F("maintenancedate"), F("next maintenance date"))
|
||||
MAKE_PSTR_LIST(maintenanceType, F_(maintenance), F("maintenance scheduled"))
|
||||
MAKE_PSTR_LIST(maintenanceTime, F("maintenancetime"), F("maintenance set time"))
|
||||
MAKE_PSTR_LIST(maintenanceTime, F("maintenancetime"), F("time to next maintenance"))
|
||||
|
||||
// heatpump/compress specific
|
||||
MAKE_PSTR_LIST(upTimeControl, F("uptimecontrol"), F("operating time total heat"))
|
||||
MAKE_PSTR_LIST(upTimeCompHeating, F("uptimecompheating"), F("operating time compressor heating"))
|
||||
MAKE_PSTR_LIST(upTimeCompCooling, F("uptimecompcooling"), F("operating time compressor cooling"))
|
||||
MAKE_PSTR_LIST(upTimeCompWw, F("uptimecompww"), F("operating time compressor warm water"))
|
||||
MAKE_PSTR_LIST(upTimeCompWw, F("uptimecompww"), F("operating time compressor dhw"))
|
||||
MAKE_PSTR_LIST(upTimeCompPool, F("uptimecomppool"), F("operating time compressor pool"))
|
||||
MAKE_PSTR_LIST(totalcompStarts, F("totalcompstarts"), F("total compressor control starts"))
|
||||
MAKE_PSTR_LIST(totalCompStarts, F("totalcompstarts"), F("total compressor control starts"))
|
||||
MAKE_PSTR_LIST(heatingStarts, F("heatingstarts"), F("heating control starts"))
|
||||
MAKE_PSTR_LIST(coolingStarts, F("coolingstarts"), F("cooling control starts"))
|
||||
MAKE_PSTR_LIST(wwStarts2, F("wwstarts2"), F("warm water control starts"))
|
||||
MAKE_PSTR_LIST(poolStarts, F("poolstarts"), F("pool control starts"))
|
||||
MAKE_PSTR_LIST(nrgConsTotal, F("nrgconstotal"), F("total energy consumption"))
|
||||
MAKE_PSTR_LIST(nrgConsCompTotal, F("nrgconscomptotal"), F("energy consumption compressor total"))
|
||||
MAKE_PSTR_LIST(nrgConsCompHeating, F("nrgconscompheating"), F("energy consumption compressor heating"))
|
||||
MAKE_PSTR_LIST(nrgConsCompWw, F("nrgconscompww"), F("energy consumption compressor warm water"))
|
||||
MAKE_PSTR_LIST(nrgConsCompWw, F("nrgconscompww"), F("energy consumption compressor dhw"))
|
||||
MAKE_PSTR_LIST(nrgConsCompCooling, F("nrgconscompcooling"), F("energy consumption compressor cooling"))
|
||||
MAKE_PSTR_LIST(nrgConsCompPool, F("nrgconscomppool"), F("energy consumption compressor pool"))
|
||||
MAKE_PSTR_LIST(nrgSuppTotal, F("nrgsupptotal"), F("total energy supplied"))
|
||||
MAKE_PSTR_LIST(nrgSuppHeating, F("nrgsuppheating"), F("total energy supplied heating"))
|
||||
MAKE_PSTR_LIST(nrgSuppWw, F("nrgsuppww"), F("total energy warm supplied warm water"))
|
||||
MAKE_PSTR_LIST(nrgSuppWw, F("nrgsuppww"), F("total energy warm supplied dhw"))
|
||||
MAKE_PSTR_LIST(nrgSuppCooling, F("nrgsuppcooling"), F("total energy supplied cooling"))
|
||||
MAKE_PSTR_LIST(nrgSuppPool, F("nrgsupppool"), F("total energy supplied pool"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsTotal, F("auxelecheatnrgconstotal"), F("auxiliary electrical heater energy consumption total"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsHeating, F("auxelecheatnrgconsheating"), F("auxiliary electrical heater energy consumption heating"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsWW, F("auxelecheatnrgconsww"), F("auxiliary electrical heater energy consumption warm water"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsWW, F("auxelecheatnrgconsww"), F("auxiliary electrical heater energy consumption dhw"))
|
||||
MAKE_PSTR_LIST(auxElecHeatNrgConsPool, F("auxelecheatnrgconspool"), F("auxiliary electrical heater energy consumption pool"))
|
||||
|
||||
MAKE_PSTR_LIST(hpPower, F("hppower"), F("Compressor power output"))
|
||||
MAKE_PSTR_LIST(hpCompOn, F("hpcompon"), F("HP Compressor"))
|
||||
MAKE_PSTR_LIST(hpHeatingOn, F("hpheatingon"), F("HP Heating"))
|
||||
MAKE_PSTR_LIST(hpCoolingOn, F("hpcoolingon"), F("HP Cooling"))
|
||||
MAKE_PSTR_LIST(hpWwOn, F("hpwwon"), F("HP Warm water"))
|
||||
MAKE_PSTR_LIST(hpWwOn, F("hpwwon"), F("HP dhw"))
|
||||
MAKE_PSTR_LIST(hpPoolOn, F("hppoolon"), F("HP Pool"))
|
||||
MAKE_PSTR_LIST(hpBrinePumpSpd, F("hpbrinepumpspd"), F("Brine Pump Speed"))
|
||||
MAKE_PSTR_LIST(hpCompSpd, F("hpcompspd"), F("Compressor Speed"))
|
||||
@@ -481,12 +505,8 @@ MAKE_PSTR_LIST(hpTr7, F("hptr7"), F("refrigerant temperature gas side (condenser
|
||||
MAKE_PSTR_LIST(hpTl2, F("hptl2"), F("air inlet temperature (TL2)"))
|
||||
MAKE_PSTR_LIST(hpPl1, F("hppl1"), F("low pressure side temperature (PL1)"))
|
||||
MAKE_PSTR_LIST(hpPh1, F("hpph1"), F("high pressure side temperature (PH1)"))
|
||||
MAKE_PSTR_LIST(poolSetTemp, F("poolsettemp"), F("pool set temperature"))
|
||||
MAKE_PSTR_LIST(poolTemp, F("pooltemp"), F("pool temperature"))
|
||||
MAKE_PSTR_LIST(poolShuntStatus, F("poolshuntstatus"), F("pool shunt status opening/closing"))
|
||||
MAKE_PSTR_LIST(poolShunt, F("poolshunt"), F("pool shunt open/close (0% = pool / 100% = heat)"))
|
||||
|
||||
// the following are warm water for the boiler and automatically tagged with 'ww'
|
||||
// the following are dhw for the boiler and automatically tagged with 'ww'
|
||||
MAKE_PSTR_LIST(wwSelTemp, F("wwseltemp"), F("selected temperature"))
|
||||
MAKE_PSTR_LIST(wwSelTempLow, F("wwseltemplow"), F("selected lower temperature"))
|
||||
MAKE_PSTR_LIST(wwSelTempOff, F("wwseltempoff"), F("selected temperature for off"))
|
||||
@@ -499,7 +519,7 @@ MAKE_PSTR_LIST(wwMaxPower, F("wwmaxpower"), F("max power"))
|
||||
MAKE_PSTR_LIST(wwCircPump, F("wwcircpump"), F("circulation pump available"))
|
||||
MAKE_PSTR_LIST(wwChargeType, F("wwchargetype"), F("charging type"))
|
||||
MAKE_PSTR_LIST(wwDisinfectionTemp, F("wwdisinfectiontemp"), F("disinfection temperature"))
|
||||
MAKE_PSTR_LIST(wwCircMode, F("wwcircmode"), F("circulation pump frequency"))
|
||||
MAKE_PSTR_LIST(wwCircMode, F("wwcircmode"), F("circulation pump mode")) // also used in thermostat
|
||||
MAKE_PSTR_LIST(wwCirc, F("wwcirc"), F("circulation active"))
|
||||
MAKE_PSTR_LIST(wwCurTemp, F("wwcurtemp"), F("current intern temperature"))
|
||||
MAKE_PSTR_LIST(wwCurTemp2, F("wwcurtemp2"), F("current extern temperature"))
|
||||
@@ -508,61 +528,62 @@ MAKE_PSTR_LIST(wwStorageTemp1, F("wwstoragetemp1"), F("storage intern temperatur
|
||||
MAKE_PSTR_LIST(wwStorageTemp2, F("wwstoragetemp2"), F("storage extern temperature"))
|
||||
MAKE_PSTR_LIST(wwActivated, F("wwactivated"), F("activated"))
|
||||
MAKE_PSTR_LIST(wwOneTime, F("wwonetime"), F("one time charging"))
|
||||
MAKE_PSTR_LIST(wwDisinfect, F("wwdisinfect"), F("disinfection"))
|
||||
MAKE_PSTR_LIST(wwDisinfecting, F("wwdisinfecting"), F("disinfecting"))
|
||||
MAKE_PSTR_LIST(wwCharging, F("wwcharging"), F("charging"))
|
||||
MAKE_PSTR_LIST(wwRecharging, F("wwrecharging"), F("recharging"))
|
||||
MAKE_PSTR_LIST(wwTempOK, F("wwtempok"), F("temperature ok"))
|
||||
MAKE_PSTR_LIST(wwActive, F("wwactive"), F("active"))
|
||||
MAKE_PSTR_LIST(wwHeat, F("wwheat"), F("heating"))
|
||||
MAKE_PSTR_LIST(ww3wayValve, F("ww3wayvalve"), F("3way valve active"))
|
||||
MAKE_PSTR_LIST(wwSetPumpPower, F("wwsetpumppower"), F("set pump power"))
|
||||
MAKE_PSTR_LIST(wwMixerTemp, F("wwmixertemp"), F("mixer temperature"))
|
||||
MAKE_PSTR_LIST(wwTankMiddleTemp, F("wwtankmiddletemp"), F("tank middle temperature (TS3)"))
|
||||
MAKE_PSTR_LIST(wwCylMiddleTemp, F("wwcylmiddletemp"), F("cylinder middle temperature (TS3)"))
|
||||
MAKE_PSTR_LIST(wwStarts, F("wwstarts"), F("starts"))
|
||||
MAKE_PSTR_LIST(wwStarts2, F("wwstarts2"), F("control starts"))
|
||||
MAKE_PSTR_LIST(wwWorkM, F("wwworkm"), F("active time"))
|
||||
MAKE_PSTR_LIST(wwHystOn, F("wwhyston"), F("hysteresis on temperature"))
|
||||
MAKE_PSTR_LIST(wwHystOff, F("wwhystoff"), F("hysteresis off temperature"))
|
||||
MAKE_PSTR_LIST(wwProgMode, F("wwprogmode"), F("program mode"))
|
||||
MAKE_PSTR_LIST(wwCircProg, F("wwcircprog"), F("circulation program mode"))
|
||||
// MAKE_PSTR_LIST(wwDisinfect, F("wwdisinfect"), F("disinfection")) // same as in boiler
|
||||
MAKE_PSTR_LIST(wwDisinfectDay, F("wwdisinfectday"), F("disinfection day"))
|
||||
MAKE_PSTR_LIST(wwDisinfectHour, F("wwdisinfecthour"), F("disinfection hour"))
|
||||
MAKE_PSTR_LIST(wwDisinfectTime, F("wwdisinfecttime"), F("disinfection time"))
|
||||
MAKE_PSTR_LIST(wwProgMode, F("wwprogmode"), F("program"))
|
||||
MAKE_PSTR_LIST(wwCircProg, F("wwcircprog"), F("circulation program"))
|
||||
MAKE_PSTR_LIST(wwMaxTemp, F("wwmaxtemp"), F("maximum temperature"))
|
||||
MAKE_PSTR_LIST(wwOneTimeKey, F("wwonetimekey"), F("one time key function"))
|
||||
|
||||
// thermostat
|
||||
// commands, with no long name so they only appear in the MQTT payloads
|
||||
MAKE_PSTR_LIST(temp, F("temp"))
|
||||
MAKE_PSTR_LIST(hatemp, F("hatemp"))
|
||||
MAKE_PSTR_LIST(hamode, F("hamode"))
|
||||
|
||||
// mqtt values / commands
|
||||
MAKE_PSTR_LIST(switchtime, F("switchtime"), F("single program switchtime"))
|
||||
MAKE_PSTR_LIST(switchtime1, F("switchtime1"), F("own1 program switchtime"))
|
||||
MAKE_PSTR_LIST(switchtime2, F("switchtime2"), F("own2 program switchtime"))
|
||||
MAKE_PSTR_LIST(wwswitchtime, F("wwswitchtime"), F("program switchtime"))
|
||||
MAKE_PSTR_LIST(wwcircswitchtime, F("wwcircswitchtime"), F("circulation program switchtime"))
|
||||
MAKE_PSTR_LIST(dateTime, F("datetime"), F("date/time"))
|
||||
MAKE_PSTR_LIST(errorCode, F("errorcode"), F("error code"))
|
||||
MAKE_PSTR_LIST(ibaMainDisplay, F("display"), F("display"))
|
||||
MAKE_PSTR_LIST(ibaLanguage, F("language"), F("language"))
|
||||
MAKE_PSTR_LIST(ibaClockOffset, F("clockoffset"), F("clock offset"))
|
||||
MAKE_PSTR_LIST(ibaBuildingType, F("building"), F("building"))
|
||||
MAKE_PSTR_LIST(ibaBuildingType, F("building"), F("building type"))
|
||||
MAKE_PSTR_LIST(heatingPID, F("heatingpid"), F("heating PID"))
|
||||
MAKE_PSTR_LIST(ibaCalIntTemperature, F("intoffset"), F("offset internal temperature"))
|
||||
MAKE_PSTR_LIST(ibaCalIntTemperature, F("intoffset"), F("internal temperature offset"))
|
||||
MAKE_PSTR_LIST(ibaMinExtTemperature, F("minexttemp"), F("minimal external temperature"))
|
||||
MAKE_PSTR_LIST(backlight, F("backlight"), F("key backlight"))
|
||||
MAKE_PSTR_LIST(damping, F("damping"), F("damping outdoor temperature"))
|
||||
|
||||
MAKE_PSTR_LIST(tempsensor1, F("inttemp1"), F("temperature sensor 1"))
|
||||
MAKE_PSTR_LIST(tempsensor2, F("inttemp2"), F("temperature sensor 2"))
|
||||
MAKE_PSTR_LIST(dampedoutdoortemp, F("dampedoutdoortemp"), F("damped outdoor temperature"))
|
||||
MAKE_PSTR_LIST(floordrystatus, F("floordry"), F("floor drying"))
|
||||
MAKE_PSTR_LIST(floordrytemp, F("floordrytemp"), F("floor drying temperature"))
|
||||
// thermostat ww
|
||||
MAKE_PSTR_LIST(wwMode, F("wwmode"), F("mode"))
|
||||
MAKE_PSTR_LIST(wwSetTempLow, F("wwsettemplow"), F("set low temperature"))
|
||||
MAKE_PSTR_LIST(wwCharge, F("wwcharge"), F("charge"))
|
||||
MAKE_PSTR_LIST(wwChargeDuration, F("wwchargeduration"), F("charge duration"))
|
||||
MAKE_PSTR_LIST(wwDisinfect, F("wwdisinfect"), F("disinfection"))
|
||||
MAKE_PSTR_LIST(wwDisinfectDay, F("wwdisinfectday"), F("disinfection day"))
|
||||
MAKE_PSTR_LIST(wwDisinfectHour, F("wwdisinfecthour"), F("disinfection hour"))
|
||||
MAKE_PSTR_LIST(wwDisinfectTime, F("wwdisinfecttime"), F("disinfection time"))
|
||||
MAKE_PSTR_LIST(wwExtra1, F("wwextra1"), F("circuit 1 extra"))
|
||||
MAKE_PSTR_LIST(wwExtra2, F("wwextra2"), F("circuit 2 extra"))
|
||||
MAKE_PSTR_LIST(setpoint_roomTemp, F("seltemp"), F("selected room temperature"))
|
||||
MAKE_PSTR_LIST(curr_roomTemp, F("currtemp"), F("current room temperature"))
|
||||
MAKE_PSTR_LIST(wwDailyHeating, F("wwdailyheating"), F("daily heating"))
|
||||
MAKE_PSTR_LIST(wwDailyHeatTime, F("wwdailyheattime"), F("daily heating time"))
|
||||
// thermostat hc
|
||||
MAKE_PSTR_LIST(selRoomTemp, F("seltemp"), F("selected room temperature"))
|
||||
MAKE_PSTR_LIST(roomTemp, F("currtemp"), F("current room temperature"))
|
||||
MAKE_PSTR_LIST(mode, F("mode"), F("mode"))
|
||||
MAKE_PSTR_LIST(modetype, F("modetype"), F("mode type"))
|
||||
MAKE_PSTR_LIST(fastheatup, F("fastheatup"), F("fast heatup"))
|
||||
@@ -571,7 +592,7 @@ MAKE_PSTR_LIST(heattemp, F("heattemp"), F("heat temperature"))
|
||||
MAKE_PSTR_LIST(nighttemp, F("nighttemp"), F("night temperature"))
|
||||
MAKE_PSTR_LIST(ecotemp, F("ecotemp"), F("eco temperature"))
|
||||
MAKE_PSTR_LIST(manualtemp, F("manualtemp"), F("manual temperature"))
|
||||
MAKE_PSTR_LIST(tempautotemp, F("tempautotemp"), F("temporary room temperature automode"))
|
||||
MAKE_PSTR_LIST(tempautotemp, F("tempautotemp"), F("temporary set temperature automode"))
|
||||
MAKE_PSTR_LIST(comforttemp, F("comforttemp"), F("comfort temperature"))
|
||||
MAKE_PSTR_LIST(summertemp, F("summertemp"), F("summer temperature"))
|
||||
MAKE_PSTR_LIST(designtemp, F("designtemp"), F("design temperature"))
|
||||
@@ -591,12 +612,11 @@ MAKE_PSTR_LIST(vacations, F("vacations"), F("vacation dates"))
|
||||
MAKE_PSTR_LIST(program, F("program"), F("program"))
|
||||
MAKE_PSTR_LIST(pause, F("pause"), F("pause time"))
|
||||
MAKE_PSTR_LIST(party, F("party"), F("party time"))
|
||||
MAKE_PSTR_LIST(wwprio, F("wwprio"), F("warm water priority"))
|
||||
|
||||
MAKE_PSTR_LIST(wwprio, F("wwprio"), F("dhw priority"))
|
||||
MAKE_PSTR_LIST(holidaytemp, F("holidaytemp"), F("holiday temperature"))
|
||||
MAKE_PSTR_LIST(summermode, F("summermode"), F("summer mode"))
|
||||
MAKE_PSTR_LIST(holidaymode, F("holidaymode"), F("holiday mode"))
|
||||
MAKE_PSTR_LIST(flowtempoffset, F("flowtempoffset"), F("flow temperature offset"))
|
||||
MAKE_PSTR_LIST(flowtempoffset, F("flowtempoffset"), F("flow temperature offset for mixer"))
|
||||
MAKE_PSTR_LIST(reducemode, F("reducemode"), F("reduce mode"))
|
||||
MAKE_PSTR_LIST(noreducetemp, F("noreducetemp"), F("no reduce below temperature"))
|
||||
MAKE_PSTR_LIST(remotetemp, F("remotetemp"), F("room temperature from remote"))
|
||||
@@ -609,67 +629,123 @@ MAKE_PSTR_LIST(dewTemperature, F("dewtemperature"), F("dew point temperature"))
|
||||
|
||||
// mixer
|
||||
MAKE_PSTR_LIST(flowSetTemp, F("flowsettemp"), F("setpoint flow temperature"))
|
||||
MAKE_PSTR_LIST(flowTempHc, F("flowtemphc"), F("flow temperature in assigned hc (TC1)"))
|
||||
MAKE_PSTR_LIST(pumpStatus, F("pumpstatus"), F("pump status in assigned hc (PC1)"))
|
||||
MAKE_PSTR_LIST(mixerStatus, F("valvestatus"), F("mixing valve actuator in assigned hc (VC1)"))
|
||||
MAKE_PSTR_LIST(flowTempHc, F("flowtemphc"), F("flow temperature (TC1)"))
|
||||
MAKE_PSTR_LIST(pumpStatus, F("pumpstatus"), F("pump status (PC1)"))
|
||||
MAKE_PSTR_LIST(mixerStatus, F("valvestatus"), F("mixing valve actuator (VC1)"))
|
||||
MAKE_PSTR_LIST(flowTempVf, F("flowtempvf"), F("flow temperature in header (T0/Vf)"))
|
||||
MAKE_PSTR_LIST(mixerSetTime, F("valvesettime"), F("time to set valve"))
|
||||
// mixer prefixed with wwc
|
||||
MAKE_PSTR_LIST(wwPumpStatus, F("pumpstatus"), F("pump status in assigned wwc (PC1)"))
|
||||
MAKE_PSTR_LIST(wwTempStatus, F("wwtempstatus"), F("temperature switch in assigned wwc (MC1)"))
|
||||
MAKE_PSTR_LIST(wwTemp, F("wwtemp"), F("current temperature"))
|
||||
// mixer pool
|
||||
MAKE_PSTR_LIST(poolSetTemp, F("poolsettemp"), F("pool set temperature"))
|
||||
MAKE_PSTR_LIST(poolTemp, F("pooltemp"), F("pool temperature"))
|
||||
MAKE_PSTR_LIST(poolShuntStatus, F("poolshuntstatus"), F("pool shunt status opening/closing"))
|
||||
MAKE_PSTR_LIST(poolShunt, F("poolshunt"), F("pool shunt open/close (0% = pool / 100% = heat)"))
|
||||
|
||||
// solar
|
||||
MAKE_PSTR_LIST(type, F("type"), F("type"))
|
||||
MAKE_PSTR_LIST(collectorTemp, F("collectortemp"), F("collector temperature (TS1)"))
|
||||
MAKE_PSTR_LIST(tankBottomTemp, F("tankbottomtemp"), F("tank bottom temperature (TS2)"))
|
||||
MAKE_PSTR_LIST(tank2BottomTemp, F("tank2bottomtemp"), F("second tank bottom temperature (TS5)"))
|
||||
MAKE_PSTR_LIST(collector2Temp, F("collector2temp"), F("collector 2 temperature (TS7)"))
|
||||
MAKE_PSTR_LIST(cylBottomTemp, F("cylbottomtemp"), F("cylinder bottom temperature (TS2)"))
|
||||
MAKE_PSTR_LIST(cyl2BottomTemp, F("cyl2bottomtemp"), F("second cylinder bottom temperature (TS5)"))
|
||||
MAKE_PSTR_LIST(heatExchangerTemp, F("heatexchangertemp"), F("heat exchanger temperature (TS6)"))
|
||||
MAKE_PSTR_LIST(cylMiddleTemp, F("cylmiddletemp"), F("cylinder middle temperature (TS3)"))
|
||||
MAKE_PSTR_LIST(retHeatAssist, F("retheatassist"), F("return temperature heat assistance (TS4)"))
|
||||
// correct name for M1? value not found, try this:
|
||||
MAKE_PSTR_LIST(m1Valve, F("heatassistvalve"), F("heat assistance valve (M1)"))
|
||||
MAKE_PSTR_LIST(m1Power, F("heatassistpower"), F("heat assistance valve power (M1)"))
|
||||
MAKE_PSTR_LIST(collectorMaxTemp, F("collectormaxtemp"), F("maximum collector temperature"))
|
||||
MAKE_PSTR_LIST(collectorMinTemp, F("collectormintemp"), F("minimum collector temperature"))
|
||||
MAKE_PSTR_LIST(tankMaxTemp, F("tankmaxtemp"), F("maximum tank temperature"))
|
||||
MAKE_PSTR_LIST(solarPumpModulation, F("solarpumpmodulation"), F("pump modulation (PS1)"))
|
||||
MAKE_PSTR_LIST(cylinderPumpModulation, F("cylinderpumpmodulation"), F("cylinder pump modulation (PS5)"))
|
||||
MAKE_PSTR_LIST(cylMaxTemp, F("cylmaxtemp"), F("maximum cylinder temperature"))
|
||||
// MAKE_PSTR_LIST(cyl2MaxTemp, F("cyl2maxtemp"), F("maximum cylinder 2 temperature"))
|
||||
MAKE_PSTR_LIST(solarPumpMod, F("solarpumpmod"), F("pump modulation (PS1)"))
|
||||
MAKE_PSTR_LIST(cylPumpMod, F("cylpumpmod"), F("cylinder pump modulation (PS5)"))
|
||||
MAKE_PSTR_LIST(solarPump, F("solarpump"), F("pump (PS1)"))
|
||||
MAKE_PSTR_LIST(solarPump2, F("solarpump2"), F("pump 2 (PS4)"))
|
||||
MAKE_PSTR_LIST(solarPump2Mod, F("solarpump2mod"), F("pump 2 modulation (PS4)"))
|
||||
MAKE_PSTR_LIST(valveStatus, F("valvestatus"), F("valve status"))
|
||||
MAKE_PSTR_LIST(tankHeated, F("tankheated"), F("tank heated"))
|
||||
MAKE_PSTR_LIST(cylHeated, F("cylheated"), F("cyl heated"))
|
||||
MAKE_PSTR_LIST(collectorShutdown, F("collectorshutdown"), F("collector shutdown"))
|
||||
MAKE_PSTR_LIST(pumpWorkTime, F("pumpworktime"), F("pump working time"))
|
||||
MAKE_PSTR_LIST(pump2WorkTime, F("pump2worktime"), F("pump 2 working time"))
|
||||
MAKE_PSTR_LIST(m1WorkTime, F("m1worktime"), F("differential control working time"))
|
||||
MAKE_PSTR_LIST(energyLastHour, F("energylasthour"), F("energy last hour"))
|
||||
MAKE_PSTR_LIST(energyTotal, F("energytotal"), F("energy total"))
|
||||
MAKE_PSTR_LIST(energyToday, F("energytoday"), F("energy today"))
|
||||
MAKE_PSTR_LIST(pumpMinMod, F("pumpminmod"), F("minimum pump modulation"))
|
||||
MAKE_PSTR_LIST(maxFlow, F("maxflow"), F("maximum solar flow"))
|
||||
MAKE_PSTR_LIST(solarPower, F("solarpower"), F("actual solar power"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnonDiff, F("turnondiff"), F("pump turn on difference"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnoffDiff, F("turnoffdiff"), F("pump turn off difference"))
|
||||
MAKE_PSTR_LIST(pump2MinMod, F("pump2minmod"), F("minimum pump 2 modulation"))
|
||||
MAKE_PSTR_LIST(solarPump2TurnonDiff, F("turnondiff2"), F("pump 2 turn on difference"))
|
||||
MAKE_PSTR_LIST(solarPump2TurnoffDiff, F("turnoffdiff2"), F("pump 2 turn off difference"))
|
||||
|
||||
// solar ww
|
||||
MAKE_PSTR_LIST(wwTemp1, F("wwtemp1"), F("temperature 1"))
|
||||
MAKE_PSTR_LIST(wwTemp3, F("wwtemp3"), F("temperature 3"))
|
||||
MAKE_PSTR_LIST(wwTemp4, F("wwtemp4"), F("temperature 4"))
|
||||
MAKE_PSTR_LIST(wwTemp5, F("wwtemp5"), F("temperature 5"))
|
||||
MAKE_PSTR_LIST(wwTemp7, F("wwtemp7"), F("temperature 7"))
|
||||
MAKE_PSTR_LIST(wwPump, F("wwpump"), F("pump"))
|
||||
// solar ww and mixer wwc
|
||||
MAKE_PSTR_LIST(wwMinTemp, F("wwmintemp"), F("minimum temperature"))
|
||||
MAKE_PSTR_LIST(pumpMinMod, F("pumpminmod"), F("minimum pump modulation"))
|
||||
MAKE_PSTR_LIST(maxFlow, F("maxflow"), F("maximum solar flow"))
|
||||
MAKE_PSTR_LIST(solarPower, F("solarpower"), F("actual solar power"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnonDiff, F("turnondiff"), F("pump turn on difference"))
|
||||
MAKE_PSTR_LIST(solarPumpTurnoffDiff, F("turnoffdiff"), F("pump turn off difference"))
|
||||
MAKE_PSTR_LIST(wwRedTemp, F("wwredtemp"), F("reduced temperature"))
|
||||
MAKE_PSTR_LIST(wwDailyTemp, F("wwdailytemp"), F("daily temperature"))
|
||||
MAKE_PSTR_LIST(wwKeepWarm, F("wwkeepwarm"), F("keep warm"))
|
||||
MAKE_PSTR_LIST(wwStatus2, F("wwstatus2"), F("status 2"))
|
||||
MAKE_PSTR_LIST(enum_wwStatus2, F(""), F(""), F(""), F("no_heat"), F(""), F(""), F("heatrequest"), F(""), F("disinfecting"), F("hold"))
|
||||
MAKE_PSTR_LIST(wwPumpMod, F("wwpumpmod"), F("pump modulation"))
|
||||
MAKE_PSTR_LIST(wwFlow, F("wwflow"), F("flow rate"))
|
||||
// extra mixer ww
|
||||
MAKE_PSTR_LIST(wwRequiredTemp, F("wwrequiredtemp"), F("required temperature"))
|
||||
MAKE_PSTR_LIST(wwDiffTemp, F("wwdifftemp"), F("start differential temperature"))
|
||||
|
||||
// Solar SM100
|
||||
//SM100
|
||||
MAKE_PSTR_LIST(heatTransferSystem, F("heattransfersystem"), F("heattransfer system"))
|
||||
MAKE_PSTR_LIST(externalTank, F("externaltank"), F("external tank"))
|
||||
MAKE_PSTR_LIST(externalCyl, F("externalcyl"), F("external cylinder"))
|
||||
MAKE_PSTR_LIST(thermalDisinfect, F("thermaldisinfect"), F("thermal disinfection"))
|
||||
MAKE_PSTR_LIST(heatMetering, F("heatmetering"), F("heatmetering"))
|
||||
MAKE_PSTR_LIST(solarIsEnabled, F("solarenabled"), F("solarmodule enabled"))
|
||||
|
||||
// telegram 0x035A
|
||||
MAKE_PSTR_LIST(solarPumpMode, F("solarpumpmode"), F("solar pump mode"))
|
||||
MAKE_PSTR_LIST(solarPumpMode, F("solarpumpmode"), F("pump mode"))
|
||||
MAKE_PSTR_LIST(solarPumpKick, F("pumpkick"), F("pumpkick"))
|
||||
MAKE_PSTR_LIST(plainWaterMode, F("plainwatermode"), F("plain water mode"))
|
||||
MAKE_PSTR_LIST(doubleMatchFlow, F("doublematchflow"), F("doublematchflow"))
|
||||
MAKE_PSTR_LIST(solarPump2Mode, F("pump2mode"), F("pump 2 mode"))
|
||||
MAKE_PSTR_LIST(solarPump2Kick, F("pump2kick"), F("pumpkick 2"))
|
||||
|
||||
// telegram 0x035F
|
||||
MAKE_PSTR_LIST(cylPriority, F("cylpriority"), F("cylinder priority"))
|
||||
|
||||
// telegram 0x380
|
||||
MAKE_PSTR_LIST(climateZone, F("climatezone"), F("climate zone"))
|
||||
MAKE_PSTR_LIST(collector1Area, F("collector1area"), F("collector 1 area"))
|
||||
MAKE_PSTR_LIST(collector1Type, F("collector1type"), F("collector 1 type"))
|
||||
MAKE_PSTR_LIST(collector2Area, F("collector2area"), F("collector 2 area"))
|
||||
MAKE_PSTR_LIST(collector2Type, F("collector2type"), F("collector 2 type"))
|
||||
|
||||
// telegram 0x0363 heatCounter
|
||||
MAKE_PSTR_LIST(heatCntFlowTemp, F("heatcntflowtemp"), F("heat counter flow temperature"))
|
||||
MAKE_PSTR_LIST(heatCntRetTemp, F("heatcntrettemp"), F("heat counter return temperature"))
|
||||
MAKE_PSTR_LIST(heatCnt, F("heatcnt"), F("heat counter impulses"))
|
||||
MAKE_PSTR_LIST(swapFlowTemp, F("swapflowtemp"), F("swap flow temperature (TS14)"))
|
||||
MAKE_PSTR_LIST(swapRetTemp, F("swaprettemp"), F("swap return temperature (TS15)"))
|
||||
|
||||
// switch
|
||||
MAKE_PSTR_LIST(activated, F("activated"), F("activated"))
|
||||
MAKE_PSTR_LIST(status, F("status"), F("status"))
|
||||
|
||||
// unknown fields to track (SM10)
|
||||
MAKE_PSTR_LIST(data11, F("data11"), F("unknown datafield 11"))
|
||||
MAKE_PSTR_LIST(data12, F("data12"), F("unknown datafield 12"))
|
||||
MAKE_PSTR_LIST(data8, F("data8"), F("unknown datafield 8"))
|
||||
MAKE_PSTR_LIST(data0, F("data0"), F("unknown datafield 0"))
|
||||
MAKE_PSTR_LIST(data1, F("data1"), F("unknown datafield 1"))
|
||||
MAKE_PSTR_LIST(setting3, F("setting3"), F("unknown setting 3"))
|
||||
MAKE_PSTR_LIST(setting4, F("setting4"), F("unknown setting 4"))
|
||||
|
||||
// RF sensor, id 0x40, telegram 0x435
|
||||
MAKE_PSTR_LIST(RFTemp, F("rftemp"), F("RF room temperature sensor"));
|
||||
|
||||
467
src/mqtt.cpp
467
src/mqtt.cpp
@@ -19,6 +19,7 @@
|
||||
#include "mqtt.h"
|
||||
#include "emsesp.h"
|
||||
#include "version.h"
|
||||
#include "emsdevice.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -35,21 +36,39 @@ uint32_t Mqtt::publish_time_mixer_;
|
||||
uint32_t Mqtt::publish_time_sensor_;
|
||||
uint32_t Mqtt::publish_time_other_;
|
||||
bool Mqtt::mqtt_enabled_;
|
||||
uint8_t Mqtt::ha_climate_format_;
|
||||
bool Mqtt::ha_enabled_;
|
||||
uint8_t Mqtt::nested_format_;
|
||||
std::string Mqtt::discovery_prefix_;
|
||||
bool Mqtt::send_response_;
|
||||
bool Mqtt::publish_single_;
|
||||
|
||||
std::deque<Mqtt::QueuedMqttMessage> Mqtt::mqtt_messages_;
|
||||
std::vector<Mqtt::MQTTSubFunction> Mqtt::mqtt_subfunctions_;
|
||||
|
||||
uint16_t Mqtt::mqtt_publish_fails_ = 0;
|
||||
uint32_t Mqtt::mqtt_publish_fails_ = 0;
|
||||
bool Mqtt::connecting_ = false;
|
||||
bool Mqtt::initialized_ = false;
|
||||
uint8_t Mqtt::connectcount_ = 0;
|
||||
uint16_t Mqtt::mqtt_message_id_ = 0;
|
||||
uint32_t Mqtt::mqtt_message_id_ = 0;
|
||||
char will_topic_[Mqtt::MQTT_TOPIC_MAX_SIZE]; // because MQTT library keeps only char pointer
|
||||
|
||||
// Home Assistant specific
|
||||
// icons from https://materialdesignicons.com used with the UOMs (unit of measurements)
|
||||
MAKE_PSTR_WORD(measurement)
|
||||
MAKE_PSTR_WORD(total_increasing)
|
||||
MAKE_PSTR(icondegrees, "mdi:coolant-temperature") // DeviceValueUOM::DEGREES
|
||||
MAKE_PSTR(iconpercent, "mdi:percent-outline") // DeviceValueUOM::PERCENT
|
||||
MAKE_PSTR(icontime, "mdi:clock-outline") // DeviceValueUOM::SECONDS MINUTES & HOURS
|
||||
MAKE_PSTR(iconkb, "mdi:memory") // DeviceValueUOM::KB
|
||||
MAKE_PSTR(iconlmin, "mdi:water-boiler") // DeviceValueUOM::LMIN
|
||||
MAKE_PSTR(iconkwh, "mdi:transmission-tower") // DeviceValueUOM::KWH & WH
|
||||
MAKE_PSTR(iconua, "mdi:lightning-bolt-circle") // DeviceValueUOM::UA
|
||||
MAKE_PSTR(iconbar, "mdi:gauge") // DeviceValueUOM::BAR
|
||||
MAKE_PSTR(iconkw, "mdi:omega") // DeviceValueUOM::KW & W
|
||||
MAKE_PSTR(icondbm, "mdi:wifi-strength-2") // DeviceValueUOM::DBM
|
||||
MAKE_PSTR(iconnum, "mdi:counter") // DeviceValueUOM::NONE
|
||||
MAKE_PSTR(icondevice, "mdi:home-automation") // for devices in HA
|
||||
|
||||
uuid::log::Logger Mqtt::logger_{F_(mqtt), uuid::log::Facility::DAEMON};
|
||||
|
||||
// subscribe to an MQTT topic, and store the associated callback function
|
||||
@@ -81,6 +100,12 @@ void Mqtt::subscribe(const uint8_t device_type, const std::string & topic, mqtt_
|
||||
queue_subscribe_message(topic);
|
||||
}
|
||||
|
||||
// subscribe without storing to subfunctions
|
||||
void Mqtt::subscribe(const std::string & topic) {
|
||||
// add to MQTT queue as a subscribe operation
|
||||
queue_subscribe_message(topic);
|
||||
}
|
||||
|
||||
// resubscribe to all MQTT topics
|
||||
// if it's already in the queue, ignore it
|
||||
void Mqtt::resubscribe() {
|
||||
@@ -147,7 +172,7 @@ void Mqtt::loop() {
|
||||
|
||||
if (publish_time_other_ && (currentMillis - last_publish_other_ > publish_time_other_)) {
|
||||
last_publish_other_ = (currentMillis / publish_time_other_) * publish_time_other_;
|
||||
EMSESP::publish_other_values();
|
||||
EMSESP::publish_other_values(); // switch and heatpump
|
||||
} else
|
||||
|
||||
if (publish_time_sensor_ && (currentMillis - last_publish_sensor_ > publish_time_sensor_)) {
|
||||
@@ -182,7 +207,9 @@ void Mqtt::show_mqtt(uuid::console::Shell & shell) {
|
||||
for (const auto & message : mqtt_messages_) {
|
||||
auto content = message.content_;
|
||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||
if ((strncmp(content->topic.c_str(), "homeassistant/", 13) != 0)) {
|
||||
|
||||
// prefix base, only if it's not a discovery topic
|
||||
if (content->topic.compare(0, discovery_prefix().size(), discovery_prefix()) == 0) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", Mqtt::base().c_str(), content->topic.c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s", content->topic.c_str());
|
||||
@@ -303,12 +330,12 @@ void Mqtt::show_topic_handlers(uuid::console::Shell & shell, const uint8_t devic
|
||||
return;
|
||||
}
|
||||
|
||||
shell.print(F(" Subscribed MQTT topics: "));
|
||||
for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
|
||||
if (mqtt_subfunction.device_type_ == device_type) {
|
||||
shell.printf(F("%s "), mqtt_subfunction.topic_.c_str());
|
||||
}
|
||||
}
|
||||
// shell.print(F(" Subscribed MQTT topics: "));
|
||||
// for (const auto & mqtt_subfunction : mqtt_subfunctions_) {
|
||||
// if (mqtt_subfunction.device_type_ == device_type) {
|
||||
// shell.printf(F("%s "), mqtt_subfunction.topic_.c_str());
|
||||
// }
|
||||
// }
|
||||
shell.println();
|
||||
}
|
||||
|
||||
@@ -360,14 +387,15 @@ void Mqtt::reset_mqtt() {
|
||||
|
||||
void Mqtt::load_settings() {
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & mqttSettings) {
|
||||
mqtt_base_ = mqttSettings.base.c_str(); // Convert String to std::string
|
||||
mqtt_qos_ = mqttSettings.mqtt_qos;
|
||||
mqtt_retain_ = mqttSettings.mqtt_retain;
|
||||
mqtt_enabled_ = mqttSettings.enabled;
|
||||
ha_enabled_ = mqttSettings.ha_enabled;
|
||||
ha_climate_format_ = mqttSettings.ha_climate_format;
|
||||
nested_format_ = mqttSettings.nested_format;
|
||||
send_response_ = mqttSettings.send_response;
|
||||
mqtt_base_ = mqttSettings.base.c_str(); // Convert String to std::string
|
||||
mqtt_qos_ = mqttSettings.mqtt_qos;
|
||||
mqtt_retain_ = mqttSettings.mqtt_retain;
|
||||
mqtt_enabled_ = mqttSettings.enabled;
|
||||
ha_enabled_ = mqttSettings.ha_enabled;
|
||||
nested_format_ = mqttSettings.nested_format;
|
||||
publish_single_ = mqttSettings.publish_single;
|
||||
send_response_ = mqttSettings.send_response;
|
||||
discovery_prefix_ = mqttSettings.discovery_prefix.c_str();
|
||||
|
||||
// convert to milliseconds
|
||||
publish_time_boiler_ = mqttSettings.publish_time_boiler * 1000;
|
||||
@@ -465,6 +493,9 @@ void Mqtt::set_publish_time_sensor(uint16_t publish_time) {
|
||||
}
|
||||
|
||||
bool Mqtt::get_publish_onchange(uint8_t device_type) {
|
||||
if (publish_single_ && !ha_enabled_) {
|
||||
return false;
|
||||
}
|
||||
if (device_type == EMSdevice::DeviceType::BOILER) {
|
||||
if (!publish_time_boiler_) {
|
||||
return true;
|
||||
@@ -584,7 +615,7 @@ void Mqtt::ha_status() {
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
dev["name"] = F_(EMSESP); // "EMS-ESP"
|
||||
dev["sw"] = EMSESP_APP_VERSION;
|
||||
dev["sw"] = "v" + std::string(EMSESP_APP_VERSION);
|
||||
dev["mf"] = FJSON("proddy");
|
||||
dev["mdl"] = F_(EMSESP); // "EMS-ESP"
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
@@ -596,39 +627,18 @@ void Mqtt::ha_status() {
|
||||
|
||||
// create the sensors - must match the MQTT payload keys
|
||||
if (!EMSESP::system_.ethernet_connected()) {
|
||||
publish_ha_sensor_config(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("WiFi RSSI"), EMSdevice::DeviceType::SYSTEM, F("rssi"), DeviceValueUOM::DBM);
|
||||
publish_ha_sensor_config(DeviceValueType::INT,
|
||||
DeviceValueTAG::TAG_HEARTBEAT,
|
||||
F("WiFi strength"),
|
||||
EMSdevice::DeviceType::SYSTEM,
|
||||
F("wifistrength"),
|
||||
DeviceValueUOM::PERCENT);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("WiFi RSSI"), F("rssi"), DeviceValueUOM::DBM);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("WiFi strength"), F("wifistrength"), DeviceValueUOM::PERCENT);
|
||||
}
|
||||
|
||||
publish_ha_sensor_config(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Uptime"), EMSdevice::DeviceType::SYSTEM, F("uptime"), DeviceValueUOM::NONE);
|
||||
publish_ha_sensor_config(DeviceValueType::INT,
|
||||
DeviceValueTAG::TAG_HEARTBEAT,
|
||||
F("Uptime (sec)"),
|
||||
EMSdevice::DeviceType::SYSTEM,
|
||||
F("uptime_sec"),
|
||||
DeviceValueUOM::SECONDS);
|
||||
publish_ha_sensor_config(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Free memory"), EMSdevice::DeviceType::SYSTEM, F("freemem"), DeviceValueUOM::KB);
|
||||
publish_ha_sensor_config(DeviceValueType::INT,
|
||||
DeviceValueTAG::TAG_HEARTBEAT,
|
||||
F("MQTT fails"),
|
||||
EMSdevice::DeviceType::SYSTEM,
|
||||
F("mqttfails"),
|
||||
DeviceValueUOM::TIMES);
|
||||
publish_ha_sensor_config(DeviceValueType::INT,
|
||||
DeviceValueTAG::TAG_HEARTBEAT,
|
||||
F("Rx received"),
|
||||
EMSdevice::DeviceType::SYSTEM,
|
||||
F("rxreceived"),
|
||||
DeviceValueUOM::TIMES);
|
||||
publish_ha_sensor_config(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Rx fails"), EMSdevice::DeviceType::SYSTEM, F("rxfails"), DeviceValueUOM::TIMES);
|
||||
publish_ha_sensor_config(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Tx reads"), EMSdevice::DeviceType::SYSTEM, F("txreads"), DeviceValueUOM::TIMES);
|
||||
publish_ha_sensor_config(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Tx writes"), EMSdevice::DeviceType::SYSTEM, F("txwrites"), DeviceValueUOM::TIMES);
|
||||
publish_ha_sensor_config(DeviceValueType::INT, DeviceValueTAG::TAG_HEARTBEAT, F("Tx fails"), EMSdevice::DeviceType::SYSTEM, F("txfails"), DeviceValueUOM::TIMES);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Uptime"), F("uptime"), DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Uptime (sec)"), F("uptime_sec"), DeviceValueUOM::SECONDS);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Free memory"), F("freemem"), DeviceValueUOM::KB);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("MQTT fails"), F("mqttfails"), DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Rx received"), F("rxreceived"), DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Rx fails"), F("rxfails"), DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Tx reads"), F("txreads"), DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Tx writes"), F("txwrites"), DeviceValueUOM::NONE);
|
||||
publish_system_ha_sensor_config(DeviceValueType::INT, F("Tx fails"), F("txfails"), DeviceValueUOM::NONE);
|
||||
}
|
||||
|
||||
// add sub or pub task to the queue.
|
||||
@@ -639,16 +649,6 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// if it's a publish and the payload is empty, stop
|
||||
/*
|
||||
if ((operation == Operation::PUBLISH) && (payload.empty())) {
|
||||
#ifdef EMSESP_DEBUG
|
||||
LOG_WARNING("[DEBUG] Publish empty payload - quitting");
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
*/
|
||||
|
||||
// take the topic and prefix the base, unless its for HA
|
||||
std::shared_ptr<MqttMessage> message;
|
||||
message = std::make_shared<MqttMessage>(operation, topic, payload, retain);
|
||||
@@ -668,6 +668,7 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
|
||||
// if the queue is full, make room but removing the last one
|
||||
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) {
|
||||
mqtt_messages_.pop_front();
|
||||
LOG_WARNING(F("Queue overflow, removing one message"));
|
||||
}
|
||||
mqtt_messages_.emplace_back(mqtt_message_id_++, std::move(message));
|
||||
|
||||
@@ -744,12 +745,12 @@ void Mqtt::publish_ha(const std::string & topic) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string fulltopic = read_flash_string(F_(homeassistant)) + topic;
|
||||
std::string fulltopic = Mqtt::discovery_prefix() + topic;
|
||||
#if defined(EMSESP_DEBUG)
|
||||
LOG_DEBUG(F("[DEBUG] Publishing empty HA topic=%s"), fulltopic.c_str());
|
||||
#endif
|
||||
|
||||
publish(fulltopic); // call it immediately, don't queue it
|
||||
publish(fulltopic);
|
||||
}
|
||||
|
||||
// publish a Home Assistant config topic and payload, with retain flag off.
|
||||
@@ -762,7 +763,7 @@ void Mqtt::publish_ha(const std::string & topic, const JsonObject & payload) {
|
||||
payload_text.reserve(measureJson(payload) + 1);
|
||||
serializeJson(payload, payload_text); // convert json to string
|
||||
|
||||
std::string fulltopic = read_flash_string(F_(homeassistant)) + topic;
|
||||
std::string fulltopic = Mqtt::discovery_prefix() + topic;
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
LOG_DEBUG(F("Publishing HA topic=%s, payload=%s"), fulltopic.c_str(), payload_text.c_str());
|
||||
#elif defined(EMSESP_DEBUG)
|
||||
@@ -785,7 +786,7 @@ void Mqtt::process_queue() {
|
||||
auto message = mqtt_message.content_;
|
||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
if (message->topic.find(read_flash_string(F_(homeassistant))) == 0) {
|
||||
if (message->topic.find(discovery_prefix_) == 0) {
|
||||
strcpy(topic, message->topic.c_str()); // leave topic as it is
|
||||
} else {
|
||||
snprintf(topic, MQTT_TOPIC_MAX_SIZE, "%s/%s", mqtt_base_.c_str(), message->topic.c_str());
|
||||
@@ -851,27 +852,74 @@ void Mqtt::process_queue() {
|
||||
mqtt_messages_.pop_front(); // remove the message from the queue
|
||||
}
|
||||
|
||||
void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevice::DeviceValueType
|
||||
uint8_t tag, // EMSdevice::DeviceValueTAG
|
||||
const __FlashStringHelper * name,
|
||||
const uint8_t device_type, // EMSdevice::DeviceType
|
||||
const __FlashStringHelper * entity,
|
||||
const uint8_t uom) { // EMSdevice::DeviceValueUOM (0=NONE)
|
||||
publish_ha_sensor_config(type, tag, name, device_type, entity, uom, false, false);
|
||||
// publish HA sensor for System using the heartbeat tag
|
||||
void Mqtt::publish_system_ha_sensor_config(uint8_t type, const __FlashStringHelper * name, const __FlashStringHelper * entity, const uint8_t uom) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
JsonObject dev_json = doc.createNestedObject("dev");
|
||||
|
||||
JsonArray ids = dev_json.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
publish_ha_sensor_config(type, DeviceValueTAG::TAG_HEARTBEAT, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, false, nullptr, 0, 0, 0, dev_json);
|
||||
}
|
||||
|
||||
// create's a ha sensor config topic from a device value object
|
||||
// and also takes a flag to see whether it will also create the main HA device config
|
||||
void Mqtt::publish_ha_sensor_config(DeviceValue & dv, const std::string & model, const std::string & brand, const bool remove, const bool create_device_config) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> dev_json;
|
||||
|
||||
// HA config for a sensor and binary_sensor entity
|
||||
// always create the ids
|
||||
JsonArray ids = dev_json.createNestedArray("ids");
|
||||
char ha_device[40];
|
||||
std::string device_type_name = EMSdevice::device_type_2_device_name(dv.device_type);
|
||||
snprintf(ha_device, sizeof(ha_device), "ems-esp-%s", device_type_name.c_str());
|
||||
ids.add(ha_device);
|
||||
|
||||
if (create_device_config) {
|
||||
device_type_name[0] = toupper(device_type_name[0]); // capitalize
|
||||
dev_json["name"] = "EMS-ESP " + device_type_name;
|
||||
dev_json["mf"] = brand;
|
||||
dev_json["mdl"] = model;
|
||||
dev_json["via_device"] = "ems-esp";
|
||||
}
|
||||
|
||||
// calculate the min and max
|
||||
int16_t dv_set_min, dv_set_max;
|
||||
(void)dv.get_min_max(dv_set_min, dv_set_max);
|
||||
|
||||
publish_ha_sensor_config(dv.type,
|
||||
dv.tag,
|
||||
dv.full_name,
|
||||
dv.device_type,
|
||||
dv.short_name,
|
||||
dv.uom,
|
||||
remove,
|
||||
create_device_config,
|
||||
dv.has_cmd,
|
||||
dv.options,
|
||||
dv.options_size,
|
||||
dv_set_min,
|
||||
dv_set_max,
|
||||
dev_json.as<JsonObject>());
|
||||
}
|
||||
|
||||
// MQTT discovery configs
|
||||
// entity must match the key/value pair in the *_data topic
|
||||
// note: some string copying here into chars, it looks messy but does help with heap fragmentation issues
|
||||
void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevice::DeviceValueType
|
||||
uint8_t tag, // EMSdevice::DeviceValueTAG
|
||||
const __FlashStringHelper * name,
|
||||
const uint8_t device_type, // EMSdevice::DeviceType
|
||||
const __FlashStringHelper * entity,
|
||||
const uint8_t uom, // EMSdevice::DeviceValueUOM (0=NONE)
|
||||
const bool remove, // true if we want to remove this topic
|
||||
const bool has_cmd) {
|
||||
// note: some extra string copying done here, it looks messy but does help with heap fragmentation issues
|
||||
void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevice::DeviceValueType
|
||||
uint8_t tag, // EMSdevice::DeviceValueTAG
|
||||
const __FlashStringHelper * name, // fullname
|
||||
const uint8_t device_type, // EMSdevice::DeviceType
|
||||
const __FlashStringHelper * entity, // shortname
|
||||
const uint8_t uom, // EMSdevice::DeviceValueUOM (0=NONE)
|
||||
const bool remove, // true if we want to remove this topic
|
||||
const bool create_device_config, // true if need to create main device config
|
||||
const bool has_cmd,
|
||||
const __FlashStringHelper * const * options,
|
||||
uint8_t options_size,
|
||||
const int16_t dv_set_min,
|
||||
const int16_t dv_set_max,
|
||||
const JsonObject & dev_json) {
|
||||
// ignore if name (fullname) is empty
|
||||
if (name == nullptr) {
|
||||
return;
|
||||
@@ -890,35 +938,119 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevi
|
||||
}
|
||||
|
||||
// build unique identifier which will be used in the topic, replacing all . with _ as not to break HA
|
||||
std::string uniq(50, '\0');
|
||||
snprintf(&uniq[0], uniq.capacity() + 1, "%s_%s", device_name, new_entity);
|
||||
std::replace(uniq.begin(), uniq.end(), '.', '_');
|
||||
char uniq[101];
|
||||
snprintf(uniq, sizeof(uniq), "%s_%s", device_name, new_entity);
|
||||
Helpers::replace_char(uniq, '.', '_');
|
||||
|
||||
// create the topic
|
||||
// use_ha_sensor is true if we're using the Sensor Entity https://developers.home-assistant.io/docs/core/entity/sensor
|
||||
bool use_ha_sensor = false;
|
||||
|
||||
// create the topic, depending on the type and whether the device entity is writable (a command)
|
||||
// https://developers.home-assistant.io/docs/core/entity
|
||||
char topic[MQTT_TOPIC_MAX_SIZE];
|
||||
if (type == DeviceValueType::BOOL) {
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s/config", mqtt_base_.c_str(), uniq.c_str()); // binary sensor
|
||||
// if it's a command then we can use Number, Switch. Otherwise stick to Sensor
|
||||
if (has_cmd) {
|
||||
switch (type) {
|
||||
case DeviceValueType::INT:
|
||||
case DeviceValueType::UINT:
|
||||
case DeviceValueType::SHORT:
|
||||
case DeviceValueType::USHORT:
|
||||
case DeviceValueType::ULONG:
|
||||
// number - https://www.home-assistant.io/integrations/number.mqtt/
|
||||
// https://developers.home-assistant.io/docs/core/entity/number
|
||||
|
||||
snprintf(topic, sizeof(topic), "number/%s/%s/config", mqtt_base_.c_str(), uniq);
|
||||
break;
|
||||
case DeviceValueType::BOOL:
|
||||
// switch - https://www.home-assistant.io/integrations/switch.mqtt/
|
||||
snprintf(topic, sizeof(topic), "switch/%s/%s/config", mqtt_base_.c_str(), uniq);
|
||||
break;
|
||||
case DeviceValueType::ENUM:
|
||||
// select - https://www.home-assistant.io/integrations/select.mqtt
|
||||
snprintf(topic, sizeof(topic), "select/%s/%s/config", mqtt_base_.c_str(), uniq);
|
||||
break;
|
||||
default:
|
||||
// plain old sensor
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s/config", mqtt_base_.c_str(), uniq);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s/config", mqtt_base_.c_str(), uniq.c_str()); // normal HA sensor, not a boolean one
|
||||
// plain old read only device entity
|
||||
if (type == DeviceValueType::BOOL) {
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s/config", mqtt_base_.c_str(), uniq); // binary sensor
|
||||
} else {
|
||||
use_ha_sensor = true;
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s/config", mqtt_base_.c_str(), uniq); // normal HA sensor, not a boolean one
|
||||
}
|
||||
}
|
||||
|
||||
// if we're asking to remove this topic, send an empty payload
|
||||
// if we're asking to remove this topic, send an empty payload and exit
|
||||
// https://github.com/emsesp/EMS-ESP32/issues/196
|
||||
if (remove) {
|
||||
LOG_WARNING(F("Lost device value for %s. Removing HA config"), uniq.c_str());
|
||||
LOG_DEBUG(F("Removing HA config for %s"), uniq);
|
||||
publish_ha(topic);
|
||||
return;
|
||||
}
|
||||
|
||||
bool have_tag = !EMSdevice::tag_to_string(tag).empty();
|
||||
|
||||
// nested_format is 1 if nested, otherwise 2 for single topics
|
||||
bool is_nested = (nested_format_ == 1);
|
||||
bool have_tag = !EMSdevice::tag_to_string(tag).empty();
|
||||
bool is_nested = (nested_format_ == 1); // nested_format is 1 if nested, otherwise 2 for single topics
|
||||
|
||||
// build the payload
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_HA_CONFIG);
|
||||
doc["~"] = mqtt_base_;
|
||||
doc["uniq_id"] = uniq;
|
||||
|
||||
const char * ic_ha = "ic"; // icon - only set this if there is no device class
|
||||
const char * sc_ha = "state_class"; // state class
|
||||
const char * uom_ha = "unit_of_meas"; // unit of measure
|
||||
|
||||
// handle commands, which are device entities that are writable
|
||||
// we add the command topic parameter
|
||||
// note: there is no way to handle strings in HA so datetimes (e.g. set_datetime, set_holiday, set_wwswitchtime etc) are excluded
|
||||
if (has_cmd) {
|
||||
// command topic back to EMS-ESP
|
||||
char command_topic[105];
|
||||
snprintf(command_topic, sizeof(command_topic), "~/%s", uniq);
|
||||
Helpers::replace_char(command_topic, '_', '/');
|
||||
doc["command_topic"] = command_topic;
|
||||
|
||||
// for enums, add options
|
||||
if (type == DeviceValueType::ENUM) {
|
||||
JsonArray option_list = doc.createNestedArray("options");
|
||||
for (uint8_t i = 0; i < options_size; i++) {
|
||||
option_list.add(options[i]);
|
||||
}
|
||||
} else if (type != DeviceValueType::STRING) {
|
||||
// Must be Numeric....
|
||||
// mode can be auto, slider or box. Because its fiddly and error prone, force conversion to box
|
||||
// but... this is not currently supported in HA MQTT Number yet!
|
||||
// doc["mode"] = "box";
|
||||
}
|
||||
|
||||
// set min and max values, if we have a valid range
|
||||
if (dv_set_min != 0 || dv_set_max != 0) {
|
||||
doc["min"] = dv_set_min;
|
||||
doc["max"] = dv_set_max;
|
||||
if ((uom == DeviceValueUOM::DEGREES) || (uom == DeviceValueUOM::DEGREES_R)) {
|
||||
doc["step"] = 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
// set icons
|
||||
// since these don't have a device class we need to add the icon ourselves
|
||||
switch (uom) {
|
||||
case DeviceValueUOM::DEGREES:
|
||||
case DeviceValueUOM::DEGREES_R:
|
||||
doc[ic_ha] = F_(icondegrees);
|
||||
break;
|
||||
case DeviceValueUOM::PERCENT:
|
||||
doc[ic_ha] = F_(iconpercent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// state topic
|
||||
char stat_t[MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(stat_t, sizeof(stat_t), "~/%s", tag_to_topic(device_type, tag).c_str());
|
||||
@@ -936,7 +1068,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevi
|
||||
|
||||
// value template
|
||||
// if its nested mqtt format then use the appended entity name, otherwise take the original
|
||||
char val_tpl[50];
|
||||
char val_tpl[75];
|
||||
if (is_nested) {
|
||||
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", new_entity);
|
||||
} else {
|
||||
@@ -944,126 +1076,109 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdevi
|
||||
}
|
||||
doc["val_tpl"] = val_tpl;
|
||||
|
||||
// look at the device value type
|
||||
// special case to handle booleans
|
||||
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
||||
// always render boolean as strings true & false
|
||||
// and has no unit of measure or icon
|
||||
if (type == DeviceValueType::BOOL) {
|
||||
// how to render boolean. HA only accepts String values
|
||||
char result[10];
|
||||
doc[F("payload_on")] = Helpers::render_boolean(result, true);
|
||||
doc[F("payload_off")] = Helpers::render_boolean(result, false);
|
||||
doc[sc_ha] = F_(measurement);
|
||||
} else {
|
||||
// set default state and device class for HA
|
||||
auto set_state_class = State_class::NONE;
|
||||
auto set_device_class = Device_class::NONE;
|
||||
|
||||
// unit of measure and map the HA icon
|
||||
// always set the uom
|
||||
if (uom != DeviceValueUOM::NONE) {
|
||||
doc["unit_of_meas"] = EMSdevice::uom_to_string(uom);
|
||||
doc[uom_ha] = EMSdevice::uom_to_string(uom);
|
||||
}
|
||||
}
|
||||
|
||||
// this next section is building using the Sensor Entity
|
||||
// https://developers.home-assistant.io/docs/core/entity/sensor
|
||||
// for read only sensors. It uses a device class to determine icon
|
||||
// and state class
|
||||
|
||||
if (use_ha_sensor) {
|
||||
const char * dc_ha = "device_class"; // device class
|
||||
|
||||
switch (uom) {
|
||||
case DeviceValueUOM::DEGREES:
|
||||
doc["ic"] = F_(icondegrees);
|
||||
set_device_class = Device_class::TEMPERATURE;
|
||||
case DeviceValueUOM::DEGREES_R:
|
||||
doc[sc_ha] = F_(measurement);
|
||||
doc[dc_ha] = F("temperature"); // no icon needed
|
||||
break;
|
||||
case DeviceValueUOM::PERCENT:
|
||||
doc["ic"] = F_(iconpercent);
|
||||
set_device_class = Device_class::POWER_FACTOR;
|
||||
doc[sc_ha] = F_(measurement);
|
||||
doc[dc_ha] = F("power_factor"); // no icon needed
|
||||
break;
|
||||
case DeviceValueUOM::SECONDS:
|
||||
case DeviceValueUOM::MINUTES:
|
||||
case DeviceValueUOM::HOURS:
|
||||
doc["ic"] = F_(icontime);
|
||||
doc[ic_ha] = F_(icontime);
|
||||
if (type == DeviceValueType::TIME) {
|
||||
doc[sc_ha] = F_(total_increasing);
|
||||
} else {
|
||||
doc[sc_ha] = F_(measurement);
|
||||
}
|
||||
break;
|
||||
case DeviceValueUOM::KB:
|
||||
doc["ic"] = F_(iconkb);
|
||||
doc[ic_ha] = F_(iconkb);
|
||||
break;
|
||||
case DeviceValueUOM::LMIN:
|
||||
doc["ic"] = F_(iconlmin);
|
||||
doc[ic_ha] = F_(iconlmin);
|
||||
doc[sc_ha] = F_(measurement);
|
||||
break;
|
||||
case DeviceValueUOM::WH:
|
||||
if (entity == FL_(energyToday)[0]) {
|
||||
doc[sc_ha] = F_(total_increasing);
|
||||
} else {
|
||||
doc[sc_ha] = F_(measurement);
|
||||
}
|
||||
doc[dc_ha] = F("energy"); // no icon needed
|
||||
break;
|
||||
case DeviceValueUOM::KWH:
|
||||
doc["ic"] = F_(iconkwh);
|
||||
set_state_class = State_class::TOTAL_INCREASING;
|
||||
set_device_class = Device_class::ENERGY;
|
||||
doc[sc_ha] = F_(total_increasing);
|
||||
doc[dc_ha] = F("energy"); // no icon needed
|
||||
break;
|
||||
case DeviceValueUOM::UA:
|
||||
doc["ic"] = F_(iconua);
|
||||
doc[ic_ha] = F_(iconua);
|
||||
doc[sc_ha] = F_(measurement);
|
||||
break;
|
||||
case DeviceValueUOM::BAR:
|
||||
doc["ic"] = F_(iconbar);
|
||||
set_device_class = Device_class::PRESSURE;
|
||||
doc[sc_ha] = F_(measurement);
|
||||
doc[dc_ha] = F("pressure");
|
||||
break;
|
||||
case DeviceValueUOM::W:
|
||||
case DeviceValueUOM::KW:
|
||||
doc["ic"] = F_(iconkw);
|
||||
set_state_class = State_class::MEASUREMENT;
|
||||
set_device_class = Device_class::POWER;
|
||||
doc[sc_ha] = F_(measurement);
|
||||
doc[dc_ha] = F("power");
|
||||
break;
|
||||
case DeviceValueUOM::DBM:
|
||||
doc["ic"] = F_(icondbm);
|
||||
set_device_class = Device_class::SIGNAL_STRENGTH;
|
||||
doc[sc_ha] = F_(measurement);
|
||||
doc[dc_ha] = F("signal_strength");
|
||||
break;
|
||||
case DeviceValueUOM::NONE:
|
||||
if (type == DeviceValueType::INT || type == DeviceValueType::UINT || type == DeviceValueType::SHORT || type == DeviceValueType::USHORT
|
||||
|| type == DeviceValueType::ULONG) {
|
||||
doc["ic"] = F_(iconnum);
|
||||
// for device entities which have numerical values, with no UOM
|
||||
if ((type != DeviceValueType::STRING)
|
||||
&& (type == DeviceValueType::INT || type == DeviceValueType::UINT || type == DeviceValueType::SHORT || type == DeviceValueType::USHORT
|
||||
|| type == DeviceValueType::ULONG)) {
|
||||
doc[ic_ha] = F_(iconnum); // set icon
|
||||
// determine if its a measurement or total increasing
|
||||
// most of the values are measurement. for example Tx Reads will increment but can be reset to 0 after a restart
|
||||
// all the starts are increasing, and they are ULONGs
|
||||
if (type == DeviceValueType::ULONG) {
|
||||
doc[sc_ha] = F_(total_increasing);
|
||||
} else {
|
||||
doc[sc_ha] = F_(measurement); // default to measurement
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DeviceValueUOM::TIMES:
|
||||
set_state_class = State_class::TOTAL_INCREASING;
|
||||
doc["ic"] = F_(iconnum);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// see if we need to set the state_class and device_class
|
||||
// ignore any commands
|
||||
if (!has_cmd) {
|
||||
// state class
|
||||
if (set_state_class == State_class::MEASUREMENT) {
|
||||
doc["state_class"] = F("measurement");
|
||||
} else if (set_state_class == State_class::TOTAL_INCREASING) {
|
||||
doc["state_class"] = F("total_increasing");
|
||||
}
|
||||
|
||||
// device class
|
||||
switch (set_device_class) {
|
||||
case Device_class::ENERGY:
|
||||
doc["device_class"] = F("energy");
|
||||
break;
|
||||
case Device_class::POWER:
|
||||
doc["device_class"] = F("power");
|
||||
break;
|
||||
case Device_class::POWER_FACTOR:
|
||||
doc["device_class"] = F("power_factor");
|
||||
break;
|
||||
case Device_class::PRESSURE:
|
||||
doc["device_class"] = F("pressure");
|
||||
break;
|
||||
case Device_class::SIGNAL_STRENGTH:
|
||||
doc["device_class"] = F("signal_strength");
|
||||
break;
|
||||
case Device_class::TEMPERATURE:
|
||||
doc["device_class"] = F("temperature");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
|
||||
// for System commands we'll use the ID EMS-ESP
|
||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||
ids.add("ems-esp");
|
||||
} else {
|
||||
char ha_device[40];
|
||||
snprintf(ha_device, sizeof(ha_device), "ems-esp-%s", device_name);
|
||||
ids.add(ha_device);
|
||||
}
|
||||
// add the dev json object to the end
|
||||
doc["dev"] = dev_json;
|
||||
|
||||
publish_ha(topic, doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
87
src/mqtt.h
87
src/mqtt.h
@@ -19,22 +19,13 @@
|
||||
#ifndef EMSESP_MQTT_H_
|
||||
#define EMSESP_MQTT_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
|
||||
#include <AsyncMqttClient.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "system.h"
|
||||
#include "console.h"
|
||||
#include "command.h"
|
||||
|
||||
#include <uuid/log.h>
|
||||
#include "emsdevicevalue.h"
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
@@ -88,15 +79,12 @@ class Mqtt {
|
||||
|
||||
};
|
||||
|
||||
// for Home Assistant
|
||||
enum class State_class { NONE, MEASUREMENT, TOTAL_INCREASING };
|
||||
enum class Device_class { NONE, TEMPERATURE, POWER_FACTOR, ENERGY, PRESSURE, POWER, SIGNAL_STRENGTH };
|
||||
|
||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // note this should really match the user setting in mqttSettings.maxTopicLength
|
||||
|
||||
static void on_connect();
|
||||
|
||||
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);
|
||||
@@ -112,21 +100,25 @@ class Mqtt {
|
||||
static void publish_ha(const __FlashStringHelper * topic, const JsonObject & payload);
|
||||
static void publish_ha(const std::string & topic);
|
||||
|
||||
static void publish_ha_sensor_config(uint8_t type,
|
||||
uint8_t tag,
|
||||
const __FlashStringHelper * name,
|
||||
const uint8_t device_type,
|
||||
const __FlashStringHelper * entity,
|
||||
const uint8_t uom,
|
||||
const bool remove,
|
||||
const bool has_cmd);
|
||||
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,
|
||||
uint8_t tag,
|
||||
const __FlashStringHelper * name,
|
||||
const uint8_t device_type,
|
||||
const __FlashStringHelper * entity,
|
||||
const uint8_t uom);
|
||||
static void publish_ha_sensor_config(uint8_t type,
|
||||
uint8_t tag,
|
||||
const __FlashStringHelper * name,
|
||||
const uint8_t device_type,
|
||||
const __FlashStringHelper * entity,
|
||||
const uint8_t uom,
|
||||
const bool remove,
|
||||
const bool create_device_config,
|
||||
const bool has_cmd,
|
||||
const __FlashStringHelper * const * options,
|
||||
uint8_t options_size,
|
||||
const int16_t dv_set_min,
|
||||
const int16_t dv_set_max,
|
||||
const JsonObject & dev_json);
|
||||
|
||||
static void publish_system_ha_sensor_config(uint8_t type, const __FlashStringHelper * name, const __FlashStringHelper * entity, const uint8_t uom);
|
||||
|
||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
|
||||
static void show_mqtt(uuid::console::Shell & shell);
|
||||
@@ -165,11 +157,19 @@ class Mqtt {
|
||||
return mqtt_base_;
|
||||
}
|
||||
|
||||
// returns the discovery MQTT topic prefix and adds a /
|
||||
static std::string discovery_prefix() {
|
||||
if (discovery_prefix_.empty()) {
|
||||
return std::string{};
|
||||
}
|
||||
return discovery_prefix_ + "/";
|
||||
}
|
||||
|
||||
static void base(const char * base) {
|
||||
mqtt_base_ = base;
|
||||
}
|
||||
|
||||
static uint16_t publish_count() {
|
||||
static uint32_t publish_count() {
|
||||
return mqtt_message_id_;
|
||||
}
|
||||
|
||||
@@ -179,21 +179,25 @@ class Mqtt {
|
||||
|
||||
static void reset_mqtt();
|
||||
|
||||
static uint8_t ha_climate_format() {
|
||||
return ha_climate_format_;
|
||||
}
|
||||
|
||||
// nested_format is 1 if nested, otherwise 2 for single topics
|
||||
static uint8_t nested_format() {
|
||||
return nested_format_;
|
||||
}
|
||||
|
||||
static void nested_format(uint8_t nested_format) {
|
||||
nested_format_ = nested_format;
|
||||
static bool is_nested() {
|
||||
return nested_format_ == 1;
|
||||
}
|
||||
|
||||
static void ha_climate_format(uint8_t ha_climate_format) {
|
||||
ha_climate_format_ = ha_climate_format;
|
||||
static bool publish_single() {
|
||||
return publish_single_;
|
||||
}
|
||||
|
||||
static void publish_single(bool publish_single) {
|
||||
publish_single_ = publish_single;
|
||||
}
|
||||
|
||||
static void nested_format(uint8_t nested_format) {
|
||||
nested_format_ = nested_format;
|
||||
}
|
||||
|
||||
static bool ha_enabled() {
|
||||
@@ -227,7 +231,7 @@ class Mqtt {
|
||||
static const std::string tag_to_topic(uint8_t device_type, uint8_t tag);
|
||||
|
||||
struct QueuedMqttMessage {
|
||||
const uint16_t id_;
|
||||
const uint32_t id_;
|
||||
const std::shared_ptr<const MqttMessage> content_;
|
||||
uint8_t retry_count_;
|
||||
uint16_t packet_id_;
|
||||
@@ -247,7 +251,7 @@ class Mqtt {
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static AsyncMqttClient * mqttClient_;
|
||||
static uint16_t mqtt_message_id_;
|
||||
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
|
||||
@@ -285,7 +289,7 @@ class Mqtt {
|
||||
|
||||
static bool connecting_;
|
||||
static bool initialized_;
|
||||
static uint16_t mqtt_publish_fails_;
|
||||
static uint32_t mqtt_publish_fails_;
|
||||
static uint8_t connectcount_;
|
||||
|
||||
// settings, copied over
|
||||
@@ -300,9 +304,10 @@ class Mqtt {
|
||||
static uint32_t publish_time_other_;
|
||||
static uint32_t publish_time_sensor_;
|
||||
static bool mqtt_enabled_;
|
||||
static uint8_t ha_climate_format_;
|
||||
static bool ha_enabled_;
|
||||
static uint8_t nested_format_;
|
||||
static std::string discovery_prefix_;
|
||||
static bool publish_single_;
|
||||
static bool send_response_;
|
||||
};
|
||||
|
||||
|
||||
@@ -80,15 +80,6 @@ void Shower::loop() {
|
||||
LOG_DEBUG(F("[Shower] finished with duration %d"), duration_);
|
||||
}
|
||||
}
|
||||
#if defined(EMSESP_DEBUG)
|
||||
else {
|
||||
if (shower_state_) {
|
||||
Mqtt::publish("message", "shower state is ON");
|
||||
} else {
|
||||
Mqtt::publish("message", "shower state is OFF");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// reset everything
|
||||
timer_start_ = 0;
|
||||
@@ -133,9 +124,17 @@ void Shower::shower_alert_start() {
|
||||
void Shower::publish_shower_data() {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
|
||||
char result[10];
|
||||
doc["shower_timer"] = Helpers::render_boolean(result, shower_timer_);
|
||||
doc["shower_alert"] = Helpers::render_boolean(result, shower_alert_);
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
doc["shower_timer"] = shower_timer_;
|
||||
doc["shower_alert"] = shower_alert_;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
doc["shower_timer"] = shower_timer_ ? 1 : 0;
|
||||
doc["shower_alert"] = shower_alert_ ? 1 : 0;
|
||||
} else {
|
||||
char result[10];
|
||||
doc["shower_timer"] = Helpers::render_boolean(result, shower_timer_);
|
||||
doc["shower_alert"] = Helpers::render_boolean(result, shower_alert_);
|
||||
}
|
||||
|
||||
// only publish shower duration if there is a value
|
||||
if (duration_ > SHOWER_MIN_DURATION) {
|
||||
@@ -165,6 +164,7 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
}
|
||||
old_shower_state_ = shower_state_; // copy current state
|
||||
|
||||
// always publish as a string
|
||||
char s[7];
|
||||
Mqtt::publish(F("shower_active"), Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
|
||||
|
||||
@@ -175,13 +175,16 @@ void Shower::set_shower_state(bool state, bool force) {
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc;
|
||||
doc["name"] = FJSON("Shower Active");
|
||||
doc["uniq_id"] = FJSON("shower_active");
|
||||
doc["~"] = Mqtt::base(); // default ems-esp
|
||||
doc["~"] = Mqtt::base();
|
||||
doc["stat_t"] = FJSON("~/shower_active");
|
||||
|
||||
// always render boolean as strings for HA
|
||||
char result[10];
|
||||
doc[F("payload_on")] = Helpers::render_boolean(result, true);
|
||||
doc[F("payload_off")] = Helpers::render_boolean(result, false);
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
|
||||
JsonObject dev = doc.createNestedObject("dev");
|
||||
JsonArray ids = dev.createNestedArray("ids");
|
||||
ids.add("ems-esp");
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
608
src/system.cpp
608
src/system.cpp
@@ -54,21 +54,40 @@ bool System::restart_requested_ = false;
|
||||
|
||||
// send on/off to a gpio pin
|
||||
// value: true = HIGH, false = LOW
|
||||
// e.g. http://ems-esp/api?device=system&cmd=pin&data=1&id=2
|
||||
bool System::command_pin(const char * value, const int8_t id) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
|
||||
if (!is_valid_gpio(id)) {
|
||||
LOG_INFO(F("invalid GPIO number"));
|
||||
LOG_INFO(F("Invalid GPIO number"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool v = false;
|
||||
if (Helpers::value2bool(value, v)) {
|
||||
bool v = false;
|
||||
std::string v1 = {7, '\0'};
|
||||
int v2 = 0;
|
||||
|
||||
if (id == 25 && Helpers::value2number(value, v2)) {
|
||||
if (v2 >= 0 && v2 <= 255) {
|
||||
dacWrite(id, v2);
|
||||
return true;
|
||||
}
|
||||
} else if (Helpers::value2bool(value, v)) {
|
||||
pinMode(id, OUTPUT);
|
||||
digitalWrite(id, v);
|
||||
LOG_INFO(F("GPIO %d set to %s"), id, v ? "HIGH" : "LOW");
|
||||
return true;
|
||||
} else if (Helpers::value2string(value, v1)) {
|
||||
if (v1 == "input" || v1 == "in" || v1 == "-1") {
|
||||
pinMode(id, INPUT);
|
||||
v = digitalRead(id);
|
||||
LOG_INFO(F("GPIO %d set input, state %s"), id, v ? "HIGH" : "LOW");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(F("GPIO %d: invalid value"), id);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -80,7 +99,7 @@ bool System::command_send(const char * value, const int8_t id) {
|
||||
|
||||
// fetch device values
|
||||
bool System::command_fetch(const char * value, const int8_t id) {
|
||||
std::string value_s(14, '\0');
|
||||
std::string value_s;
|
||||
if (Helpers::value2string(value, value_s)) {
|
||||
if (value_s == "all") {
|
||||
LOG_INFO(F("Requesting data from EMS devices"));
|
||||
@@ -107,7 +126,7 @@ bool System::command_fetch(const char * value, const int8_t id) {
|
||||
|
||||
// mqtt publish
|
||||
bool System::command_publish(const char * value, const int8_t id) {
|
||||
std::string value_s(14, '\0');
|
||||
std::string value_s;
|
||||
if (Helpers::value2string(value, value_s)) {
|
||||
if (value_s == "ha") {
|
||||
EMSESP::publish_all(true); // includes HA
|
||||
@@ -126,16 +145,17 @@ bool System::command_publish(const char * value, const int8_t id) {
|
||||
EMSESP::publish_device_values(EMSdevice::DeviceType::MIXER);
|
||||
return true;
|
||||
} else if (value_s == "other") {
|
||||
EMSESP::publish_other_values();
|
||||
EMSESP::publish_other_values(); // switch and heat pump
|
||||
return true;
|
||||
} else if (value_s == read_flash_string(F_(dallassensor))) {
|
||||
} else if ((value_s == read_flash_string(F_(dallassensor))) || (value_s == read_flash_string(F_(analogsensor)))) {
|
||||
EMSESP::publish_sensor_values(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
EMSESP::publish_all(); // ignore value and id
|
||||
EMSESP::publish_all();
|
||||
LOG_INFO(F("Publishing all data to MQTT"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -163,6 +183,9 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
EMSESP::watch_id(0);
|
||||
}
|
||||
EMSESP::watch(w);
|
||||
if (Mqtt::publish_single()) {
|
||||
Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[w]).c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
uint16_t i = Helpers::hextoint(value);
|
||||
@@ -171,6 +194,12 @@ bool System::command_watch(const char * value, const int8_t id) {
|
||||
if (EMSESP::watch() == EMSESP::Watch::WATCH_OFF) {
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_ON);
|
||||
}
|
||||
if (Mqtt::publish_single()) {
|
||||
char s[10];
|
||||
snprintf(s, sizeof(s), "0x%04X", i);
|
||||
Mqtt::publish(F("system/watch"), s);
|
||||
// Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -188,7 +217,7 @@ void System::system_restart() {
|
||||
|
||||
// saves all settings
|
||||
void System::wifi_reconnect() {
|
||||
LOG_INFO(F("Wifi reconnecting..."));
|
||||
LOG_INFO(F("WiFi reconnecting..."));
|
||||
Shell::loop_all();
|
||||
EMSESP::console_.loop();
|
||||
delay(1000); // wait a second
|
||||
@@ -196,7 +225,6 @@ void System::wifi_reconnect() {
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->callUpdateHandlers("local"); // in case we've changed ssid or password
|
||||
}
|
||||
|
||||
// format fs
|
||||
// format the FS. Wipes everything.
|
||||
void System::format(uuid::console::Shell & shell) {
|
||||
auto msg = F("Formatting file system. This will reset all settings to their defaults");
|
||||
@@ -234,7 +262,7 @@ void System::syslog_start() {
|
||||
syslog_.hostname(hostname().c_str());
|
||||
|
||||
// register the command
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog_level), System::command_syslog_level, F("changes syslog level"), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, F("change the syslog level"), CommandFlag::ADMIN_ONLY);
|
||||
|
||||
} else if (was_enabled) {
|
||||
// in case service is still running, this flushes the queue
|
||||
@@ -244,30 +272,50 @@ void System::syslog_start() {
|
||||
syslog_.mark_interval(0);
|
||||
syslog_.destination("");
|
||||
}
|
||||
|
||||
if (Mqtt::publish_single()) {
|
||||
Mqtt::publish(F("system/syslog"), syslog_enabled_ ? read_flash_string(FL_(enum_syslog_level)[syslog_level_ + 1]).c_str() : "off");
|
||||
if (EMSESP::watch_id() == 0 || EMSESP::watch() == 0) {
|
||||
Mqtt::publish(F("system/watch"), read_flash_string(FL_(enum_watch)[EMSESP::watch()]).c_str());
|
||||
} else {
|
||||
char s[10];
|
||||
snprintf(s, sizeof(s), "0x%04X", EMSESP::watch_id());
|
||||
Mqtt::publish(F("system/watch"), s);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// read all the settings except syslog from the config files and store locally
|
||||
// read some specific system settings to store locally for faster access
|
||||
void System::get_settings() {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) {
|
||||
// Button
|
||||
pbutton_gpio_ = settings.pbutton_gpio;
|
||||
|
||||
// ADC
|
||||
pbutton_gpio_ = settings.pbutton_gpio;
|
||||
analog_enabled_ = settings.analog_enabled;
|
||||
low_clock_ = settings.low_clock;
|
||||
hide_led_ = settings.hide_led;
|
||||
led_gpio_ = settings.led_gpio;
|
||||
board_profile_ = settings.board_profile;
|
||||
telnet_enabled_ = settings.telnet_enabled;
|
||||
|
||||
// Sysclock
|
||||
low_clock_ = settings.low_clock;
|
||||
rx_gpio_ = settings.rx_gpio;
|
||||
tx_gpio_ = settings.tx_gpio;
|
||||
dallas_gpio_ = settings.dallas_gpio;
|
||||
|
||||
// LED
|
||||
hide_led_ = settings.hide_led;
|
||||
led_gpio_ = settings.led_gpio;
|
||||
syslog_enabled_ = settings.syslog_enabled;
|
||||
syslog_level_ = settings.syslog_level;
|
||||
syslog_mark_interval_ = settings.syslog_mark_interval;
|
||||
syslog_host_ = settings.syslog_host;
|
||||
syslog_port_ = settings.syslog_port;
|
||||
|
||||
// Board profile
|
||||
board_profile_ = settings.board_profile;
|
||||
fahrenheit_ = settings.fahrenheit;
|
||||
bool_format_ = settings.bool_format;
|
||||
enum_format_ = settings.enum_format;
|
||||
readonly_mode_ = settings.readonly_mode;
|
||||
|
||||
// Ethernet PHY
|
||||
phy_type_ = settings.phy_type;
|
||||
phy_type_ = settings.phy_type;
|
||||
eth_power_ = settings.eth_power;
|
||||
eth_phy_addr_ = settings.eth_phy_addr;
|
||||
eth_clock_mode_ = settings.eth_clock_mode;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -328,7 +376,7 @@ void System::start(uint32_t heap_start) {
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// disable bluetooth module
|
||||
periph_module_disable(PERIPH_BT_MODULE);
|
||||
// periph_module_disable(PERIPH_BT_MODULE);
|
||||
if (low_clock_) {
|
||||
setCpuFrequencyMhz(160);
|
||||
}
|
||||
@@ -336,12 +384,10 @@ void System::start(uint32_t heap_start) {
|
||||
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
hostname(networkSettings.hostname.c_str()); // sets the hostname
|
||||
LOG_INFO(F("System name: %s"), hostname().c_str());
|
||||
});
|
||||
|
||||
commands_init(); // console & api commands
|
||||
led_init(false); // init LED
|
||||
adc_init(false); // analog ADC
|
||||
button_init(false); // the special button
|
||||
network_init(false); // network
|
||||
syslog_start(); // start Syslog
|
||||
@@ -349,21 +395,6 @@ void System::start(uint32_t heap_start) {
|
||||
EMSESP::init_uart(); // start UART
|
||||
}
|
||||
|
||||
// adc and bluetooth
|
||||
void System::adc_init(bool refresh) {
|
||||
if (refresh) {
|
||||
get_settings();
|
||||
}
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// disable ADC
|
||||
/*
|
||||
if (!analog_enabled_) {
|
||||
adc_power_release(); // turn off ADC to save power if not needed
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
// button single click
|
||||
void System::button_OnClick(PButton & b) {
|
||||
LOG_DEBUG(F("Button pressed - single click"));
|
||||
@@ -403,9 +434,9 @@ void System::button_init(bool refresh) {
|
||||
|
||||
if (is_valid_gpio(pbutton_gpio_)) {
|
||||
if (!myPButton_.init(pbutton_gpio_, HIGH)) {
|
||||
LOG_INFO(F("Multi-functional button not detected"));
|
||||
LOG_DEBUG(F("Multi-functional button not detected"));
|
||||
} else {
|
||||
LOG_INFO(F("Multi-functional button enabled"));
|
||||
LOG_DEBUG(F("Multi-functional button enabled"));
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(F("Invalid button GPIO. Check config."));
|
||||
@@ -424,8 +455,8 @@ void System::led_init(bool refresh) {
|
||||
}
|
||||
|
||||
if ((led_gpio_ != 0) && is_valid_gpio(led_gpio_)) {
|
||||
pinMode(led_gpio_, OUTPUT); // 0 means disabled
|
||||
digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
|
||||
pinMode(led_gpio_, OUTPUT); // 0 means disabled
|
||||
digitalWrite(led_gpio_, !LED_ON); // start with LED off
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,9 +493,6 @@ void System::loop() {
|
||||
|
||||
led_monitor(); // check status and report back using the LED
|
||||
system_check(); // check system health
|
||||
if (analog_enabled_) {
|
||||
measure_analog();
|
||||
}
|
||||
|
||||
// send out heartbeat
|
||||
uint32_t currentMillis = uuid::get_uptime();
|
||||
@@ -509,29 +537,30 @@ bool System::heartbeat_json(JsonObject & output) {
|
||||
output["bus_status"] = FJSON("disconnected");
|
||||
}
|
||||
|
||||
output["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
|
||||
output["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
output["uptime_sec"] = uuid::get_uptime_sec();
|
||||
output["rxreceived"] = EMSESP::rxservice_.telegram_count();
|
||||
output["rxfails"] = EMSESP::rxservice_.telegram_error_count();
|
||||
output["txreads"] = EMSESP::txservice_.telegram_read_count();
|
||||
output["txwrites"] = EMSESP::txservice_.telegram_write_count();
|
||||
output["txfails"] = EMSESP::txservice_.telegram_fail_count();
|
||||
output["txfails"] = EMSESP::txservice_.telegram_read_fail_count() + EMSESP::txservice_.telegram_write_fail_count();
|
||||
|
||||
if (Mqtt::enabled()) {
|
||||
output["mqttfails"] = Mqtt::publish_fails();
|
||||
output["mqttfails"] = Mqtt::publish_fails();
|
||||
}
|
||||
if (EMSESP::dallas_enabled()) {
|
||||
output["dallasfails"] = EMSESP::sensor_fails();
|
||||
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();
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
output["freemem"] = ESP.getFreeHeap() / 1000L; // kilobytes
|
||||
#endif
|
||||
|
||||
if (analog_enabled_) {
|
||||
output["adc"] = analog_;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!ethernet_connected_) {
|
||||
int8_t rssi = WiFi.RSSI();
|
||||
@@ -550,43 +579,14 @@ void System::send_heartbeat() {
|
||||
return;
|
||||
}
|
||||
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_SMALL> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> doc;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
|
||||
if (heartbeat_json(json)) {
|
||||
Mqtt::publish(F_(heartbeat), doc.as<JsonObject>()); // send to MQTT with retain off. This will add to MQTT queue.
|
||||
Mqtt::publish(F_(heartbeat), json); // send to MQTT with retain off. This will add to MQTT queue.
|
||||
}
|
||||
}
|
||||
|
||||
// measure and moving average adc
|
||||
void System::measure_analog() {
|
||||
static uint32_t measure_last_ = 0;
|
||||
|
||||
if (!measure_last_ || (uint32_t)(uuid::get_uptime() - measure_last_) >= SYSTEM_MEASURE_ANALOG_INTERVAL) {
|
||||
measure_last_ = uuid::get_uptime();
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
uint16_t a = 0;
|
||||
#else
|
||||
uint16_t a = analogReadMilliVolts(ADC1_CHANNEL_0_GPIO_NUM);
|
||||
#endif
|
||||
static uint32_t sum_ = 0;
|
||||
|
||||
if (!analog_) { // init first time
|
||||
analog_ = a;
|
||||
sum_ = a * 512;
|
||||
} else { // simple moving average filter
|
||||
sum_ = (sum_ * 511) / 512 + a;
|
||||
analog_ = sum_ / 512;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sets rate of led flash
|
||||
void System::set_led_speed(uint32_t speed) {
|
||||
led_flash_speed_ = speed;
|
||||
led_monitor();
|
||||
}
|
||||
|
||||
// initializes network
|
||||
void System::network_init(bool refresh) {
|
||||
if (refresh) {
|
||||
@@ -600,72 +600,57 @@ void System::network_init(bool refresh) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t phy_addr; // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
|
||||
int power; // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
|
||||
int mdc; // Pin# of the I²C clock signal for the Ethernet PHY
|
||||
int mdio; // Pin# of the I²C IO signal for the Ethernet PHY
|
||||
eth_phy_type_t type; // Type of the Ethernet PHY (LAN8720 or TLK110)
|
||||
eth_clock_mode_t clock_mode; // ETH_CLOCK_GPIO0_IN or ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT for 50Hz inverted clock
|
||||
|
||||
if (phy_type_ == PHY_type::PHY_TYPE_LAN8720) {
|
||||
phy_addr = 1;
|
||||
power = 16;
|
||||
mdc = 23;
|
||||
mdio = 18;
|
||||
type = ETH_PHY_LAN8720;
|
||||
clock_mode = ETH_CLOCK_GPIO0_IN;
|
||||
} else if (phy_type_ == PHY_type::PHY_TYPE_TLK110) {
|
||||
phy_addr = 31;
|
||||
power = -1;
|
||||
mdc = 23;
|
||||
mdio = 18;
|
||||
type = ETH_PHY_TLK110;
|
||||
clock_mode = ETH_CLOCK_GPIO0_IN;
|
||||
} else {
|
||||
return; // no valid profile
|
||||
}
|
||||
|
||||
// special case for Olimex ESP32-EVB (LAN8720) (different power and phy_addr)
|
||||
if (board_profile_.equals("OLIMEX")) {
|
||||
phy_addr = 0;
|
||||
power = -1;
|
||||
mdc = 23;
|
||||
mdio = 18;
|
||||
type = ETH_PHY_LAN8720;
|
||||
clock_mode = ETH_CLOCK_GPIO0_IN;
|
||||
}
|
||||
// configure Ethernet
|
||||
int mdc = 23; // Pin# of the I²C clock signal for the Ethernet PHY - hardcoded
|
||||
int mdio = 18; // Pin# of the I²C IO signal for the Ethernet PHY - hardcoded
|
||||
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
|
||||
eth_clock_mode_t clock_mode = (eth_clock_mode_t)eth_clock_mode_;
|
||||
|
||||
ETH.begin(phy_addr, power, mdc, mdio, type, clock_mode);
|
||||
}
|
||||
|
||||
// check health of system, done every few seconds
|
||||
// check health of system, done every 5 seconds
|
||||
void System::system_check() {
|
||||
if (!last_system_check_ || ((uint32_t)(uuid::get_uptime() - last_system_check_) >= SYSTEM_CHECK_FREQUENCY)) {
|
||||
last_system_check_ = uuid::get_uptime();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
// check if we have a valid network connection
|
||||
if (!ethernet_connected() && (WiFi.status() != WL_CONNECTED)) {
|
||||
set_led_speed(LED_WARNING_BLINK_FAST);
|
||||
system_healthy_ = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// not healthy if bus not connected
|
||||
if (!EMSbus::bus_connected()) {
|
||||
if (system_healthy_) {
|
||||
LOG_ERROR(F("Error: No connection to the EMS bus"));
|
||||
}
|
||||
system_healthy_ = false;
|
||||
set_led_speed(LED_WARNING_BLINK); // flash every 1/2 second from now on
|
||||
healthcheck_ |= HEALTHCHECK_NO_NETWORK;
|
||||
} else {
|
||||
// if it was unhealthy but now we're better, make sure the LED is solid again cos we've been healed
|
||||
if (!system_healthy_) {
|
||||
system_healthy_ = true;
|
||||
send_heartbeat();
|
||||
healthcheck_ &= ~HEALTHCHECK_NO_NETWORK;
|
||||
}
|
||||
|
||||
// check if we have a bus connection
|
||||
if (!EMSbus::bus_connected()) {
|
||||
healthcheck_ |= HEALTHCHECK_NO_BUS;
|
||||
} else {
|
||||
healthcheck_ &= ~HEALTHCHECK_NO_BUS;
|
||||
}
|
||||
|
||||
// see if the healthcheck state has changed
|
||||
static uint8_t last_healthcheck_ = 0;
|
||||
if (healthcheck_ != last_healthcheck_) {
|
||||
last_healthcheck_ = healthcheck_;
|
||||
// see if we're better now
|
||||
if (healthcheck_ == 0) {
|
||||
// everything is healthy, show LED permanently on or off depending on setting
|
||||
if (led_gpio_) {
|
||||
digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
|
||||
}
|
||||
send_heartbeat();
|
||||
} else {
|
||||
// turn off LED so we're ready to the flashes
|
||||
if (led_gpio_) {
|
||||
digitalWrite(led_gpio_, !LED_ON);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -676,45 +661,94 @@ void System::commands_init() {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM,
|
||||
F_(pin),
|
||||
System::command_pin,
|
||||
F("sets a GPIO on/off"),
|
||||
F("set a GPIO on/off"),
|
||||
CommandFlag::MQTT_SUB_FLAG_NOSUB | CommandFlag::ADMIN_ONLY); // dont create a MQTT topic for this
|
||||
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("sends a telegram"), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refreshes all EMS values"), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restarts EMS-ESP"), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(send), System::command_send, F("send a telegram"), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(fetch), System::command_fetch, F("refresh all EMS values"), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(restart), System::command_restart, F("restart EMS-ESP"), CommandFlag::ADMIN_ONLY);
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(watch), System::command_watch, F("watch incoming telegrams"));
|
||||
// Command::add(EMSdevice::DeviceType::SYSTEM, F_(syslog), System::command_syslog_level, F("set syslog level"), CommandFlag::ADMIN_ONLY);
|
||||
|
||||
if (Mqtt::enabled()) {
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("forces a MQTT publish"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(publish), System::command_publish, F("force a MQTT publish"));
|
||||
}
|
||||
|
||||
// these commands will return data in JSON format
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(info), System::command_info, F("show system status"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("shows system settings"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("shows system commands"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(settings), System::command_settings, F("fetch system settings"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(customizations), System::command_customizations, F("fetch system customizations"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F_(commands), System::command_commands, F("fetch system commands"));
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("runs a specific test"));
|
||||
Command::add(EMSdevice::DeviceType::SYSTEM, F("test"), System::command_test, F("run a specific test"));
|
||||
#endif
|
||||
|
||||
// MQTT subscribe "ems-esp/system/#"
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::SYSTEM, "system/#", nullptr); // use empty function callback
|
||||
}
|
||||
|
||||
// flashes the LED
|
||||
// uses LED to show system health
|
||||
void System::led_monitor() {
|
||||
if (!led_gpio_) {
|
||||
return;
|
||||
// we only need to run the LED healthcheck if there are errors
|
||||
if (!healthcheck_) {
|
||||
return; // all good
|
||||
}
|
||||
|
||||
static uint32_t led_last_blink_ = 0;
|
||||
static uint32_t led_long_timer_ = 1; // 1 will kick it off immediately
|
||||
static uint32_t led_short_timer_ = 0;
|
||||
static uint8_t led_flash_step_ = 0; // 0 means we're not in the short flash timer
|
||||
auto current_time = uuid::get_uptime();
|
||||
|
||||
if (!led_last_blink_ || (uint32_t)(uuid::get_uptime() - led_last_blink_) >= led_flash_speed_) {
|
||||
led_last_blink_ = uuid::get_uptime();
|
||||
// first long pause before we start flashing
|
||||
if (led_long_timer_ && (uint32_t)(current_time - led_long_timer_) >= HEALTHCHECK_LED_LONG_DUARATION) {
|
||||
// Serial.println("starting the flash check");
|
||||
led_short_timer_ = current_time; // start the short timer
|
||||
led_long_timer_ = 0; // stop long timer
|
||||
led_flash_step_ = 1; // enable the short flash timer
|
||||
}
|
||||
|
||||
// if bus_not_connected or network not connected, start flashing
|
||||
if (!system_healthy_) {
|
||||
digitalWrite(led_gpio_, !digitalRead(led_gpio_));
|
||||
// the flash timer which starts after the long pause
|
||||
if (led_flash_step_ && (uint32_t)(current_time - led_short_timer_) >= HEALTHCHECK_LED_FLASH_DUARATION) {
|
||||
led_long_timer_ = 0; // stop the long timer
|
||||
led_short_timer_ = current_time;
|
||||
static bool led_on_ = false;
|
||||
|
||||
if (++led_flash_step_ == 8) {
|
||||
// reset the whole sequence
|
||||
// Serial.println("resetting flash check");
|
||||
led_long_timer_ = uuid::get_uptime();
|
||||
led_flash_step_ = 0;
|
||||
digitalWrite(led_gpio_, !LED_ON); // LED off
|
||||
} else if (led_flash_step_ % 2) {
|
||||
// handle the step events (on odd numbers 3,5,7,etc). see if we need to turn on a LED
|
||||
// 1 flash is the EMS bus is not connected
|
||||
// 2 flashes if the network (wifi or ethernet) is not connected
|
||||
// 3 flashes is both the bus and the network are not connected. Then you know you're truly f*cked.
|
||||
|
||||
if ((led_flash_step_ == 3)
|
||||
&& (((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK) || ((healthcheck_ & HEALTHCHECK_NO_BUS) == HEALTHCHECK_NO_BUS))) {
|
||||
led_on_ = true;
|
||||
}
|
||||
|
||||
if ((led_flash_step_ == 5) && ((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK)) {
|
||||
led_on_ = true;
|
||||
}
|
||||
|
||||
if ((led_flash_step_ == 7) && ((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK)
|
||||
&& ((healthcheck_ & HEALTHCHECK_NO_BUS) == HEALTHCHECK_NO_BUS)) {
|
||||
led_on_ = true;
|
||||
}
|
||||
|
||||
if (led_on_ && led_gpio_) {
|
||||
digitalWrite(led_gpio_, LED_ON);
|
||||
}
|
||||
} else {
|
||||
// turn the led off after the flash, on even number count
|
||||
if (led_on_ && led_gpio_) {
|
||||
digitalWrite(led_gpio_, !LED_ON);
|
||||
led_on_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,19 +795,19 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
|
||||
switch (WiFi.status()) {
|
||||
case WL_IDLE_STATUS:
|
||||
shell.printfln(F("WiFi: Idle"));
|
||||
shell.printfln(F("Network: Idle"));
|
||||
break;
|
||||
|
||||
case WL_NO_SSID_AVAIL:
|
||||
shell.printfln(F("WiFi: Network not found"));
|
||||
shell.printfln(F("Network: Network not found"));
|
||||
break;
|
||||
|
||||
case WL_SCAN_COMPLETED:
|
||||
shell.printfln(F("WiFi: Network scan complete"));
|
||||
shell.printfln(F("Network: Network scan complete"));
|
||||
break;
|
||||
|
||||
case WL_CONNECTED:
|
||||
shell.printfln(F("WiFi: Connected"));
|
||||
shell.printfln(F("Network: connected"));
|
||||
shell.printfln(F("SSID: %s"), WiFi.SSID().c_str());
|
||||
shell.printfln(F("BSSID: %s"), WiFi.BSSIDstr().c_str());
|
||||
shell.printfln(F("RSSI: %d dBm (%d %%)"), WiFi.RSSI(), wifi_quality(WiFi.RSSI()));
|
||||
@@ -788,28 +822,28 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
break;
|
||||
|
||||
case WL_CONNECT_FAILED:
|
||||
shell.printfln(F("WiFi: Connection failed"));
|
||||
shell.printfln(F("WiFi Network: Connection failed"));
|
||||
break;
|
||||
|
||||
case WL_CONNECTION_LOST:
|
||||
shell.printfln(F("WiFi: Connection lost"));
|
||||
shell.printfln(F("WiFi Network: Connection lost"));
|
||||
break;
|
||||
|
||||
case WL_DISCONNECTED:
|
||||
shell.printfln(F("WiFi: Disconnected"));
|
||||
shell.printfln(F("WiFi Network: Disconnected"));
|
||||
break;
|
||||
|
||||
case WL_NO_SHIELD:
|
||||
default:
|
||||
shell.printfln(F("WiFi: Unknown"));
|
||||
shell.printfln(F("WiFi Network: Unknown"));
|
||||
break;
|
||||
}
|
||||
|
||||
shell.println();
|
||||
|
||||
// show Ethernet
|
||||
// show Ethernet if connected
|
||||
if (ethernet_connected_) {
|
||||
shell.printfln(F("Ethernet: Connected"));
|
||||
shell.printfln(F("Wired Network: connected"));
|
||||
shell.printfln(F("MAC address: %s"), ETH.macAddress().c_str());
|
||||
shell.printfln(F("Hostname: %s"), ETH.getHostname());
|
||||
shell.printfln(F("IPv4 address: %s/%s"), uuid::printable_to_string(ETH.localIP()).c_str(), uuid::printable_to_string(ETH.subnetMask()).c_str());
|
||||
@@ -818,8 +852,6 @@ void System::show_system(uuid::console::Shell & shell) {
|
||||
if (ETH.localIPv6().toString() != "0000:0000:0000:0000:0000:0000:0000:0000") {
|
||||
shell.printfln(F("IPv6 address: %s"), uuid::printable_to_string(ETH.localIPv6()).c_str());
|
||||
}
|
||||
} else {
|
||||
shell.printfln(F("Ethernet: disconnected"));
|
||||
}
|
||||
|
||||
shell.println();
|
||||
@@ -856,18 +888,20 @@ bool System::command_commands(const char * value, const int8_t id, JsonObject &
|
||||
// export all settings to JSON text
|
||||
// http://ems-esp/api/system/settings
|
||||
// value and id are ignored
|
||||
// note: ssid and passwords are excluded
|
||||
bool System::command_settings(const char * value, const int8_t id, JsonObject & output) {
|
||||
JsonObject node;
|
||||
output["label"] = "settings";
|
||||
|
||||
node = output.createNestedObject("System");
|
||||
JsonObject node = output.createNestedObject("System");
|
||||
node["version"] = EMSESP_APP_VERSION;
|
||||
|
||||
// hide ssid from this list
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & settings) {
|
||||
node = output.createNestedObject("Network");
|
||||
node["hostname"] = settings.hostname;
|
||||
node["static_ip_config"] = settings.staticIPConfig;
|
||||
node["enableIPv6"] = settings.enableIPv6;
|
||||
node["low_bandwidth"] = settings.bandwidth20;
|
||||
node["disable_sleep"] = settings.nosleep;
|
||||
JsonUtils::writeIP(node, "local_ip", settings.localIP);
|
||||
JsonUtils::writeIP(node, "gateway_ip", settings.gatewayIP);
|
||||
JsonUtils::writeIP(node, "subnet_mask", settings.subnetMask);
|
||||
@@ -878,35 +912,41 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::esp8266React.getAPSettingsService()->read([&](APSettings & settings) {
|
||||
node = output.createNestedObject("AP");
|
||||
node["provision_mode"] = settings.provisionMode;
|
||||
const char * pM[] = {"always", "disconnected", "never"};
|
||||
node["provision_mode"] = pM[settings.provisionMode];
|
||||
node["security"] = settings.password.length() ? "wpa2" : "open";
|
||||
node["ssid"] = settings.ssid;
|
||||
node["local_ip"] = settings.localIP.toString();
|
||||
node["gateway_ip"] = settings.gatewayIP.toString();
|
||||
node["subnet_mask"] = settings.subnetMask.toString();
|
||||
node["channel"] = settings.channel;
|
||||
node["ssid_hidden"] = settings.ssidHidden;
|
||||
node["max_clients"] = settings.maxClients;
|
||||
});
|
||||
#endif
|
||||
|
||||
EMSESP::esp8266React.getMqttSettingsService()->read([&](MqttSettings & settings) {
|
||||
node = output.createNestedObject("MQTT");
|
||||
node["enabled"] = settings.enabled;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
node["host"] = settings.host;
|
||||
node["port"] = settings.port;
|
||||
node["username"] = settings.username;
|
||||
node["client_id"] = settings.clientId;
|
||||
node["keep_alive"] = settings.keepAlive;
|
||||
node["clean_session"] = settings.cleanSession;
|
||||
#endif
|
||||
node = output.createNestedObject("MQTT");
|
||||
node["enabled"] = settings.enabled;
|
||||
node["host"] = settings.host;
|
||||
node["port"] = settings.port;
|
||||
node["username"] = settings.username;
|
||||
node["client_id"] = settings.clientId;
|
||||
node["keep_alive"] = settings.keepAlive;
|
||||
node["clean_session"] = settings.cleanSession;
|
||||
node["base"] = settings.base;
|
||||
node["discovery_prefix"] = settings.discovery_prefix;
|
||||
node["nested_format"] = settings.nested_format;
|
||||
node["ha_enabled"] = settings.ha_enabled;
|
||||
node["mqtt_qos"] = settings.mqtt_qos;
|
||||
node["mqtt_retain"] = settings.mqtt_retain;
|
||||
node["publish_time_boiler"] = settings.publish_time_boiler;
|
||||
node["publish_time_thermostat"] = settings.publish_time_thermostat;
|
||||
node["publish_time_solar"] = settings.publish_time_solar;
|
||||
node["publish_time_mixer"] = settings.publish_time_mixer;
|
||||
node["publish_time_other"] = settings.publish_time_other;
|
||||
node["publish_time_sensor"] = settings.publish_time_sensor;
|
||||
node["ha_climate_format"] = settings.ha_climate_format;
|
||||
node["ha_enabled"] = settings.ha_enabled;
|
||||
node["mqtt_qos"] = settings.mqtt_qos;
|
||||
node["mqtt_retain"] = settings.mqtt_retain;
|
||||
node["publish_single"] = settings.publish_single;
|
||||
node["send_response"] = settings.send_response;
|
||||
});
|
||||
|
||||
@@ -949,15 +989,73 @@ bool System::command_settings(const char * value, const int8_t id, JsonObject &
|
||||
node["dallas_gpio"] = settings.dallas_gpio;
|
||||
node["pbutton_gpio"] = settings.pbutton_gpio;
|
||||
node["led_gpio"] = settings.led_gpio;
|
||||
node["phy_type"] = settings.phy_type;
|
||||
|
||||
node["hide_led"] = settings.hide_led;
|
||||
node["notoken_api"] = settings.notoken_api;
|
||||
node["hide_led"] = settings.hide_led;
|
||||
node["notoken_api"] = settings.notoken_api;
|
||||
node["readonly_mode"] = settings.readonly_mode;
|
||||
|
||||
node["fahrenheit"] = settings.fahrenheit;
|
||||
node["dallas_parasite"] = settings.dallas_parasite;
|
||||
node["dallas_format"] = settings.dallas_format;
|
||||
node["bool_format"] = settings.bool_format;
|
||||
node["enum_format"] = settings.enum_format;
|
||||
node["analog_enabled"] = settings.analog_enabled;
|
||||
node["telnet_enabled"] = settings.telnet_enabled;
|
||||
|
||||
node["phy_type"] = settings.phy_type;
|
||||
node["eth_power"] = settings.eth_power;
|
||||
node["eth_phy_addr"] = settings.eth_phy_addr;
|
||||
node["eth_clock_mode"] = settings.eth_clock_mode;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// http://ems-esp/api/system/customizations
|
||||
bool System::command_customizations(const char * value, const int8_t id, JsonObject & output) {
|
||||
output["label"] = "customizations";
|
||||
|
||||
JsonObject node = output.createNestedObject("Customizations");
|
||||
|
||||
// hide ssid from this list
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
// sensors
|
||||
JsonArray sensorsJson = node.createNestedArray("sensors");
|
||||
for (const auto & sensor : settings.sensorCustomizations) {
|
||||
JsonObject sensorJson = sensorsJson.createNestedObject();
|
||||
sensorJson["id_str"] = sensor.id_str; // key, is
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
}
|
||||
|
||||
JsonArray analogJson = node.createNestedArray("analogs");
|
||||
for (const AnalogCustomization & sensor : settings.analogCustomizations) {
|
||||
JsonObject sensorJson = analogJson.createNestedObject();
|
||||
sensorJson["gpio"] = sensor.id;
|
||||
sensorJson["name"] = sensor.name;
|
||||
if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) {
|
||||
sensorJson["type"] = sensor.type;
|
||||
} else {
|
||||
sensorJson["type"] = FL_(enum_sensortype)[sensor.type];
|
||||
}
|
||||
if (sensor.type == AnalogSensor::AnalogType::ADC) {
|
||||
sensorJson["offset"] = sensor.offset;
|
||||
sensorJson["factor"] = sensor.factor;
|
||||
sensorJson["uom"] = EMSdevice::uom_to_string(sensor.uom);
|
||||
}
|
||||
}
|
||||
|
||||
// exclude entities
|
||||
JsonArray exclude_entitiesJson = node.createNestedArray("exclude_entities");
|
||||
for (const auto & entityCustomization : settings.entityCustomizations) {
|
||||
JsonObject entityJson = exclude_entitiesJson.createNestedObject();
|
||||
entityJson["product_id"] = entityCustomization.product_id;
|
||||
entityJson["device_id"] = entityCustomization.device_id;
|
||||
|
||||
JsonArray exclude_entityJson = entityJson.createNestedArray("entity_ids");
|
||||
for (uint8_t entity_id : entityCustomization.entity_ids) {
|
||||
exclude_entityJson.add(entity_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -981,7 +1079,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["reset reason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1);
|
||||
|
||||
if (EMSESP::dallas_enabled()) {
|
||||
node["Dallas sensors"] = EMSESP::sensor_devices().size();
|
||||
node["Temperature sensors"] = EMSESP::dallassensor_.no_sensors();
|
||||
}
|
||||
|
||||
if (EMSESP::analog_enabled()) {
|
||||
node["Analog sensors"] = EMSESP::analogsensor_.no_sensors();
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
@@ -1001,7 +1103,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["IPv6 address"] = uuid::printable_to_string(WiFi.localIPv6());
|
||||
}
|
||||
} else if (EMSESP::system_.ethernet_connected()) {
|
||||
node["connection"] = F("Ethernet");
|
||||
node["connection"] = F("Wired");
|
||||
node["hostname"] = ETH.getHostname();
|
||||
node["MAC"] = ETH.macAddress();
|
||||
node["IPv4 address"] = uuid::printable_to_string(ETH.localIP()) + "/" + uuid::printable_to_string(ETH.subnetMask());
|
||||
@@ -1021,7 +1123,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
node["bus status"] = (F("disconnected"));
|
||||
break;
|
||||
case EMSESP::BUS_STATUS_TX_ERRORS:
|
||||
node["bus status"] = (F("connected, instable tx"));
|
||||
node["bus status"] = (F("connected, tx issues - try a different tx-mode"));
|
||||
break;
|
||||
case EMSESP::BUS_STATUS_CONNECTED:
|
||||
default:
|
||||
@@ -1030,27 +1132,39 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
}
|
||||
|
||||
if (EMSESP::bus_status() != EMSESP::BUS_STATUS_OFFLINE) {
|
||||
node["bus protocol"] = EMSbus::is_ht3() ? F("HT3") : F("Buderus");
|
||||
node["telegrams received"] = EMSESP::rxservice_.telegram_count();
|
||||
node["read requests sent"] = EMSESP::txservice_.telegram_read_count();
|
||||
node["write requests sent"] = EMSESP::txservice_.telegram_write_count();
|
||||
node["incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count();
|
||||
node["tx fails"] = EMSESP::txservice_.telegram_fail_count();
|
||||
node["rx line quality"] = EMSESP::rxservice_.quality();
|
||||
node["tx line quality"] = EMSESP::txservice_.quality();
|
||||
node["bus protocol"] = EMSbus::is_ht3() ? F("HT3") : F("Buderus");
|
||||
node["bus telegrams received (rx)"] = EMSESP::rxservice_.telegram_count();
|
||||
node["bus reads (tx)"] = EMSESP::txservice_.telegram_read_count();
|
||||
node["bus writes (tx)"] = EMSESP::txservice_.telegram_write_count();
|
||||
node["bus incomplete telegrams"] = EMSESP::rxservice_.telegram_error_count();
|
||||
node["bus reads failed"] = EMSESP::txservice_.telegram_read_fail_count();
|
||||
node["bus writes failed"] = EMSESP::txservice_.telegram_write_fail_count();
|
||||
node["bus rx line quality"] = EMSESP::rxservice_.quality();
|
||||
node["bus tx line quality"] = (EMSESP::txservice_.read_quality() + EMSESP::txservice_.read_quality()) / 2;
|
||||
if (Mqtt::enabled()) {
|
||||
node["MQTT"] = Mqtt::connected() ? F_(connected) : F_(disconnected);
|
||||
node["MQTT status"] = Mqtt::connected() ? F_(connected) : F_(disconnected);
|
||||
node["MQTT publishes"] = Mqtt::publish_count();
|
||||
node["MQTT publish fails"] = Mqtt::publish_fails();
|
||||
}
|
||||
node["Temperature sensors"] = EMSESP::dallassensor_.no_sensors();
|
||||
if (EMSESP::dallas_enabled()) {
|
||||
node["Dallas reads"] = EMSESP::sensor_reads();
|
||||
node["Dallas fails"] = EMSESP::sensor_fails();
|
||||
node["Temperature sensor reads"] = EMSESP::dallassensor_.reads();
|
||||
node["Temperature sensor fails"] = EMSESP::dallassensor_.fails();
|
||||
}
|
||||
node["Analog sensors"] = EMSESP::analogsensor_.no_sensors();
|
||||
if (EMSESP::analog_enabled()) {
|
||||
node["Analog sensor reads"] = EMSESP::analogsensor_.reads();
|
||||
node["Analog sensor fails"] = EMSESP::analogsensor_.fails();
|
||||
}
|
||||
node["API calls"] = WebAPIService::api_count();
|
||||
node["API fails"] = WebAPIService::api_fails();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (EMSESP::system_.syslog_enabled_) {
|
||||
node["syslog IP"] = syslog_.ip();
|
||||
node["syslog active"] = syslog_.started();
|
||||
node["syslog_started"] = syslog_.started();
|
||||
node["syslog_level"] = FL_(enum_syslog_level)[syslog_.log_level() + 1];
|
||||
node["syslog_ip"] = syslog_.ip();
|
||||
node["syslog_queue"] = syslog_.queued();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1064,9 +1178,17 @@ bool System::command_info(const char * value, const int8_t id, JsonObject & outp
|
||||
obj["type"] = emsdevice->device_type_name();
|
||||
obj["name"] = emsdevice->to_string();
|
||||
char result[200];
|
||||
(void)emsdevice->show_telegram_handlers(result);
|
||||
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::RECEIVED);
|
||||
if (result[0] != '\0') {
|
||||
obj["handlers"] = result; // don't show hanlders if there aren't any
|
||||
obj["handlers_received"] = result; // don't show handlers if there aren't any
|
||||
}
|
||||
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::FETCHED);
|
||||
if (result[0] != '\0') {
|
||||
obj["handlers_fetched"] = result;
|
||||
}
|
||||
(void)emsdevice->show_telegram_handlers(result, EMSdevice::Handlers::PENDING);
|
||||
if (result[0] != '\0') {
|
||||
obj["handlers_pending"] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1084,29 +1206,55 @@ bool System::command_test(const char * value, const int8_t id) {
|
||||
#endif
|
||||
|
||||
// takes a board profile and populates a data array with GPIO configurations
|
||||
// data = led, dallas, rx, tx, button, phy_type
|
||||
// returns false if profile is not found
|
||||
bool System::load_board_profile(std::vector<uint8_t> & data, const std::string & board_profile) {
|
||||
//
|
||||
// data = led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
//
|
||||
// clock modes:
|
||||
// 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 cloc
|
||||
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}; // 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}; // BBQKees Gateway 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}; // 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}; // 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}; // 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}; // 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 Dallas)
|
||||
} 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 == "CUSTOM") {
|
||||
// send back current values
|
||||
data = {(int8_t)EMSESP::system_.led_gpio_,
|
||||
(int8_t)EMSESP::system_.dallas_gpio_,
|
||||
(int8_t)EMSESP::system_.rx_gpio_,
|
||||
(int8_t)EMSESP::system_.tx_gpio_,
|
||||
(int8_t)EMSESP::system_.pbutton_gpio_,
|
||||
(int8_t)EMSESP::system_.phy_type_,
|
||||
EMSESP::system_.eth_power_,
|
||||
(int8_t)EMSESP::system_.eth_phy_addr_,
|
||||
(int8_t)EMSESP::system_.eth_clock_mode_};
|
||||
} else {
|
||||
data = {EMSESP_DEFAULT_LED_GPIO,
|
||||
EMSESP_DEFAULT_DALLAS_GPIO,
|
||||
EMSESP_DEFAULT_RX_GPIO,
|
||||
EMSESP_DEFAULT_TX_GPIO,
|
||||
EMSESP_DEFAULT_PBUTTON_GPIO,
|
||||
EMSESP_DEFAULT_PHY_TYPE};
|
||||
return (board_profile == "CUSTOM");
|
||||
// unknown, use defaults
|
||||
data = {
|
||||
EMSESP_DEFAULT_LED_GPIO,
|
||||
EMSESP_DEFAULT_DALLAS_GPIO,
|
||||
EMSESP_DEFAULT_RX_GPIO,
|
||||
EMSESP_DEFAULT_TX_GPIO,
|
||||
EMSESP_DEFAULT_PBUTTON_GPIO,
|
||||
EMSESP_DEFAULT_PHY_TYPE,
|
||||
-1, // power
|
||||
0, // phy_addr,
|
||||
0 // clock_mode
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1147,7 +1295,7 @@ const std::string System::reset_reason(uint8_t cpu) {
|
||||
case 13:
|
||||
return ("RTC watch dog reset: CPU");
|
||||
case 14:
|
||||
return ("APP CPU reseted by PRO CPU");
|
||||
return ("APP CPU reset by PRO CPU");
|
||||
case 15:
|
||||
return ("Brownout reset");
|
||||
case 16:
|
||||
|
||||
107
src/system.h
107
src/system.h
@@ -28,7 +28,6 @@
|
||||
#include "telegram.h"
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include "driver/adc.h"
|
||||
#include <esp_wifi.h>
|
||||
#include <esp_bt.h>
|
||||
#include <ETH.h>
|
||||
@@ -63,6 +62,7 @@ class System {
|
||||
|
||||
static bool command_info(const char * value, const int8_t id, JsonObject & output);
|
||||
static bool command_settings(const char * value, const int8_t id, JsonObject & output);
|
||||
static bool command_customizations(const char * value, const int8_t id, JsonObject & output);
|
||||
static bool command_commands(const char * value, const int8_t id, JsonObject & output);
|
||||
|
||||
const std::string reset_reason(uint8_t cpu);
|
||||
@@ -80,13 +80,12 @@ class System {
|
||||
void send_heartbeat();
|
||||
|
||||
void led_init(bool refresh);
|
||||
void adc_init(bool refresh);
|
||||
void network_init(bool refresh);
|
||||
void button_init(bool refresh);
|
||||
void commands_init();
|
||||
|
||||
static bool is_valid_gpio(uint8_t pin);
|
||||
static bool load_board_profile(std::vector<uint8_t> & data, const std::string & board_profile);
|
||||
static bool load_board_profile(std::vector<int8_t> & data, const std::string & board_profile);
|
||||
|
||||
static void restart_requested(bool restart_requested) {
|
||||
restart_requested_ = restart_requested;
|
||||
@@ -96,12 +95,47 @@ class System {
|
||||
return restart_requested_;
|
||||
}
|
||||
|
||||
bool telnet_enabled() {
|
||||
return telnet_enabled_;
|
||||
}
|
||||
|
||||
bool analog_enabled() {
|
||||
return analog_enabled_;
|
||||
}
|
||||
|
||||
uint16_t analog() {
|
||||
return analog_;
|
||||
bool readonly_mode() {
|
||||
return readonly_mode_;
|
||||
}
|
||||
|
||||
void readonly_mode(bool readonly_mode) {
|
||||
readonly_mode_ = readonly_mode;
|
||||
}
|
||||
|
||||
uint8_t bool_format() {
|
||||
return bool_format_;
|
||||
}
|
||||
|
||||
// see default_settings.h
|
||||
// BOOL_FORMAT_ONOFF_STR = 1,
|
||||
// BOOL_FORMAT_ONOFF_STR_CAP = 2
|
||||
// BOOL_FORMAT_TRUEFALSE_STR = 3
|
||||
// BOOL_FORMAT_TRUEFALSE = 4
|
||||
// BOOL_FORMAT_10_STR = 5
|
||||
// BOOL_FORMAT_10 = 6
|
||||
void bool_format(uint8_t format) {
|
||||
bool_format_ = format;
|
||||
}
|
||||
|
||||
uint8_t enum_format() {
|
||||
return enum_format_;
|
||||
}
|
||||
|
||||
void enum_format(uint8_t format) {
|
||||
enum_format_ = format;
|
||||
}
|
||||
|
||||
std::string board_profile() {
|
||||
return std::string(board_profile_.c_str());
|
||||
}
|
||||
|
||||
std::string hostname() {
|
||||
@@ -128,6 +162,18 @@ class System {
|
||||
#endif
|
||||
}
|
||||
|
||||
void fahrenheit(bool b) {
|
||||
fahrenheit_ = b;
|
||||
}
|
||||
|
||||
bool fahrenheit() {
|
||||
return fahrenheit_;
|
||||
}
|
||||
|
||||
void healthcheck(uint8_t healthcheck) {
|
||||
healthcheck_ = healthcheck;
|
||||
}
|
||||
|
||||
void show_system(uuid::console::Shell & shell);
|
||||
void wifi_reconnect();
|
||||
void show_users(uuid::console::Shell & shell);
|
||||
@@ -148,46 +194,59 @@ class System {
|
||||
static constexpr uint32_t BUTTON_LongPressDelay = 750; // Hold period for a long press event (in ms)
|
||||
static constexpr uint32_t BUTTON_VLongPressDelay = 9000; // Hold period for a very long press event (in ms)
|
||||
|
||||
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // check every 5 seconds
|
||||
static constexpr uint32_t LED_WARNING_BLINK = 1000; // pulse to show no connection, 1 sec
|
||||
static constexpr uint32_t LED_WARNING_BLINK_FAST = 100; // flash quickly for boot up sequence
|
||||
static constexpr uint32_t SYSTEM_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min)
|
||||
static constexpr uint32_t SYSTEM_MEASURE_ANALOG_INTERVAL = 500;
|
||||
static constexpr uint8_t LED_ON = HIGH; // LED
|
||||
// healthcheck
|
||||
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // do a system check every 5 seconds
|
||||
static constexpr uint32_t HEALTHCHECK_LED_LONG_DUARATION = 1500;
|
||||
static constexpr uint32_t HEALTHCHECK_LED_FLASH_DUARATION = 150;
|
||||
static constexpr uint8_t HEALTHCHECK_NO_BUS = (1 << 0); // 1
|
||||
static constexpr uint8_t HEALTHCHECK_NO_NETWORK = (1 << 1); // 2
|
||||
static constexpr uint32_t SYSTEM_HEARTBEAT_INTERVAL = 60000; // in milliseconds, how often the MQTT heartbeat is sent (1 min)
|
||||
static constexpr uint8_t LED_ON = HIGH; // LED on
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static uuid::syslog::SyslogService syslog_;
|
||||
#endif
|
||||
|
||||
void led_monitor();
|
||||
void set_led_speed(uint32_t speed);
|
||||
void system_check();
|
||||
void measure_analog();
|
||||
|
||||
int8_t wifi_quality(int8_t dBm);
|
||||
|
||||
bool system_healthy_ = false;
|
||||
uint32_t led_flash_speed_ = LED_WARNING_BLINK_FAST; // default boot flashes quickly
|
||||
uint32_t last_heartbeat_ = 0;
|
||||
uint32_t last_system_check_ = 0;
|
||||
bool upload_status_ = false; // true if we're in the middle of a OTA firmware upload
|
||||
bool ethernet_connected_ = false;
|
||||
uint16_t analog_;
|
||||
uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection
|
||||
uint32_t last_heartbeat_ = 0;
|
||||
uint32_t last_system_check_ = 0;
|
||||
|
||||
// settings
|
||||
std::string hostname_ = "ems-esp";
|
||||
bool upload_status_ = false; // true if we're in the middle of a OTA firmware upload
|
||||
bool ethernet_connected_ = false;
|
||||
|
||||
// EMS-ESP settings
|
||||
// copies from WebSettings class in WebSettingsService.h
|
||||
std::string hostname_ = FACTORY_WIFI_HOSTNAME;
|
||||
bool hide_led_;
|
||||
uint8_t led_gpio_;
|
||||
bool syslog_enabled_ = false;
|
||||
bool analog_enabled_;
|
||||
bool low_clock_;
|
||||
String board_profile_;
|
||||
uint8_t pbutton_gpio_;
|
||||
uint8_t phy_type_;
|
||||
uint8_t rx_gpio_;
|
||||
uint8_t tx_gpio_;
|
||||
uint8_t dallas_gpio_;
|
||||
bool telnet_enabled_;
|
||||
bool syslog_enabled_;
|
||||
int8_t syslog_level_;
|
||||
uint32_t syslog_mark_interval_;
|
||||
String syslog_host_;
|
||||
uint16_t syslog_port_;
|
||||
bool fahrenheit_;
|
||||
uint8_t bool_format_;
|
||||
uint8_t enum_format_;
|
||||
bool readonly_mode_;
|
||||
|
||||
// ethernet
|
||||
uint8_t phy_type_;
|
||||
int8_t eth_power_;
|
||||
uint8_t eth_phy_addr_;
|
||||
uint8_t eth_clock_mode_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -37,6 +37,7 @@ const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E,
|
||||
0xF9, 0xFB, 0xFD, 0xFF, 0xF1, 0xF3, 0xF5, 0xF7, 0xE9, 0xEB, 0xED, 0xEF, 0xE1, 0xE3, 0xE5, 0xE7};
|
||||
|
||||
uint32_t EMSbus::last_bus_activity_ = 0; // timestamp of last time a valid Rx came in
|
||||
uint32_t EMSbus::bus_uptime_start_ = 0; // timestamp of when the bus was started
|
||||
bool EMSbus::bus_connected_ = false; // start assuming the bus hasn't been connected
|
||||
uint8_t EMSbus::ems_mask_ = EMS_MASK_UNSET; // unset so its triggered when booting, the its 0x00=buderus, 0x80=junker/ht3
|
||||
uint8_t EMSbus::ems_bus_id_ = EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
@@ -262,7 +263,7 @@ void TxService::start() {
|
||||
read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||
}
|
||||
|
||||
// sends a 1 byte poll which is our own device ID
|
||||
// sends a 1 byte poll which is our own deviceID
|
||||
void TxService::send_poll() {
|
||||
//LOG_DEBUG(F("Ack %02X"),ems_bus_id() ^ ems_mask());
|
||||
if (tx_mode()) {
|
||||
@@ -359,13 +360,18 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t length = message_p;
|
||||
|
||||
telegram_last_ = std::make_shared<Telegram>(*telegram); // make a copy of the telegram
|
||||
|
||||
uint8_t length = message_p;
|
||||
telegram_raw[length] = calculate_crc(telegram_raw, length); // generate and append CRC to the end
|
||||
length++; // add one since we want to now include the CRC
|
||||
|
||||
length++; // add one since we want to now include the CRC
|
||||
// if we're in simulation mode, don't send anything, just quit
|
||||
if (EMSESP::system_.readonly_mode() && (telegram->operation == Telegram::Operation::TX_WRITE)) {
|
||||
LOG_INFO(F("[readonly] Sending write Tx telegram: %s"), Helpers::data_to_hex(telegram_raw, length - 1).c_str());
|
||||
tx_state(Telegram::Operation::NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(F("Sending %s Tx [#%d], telegram: %s"),
|
||||
(telegram->operation == Telegram::Operation::TX_WRITE) ? F("write") : F("read"),
|
||||
@@ -373,12 +379,19 @@ void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
Helpers::data_to_hex(telegram_raw, length - 1).c_str()); // exclude the last CRC byte
|
||||
|
||||
set_post_send_query(tx_telegram.validateid_);
|
||||
// send the telegram to the UART Tx
|
||||
|
||||
//
|
||||
// this is the core send command to the UART
|
||||
//
|
||||
uint16_t status = EMSuart::transmit(telegram_raw, length);
|
||||
|
||||
if (status == EMS_TX_STATUS_ERR) {
|
||||
LOG_ERROR(F("Failed to transmit Tx via UART."));
|
||||
increment_telegram_fail_count(); // another Tx fail
|
||||
if (telegram->operation == Telegram::Operation::TX_READ) {
|
||||
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
|
||||
return;
|
||||
}
|
||||
@@ -577,9 +590,13 @@ void 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
|
||||
increment_telegram_fail_count(); // another Tx fail
|
||||
EMSESP::wait_validate(0); // do not wait for validation
|
||||
reset_retry_count(); // give up
|
||||
if (operation == Telegram::Operation::TX_READ) {
|
||||
increment_telegram_read_fail_count(); // another Tx fail
|
||||
} else {
|
||||
increment_telegram_write_fail_count(); // another Tx fail
|
||||
}
|
||||
EMSESP::wait_validate(0); // do not wait for validation
|
||||
|
||||
LOG_ERROR(F("Last Tx %s operation failed after %d retries. Ignoring request: %s"),
|
||||
(operation == Telegram::Operation::TX_WRITE) ? F("Write") : F("Read"),
|
||||
|
||||
104
src/telegram.h
104
src/telegram.h
@@ -34,7 +34,7 @@
|
||||
#include "helpers.h"
|
||||
|
||||
#define MAX_RX_TELEGRAMS 10 // size of Rx queue
|
||||
#define MAX_TX_TELEGRAMS 30 // size of Tx queue
|
||||
#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
|
||||
@@ -166,6 +166,7 @@ class EMSbus {
|
||||
ems_bus_id_ = ems_bus_id;
|
||||
}
|
||||
|
||||
// checks every 30 seconds if the EMS bus is still alive
|
||||
static bool bus_connected() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if ((uuid::get_uptime() - last_bus_activity_) > EMS_BUS_TIMEOUT) {
|
||||
@@ -179,8 +180,22 @@ class EMSbus {
|
||||
|
||||
// sets the flag for EMS bus connected
|
||||
static void last_bus_activity(uint32_t timestamp) {
|
||||
// record the first time we connected to the BUS, as this will be our uptime
|
||||
if (!last_bus_activity_) {
|
||||
bus_uptime_start_ = timestamp;
|
||||
}
|
||||
|
||||
last_bus_activity_ = timestamp;
|
||||
bus_connected_ = true;
|
||||
|
||||
bus_connected_ = true;
|
||||
}
|
||||
|
||||
// return bus uptime in seconds
|
||||
static uint32_t bus_uptime() {
|
||||
if (!bus_uptime_start_) {
|
||||
return 0; // not yet initialized
|
||||
}
|
||||
return (uint32_t)((uuid::get_uptime() - bus_uptime_start_) / 1000ULL);
|
||||
}
|
||||
|
||||
static uint8_t tx_state() {
|
||||
@@ -196,6 +211,7 @@ class EMSbus {
|
||||
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
|
||||
@@ -224,6 +240,7 @@ class RxService : public EMSbus {
|
||||
return telegram_error_count_;
|
||||
}
|
||||
|
||||
// returns a %
|
||||
uint8_t quality() const {
|
||||
if (telegram_error_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
@@ -308,49 +325,61 @@ class TxService : public EMSbus {
|
||||
uint32_t telegram_read_count() const {
|
||||
return telegram_read_count_;
|
||||
}
|
||||
uint32_t telegram_write_count() const {
|
||||
return telegram_write_count_;
|
||||
}
|
||||
|
||||
void telegram_read_count(uint8_t telegram_read_count) {
|
||||
void telegram_read_count(uint32_t telegram_read_count) {
|
||||
telegram_read_count_ = telegram_read_count;
|
||||
}
|
||||
|
||||
void telegram_write_count(uint32_t telegram_write_count) {
|
||||
telegram_write_count_ = telegram_write_count;
|
||||
}
|
||||
|
||||
void increment_telegram_read_count() {
|
||||
telegram_read_count_++;
|
||||
}
|
||||
|
||||
uint32_t telegram_fail_count() const {
|
||||
return telegram_fail_count_;
|
||||
}
|
||||
|
||||
void telegram_fail_count(uint8_t telegram_fail_count) {
|
||||
telegram_fail_count_ = telegram_fail_count;
|
||||
}
|
||||
|
||||
uint8_t quality() const {
|
||||
if (telegram_fail_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
}
|
||||
if (telegram_fail_count_ >= telegram_read_count_) {
|
||||
return 100;
|
||||
}
|
||||
return (100 - (uint8_t)(((float)telegram_fail_count_ / telegram_read_count_ * 100)));
|
||||
}
|
||||
|
||||
void increment_telegram_fail_count() {
|
||||
telegram_fail_count_++;
|
||||
}
|
||||
|
||||
uint32_t telegram_write_count() const {
|
||||
return telegram_write_count_;
|
||||
}
|
||||
|
||||
void telegram_write_count(uint8_t telegram_write_count) {
|
||||
telegram_write_count_ = telegram_write_count;
|
||||
}
|
||||
|
||||
void increment_telegram_write_count() {
|
||||
telegram_write_count_++;
|
||||
}
|
||||
|
||||
uint32_t telegram_read_fail_count() const {
|
||||
return telegram_read_fail_count_;
|
||||
}
|
||||
|
||||
uint32_t telegram_write_fail_count() const {
|
||||
return telegram_write_fail_count_;
|
||||
}
|
||||
|
||||
void telegram_fail_count(uint32_t telegram_fail_count) {
|
||||
telegram_read_fail_count_ = telegram_fail_count;
|
||||
telegram_write_fail_count_ = telegram_fail_count;
|
||||
}
|
||||
|
||||
uint8_t read_quality() const {
|
||||
if (telegram_read_fail_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
}
|
||||
return (100 - (uint8_t)((telegram_read_fail_count_ * 100 / (telegram_read_fail_count_ + telegram_read_count_))));
|
||||
}
|
||||
|
||||
uint8_t write_quality() const {
|
||||
if (telegram_write_fail_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
}
|
||||
return (100 - (uint8_t)((telegram_write_fail_count_ * 100 / (telegram_write_fail_count_ + telegram_write_count_))));
|
||||
}
|
||||
|
||||
void increment_telegram_read_fail_count() {
|
||||
telegram_read_fail_count_++;
|
||||
}
|
||||
|
||||
void increment_telegram_write_fail_count() {
|
||||
telegram_write_fail_count_++;
|
||||
}
|
||||
|
||||
struct QueuedTxTelegram {
|
||||
const uint16_t id_;
|
||||
const std::shared_ptr<const Telegram> telegram_;
|
||||
@@ -381,9 +410,10 @@ 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_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
|
||||
@@ -392,9 +422,7 @@ class TxService : public EMSbus {
|
||||
|
||||
uint8_t tx_telegram_id_ = 0; // queue counter
|
||||
|
||||
|
||||
void send_telegram(const QueuedTxTelegram & tx_telegram);
|
||||
// void send_telegram(const uint8_t * data, const uint8_t length);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -53,6 +53,38 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(command, "2thermostats") == 0) {
|
||||
EMSESP::logger().info(F("Testing with multiple thermostats..."));
|
||||
|
||||
add_device(0x08, 123); // GB072
|
||||
add_device(0x10, 158); // RC310
|
||||
add_device(0x18, 157); // Bosch CR100
|
||||
|
||||
// Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25)
|
||||
uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A,
|
||||
0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00});
|
||||
|
||||
// Boiler -> Thermostat, UBAParameterWW(0x33), telegram: 08 97 33 00 23 24 (#data=2)
|
||||
uart_telegram({0x08, 0x90, 0x33, 0x00, 0x23, 0x24});
|
||||
|
||||
// Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13)
|
||||
uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00});
|
||||
|
||||
// Thermostat 0x2A5 for HC1
|
||||
uart_telegram({0x10, 00, 0xFF, 00, 01, 0xA5, 0x80, 00, 01, 0x30, 0x28, 00, 0x30, 0x28, 01, 0x54,
|
||||
03, 03, 01, 01, 0x54, 02, 0xA8, 00, 00, 0x11, 01, 03, 0xFF, 0xFF, 00});
|
||||
|
||||
// RC300WWmode2(0x31D), data: 00 00 09 07
|
||||
uart_telegram({0x10, 00, 0xFF, 00, 02, 0x1D, 00, 00, 0x09, 0x07});
|
||||
|
||||
// 2nd thermostat
|
||||
// Thermostat RCPLUSStatusMessage_HC2(0x01A6)
|
||||
uart_telegram({0x98, 0x00, 0xFF, 0x00, 0x01, 0xA6, 0x00, 0xCF, 0x21, 0x2E, 0x00, 0x00, 0x2E, 0x24,
|
||||
0x03, 0x25, 0x03, 0x03, 0x01, 0x03, 0x25, 0x00, 0xC8, 0x00, 0x00, 0x11, 0x01, 0x03});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strcmp(command, "310") == 0) {
|
||||
EMSESP::logger().info(F("Adding a GB072/RC310 combo..."));
|
||||
|
||||
@@ -141,6 +173,8 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
|
||||
if (strcmp(command, "thermostat") == 0) {
|
||||
EMSESP::logger().info(F("Adding thermostat..."));
|
||||
Mqtt::nested_format(1); // use nested
|
||||
// Mqtt::nested_format(2); // single
|
||||
|
||||
add_device(0x10, 192); // FW120
|
||||
|
||||
@@ -189,14 +223,12 @@ bool Test::run_test(const char * command, int8_t id) {
|
||||
|
||||
// These next tests are run from the Console
|
||||
// using the test command
|
||||
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & data) {
|
||||
// switch to su
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
|
||||
// init stuff
|
||||
Mqtt::ha_enabled(true);
|
||||
EMSESP::dallassensor_.dallas_format(1);
|
||||
Mqtt::ha_climate_format(1);
|
||||
EMSESP::rxservice_.ems_mask(EMSbus::EMS_MASK_BUDERUS);
|
||||
EMSESP::watch(EMSESP::Watch::WATCH_RAW); // raw
|
||||
|
||||
@@ -334,7 +366,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
shell.printfln(F("Testing unknown2..."));
|
||||
|
||||
// simulate getting version information back from an unknown device
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x5A, 0x01, 0x02}); // product id is 90 which doesn't exist
|
||||
rx_telegram({0x09, 0x0B, 0x02, 0x00, 0x5A, 0x01, 0x02}); // productID is 90 which doesn't exist
|
||||
}
|
||||
|
||||
if (command == "gateway") {
|
||||
@@ -351,9 +383,19 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
shell.invoke_command("show mqtt");
|
||||
}
|
||||
|
||||
if (command == "2thermostats") {
|
||||
shell.printfln(F("Testing multiple thermostats..."));
|
||||
run_test("2thermostats");
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("show devices");
|
||||
}
|
||||
|
||||
if (command == "web") {
|
||||
shell.printfln(F("Testing Web..."));
|
||||
|
||||
Mqtt::enabled(false); // turn off mqtt
|
||||
Mqtt::ha_enabled(false); // turn off ha
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
@@ -373,10 +415,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
shell.println();
|
||||
Serial.print(COLOR_RESET);
|
||||
|
||||
|
||||
doc.clear();
|
||||
JsonObject root = doc.to<JsonObject>();
|
||||
emsdevice->generate_values_json_web(root);
|
||||
// JsonObject root = doc.to<JsonObject>();
|
||||
// emsdevice->generate_values_web(root);
|
||||
|
||||
JsonArray output = doc.to<JsonArray>();
|
||||
emsdevice->generate_values_web_all(output);
|
||||
|
||||
Serial.print(COLOR_BRIGHT_MAGENTA);
|
||||
serializeJson(doc, Serial);
|
||||
@@ -453,7 +497,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
}
|
||||
|
||||
if (command == "ha") {
|
||||
shell.printfln(F("Testing HA discovery"));
|
||||
shell.printfln(F("Testing HA mqtt discovery"));
|
||||
Mqtt::ha_enabled(true);
|
||||
// Mqtt::nested_format(1);
|
||||
Mqtt::nested_format(2);
|
||||
@@ -466,25 +510,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
shell.invoke_command("call system publish");
|
||||
shell.invoke_command("show mqtt");
|
||||
|
||||
shell.invoke_command("call boiler fanwork");
|
||||
shell.invoke_command("call thermostat seltemp"); // sensor.thermostat_hc1_selected_room_temperature
|
||||
shell.invoke_command("call thermostat entities");
|
||||
shell.invoke_command("call boiler entities");
|
||||
}
|
||||
|
||||
if (command == "dv") {
|
||||
shell.printfln(F("Testing device value rendering"));
|
||||
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::nested_format(1);
|
||||
Mqtt::send_response(false);
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
// change a value to null/bogus/dormant
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
// shell.invoke_command("call boiler fanwork");
|
||||
// shell.invoke_command("call thermostat seltemp"); // sensor.thermostat_hc1_selected_room_temperature
|
||||
// shell.invoke_command("call thermostat entities");
|
||||
// shell.invoke_command("call boiler entities");
|
||||
}
|
||||
|
||||
if (command == "lastcode") {
|
||||
@@ -505,6 +534,67 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
// shell.invoke_command("show");
|
||||
}
|
||||
|
||||
if (command == "dv") {
|
||||
shell.printfln(F("Testing device value rendering"));
|
||||
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::nested_format(1);
|
||||
Mqtt::send_response(false);
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
shell.invoke_command("call boiler wwseltemp");
|
||||
shell.invoke_command("call system publish");
|
||||
}
|
||||
|
||||
if (command == "dallas") {
|
||||
shell.printfln(F("Testing adding Dallas sensor"));
|
||||
Mqtt::ha_enabled(true);
|
||||
Mqtt::nested_format(1);
|
||||
// Mqtt::nested_format(0);
|
||||
|
||||
emsesp::EMSESP::dallassensor_.test();
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
|
||||
// rename
|
||||
EMSESP::dallassensor_.update("01-0203-0405-0607", "testdallas", 2);
|
||||
shell.invoke_command("show");
|
||||
shell.invoke_command("call system publish");
|
||||
}
|
||||
|
||||
if (command == "analog") {
|
||||
shell.printfln(F("Testing adding Analog sensor"));
|
||||
Mqtt::ha_enabled(true);
|
||||
// Mqtt::ha_enabled(false);
|
||||
Mqtt::nested_format(1);
|
||||
// Mqtt::nested_format(0);
|
||||
|
||||
emsesp::EMSESP::analogsensor_.test();
|
||||
shell.invoke_command("show");
|
||||
// shell.invoke_command("call system publish");
|
||||
// shell.invoke_command("show mqtt");
|
||||
|
||||
// rename
|
||||
// bool update(uint8_t id, const std::string & name, int16_t offset, float factor, uint8_t uom, uint8_t type);
|
||||
EMSESP::analogsensor_.update(36, "analogtest", 2, 0.7, 17, 1);
|
||||
shell.invoke_command("show");
|
||||
// shell.invoke_command("call system publish");
|
||||
}
|
||||
|
||||
if (command == "healthcheck") {
|
||||
uint8_t n = 0;
|
||||
if (!data.empty()) {
|
||||
n = Helpers::atoint(data.c_str());
|
||||
}
|
||||
|
||||
// n=1 = EMSESP::system_.HEALTHCHECK_NO_BUS
|
||||
// n=2 = EMSESP::system_.HEALTHCHECK_NO_NETWORK
|
||||
shell.printfln(F("Testing healthcheck with %d"), n);
|
||||
EMSESP::system_.healthcheck(n);
|
||||
}
|
||||
|
||||
if (command == "dv2") {
|
||||
shell.printfln(F("Testing device value lost"));
|
||||
|
||||
@@ -517,6 +607,41 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
shell.invoke_command("call system publish");
|
||||
}
|
||||
|
||||
if (command == "api_values") {
|
||||
shell.printfln(F("Testing API getting values"));
|
||||
Mqtt::ha_enabled(false);
|
||||
Mqtt::nested_format(1);
|
||||
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
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
AsyncWebServerRequest request;
|
||||
DynamicJsonDocument doc(2000);
|
||||
JsonVariant json;
|
||||
request.method(HTTP_GET);
|
||||
request.url("/api/boiler/wwcirc");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
request.url("/api/boiler/values");
|
||||
EMSESP::webAPIService.webAPIService_get(&request);
|
||||
}
|
||||
|
||||
if (command == "mqtt_post") {
|
||||
shell.printfln(F("Testing MQTT incoming changes"));
|
||||
Mqtt::ha_enabled(false);
|
||||
Mqtt::nested_format(1);
|
||||
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
|
||||
|
||||
run_test("boiler");
|
||||
run_test("thermostat");
|
||||
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/wwseltemp", "59");
|
||||
}
|
||||
|
||||
if (command == "api") {
|
||||
shell.printfln(F("Testing API with MQTT and REST, standalone"));
|
||||
|
||||
@@ -609,10 +734,12 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
/*
|
||||
requestX.url("/api"); // should fail
|
||||
EMSESP::webAPIService.webAPIService_get(&requestX);
|
||||
*/
|
||||
|
||||
requestX.method(HTTP_POST);
|
||||
|
||||
char dataX[] = "{\"device\":\"thermostat\", \"entity\":\"seltemp\",\"value\":13}";
|
||||
/*
|
||||
char dataX[] = "{\"device\":\"system\", \"entity\":\"settings\"}";
|
||||
deserializeJson(docX, dataX);
|
||||
jsonX = docX.as<JsonVariant>();
|
||||
requestX.url("/api");
|
||||
@@ -620,6 +747,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
return;
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
// char dataX[] = "{\"value\":\"0B 88 19 19 02\"}";
|
||||
char dataX[] = "{\"name\":\"temp\",\"value\":11}";
|
||||
@@ -702,6 +830,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// Web API TESTS
|
||||
AsyncWebServerRequest request;
|
||||
|
||||
request.method(HTTP_GET);
|
||||
|
||||
request.url("/api/thermostat"); // check if defaults to info
|
||||
@@ -727,7 +856,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
JsonVariant json;
|
||||
|
||||
// 1
|
||||
char data1[] = "{\"name\":\"temp\",\"value\":11}";
|
||||
char data1[] = "{\"entity\":\"seltemp\",\"value\":11}";
|
||||
deserializeJson(doc, data1);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat");
|
||||
@@ -737,11 +866,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
char data2[] = "{\"value\":12}";
|
||||
deserializeJson(doc, data2);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api/thermostat/temp");
|
||||
request.url("/api/thermostat/seltemp");
|
||||
EMSESP::webAPIService.webAPIService_post(&request, json);
|
||||
|
||||
// 3
|
||||
char data3[] = "{\"device\":\"thermostat\", \"name\":\"seltemp\",\"value\":13}";
|
||||
char data3[] = "{\"device\":\"thermostat\", \"cmd\":\"seltemp\",\"value\":13}";
|
||||
deserializeJson(doc, data3);
|
||||
json = doc.as<JsonVariant>();
|
||||
request.url("/api");
|
||||
@@ -1127,8 +1256,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
|
||||
DynamicJsonDocument doc(EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
|
||||
char key[6];
|
||||
char value[6];
|
||||
char key[8];
|
||||
char value[8];
|
||||
|
||||
// fit it up, to its limit of the Json buffer (which is about 169 records)
|
||||
for (uint8_t i = 0; i < 200; i++) {
|
||||
@@ -1170,23 +1299,29 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd) {
|
||||
char boiler_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char thermostat_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char system_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
EMSESP::EMSESP::mqtt_.publish("boiler", "test me");
|
||||
Mqtt::show_mqtt(shell); // show queue
|
||||
|
||||
strcpy(boiler_topic, "ems-esp/boiler");
|
||||
strcpy(thermostat_topic, "ems-esp/thermostat");
|
||||
strcpy(system_topic, "ems-esp/system");
|
||||
|
||||
EMSESP::mqtt_.incoming(boiler_topic, ""); // test if ignore empty payloads
|
||||
// test publishing
|
||||
EMSESP::EMSESP::mqtt_.publish(boiler_topic, "test me");
|
||||
|
||||
// test receiving
|
||||
EMSESP::mqtt_.incoming(boiler_topic, ""); // test if ignore empty payloads, should return values
|
||||
|
||||
EMSESP::mqtt_.incoming(boiler_topic, "12345"); // error: invalid format
|
||||
EMSESP::mqtt_.incoming("bad_topic", "12345"); // error: no matching topic
|
||||
EMSESP::mqtt_.incoming("bad_topic", "123456"); // error: no matching topic
|
||||
EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"garbage\",\"data\":22.52}"); // error: should report error
|
||||
|
||||
EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"comfort\",\"data\":\"eco\"}");
|
||||
EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"wwactivated\",\"data\":\"1\"}"); // with quotes
|
||||
EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"wwactivated\",\"data\":1}"); // without quotes
|
||||
EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"flowtemp\",\"data\":55}");
|
||||
EMSESP::mqtt_.incoming(boiler_topic, "{\"cmd\":\"selflowtemp\",\"data\":55}");
|
||||
|
||||
// test direct commands
|
||||
EMSESP::mqtt_.incoming("ems-esp/boiler/selflowtemp", "56");
|
||||
|
||||
EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"send\",\"data\":\"01 02 03 04 05\"}");
|
||||
EMSESP::mqtt_.incoming(system_topic, "{\"cmd\":\"pin\",\"id\":12,\"data\":\"1\"}");
|
||||
|
||||
@@ -30,22 +30,29 @@ namespace emsesp {
|
||||
// #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 "boiler"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt2"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt_nested"
|
||||
// #define EMSESP_DEBUG_DEFAULT "ha"
|
||||
#define EMSESP_DEBUG_DEFAULT "ha"
|
||||
// #define EMSESP_DEBUG_DEFAULT "board_profile"
|
||||
// #define EMSESP_DEBUG_DEFAULT "shower_alert"
|
||||
// #define EMSESP_DEBUG_DEFAULT "310"
|
||||
// #define EMSESP_DEBUG_DEFAULT "render"
|
||||
#define EMSESP_DEBUG_DEFAULT "api"
|
||||
// #define EMSESP_DEBUG_DEFAULT "api"
|
||||
// #define EMSESP_DEBUG_DEFAULT "crash"
|
||||
// #define EMSESP_DEBUG_DEFAULT "dv"
|
||||
// #define EMSESP_DEBUG_DEFAULT "lastcode"
|
||||
// #define EMSESP_DEBUG_DEFAULT "2thermostats"
|
||||
// #define EMSESP_DEBUG_DEFAULT "dallas"
|
||||
// #define EMSESP_DEBUG_DEFAULT "analog"
|
||||
// #define EMSESP_DEBUG_DEFAULT "api_values"
|
||||
// #define EMSESP_DEBUG_DEFAULT "mqtt_post"
|
||||
|
||||
class Test {
|
||||
public:
|
||||
static void run_test(uuid::console::Shell & shell, const std::string & command);
|
||||
static void run_test(uuid::console::Shell & shell, const std::string & command, const std::string & data = "");
|
||||
static bool run_test(const char * command, int8_t id = 0);
|
||||
static void dummy_mqtt_commands(const char * message);
|
||||
static void rx_telegram(const std::vector<uint8_t> & data);
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.3.1b1"
|
||||
#define EMSESP_APP_VERSION "3.4.0b0"
|
||||
|
||||
@@ -24,6 +24,9 @@ using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uint32_t WebAPIService::api_count_ = 0;
|
||||
uint16_t WebAPIService::api_fails_ = 0;
|
||||
|
||||
WebAPIService::WebAPIService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _apiHandler("/api", std::bind(&WebAPIService::webAPIService_post, this, _1, _2), 256) { // for POSTS, must use 'Content-Type: application/json' in header
|
||||
@@ -66,6 +69,33 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
is_admin = settings.notoken_api | AuthenticationPredicates::IS_ADMIN(authentication);
|
||||
});
|
||||
|
||||
// check for query parameters first, the old style from v2
|
||||
// api?device={device}&cmd={name}&data={value}&id={hc}
|
||||
if (request->url() == "/api") {
|
||||
// get the device
|
||||
if (request->hasParam(F_(device))) {
|
||||
input["device"] = request->getParam(F_(device))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(cmd))) {
|
||||
input["cmd"] = request->getParam(F_(cmd))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(data))) {
|
||||
input["data"] = request->getParam(F_(data))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(value))) {
|
||||
input["value"] = request->getParam(F_(value))->value().c_str();
|
||||
}
|
||||
if (request->hasParam(F_(id))) {
|
||||
input["id"] = Helpers::atoint(request->getParam(F_(id))->value().c_str());
|
||||
}
|
||||
if (request->hasParam(F_(hc))) {
|
||||
input["hc"] = Helpers::atoint(request->getParam(F_(hc))->value().c_str());
|
||||
}
|
||||
if (request->hasParam(F_(wwc))) {
|
||||
input["wwc"] = Helpers::atoint(request->getParam(F_(wwc))->value().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// output json buffer
|
||||
PrettyAsyncJsonResponse * response = new PrettyAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
JsonObject output = response->getRoot();
|
||||
@@ -81,8 +111,9 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
snprintf(error, sizeof(error), "Call failed with error code (%s)", Command::return_code_string(return_code).c_str());
|
||||
}
|
||||
emsesp::EMSESP::logger().err(error);
|
||||
api_fails_++;
|
||||
} else {
|
||||
emsesp::EMSESP::logger().debug(F("API command called successfully"));
|
||||
// emsesp::EMSESP::logger().debug(F("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";
|
||||
@@ -96,6 +127,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject & input) {
|
||||
response->setLength();
|
||||
response->setContentType("application/json");
|
||||
request->send(response);
|
||||
api_count_++;
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
Serial.print(COLOR_YELLOW);
|
||||
|
||||
@@ -19,14 +19,6 @@
|
||||
#ifndef WebAPIService_h
|
||||
#define WebAPIService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#define EMSESP_API_SERVICE_PATH "/api"
|
||||
|
||||
namespace emsesp {
|
||||
@@ -38,10 +30,21 @@ class WebAPIService {
|
||||
void webAPIService_post(AsyncWebServerRequest * request, JsonVariant & json); // for POSTs
|
||||
void webAPIService_get(AsyncWebServerRequest * request); // for GETs
|
||||
|
||||
static uint32_t api_count() {
|
||||
return api_count_;
|
||||
}
|
||||
|
||||
static uint16_t api_fails() {
|
||||
return api_fails_;
|
||||
}
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
AsyncCallbackJsonWebHandler _apiHandler; // for POSTs
|
||||
|
||||
static uint32_t api_count_;
|
||||
static uint16_t api_fails_;
|
||||
|
||||
void parse(AsyncWebServerRequest * request, JsonObject & input);
|
||||
};
|
||||
|
||||
|
||||
272
src/web/WebCustomizationService.cpp
Normal file
272
src/web/WebCustomizationService.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 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
|
||||
|
||||
WebCustomizationService::WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(WebCustomization::read,
|
||||
WebCustomization::update,
|
||||
this,
|
||||
server,
|
||||
EMSESP_CUSTOMIZATION_SERVICE_PATH,
|
||||
securityManager,
|
||||
AuthenticationPredicates::IS_AUTHENTICATED)
|
||||
, _fsPersistence(WebCustomization::read, WebCustomization::update, this, fs, EMSESP_CUSTOMIZATION_FILE)
|
||||
, _exclude_entities_handler(EXCLUDE_ENTITIES_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebCustomizationService::exclude_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(DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebCustomizationService::devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
server->on(RESET_CUSTOMIZATION_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&WebCustomizationService::reset_customization, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
_exclude_entities_handler.setMethod(HTTP_POST);
|
||||
_exclude_entities_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_exclude_entities_handler);
|
||||
|
||||
_device_entities_handler.setMethod(HTTP_POST);
|
||||
_device_entities_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_entities_handler);
|
||||
}
|
||||
|
||||
// this creates the customization file, saving to the FS
|
||||
void WebCustomization::read(WebCustomization & settings, JsonObject & root) {
|
||||
// Dallas Sensor customization
|
||||
JsonArray sensorsJson = root.createNestedArray("sensors");
|
||||
for (const SensorCustomization & sensor : settings.sensorCustomizations) {
|
||||
JsonObject sensorJson = sensorsJson.createNestedObject();
|
||||
sensorJson["id_str"] = sensor.id_str; // is
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
}
|
||||
|
||||
// Analog Sensor customization
|
||||
JsonArray analogJson = root.createNestedArray("analogs");
|
||||
for (const AnalogCustomization & sensor : settings.analogCustomizations) {
|
||||
JsonObject sensorJson = analogJson.createNestedObject();
|
||||
sensorJson["id"] = sensor.id; // i
|
||||
sensorJson["name"] = sensor.name; // n
|
||||
sensorJson["offset"] = sensor.offset; // o
|
||||
sensorJson["factor"] = sensor.factor; // f
|
||||
sensorJson["uom"] = sensor.uom; // u
|
||||
sensorJson["type"] = sensor.type; // t
|
||||
}
|
||||
|
||||
// Exclude entities customization
|
||||
JsonArray exclude_entitiesJson = root.createNestedArray("exclude_entities");
|
||||
for (const EntityCustomization & entityCustomization : settings.entityCustomizations) {
|
||||
JsonObject entityJson = exclude_entitiesJson.createNestedObject();
|
||||
entityJson["product_id"] = entityCustomization.product_id;
|
||||
entityJson["device_id"] = entityCustomization.device_id;
|
||||
|
||||
JsonArray exclude_entityJson = entityJson.createNestedArray("entity_ids");
|
||||
for (uint8_t entity_id : entityCustomization.entity_ids) {
|
||||
exclude_entityJson.add(entity_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call on initialization and also when the page is saved via web
|
||||
// this loads the data into the internal class
|
||||
StateUpdateResult WebCustomization::update(JsonObject & root, WebCustomization & settings) {
|
||||
// Dallas Sensor customization
|
||||
settings.sensorCustomizations.clear();
|
||||
if (root["sensors"].is<JsonArray>()) {
|
||||
for (const JsonObject sensorJson : root["sensors"].as<JsonArray>()) {
|
||||
// create each of the sensor, overwritting any previous settings
|
||||
SensorCustomization sensor = SensorCustomization();
|
||||
sensor.id_str = sensorJson["id_str"].as<std::string>();
|
||||
sensor.name = sensorJson["name"].as<std::string>();
|
||||
sensor.offset = sensorJson["offset"];
|
||||
settings.sensorCustomizations.push_back(sensor); // add to list
|
||||
}
|
||||
}
|
||||
|
||||
// Analog Sensor customization
|
||||
settings.analogCustomizations.clear();
|
||||
if (root["analogs"].is<JsonArray>()) {
|
||||
for (const JsonObject analogJson : root["analogs"].as<JsonArray>()) {
|
||||
// create each of the sensor, overwritting any previous settings
|
||||
AnalogCustomization sensor = AnalogCustomization();
|
||||
sensor.id = analogJson["id"];
|
||||
sensor.name = analogJson["name"].as<std::string>();
|
||||
sensor.offset = analogJson["offset"];
|
||||
sensor.factor = analogJson["factor"];
|
||||
sensor.uom = analogJson["uom"];
|
||||
sensor.type = analogJson["type"];
|
||||
settings.analogCustomizations.push_back(sensor); // add to list
|
||||
}
|
||||
}
|
||||
|
||||
// load array of entities id's to exclude, building up the object class
|
||||
settings.entityCustomizations.clear();
|
||||
if (root["exclude_entities"].is<JsonArray>()) {
|
||||
for (const JsonObject exclude_entities : root["exclude_entities"].as<JsonArray>()) {
|
||||
EntityCustomization new_entry = EntityCustomization();
|
||||
new_entry.product_id = exclude_entities["product_id"];
|
||||
new_entry.device_id = exclude_entities["device_id"];
|
||||
|
||||
for (const JsonVariant exclude_entity_id : exclude_entities["entity_ids"].as<JsonArray>()) {
|
||||
new_entry.entity_ids.push_back(exclude_entity_id.as<uint8_t>()); // add entity list
|
||||
}
|
||||
settings.entityCustomizations.push_back(new_entry); // save the new object
|
||||
}
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
|
||||
// deletes the customization file
|
||||
void WebCustomizationService::reset_customization(AsyncWebServerRequest * request) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (LITTLEFS.remove(EMSESP_CUSTOMIZATION_FILE)) {
|
||||
AsyncWebServerResponse * response = request->beginResponse(200); // OK
|
||||
request->send(response);
|
||||
EMSESP::system_.restart_requested(true);
|
||||
return;
|
||||
}
|
||||
// failed
|
||||
AsyncWebServerResponse * response = request->beginResponse(204); // no content error
|
||||
request->send(response);
|
||||
#endif
|
||||
}
|
||||
|
||||
// send back a short list devices used in the customization page
|
||||
void WebCustomizationService::devices(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
for (auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->has_entities()) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
obj["i"] = emsdevice->unique_id(); // a unique id
|
||||
|
||||
// shortname - we prefix the count to make it unique
|
||||
uint8_t device_index = EMSESP::device_index(emsdevice->device_type(), emsdevice->unique_id());
|
||||
if (device_index) {
|
||||
char s[10];
|
||||
obj["s"] = emsdevice->device_type_name() + Helpers::smallitoa(s, device_index);
|
||||
} else {
|
||||
obj["s"] = emsdevice->device_type_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// send back list device entities
|
||||
void WebCustomizationService::device_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(true, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->unique_id() == json["id"]) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
JsonArray output = response->getRoot();
|
||||
emsdevice->generate_values_web_all(output);
|
||||
#endif
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// invalid, but send OK anyway
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// takes a list of excluded ids send from the webUI
|
||||
// saves it in the customization service
|
||||
// and updates the entity list real-time
|
||||
void WebCustomizationService::exclude_entities(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
// find the device using the unique_id
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
uint8_t unique_device_id = json["id"];
|
||||
if (emsdevice->unique_id() == unique_device_id) {
|
||||
JsonArray entity_ids = json["entity_ids"];
|
||||
|
||||
std::vector<uint8_t> temp;
|
||||
for (JsonVariant id : entity_ids) {
|
||||
uint8_t entity_id = id.as<int>();
|
||||
emsdevice->exclude_entity(entity_id); // this will have immediate affect
|
||||
temp.push_back(entity_id);
|
||||
}
|
||||
|
||||
// Save the list to the customization file
|
||||
uint8_t product_id = emsdevice->product_id();
|
||||
uint8_t device_id = emsdevice->device_id();
|
||||
|
||||
EMSESP::webCustomizationService.update(
|
||||
[&](WebCustomization & settings) {
|
||||
// if it exists (productid and deviceid match) overwrite it
|
||||
for (auto & entityCustomization : settings.entityCustomizations) {
|
||||
if ((entityCustomization.product_id == product_id) && (entityCustomization.device_id == device_id)) {
|
||||
// already exists, clear the list and add the new values
|
||||
entityCustomization.entity_ids.clear();
|
||||
for (uint8_t i = 0; i < temp.size(); i++) {
|
||||
entityCustomization.entity_ids.push_back(temp[i]);
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
}
|
||||
}
|
||||
// create a new entry in the list
|
||||
EntityCustomization new_entry;
|
||||
new_entry.product_id = product_id;
|
||||
new_entry.device_id = device_id;
|
||||
for (uint8_t i = 0; i < temp.size(); i++) {
|
||||
new_entry.entity_ids.push_back(temp[i]);
|
||||
}
|
||||
settings.entityCustomizations.push_back(new_entry);
|
||||
return StateUpdateResult::CHANGED;
|
||||
},
|
||||
"local");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(200); // OK
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// load the settings when the service starts
|
||||
void WebCustomizationService::begin() {
|
||||
_fsPersistence.readFromFS();
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
106
src/web/WebCustomizationService.h
Normal file
106
src/web/WebCustomizationService.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020 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 WebCustomizationService_h
|
||||
#define WebCustomizationService_h
|
||||
|
||||
#define EMSESP_CUSTOMIZATION_FILE "/config/emsespCustomization.json"
|
||||
|
||||
// GET
|
||||
#define DEVICES_SERVICE_PATH "/rest/devices"
|
||||
#define EMSESP_CUSTOMIZATION_SERVICE_PATH "/rest/customization"
|
||||
|
||||
// POST
|
||||
#define DEVICE_ENTITIES_PATH "/rest/deviceEntities"
|
||||
#define EXCLUDE_ENTITIES_PATH "/rest/excludeEntities"
|
||||
#define RESET_CUSTOMIZATION_SERVICE_PATH "/rest/resetCustomizations"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// Customization for dallas sensor
|
||||
class SensorCustomization {
|
||||
public:
|
||||
std::string id_str;
|
||||
std::string name;
|
||||
uint16_t offset;
|
||||
};
|
||||
|
||||
class AnalogCustomization {
|
||||
public:
|
||||
uint8_t id;
|
||||
std::string name;
|
||||
uint16_t offset;
|
||||
float factor;
|
||||
uint8_t uom; // 0 is none
|
||||
int8_t type; // -1 is for deletion
|
||||
|
||||
// used for removing from a list
|
||||
bool operator==(const AnalogCustomization & a) const {
|
||||
return id == a.id;
|
||||
}
|
||||
bool operator!=(const AnalogCustomization & a) const {
|
||||
return !operator==(a);
|
||||
}
|
||||
};
|
||||
|
||||
// we use product_id and device_id to make the device unique
|
||||
class EntityCustomization {
|
||||
public:
|
||||
uint8_t product_id; // device's product id
|
||||
uint8_t device_id; // device's device id
|
||||
std::vector<uint8_t> entity_ids; // array of entity ids to exclude
|
||||
};
|
||||
|
||||
class WebCustomization {
|
||||
public:
|
||||
std::list<SensorCustomization> sensorCustomizations; // for sensor names and offsets
|
||||
std::list<AnalogCustomization> analogCustomizations; // for analog sensors
|
||||
std::list<EntityCustomization> entityCustomizations; // for a list of entities that should be excluded from the device list
|
||||
|
||||
static void read(WebCustomization & settings, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, WebCustomization & settings);
|
||||
};
|
||||
|
||||
class WebCustomizationService : public StatefulService<WebCustomization> {
|
||||
public:
|
||||
WebCustomizationService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
|
||||
// make all functions public so we can test in the debug and standalone mode
|
||||
#ifndef EMSESP_STANDALONE
|
||||
private:
|
||||
#endif
|
||||
|
||||
HttpEndpoint<WebCustomization> _httpEndpoint;
|
||||
FSPersistence<WebCustomization> _fsPersistence;
|
||||
|
||||
// GET
|
||||
void devices(AsyncWebServerRequest * request);
|
||||
|
||||
// POST
|
||||
void exclude_entities(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void device_entities(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void reset_customization(AsyncWebServerRequest * request);
|
||||
|
||||
AsyncCallbackJsonWebHandler _exclude_entities_handler, _device_entities_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
@@ -23,69 +23,131 @@ namespace emsesp {
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WebDataService::WebDataService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _device_dataHandler(DEVICE_DATA_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED))
|
||||
, _writevalue_dataHandler(WRITE_VALUE_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_value, this, _1, _2), AuthenticationPredicates::IS_ADMIN))
|
||||
, _writesensor_dataHandler(WRITE_SENSOR_SERVICE_PATH,
|
||||
securityManager->wrapCallback(std::bind(&WebDataService::write_sensor, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
server->on(EMSESP_DATA_SERVICE_PATH,
|
||||
: _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)) {
|
||||
server->on(CORE_DATA_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::core_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
server->on(SENSOR_DATA_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::sensor_data, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
|
||||
server->on(SCAN_DEVICES_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&WebDataService::scan_devices, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
_device_dataHandler.setMethod(HTTP_POST);
|
||||
_device_dataHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_dataHandler);
|
||||
_device_data_handler.setMethod(HTTP_POST);
|
||||
_device_data_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_device_data_handler);
|
||||
|
||||
_writevalue_dataHandler.setMethod(HTTP_POST);
|
||||
_writevalue_dataHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_writevalue_dataHandler);
|
||||
_write_value_handler.setMethod(HTTP_POST);
|
||||
_write_value_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_value_handler);
|
||||
|
||||
_writesensor_dataHandler.setMethod(HTTP_POST);
|
||||
_writesensor_dataHandler.setMaxContentLength(256);
|
||||
server->addHandler(&_writesensor_dataHandler);
|
||||
_write_sensor_handler.setMethod(HTTP_POST);
|
||||
_write_sensor_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_sensor_handler);
|
||||
|
||||
_write_analog_handler.setMethod(HTTP_POST);
|
||||
_write_analog_handler.setMaxContentLength(256);
|
||||
server->addHandler(&_write_analog_handler);
|
||||
}
|
||||
|
||||
// scan devices service
|
||||
void WebDataService::scan_devices(AsyncWebServerRequest * request) {
|
||||
EMSESP::logger().info(F("Scanning devices..."));
|
||||
EMSESP::scan_devices();
|
||||
request->send(200);
|
||||
}
|
||||
|
||||
void WebDataService::all_devices(AsyncWebServerRequest * request) {
|
||||
// this is used in the dashboard and contains all ems device information
|
||||
// /coreData endpoint
|
||||
void WebDataService::core_data(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// list is already sorted by device type
|
||||
// Ignore Contoller
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
for (auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice && emsdevice->device_type() != EMSdevice::DeviceType::CONTROLLER) {
|
||||
JsonObject obj = devices.createNestedObject();
|
||||
obj["i"] = emsdevice->unique_id(); // id
|
||||
obj["i"] = emsdevice->unique_id(); // a unique id
|
||||
obj["t"] = emsdevice->device_type_name(); // type
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// sensors stuff
|
||||
root["active_sensors"] = EMSESP::dallassensor_.no_sensors() + (EMSESP::analogsensor_.analog_enabled() ? EMSESP::analogsensor_.no_sensors() : 0);
|
||||
root["analog_enabled"] = EMSESP::analogsensor_.analog_enabled();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// sensor data - sends back to web
|
||||
// /sensorData endpoint
|
||||
// the "sensors" and "analogs" are arrays and must exist
|
||||
void WebDataService::sensor_data(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_XLARGE_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
// dallas sensors
|
||||
JsonArray sensors = root.createNestedArray("sensors");
|
||||
if (EMSESP::have_sensors()) {
|
||||
uint8_t i = 1;
|
||||
for (const auto & sensor : EMSESP::sensor_devices()) {
|
||||
if (EMSESP::dallassensor_.have_sensors()) {
|
||||
for (const auto & sensor : EMSESP::dallassensor_.sensors()) {
|
||||
JsonObject obj = sensors.createNestedObject();
|
||||
obj["n"] = i++; // no
|
||||
obj["i"] = sensor.to_string(true); // id
|
||||
obj["t"] = (float)(sensor.temperature_c) / 10; // temp
|
||||
obj["o"] = (float)(sensor.offset()) / 10; // offset
|
||||
obj["is"] = sensor.id_str(); // id
|
||||
obj["n"] = sensor.name(); // name
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
obj["t"] = (float)sensor.temperature_c * 0.18 + 32;
|
||||
}
|
||||
obj["u"] = DeviceValueUOM::FAHRENHEIT;
|
||||
obj["o"] = (float)sensor.offset() * 0.18;
|
||||
} else {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
obj["t"] = (float)sensor.temperature_c / 10;
|
||||
}
|
||||
obj["u"] = DeviceValueUOM::DEGREES;
|
||||
obj["o"] = (float)(sensor.offset()) / 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EMSESP::system_.analog_enabled()) {
|
||||
root["analog"] = EMSESP::system_.analog();
|
||||
// analog sensors
|
||||
// assume list is already sorted by id
|
||||
JsonArray analogs = root.createNestedArray("analogs");
|
||||
if (EMSESP::analog_enabled() && EMSESP::analogsensor_.have_sensors()) {
|
||||
for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
|
||||
// don't send if it's marked for removal
|
||||
if (sensor.type() != AnalogSensor::AnalogType::MARK_DELETED) {
|
||||
JsonObject obj = analogs.createNestedObject();
|
||||
obj["i"] = sensor.id();
|
||||
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::round2(sensor.value(), 1); // is optional and is a float
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
@@ -96,7 +158,7 @@ void WebDataService::all_devices(AsyncWebServerRequest * request) {
|
||||
// Compresses the JSON using MsgPack https://msgpack.org/index.html
|
||||
void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN);
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXXLARGE_DYN);
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice) {
|
||||
if (emsdevice->unique_id() == json["id"]) {
|
||||
@@ -106,8 +168,8 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
||||
}
|
||||
EMSESP::wait_validate(0); // reset in case of timeout
|
||||
#ifndef EMSESP_STANDALONE
|
||||
JsonObject root = response->getRoot();
|
||||
emsdevice->generate_values_json_web(root);
|
||||
JsonObject output = response->getRoot();
|
||||
emsdevice->generate_values_web(output);
|
||||
#endif
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
@@ -117,12 +179,13 @@ void WebDataService::device_data(AsyncWebServerRequest * request, JsonVariant &
|
||||
}
|
||||
}
|
||||
|
||||
// invalid
|
||||
// invalid but send ok
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// takes a command and its data value from a specific Device, from the Web
|
||||
|
||||
// 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) {
|
||||
if (json.is<JsonObject>()) {
|
||||
@@ -180,23 +243,42 @@ void WebDataService::write_value(AsyncWebServerRequest * request, JsonVariant &
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// takes a sensorname and optional offset from the Web
|
||||
// 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) {
|
||||
bool ok = false;
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject sensor = json["sensor"];
|
||||
JsonObject sensor = json;
|
||||
|
||||
// if valid add.
|
||||
uint8_t no = sensor["no"];
|
||||
if (no > 0 && no < 100) {
|
||||
char name[20];
|
||||
std::string id = sensor["id"];
|
||||
strlcpy(name, id.c_str(), sizeof(name));
|
||||
float offset = sensor["offset"]; // this will be a float value. We'll convert it to int and * 10 it
|
||||
int16_t offset10 = offset * 10;
|
||||
char idstr[3];
|
||||
ok = EMSESP::dallassensor_.update(Helpers::itoa(idstr, no, 10), name, offset10);
|
||||
std::string id_str = sensor["id_str"]; // this is the key
|
||||
std::string name = sensor["name"];
|
||||
|
||||
// calculate offset. We'll convert it to an int and * 10
|
||||
float offset = sensor["offset"];
|
||||
int16_t offset10 = offset * 10;
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
offset10 = offset / 0.18;
|
||||
}
|
||||
ok = EMSESP::dallassensor_.update(id_str, name, offset10);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
// update the analog record, or create a new one
|
||||
void WebDataService::write_analog(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
bool ok = false;
|
||||
if (json.is<JsonObject>()) {
|
||||
JsonObject analog = json;
|
||||
|
||||
uint8_t id = analog["id"]; // this is the unique key
|
||||
std::string name = analog["name"];
|
||||
float factor = analog["factor"];
|
||||
int16_t offset = analog["offset"];
|
||||
uint8_t uom = analog["uom"];
|
||||
int8_t type = analog["type"];
|
||||
ok = EMSESP::analogsensor_.update(id, name, offset, factor, uom, type);
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(ok ? 200 : 204);
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
#ifndef WebDataService_h
|
||||
#define WebDataService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define EMSESP_DATA_SERVICE_PATH "/rest/data"
|
||||
// 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"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -42,15 +42,17 @@ class WebDataService {
|
||||
#endif
|
||||
|
||||
// GET
|
||||
void all_devices(AsyncWebServerRequest * request);
|
||||
void scan_devices(AsyncWebServerRequest * request);
|
||||
void core_data(AsyncWebServerRequest * request);
|
||||
void sensor_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);
|
||||
|
||||
AsyncCallbackJsonWebHandler _device_dataHandler, _writevalue_dataHandler, _writesensor_dataHandler;
|
||||
AsyncCallbackJsonWebHandler _device_data_handler, _write_value_handler, _write_sensor_handler, _write_analog_handler;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -27,6 +27,7 @@ WebLogService::WebLogService(AsyncWebServer * server, SecurityManager * security
|
||||
, setValues_(LOG_SETTINGS_PATH, std::bind(&WebLogService::setValues, this, _1, _2), 256) { // for POSTS
|
||||
|
||||
events_.setFilter(securityManager->filterRequest(AuthenticationPredicates::IS_ADMIN));
|
||||
|
||||
server->addHandler(&events_);
|
||||
server->on(EVENT_SOURCE_LOG_PATH, HTTP_GET, std::bind(&WebLogService::forbidden, this, _1));
|
||||
|
||||
@@ -104,6 +105,14 @@ WebLogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_
|
||||
}
|
||||
|
||||
void WebLogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
||||
/*
|
||||
// special case for trace, show trace and notice messages only
|
||||
// added by mvdp
|
||||
if (log_level() == uuid::log::Level::TRACE && message->level != uuid::log::Level::TRACE && message->level != uuid::log::Level::NOTICE) {
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (log_messages_.size() >= maximum_log_messages_) {
|
||||
log_messages_.pop_front();
|
||||
}
|
||||
@@ -184,7 +193,7 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
|
||||
|
||||
// send the complete log buffer to the API, not filtering on log level
|
||||
void WebLogService::fetchLog(AsyncWebServerRequest * request) {
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_XXLARGE_DYN); // 16kb buffer
|
||||
MsgpackAsyncJsonResponse * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN + 192 * log_messages_.size());
|
||||
JsonObject root = response->getRoot();
|
||||
JsonArray log = root.createNestedArray("events");
|
||||
|
||||
|
||||
@@ -19,13 +19,6 @@
|
||||
#ifndef WebLogService_h
|
||||
#define WebLogService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
#define EVENT_SOURCE_LOG_PATH "/es/log"
|
||||
#define FETCH_LOG_PATH "/rest/fetchLog"
|
||||
#define LOG_SETTINGS_PATH "/rest/logSettings"
|
||||
|
||||
@@ -50,63 +50,49 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
|
||||
root["shower_alert"] = settings.shower_alert;
|
||||
root["rx_gpio"] = settings.rx_gpio;
|
||||
root["tx_gpio"] = settings.tx_gpio;
|
||||
root["phy_type"] = settings.phy_type;
|
||||
root["dallas_gpio"] = settings.dallas_gpio;
|
||||
root["dallas_parasite"] = settings.dallas_parasite;
|
||||
root["led_gpio"] = settings.led_gpio;
|
||||
root["hide_led"] = settings.hide_led;
|
||||
root["low_clock"] = settings.low_clock;
|
||||
root["telnet_enabled"] = settings.telnet_enabled;
|
||||
root["notoken_api"] = settings.notoken_api;
|
||||
root["readonly_mode"] = settings.readonly_mode;
|
||||
root["analog_enabled"] = settings.analog_enabled;
|
||||
root["pbutton_gpio"] = settings.pbutton_gpio;
|
||||
root["solar_maxflow"] = settings.solar_maxflow;
|
||||
root["board_profile"] = settings.board_profile;
|
||||
root["dallas_format"] = settings.dallas_format;
|
||||
root["fahrenheit"] = settings.fahrenheit;
|
||||
root["bool_format"] = settings.bool_format;
|
||||
root["enum_format"] = settings.enum_format;
|
||||
root["weblog_level"] = settings.weblog_level;
|
||||
root["weblog_buffer"] = settings.weblog_buffer;
|
||||
root["weblog_compact"] = settings.weblog_compact;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "sensor_id%d", i);
|
||||
root[buf] = settings.sensor[i].id;
|
||||
snprintf(buf, sizeof(buf), "sensor_name%d", i);
|
||||
root[buf] = settings.sensor[i].name;
|
||||
snprintf(buf, sizeof(buf), "sensor_offset%d", i);
|
||||
root[buf] = settings.sensor[i].offset;
|
||||
}
|
||||
root["phy_type"] = settings.phy_type;
|
||||
root["eth_power"] = settings.eth_power;
|
||||
root["eth_phy_addr"] = settings.eth_phy_addr;
|
||||
root["eth_clock_mode"] = settings.eth_clock_mode;
|
||||
}
|
||||
|
||||
// call on initialization and also when settings are updated via web or console
|
||||
StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings) {
|
||||
// load default GPIO configuration based on board profile
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button, phy_type
|
||||
std::vector<int8_t> data; // // led, dallas, rx, tx, button, phy_type, eth_power, eth_phy_addr, eth_clock_mode
|
||||
|
||||
String old_board_profile = settings.board_profile;
|
||||
settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE;
|
||||
settings.board_profile = root["board_profile"] | EMSESP_DEFAULT_BOARD_PROFILE;
|
||||
if (!System::load_board_profile(data, settings.board_profile.c_str())) {
|
||||
settings.board_profile = EMSESP_DEFAULT_BOARD_PROFILE; // invalid board configuration, override the default in case it has been misspelled
|
||||
}
|
||||
|
||||
uint8_t default_led_gpio = data[0];
|
||||
uint8_t default_dallas_gpio = data[1];
|
||||
uint8_t default_rx_gpio = data[2];
|
||||
uint8_t default_tx_gpio = data[3];
|
||||
uint8_t default_pbutton_gpio = data[4];
|
||||
uint8_t default_phy_type = data[5];
|
||||
|
||||
if (old_board_profile != settings.board_profile) {
|
||||
EMSESP::logger().info(F("EMS-ESP version %s"), EMSESP_APP_VERSION);
|
||||
|
||||
// check to see if we have a settings file, if not it's a fresh install
|
||||
if (!root.size()) {
|
||||
EMSESP::logger().info(F("Initializing configuration with board profile %s"), settings.board_profile.c_str());
|
||||
} else {
|
||||
EMSESP::logger().info(F("Using configuration from board profile %s"), settings.board_profile.c_str());
|
||||
}
|
||||
}
|
||||
uint8_t default_led_gpio = data[0];
|
||||
uint8_t default_dallas_gpio = data[1];
|
||||
uint8_t default_rx_gpio = data[2];
|
||||
uint8_t default_tx_gpio = data[3];
|
||||
uint8_t default_pbutton_gpio = data[4];
|
||||
uint8_t default_phy_type = data[5];
|
||||
uint8_t default_eth_power = data[6];
|
||||
uint8_t default_eth_phy_addr = data[7];
|
||||
uint8_t default_eth_clock_mode = data[8];
|
||||
|
||||
int prev;
|
||||
reset_flags();
|
||||
@@ -135,6 +121,10 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.syslog_mark_interval = root["syslog_mark_interval"] | EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL;
|
||||
check_flag(prev, settings.syslog_mark_interval, ChangeFlags::SYSLOG);
|
||||
|
||||
prev = settings.syslog_port;
|
||||
settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT;
|
||||
check_flag(prev, settings.syslog_port, ChangeFlags::SYSLOG);
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
String old_syslog_host = settings.syslog_host;
|
||||
settings.syslog_host = root["syslog_host"] | EMSESP_DEFAULT_SYSLOG_HOST;
|
||||
@@ -143,15 +133,6 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
}
|
||||
#endif
|
||||
|
||||
prev = settings.syslog_port;
|
||||
settings.syslog_port = root["syslog_port"] | EMSESP_DEFAULT_SYSLOG_PORT;
|
||||
check_flag(prev, settings.syslog_port, ChangeFlags::SYSLOG);
|
||||
|
||||
// adc
|
||||
prev = settings.analog_enabled;
|
||||
settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED;
|
||||
check_flag(prev, settings.analog_enabled, ChangeFlags::ADC);
|
||||
|
||||
// button
|
||||
prev = settings.pbutton_gpio;
|
||||
settings.pbutton_gpio = root["pbutton_gpio"] | default_pbutton_gpio;
|
||||
@@ -181,15 +162,48 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.hide_led = root["hide_led"] | EMSESP_DEFAULT_HIDE_LED;
|
||||
check_flag(prev, settings.hide_led, ChangeFlags::LED);
|
||||
|
||||
//
|
||||
// next ones are settings that don't need any follow-up actions
|
||||
//
|
||||
// adc
|
||||
prev = settings.analog_enabled;
|
||||
settings.analog_enabled = root["analog_enabled"] | EMSESP_DEFAULT_ANALOG_ENABLED;
|
||||
check_flag(prev, settings.analog_enabled, ChangeFlags::ADC);
|
||||
|
||||
// these need reboots to be applied
|
||||
settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
//
|
||||
// these need reboots to be applied...
|
||||
//
|
||||
prev = settings.telnet_enabled;
|
||||
settings.telnet_enabled = root["telnet_enabled"] | EMSESP_DEFAULT_TELNET_ENABLED;
|
||||
check_flag(prev, settings.telnet_enabled, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.ems_bus_id;
|
||||
settings.ems_bus_id = root["ems_bus_id"] | EMSESP_DEFAULT_EMS_BUS_ID;
|
||||
check_flag(prev, settings.ems_bus_id, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.low_clock;
|
||||
settings.low_clock = root["low_clock"] | false;
|
||||
check_flag(prev, settings.low_clock, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.master_thermostat;
|
||||
settings.master_thermostat = root["master_thermostat"] | EMSESP_DEFAULT_MASTER_THERMOSTAT;
|
||||
settings.low_clock = root["low_clock"] | false;
|
||||
settings.phy_type = root["phy_type"] | default_phy_type; // use whatever came from the board profile
|
||||
check_flag(prev, settings.master_thermostat, ChangeFlags::RESTART);
|
||||
|
||||
// use whatever came from the board profile
|
||||
prev = settings.phy_type;
|
||||
settings.phy_type = root["phy_type"] | default_phy_type;
|
||||
check_flag(prev, settings.phy_type, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.eth_power;
|
||||
settings.eth_power = root["eth_power"] | default_eth_power;
|
||||
check_flag(prev, settings.eth_power, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.eth_phy_addr;
|
||||
settings.eth_phy_addr = root["eth_phy_addr"] | default_eth_phy_addr;
|
||||
check_flag(prev, settings.eth_phy_addr, ChangeFlags::RESTART);
|
||||
|
||||
prev = settings.eth_clock_mode;
|
||||
settings.eth_clock_mode = root["eth_clock_mode"] | default_eth_clock_mode;
|
||||
check_flag(prev, settings.eth_clock_mode, ChangeFlags::RESTART);
|
||||
|
||||
// without checks...
|
||||
|
||||
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
|
||||
EMSESP::trace_raw(settings.trace_raw);
|
||||
@@ -197,27 +211,25 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
|
||||
settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
|
||||
settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
|
||||
|
||||
settings.dallas_format = root["dallas_format"] | EMSESP_DEFAULT_DALLAS_FORMAT;
|
||||
EMSESP::dallassensor_.dallas_format(settings.dallas_format);
|
||||
settings.fahrenheit = root["fahrenheit"] | false;
|
||||
EMSESP::system_.fahrenheit(settings.fahrenheit);
|
||||
|
||||
settings.readonly_mode = root["readonly_mode"] | false;
|
||||
EMSESP::system_.readonly_mode(settings.readonly_mode);
|
||||
|
||||
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
|
||||
EMSESP::bool_format(settings.bool_format);
|
||||
EMSESP::system_.bool_format(settings.bool_format);
|
||||
|
||||
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
|
||||
EMSESP::enum_format(settings.enum_format);
|
||||
EMSESP::system_.enum_format(settings.enum_format);
|
||||
|
||||
settings.weblog_level = root["weblog_level"] | EMSESP_DEFAULT_WEBLOG_LEVEL;
|
||||
settings.weblog_buffer = root["weblog_buffer"] | EMSESP_DEFAULT_WEBLOG_BUFFER;
|
||||
settings.weblog_compact = root["weblog_compact"] | EMSESP_DEFAULT_WEBLOG_COMPACT;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_NUM_SENSOR_NAMES; i++) {
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "sensor_id%d", i);
|
||||
settings.sensor[i].id = root[buf] | EMSESP_DEFAULT_SENSOR_NAME;
|
||||
snprintf(buf, sizeof(buf), "sensor_name%d", i);
|
||||
settings.sensor[i].name = root[buf] | EMSESP_DEFAULT_SENSOR_NAME;
|
||||
snprintf(buf, sizeof(buf), "sensor_offset%d", i);
|
||||
settings.sensor[i].offset = root[buf] | 0;
|
||||
// save the settings
|
||||
if (flags_ == WebSettings::ChangeFlags::RESTART) {
|
||||
return StateUpdateResult::CHANGED_RESTART; // tell WebUI that a restart is needed
|
||||
}
|
||||
|
||||
return StateUpdateResult::CHANGED;
|
||||
@@ -243,7 +255,7 @@ void WebSettingsService::onUpdate() {
|
||||
}
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::ADC)) {
|
||||
EMSESP::system_.adc_init(true); // reload settings
|
||||
EMSESP::analogsensor_.start();
|
||||
}
|
||||
|
||||
if (WebSettings::has_flags(WebSettings::ChangeFlags::BUTTON)) {
|
||||
@@ -270,22 +282,20 @@ void WebSettingsService::board_profile(AsyncWebServerRequest * request, JsonVari
|
||||
if (json.is<JsonObject>()) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM);
|
||||
JsonObject root = response->getRoot();
|
||||
if (json.containsKey("code")) {
|
||||
String board_profile = json["code"];
|
||||
std::vector<uint8_t> data; // led, dallas, rx, tx, button
|
||||
// check for valid board
|
||||
if (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];
|
||||
} else {
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
|
||||
@@ -16,25 +16,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef WebSettingsConfig_h
|
||||
#define WebSettingsConfig_h
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#ifndef WebSettingsService_h
|
||||
#define WebSettingsService_h
|
||||
|
||||
#include "../default_settings.h"
|
||||
|
||||
#define EMSESP_SETTINGS_FILE "/config/emsespSettings.json"
|
||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/emsespSettings"
|
||||
#define EMSESP_SETTINGS_SERVICE_PATH "/rest/settings"
|
||||
#define EMSESP_BOARD_PROFILE_SERVICE_PATH "/rest/boardProfile"
|
||||
|
||||
#define MAX_NUM_SENSOR_NAMES 20
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum { BOOL_FORMAT_ONOFF = 1, BOOL_FORMAT_ONOFF_CAP, BOOL_FORMAT_TRUEFALSE, BOOL_FORMAT_10 }; // matches Web UI settings
|
||||
enum { ENUM_FORMAT_TEXT = 1, ENUM_FORMAT_NUMBER }; // matches Web UI settings
|
||||
|
||||
class WebSettings {
|
||||
public:
|
||||
uint8_t tx_mode;
|
||||
@@ -55,38 +47,39 @@ class WebSettings {
|
||||
uint8_t led_gpio;
|
||||
bool hide_led;
|
||||
bool low_clock;
|
||||
bool telnet_enabled;
|
||||
bool notoken_api;
|
||||
bool readonly_mode;
|
||||
bool analog_enabled;
|
||||
uint8_t pbutton_gpio;
|
||||
uint8_t solar_maxflow;
|
||||
String board_profile;
|
||||
uint8_t phy_type;
|
||||
uint8_t dallas_format;
|
||||
uint8_t bool_format;
|
||||
uint8_t enum_format;
|
||||
int8_t weblog_level;
|
||||
uint8_t weblog_buffer;
|
||||
bool weblog_compact;
|
||||
bool fahrenheit;
|
||||
|
||||
struct {
|
||||
String id;
|
||||
String name;
|
||||
int16_t offset;
|
||||
} sensor[MAX_NUM_SENSOR_NAMES];
|
||||
uint8_t phy_type;
|
||||
int8_t eth_power; // -1 means disabled
|
||||
uint8_t eth_phy_addr;
|
||||
uint8_t eth_clock_mode;
|
||||
|
||||
static void read(WebSettings & settings, JsonObject & root);
|
||||
static StateUpdateResult update(JsonObject & root, WebSettings & settings);
|
||||
|
||||
enum ChangeFlags : uint8_t {
|
||||
|
||||
NONE = 0,
|
||||
UART = (1 << 0), // 1
|
||||
SYSLOG = (1 << 1), // 2
|
||||
ADC = (1 << 2), // 4
|
||||
DALLAS = (1 << 3), // 8
|
||||
SHOWER = (1 << 4), // 16
|
||||
LED = (1 << 5), // 32
|
||||
BUTTON = (1 << 6) // 64
|
||||
NONE = 0,
|
||||
UART = (1 << 0), // 1
|
||||
SYSLOG = (1 << 1), // 2
|
||||
ADC = (1 << 2), // 4 - analog
|
||||
DALLAS = (1 << 3), // 8
|
||||
SHOWER = (1 << 4), // 16
|
||||
LED = (1 << 5), // 32
|
||||
BUTTON = (1 << 6), // 64
|
||||
RESTART = 0xFF
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -34,13 +34,13 @@ WebStatusService::WebStatusService(AsyncWebServer * server, SecurityManager * se
|
||||
void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
switch (event) {
|
||||
case SYSTEM_EVENT_STA_DISCONNECTED:
|
||||
EMSESP::logger().info(F("WiFi Disconnected. Reason code=%d"), info.disconnected.reason);
|
||||
EMSESP::logger().info(F("WiFi disconnected. Reason code=%d"), info.disconnected.reason);
|
||||
WiFi.disconnect(true);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_STA_GOT_IP:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
EMSESP::logger().info(F("WiFi connected with IP=%s, hostname=%s"), WiFi.localIP().toString().c_str(), WiFi.getHostname());
|
||||
#endif
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
if (!networkSettings.enableIPv6) {
|
||||
@@ -52,7 +52,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_ETH_START:
|
||||
EMSESP::logger().info(F("Ethernet initialized"));
|
||||
// EMSESP::logger().info(F("Ethernet initialized"));
|
||||
ETH.setHostname(EMSESP::system_.hostname().c_str());
|
||||
|
||||
// configure for static IP
|
||||
@@ -68,7 +68,7 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
// prevent double calls
|
||||
if (!EMSESP::system_.ethernet_connected()) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::logger().info(F("Ethernet Connected with IP=%s, speed %d Mbps"), ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
||||
EMSESP::logger().info(F("Ethernet connected with IP=%s, speed %d Mbps"), ETH.localIP().toString().c_str(), ETH.linkSpeed());
|
||||
#endif
|
||||
EMSESP::esp8266React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) {
|
||||
if (!networkSettings.enableIPv6) {
|
||||
@@ -82,12 +82,12 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||
EMSESP::logger().info(F("Ethernet Disconnected"));
|
||||
EMSESP::logger().info(F("Ethernet disconnected"));
|
||||
EMSESP::system_.ethernet_connected(false);
|
||||
break;
|
||||
|
||||
case SYSTEM_EVENT_ETH_STOP:
|
||||
EMSESP::logger().info(F("Ethernet Stopped"));
|
||||
EMSESP::logger().info(F("Ethernet stopped"));
|
||||
EMSESP::system_.ethernet_connected(false);
|
||||
break;
|
||||
|
||||
@@ -110,9 +110,9 @@ void WebStatusService::WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
|
||||
|
||||
case SYSTEM_EVENT_GOT_IP6:
|
||||
if (EMSESP::system_.ethernet_connected()) {
|
||||
EMSESP::logger().info(F("Ethernet Connected with IP=%s, speed %d Mbps"), ETH.localIPv6().toString().c_str(), ETH.linkSpeed());
|
||||
EMSESP::logger().info(F("Ethernet connected with IP=%s, speed %d Mbps"), ETH.localIPv6().toString().c_str(), ETH.linkSpeed());
|
||||
} else {
|
||||
EMSESP::logger().info(F("WiFi Connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname());
|
||||
EMSESP::logger().info(F("WiFi connected with IP=%s, hostname=%s"), WiFi.localIPv6().toString().c_str(), WiFi.getHostname());
|
||||
}
|
||||
EMSESP::system_.send_heartbeat();
|
||||
EMSESP::system_.syslog_start();
|
||||
@@ -129,11 +129,34 @@ void WebStatusService::webStatusService(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, EMSESP_JSON_SIZE_MEDIUM_DYN);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
root["rx_received"] = EMSESP::rxservice_.telegram_count();
|
||||
root["tx_sent"] = EMSESP::txservice_.telegram_read_count() + EMSESP::txservice_.telegram_write_count();
|
||||
root["rx_quality"] = EMSESP::rxservice_.quality();
|
||||
root["tx_quality"] = EMSESP::txservice_.quality();
|
||||
root["status"] = EMSESP::bus_status(); // 0, 1 or 2
|
||||
root["num_devices"] = EMSESP::count_devices(); // excluding Controller
|
||||
root["num_sensors"] = EMSESP::dallassensor_.no_sensors();
|
||||
root["num_analogs"] = EMSESP::analogsensor_.no_sensors();
|
||||
root["tx_mode"] = EMSESP::txservice_.tx_mode();
|
||||
root["rx_received"] = EMSESP::rxservice_.telegram_count();
|
||||
root["tx_reads"] = EMSESP::txservice_.telegram_read_count();
|
||||
root["tx_writes"] = EMSESP::txservice_.telegram_write_count();
|
||||
root["rx_quality"] = EMSESP::rxservice_.quality();
|
||||
root["tx_read_quality"] = EMSESP::txservice_.read_quality();
|
||||
root["tx_write_quality"] = EMSESP::txservice_.write_quality();
|
||||
root["rx_fails"] = EMSESP::rxservice_.telegram_error_count();
|
||||
root["tx_read_fails"] = EMSESP::txservice_.telegram_read_fail_count();
|
||||
root["tx_write_fails"] = EMSESP::txservice_.telegram_write_fail_count();
|
||||
root["sensor_fails"] = EMSESP::dallassensor_.fails();
|
||||
root["sensor_reads"] = EMSESP::dallassensor_.reads();
|
||||
root["sensor_quality"] = EMSESP::dallassensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::dallassensor_.fails()) / EMSESP::dallassensor_.reads());
|
||||
root["analog_fails"] = EMSESP::analogsensor_.fails();
|
||||
root["analog_reads"] = EMSESP::analogsensor_.reads();
|
||||
root["analog_quality"] = EMSESP::analogsensor_.reads() == 0 ? 100 : 100 - (uint8_t)((100 * EMSESP::analogsensor_.fails()) / EMSESP::analogsensor_.reads());
|
||||
root["mqtt_fails"] = Mqtt::publish_fails();
|
||||
root["mqtt_count"] = Mqtt::publish_count();
|
||||
root["mqtt_quality"] = Mqtt::publish_count() == 0 ? 100 : 100 - (Mqtt::publish_fails() * 100) / (Mqtt::publish_count() + Mqtt::publish_fails());
|
||||
root["api_calls"] = WebAPIService::api_count(); // + WebAPIService::api_fails();
|
||||
root["api_fails"] = WebAPIService::api_fails();
|
||||
root["api_quality"] =
|
||||
WebAPIService::api_count() == 0 ? 100 : 100 - (WebAPIService::api_fails() * 100) / (WebAPIService::api_count() + WebAPIService::api_fails());
|
||||
root["uptime"] = EMSbus::bus_uptime();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
|
||||
@@ -19,15 +19,9 @@
|
||||
#ifndef WebStatusService_h
|
||||
#define WebStatusService_h
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
|
||||
#include <ESPmDNS.h>
|
||||
|
||||
#define EMSESP_STATUS_SERVICE_PATH "/rest/emsespStatus"
|
||||
#define EMSESP_STATUS_SERVICE_PATH "/rest/status"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user