mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 01:09:51 +03:00
merge #2108
This commit is contained in:
796
src/core/analogsensor.cpp
Normal file
796
src/core/analogsensor.cpp
Normal file
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
* 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(true); // fetch the list of sensors from our customization service
|
||||
|
||||
if (!analog_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
analogSetAttenuation(ADC_2_5db); // for all channels 1.5V
|
||||
|
||||
LOG_INFO("Starting Analog Sensor service");
|
||||
|
||||
// Add API calls
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::ANALOGSENSOR,
|
||||
F_(setvalue),
|
||||
[&](const char * value, const int8_t id) { return command_setvalue(value, id); },
|
||||
FL_(setiovalue_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "%s/#", F_(analogsensor));
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::ANALOGSENSOR, topic, nullptr); // use empty function callback
|
||||
}
|
||||
|
||||
// load settings from the customization file, sorts them and initializes the GPIOs
|
||||
void AnalogSensor::reload(bool get_nvs) {
|
||||
EMSESP::webSettingsService.read([&](WebSettings & settings) { analog_enabled_ = settings.analog_enabled; });
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
analog_enabled_ = true; // for local offline testing
|
||||
#endif
|
||||
for (auto sensor : sensors_) {
|
||||
remove_ha_topic(sensor.type(), sensor.gpio());
|
||||
sensor.ha_registered = false;
|
||||
}
|
||||
|
||||
if (!analog_enabled_) {
|
||||
sensors_.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// load the list of analog sensors from the customization service
|
||||
// and store them locally and then activate them
|
||||
EMSESP::webCustomizationService.read([&](WebCustomization & settings) {
|
||||
auto it = sensors_.begin();
|
||||
for (auto & sensor_ : sensors_) {
|
||||
// update existing sensors
|
||||
bool found = false;
|
||||
for (const auto & sensor : settings.analogCustomizations) { // search customlist
|
||||
if (sensor_.gpio() == sensor.gpio) {
|
||||
// for output sensors set value to new start-value
|
||||
if ((sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT)
|
||||
&& (sensor_.type() != sensor.type || sensor_.offset() != sensor.offset || sensor_.factor() != sensor.factor)) {
|
||||
sensor_.set_value(sensor.offset);
|
||||
}
|
||||
sensor_.set_name(sensor.name);
|
||||
sensor_.set_type(sensor.type);
|
||||
sensor_.set_offset(sensor.offset);
|
||||
sensor_.set_factor(sensor.factor);
|
||||
sensor_.set_uom(sensor.uom);
|
||||
sensor_.ha_registered = false;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
sensors_.erase(it);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
// add new sensors from list
|
||||
for (const auto & sensor : settings.analogCustomizations) {
|
||||
bool found = false;
|
||||
for (const auto & sensor_ : sensors_) {
|
||||
if (sensor_.gpio() == sensor.gpio) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
sensors_.emplace_back(sensor.gpio, sensor.name, sensor.offset, sensor.factor, sensor.uom, sensor.type);
|
||||
sensors_.back().ha_registered = false; // this will trigger recreate of the HA config
|
||||
if (sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT) {
|
||||
sensors_.back().set_value(sensor.offset);
|
||||
} else {
|
||||
sensors_.back().set_value(0); // reset value only for new sensors
|
||||
}
|
||||
}
|
||||
if (sensor.type == AnalogType::COUNTER || sensor.type >= AnalogType::DIGITAL_OUT) {
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::ANALOGSENSOR,
|
||||
sensor.name.c_str(),
|
||||
[&](const char * value, const int8_t id) { return command_setvalue(value, sensor.gpio); },
|
||||
sensor.type == AnalogType::COUNTER ? FL_(counter)
|
||||
: sensor.type == AnalogType::DIGITAL_OUT ? FL_(digital_out)
|
||||
: FL_(pwm),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
// first check if the GPIO is valid. If not, force set it to disabled
|
||||
if (!System::is_valid_gpio(sensor.gpio())) {
|
||||
LOG_WARNING("Bad GPIO %d for Sensor %s. Disabling.", sensor.gpio(), sensor.name().c_str());
|
||||
sensor.set_type(AnalogType::NOTUSED); // set disabled
|
||||
continue; // skip this loop pass
|
||||
}
|
||||
|
||||
if ((sensor.gpio() == 25 || sensor.gpio() == 26)
|
||||
&& (sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::RATE
|
||||
|| sensor.type() == AnalogType::TIMER)) {
|
||||
// pullup is mapped to DAC, so set to 3.3V
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (sensor.gpio() == 25 || sensor.gpio() == 26) {
|
||||
dacWrite(sensor.gpio(), 255);
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
if (sensor.gpio() == 17 || sensor.gpio() == 18) {
|
||||
dacWrite(sensor.gpio(), 255);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (sensor.type() == AnalogType::ADC) {
|
||||
LOG_DEBUG("ADC Sensor on GPIO %02d", sensor.gpio());
|
||||
// analogSetPinAttenuation does not work with analogReadMilliVolts
|
||||
sensor.analog_ = 0; // initialize
|
||||
sensor.last_reading_ = 0;
|
||||
} else if (sensor.type() == AnalogType::COUNTER) {
|
||||
LOG_DEBUG("I/O Counter on GPIO %02d", sensor.gpio());
|
||||
pinMode(sensor.gpio(), INPUT_PULLUP);
|
||||
sensor.polltime_ = 0;
|
||||
sensor.poll_ = digitalRead(sensor.gpio());
|
||||
if (double_t val = EMSESP::nvs_.getDouble(sensor.name().c_str(), 0)) {
|
||||
sensor.set_value(val);
|
||||
}
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
|
||||
LOG_DEBUG("Timer/Rate on GPIO %02d", sensor.gpio());
|
||||
pinMode(sensor.gpio(), INPUT_PULLUP);
|
||||
sensor.polltime_ = uuid::get_uptime();
|
||||
sensor.last_polltime_ = uuid::get_uptime();
|
||||
sensor.poll_ = digitalRead(sensor.gpio());
|
||||
sensor.set_offset(0);
|
||||
sensor.set_value(0);
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
LOG_DEBUG("Digital Read on GPIO %02d", sensor.gpio());
|
||||
pinMode(sensor.gpio(), INPUT_PULLUP);
|
||||
sensor.set_value(digitalRead(sensor.gpio())); // initial value
|
||||
sensor.set_uom(0); // no uom, just for safe measures
|
||||
sensor.polltime_ = 0;
|
||||
sensor.poll_ = digitalRead(sensor.gpio());
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
LOG_DEBUG("Digital Write on GPIO %02d", sensor.gpio());
|
||||
pinMode(sensor.gpio(), OUTPUT);
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (sensor.gpio() == 25 || sensor.gpio() == 26) {
|
||||
if (sensor.offset() > 255) {
|
||||
sensor.set_offset(255);
|
||||
} else if (sensor.offset() < 0) {
|
||||
sensor.set_offset(0);
|
||||
}
|
||||
dacWrite(sensor.gpio(), sensor.offset());
|
||||
sensor.set_value(sensor.offset());
|
||||
} else
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
if (sensor.gpio() == 17 || sensor.gpio() == 18) {
|
||||
if (sensor.offset() > 255) {
|
||||
sensor.set_offset(255);
|
||||
} else if (sensor.offset() < 0) {
|
||||
sensor.set_offset(0);
|
||||
}
|
||||
dacWrite(sensor.gpio(), sensor.offset());
|
||||
sensor.set_value(sensor.offset());
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (sensor.uom() == 0) { // set state from NVS
|
||||
if (!get_nvs || EMSESP::nvs_.getChar(sensor.name().c_str(), -1) == -1) {
|
||||
EMSESP::nvs_.putChar(sensor.name().c_str(), (int8_t)sensor.offset());
|
||||
} else {
|
||||
sensor.set_offset(EMSESP::nvs_.getChar(sensor.name().c_str()));
|
||||
}
|
||||
}
|
||||
digitalWrite(sensor.gpio(), (sensor.offset() == 0) ^ (sensor.factor() > 0));
|
||||
sensor.set_value(sensor.offset());
|
||||
}
|
||||
publish_sensor(sensor);
|
||||
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
||||
LOG_DEBUG("PWM output on GPIO %02d", sensor.gpio());
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
ledcAttach(sensor.gpio(), sensor.factor(), 13);
|
||||
#else
|
||||
uint8_t channel = sensor.type() - AnalogType::PWM_0;
|
||||
ledcSetup(channel, sensor.factor(), 13);
|
||||
ledcAttachPin(sensor.gpio(), channel);
|
||||
#endif
|
||||
if (sensor.offset() > 100) {
|
||||
sensor.set_offset(100);
|
||||
} else if (sensor.offset() < 0) {
|
||||
sensor.set_offset(0);
|
||||
}
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
||||
#else
|
||||
ledcWrite(channel, (uint32_t)(sensor.offset() * 8191 / 100));
|
||||
#endif
|
||||
sensor.set_value(sensor.offset());
|
||||
sensor.set_uom(DeviceValueUOM::PERCENT);
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// measure input sensors and moving average adc
|
||||
void AnalogSensor::measure() {
|
||||
static uint32_t measure_last_ = uuid::get_uptime() - MEASURE_ANALOG_INTERVAL;
|
||||
|
||||
// measure interval 500ms for adc sensors
|
||||
if ((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.gpio()); // 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 with debounce
|
||||
// go through the list of digital sensors
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::TIMER
|
||||
|| sensor.type() == AnalogType::RATE) {
|
||||
auto old_value = sensor.value(); // remember current value before reading
|
||||
auto current_reading = digitalRead(sensor.gpio());
|
||||
if (sensor.poll_ != current_reading) { // check for pinchange
|
||||
sensor.polltime_ = uuid::get_uptime(); // remember time of pinchange
|
||||
sensor.poll_ = current_reading;
|
||||
}
|
||||
// debounce and check for real pinchange
|
||||
if (uuid::get_uptime() - sensor.polltime_ >= 15 && sensor.poll_ != sensor.last_reading_) {
|
||||
sensor.last_reading_ = sensor.poll_;
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
sensor.set_value(sensor.poll_);
|
||||
} else if (!sensor.poll_) { // falling edge
|
||||
if (sensor.type() == AnalogType::COUNTER) {
|
||||
sensor.set_value(old_value + sensor.factor());
|
||||
// EMSESP::nvs_.putDouble(sensor.name().c_str(), sensor.value());
|
||||
} else if (sensor.type() == AnalogType::RATE) { // default uom: Hz (1/sec) with factor 1
|
||||
sensor.set_value(sensor.factor() * 1000 / (sensor.polltime_ - sensor.last_polltime_));
|
||||
} else if (sensor.type() == AnalogType::TIMER) { // default seconds with factor 1
|
||||
sensor.set_value(sensor.factor() * (sensor.polltime_ - sensor.last_polltime_) / 1000);
|
||||
}
|
||||
sensor.last_polltime_ = sensor.polltime_;
|
||||
}
|
||||
}
|
||||
|
||||
// see if there is a change and increment # reads
|
||||
if (old_value != sensor.value()) {
|
||||
sensorreads_++;
|
||||
changed_ = true;
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store counter-values only every hour to reduce flash wear
|
||||
static uint8_t lastSaveHour = 0;
|
||||
time_t now = time(nullptr);
|
||||
tm * tm_ = localtime(&now);
|
||||
if (tm_->tm_hour != lastSaveHour) {
|
||||
lastSaveHour = tm_->tm_hour;
|
||||
store_counters();
|
||||
}
|
||||
}
|
||||
|
||||
// store counters to NVS, called every hour, on restart and update
|
||||
void AnalogSensor::store_counters() {
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() == AnalogType::COUNTER) {
|
||||
if (sensor.value() != EMSESP::nvs_.getDouble(sensor.name().c_str())) {
|
||||
EMSESP::nvs_.putDouble(sensor.name().c_str(), sensor.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogSensor::loop() {
|
||||
if (!analog_enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
measure(); // take the measurements
|
||||
}
|
||||
|
||||
// update analog information name and offset
|
||||
// a type value of -1 is used to delete the sensor
|
||||
bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted) {
|
||||
// first see if we can find the sensor in our customization list
|
||||
bool found_sensor = false;
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
for (auto & AnalogCustomization : settings.analogCustomizations) {
|
||||
if (AnalogCustomization.type == AnalogType::COUNTER || AnalogCustomization.type >= AnalogType::DIGITAL_OUT) {
|
||||
Command::erase_command(EMSdevice::DeviceType::ANALOGSENSOR, AnalogCustomization.name.c_str());
|
||||
}
|
||||
if (name.empty()) {
|
||||
char n[20];
|
||||
snprintf(n, sizeof(n), "%s_%02d", FL_(AnalogTypeName)[type], gpio);
|
||||
name = n;
|
||||
}
|
||||
if (AnalogCustomization.gpio == gpio) {
|
||||
found_sensor = true; // found the record
|
||||
// see if it's marked for deletion
|
||||
if (deleted) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name.c_str());
|
||||
LOG_DEBUG("Removing analog sensor GPIO %02d", gpio);
|
||||
settings.analogCustomizations.remove(AnalogCustomization);
|
||||
} else {
|
||||
// update existing record
|
||||
if (name != AnalogCustomization.name) {
|
||||
EMSESP::nvs_.remove(AnalogCustomization.name.c_str());
|
||||
}
|
||||
AnalogCustomization.name = name;
|
||||
AnalogCustomization.offset = offset;
|
||||
AnalogCustomization.factor = factor;
|
||||
AnalogCustomization.uom = uom;
|
||||
AnalogCustomization.type = type;
|
||||
LOG_DEBUG("Customizing existing analog GPIO %02d", gpio);
|
||||
}
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
}
|
||||
}
|
||||
return StateUpdateResult::UNCHANGED;
|
||||
});
|
||||
|
||||
// if the sensor exists and we're using HA, delete the old HA record
|
||||
if (found_sensor && Mqtt::ha_enabled()) {
|
||||
remove_ha_topic(type, gpio); // the GPIO
|
||||
}
|
||||
|
||||
// we didn't find it, it's new, so create and store it in the customization list
|
||||
if (!found_sensor) {
|
||||
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
|
||||
auto newSensor = AnalogCustomization();
|
||||
newSensor.gpio = gpio;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
newSensor.factor = factor;
|
||||
newSensor.uom = uom;
|
||||
newSensor.type = type;
|
||||
settings.analogCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for analog sensor GPIO %02d", gpio);
|
||||
return StateUpdateResult::CHANGED; // persist the change
|
||||
});
|
||||
}
|
||||
|
||||
// reloads the sensors in the customizations file into the sensors list
|
||||
reload();
|
||||
|
||||
// return false if it's an invalid GPIO, an error will show in WebUI
|
||||
// and reported as an error in the log
|
||||
return System::is_valid_gpio(gpio);
|
||||
}
|
||||
|
||||
// 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(const Sensor & sensor) const {
|
||||
if (Mqtt::publish_single()) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", F_(analogsensor), sensor.name().c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(analogsensor), "_data", sensor.name().c_str());
|
||||
}
|
||||
char payload[10];
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.value(), 2)); // always publish as doubles
|
||||
}
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", F_(analogsensor), sensor.name().c_str());
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
}
|
||||
|
||||
// send empty config topic to remove the entry from HA
|
||||
void AnalogSensor::remove_ha_topic(const int8_t type, const uint8_t gpio) const {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Removing HA config for analog sensor GPIO %02d", gpio);
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (type == AnalogType::DIGITAL_OUT && gpio != 25 && gpio != 26) {
|
||||
#else
|
||||
if (type == AnalogType::DIGITAL_OUT) {
|
||||
#endif
|
||||
snprintf(topic, sizeof(topic), "switch/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
|
||||
} else if (type == AnalogType::DIGITAL_OUT) { // DAC
|
||||
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
|
||||
} else if (type >= AnalogType::PWM_0) {
|
||||
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
|
||||
} else if (type == AnalogType::DIGITAL_IN) {
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), gpio);
|
||||
}
|
||||
Mqtt::queue_remove_topic(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);
|
||||
}
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.type() != AnalogType::NOTUSED) {
|
||||
if (Mqtt::is_nested()) {
|
||||
char s[10];
|
||||
JsonObject dataSensor = doc[Helpers::smallitoa(s, sensor.gpio())].to<JsonObject>();
|
||||
dataSensor["name"] = sensor.name();
|
||||
switch (sensor.type()) {
|
||||
case AnalogType::COUNTER:
|
||||
case AnalogType::TIMER:
|
||||
case AnalogType::RATE:
|
||||
case AnalogType::ADC:
|
||||
case AnalogType::PWM_0:
|
||||
case AnalogType::PWM_1:
|
||||
case AnalogType::PWM_2:
|
||||
dataSensor["value"] = serialized(Helpers::render_value(s, sensor.value(), 2)); // double
|
||||
break;
|
||||
case AnalogType::DIGITAL_IN:
|
||||
case AnalogType::DIGITAL_OUT:
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
dataSensor["value"] = sensor.value() != 0;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
dataSensor["value"] = sensor.value() != 0 ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
dataSensor["value"] = Helpers::render_boolean(result, sensor.value() != 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||
doc[sensor.name()] = sensor.value() != 0;
|
||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||
doc[sensor.name()] = sensor.value() != 0 ? 1 : 0;
|
||||
} else {
|
||||
char result[12];
|
||||
doc[sensor.name()] = Helpers::render_boolean(result, sensor.value() != 0);
|
||||
}
|
||||
} else {
|
||||
char s[10];
|
||||
doc[sensor.name()] = serialized(Helpers::render_value(s, sensor.value(), 2));
|
||||
}
|
||||
|
||||
// create HA config if hasn't already been done
|
||||
if (Mqtt::ha_enabled() && (!sensor.ha_registered || force)) {
|
||||
LOG_DEBUG("Recreating HA config for analog sensor GPIO %02d", sensor.gpio());
|
||||
|
||||
JsonDocument config;
|
||||
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/%s_data", Mqtt::base().c_str(), F_(analogsensor)); // use base path
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
char val_obj[50];
|
||||
char val_cond[95];
|
||||
if (Mqtt::is_nested()) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%02d']['value']", sensor.gpio());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%02d'] is defined and %s is defined", sensor.gpio(), val_obj);
|
||||
} else {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
char sample_val[12] = "0";
|
||||
if (sensor.type() == AnalogType::DIGITAL_IN || sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
Helpers::render_boolean(sample_val, false);
|
||||
}
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
|
||||
|
||||
char uniq_s[70];
|
||||
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "%s_%s_%02d", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||
} else {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "%s_%02d", F_(analogsensor), sensor.gpio());
|
||||
}
|
||||
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id
|
||||
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s", sensor.name().c_str());
|
||||
config["name"] = name;
|
||||
|
||||
if (sensor.uom() != DeviceValueUOM::NONE) {
|
||||
config["unit_of_meas"] = EMSdevice::uom_to_string(sensor.uom());
|
||||
}
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
|
||||
// Set commands for some analog types
|
||||
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (sensor.type() == AnalogType::DIGITAL_OUT && sensor.gpio() != 25 && sensor.gpio() != 26) {
|
||||
#else
|
||||
if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
#endif
|
||||
snprintf(topic, sizeof(topic), "switch/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
Mqtt::add_ha_bool(config);
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) { // DAC
|
||||
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
config["min"] = 0;
|
||||
config["max"] = 255;
|
||||
config["mode"] = "box"; // auto, slider or box
|
||||
config["step"] = 1;
|
||||
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
||||
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
config["min"] = 0;
|
||||
config["max"] = 100;
|
||||
config["mode"] = "box"; // auto, slider or box
|
||||
config["step"] = 0.1;
|
||||
} else if (sensor.type() == AnalogType::COUNTER) {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
||||
config["cmd_t"] = command_topic;
|
||||
config["stat_cla"] = "total_increasing";
|
||||
// config["mode"] = "box"; // auto, slider or box
|
||||
// config["step"] = sensor.factor();
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||
Mqtt::add_ha_bool(config);
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||
config["stat_cla"] = "measurement";
|
||||
}
|
||||
|
||||
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
|
||||
bool is_ha_device_created = false;
|
||||
for (auto const & sensor : sensors_) {
|
||||
if (sensor.ha_registered) {
|
||||
is_ha_device_created = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::add_ha_sections_to_doc("analog", stat_t, config, !is_ha_device_created, val_cond);
|
||||
|
||||
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "%s_data", F_(analogsensor));
|
||||
Mqtt::queue_publish(topic, doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
// called from emsesp.cpp for commands
|
||||
// searches sensor by name
|
||||
bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int8_t id) {
|
||||
if (sensors_.empty()) {
|
||||
return true; // no sensors, return true
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
output[sensor.name()] = sensor.value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(entities))) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
get_value_json(output[sensor.name()].to<JsonObject>(), sensor);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is for a specific sensor
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
// match custom name or sensor GPIO
|
||||
if (cmd == Helpers::toLower(sensor.name()) || Helpers::atoint(cmd) == sensor.gpio()) {
|
||||
get_value_json(output, sensor);
|
||||
return Command::set_attribute(output, cmd, attribute_s);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||
output["name"] = sensor.name();
|
||||
output["fullname"] = sensor.name();
|
||||
output["gpio"] = sensor.gpio();
|
||||
output["type"] = F_(number);
|
||||
output["analog"] = FL_(list_sensortype)[sensor.type()];
|
||||
output["value"] = sensor.value();
|
||||
output["readable"] = true;
|
||||
output["writeable"] = sensor.type() == AnalogType::COUNTER || (sensor.type() >= AnalogType::DIGITAL_OUT && sensor.type() <= AnalogType::PWM_2);
|
||||
output["visible"] = true;
|
||||
if (sensor.type() == AnalogType::COUNTER) {
|
||||
output["min"] = 0;
|
||||
output["max"] = 4000000;
|
||||
output["start_value"] = sensor.offset();
|
||||
output["factor"] = sensor.factor();
|
||||
output["uom"] = EMSdevice::uom_to_string(sensor.uom());
|
||||
} else if (sensor.type() == AnalogType::ADC) {
|
||||
output["offset"] = sensor.offset();
|
||||
output["factor"] = sensor.factor();
|
||||
output["uom"] = EMSdevice::uom_to_string(sensor.uom());
|
||||
} else if (sensor.type() == AnalogType::TIMER || sensor.type() == AnalogType::RATE) {
|
||||
output["factor"] = sensor.factor();
|
||||
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
||||
output["frequency"] = sensor.factor();
|
||||
output["min"] = 0;
|
||||
output["max"] = 100;
|
||||
output["uom"] = EMSdevice::uom_to_string(sensor.uom());
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
output["min"] = 0;
|
||||
output["max"] = sensor.gpio() == 25 || sensor.gpio() == 26 ? 255 : 1;
|
||||
char state[][2] = {"?", "0", "1"};
|
||||
output["start"] = state[sensor.uom()];
|
||||
}
|
||||
}
|
||||
|
||||
// this creates the sensor, initializing everything
|
||||
AnalogSensor::Sensor::Sensor(const uint8_t gpio, const std::string & name, const double offset, const double factor, const uint8_t uom, const int8_t type)
|
||||
: gpio_(gpio)
|
||||
, name_(name)
|
||||
, offset_(offset)
|
||||
, factor_(factor)
|
||||
, uom_(uom)
|
||||
, type_(type) {
|
||||
value_ = 0; // init value to 0 always
|
||||
}
|
||||
|
||||
// set the dig_out/counter/DAC/PWM value, id is gpio-no
|
||||
bool AnalogSensor::command_setvalue(const char * value, const int8_t gpio) {
|
||||
float val;
|
||||
if (!Helpers::value2float(value, val)) {
|
||||
bool b;
|
||||
if (!Helpers::value2bool(value, b)) {
|
||||
return false;
|
||||
}
|
||||
val = b ? 1 : 0;
|
||||
}
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.gpio() == gpio) {
|
||||
double oldoffset = sensor.offset();
|
||||
if (sensor.type() == AnalogType::COUNTER) {
|
||||
if (val < 0 || value[0] == '+') { // sign corrects values
|
||||
sensor.set_offset(sensor.value() + val);
|
||||
sensor.set_value(sensor.value() + val);
|
||||
} else { // positive values are set
|
||||
sensor.set_offset(val);
|
||||
sensor.set_value(val);
|
||||
}
|
||||
if (oldoffset != sensor.offset() && sensor.offset() != EMSESP::nvs_.getDouble(sensor.name().c_str())) {
|
||||
EMSESP::nvs_.putDouble(sensor.name().c_str(), sensor.value());
|
||||
}
|
||||
} else if (sensor.type() == AnalogType::ADC) {
|
||||
sensor.set_offset(val);
|
||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) {
|
||||
uint8_t v = val;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if ((sensor.gpio() == 25 || sensor.gpio() == 26) && v <= 255) {
|
||||
sensor.set_offset(v);
|
||||
sensor.set_value(v);
|
||||
pinMode(sensor.gpio(), OUTPUT);
|
||||
dacWrite(sensor.gpio(), sensor.offset());
|
||||
} else
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
if ((sensor.gpio() == 17 || sensor.gpio() == 18) && v <= 255) {
|
||||
sensor.set_offset(v);
|
||||
sensor.set_value(v);
|
||||
pinMode(sensor.gpio(), OUTPUT);
|
||||
dacWrite(sensor.gpio(), sensor.offset());
|
||||
} else
|
||||
#endif
|
||||
if (v == 0 || v == 1) {
|
||||
sensor.set_offset(v);
|
||||
sensor.set_value(v);
|
||||
pinMode(sensor.gpio(), OUTPUT);
|
||||
digitalWrite(sensor.gpio(), (sensor.offset() == 0) ^ (sensor.factor() > 0));
|
||||
if (sensor.uom() == 0 && EMSESP::nvs_.getChar(sensor.name().c_str()) != (int8_t)sensor.offset()) {
|
||||
EMSESP::nvs_.putChar(sensor.name().c_str(), (int8_t)sensor.offset());
|
||||
}
|
||||
}
|
||||
} else if (sensor.type() >= AnalogType::PWM_0 && sensor.type() <= AnalogType::PWM_2) {
|
||||
if (val > 100) {
|
||||
val = 100;
|
||||
} else if (val < 0) {
|
||||
val = 0;
|
||||
}
|
||||
sensor.set_offset(val);
|
||||
sensor.set_value(val);
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
ledcWrite(sensor.gpio(), (uint32_t)(sensor.offset() * 8191 / 100));
|
||||
#else
|
||||
uint8_t channel = sensor.type() - AnalogType::PWM_0;
|
||||
ledcWrite(channel, (uint32_t)(val * 8191 / 100));
|
||||
#endif
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (oldoffset != sensor.offset()) {
|
||||
publish_sensor(sensor);
|
||||
changed_ = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
190
src/core/analogsensor.h
Normal file
190
src/core/analogsensor.h
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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"
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// names, same order as AnalogType
|
||||
MAKE_ENUM_FIXED(AnalogTypeName, "disabled", "dig_in", "counter", "adc", "timer", "rate", "dig_out", "pwm0", "pwm1", "pwm2")
|
||||
|
||||
class AnalogSensor {
|
||||
public:
|
||||
class Sensor {
|
||||
public:
|
||||
Sensor(const uint8_t gpio, const std::string & name, const double offset, const double factor, const uint8_t uom, const int8_t type);
|
||||
~Sensor() = default;
|
||||
|
||||
void set_offset(const double offset) {
|
||||
offset_ = offset;
|
||||
}
|
||||
|
||||
std::string name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
void set_name(const std::string & name) {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
uint8_t gpio() const {
|
||||
return gpio_;
|
||||
}
|
||||
|
||||
double value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void set_value(const double value) {
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
double factor() const {
|
||||
return factor_;
|
||||
}
|
||||
|
||||
void set_factor(const double factor) {
|
||||
factor_ = factor;
|
||||
}
|
||||
|
||||
double 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(const 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
|
||||
uint16_t count_ = 0; // counter raw counts
|
||||
uint32_t polltime_ = 0; // digital IO & COUNTER debounce time
|
||||
int poll_ = 0;
|
||||
uint32_t last_polltime_ = 0; // for timer
|
||||
|
||||
private:
|
||||
uint8_t gpio_;
|
||||
std::string name_;
|
||||
double offset_;
|
||||
double factor_;
|
||||
uint8_t uom_;
|
||||
double value_; // double because of the factor is a double
|
||||
int8_t type_; // one of the AnalogType enum
|
||||
};
|
||||
|
||||
AnalogSensor() = default;
|
||||
~AnalogSensor() = default;
|
||||
|
||||
enum AnalogType : int8_t {
|
||||
NOTUSED, // 0 - disabled
|
||||
DIGITAL_IN,
|
||||
COUNTER,
|
||||
ADC,
|
||||
TIMER,
|
||||
RATE,
|
||||
DIGITAL_OUT,
|
||||
PWM_0,
|
||||
PWM_1,
|
||||
PWM_2
|
||||
};
|
||||
|
||||
void start();
|
||||
void loop();
|
||||
void publish_sensor(const Sensor & sensor) const;
|
||||
void publish_values(const bool force);
|
||||
void reload(bool get_nvs = false);
|
||||
bool updated_values();
|
||||
|
||||
// return back reference to the sensor list, used by other classes
|
||||
std::vector<Sensor> sensors() const {
|
||||
return sensors_;
|
||||
}
|
||||
|
||||
uint32_t reads() const {
|
||||
return sensorreads_;
|
||||
}
|
||||
|
||||
uint32_t fails() const {
|
||||
return sensorfails_;
|
||||
}
|
||||
|
||||
bool analog_enabled() const {
|
||||
return (analog_enabled_);
|
||||
}
|
||||
|
||||
bool have_sensors() const {
|
||||
return (!sensors_.empty());
|
||||
}
|
||||
|
||||
size_t count_entities(bool include_disabled = true) const {
|
||||
if (!include_disabled) {
|
||||
// count number of items in sensors_ where type is not set to disabled
|
||||
return std::count_if(sensors_.begin(), sensors_.end(), [](const Sensor & sensor) { return sensor.type() != AnalogSensor::AnalogType::NOTUSED; });
|
||||
}
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
bool update(uint8_t gpio, std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted = false);
|
||||
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
||||
void store_counters();
|
||||
|
||||
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 int8_t type, const uint8_t id) const;
|
||||
bool command_setvalue(const char * value, const int8_t gpio);
|
||||
void measure();
|
||||
void addSensorJson(JsonObject output, const Sensor & sensor);
|
||||
void get_value_json(JsonObject output, const Sensor & sensor);
|
||||
|
||||
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
|
||||
840
src/core/command.cpp
Normal file
840
src/core/command.cpp
Normal file
@@ -0,0 +1,840 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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 "command.h"
|
||||
#include "emsdevice.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger Command::logger_{F_(command), uuid::log::Facility::DAEMON};
|
||||
|
||||
std::vector<Command::CmdFunction> Command::cmdfunctions_;
|
||||
|
||||
// takes a URI path and a json body, parses the data and calls the command
|
||||
// the path is leading so if duplicate keys are in the input JSON it will be ignored
|
||||
// the entry point will be either via the Web API (api/) or MQTT (<base>/)
|
||||
// returns a return code and json output
|
||||
uint8_t Command::process(const char * path, const bool is_admin, const JsonObject input, JsonObject output) {
|
||||
SUrlParser p; // parse URL for the path names
|
||||
p.parse(path);
|
||||
|
||||
// check first if it's from API or MQTT, if so strip the "api/" or "<base>/" from the path
|
||||
if (p.paths().size() && ((p.paths().front() == "api") || (p.paths().front() == Mqtt::base()))) {
|
||||
p.paths().erase(p.paths().begin());
|
||||
} else {
|
||||
return json_message(CommandRet::ERROR, "invalid path", output, path); // error
|
||||
}
|
||||
|
||||
// re-calculate new path
|
||||
// if there is only a path (URL) and no body then error!
|
||||
size_t num_paths = p.paths().size();
|
||||
if (!num_paths && !input.size()) {
|
||||
return json_message(CommandRet::ERROR, "missing command in path", output);
|
||||
}
|
||||
|
||||
std::string cmd_s;
|
||||
int8_t id_n = -1; // default hc
|
||||
|
||||
// check for a device as first item in the path
|
||||
const char * device_s = nullptr;
|
||||
if (!num_paths) {
|
||||
// we must look for the device in the JSON body
|
||||
if (input["device"].is<const char *>()) {
|
||||
device_s = input["device"];
|
||||
}
|
||||
} else {
|
||||
// extract it from the path
|
||||
device_s = p.paths().front().c_str(); // get the device type name (boiler, thermostat, system etc)
|
||||
}
|
||||
|
||||
// validate the device, make sure it exists
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(device_s);
|
||||
if (!device_has_commands(device_type)) {
|
||||
char err[100];
|
||||
snprintf(err, sizeof(err), "unknown device %s", device_s);
|
||||
LOG_WARNING("Command failed: %s", err);
|
||||
output["message"] = err;
|
||||
return CommandRet::NOT_FOUND;
|
||||
}
|
||||
|
||||
// the next value on the path should be the command or entity name
|
||||
const char * command_p = nullptr;
|
||||
if (num_paths == 2) {
|
||||
command_p = p.paths()[1].c_str();
|
||||
} else if (num_paths == 3) {
|
||||
// concatenate the path into one string as it could be in the format 'hc/XXX'
|
||||
char command[COMMAND_MAX_LENGTH];
|
||||
snprintf(command, sizeof(command), "%s/%s", p.paths()[1].c_str(), p.paths()[2].c_str());
|
||||
command_p = command;
|
||||
} else if (num_paths > 3) {
|
||||
// concatenate the path into one string as it could be in the format 'hc/XXX/attribute'
|
||||
char command[COMMAND_MAX_LENGTH];
|
||||
snprintf(command, sizeof(command), "%s/%s/%s", p.paths()[1].c_str(), p.paths()[2].c_str(), p.paths()[3].c_str());
|
||||
command_p = command;
|
||||
} else {
|
||||
// take it from the JSON
|
||||
if (input["entity"].is<const char *>()) {
|
||||
command_p = input["entity"];
|
||||
} else if (input["cmd"].is<const char *>()) {
|
||||
command_p = input["cmd"];
|
||||
}
|
||||
}
|
||||
|
||||
// some commands may be prefixed with hc. dhw. or hc/ or dhw/ so extract these if they exist
|
||||
// parse_command_string returns the extracted command
|
||||
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
||||
command_p = parse_command_string(command_p, id_n);
|
||||
}
|
||||
if (command_p == nullptr) {
|
||||
// handle dead endpoints like api/system or api/boiler
|
||||
// default to 'value' for all devices
|
||||
if (num_paths < (id_n > 0 ? 4 : 3)) {
|
||||
command_p = F_(values);
|
||||
} else {
|
||||
return json_message(CommandRet::NOT_FOUND, "missing or bad command", output);
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't have an id/hc/dhw try and get it from the JSON input. It must be a number.
|
||||
// it's allowed to have no id, and then keep the default to -1
|
||||
if (id_n == -1) {
|
||||
if (input["hc"].is<int>()) {
|
||||
id_n = input["hc"];
|
||||
} else if (input["dhw"].is<int>()) {
|
||||
id_n = input["dhw"];
|
||||
id_n += DeviceValueTAG::TAG_DHW1 - DeviceValueTAG::TAG_HC1; // dhw1 has id 9
|
||||
} else if (input["id"].is<int>()) {
|
||||
id_n = input["id"];
|
||||
} else if (input["ahs"].is<int>()) {
|
||||
id_n = input["ahs"];
|
||||
id_n += DeviceValueTAG::TAG_AHS1 - DeviceValueTAG::TAG_HC1; // ahs1 has id 19
|
||||
} else if (input["hs"].is<int>()) {
|
||||
id_n = input["hs"];
|
||||
id_n += DeviceValueTAG::TAG_HS1 - DeviceValueTAG::TAG_HC1; // hs1 has id 20
|
||||
}
|
||||
}
|
||||
|
||||
// the value must always come from the input JSON. It's allowed to be empty.
|
||||
JsonVariant data;
|
||||
if (input["data"].is<JsonVariantConst>()) {
|
||||
data = input["data"];
|
||||
} else if (input["value"].is<JsonVariantConst>()) {
|
||||
data = input["value"];
|
||||
}
|
||||
|
||||
// check if data is entity like device/hc/name/value
|
||||
if (data.is<const char *>()) {
|
||||
const char * d = data.as<const char *>();
|
||||
if (strlen(d)) {
|
||||
char * device_end = (char *)strchr(d, '/');
|
||||
if (device_end != nullptr) {
|
||||
char device_s[20] = {'\0'};
|
||||
const char * device_p = device_s;
|
||||
const char * data_p = nullptr;
|
||||
strlcpy(device_s, d, device_end - d + 1);
|
||||
data_p = device_end + 1;
|
||||
int8_t id_d = -1;
|
||||
uint8_t device_type1 = EMSdevice::device_name_2_device_type(device_p);
|
||||
if (device_type1 >= EMSdevice::DeviceType::BOILER) {
|
||||
data_p = parse_command_string(data_p, id_d);
|
||||
}
|
||||
if (data_p == nullptr) {
|
||||
return CommandRet::INVALID;
|
||||
}
|
||||
|
||||
char data_s[COMMAND_MAX_LENGTH];
|
||||
strlcpy(data_s, Helpers::toLower(data_p).c_str(), 30);
|
||||
if (strstr(data_s, "/value") == nullptr) {
|
||||
strlcat(data_s, "/value", sizeof(data_s) - 6);
|
||||
}
|
||||
|
||||
if (Command::call(device_type1, data_s, "", true, id_d, output) != CommandRet::OK) {
|
||||
return CommandRet::INVALID;
|
||||
}
|
||||
|
||||
if (output["api_data"].is<std::string>()) {
|
||||
std::string api_data = output["api_data"];
|
||||
output.clear();
|
||||
return Command::call(device_type, command_p, api_data.c_str(), is_admin, id_n, output);
|
||||
}
|
||||
|
||||
return CommandRet::INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// call the command based on the type
|
||||
uint8_t return_code = CommandRet::ERROR;
|
||||
if (data.is<const char *>()) {
|
||||
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.as<int32_t>(), 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, data.as<float>(), 2), is_admin, id_n, output);
|
||||
} else if (data.is<bool>()) {
|
||||
return_code = Command::call(device_type, command_p, data.as<bool>() ? "1" : "0", is_admin, id_n, output);
|
||||
} else if (data.isNull()) {
|
||||
return_code = Command::call(device_type, command_p, "", is_admin, id_n, output); // empty, will do a query instead
|
||||
} else {
|
||||
return json_message(CommandRet::ERROR, "cannot parse command", output); // can't process
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
|
||||
const char * Command::return_code_string(const uint8_t return_code) {
|
||||
switch (return_code) {
|
||||
case CommandRet::ERROR:
|
||||
return "Error";
|
||||
case CommandRet::OK:
|
||||
return "OK";
|
||||
case CommandRet::NOT_FOUND:
|
||||
return "Not Found";
|
||||
case CommandRet::NOT_ALLOWED:
|
||||
return "Not Authorized";
|
||||
case CommandRet::FAIL:
|
||||
return "Failed";
|
||||
case CommandRet::INVALID:
|
||||
return "Invalid";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
char s[4];
|
||||
return Helpers::smallitoa(s, return_code);
|
||||
}
|
||||
|
||||
// takes a string like "hc1/seltemp" or "seltemp" or "dhw2.seltemp" and tries to get the id and cmd
|
||||
// returns start position of the command string
|
||||
const char * Command::parse_command_string(const char * command, int8_t & id) {
|
||||
if (command == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// remember unchanged command
|
||||
const char * cmd_org = command;
|
||||
int8_t id_org = id;
|
||||
|
||||
// convert cmd to lowercase and compare
|
||||
char * lowerCmd = strdup(command);
|
||||
for (char * p = lowerCmd; *p; p++) {
|
||||
*p = tolower(*p);
|
||||
}
|
||||
|
||||
// check prefix and valid number range, also check 'id'
|
||||
if (!strncmp(lowerCmd, "hc", 2) && command[2] >= '1' && command[2] <= '8') {
|
||||
id = command[2] - '0';
|
||||
command += 3;
|
||||
} else if (!strncmp(lowerCmd, "dhw", 3) && command[3] == '1' && command[4] == '0') {
|
||||
id = DeviceValueTAG::TAG_DHW10; //18;
|
||||
command += 5;
|
||||
} else if (!strncmp(lowerCmd, "dhw", 3) && command[3] >= '1' && command[3] <= '9') {
|
||||
id = command[3] - '1' + DeviceValueTAG::TAG_DHW1; //9;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "id", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '9') {
|
||||
id = command[3] - '0' + 10;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "id", 2) && command[2] >= '1' && command[2] <= '9') {
|
||||
id = command[2] - '0';
|
||||
command += 3;
|
||||
} else if (!strncmp(lowerCmd, "ahs", 3) && command[3] >= '1' && command[3] <= '1') { // only ahs1 for now
|
||||
id = command[3] - '1' + DeviceValueTAG::TAG_AHS1; // 19;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "hs", 2) && command[2] == '1' && command[3] >= '0' && command[3] <= '6') {
|
||||
id = command[3] - '0' + DeviceValueTAG::TAG_HS10; //29;
|
||||
command += 4;
|
||||
} else if (!strncmp(lowerCmd, "hs", 2) && command[2] >= '1' && command[2] <= '9') {
|
||||
id = command[2] - '1' + DeviceValueTAG::TAG_HS1; //20;
|
||||
command += 3;
|
||||
} else if (!strncmp(lowerCmd, "dhw", 3)) { // no number
|
||||
id = DeviceValueTAG::TAG_DHW1;
|
||||
command += 3;
|
||||
}
|
||||
|
||||
free(lowerCmd);
|
||||
|
||||
// return original if no seperator
|
||||
if (command[0] != '/' && command[0] != '.') {
|
||||
id = id_org;
|
||||
return cmd_org;
|
||||
}
|
||||
command++;
|
||||
|
||||
// return null for empty command
|
||||
if (command[0] == '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
// check if command contains an attribute
|
||||
const char * Command::get_attribute(const char * cmd) {
|
||||
char * breakp = (char *)strchr(cmd, '/');
|
||||
if (breakp) {
|
||||
*breakp = '\0';
|
||||
return breakp + 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Command::set_attribute(JsonObject output, const char * cmd, const char * attribute) {
|
||||
if (attribute == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (output[attribute].is<JsonVariantConst>()) {
|
||||
std::string data = output[attribute].as<std::string>();
|
||||
output.clear();
|
||||
output["api_data"] = data; // always as a string
|
||||
return true;
|
||||
}
|
||||
return EMSESP::return_not_found(output, attribute, cmd); // not found
|
||||
}
|
||||
|
||||
// calls a command directly
|
||||
uint8_t Command::call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id) {
|
||||
// create a temporary buffer
|
||||
JsonDocument output_doc;
|
||||
JsonObject output = output_doc.to<JsonObject>();
|
||||
|
||||
// authenticated is always true and ID is the default value
|
||||
return call(device_type, cmd, value, true, id, output);
|
||||
}
|
||||
|
||||
// calls a command. Takes a json object for output.
|
||||
// id may be used to represent a heating circuit for example
|
||||
// returns 0 if the command errored, 1 (TRUE) if ok, 2 if not found, 3 if error or 4 if not allowed
|
||||
uint8_t Command::call(const uint8_t device_type, const char * command, const char * value, const bool is_admin, const int8_t id, JsonObject output) {
|
||||
if (command == nullptr) {
|
||||
return CommandRet::NOT_FOUND;
|
||||
}
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
strlcpy(cmd, Helpers::toLower(command).c_str(), sizeof(cmd));
|
||||
|
||||
auto dname = EMSdevice::device_type_2_device_name(device_type); // device name, not translated
|
||||
|
||||
// check first if there is only a command being called without a value
|
||||
// it could be an endpoint like a device's entity or attribute e.g. api/boiler/nrgheat or /api/boiler/nrgheat/value
|
||||
// or a special command like 'info', 'values', 'commands', 'entities' etc
|
||||
bool single_command = (!value || !strlen(value));
|
||||
if (single_command) {
|
||||
if (!strcmp(cmd, F_(commands))) {
|
||||
return Command::list(device_type, output);
|
||||
}
|
||||
if (EMSESP::get_device_value_info(output, cmd, id, device_type)) { // entity = cmd
|
||||
LOG_DEBUG("Fetched device entity/attributes for %s/%s", dname, cmd);
|
||||
return CommandRet::OK;
|
||||
}
|
||||
} else if (device_type == EMSdevice::DeviceType::SYSTEM && strchr(cmd, '/')) {
|
||||
// check service commands, if not found continue with commandsfunctions
|
||||
if (EMSESP::system_.command_service(cmd, value)) {
|
||||
return CommandRet::OK;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t device_id = EMSESP::device_id_from_cmd(device_type, cmd, id);
|
||||
|
||||
// determine flags based on id (which is the tag)
|
||||
uint8_t flag = CommandFlag::CMD_FLAG_DEFAULT;
|
||||
if (id >= DeviceValueTAG::TAG_HC1 && id <= DeviceValueTAG::TAG_HC8) {
|
||||
flag = CommandFlag::CMD_FLAG_HC;
|
||||
} else if (id >= DeviceValueTAG::TAG_DHW1 && id <= DeviceValueTAG::TAG_DHW10) {
|
||||
flag = CommandFlag::CMD_FLAG_DHW;
|
||||
} else if (id >= DeviceValueTAG::TAG_HS1 && id <= DeviceValueTAG::TAG_HS16) {
|
||||
flag = CommandFlag::CMD_FLAG_HS;
|
||||
} else if (id >= DeviceValueTAG::TAG_AHS1 && id <= DeviceValueTAG::TAG_AHS1) {
|
||||
flag = CommandFlag::CMD_FLAG_AHS;
|
||||
}
|
||||
|
||||
// see if there is a command registered and it's valid
|
||||
auto cf = find_command(device_type, device_id, cmd, flag);
|
||||
if (!cf) {
|
||||
// if we don't already have a message set, set it to invalid command
|
||||
if (output["message"]) {
|
||||
LOG_WARNING("Command failed: %s", output["message"].as<const char *>());
|
||||
} else {
|
||||
std::string err = "no " + std::string(cmd) + " in " + dname;
|
||||
output["message"] = err;
|
||||
LOG_WARNING("Command failed: %s", err.c_str());
|
||||
}
|
||||
return CommandRet::ERROR;
|
||||
}
|
||||
|
||||
// before calling the command, check permissions and abort if not authorized
|
||||
if (cf->has_flags(CommandFlag::ADMIN_ONLY) && !is_admin) {
|
||||
LOG_WARNING("Command failed: authentication failed");
|
||||
output["message"] = "authentication failed";
|
||||
return CommandRet::NOT_ALLOWED; // command not allowed
|
||||
}
|
||||
|
||||
// call the function based on command function type
|
||||
// commands return true or false only (bool)
|
||||
uint8_t return_code = CommandRet::OK;
|
||||
if (cf->cmdfunction_json_) {
|
||||
// handle commands that report back a JSON body
|
||||
return_code = ((cf->cmdfunction_json_)(value, id, output)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
} else if (cf->cmdfunction_) {
|
||||
// if it's a read only command and we're trying to set a value, return an error
|
||||
if (!single_command && EMSESP::cmd_is_readonly(device_type, device_id, cmd, id)) {
|
||||
return_code = CommandRet::INVALID; // error on readonly or invalid hc
|
||||
} else {
|
||||
// call the command...
|
||||
return_code = ((cf->cmdfunction_)(value, id)) ? CommandRet::OK : CommandRet::ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// report back. If not OK show output from error, otherwise return the error code
|
||||
if (return_code != CommandRet::OK) {
|
||||
char error[100];
|
||||
if (single_command) {
|
||||
snprintf(error, sizeof(error), "Command %s failed (%s)", cmd, return_code_string(return_code));
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Command %s: %s failed (%s)", cmd, value, return_code_string(return_code));
|
||||
}
|
||||
output.clear();
|
||||
output["message"] = error;
|
||||
LOG_WARNING(error);
|
||||
} else {
|
||||
// build up the log string for reporting back
|
||||
// We send the log message as Warning so it appears in the log (debug is only enabled when compiling with DEBUG)
|
||||
std::string ro = EMSESP::system_.readonly_mode() ? "[readonly] " : "";
|
||||
auto description = Helpers::translated_word(cf->description_);
|
||||
char info_s[100];
|
||||
if (strlen(description)) {
|
||||
snprintf(info_s, sizeof(info_s), "%s/%s (%s)", dname, cmd, description);
|
||||
} else {
|
||||
snprintf(info_s, sizeof(info_s), "%s/%s", dname, cmd);
|
||||
}
|
||||
if (single_command) {
|
||||
// log as DEBUG (TRACE) regardless if compiled with EMSESP_DEBUG
|
||||
logger_.debug("%sCalled command %s", ro.c_str(), info_s);
|
||||
} else {
|
||||
if (id > 0) {
|
||||
LOG_INFO(("%sCalled command %s with value %s and id %d on device 0x%02X"), ro.c_str(), info_s, value, id, device_id);
|
||||
} else {
|
||||
LOG_INFO(("%sCalled command %s with value %s"), ro.c_str(), info_s, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
||||
// add a command to the list, which does not return json
|
||||
void Command::add(const uint8_t device_type, const uint8_t device_id, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
|
||||
// if the command already exists for that device type don't add it
|
||||
if (find_command(device_type, device_id, cmd, flags) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the description is empty, it's hidden which means it will not show up in Web API or Console as an available command
|
||||
if (!description) {
|
||||
flags |= CommandFlag::HIDDEN;
|
||||
}
|
||||
|
||||
cmdfunctions_.emplace_back(device_type, device_id, flags, cmd, cb, nullptr, description); // callback for json is nullptr
|
||||
}
|
||||
|
||||
// add a command with no json output
|
||||
// system/temperature/analog devices uses device_id 0
|
||||
void Command::add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags) {
|
||||
add(device_type, 0, cmd, cb, description, flags);
|
||||
}
|
||||
|
||||
// add a command to the list, which does return a json object as output
|
||||
void Command::add(const uint8_t device_type, const char * cmd, const cmd_json_function_p cb, const char * const * description, uint8_t flags) {
|
||||
// if the command already exists for that device type don't add it
|
||||
if (find_command(device_type, 0, cmd, flags) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdfunctions_.emplace_back(device_type, 0, flags, cmd, nullptr, cb, description); // callback for json is included
|
||||
}
|
||||
|
||||
// see if a command exists for that device type
|
||||
// is not case sensitive
|
||||
Command::CmdFunction * Command::find_command(const uint8_t device_type, const uint8_t device_id, const char * cmd, const uint8_t flag) {
|
||||
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto & cf : cmdfunctions_) {
|
||||
if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type) && (!device_id || cf.device_id_ == device_id)
|
||||
&& (flag == CommandFlag::CMD_FLAG_DEFAULT || (flag & 0x3F) == (cf.flags_ & 0x3F))) {
|
||||
return &cf;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr; // command not found
|
||||
}
|
||||
|
||||
void Command::erase_device_commands(const uint8_t device_type) {
|
||||
if (cmdfunctions_.empty()) {
|
||||
return;
|
||||
}
|
||||
auto it = cmdfunctions_.end();
|
||||
do {
|
||||
int i = it - cmdfunctions_.begin();
|
||||
if (cmdfunctions_[i].device_type_ == device_type) {
|
||||
cmdfunctions_.erase(it);
|
||||
}
|
||||
} while (it-- > cmdfunctions_.begin());
|
||||
}
|
||||
|
||||
void Command::erase_command(const uint8_t device_type, const char * cmd, uint8_t flag) {
|
||||
if ((cmd == nullptr) || (strlen(cmd) == 0) || (cmdfunctions_.empty())) {
|
||||
return;
|
||||
}
|
||||
auto it = cmdfunctions_.begin();
|
||||
for (auto const & cf : cmdfunctions_) {
|
||||
if (Helpers::toLower(cmd) == Helpers::toLower(cf.cmd_) && (cf.device_type_ == device_type) && ((flag & 0x3F) == (cf.flags_ & 0x3F))) {
|
||||
cmdfunctions_.erase(it);
|
||||
return;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// get the tagged command
|
||||
std::string Command::tagged_cmd(const std::string & cmd, const uint8_t flag) {
|
||||
switch (flag & 0x3F) {
|
||||
case CommandFlag::CMD_FLAG_HC:
|
||||
return "[hc<n>.]" + cmd;
|
||||
case CommandFlag::CMD_FLAG_DHW:
|
||||
return "dhw[n]." + cmd;
|
||||
case CommandFlag::CMD_FLAG_HS:
|
||||
return "hs<n>." + cmd;
|
||||
case CommandFlag::CMD_FLAG_AHS:
|
||||
return "ahs<n>." + cmd;
|
||||
default:
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
|
||||
// list all commands for a specific device, output as json
|
||||
bool Command::list(const uint8_t device_type, JsonObject output) {
|
||||
// common commands
|
||||
output[F_(info)] = Helpers::translated_word(FL_(info_cmd));
|
||||
output[F_(values)] = Helpers::translated_word(FL_(values_cmd));
|
||||
output[F_(commands)] = Helpers::translated_word(FL_(commands_cmd));
|
||||
output[F_(entities)] = Helpers::translated_word(FL_(entities_cmd));
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||
output["settings/showertimer"] = Helpers::translated_word(FL_(system_cmd));
|
||||
output["settings/showeralert"] = Helpers::translated_word(FL_(system_cmd));
|
||||
output["settings/hideled"] = Helpers::translated_word(FL_(system_cmd));
|
||||
output["settings/analogenabled"] = Helpers::translated_word(FL_(system_cmd));
|
||||
output["mqtt/enabled"] = Helpers::translated_word(FL_(system_cmd));
|
||||
output["ntp/enabled"] = Helpers::translated_word(FL_(system_cmd));
|
||||
output["ap/enabled"] = Helpers::translated_word(FL_(system_cmd));
|
||||
output["syslog/enabled"] = Helpers::translated_word(FL_(system_cmd));
|
||||
}
|
||||
// create a list of commands we have registered, and sort them
|
||||
std::list<std::string> sorted_cmds;
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
|
||||
sorted_cmds.push_back(tagged_cmd(cf.cmd_, cf.flags_));
|
||||
}
|
||||
}
|
||||
sorted_cmds.sort();
|
||||
|
||||
for (const auto & cl : sorted_cmds) {
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == tagged_cmd(cf.cmd_, cf.flags_))) {
|
||||
output[cl] = Helpers::translated_word(cf.description_);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// output list of all commands to console for a specific DeviceType
|
||||
void Command::show(uuid::console::Shell & shell, uint8_t device_type, bool verbose) {
|
||||
// create list of commands we have registered
|
||||
std::list<std::string> sorted_cmds;
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN)) {
|
||||
sorted_cmds.push_back(tagged_cmd(cf.cmd_, cf.flags_));
|
||||
}
|
||||
}
|
||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||
sorted_cmds.emplace_back("settings/showertimer");
|
||||
sorted_cmds.emplace_back("settings/showeralert");
|
||||
sorted_cmds.emplace_back("settings/hideled");
|
||||
sorted_cmds.emplace_back("settings/analogenabled");
|
||||
sorted_cmds.emplace_back("mqtt/enabled");
|
||||
sorted_cmds.emplace_back("ntp/enabled");
|
||||
sorted_cmds.emplace_back("ap/enabled");
|
||||
sorted_cmds.emplace_back("syslog/enabled");
|
||||
}
|
||||
sorted_cmds.sort(); // sort them
|
||||
|
||||
// if not in verbose mode, just print them on a single line and exit
|
||||
if (!verbose) {
|
||||
sorted_cmds.emplace_front(F_(info));
|
||||
sorted_cmds.emplace_front(F_(commands));
|
||||
sorted_cmds.emplace_front(F_(values));
|
||||
sorted_cmds.emplace_front(F_(entities));
|
||||
for (const auto & cl : sorted_cmds) {
|
||||
shell.print(cl);
|
||||
shell.print(" ");
|
||||
}
|
||||
shell.println();
|
||||
return;
|
||||
}
|
||||
|
||||
// verbose mode
|
||||
shell.printfln("\n%s%s %s:%s", COLOR_BOLD_ON, COLOR_YELLOW, EMSdevice::device_type_2_device_name(device_type), COLOR_RESET);
|
||||
|
||||
for (const auto & cl : sorted_cmds) {
|
||||
// find and print the description
|
||||
for (const auto & cf : cmdfunctions_) {
|
||||
if ((cf.device_type_ == device_type) && !cf.has_flags(CommandFlag::HIDDEN) && cf.description_ && (cl == tagged_cmd(cf.cmd_, cf.flags_))) {
|
||||
uint8_t i = cl.length();
|
||||
shell.print(" ");
|
||||
shell.print(cl);
|
||||
// pad with spaces
|
||||
while (i++ < 30) {
|
||||
shell.print(' ');
|
||||
}
|
||||
shell.print(COLOR_BRIGHT_CYAN);
|
||||
if (cf.has_flags(CommandFlag::CMD_FLAG_HC)) {
|
||||
shell.print(Helpers::translated_word(FL_(tag_hcx)));
|
||||
shell.print(' ');
|
||||
} else if (cf.has_flags(CommandFlag::CMD_FLAG_DHW)) {
|
||||
shell.print(Helpers::translated_word(FL_(tag_dhwx)));
|
||||
shell.print(' ');
|
||||
} else if (cf.has_flags(CommandFlag::CMD_FLAG_AHS)) {
|
||||
shell.print(Helpers::translated_word(FL_(tag_ahsx)));
|
||||
shell.print(' ');
|
||||
} else if (cf.has_flags(CommandFlag::CMD_FLAG_HS)) {
|
||||
shell.print(Helpers::translated_word(FL_(tag_hsx)));
|
||||
shell.print(' ');
|
||||
}
|
||||
shell.print(Helpers::translated_word(cf.description_));
|
||||
if (!cf.has_flags(CommandFlag::ADMIN_ONLY)) {
|
||||
shell.print(' ');
|
||||
shell.print(COLOR_BRIGHT_GREEN);
|
||||
shell.print('*');
|
||||
}
|
||||
shell.print(COLOR_RESET);
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
}
|
||||
|
||||
// see if a device_type is active and has associated commands
|
||||
// returns false if the device has no commands
|
||||
bool Command::device_has_commands(const uint8_t device_type) {
|
||||
if (device_type == EMSdevice::DeviceType::UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||
return true; // we always have System
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::SCHEDULER) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::CUSTOM) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::TEMPERATURESENSOR) {
|
||||
return EMSESP::sensor_enabled();
|
||||
}
|
||||
|
||||
if (device_type == EMSdevice::DeviceType::ANALOGSENSOR) {
|
||||
return EMSESP::analog_enabled();
|
||||
}
|
||||
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::CUSTOM));
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::SCHEDULER));
|
||||
if (EMSESP::sensor_enabled()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::TEMPERATURESENSOR));
|
||||
}
|
||||
if (EMSESP::analog_enabled()) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(EMSdevice::DeviceType::ANALOGSENSOR));
|
||||
}
|
||||
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice && (emsdevice->device_type() == device_class.first) && (device_has_commands(device_class.first))) {
|
||||
shell.printf("%s ", EMSdevice::device_type_2_device_name(device_class.first));
|
||||
break; // we only want to show one (not multiple of the same device types)
|
||||
}
|
||||
}
|
||||
}
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// 'show commands' : output list of all commands to console
|
||||
// calls show with verbose mode set
|
||||
void Command::show_all(uuid::console::Shell & shell) {
|
||||
shell.printfln("Showing all available commands (%s*%s=authentication not required).", COLOR_BRIGHT_GREEN, COLOR_RESET);
|
||||
shell.println("Note, each listed device includes these additional commands:");
|
||||
shell.printf(" info \t\t\t\t%slist all values with description%s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" commands \t\t\t%slist all commands %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" values \t\t\t%slist all values %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
shell.printf(" entities \t\t\t%slist all entities %s*", COLOR_BRIGHT_CYAN, COLOR_BRIGHT_GREEN);
|
||||
shell.println(COLOR_RESET);
|
||||
|
||||
// show system ones first
|
||||
show(shell, EMSdevice::DeviceType::SYSTEM, true);
|
||||
show(shell, EMSdevice::DeviceType::CUSTOM, true);
|
||||
show(shell, EMSdevice::DeviceType::SCHEDULER, true);
|
||||
|
||||
// then sensors
|
||||
if (EMSESP::sensor_enabled()) {
|
||||
show(shell, EMSdevice::DeviceType::TEMPERATURESENSOR, true);
|
||||
}
|
||||
if (EMSESP::analog_enabled()) {
|
||||
show(shell, EMSdevice::DeviceType::ANALOGSENSOR, true);
|
||||
}
|
||||
|
||||
// now EMS devices, do this in the order of factory classes to keep a consistent order when displaying
|
||||
for (const auto & device_class : EMSFactory::device_handlers()) {
|
||||
if (Command::device_has_commands(device_class.first)) {
|
||||
show(shell, device_class.first, true);
|
||||
}
|
||||
}
|
||||
|
||||
shell.println();
|
||||
}
|
||||
|
||||
// creates a single json document with an error message
|
||||
// object is optional
|
||||
uint8_t Command::json_message(uint8_t error_code, const char * message, const JsonObject output, const char * object) {
|
||||
output.clear();
|
||||
if (object) {
|
||||
output["message"] = std::string(message) + " " + object;
|
||||
} else {
|
||||
output["message"] = message;
|
||||
}
|
||||
return error_code;
|
||||
}
|
||||
|
||||
// **************************
|
||||
// **** SUrlParser class ****
|
||||
// **************************
|
||||
|
||||
// Extract only the path component from the passed URI and normalized it
|
||||
// e.g. //one/two////three/// becomes /one/two/three
|
||||
std::string SUrlParser::path() {
|
||||
std::string s = "/"; // set up the beginning slash
|
||||
for (const std::string & f : m_folders) {
|
||||
s += f;
|
||||
s += "/";
|
||||
}
|
||||
s.pop_back(); // deleting last letter, that is slash '/'
|
||||
return std::string(s);
|
||||
}
|
||||
|
||||
SUrlParser::SUrlParser(const char * uri) {
|
||||
parse(uri);
|
||||
}
|
||||
|
||||
bool SUrlParser::parse(const char * uri) {
|
||||
if (uri == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*uri == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_folders.clear();
|
||||
m_keysvalues.clear();
|
||||
enum Type { begin, folder, param, value };
|
||||
std::string s;
|
||||
|
||||
const char * c = uri;
|
||||
enum Type t = Type::begin;
|
||||
std::string last_param;
|
||||
|
||||
do {
|
||||
if (*c == '/') {
|
||||
if (s.length() > 0) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
t = Type::folder;
|
||||
} else if (*c == '?' && (t == Type::folder || t == Type::begin)) {
|
||||
if (s.length() > 0) {
|
||||
m_folders.push_back(s);
|
||||
s.clear();
|
||||
}
|
||||
t = Type::param;
|
||||
} else if (*c == '=' && (t == Type::param || t == Type::begin)) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
s.clear();
|
||||
t = Type::value;
|
||||
} else if (*c == '&' && (t == Type::value || t == Type::param || t == Type::begin)) {
|
||||
if (t == Type::value) {
|
||||
m_keysvalues[last_param] = s;
|
||||
} else if ((t == Type::param || t == Type::begin) && (s.length() > 0)) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
t = Type::param;
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() > 0) {
|
||||
if (t == Type::value) {
|
||||
m_keysvalues[last_param] = s;
|
||||
} else if (t == Type::folder || t == Type::begin) {
|
||||
m_folders.push_back(s);
|
||||
} else if (t == Type::param) {
|
||||
m_keysvalues[s] = "";
|
||||
last_param = s;
|
||||
}
|
||||
s.clear();
|
||||
} else if (*c == '\0' && s.length() == 0) {
|
||||
if (t == Type::param && last_param.length() > 0) {
|
||||
m_keysvalues[last_param] = "";
|
||||
}
|
||||
s.clear();
|
||||
} else {
|
||||
s += *c;
|
||||
}
|
||||
} while (*c++ != '\0');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
175
src/core/command.h
Normal file
175
src/core/command.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_COMMAND_H_
|
||||
#define EMSESP_COMMAND_H_
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "console.h"
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
#define COMMAND_MAX_LENGTH 50
|
||||
|
||||
// mqtt flags for command subscriptions
|
||||
enum CommandFlag : uint8_t {
|
||||
CMD_FLAG_DEFAULT = 0, // 0 no flags set, always subscribe to MQTT
|
||||
CMD_FLAG_HC = (1 << 0), // 1 TAG_HC1 - TAG_HC8
|
||||
CMD_FLAG_DHW = (1 << 1), // 2 TAG_DHW1 - TAG_DHW4
|
||||
CMD_FLAG_AHS = (1 << 2), // 4 TAG_AHS1
|
||||
CMD_FLAG_HS = (1 << 3), // 8 TAG_HS1 - TAG_HS16
|
||||
HIDDEN = (1 << 6), // 64 do not show in API or Web
|
||||
ADMIN_ONLY = (1 << 7) // 128 requires authentication
|
||||
|
||||
};
|
||||
|
||||
// return status after calling a Command
|
||||
enum CommandRet : uint8_t {
|
||||
FAIL = 0, // 0 or FALSE
|
||||
OK, // 1 or TRUE
|
||||
NOT_FOUND, // 2
|
||||
ERROR, // 3
|
||||
NOT_ALLOWED, // 4 - needs authentication
|
||||
INVALID // 5 - invalid (tag)
|
||||
};
|
||||
|
||||
using cmd_function_p = std::function<bool(const char * data, const int8_t id)>;
|
||||
using cmd_json_function_p = std::function<bool(const char * data, const int8_t id, JsonObject output)>;
|
||||
|
||||
class Command {
|
||||
public:
|
||||
struct CmdFunction {
|
||||
uint8_t device_type_; // DeviceType::
|
||||
uint8_t device_id_;
|
||||
uint8_t flags_; // mqtt flags for command subscriptions
|
||||
const char * cmd_;
|
||||
cmd_function_p cmdfunction_;
|
||||
cmd_json_function_p cmdfunction_json_;
|
||||
const char * const * description_;
|
||||
|
||||
CmdFunction(const uint8_t device_type,
|
||||
const uint8_t device_id,
|
||||
const uint8_t flags,
|
||||
const char * cmd,
|
||||
const cmd_function_p cmdfunction,
|
||||
const cmd_json_function_p cmdfunction_json,
|
||||
const char * const * description)
|
||||
: device_type_(device_type)
|
||||
, device_id_(device_id)
|
||||
, flags_(flags)
|
||||
, cmd_(cmd)
|
||||
, cmdfunction_(cmdfunction)
|
||||
, cmdfunction_json_(cmdfunction_json)
|
||||
, description_(description) {
|
||||
}
|
||||
|
||||
inline void add_flags(uint8_t flags) {
|
||||
flags_ |= flags;
|
||||
}
|
||||
inline bool has_flags(uint8_t flags) const {
|
||||
return (flags_ & flags) == flags;
|
||||
}
|
||||
inline void remove_flags(uint8_t flags) {
|
||||
flags_ &= ~flags;
|
||||
}
|
||||
inline uint8_t flags() const {
|
||||
return flags_;
|
||||
}
|
||||
};
|
||||
|
||||
static std::vector<CmdFunction> commands() {
|
||||
return cmdfunctions_;
|
||||
}
|
||||
|
||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const bool is_admin, const int8_t id, JsonObject output);
|
||||
static uint8_t call(const uint8_t device_type, const char * cmd, const char * value, const int8_t id = -1);
|
||||
|
||||
// with normal call back function taking a value and id
|
||||
static void add(const uint8_t device_type,
|
||||
const uint8_t device_id,
|
||||
const char * cmd,
|
||||
const cmd_function_p cb,
|
||||
const char * const * description,
|
||||
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||
|
||||
// same for system/temperature/analog devices
|
||||
static void
|
||||
add(const uint8_t device_type, const char * cmd, const cmd_function_p cb, const char * const * description, uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||
// callback function taking value, id and a json object for its output
|
||||
static void add(const uint8_t device_type,
|
||||
const char * cmd,
|
||||
const cmd_json_function_p cb,
|
||||
const char * const * description,
|
||||
uint8_t flags = CommandFlag::CMD_FLAG_DEFAULT);
|
||||
|
||||
static void show_all(uuid::console::Shell & shell);
|
||||
static Command::CmdFunction * find_command(const uint8_t device_type, const uint8_t device_id, const char * cmd, const uint8_t flag);
|
||||
static std::string tagged_cmd(const std::string & cmd, const uint8_t flag);
|
||||
|
||||
static void erase_device_commands(const uint8_t device_type);
|
||||
static void erase_command(const uint8_t device_type, const char * cmd, uint8_t flag = CommandFlag::CMD_FLAG_DEFAULT);
|
||||
static void show(uuid::console::Shell & shell, uint8_t device_type, bool verbose);
|
||||
static void show_devices(uuid::console::Shell & shell);
|
||||
static bool device_has_commands(const uint8_t device_type);
|
||||
|
||||
static bool list(const uint8_t device_type, JsonObject output);
|
||||
|
||||
static uint8_t process(const char * path, const bool is_admin, const JsonObject input, JsonObject output);
|
||||
|
||||
static const char * parse_command_string(const char * command, int8_t & id);
|
||||
static const char * get_attribute(const char * cmd);
|
||||
static bool set_attribute(JsonObject output, const char * cmd, const char * attribute);
|
||||
|
||||
static const char * return_code_string(const uint8_t return_code);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static std::vector<CmdFunction> cmdfunctions_; // the list of commands
|
||||
|
||||
static uint8_t json_message(uint8_t error_code, const char * message, JsonObject output, const char * object = nullptr);
|
||||
};
|
||||
|
||||
class SUrlParser {
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> m_keysvalues;
|
||||
std::vector<std::string> m_folders;
|
||||
|
||||
public:
|
||||
SUrlParser() = default;
|
||||
SUrlParser(const char * url);
|
||||
|
||||
bool parse(const char * url);
|
||||
|
||||
std::vector<std::string> & paths() {
|
||||
return m_folders;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::string> & params() {
|
||||
return m_keysvalues;
|
||||
};
|
||||
|
||||
std::string path();
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
87
src/core/common.h
Normal file
87
src/core/common.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_COMMON_H
|
||||
#define EMSESP_COMMON_H
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
using uuid::log::Level;
|
||||
|
||||
#if defined(EMSESP_DEBUG)
|
||||
#define LOG_DEBUG(...) logger_.debug(__VA_ARGS__)
|
||||
#else
|
||||
#define LOG_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#define LOG_INFO(...) logger_.info(__VA_ARGS__)
|
||||
#define LOG_TRACE(...) logger_.trace(__VA_ARGS__)
|
||||
#define LOG_NOTICE(...) logger_.notice(__VA_ARGS__)
|
||||
#define LOG_WARNING(...) logger_.warning(__VA_ARGS__)
|
||||
#define LOG_ERROR(...) logger_.err(__VA_ARGS__)
|
||||
|
||||
// flash strings
|
||||
using uuid::string_vector;
|
||||
using string_vector = std::vector<const char *>;
|
||||
|
||||
#ifdef FPSTR
|
||||
#undef FPSTR
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
|
||||
#define FPSTR(pstr_pointer) pstr_pointer
|
||||
#define MAKE_WORD_CUSTOM(string_name, string_literal) static const char __pstr__##string_name[] = string_literal;
|
||||
#define MAKE_WORD(string_name) MAKE_WORD_CUSTOM(string_name, #string_name)
|
||||
|
||||
#define F_(string_name) (__pstr__##string_name)
|
||||
#define FL_(list_name) (__pstr__L_##list_name)
|
||||
|
||||
// The language settings below must match system.cpp
|
||||
#if defined(EMSESP_TEST)
|
||||
// in Test mode use two languages (en & de) to save flash memory needed for the tests
|
||||
#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
|
||||
#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
|
||||
#elif defined(EMSESP_EN_ONLY)
|
||||
// EN only
|
||||
#define MAKE_WORD_TRANSLATION(list_name, en, ...) static const char * const __pstr__L_##list_name[] = {en, nullptr};
|
||||
#define MAKE_TRANSLATION(list_name, shortname, en, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, nullptr};
|
||||
#elif defined(EMSESP_DE_ONLY)
|
||||
// EN + DE
|
||||
#define MAKE_WORD_TRANSLATION(list_name, en, de, ...) static const char * const __pstr__L_##list_name[] = {en, de, nullptr};
|
||||
#define MAKE_TRANSLATION(list_name, shortname, en, de, ...) static const char * const __pstr__L_##list_name[] = {shortname, en, de, nullptr};
|
||||
#else
|
||||
#define MAKE_WORD_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
#define MAKE_TRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
#endif
|
||||
|
||||
#define MAKE_NOTRANSLATION(list_name, ...) static const char * const __pstr__L_##list_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// fixed strings, no translations
|
||||
#define MAKE_ENUM_FIXED(enum_name, ...) static const char * const __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// with translations
|
||||
#define MAKE_ENUM(enum_name, ...) static const char * const * __pstr__L_##enum_name[] = {__VA_ARGS__, nullptr};
|
||||
|
||||
// clang-format on
|
||||
|
||||
// load translations
|
||||
#include "locale_translations.h"
|
||||
#include "locale_common.h"
|
||||
|
||||
#endif
|
||||
706
src/core/console.cpp
Normal file
706
src/core/console.cpp
Normal file
@@ -0,0 +1,706 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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 "console.h"
|
||||
#include "console_stream.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
#include "../test/test.h"
|
||||
#endif
|
||||
|
||||
using ::uuid::console::Commands;
|
||||
using ::uuid::console::Shell;
|
||||
using LogLevel = ::uuid::log::Level;
|
||||
using LogFacility = ::uuid::log::Facility;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
static constexpr unsigned long INVALID_PASSWORD_DELAY_MS = 3000;
|
||||
|
||||
static inline EMSESPShell & to_shell(Shell & shell) {
|
||||
return static_cast<EMSESPShell &>(shell);
|
||||
}
|
||||
|
||||
#define NO_ARGUMENTS \
|
||||
std::vector<std::string> { \
|
||||
}
|
||||
|
||||
// add static functions here....
|
||||
|
||||
static void console_log_level(Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (!arguments.empty()) {
|
||||
uuid::log::Level level;
|
||||
|
||||
if (uuid::log::parse_level_lowercase(arguments[0], level)) {
|
||||
shell.log_level(level);
|
||||
} else {
|
||||
shell.printfln(F_(invalid_log_level));
|
||||
return;
|
||||
}
|
||||
}
|
||||
shell.printfln(F_(log_level_fmt), uuid::log::format_level_uppercase(shell.log_level()));
|
||||
}
|
||||
|
||||
static std::vector<std::string> log_level_autocomplete(Shell & shell, const std::vector<std::string> & current_arguments, const std::string & next_argument) {
|
||||
return uuid::log::levels_lowercase();
|
||||
}
|
||||
|
||||
static void setup_commands(std::shared_ptr<Commands> const & commands) {
|
||||
// exit, help, log
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, {F_(exit)}, EMSESPShell::main_exit_function);
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, {F_(help)}, EMSESPShell::main_help_function);
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, {F_(log)}, {F_(log_level_optional)}, console_log_level, log_level_autocomplete);
|
||||
|
||||
//
|
||||
// Show commands
|
||||
//
|
||||
commands->add_command(
|
||||
ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
{F_(show)},
|
||||
{F_(show_commands)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.empty()) {
|
||||
EMSESP::system_.show_system(shell);
|
||||
return;
|
||||
}
|
||||
|
||||
auto const & command = arguments.front();
|
||||
if (command == F_(commands)) {
|
||||
Command::show_all(shell);
|
||||
} else if (command == F_(system)) {
|
||||
EMSESP::system_.show_system(shell);
|
||||
} else if (command == F_(users) && (shell.has_flags(CommandFlags::ADMIN))) {
|
||||
EMSESP::system_.show_users(shell); // admin only
|
||||
} else if (command == F_(devices)) {
|
||||
EMSESP::show_devices(shell);
|
||||
} else if (command == F_(log)) {
|
||||
EMSESP::webLogService.show(shell);
|
||||
} else if (command == F_(ems)) {
|
||||
EMSESP::show_ems(shell);
|
||||
} else if (command == F_(values)) {
|
||||
EMSESP::show_device_values(shell);
|
||||
EMSESP::show_sensor_values(shell);
|
||||
} else if (command == F_(mqtt)) {
|
||||
Mqtt::show_mqtt(shell);
|
||||
} else {
|
||||
shell.printfln("Unknown show command");
|
||||
}
|
||||
},
|
||||
[](Shell const & shell, const std::vector<std::string> & current_arguments, const std::string & next_argument) -> std::vector<std::string> {
|
||||
return std::vector<std::string>{"system", "users", "devices", "log", "ems", "values", "mqtt", "commands"};
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
// System commands
|
||||
//
|
||||
#if defined(EMSESP_TEST)
|
||||
// create commands test
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
{"test"},
|
||||
string_vector{F_(name_optional), F_(data_optional), F_(id_optional)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.empty()) {
|
||||
Test::run_test(shell, "default");
|
||||
} else if (arguments.size() == 1) {
|
||||
Test::run_test(shell, arguments.front());
|
||||
} else if (arguments.size() == 2) {
|
||||
Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str());
|
||||
} else {
|
||||
Test::run_test(shell, arguments[0].c_str(), arguments[1].c_str(), arguments[2].c_str());
|
||||
}
|
||||
});
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, {"t"}, [=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
Test::run_test(shell, "default");
|
||||
});
|
||||
#endif
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::USER, {F_(su)}, [=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
auto become_admin = [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE, LogFacility::AUTH, F("Admin session opened on console %s"), to_shell(shell).console_name().c_str());
|
||||
shell.add_flags(CommandFlags::ADMIN);
|
||||
};
|
||||
|
||||
if (shell.has_flags(CommandFlags::ADMIN)) {
|
||||
return;
|
||||
} else if (shell.has_flags(CommandFlags::LOCAL)) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.enter_password(F_(password_prompt), [=](Shell & shell, bool completed, const std::string & password) {
|
||||
if (completed) {
|
||||
uint64_t now = uuid::get_uptime_ms();
|
||||
|
||||
EMSESP::esp32React.getSecuritySettingsService()->read([&](SecuritySettings & securitySettings) {
|
||||
if (!password.empty() && (securitySettings.jwtSecret.equals(password.c_str()))) {
|
||||
become_admin(shell);
|
||||
} else {
|
||||
shell.delay_until(now + INVALID_PASSWORD_DELAY_MS, [](Shell & shell) {
|
||||
shell.logger().log(LogLevel::NOTICE,
|
||||
LogFacility::AUTH,
|
||||
F("Invalid admin password on console %s"),
|
||||
to_shell(shell).console_name().c_str());
|
||||
shell.println(F("su: incorrect password"));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(passwd)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||
if (completed) {
|
||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
EMSESP::esp32React.getSecuritySettingsService()->update([&](SecuritySettings & securitySettings) {
|
||||
securitySettings.jwtSecret = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("Admin password updated");
|
||||
} else {
|
||||
shell.println("Passwords do not match");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
{F_(restart)},
|
||||
{F_(partitionname_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.size()) {
|
||||
EMSESP::system_.system_restart(arguments.front().c_str());
|
||||
} else {
|
||||
EMSESP::system_.system_restart();
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(wifi), F_(reconnect)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) { EMSESP::system_.wifi_reconnect(); });
|
||||
|
||||
//
|
||||
// SET commands
|
||||
//
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(set), F_(wifi), F_(password)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
shell.enter_password(F_(new_password_prompt1), [](Shell & shell, bool completed, const std::string & password1) {
|
||||
if (completed) {
|
||||
shell.enter_password(F_(new_password_prompt2), [password1](Shell & shell, bool completed, const std::string & password2) {
|
||||
if (completed) {
|
||||
if (password1 == password2) {
|
||||
EMSESP::esp32React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
||||
networkSettings.password = password2.c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("WiFi password updated. Reconnecting...");
|
||||
EMSESP::system_.wifi_reconnect();
|
||||
} else {
|
||||
shell.println("Passwords do not match");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(set), F_(hostname)},
|
||||
{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
shell.println("The network connection will be reset...");
|
||||
Shell::loop_all();
|
||||
delay(1000); // wait a second
|
||||
EMSESP::esp32React.getNetworkSettingsService()->update([&](NetworkSettings & networkSettings) {
|
||||
networkSettings.hostname = arguments.front().c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(set), F_(wifi), F_(ssid)},
|
||||
{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
EMSESP::esp32React.getNetworkSettingsService()->updateWithoutPropagation([&](NetworkSettings & networkSettings) {
|
||||
networkSettings.ssid = arguments.front().c_str();
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.println("WiFi ssid updated. Reconnecting...");
|
||||
EMSESP::system_.wifi_reconnect();
|
||||
});
|
||||
|
||||
|
||||
commands->add_command(
|
||||
ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(set), F_(board_profile)},
|
||||
{F_(name_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
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("Invalid board profile (S32, E32, E32V2, MH-ET, NODEMCU, LOLIN, OLIMEX, OLIMEXPOE, C3MINI, S2MINI, S3MINI, S32S3, 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];
|
||||
settings.eth_power = data[6]; // can be -1
|
||||
settings.eth_phy_addr = data[7];
|
||||
settings.eth_clock_mode = data[8];
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
shell.printfln("Loaded board profile %s", board_profile.c_str());
|
||||
EMSESP::system_.network_init(true);
|
||||
});
|
||||
|
||||
commands->add_command(
|
||||
ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(set), F_(bus_id)},
|
||||
{F_(deviceid_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t device_id = Helpers::hextoint(arguments.front().c_str());
|
||||
if ((device_id == 0x0B) || (device_id == 0x0D) || (device_id == 0x0A) || (device_id == 0x0F) || (device_id == 0x12)) {
|
||||
EMSESP::webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.ems_bus_id = device_id;
|
||||
shell.printfln(F_(bus_id_fmt), settings.ems_bus_id);
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
} else {
|
||||
shell.println("Must be 0B, 0D, 0A, 0E, 0F, or 48 - 4D");
|
||||
}
|
||||
},
|
||||
[](Shell & shell, const std::vector<std::string> & current_arguments, const std::string & next_argument) -> std::vector<std::string> {
|
||||
return std::vector<std::string>{"0B", "0D", "0A", "0E", "0F", "48", "49", "4A", "4B", "4C", "4D"};
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
string_vector{F_(set), F_(tx_mode)},
|
||||
{F_(n_mandatory)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint8_t tx_mode = std::strtol(arguments[0].c_str(), nullptr, 10);
|
||||
// save the tx_mode
|
||||
EMSESP::webSettingsService.update([&](WebSettings & settings) {
|
||||
settings.tx_mode = tx_mode;
|
||||
shell.printfln(F_(tx_mode_fmt), settings.tx_mode);
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
EMSESP::uart_init();
|
||||
});
|
||||
|
||||
//
|
||||
// EMS device commands
|
||||
//
|
||||
|
||||
commands->add_command(ShellContext::MAIN, CommandFlags::ADMIN, {F_(scan)}, {F_(deep_optional)}, [](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.empty()) {
|
||||
EMSESP::scan_devices();
|
||||
} else {
|
||||
shell.printfln("Performing a deep scan...");
|
||||
EMSESP::clear_all_devices();
|
||||
// device IDs taken from device_library.h
|
||||
// send the read command with Version command
|
||||
const std::vector<uint8_t> Device_Ids = {0x02, 0x08, 0x09, 0x10, 0x11, 0x12, 0x15, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x20, 0x21, 0x22, 0x23,
|
||||
0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x30, 0x38, 0x40, 0x41, 0x48, 0x50, 0x51, 0x60};
|
||||
for (const uint8_t device_id : Device_Ids) {
|
||||
EMSESP::send_read_request(EMSdevice::EMS_TYPE_VERSION, device_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// read <deviceID> <type ID> [offset] [length]
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
{F_(read)},
|
||||
string_vector{F_(deviceid_mandatory), F_(typeid_mandatory), F_(offset_optional), F_(length_optional)},
|
||||
[=](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
// loop through arguments and add to data as text, separated by a space
|
||||
std::string data;
|
||||
for (const auto & arg : arguments) {
|
||||
if (!data.empty()) {
|
||||
data += " ";
|
||||
}
|
||||
data += arg;
|
||||
}
|
||||
|
||||
if (!System::readCommand(data.c_str())) {
|
||||
shell.printfln("Invalid deviceID");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(ShellContext::MAIN,
|
||||
CommandFlags::USER,
|
||||
{F_(watch)},
|
||||
string_vector{F_(watch_format_optional), F_(watchid_optional)},
|
||||
[](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
uint16_t watch_id = WATCH_ID_NONE;
|
||||
|
||||
// only use english commands, not the translations
|
||||
if (!arguments.empty()) {
|
||||
// get raw/pretty
|
||||
if (arguments[0] == F_(raw)) {
|
||||
EMSESP::watch(EMSESP::WATCH_RAW); // raw
|
||||
} else if (arguments[0] == (FL_(on)[0])) {
|
||||
EMSESP::watch(EMSESP::WATCH_ON); // on
|
||||
} else if (arguments[0] == (FL_(off)[0])) {
|
||||
EMSESP::watch(EMSESP::WATCH_OFF); // off
|
||||
} else if (arguments[0] == (FL_(unknown)[0])) {
|
||||
EMSESP::watch(EMSESP::WATCH_UNKNOWN); // unknown
|
||||
watch_id = WATCH_ID_NONE;
|
||||
} else {
|
||||
watch_id = Helpers::hextoint(arguments[0].c_str());
|
||||
if (watch_id > 0 && ((EMSESP::watch() == EMSESP::WATCH_OFF) || (EMSESP::watch() == EMSESP::WATCH_UNKNOWN))) {
|
||||
EMSESP::watch(EMSESP::WATCH_ON); // on
|
||||
} else if (watch_id == 0) {
|
||||
EMSESP::watch(EMSESP::WATCH_OFF); // off
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (arguments.size() == 2) {
|
||||
// get the watch_id if its set
|
||||
watch_id = Helpers::hextoint(arguments[1].c_str());
|
||||
}
|
||||
|
||||
EMSESP::watch_id(watch_id);
|
||||
} else {
|
||||
shell.printfln("Invalid: use watch raw|on|off|unknown|id [id]");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t watch = EMSESP::watch();
|
||||
if (watch == EMSESP::WATCH_OFF) {
|
||||
shell.printfln("Watching telegrams is off");
|
||||
return;
|
||||
}
|
||||
|
||||
// if logging is off, the watch won't show anything, show force it back to NOTICE
|
||||
if (shell.log_level() < Level::NOTICE) {
|
||||
shell.log_level(Level::NOTICE);
|
||||
shell.printfln("Setting log level to Notice");
|
||||
}
|
||||
|
||||
if (watch == EMSESP::WATCH_ON) {
|
||||
shell.printfln("Watching incoming telegrams, displayed in decoded format");
|
||||
} else if (watch == EMSESP::WATCH_RAW) {
|
||||
shell.printfln("Watching incoming telegrams, displayed as raw bytes"); // WATCH_RAW
|
||||
} else {
|
||||
shell.printfln("Watching unknown telegrams"); // WATCH_UNKNOWN
|
||||
}
|
||||
|
||||
watch_id = EMSESP::watch_id();
|
||||
if (watch_id > 0x80) {
|
||||
shell.printfln("Filtering only telegrams that match a telegram type of 0x%02X", watch_id);
|
||||
} else if (watch_id != WATCH_ID_NONE) {
|
||||
shell.printfln("Filtering only telegrams that match a deviceID or telegram type of 0x%02X", watch_id);
|
||||
}
|
||||
});
|
||||
|
||||
commands->add_command(
|
||||
ShellContext::MAIN,
|
||||
CommandFlags::ADMIN,
|
||||
{F_(call)},
|
||||
string_vector{F_(device_type_optional), F_(cmd_optional), F_(data_optional), F_(id_optional)},
|
||||
[&](Shell & shell, const std::vector<std::string> & arguments) {
|
||||
if (arguments.empty()) {
|
||||
Command::show_all(shell); // list options
|
||||
return;
|
||||
}
|
||||
|
||||
// validate the device_type
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(arguments[0].c_str());
|
||||
if (!Command::device_has_commands(device_type)) {
|
||||
shell.print("Invalid device. Available devices are: ");
|
||||
Command::show_devices(shell);
|
||||
return;
|
||||
}
|
||||
|
||||
// validate that a command is present
|
||||
if (arguments.size() < 2) {
|
||||
shell.print("Missing command. Available commands are: ");
|
||||
Command::show(shell, device_type, false); // non-verbose mode
|
||||
return;
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
int8_t id = -1;
|
||||
auto arg = Helpers::toLower(arguments[1]);
|
||||
const char * cmd = arg.c_str(); // prevent loosing pointer after object destruction
|
||||
uint8_t return_code = CommandRet::OK;
|
||||
JsonObject json = doc.to<JsonObject>();
|
||||
bool has_data = false;
|
||||
|
||||
if (device_type >= EMSdevice::DeviceType::BOILER) {
|
||||
cmd = Command::parse_command_string(cmd, id); // extract hc or dhw
|
||||
}
|
||||
if (cmd == nullptr) {
|
||||
cmd = F_(values);
|
||||
}
|
||||
|
||||
if (arguments.size() == 2) {
|
||||
// no value specified, just the cmd
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, id, json);
|
||||
} else if (arguments.size() == 3) {
|
||||
if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) {
|
||||
// info has a id but no value
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, atoi(arguments.back().c_str()), json);
|
||||
} else if (arguments[2] == "?") {
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, id, json);
|
||||
} else {
|
||||
has_data = true;
|
||||
// has a value but no id so use -1
|
||||
return_code = Command::call(device_type, cmd, arguments.back().c_str(), true, id, json);
|
||||
}
|
||||
} else {
|
||||
// use value, which could be an id or hc
|
||||
if (arguments[2] == "?") {
|
||||
return_code = Command::call(device_type, cmd, nullptr, true, atoi(arguments[3].c_str()), json);
|
||||
} else {
|
||||
has_data = true;
|
||||
return_code = Command::call(device_type, cmd, arguments[2].c_str(), true, atoi(arguments[3].c_str()), json);
|
||||
}
|
||||
}
|
||||
|
||||
if (return_code == CommandRet::OK) {
|
||||
if (json.size()) {
|
||||
if (json["api_data"].is<std::string>()) {
|
||||
std::string data = json["api_data"];
|
||||
shell.println(data.c_str());
|
||||
return;
|
||||
}
|
||||
serializeJsonPretty(doc, shell);
|
||||
shell.println();
|
||||
return;
|
||||
} else if (!has_data) {
|
||||
// show message if no data returned (e.g. for analogsensor, temperaturesensor, custom)
|
||||
shell.println("Command executed");
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (return_code == CommandRet::NOT_FOUND) {
|
||||
shell.println("Unknown command");
|
||||
shell.print("Available commands are: ");
|
||||
Command::show(shell, device_type, false); // non-verbose mode
|
||||
} else if ((return_code == CommandRet::ERROR) || (return_code == CommandRet::FAIL)) {
|
||||
shell.printfln("Bad syntax. Check arguments.");
|
||||
} else {
|
||||
shell.printfln("Command failed with error code %d", return_code);
|
||||
}
|
||||
},
|
||||
[](Shell & shell, const std::vector<std::string> & current_arguments, const std::string & next_argument) -> std::vector<std::string> {
|
||||
if (current_arguments.empty()) {
|
||||
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::TEMPERATURESENSOR));
|
||||
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));
|
||||
}
|
||||
}
|
||||
return devices_list;
|
||||
} else if (current_arguments.size() == 1) {
|
||||
uint8_t device_type = EMSdevice::device_name_2_device_type(current_arguments[0].c_str());
|
||||
if (Command::device_has_commands(device_type)) {
|
||||
std::vector<std::string> command_list;
|
||||
for (const auto & cf : Command::commands()) {
|
||||
if (cf.device_type_ == device_type) {
|
||||
command_list.emplace_back(cf.cmd_);
|
||||
}
|
||||
}
|
||||
return command_list;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<Commands> EMSESPShell::commands_ = [] {
|
||||
auto commands = std::make_shared<Commands>();
|
||||
setup_commands(commands);
|
||||
return commands;
|
||||
}();
|
||||
|
||||
EMSESPShell::EMSESPShell(EMSESP & emsesp, Stream & stream, unsigned int context, unsigned int flags)
|
||||
: Shell(stream, commands_, context, flags)
|
||||
, emsesp_(emsesp) {
|
||||
}
|
||||
|
||||
void EMSESPShell::started() {
|
||||
logger().log(LogLevel::INFO, LogFacility::CONSOLE, F("User session opened on console %s"), console_name().c_str());
|
||||
}
|
||||
|
||||
void EMSESPShell::stopped() {
|
||||
if (has_flags(CommandFlags::ADMIN)) {
|
||||
logger().log(LogLevel::INFO, LogFacility::AUTH, F("Admin session closed on console %s"), console_name().c_str());
|
||||
}
|
||||
logger().log(LogLevel::INFO, LogFacility::CONSOLE, F("User session closed on console %s"), console_name().c_str());
|
||||
}
|
||||
|
||||
// show welcome banner
|
||||
void EMSESPShell::display_banner() {
|
||||
println();
|
||||
printfln("┌───────────────────────────────────────┐");
|
||||
printfln("│ %sEMS-ESP version %-20s%s │", COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_BOLD_OFF);
|
||||
printfln("│ │");
|
||||
printfln("│ %shelp%s to show available commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("│ %ssu%s to access admin commands │", COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("│ │");
|
||||
printfln("│ %s%shttps://github.com/emsesp/EMS-ESP32%s │", COLOR_BRIGHT_GREEN, COLOR_UNDERLINE, COLOR_RESET);
|
||||
printfln("│ │");
|
||||
printfln("└───────────────────────────────────────┘");
|
||||
println();
|
||||
|
||||
// set console name
|
||||
EMSESP::esp32React.getNetworkSettingsService()->read([&](NetworkSettings & networkSettings) { console_hostname_ = networkSettings.hostname.c_str(); });
|
||||
if (console_hostname_.empty()) {
|
||||
console_hostname_ = "ems-esp";
|
||||
}
|
||||
}
|
||||
|
||||
std::string EMSESPShell::hostname_text() {
|
||||
return console_hostname_;
|
||||
}
|
||||
|
||||
std::string EMSESPShell::context_text() {
|
||||
return std::string{};
|
||||
|
||||
// auto shell_context = static_cast<ShellContext>(context());
|
||||
// if (shell_context == ShellContext::MAIN) {
|
||||
// return std::string{'/'};
|
||||
// } else if (shell_context == ShellContext::FILESYSTEM) {
|
||||
// return "/fs");
|
||||
// } else {
|
||||
// return std::string{};
|
||||
// }
|
||||
}
|
||||
|
||||
// when in su (admin) show # as the prompt suffix
|
||||
std::string EMSESPShell::prompt_suffix() {
|
||||
#ifndef EMSESP_UNITY
|
||||
if (has_flags(CommandFlags::ADMIN)) {
|
||||
return std::string{'#'};
|
||||
} else {
|
||||
return std::string{'$'};
|
||||
}
|
||||
#else
|
||||
// don't bother with prompt suffix if we're testing Unity output
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
void EMSESPShell::end_of_transmission() {
|
||||
invoke_command(F_(exit));
|
||||
}
|
||||
|
||||
void EMSESPShell::main_help_function(Shell & shell, const std::vector<std::string> & arguments) {
|
||||
shell.println();
|
||||
#if defined(EMSESP_DEBUG)
|
||||
shell.printfln("%s%sEMS-ESP version %s%s", COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_RESET);
|
||||
#else
|
||||
shell.printfln("%s%sEMS-ESP version %s%s (DEBUG)", COLOR_BRIGHT_GREEN, COLOR_BOLD_ON, EMSESP_APP_VERSION, COLOR_RESET);
|
||||
#endif
|
||||
shell.println();
|
||||
shell.print_all_available_commands();
|
||||
}
|
||||
|
||||
void EMSESPShell::main_exit_function(Shell & shell, const std::vector<std::string> & arguments) {
|
||||
shell.stop();
|
||||
}
|
||||
|
||||
// **** EMSESPConsole Class *****
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
std::vector<bool> EMSESPConsole::ptys_;
|
||||
#endif
|
||||
|
||||
EMSESPConsole::EMSESPConsole(EMSESP & emsesp, Stream & stream, bool local)
|
||||
: EMSESPShell(emsesp, stream, ShellContext::MAIN, local ? (CommandFlags::USER | CommandFlags::LOCAL) : CommandFlags::USER)
|
||||
, name_("ttyS0")
|
||||
#ifndef EMSESP_STANDALONE
|
||||
, pty_(std::numeric_limits<size_t>::max())
|
||||
, addr_()
|
||||
, port_(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESPConsole::EMSESPConsole(EMSESP & emsesp, Stream & stream, const IPAddress & addr, uint16_t port)
|
||||
: EMSESPShell(emsesp, stream, ShellContext::MAIN, CommandFlags::USER)
|
||||
, addr_(addr)
|
||||
, port_(port) {
|
||||
std::array<char, 16> text;
|
||||
|
||||
pty_ = 0;
|
||||
while (pty_ < ptys_.size() && ptys_[pty_])
|
||||
pty_++;
|
||||
if (pty_ == ptys_.size()) {
|
||||
ptys_.push_back(true);
|
||||
} else {
|
||||
ptys_[pty_] = true;
|
||||
}
|
||||
|
||||
snprintf(text.data(), text.size(), "pty%u", pty_);
|
||||
name_ = text.data();
|
||||
|
||||
logger().info("Allocated console %s for connection from [%s]:%u", name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
|
||||
}
|
||||
#endif
|
||||
|
||||
EMSESPConsole::~EMSESPConsole() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (pty_ != SIZE_MAX) {
|
||||
logger().info("Shutdown console %s for connection from [%s]:%u", name_.c_str(), uuid::printable_to_string(addr_).c_str(), port_);
|
||||
|
||||
ptys_[pty_] = false;
|
||||
ptys_.shrink_to_fit();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string EMSESPConsole::console_name() {
|
||||
return name_;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
70
src/core/console.h
Normal file
70
src/core/console.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_CONSOLE_H
|
||||
#define EMSESP_CONSOLE_H
|
||||
|
||||
#include <uuid/console.h>
|
||||
#include <uuid/log.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "system.h"
|
||||
#include "mqtt.h"
|
||||
|
||||
#ifdef LOCAL
|
||||
#undef LOCAL
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum ShellContext : uint8_t { MAIN = 0, SYSTEM, END };
|
||||
|
||||
class EMSESP;
|
||||
|
||||
class EMSESPShell : public uuid::console::Shell {
|
||||
public:
|
||||
~EMSESPShell() override = default;
|
||||
|
||||
virtual std::string console_name() = 0;
|
||||
|
||||
static void main_help_function(Shell & shell, const std::vector<std::string> & arguments);
|
||||
static void main_exit_function(Shell & shell, const std::vector<std::string> & arguments);
|
||||
|
||||
EMSESP & emsesp_;
|
||||
|
||||
protected:
|
||||
EMSESPShell(EMSESP & emsesp, Stream & stream, unsigned int context, unsigned int flags);
|
||||
|
||||
static std::shared_ptr<uuid::console::Commands> commands_;
|
||||
|
||||
// our custom functions for Shell
|
||||
void started() override;
|
||||
void stopped() override;
|
||||
void display_banner() override;
|
||||
std::string hostname_text() override;
|
||||
std::string context_text() override;
|
||||
std::string prompt_suffix() override;
|
||||
void end_of_transmission() override;
|
||||
|
||||
private:
|
||||
std::string console_hostname_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
50
src/core/console_stream.h
Normal file
50
src/core/console_stream.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* mcu-app - Microcontroller application framework
|
||||
* Copyright 2022 Simon Arlott
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <uuid/console.h>
|
||||
|
||||
#include "console.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSESPConsole : public EMSESPShell {
|
||||
public:
|
||||
EMSESPConsole(EMSESP & emsesp, Stream & stream, bool local);
|
||||
#ifndef ENV_NATIVE
|
||||
EMSESPConsole(EMSESP & emsesp, Stream & stream, const IPAddress & addr, uint16_t port);
|
||||
#endif
|
||||
~EMSESPConsole() override;
|
||||
|
||||
std::string console_name() override;
|
||||
|
||||
private:
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static std::vector<bool> ptys_;
|
||||
#endif
|
||||
|
||||
std::string name_;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
size_t pty_;
|
||||
IPAddress addr_;
|
||||
uint16_t port_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
300
src/core/default_settings.h
Normal file
300
src/core/default_settings.h
Normal file
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_DEFAULT_SETTINGS_H
|
||||
#define EMSESP_DEFAULT_SETTINGS_H
|
||||
|
||||
// GENERAL SETTINGS
|
||||
|
||||
#ifndef EMSESP_DEFAULT_LOCALE
|
||||
#define EMSESP_DEFAULT_LOCALE EMSESP_LOCALE_EN // English
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_VERSION
|
||||
#define EMSESP_DEFAULT_VERSION ""
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TX_MODE
|
||||
#define EMSESP_DEFAULT_TX_MODE 1 // EMS1.0
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_EMS_BUS_ID
|
||||
#define EMSESP_DEFAULT_EMS_BUS_ID 0x0B // service key
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SYSLOG_ENABLED
|
||||
#define EMSESP_DEFAULT_SYSLOG_ENABLED false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SYSLOG_LEVEL
|
||||
#define EMSESP_DEFAULT_SYSLOG_LEVEL 3 // ERR
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL
|
||||
#define EMSESP_DEFAULT_SYSLOG_MARK_INTERVAL 0
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SYSLOG_HOST
|
||||
#define EMSESP_DEFAULT_SYSLOG_HOST ""
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SYSLOG_PORT
|
||||
#define EMSESP_DEFAULT_SYSLOG_PORT 514
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TRACELOG_RAW
|
||||
#define EMSESP_DEFAULT_TRACELOG_RAW false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOILER_HEATINGOFF
|
||||
#define EMSESP_DEFAULT_BOILER_HEATINGOFF false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_REMOTE_TIMEOUT
|
||||
#define EMSESP_DEFAULT_REMOTE_TIMEOUT 24
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_REMOTE_TIMEOUT_EN
|
||||
#define EMSESP_DEFAULT_REMOTE_TIMEOUT_EN false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SHOWER_TIMER
|
||||
#define EMSESP_DEFAULT_SHOWER_TIMER false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SHOWER_ALERT
|
||||
#define EMSESP_DEFAULT_SHOWER_ALERT false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SHOWER_ALERT_TRIGGER
|
||||
#define EMSESP_DEFAULT_SHOWER_ALERT_TRIGGER 7
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SHOWER_MIN_DURATION
|
||||
#define EMSESP_DEFAULT_SHOWER_MIN_DURATION 180
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SHOWER_ALERT_COLDSHOT
|
||||
#define EMSESP_DEFAULT_SHOWER_ALERT_COLDSHOT 10
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_HIDE_LED
|
||||
#define EMSESP_DEFAULT_HIDE_LED false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DALLAS_PARASITE
|
||||
#define EMSESP_DEFAULT_DALLAS_PARASITE false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_NOTOKEN_API
|
||||
#define EMSESP_DEFAULT_NOTOKEN_API false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
|
||||
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_ENUM_FORMAT
|
||||
#define EMSESP_DEFAULT_ENUM_FORMAT 1 // Text
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_ANALOG_ENABLED
|
||||
#define EMSESP_DEFAULT_ANALOG_ENABLED true
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TELNET_ENABLED
|
||||
#define EMSESP_DEFAULT_TELNET_ENABLED true
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_MODBUS_ENABLED
|
||||
#define EMSESP_DEFAULT_MODBUS_ENABLED false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_MODBUS_PORT
|
||||
#define EMSESP_DEFAULT_MODBUS_PORT 502
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_MODBUS_MAX_CLIENTS
|
||||
#define EMSESP_DEFAULT_MODBUS_MAX_CLIENTS 10
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_MODBUS_TIMEOUT
|
||||
#define EMSESP_DEFAULT_MODBUS_TIMEOUT 300
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOARD_PROFILE
|
||||
#define EMSESP_DEFAULT_BOARD_PROFILE "default"
|
||||
#endif
|
||||
|
||||
// Default GPIO PIN definitions
|
||||
|
||||
#ifndef EMSESP_DEFAULT_RX_GPIO
|
||||
#define EMSESP_DEFAULT_RX_GPIO 23 // D7
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_TX_GPIO
|
||||
#define EMSESP_DEFAULT_TX_GPIO 5 // D8
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DALLAS_GPIO
|
||||
#define EMSESP_DEFAULT_DALLAS_GPIO 18
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_LED_GPIO
|
||||
#define EMSESP_DEFAULT_LED_GPIO 2
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PBUTTON_GPIO
|
||||
#define EMSESP_DEFAULT_PBUTTON_GPIO 0
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PHY_TYPE
|
||||
#define EMSESP_DEFAULT_PHY_TYPE 0 // No Ethernet, just Wifi. PHY_type::PHY_TYPE_NONE,
|
||||
#endif
|
||||
|
||||
// MQTT
|
||||
|
||||
#ifndef EMSESP_DEFAULT_BOOL_FORMAT
|
||||
#define EMSESP_DEFAULT_BOOL_FORMAT 1 // on/off
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_MQTT_QOS
|
||||
#define EMSESP_DEFAULT_MQTT_QOS 0
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_MQTT_RETAIN
|
||||
#define EMSESP_DEFAULT_MQTT_RETAIN false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_HA_ENABLED
|
||||
#define EMSESP_DEFAULT_HA_ENABLED false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_TIME
|
||||
#define EMSESP_DEFAULT_PUBLISH_TIME 10
|
||||
#endif
|
||||
|
||||
// default for scheduler etc
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_TIME_OTHER
|
||||
#define EMSESP_DEFAULT_PUBLISH_TIME_OTHER 60
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_HEARTBEAT
|
||||
#define EMSESP_DEFAULT_PUBLISH_HEARTBEAT 60
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_NESTED_FORMAT
|
||||
#define EMSESP_DEFAULT_NESTED_FORMAT 1
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DISCOVERY_PREFIX
|
||||
#define EMSESP_DEFAULT_DISCOVERY_PREFIX "homeassistant"
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_DISCOVERY_TYPE
|
||||
#define EMSESP_DEFAULT_DISCOVERY_TYPE 0 // HA
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_SINGLE
|
||||
#define EMSESP_DEFAULT_PUBLISH_SINGLE false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_PUBLISH_SINGLE2CMD
|
||||
#define EMSESP_DEFAULT_PUBLISH_SINGLE2CMD false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SEND_RESPONSE
|
||||
#define EMSESP_DEFAULT_SEND_RESPONSE false
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SOLAR_MAXFLOW
|
||||
#define EMSESP_DEFAULT_SOLAR_MAXFLOW 30
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_SENSOR_NAME
|
||||
#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
|
||||
|
||||
#ifndef EMSESP_DEFAULT_WEBLOG_BUFFER
|
||||
#define EMSESP_DEFAULT_WEBLOG_BUFFER 50
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_WEBLOG_COMPACT
|
||||
#define EMSESP_DEFAULT_WEBLOG_COMPACT true
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_DEFAULT_ENTITY_FORMAT
|
||||
#define EMSESP_DEFAULT_ENTITY_FORMAT 1 // in MQTT discovery, single instance, shortname (EntityFormat::SINGLE_SHORT)
|
||||
#endif
|
||||
|
||||
// matches Web UI settings
|
||||
enum {
|
||||
|
||||
BOOL_FORMAT_ONOFF_STR = 1, // 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
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
ENUM_FORMAT_VALUE = 1, // 1
|
||||
ENUM_FORMAT_INDEX // 2
|
||||
|
||||
};
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C3
|
||||
#define EMSESP_PLATFORM "ESP32C3"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
#define EMSESP_PLATFORM "ESP32S2"
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define EMSESP_PLATFORM "ESP32S3"
|
||||
#elif CONFIG_IDF_TARGET_ESP32 || EMSESP_STANDALONE
|
||||
#define EMSESP_PLATFORM "ESP32"
|
||||
#else
|
||||
#error Target CONFIG_IDF_TARGET is not supported
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINO_VERSION
|
||||
#ifndef STRINGIZE
|
||||
#define STRINGIZE(s) #s
|
||||
#endif
|
||||
#if TASMOTA_SDK
|
||||
#define ARDUINO_VERSION_STR(major, minor, patch) "Tasmota Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
||||
#else
|
||||
#define ARDUINO_VERSION_STR(major, minor, patch) "ESP32 Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch)
|
||||
#endif
|
||||
#define ARDUINO_VERSION ARDUINO_VERSION_STR(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
203
src/core/device_library.h
Normal file
203
src/core/device_library.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
* These is the EMS devices that we currently recognize
|
||||
* The types and flags are stored in emsdevice.h
|
||||
*/
|
||||
|
||||
// Boilers - 0x08
|
||||
{ 8, DeviceType::BOILER, "CS*800i, Logatherm WLW*", DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
|
||||
{ 12, DeviceType::BOILER, "C1200W", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 64, DeviceType::BOILER, "BK13/BK15, Smartline, GB1*2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 72, DeviceType::BOILER, "Logano GB1*5, Logamatic MC10", DeviceFlags::EMS_DEVICE_FLAG_EMS},
|
||||
{ 81, DeviceType::BOILER, "Cascade CM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 84, DeviceType::BOILER, "Logamax Plus GB022", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 95, DeviceType::BOILER, "Condens, Logamax/Logomatic, Cerapur Top, Greenstar, Generic HT3", DeviceFlags::EMS_DEVICE_FLAG_HT3},
|
||||
{115, DeviceType::BOILER, "Topline, GB162", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{121, DeviceType::BOILER, "Cascade MCM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{122, DeviceType::BOILER, "Proline", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{123, DeviceType::BOILER, "GB*72, Trendline, Cerapur, Greenstar Si", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{131, DeviceType::BOILER, "GB212", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{132, DeviceType::BOILER, "GC7000F", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{133, DeviceType::BOILER, "Logano GB125/KB195i, Logamatic MC110", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{154, DeviceType::BOILER, "Greenstar 30Ri Compact", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{167, DeviceType::BOILER, "Cerapur Aero", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{168, DeviceType::BOILER, "Hybrid Heatpump", DeviceFlags::EMS_DEVICE_FLAG_HYBRID},
|
||||
{170, DeviceType::BOILER, "Logano GB212", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{172, DeviceType::BOILER, "Enviline, Compress 6000AW, Hybrid 3000-7000iAW, SupraEco/Geo 5xx, WLW196i/WSW196i", DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
|
||||
{173, DeviceType::BOILER, "Geo 5xx", DeviceFlags::EMS_DEVICE_FLAG_HEATPUMP},
|
||||
{195, DeviceType::BOILER, "Condens 5000i, Greenstar 8000/GC9800IW, GB192i*2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{203, DeviceType::BOILER, "Logamax U122, Cerapur", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{206, DeviceType::BOILER, "Ecomline Excellent", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{208, DeviceType::BOILER, "Logamax Plus, GB192, Condens GC9000, Greenstar ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{210, DeviceType::BOILER, "Cascade MC400", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{211, DeviceType::BOILER, "EasyControl Adapter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{219, DeviceType::BOILER, "Greenstar HIU, Logamax kompakt WS170", DeviceFlags::EMS_DEVICE_FLAG_HIU},
|
||||
{234, DeviceType::BOILER, "Logamax Plus GB122, Condense 2300, Junkers Cerapur GC2200W", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Controllers - 0x09 / 0x10 / 0x50
|
||||
{ 68, DeviceType::CONTROLLER, "BC10, RFM20", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 81, DeviceType::CONTROLLER, "CM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 84, DeviceType::CONTROLLER, "GB022", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{ 89, DeviceType::CONTROLLER, "BC10, GB142", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{ 95, DeviceType::CONTROLLER, "HT3", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{114, DeviceType::CONTROLLER, "BC10", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{121, DeviceType::CONTROLLER, "MCM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{125, DeviceType::CONTROLLER, "BC25", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{152, DeviceType::CONTROLLER, "Controller", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{168, DeviceType::CONTROLLER, "Hybrid Heatpump", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{169, DeviceType::CONTROLLER, "BC40", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{190, DeviceType::CONTROLLER, "BC10", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{194, DeviceType::CONTROLLER, "BC10", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{206, DeviceType::CONTROLLER, "Ecomline", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{207, DeviceType::CONTROLLER, "Sense II/CS200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x10
|
||||
{209, DeviceType::CONTROLLER, "ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{224, DeviceType::CONTROLLER, "9000i", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{229, DeviceType::CONTROLLER, "8700i", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{230, DeviceType::CONTROLLER, "BC Base", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
{240, DeviceType::CONTROLLER, "Rego 3000", DeviceFlags::EMS_DEVICE_FLAG_IVT}, // 0x09
|
||||
{241, DeviceType::CONTROLLER, "Condens 5000i", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x09
|
||||
|
||||
// Thermostat - not currently supporting write operations, like the Easy/100 types - 0x18
|
||||
{202, DeviceType::THERMOSTAT, "Logamatic TC100, Moduline Easy", DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
||||
{203, DeviceType::THERMOSTAT, "EasyControl, CT200", DeviceFlags::EMS_DEVICE_FLAG_EASY | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18, cannot write
|
||||
|
||||
// Thermostat - Buderus/Nefit/Bosch specific - 0x17 / 0x10 / 0x18 / 0x19-0x1B for hc2-4 / 0x38
|
||||
{ 4, DeviceType::THERMOSTAT, "UI800, BC400", DeviceFlags::EMS_DEVICE_FLAG_BC400}, // 0x10
|
||||
{ 10, DeviceType::THERMOSTAT, "CR11", DeviceFlags::EMS_DEVICE_FLAG_CR11}, // 0x18
|
||||
{ 65, DeviceType::THERMOSTAT, "RC10", DeviceFlags::EMS_DEVICE_FLAG_RC20_N},// 0x17
|
||||
{ 67, DeviceType::THERMOSTAT, "RC30", DeviceFlags::EMS_DEVICE_FLAG_RC30_N},// 0x10 - based on RC35
|
||||
{ 77, DeviceType::THERMOSTAT, "RC20, Moduline 300", DeviceFlags::EMS_DEVICE_FLAG_RC20},// 0x17
|
||||
{ 78, DeviceType::THERMOSTAT, "Moduline 400", DeviceFlags::EMS_DEVICE_FLAG_RC30}, // 0x10
|
||||
{ 79, DeviceType::THERMOSTAT, "RC10, Moduline 100", DeviceFlags::EMS_DEVICE_FLAG_RC10},// 0x17
|
||||
{ 80, DeviceType::THERMOSTAT, "Moduline 200", DeviceFlags::EMS_DEVICE_FLAG_RC10}, // 0x17
|
||||
{ 86, DeviceType::THERMOSTAT, "RC35", DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
||||
{ 90, DeviceType::THERMOSTAT, "RC10, Moduline 100", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||
{ 93, DeviceType::THERMOSTAT, "RC20RF", DeviceFlags::EMS_DEVICE_FLAG_RC20}, // 0x19
|
||||
{ 94, DeviceType::THERMOSTAT, "RFM20 Remote", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x18
|
||||
{151, DeviceType::THERMOSTAT, "RC25", DeviceFlags::EMS_DEVICE_FLAG_RC25}, // 0x17
|
||||
{157, DeviceType::THERMOSTAT, "RC200, CW100, CR120, CR50", DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18,, CR120 v22 is FLAG_BC400
|
||||
{158, DeviceType::THERMOSTAT, "RC3*0, Moduline 3000/1010H, CW400, Sense II, HPC410", DeviceFlags::EMS_DEVICE_FLAG_RC300}, // 0x10
|
||||
{165, DeviceType::THERMOSTAT, "RC100, CR10, Moduline 1000/1010", DeviceFlags::EMS_DEVICE_FLAG_RC100}, // 0x18, 0x38
|
||||
{172, DeviceType::THERMOSTAT, "Rego 2000/3000", DeviceFlags::EMS_DEVICE_FLAG_R3000}, // 0x10
|
||||
{215, DeviceType::THERMOSTAT, "Comfort RF", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||
{216, DeviceType::THERMOSTAT, "CRF200S", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||
{246, DeviceType::THERMOSTAT, "Comfort+2RF", DeviceFlags::EMS_DEVICE_FLAG_CRF | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE}, // 0x18
|
||||
{253, DeviceType::THERMOSTAT, "Rego 3000, UI800, Logamatic BC400", DeviceFlags::EMS_DEVICE_FLAG_BC400}, // 0x10
|
||||
|
||||
// Thermostat - Sieger - 0x10 / 0x17
|
||||
{ 66, DeviceType::THERMOSTAT, "ES72, RC20", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17 or remote
|
||||
{ 76, DeviceType::THERMOSTAT, "ES73", DeviceFlags::EMS_DEVICE_FLAG_RC30_N}, // 0x10
|
||||
{113, DeviceType::THERMOSTAT, "ES72, RC20", DeviceFlags::EMS_DEVICE_FLAG_RC20_N}, // 0x17
|
||||
{156, DeviceType::THERMOSTAT, "ES79", DeviceFlags::EMS_DEVICE_FLAG_RC35}, // 0x10
|
||||
|
||||
// Thermostat - Junkers - 0x10
|
||||
{105, DeviceType::THERMOSTAT, "FW100", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{106, DeviceType::THERMOSTAT, "FW200", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{107, DeviceType::THERMOSTAT, "FR100", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{108, DeviceType::THERMOSTAT, "FR110", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{109, DeviceType::THERMOSTAT, "FB10", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{110, DeviceType::THERMOSTAT, "FB100", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{111, DeviceType::THERMOSTAT, "FR10", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{116, DeviceType::THERMOSTAT, "FW500", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
{147, DeviceType::THERMOSTAT, "FR50", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD},
|
||||
{191, DeviceType::THERMOSTAT, "FR120", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS | DeviceFlags::EMS_DEVICE_FLAG_JUNKERS_OLD}, // older model
|
||||
{192, DeviceType::THERMOSTAT, "FW120", DeviceFlags::EMS_DEVICE_FLAG_JUNKERS},
|
||||
|
||||
// Thermostat remote - 0x38
|
||||
{ 3, DeviceType::THERMOSTAT, "RT800, RC220", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
|
||||
{200, DeviceType::THERMOSTAT, "RC100H, CR10H", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
|
||||
{249, DeviceType::THERMOSTAT, "TR120RF, CR20RF", DeviceFlags::EMS_DEVICE_FLAG_RC100H},
|
||||
|
||||
// Solar Modules - 0x30 (for solar), 0x2A, 0x41 (for ww)
|
||||
{ 73, DeviceType::SOLAR, "SM10", DeviceFlags::EMS_DEVICE_FLAG_SM10},
|
||||
{101, DeviceType::SOLAR, "ISM1", DeviceFlags::EMS_DEVICE_FLAG_ISM},
|
||||
{103, DeviceType::SOLAR, "ISM2", DeviceFlags::EMS_DEVICE_FLAG_ISM},
|
||||
{162, DeviceType::SOLAR, "SM50", DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{163, DeviceType::SOLAR, "SM100, MS100", DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{164, DeviceType::SOLAR, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
|
||||
// Mixer Modules - 0x20-0x27 for HC, 0x28-0x29 for WWC and 0x11 for the MP100
|
||||
{ 69, DeviceType::MIXER, "MM10", DeviceFlags::EMS_DEVICE_FLAG_MM10},
|
||||
{100, DeviceType::MIXER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||
{102, DeviceType::MIXER, "IPM2", DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||
{159, DeviceType::MIXER, "MM50", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{160, DeviceType::MIXER, "MM100", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{161, DeviceType::MIXER, "MM200", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{193, DeviceType::MIXER, "MZ100", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{204, DeviceType::POOL, "MP100", DeviceFlags::EMS_DEVICE_FLAG_MP}, // pool
|
||||
|
||||
// Heat Pumps - 0x38? This is a thermostat like RC100H
|
||||
// also prod-id of wifi module and wireless base
|
||||
{252, DeviceType::HEATPUMP, "HP Module", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Heat Pumps - 0x53
|
||||
{248, DeviceType::HEATPUMP, "Hybrid Manager HM200", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Ventilation - 0x51
|
||||
{231, DeviceType::VENTILATION, "Logavent HRV176", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Heatsource - 0x60
|
||||
{228, DeviceType::HEATSOURCE, "AM200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // alternative heatsource
|
||||
|
||||
// Connect devices - 0x02
|
||||
{171, DeviceType::CONNECT, "OpenTherm Converter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{205, DeviceType::CONNECT, "Moduline Easy Connect", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{206, DeviceType::CONNECT, "Easy Connect", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Wireless sensor base - 0x50
|
||||
{218, DeviceType::CONNECT, "M200, RFM200", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{236, DeviceType::CONNECT, "Wireless sensor base", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{238, DeviceType::CONNECT, "Wireless sensor base", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Switches - 0x11
|
||||
{ 71, DeviceType::SWITCH, "WM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// EM10/100 extension module, pump module - 0x15
|
||||
{ 243, DeviceType::EXTENSION, "EM10, EM100", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Wireless outdoor sensor T1RF - 0x16
|
||||
{ 220, DeviceType::EXTENSION, "T1RF", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x16
|
||||
|
||||
// EM10 error contact and analog flowtemp control- 0x12
|
||||
{ 74, DeviceType::ALERT, "EM10", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Gateways - 0x48
|
||||
{17, DeviceType::GATEWAY, "MX400", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // 0x48 and 0x4B
|
||||
{189, DeviceType::GATEWAY, "KM200, MB LAN 2", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
{252, DeviceType::GATEWAY, "K30RF, MX300", DeviceFlags::EMS_DEVICE_FLAG_NONE},
|
||||
|
||||
// Generic - 0x40 or other with no product-id and no version
|
||||
{0, DeviceType::GENERIC, "unknown", DeviceFlags::EMS_DEVICE_FLAG_NONE}
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
,
|
||||
{100, DeviceType::WATER, "IPM", DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||
{102, DeviceType::WATER, "IPM2", DeviceFlags::EMS_DEVICE_FLAG_IPM},
|
||||
{160, DeviceType::WATER, "MM100", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{161, DeviceType::WATER, "MM200", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{163, DeviceType::WATER, "SM100, MS100", DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{164, DeviceType::WATER, "SM200, MS200", DeviceFlags::EMS_DEVICE_FLAG_SM100},
|
||||
{248, DeviceType::MIXER, "HM210", DeviceFlags::EMS_DEVICE_FLAG_MMPLUS},
|
||||
{157, DeviceType::THERMOSTAT, "RC120", DeviceFlags::EMS_DEVICE_FLAG_CR120}
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
2177
src/core/emsdevice.cpp
Normal file
2177
src/core/emsdevice.cpp
Normal file
File diff suppressed because it is too large
Load Diff
542
src/core/emsdevice.h
Normal file
542
src/core/emsdevice.h
Normal file
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_EMSDEVICE_H_
|
||||
#define EMSESP_EMSDEVICE_H_
|
||||
|
||||
#include "emsfactory.h"
|
||||
#include "telegram.h"
|
||||
#include "mqtt.h"
|
||||
#include "helpers.h"
|
||||
#include "emsdevicevalue.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSdevice {
|
||||
public:
|
||||
virtual ~EMSdevice() = default; // destructor of base class must always be virtual because it's a polymorphic class
|
||||
|
||||
using process_function_p = std::function<void(std::shared_ptr<const Telegram>)>;
|
||||
|
||||
// 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 char * version, const char * default_name, uint8_t flags, uint8_t brand)
|
||||
: device_type_(device_type)
|
||||
, device_id_(device_id)
|
||||
, product_id_(product_id)
|
||||
, default_name_(default_name)
|
||||
, flags_(flags)
|
||||
, brand_(brand) {
|
||||
strlcpy(version_, version, sizeof(version_));
|
||||
}
|
||||
|
||||
// static functions, used outside the class like in console.cpp, command.cpp, emsesp.cpp, mqtt.cpp
|
||||
static const char * device_type_2_device_name(const uint8_t device_type);
|
||||
static uint8_t device_name_2_device_type(const char * topic);
|
||||
static const char * tag_to_string(int8_t tag, const bool translate = true);
|
||||
static const char * uom_to_string(uint8_t uom);
|
||||
static const char * tag_to_mqtt(int8_t tag);
|
||||
static uint8_t decode_brand(uint8_t value);
|
||||
static bool export_values(uint8_t device_type, JsonObject output, const int8_t id, const uint8_t output_target);
|
||||
|
||||
// non static functions
|
||||
|
||||
const char * device_type_name(); // returns short non-translated device type name
|
||||
const char * device_type_2_device_name_translated(); // returns translated device type name
|
||||
bool has_tags(const int8_t tag) const;
|
||||
bool has_cmd(const char * cmd, const int8_t id) const;
|
||||
const char * brand_to_char();
|
||||
std::string to_string();
|
||||
std::string to_string_short();
|
||||
std::string name(); // returns either default or custom name of a device (if defined)
|
||||
|
||||
bool is_device_id(uint8_t device_id) const {
|
||||
return ((device_id & 0x7F) == (device_id_ & 0x7F));
|
||||
}
|
||||
|
||||
// Getters
|
||||
uint8_t device_id() const {
|
||||
return device_id_;
|
||||
}
|
||||
|
||||
uint8_t product_id() const {
|
||||
return product_id_;
|
||||
}
|
||||
|
||||
uint8_t device_type() const {
|
||||
return device_type_; // see enum DeviceType below
|
||||
}
|
||||
|
||||
const char * version() const {
|
||||
return version_;
|
||||
}
|
||||
|
||||
uint8_t brand() const {
|
||||
return brand_;
|
||||
}
|
||||
|
||||
void active(bool active) {
|
||||
active_ = active;
|
||||
}
|
||||
|
||||
const char * default_name() const {
|
||||
return default_name_;
|
||||
}
|
||||
|
||||
// flags
|
||||
inline void add_flags(uint8_t flags) {
|
||||
flags_ |= flags;
|
||||
}
|
||||
inline bool has_flags(uint8_t flags) const {
|
||||
return (flags_ & flags) == flags;
|
||||
}
|
||||
inline void remove_flags(uint8_t flags) {
|
||||
flags_ &= ~flags;
|
||||
}
|
||||
inline uint8_t flags() const {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
// set custom device name
|
||||
void custom_name(std::string const & custom_name) {
|
||||
custom_name_ = custom_name;
|
||||
}
|
||||
|
||||
std::string custom_name() const {
|
||||
return custom_name_;
|
||||
}
|
||||
|
||||
// set device model
|
||||
void model(std::string const & model) {
|
||||
model_ = model;
|
||||
}
|
||||
|
||||
std::string model() const {
|
||||
return model_;
|
||||
}
|
||||
|
||||
inline uint8_t unique_id() const {
|
||||
return unique_id_;
|
||||
}
|
||||
|
||||
void unique_id(uint8_t unique_id) {
|
||||
unique_id_ = unique_id;
|
||||
}
|
||||
|
||||
bool has_update() const {
|
||||
return has_update_;
|
||||
}
|
||||
|
||||
void has_update(bool flag) {
|
||||
has_update_ = flag;
|
||||
}
|
||||
|
||||
void has_update(void * value) {
|
||||
has_update_ = true;
|
||||
publish_value(value);
|
||||
}
|
||||
|
||||
void has_update(char * value, const char * newvalue, size_t len) {
|
||||
if (strcmp(value, newvalue) != 0) {
|
||||
strlcpy(value, newvalue, len);
|
||||
has_update_ = true;
|
||||
publish_value(value);
|
||||
}
|
||||
}
|
||||
|
||||
void has_update(uint8_t & value, uint8_t newvalue) {
|
||||
if (value != newvalue) {
|
||||
value = newvalue;
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
void has_update(uint16_t & value, uint16_t newvalue) {
|
||||
if (value != newvalue) {
|
||||
value = newvalue;
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
void has_update(int16_t & value, int16_t newvalue) {
|
||||
if (value != newvalue) {
|
||||
value = newvalue;
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
void has_update(uint32_t & value, uint32_t newvalue) {
|
||||
if (value != newvalue) {
|
||||
value = newvalue;
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
void has_enumupdate(std::shared_ptr<const Telegram> telegram, uint8_t & value, const uint8_t index, int8_t s = 0) {
|
||||
if (telegram->read_enumvalue(value, index, s)) {
|
||||
has_update_ = true;
|
||||
publish_value((void *)&value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
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>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// modbus
|
||||
int get_modbus_value(uint8_t tag, const std::string & shortname, std::vector<uint16_t> & result);
|
||||
int modbus_value_to_json(uint8_t tag, const std::string & shortname, const std::vector<uint8_t> & modbus_data, JsonObject jsonValue);
|
||||
|
||||
enum Handlers : uint8_t { ALL, RECEIVED, FETCHED, PENDING, IGNORED };
|
||||
void show_telegram_handlers(uuid::console::Shell & shell) const;
|
||||
char * show_telegram_handlers(char * result, const size_t len, const uint8_t handlers);
|
||||
void show_mqtt_handlers(uuid::console::Shell & shell) const;
|
||||
void add_handlers_ignored(const uint16_t handler);
|
||||
|
||||
void set_climate_minmax(int8_t tag, int16_t min, uint32_t max);
|
||||
void setValueEnum(const void * value_p, const char * const ** options);
|
||||
void setCustomizationEntity(const std::string & entity_id);
|
||||
void getCustomizationEntities(std::vector<std::string> & entity_ids);
|
||||
|
||||
void register_telegram_type(const uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, const process_function_p cb);
|
||||
bool handle_telegram(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
std::string get_value_uom(const std::string & shortname) const;
|
||||
bool get_value_info(JsonObject root, const char * cmd, const int8_t id);
|
||||
void get_value_json(JsonObject output, DeviceValue & dv);
|
||||
void get_dv_info(JsonObject json);
|
||||
|
||||
enum OUTPUT_TARGET : uint8_t { API_VERBOSE, API_SHORTNAMES, MQTT, CONSOLE };
|
||||
bool generate_values(JsonObject output, const int8_t tag_filter, const bool nested, const uint8_t output_target);
|
||||
void generate_values_web(JsonObject output, const bool is_dashboard = false);
|
||||
void generate_values_web_customization(JsonArray output);
|
||||
|
||||
void add_device_value(int8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const char * const ** options,
|
||||
const char * const * options_single,
|
||||
int8_t numeric_operator,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint32_t max);
|
||||
|
||||
void register_device_value(int8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const char * const ** options,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint32_t max);
|
||||
|
||||
void
|
||||
register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom, const cmd_function_p f);
|
||||
|
||||
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const ** options, const char * const * name, uint8_t uom);
|
||||
|
||||
void register_device_value(int8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
int8_t numeric_operator,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f = nullptr);
|
||||
|
||||
void register_device_value(int8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
int8_t numeric_operator,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint32_t max);
|
||||
|
||||
// single list of options
|
||||
void register_device_value(int8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const char * const * options_single,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f = nullptr);
|
||||
|
||||
// single list of options, with no translations, with min and max
|
||||
void register_device_value(int8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const char * const * options_single,
|
||||
const char * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint32_t max);
|
||||
|
||||
// no options, optional function f
|
||||
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f = nullptr);
|
||||
|
||||
// no options, with min/max
|
||||
void register_device_value(int8_t tag, void * value_p, uint8_t type, const char * const * name, uint8_t uom, const cmd_function_p f, int16_t min, uint32_t max);
|
||||
|
||||
void write_command(const uint16_t type_id, const uint8_t offset, uint8_t * message_data, const uint8_t message_length, const uint16_t validate_typeid) const;
|
||||
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid) const;
|
||||
void write_command(const uint16_t type_id, const uint8_t offset, const uint8_t value) const;
|
||||
|
||||
void read_command(const uint16_t type_id, uint8_t offset = 0, uint8_t length = 0) const;
|
||||
|
||||
bool is_readable(const void * value_p) const;
|
||||
bool is_readonly(const std::string & cmd, const int8_t id) const;
|
||||
bool has_command(const void * value_p) const;
|
||||
void set_minmax(const void * value_p, int16_t min, uint32_t max);
|
||||
void publish_value(void * value_p) const;
|
||||
void publish_all_values();
|
||||
void mqtt_ha_entity_config_create();
|
||||
const char * telegram_type_name(std::shared_ptr<const Telegram> telegram);
|
||||
void fetch_values();
|
||||
void toggle_fetch(uint16_t telegram_id, bool toggle);
|
||||
bool is_fetch(uint16_t telegram_id) const;
|
||||
bool is_received(uint16_t telegram_id) const;
|
||||
bool has_telegram_id(uint16_t id) const;
|
||||
void ha_config_clear();
|
||||
|
||||
bool ha_config_done() const {
|
||||
return ha_config_done_;
|
||||
}
|
||||
void ha_config_done(const bool v) {
|
||||
ha_config_done_ = v;
|
||||
}
|
||||
|
||||
enum Brand : uint8_t {
|
||||
NO_BRAND = 0, // 0
|
||||
BOSCH, // 1
|
||||
JUNKERS, // 2
|
||||
BUDERUS, // 3
|
||||
NEFIT, // 4
|
||||
SIEGER, // 5
|
||||
WORCESTER, // 11
|
||||
IVT // 13
|
||||
};
|
||||
|
||||
// Unique Identifiers for each Device type, used in Dashboard table
|
||||
// 100 and above is reserved for DeviceType
|
||||
enum DeviceTypeUniqueID : uint8_t {
|
||||
SCHEDULER_UID = 96,
|
||||
ANALOGSENSOR_UID = 97,
|
||||
TEMPERATURESENSOR_UID = 98,
|
||||
CUSTOM_UID = 99 // always 99
|
||||
};
|
||||
|
||||
enum DeviceType : uint8_t {
|
||||
SYSTEM = 0, // this is us (EMS-ESP)
|
||||
TEMPERATURESENSOR, // for internal temperature sensors
|
||||
ANALOGSENSOR, // for internal analog sensors
|
||||
SCHEDULER, // for internal schedule
|
||||
CUSTOM, // for user defined entities
|
||||
BOILER, // from here on enum the ems-devices
|
||||
THERMOSTAT,
|
||||
MIXER,
|
||||
SOLAR,
|
||||
HEATPUMP,
|
||||
GATEWAY,
|
||||
SWITCH,
|
||||
CONTROLLER,
|
||||
CONNECT,
|
||||
ALERT,
|
||||
EXTENSION,
|
||||
GENERIC,
|
||||
HEATSOURCE,
|
||||
VENTILATION,
|
||||
WATER,
|
||||
POOL,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
static constexpr uint8_t EMS_DEVICES_MAX_TELEGRAMS = 20;
|
||||
|
||||
// static device IDs
|
||||
static constexpr uint8_t EMS_DEVICE_ID_BOILER = 0x08; // fixed device_id for Master Boiler/UBA
|
||||
static constexpr uint8_t EMS_DEVICE_ID_HS1 = 0x70; // fixed device_id for 1st. Cascade Boiler/UBA
|
||||
static constexpr uint8_t EMS_DEVICE_ID_HS16 = 0x7F; // fixed device_id for last Cascade Boiler/UBA
|
||||
static constexpr uint8_t EMS_DEVICE_ID_AHS1 = 0x60; // fixed device_id for alternative Heating AM200
|
||||
static constexpr uint8_t EMS_DEVICE_ID_CONTROLLER = 0x09;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_RS232 = 0x04;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_TERMINAL = 0x0A;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_SERVICEKEY = 0x0B;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_CASCADE = 0x0C;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_EASYCOM = 0x0D;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_CONVERTER = 0x0E;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_CLOCK = 0x0F;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_SWITCH = 0x11; // Switch WM10
|
||||
static constexpr uint8_t EMS_DEVICE_ID_ALERT = 0x12; // Error module EM10
|
||||
static constexpr uint8_t EMS_DEVICE_ID_EXTENSION = 0x15; // Extension module EM1000, Pump module PM10
|
||||
static constexpr uint8_t EMS_DEVICE_ID_MODEM = 0x48;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_RFSENSOR = 0x40; // RF sensor only sending, no reply
|
||||
static constexpr uint8_t EMS_DEVICE_ID_RFBASE = 0x50;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_ROOMTHERMOSTAT = 0x17; // TADO using this with no version reply #174
|
||||
static constexpr uint8_t EMS_DEVICE_ID_TADO_OLD = 0x19; // older TADO using this with no version reply, #1031
|
||||
static constexpr uint8_t EMS_DEVICE_ID_MIXER1 = 0x20; // e.g MH210 module as mixer
|
||||
static constexpr uint8_t EMS_DEVICE_ID_MIXER8 = 0x27;
|
||||
static constexpr uint8_t EMS_DEVICE_ID_DHW1 = 0x28; // MM100 module as water station
|
||||
static constexpr uint8_t EMS_DEVICE_ID_DHW2 = 0x29; // MM100 module as water station
|
||||
static constexpr uint8_t EMS_DEVICE_ID_DHW8 = 0x2F; // last DHW module id?
|
||||
|
||||
// generic type IDs
|
||||
static constexpr uint16_t EMS_TYPE_NAME = 0x01; // device config for ems devices, name ascii on offset 27ff for ems+
|
||||
static constexpr uint16_t EMS_TYPE_VERSION = 0x02; // type ID for Version information. Generic across all EMS devices.
|
||||
static constexpr uint16_t EMS_TYPE_UBADevices = 0x07; // EMS connected devices
|
||||
static constexpr uint16_t EMS_TYPE_DEVICEERROR = 0xBE;
|
||||
static constexpr uint16_t EMS_TYPE_SYSTEMERROR = 0xBF;
|
||||
static constexpr uint16_t EMS_TYPE_MENUCONFIG = 0xF7;
|
||||
static constexpr uint16_t EMS_TYPE_VALUECONFIG = 0xF9;
|
||||
|
||||
// device flags: The lower 4 bits hold the unique identifier, the upper 4 bits are used for specific flags
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_NONE = 0;
|
||||
|
||||
// Controller
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_IVT = 1;
|
||||
|
||||
// Boiler
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_EMS = 1;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_EMSPLUS = 2;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HT3 = 3;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HEATPUMP = 4;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HYBRID = 5;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_HIU = 6;
|
||||
|
||||
// Solar Module
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_SM10 = 1;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_SM100 = 2;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_ISM = 3;
|
||||
|
||||
// Mixer Module
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_MMPLUS = 1;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_MM10 = 2;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_IPM = 3;
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_MP = 4;
|
||||
|
||||
// Thermostats
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_NO_WRITE = (1 << 7); // last bit
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS_OLD = (1 << 6); // 6th bit set if older models, like FR120, FR100
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_EASY = 1;
|
||||
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_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
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_RC100H = 13; // with humidity
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_BC400 = 14; // mostly like RC300, but some changes
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_R3000 = 15; // Rego3000, same as RC300 with different wwmodes
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_CR120 = 16; // mostly like RC300, but some changes
|
||||
static constexpr uint8_t EMS_DEVICE_FLAG_CR11 = 17; // CRF200 only monitor
|
||||
|
||||
uint8_t count_entities();
|
||||
uint8_t count_entities_fav();
|
||||
bool has_entities() const;
|
||||
|
||||
// void reserve_device_values(uint8_t elements) {
|
||||
// devicevalues_.reserve(elements);
|
||||
// }
|
||||
|
||||
// void reserve_telegram_functions(uint8_t elements) {
|
||||
// telegram_functions_.reserve(elements);
|
||||
// }
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
struct TelegramFunctionDump {
|
||||
uint16_t type_id_;
|
||||
const char * name_;
|
||||
bool fetch_;
|
||||
TelegramFunctionDump(uint16_t type_id, const char * name, bool fetch)
|
||||
: type_id_(type_id)
|
||||
, name_(name)
|
||||
, fetch_(fetch) {
|
||||
}
|
||||
};
|
||||
void dump_telegram_info(std::vector<TelegramFunctionDump> & telegram_functions_dump);
|
||||
void dump_devicevalue_info();
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint8_t unique_id_;
|
||||
uint8_t device_type_ = DeviceType::SYSTEM;
|
||||
uint8_t device_id_ = 0;
|
||||
uint8_t product_id_ = 0;
|
||||
char version_[6];
|
||||
const char * default_name_; // the fixed name the EMS model taken from the device library
|
||||
std::string custom_name_ = ""; // custom name
|
||||
std::string model_ = ""; // model, taken from the 0x01 telegram. see process_deviceName()
|
||||
uint8_t flags_ = 0;
|
||||
uint8_t brand_ = Brand::NO_BRAND;
|
||||
bool active_ = true;
|
||||
|
||||
bool ha_config_done_ = false;
|
||||
bool has_update_ = false;
|
||||
|
||||
struct TelegramFunction {
|
||||
const uint16_t telegram_type_id_; // it's type_id
|
||||
const char * telegram_type_name_; // e.g. RC20Message
|
||||
bool fetch_; // if this type_id be queried automatically
|
||||
bool received_;
|
||||
const process_function_p process_function_;
|
||||
|
||||
TelegramFunction(uint16_t telegram_type_id, const char * telegram_type_name, bool fetch, bool received, const process_function_p process_function)
|
||||
: telegram_type_id_(telegram_type_id)
|
||||
, telegram_type_name_(telegram_type_name)
|
||||
, fetch_(fetch)
|
||||
, received_(received)
|
||||
, process_function_(process_function) {
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<uint16_t> handlers_ignored_;
|
||||
|
||||
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||
public: // so we can call it from WebCustomizationService::test() and EMSESP::dump_all_entities()
|
||||
#endif
|
||||
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
|
||||
std::vector<DeviceValue> devicevalues_; // all the device values
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
382
src/core/emsdevicevalue.cpp
Normal file
382
src/core/emsdevicevalue.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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 {
|
||||
|
||||
// constructor
|
||||
DeviceValue::DeviceValue(uint8_t device_type,
|
||||
int8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const char * const ** options,
|
||||
const char * const * options_single,
|
||||
int8_t numeric_operator,
|
||||
const char * const short_name,
|
||||
const char * const * fullname,
|
||||
std::string & custom_fullname,
|
||||
uint8_t uom,
|
||||
bool has_cmd,
|
||||
int16_t min,
|
||||
uint32_t max,
|
||||
uint8_t state)
|
||||
: device_type(device_type)
|
||||
, tag(tag)
|
||||
, value_p(value_p)
|
||||
, type(type)
|
||||
, options(options)
|
||||
, options_single(options_single)
|
||||
, numeric_operator(numeric_operator)
|
||||
, short_name(short_name)
|
||||
, fullname(fullname)
|
||||
, custom_fullname(custom_fullname)
|
||||
, uom(uom)
|
||||
, has_cmd(has_cmd)
|
||||
, min(min)
|
||||
, max(max)
|
||||
, state(state) {
|
||||
// calculate #options in options list
|
||||
if (options_single) {
|
||||
options_size = 1;
|
||||
} else {
|
||||
options_size = Helpers::count_items(options);
|
||||
}
|
||||
|
||||
// set the min/max
|
||||
set_custom_minmax();
|
||||
|
||||
/*
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// only added for debugging
|
||||
Serial.print(COLOR_BRIGHT_RED_BACKGROUND);
|
||||
Serial.print(" registering entity: ");
|
||||
Serial.print((short_name));
|
||||
Serial.print("/");
|
||||
if (!custom_fullname.empty()) {
|
||||
Serial.print(COLOR_BRIGHT_CYAN);
|
||||
Serial.print(custom_fullname.c_str());
|
||||
Serial.print(COLOR_RESET);
|
||||
} else {
|
||||
Serial.print(Helpers::translated_word(fullname));
|
||||
}
|
||||
Serial.print(" (#options=");
|
||||
Serial.print(options_size);
|
||||
Serial.print(",numop=");
|
||||
Serial.print(numeric_operator);
|
||||
Serial.print(") ");
|
||||
if (options != nullptr) {
|
||||
uint8_t i = 0;
|
||||
while (i < options_size) {
|
||||
Serial.print(" option");
|
||||
Serial.print(i + 1);
|
||||
Serial.print(":");
|
||||
auto str = Helpers::translated_word(options[i]);
|
||||
Serial.print(str);
|
||||
i++;
|
||||
}
|
||||
} else if (options_single != nullptr) {
|
||||
Serial.print("option1:!");
|
||||
Serial.print((options_single[0]));
|
||||
Serial.print("!");
|
||||
}
|
||||
Serial.println(COLOR_RESET);
|
||||
#endif
|
||||
*/
|
||||
}
|
||||
|
||||
// mapping of UOM, to match order in DeviceValueUOM enum emsdevicevalue.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 char * DeviceValue::DeviceValueUOM_s[] = {
|
||||
|
||||
F_(uom_blank), // 0
|
||||
F_(uom_degrees), F_(uom_degrees), F_(uom_percent), F_(uom_lmin), F_(uom_kwh), F_(uom_wh), FL_(hours)[0],
|
||||
FL_(minutes)[0], F_(uom_ua), F_(uom_bar), F_(uom_kw), F_(uom_w), F_(uom_kb), FL_(seconds)[0],
|
||||
F_(uom_dbm), F_(uom_fahrenheit), F_(uom_mv), F_(uom_sqm), F_(uom_m3), F_(uom_l), F_(uom_kmin),
|
||||
F_(uom_k), F_(uom_volts), F_(uom_mbar), F_(uom_lh), F_(uom_ctkwh), F_(uom_blank)
|
||||
|
||||
};
|
||||
|
||||
// mapping of TAGs, to match order in DeviceValueTAG enum in emsdevicevalue.h
|
||||
const char * const * DeviceValue::DeviceValueTAG_s[] = {
|
||||
|
||||
FL_(tag_device_data), // ""
|
||||
FL_(tag_hc1), // "hc1"
|
||||
FL_(tag_hc2), // "hc2"
|
||||
FL_(tag_hc3), // "hc3"
|
||||
FL_(tag_hc4), // "hc4"
|
||||
FL_(tag_hc5), // "hc5"
|
||||
FL_(tag_hc6), // "hc6"
|
||||
FL_(tag_hc7), // "hc7"
|
||||
FL_(tag_hc8), // "hc8"
|
||||
FL_(tag_dhw1), // "dhw"
|
||||
FL_(tag_dhw2), // "dhw2"
|
||||
FL_(tag_dhw3), // "dhw3"
|
||||
FL_(tag_dhw4), // "dhw4"
|
||||
FL_(tag_dhw5), // "dhw5"
|
||||
FL_(tag_dhw6), // "dhw6"
|
||||
FL_(tag_dhw7), // "dhw7"
|
||||
FL_(tag_dhw8), // "dhw8"
|
||||
FL_(tag_dhw9), // "dhw9"
|
||||
FL_(tag_dhw10), // "dhw10"
|
||||
FL_(tag_ahs1), // "ahs1"
|
||||
FL_(tag_hs1), // "hs1"
|
||||
FL_(tag_hs2), // "hs2"
|
||||
FL_(tag_hs3), // "hs3"
|
||||
FL_(tag_hs4), // "hs4"
|
||||
FL_(tag_hs5), // "hs5"
|
||||
FL_(tag_hs6), // "hs6"
|
||||
FL_(tag_hs7), // "hs7"
|
||||
FL_(tag_hs8), // "hs8"
|
||||
FL_(tag_hs9), // "hs9"
|
||||
FL_(tag_hs10), // "hs10"
|
||||
FL_(tag_hs11), // "hs11"
|
||||
FL_(tag_hs12), // "hs12"
|
||||
FL_(tag_hs13), // "hs13"
|
||||
FL_(tag_hs14), // "hs14"
|
||||
FL_(tag_hs15), // "hs15"
|
||||
FL_(tag_hs16) // "hs16"
|
||||
|
||||
};
|
||||
|
||||
// tags used in MQTT topic names. Matches sequence from DeviceValueTAG_s
|
||||
const char * const DeviceValue::DeviceValueTAG_mqtt[] = {
|
||||
|
||||
FL_(tag_device_data)[0], // ""
|
||||
FL_(tag_hc1)[0], // "hc1"
|
||||
FL_(tag_hc2)[0], // "hc2"
|
||||
FL_(tag_hc3)[0], // "hc3"
|
||||
FL_(tag_hc4)[0], // "hc4"
|
||||
FL_(tag_hc5)[0], // "hc5"
|
||||
FL_(tag_hc6)[0], // "hc6"
|
||||
FL_(tag_hc7)[0], // "hc7"
|
||||
FL_(tag_hc8)[0], // "hc8"
|
||||
FL_(tag_dhw1)[0], // "dhw"
|
||||
FL_(tag_dhw2)[0], // "dhw2"
|
||||
FL_(tag_dhw3)[0], // "dhw3"
|
||||
FL_(tag_dhw4)[0], // "dhw4"
|
||||
FL_(tag_dhw5)[0], // "dhw5"
|
||||
FL_(tag_dhw6)[0], // "dhw6"
|
||||
FL_(tag_dhw7)[0], // "dhw7"
|
||||
FL_(tag_dhw8)[0], // "dhw8"
|
||||
FL_(tag_dhw9)[0], // "dhw9"
|
||||
FL_(tag_dhw10)[0], // "dhw10"
|
||||
FL_(tag_ahs1)[0], // "ahs1"
|
||||
FL_(tag_hs1)[0], // "hs1"
|
||||
FL_(tag_hs2)[0], // "hs2"
|
||||
FL_(tag_hs3)[0], // "hs3"
|
||||
FL_(tag_hs4)[0], // "hs4"
|
||||
FL_(tag_hs5)[0], // "hs5"
|
||||
FL_(tag_hs6)[0], // "hs6"
|
||||
FL_(tag_hs7)[0], // "hs7"
|
||||
FL_(tag_hs8)[0], // "hs8"
|
||||
FL_(tag_hs9)[0], // "hs9"
|
||||
FL_(tag_hs10)[0], // "hs10"
|
||||
FL_(tag_hs11)[0], // "hs11"
|
||||
FL_(tag_hs12)[0], // "hs12"
|
||||
FL_(tag_hs13)[0], // "hs13"
|
||||
FL_(tag_hs14)[0], // "hs14"
|
||||
FL_(tag_hs15)[0], // "hs15"
|
||||
FL_(tag_hs16)[0] // "hs16"
|
||||
|
||||
};
|
||||
|
||||
// count #tags once at compile time
|
||||
uint8_t DeviceValue::NUM_TAGS = sizeof(DeviceValue::DeviceValueTAG_s) / sizeof(char * const *);
|
||||
|
||||
// checks whether the device value has an actual value
|
||||
// returns true if its valid
|
||||
// state is stored in the dv object
|
||||
bool DeviceValue::hasValue() const {
|
||||
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::INT8:
|
||||
has_value = Helpers::hasValue(*(int8_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::UINT8:
|
||||
has_value = Helpers::hasValue(*(uint8_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::INT16:
|
||||
has_value = Helpers::hasValue(*(int16_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::UINT16:
|
||||
has_value = Helpers::hasValue(*(uint16_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::UINT24:
|
||||
case DeviceValueType::TIME:
|
||||
case DeviceValueType::UINT32:
|
||||
has_value = Helpers::hasValue(*(uint32_t *)(value_p));
|
||||
break;
|
||||
case DeviceValueType::CMD:
|
||||
has_value = true; // we count command as an actual entity
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return has_value;
|
||||
}
|
||||
|
||||
// See if the device value has a tag and it's not empty
|
||||
bool DeviceValue::has_tag() const {
|
||||
return ((tag < DeviceValue::NUM_TAGS) && (tag != TAG_NONE) && strlen(DeviceValueTAG_s[tag][0]));
|
||||
}
|
||||
|
||||
// set the min and max value for a device value
|
||||
// converts to signed int, which means rounding to an whole integer
|
||||
// returns false if there is no min/max needed
|
||||
// Types BOOL, ENUM, STRING and CMD are not used
|
||||
bool DeviceValue::get_min_max(int16_t & dv_set_min, uint32_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 num operator and temperatures are °C
|
||||
if (min != 0 || max != 0) {
|
||||
dv_set_min = Helpers::transformNumFloat(min, 0, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(max, 0, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
// init default values to 0 and 0
|
||||
dv_set_min = 0;
|
||||
dv_set_max = 0;
|
||||
|
||||
if (type == DeviceValueType::UINT16) {
|
||||
dv_set_min = Helpers::transformNumFloat(0, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_UINT16_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::INT16) {
|
||||
dv_set_min = Helpers::transformNumFloat(-EMS_VALUE_INT16_NOTSET + 1, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_INT16_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::UINT8) {
|
||||
if (uom == DeviceValueUOM::PERCENT) {
|
||||
dv_set_max = 100;
|
||||
} else {
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_UINT8_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::INT8) {
|
||||
if (uom == DeviceValueUOM::PERCENT) {
|
||||
dv_set_min = -100;
|
||||
dv_set_max = 100;
|
||||
} else {
|
||||
dv_set_min = Helpers::transformNumFloat(-EMS_VALUE_INT8_NOTSET + 1, numeric_operator, fahrenheit);
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_INT8_NOTSET - 1, numeric_operator, fahrenheit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == DeviceValueType::UINT24 || type == DeviceValueType::TIME || type == DeviceValueType::UINT32) {
|
||||
dv_set_max = Helpers::transformNumFloat(EMS_VALUE_UINT24_NOTSET - 1, numeric_operator);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // nothing changed, not supported
|
||||
}
|
||||
|
||||
// extract custom min from custom_fullname
|
||||
bool DeviceValue::get_custom_min(int16_t & val) {
|
||||
auto min_pos = custom_fullname.find('>');
|
||||
bool has_min = (min_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
if (has_min) {
|
||||
int32_t v = Helpers::atoint(custom_fullname.substr(min_pos + 1).c_str());
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
if (max > 0 && v > 0 && (uint32_t)v > max) {
|
||||
return false;
|
||||
}
|
||||
val = v;
|
||||
}
|
||||
return has_min;
|
||||
}
|
||||
|
||||
// extract custom max from custom_fullname
|
||||
bool DeviceValue::get_custom_max(uint32_t & val) {
|
||||
auto max_pos = custom_fullname.find('<');
|
||||
bool has_max = (max_pos != std::string::npos);
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (uom == DeviceValueUOM::DEGREES) ? 2 : (uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
if (has_max) {
|
||||
int32_t v = Helpers::atoint(custom_fullname.substr(max_pos + 1).c_str());
|
||||
if (fahrenheit) {
|
||||
v = (v - (32 * (fahrenheit - 1))) / 1.8; // reset to °C
|
||||
}
|
||||
if (v < 0 || v < (int32_t)min) {
|
||||
return false;
|
||||
}
|
||||
val = v;
|
||||
}
|
||||
return has_max;
|
||||
}
|
||||
|
||||
// sets min max to stored custom values (if set)
|
||||
void DeviceValue::set_custom_minmax() {
|
||||
get_custom_min(min);
|
||||
get_custom_max(max);
|
||||
}
|
||||
|
||||
std::string DeviceValue::get_custom_fullname() const {
|
||||
auto min_pos = custom_fullname.find('>');
|
||||
auto max_pos = custom_fullname.find('<');
|
||||
auto minmax_pos = min_pos < max_pos ? min_pos : max_pos;
|
||||
if (minmax_pos != std::string::npos) {
|
||||
return custom_fullname.substr(0, minmax_pos);
|
||||
}
|
||||
return custom_fullname;
|
||||
}
|
||||
|
||||
// returns the translated fullname or the custom fullname (if provided)
|
||||
// always returns a std::string
|
||||
std::string DeviceValue::get_fullname() const {
|
||||
std::string customname = get_custom_fullname();
|
||||
if (customname.empty()) {
|
||||
return Helpers::translated_word(fullname);
|
||||
}
|
||||
return customname;
|
||||
}
|
||||
|
||||
// returns any custom name defined in the entity_id
|
||||
std::string DeviceValue::get_name(const std::string & entity) {
|
||||
auto pos = entity.find('|');
|
||||
if (pos != std::string::npos) {
|
||||
return entity.substr(2, pos - 2);
|
||||
}
|
||||
return entity.substr(2);
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
219
src/core/emsdevicevalue.h
Normal file
219
src/core/emsdevicevalue.h
Normal file
@@ -0,0 +1,219 @@
|
||||
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// DeviceValue holds the information for a device entity
|
||||
class DeviceValue {
|
||||
public:
|
||||
enum DeviceValueType : uint8_t {
|
||||
BOOL,
|
||||
INT8,
|
||||
UINT8,
|
||||
INT16,
|
||||
UINT16,
|
||||
UINT24,
|
||||
TIME, // same as ULONG (32 bits)
|
||||
UINT32,
|
||||
ENUM,
|
||||
STRING,
|
||||
CMD // special for commands only
|
||||
};
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevicevalue.cpp. Sequence is important!!
|
||||
// also used with HA as uom
|
||||
enum DeviceValueUOM : uint8_t {
|
||||
NONE = 0, // 0
|
||||
DEGREES, // 1 - °C
|
||||
DEGREES_R, // 2 - °C (relative temperature)
|
||||
PERCENT, // 3 - %
|
||||
LMIN, // 4 - l/min
|
||||
KWH, // 5 - kWh
|
||||
WH, // 6 - Wh
|
||||
HOURS, // 7 - h
|
||||
MINUTES, // 8 - m
|
||||
UA, // 9 - µA
|
||||
BAR, // 10 - bar
|
||||
KW, // 11 - kW
|
||||
W, // 12 - W
|
||||
KB, // 13 - kB
|
||||
SECONDS, // 14 - s
|
||||
DBM, // 15 - dBm
|
||||
FAHRENHEIT, // 16 - °F
|
||||
MV, // 17 - mV
|
||||
SQM, // 18 - m²
|
||||
M3, // 19 - m³
|
||||
L, // 20 - L
|
||||
KMIN, // 21 - K*min
|
||||
K, // 22 - K
|
||||
VOLTS, // 23 - V
|
||||
MBAR, // 24 - mbar
|
||||
LH, // 25 - l/h
|
||||
CTKWH, // 26 - ct/kWh
|
||||
CONNECTIVITY // 27 - used in HA
|
||||
};
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevicevalue.cpp
|
||||
enum DeviceValueTAG : int8_t {
|
||||
TAG_NONE = -1, // wild card
|
||||
TAG_DEVICE_DATA = 0,
|
||||
TAG_HC1,
|
||||
TAG_HC2,
|
||||
TAG_HC3,
|
||||
TAG_HC4,
|
||||
TAG_HC5,
|
||||
TAG_HC6,
|
||||
TAG_HC7,
|
||||
TAG_HC8,
|
||||
TAG_DHW1,
|
||||
TAG_DHW2,
|
||||
TAG_DHW3,
|
||||
TAG_DHW4,
|
||||
TAG_DHW5,
|
||||
TAG_DHW6,
|
||||
TAG_DHW7,
|
||||
TAG_DHW8,
|
||||
TAG_DHW9,
|
||||
TAG_DHW10,
|
||||
TAG_AHS1,
|
||||
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 {
|
||||
// low nibble active state of the device value
|
||||
DV_DEFAULT = 0, // 0 - does not yet have a value
|
||||
DV_ACTIVE = (1 << 0), // 1 - has a validated real value
|
||||
DV_HA_CONFIG_CREATED = (1 << 1), // 2 - set if the HA config topic has been created
|
||||
DV_HA_CLIMATE_NO_RT = (1 << 2), // 4 - climate created without roomTemp
|
||||
|
||||
// high nibble as mask for exclusions & special functions
|
||||
DV_WEB_EXCLUDE = (1 << 4), // 16 - not shown on web
|
||||
DV_API_MQTT_EXCLUDE = (1 << 5), // 32 - not shown on mqtt, API
|
||||
DV_READONLY = (1 << 6), // 64 - read only
|
||||
DV_FAVORITE = (1 << 7) // 128 - marked as a favorite
|
||||
};
|
||||
|
||||
// numeric operators
|
||||
// negative numbers used for multipliers
|
||||
enum DeviceValueNumOp : int8_t {
|
||||
DV_NUMOP_NONE = 0, // default
|
||||
DV_NUMOP_DIV2 = 2,
|
||||
DV_NUMOP_DIV10 = 10,
|
||||
DV_NUMOP_DIV60 = 60,
|
||||
DV_NUMOP_DIV100 = 100,
|
||||
DV_NUMOP_MUL5 = -5,
|
||||
DV_NUMOP_MUL10 = -10,
|
||||
DV_NUMOP_MUL15 = -15,
|
||||
DV_NUMOP_MUL50 = -50
|
||||
};
|
||||
|
||||
uint8_t device_type; // EMSdevice::DeviceType
|
||||
int8_t tag; // DeviceValueTAG::*
|
||||
void * value_p; // pointer to variable of any type
|
||||
uint8_t type; // DeviceValueType::*
|
||||
const char * const ** options; // options as a flash char array
|
||||
const char * const * options_single; // options are not translated
|
||||
int8_t numeric_operator;
|
||||
const char * const short_name; // used in MQTT and API
|
||||
const char * const * fullname; // used in Web and Console, is translated
|
||||
std::string custom_fullname; // optional, from customization
|
||||
uint8_t uom; // DeviceValueUOM::*
|
||||
bool has_cmd; // true if there is a Console/MQTT command which matches the short_name
|
||||
int16_t min; // min range
|
||||
uint32_t max; // max range
|
||||
uint8_t state; // DeviceValueState::*
|
||||
|
||||
uint8_t options_size; // number of options in the char array, calculated at class initialization
|
||||
|
||||
DeviceValue(uint8_t device_type, // EMSdevice::DeviceType
|
||||
int8_t tag, // DeviceValueTAG::*
|
||||
void * value_p, // pointer to variable of any type
|
||||
uint8_t type, // DeviceValueType::*
|
||||
const char * const ** options, // options as a flash char array
|
||||
const char * const * options_single, // options are not translated
|
||||
int8_t numeric_operator,
|
||||
const char * const short_name, // used in MQTT and API
|
||||
const char * const * fullname, // used in Web and Console, is translated
|
||||
std::string & custom_fullname, // optional, from customization
|
||||
uint8_t uom, // DeviceValueUOM::*
|
||||
bool has_cmd, // true if there is a Console/MQTT command which matches the short_name
|
||||
int16_t min, // min range
|
||||
uint32_t max, // max range
|
||||
uint8_t state // DeviceValueState::* (also known as the mask)
|
||||
);
|
||||
|
||||
bool hasValue() const;
|
||||
bool has_tag() const;
|
||||
bool get_min_max(int16_t & dv_set_min, uint32_t & dv_set_max);
|
||||
|
||||
void set_custom_minmax();
|
||||
bool get_custom_min(int16_t & val);
|
||||
bool get_custom_max(uint32_t & val);
|
||||
std::string get_custom_fullname() const;
|
||||
std::string get_fullname() const;
|
||||
static std::string get_name(const std::string & entity);
|
||||
|
||||
// dv state flags
|
||||
void add_state(uint8_t s) {
|
||||
state |= s;
|
||||
}
|
||||
bool has_state(uint8_t s) const {
|
||||
return (state & s) == s;
|
||||
}
|
||||
void remove_state(uint8_t s) {
|
||||
state &= ~s;
|
||||
}
|
||||
uint8_t get_state() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
static const char * DeviceValueUOM_s[];
|
||||
static const char * const * DeviceValueTAG_s[];
|
||||
static const char * const DeviceValueTAG_mqtt[];
|
||||
static uint8_t NUM_TAGS; // # tags
|
||||
};
|
||||
|
||||
}; // namespace emsesp
|
||||
|
||||
#endif
|
||||
1797
src/core/emsesp.cpp
Normal file
1797
src/core/emsesp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
304
src/core/emsesp.h
Normal file
304
src/core/emsesp.h
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_EMSESP_H
|
||||
#define EMSESP_EMSESP_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <uuid/common.h>
|
||||
#include <uuid/console.h>
|
||||
#include <uuid/log.h>
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <uuid/telnet.h>
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include "ESP32React/ESP32React.h"
|
||||
#else
|
||||
#include "../lib_standalone/ESP32React.h"
|
||||
#endif
|
||||
|
||||
#include <Preferences.h>
|
||||
|
||||
#include "../web/WebStatusService.h"
|
||||
#include "../web/WebActivityService.h"
|
||||
#include "../web/WebDataService.h"
|
||||
#include "../web/WebSettingsService.h"
|
||||
#include "../web/WebCustomizationService.h"
|
||||
#include "../web/WebSchedulerService.h"
|
||||
#include "../web/WebAPIService.h"
|
||||
#include "../web/WebLogService.h"
|
||||
#include "../web/WebCustomEntityService.h"
|
||||
#include "../web/WebModulesService.h"
|
||||
|
||||
#include "emsdevicevalue.h"
|
||||
#include "emsdevice.h"
|
||||
#include "emsfactory.h"
|
||||
#include "telegram.h"
|
||||
#include "mqtt.h"
|
||||
#include "modbus.h"
|
||||
#include "system.h"
|
||||
#include "temperaturesensor.h"
|
||||
#include "analogsensor.h"
|
||||
#include "console.h"
|
||||
#include "console_stream.h"
|
||||
#include "shower.h"
|
||||
#include "roomcontrol.h"
|
||||
#include "command.h"
|
||||
|
||||
#include "../version.h"
|
||||
|
||||
// Load external modules
|
||||
class Module {}; // forward declaration
|
||||
#include <ModuleLibrary.h>
|
||||
|
||||
#define WATCH_ID_NONE 0 // no watch id set
|
||||
|
||||
// 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
|
||||
#define MAKE_CF_CB(__f) [&](const char * value, const int8_t id) { return __f(value, id); } // for Command Function callbacks Command::cmd_function_p
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using DeviceValueUOM = DeviceValue::DeviceValueUOM;
|
||||
using DeviceValueType = DeviceValue::DeviceValueType;
|
||||
using DeviceValueState = DeviceValue::DeviceValueState;
|
||||
using DeviceValueTAG = DeviceValue::DeviceValueTAG;
|
||||
using DeviceValueNumOp = DeviceValue::DeviceValueNumOp;
|
||||
|
||||
// forward declarations for compiler
|
||||
class EMSESPShell;
|
||||
class Shower;
|
||||
|
||||
class EMSESP {
|
||||
public:
|
||||
EMSESP();
|
||||
~EMSESP() = default;
|
||||
virtual void start();
|
||||
virtual void loop();
|
||||
|
||||
static uuid::log::Logger logger();
|
||||
|
||||
static void publish_device_values(uint8_t device_type);
|
||||
static void publish_other_values();
|
||||
static void publish_sensor_values(const bool time, const bool force = false);
|
||||
static void publish_all(bool force = false);
|
||||
static void reset_mqtt_ha();
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
static void run_test(uuid::console::Shell & shell, const std::string & command); // only for testing
|
||||
static void dummy_mqtt_commands(const char * message);
|
||||
static void rx_telegram(const std::vector<uint8_t> & data);
|
||||
static void uart_telegram(const std::vector<uint8_t> & rx_data);
|
||||
#endif
|
||||
|
||||
static bool process_telegram(std::shared_ptr<const Telegram> telegram);
|
||||
static std::string pretty_telegram(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
static void send_read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0, const bool front = false);
|
||||
static void send_write_request(const uint16_t type_id,
|
||||
const uint8_t dest,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validate_typeid);
|
||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value);
|
||||
static void send_write_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t value, const uint16_t validate_typeid);
|
||||
|
||||
static bool device_exists(const uint8_t device_id);
|
||||
static void device_active(const uint8_t device_id, const bool active);
|
||||
static bool cmd_is_readonly(const uint8_t device_type, const uint8_t device_id, const char * cmd, const int8_t id);
|
||||
static uint8_t device_id_from_cmd(const uint8_t device_type, const char * cmd, const int8_t 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 bool get_device_value_info(JsonObject root, const char * cmd, const int8_t id, const uint8_t devicetype);
|
||||
|
||||
static void show_device_values(uuid::console::Shell & shell);
|
||||
static void show_sensor_values(uuid::console::Shell & shell);
|
||||
static void show_devices(uuid::console::Shell & shell);
|
||||
static void show_ems(uuid::console::Shell & shell);
|
||||
|
||||
static void dump_all_entities(uuid::console::Shell & shell);
|
||||
static void dump_all_telegrams(uuid::console::Shell & shell);
|
||||
|
||||
static void uart_init();
|
||||
|
||||
static void incoming_telegram(uint8_t * data, const uint8_t length);
|
||||
|
||||
static bool sensor_enabled() {
|
||||
return (temperaturesensor_.sensor_enabled());
|
||||
}
|
||||
|
||||
static bool analog_enabled() {
|
||||
return (analogsensor_.analog_enabled());
|
||||
}
|
||||
|
||||
enum Watch : uint8_t { WATCH_OFF, WATCH_ON, WATCH_RAW, WATCH_UNKNOWN };
|
||||
static void watch_id(uint16_t id);
|
||||
static uint16_t watch_id() {
|
||||
return watch_id_;
|
||||
}
|
||||
|
||||
static void watch(uint8_t watch) {
|
||||
watch_ = watch; // 0=off, 1=on, 2=raw
|
||||
if (watch == WATCH_OFF) {
|
||||
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 void set_response_id(uint16_t id) {
|
||||
response_id_ = id;
|
||||
}
|
||||
|
||||
static uint16_t response_id() {
|
||||
return response_id_;
|
||||
}
|
||||
|
||||
static bool wait_validate() {
|
||||
return (wait_validate_ != 0);
|
||||
}
|
||||
static void wait_validate(uint16_t wait) {
|
||||
wait_validate_ = wait;
|
||||
}
|
||||
|
||||
enum Bus_status : uint8_t { BUS_STATUS_CONNECTED = 0, BUS_STATUS_TX_ERRORS, BUS_STATUS_OFFLINE };
|
||||
static uint8_t bus_status();
|
||||
|
||||
static bool tap_water_active() {
|
||||
return tap_water_active_;
|
||||
}
|
||||
|
||||
static void tap_water_active(const bool tap_water_active) {
|
||||
tap_water_active_ = tap_water_active;
|
||||
}
|
||||
|
||||
static bool trace_raw() {
|
||||
return trace_raw_;
|
||||
}
|
||||
|
||||
static void trace_raw(bool set) {
|
||||
trace_raw_ = set;
|
||||
}
|
||||
|
||||
static void fetch_device_values(const uint8_t device_id = 0);
|
||||
static void fetch_device_values_type(const uint8_t device_type);
|
||||
static bool valid_device(const uint8_t device_id);
|
||||
static void scheduled_fetch_values();
|
||||
|
||||
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();
|
||||
|
||||
static bool return_not_found(JsonObject output, const char * msg, const char * cmd);
|
||||
|
||||
static std::deque<std::unique_ptr<EMSdevice>> emsdevices;
|
||||
|
||||
// services
|
||||
static Mqtt mqtt_;
|
||||
static Modbus * modbus_;
|
||||
static System system_;
|
||||
static TemperatureSensor temperaturesensor_;
|
||||
static AnalogSensor analogsensor_;
|
||||
static Shower shower_;
|
||||
static RxService rxservice_;
|
||||
static TxService txservice_;
|
||||
static Preferences nvs_;
|
||||
|
||||
// web controllers
|
||||
static ESP32React esp32React;
|
||||
static WebSettingsService webSettingsService;
|
||||
static WebStatusService webStatusService;
|
||||
static WebActivityService webActivityService;
|
||||
static WebDataService webDataService;
|
||||
static WebAPIService webAPIService;
|
||||
static WebLogService webLogService;
|
||||
static WebCustomizationService webCustomizationService;
|
||||
static WebSchedulerService webSchedulerService;
|
||||
static WebCustomEntityService webCustomEntityService;
|
||||
static WebModulesService webModulesService;
|
||||
|
||||
private:
|
||||
static std::string device_tostring(const uint8_t device_id);
|
||||
static void process_UBADevices(std::shared_ptr<const Telegram> telegram);
|
||||
static void process_deviceName(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();
|
||||
|
||||
void shell_prompt();
|
||||
void start_serial_console();
|
||||
|
||||
static constexpr uint32_t EMS_FETCH_FREQUENCY = 60000; // check every minute
|
||||
static constexpr uint8_t EMS_WAIT_KM_TIMEOUT = 60; // wait one minute
|
||||
|
||||
struct Device_record {
|
||||
uint8_t product_id;
|
||||
EMSdevice::DeviceType device_type;
|
||||
const char * default_name;
|
||||
uint8_t flags;
|
||||
};
|
||||
static std::vector<Device_record> device_library_;
|
||||
|
||||
static uint16_t watch_id_;
|
||||
static uint8_t watch_;
|
||||
static uint16_t read_id_;
|
||||
static bool read_next_;
|
||||
static uint16_t publish_id_;
|
||||
static uint16_t response_id_;
|
||||
static bool tap_water_active_;
|
||||
static uint8_t publish_all_idx_;
|
||||
static uint8_t unique_id_count_;
|
||||
static bool trace_raw_;
|
||||
static uint16_t wait_validate_;
|
||||
static bool wait_km_;
|
||||
static uint32_t last_fetch_;
|
||||
|
||||
// UUID stuff
|
||||
static constexpr auto & serial_console_ = Serial;
|
||||
static constexpr unsigned long SERIAL_CONSOLE_BAUD_RATE = 115200;
|
||||
|
||||
std::shared_ptr<EMSESPShell> shell_;
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uuid::telnet::TelnetService telnet_;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static uuid::log::Logger logger_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
45
src/core/emsesp_stub.hpp
Normal file
45
src/core/emsesp_stub.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_EMSESP_STUB_H
|
||||
#define EMSESP_EMSESP_STUB_H
|
||||
|
||||
#include "system.h"
|
||||
#include "mqtt.h"
|
||||
#include "temperaturesensor.h"
|
||||
// #include "../version.h"
|
||||
#include "ESP32React/ESP32React.h"
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
using uuid::log::Logger;
|
||||
|
||||
// forward declarator
|
||||
// used to bind EMS-ESP functions to external frameworks
|
||||
namespace emsesp {
|
||||
class EMSESP {
|
||||
public:
|
||||
static Mqtt mqtt_;
|
||||
static System system_;
|
||||
static TemperatureSensor temperaturesensor_;
|
||||
static Logger logger();
|
||||
static ESP32React esp32React;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
101
src/core/emsfactory.h
Normal file
101
src/core/emsfactory.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_EMSFACTORY_H_
|
||||
#define EMSESP_EMSFACTORY_H_
|
||||
|
||||
#include <map>
|
||||
#include <memory> // for unique_ptr
|
||||
|
||||
#include "emsdevice.h"
|
||||
|
||||
// Macro for class registration
|
||||
// Anonymous namespace is used to make the definitions here private to the current
|
||||
// compilation unit (current file). It is equivalent to the old C static keyword.
|
||||
#define REGISTER_FACTORY(derivedClass, device_type) \
|
||||
namespace { \
|
||||
auto registry_##derivedClass = ConcreteEMSFactory<derivedClass>(device_type); \
|
||||
}
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class EMSdevice; // forward declaration, for gcc linking
|
||||
|
||||
class EMSFactory {
|
||||
public:
|
||||
virtual ~EMSFactory() = default;
|
||||
|
||||
// Register factory object of derived class
|
||||
// using the device_type as the unique identifier
|
||||
static auto registerFactory(const uint8_t device_type, EMSFactory * factory) -> void {
|
||||
auto & reg = EMSFactory::getRegister();
|
||||
reg[device_type] = factory;
|
||||
}
|
||||
|
||||
using FactoryMap = std::map<uint8_t, EMSFactory *>;
|
||||
|
||||
// returns all registered classes (really only for debugging)
|
||||
static auto device_handlers() -> FactoryMap {
|
||||
return EMSFactory::getRegister();
|
||||
}
|
||||
|
||||
// Construct derived class returning an unique ptr
|
||||
static auto add(const uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * default_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, default_name, flags, brand));
|
||||
}
|
||||
|
||||
virtual auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) const
|
||||
-> EMSdevice * = 0;
|
||||
|
||||
private:
|
||||
// Force global variable to be initialized, thus it avoids the "initialization order fiasco"
|
||||
static auto getRegister() -> FactoryMap & {
|
||||
static FactoryMap classRegister{};
|
||||
return classRegister;
|
||||
}
|
||||
|
||||
// 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, const char * version, const char * name, uint8_t flags, uint8_t brand)
|
||||
-> EMSdevice * {
|
||||
auto it = EMSFactory::getRegister().find(device_type);
|
||||
if (it != EMSFactory::getRegister().end()) {
|
||||
return it->second->construct(device_type, device_id, product_id, version, name, flags, brand);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DerivedClass>
|
||||
class ConcreteEMSFactory : EMSFactory {
|
||||
public:
|
||||
// Register this global object on the EMSFactory register
|
||||
ConcreteEMSFactory(const uint8_t device_type) {
|
||||
EMSFactory::registerFactory(device_type, this);
|
||||
}
|
||||
|
||||
auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand) const
|
||||
-> EMSdevice * {
|
||||
return new DerivedClass(device_type, device_id, product_id, version, name, flags, brand);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
842
src/core/helpers.cpp
Normal file
842
src/core/helpers.cpp
Normal file
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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 "helpers.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// like itoa but for hex, and quicker
|
||||
// note: only for single byte hex values
|
||||
char * Helpers::hextoa(char * result, const uint8_t value) {
|
||||
char * p = result;
|
||||
uint8_t nib1 = (value >> 4) & 0x0F;
|
||||
uint8_t nib2 = (value >> 0) & 0x0F;
|
||||
*p++ = nib1 < 0xA ? '0' + nib1 : 'A' + nib1 - 0xA;
|
||||
*p++ = nib2 < 0xA ? '0' + nib2 : 'A' + nib2 - 0xA;
|
||||
*p = '\0'; // null terminate just in case
|
||||
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) {
|
||||
if (nullptr == ptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned long t = 0;
|
||||
unsigned long tmp = value;
|
||||
int count = 0;
|
||||
|
||||
if (tmp == 0) {
|
||||
count++;
|
||||
}
|
||||
|
||||
while (tmp > 0) {
|
||||
tmp = tmp / base;
|
||||
count++;
|
||||
}
|
||||
|
||||
ptr += count;
|
||||
|
||||
*ptr = '\0';
|
||||
|
||||
do {
|
||||
unsigned long res = value - base * (t = value / base);
|
||||
if (res < 10) {
|
||||
*--ptr = '0' + res;
|
||||
} else if (res < 16) {
|
||||
*--ptr = 'A' - 10 + res;
|
||||
}
|
||||
} while ((value = t) != 0);
|
||||
|
||||
return (ptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
// fast itoa 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* fast itoa
|
||||
* written by Lukás Chmela, Released under GPLv3. http://www.strudel.org.uk/itoa/ version 0.4
|
||||
* optimized for ESP32
|
||||
*/
|
||||
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';
|
||||
return result;
|
||||
}
|
||||
|
||||
char * ptr = result, *ptr1 = result;
|
||||
int32_t tmp_value;
|
||||
|
||||
do {
|
||||
tmp_value = value;
|
||||
value /= base;
|
||||
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
|
||||
} while (value);
|
||||
|
||||
// Apply negative sign
|
||||
if (tmp_value < 0) {
|
||||
*ptr++ = '-';
|
||||
}
|
||||
|
||||
*ptr-- = '\0';
|
||||
while (ptr1 < ptr) {
|
||||
char tmp_char = *ptr;
|
||||
*ptr-- = *ptr1;
|
||||
*ptr1++ = tmp_char;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// for decimals 0 to 99, printed as a 2 char string
|
||||
char * Helpers::smallitoa(char * result, const uint8_t value) {
|
||||
result[0] = ((value / 10) == 0) ? '0' : (value / 10) + '0';
|
||||
result[1] = (value % 10) + '0';
|
||||
result[2] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
// for decimals 0 to 999, printed as a string
|
||||
char * Helpers::smallitoa(char * result, const uint16_t value) {
|
||||
result[0] = ((value / 100) == 0) ? '0' : (value / 100) + '0';
|
||||
result[1] = (((value % 100) / 10) == 0) ? '0' : ((value % 100) / 10) + '0';
|
||||
result[2] = (value % 10) + '0';
|
||||
result[3] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
// work out how to display booleans
|
||||
// for strings only
|
||||
char * Helpers::render_boolean(char * result, const bool value, const bool dashboard) {
|
||||
uint8_t bool_format_ = dashboard ? EMSESP::system_.bool_dashboard() : EMSESP::system_.bool_format();
|
||||
|
||||
if (bool_format_ == BOOL_FORMAT_ONOFF_STR) {
|
||||
strlcpy(result, value ? translated_word(FL_(on)) : translated_word(FL_(off)), 12);
|
||||
} else if (bool_format_ == BOOL_FORMAT_ONOFF_STR_CAP) {
|
||||
strlcpy(result, value ? translated_word(FL_(ON)) : translated_word(FL_(OFF)), 12);
|
||||
} 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;
|
||||
}
|
||||
|
||||
// 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, int8_t format, const uint8_t fahrenheit) {
|
||||
// special check if its a boolean
|
||||
if ((uint8_t)format == EMS_VALUE_BOOL) {
|
||||
if (value == EMS_VALUE_BOOL_OFF) {
|
||||
render_boolean(result, false);
|
||||
} else if (value == EMS_VALUE_BOOL_NOTSET) {
|
||||
return nullptr;
|
||||
} else {
|
||||
render_boolean(result, true); // assume on. could have value 0x01 or 0xFF
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!hasValue(value)) {
|
||||
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(new_value, result, 10); // format = 0
|
||||
return result;
|
||||
}
|
||||
|
||||
char s2[10];
|
||||
|
||||
// special case for / 2
|
||||
if (format == 2) {
|
||||
strlcpy(result, itoa(new_value >> 1, s2, 10), 5);
|
||||
strlcat(result, ".", 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;
|
||||
|
||||
} 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 double value, const int8_t format) {
|
||||
if (format > 8) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t p[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
|
||||
|
||||
char * ret = result;
|
||||
double v = value < 0 ? value - 1.0 / (2 * p[format]) : value + 1.0 / (2 * p[format]);
|
||||
auto whole = (long long)v;
|
||||
|
||||
if (whole <= 0 && v < 0) {
|
||||
result[0] = '-';
|
||||
result++;
|
||||
whole = -whole;
|
||||
v = -v;
|
||||
}
|
||||
#ifndef EMSESP_STANDALONE
|
||||
lltoa(whole, result, 10);
|
||||
#else
|
||||
ultostr(result, whole, 10);
|
||||
#endif
|
||||
|
||||
while (*result != '\0') {
|
||||
result++;
|
||||
}
|
||||
|
||||
*result++ = '.';
|
||||
auto decimal = abs((int32_t)((v - whole) * p[format]));
|
||||
for (int8_t i = 1; i < format; i++) {
|
||||
if (decimal < p[i]) {
|
||||
*result++ = '0'; // add leading zeros
|
||||
}
|
||||
}
|
||||
itoa(decimal, result, 10);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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 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[13] = {0};
|
||||
// just print it if no conversion required (format = 0)
|
||||
if (!format) {
|
||||
strlcpy(result, itoa(new_value, s, 10), sizeof(s)); // format is 0
|
||||
return result;
|
||||
}
|
||||
|
||||
result[0] = '\0';
|
||||
|
||||
// check for negative values
|
||||
if (new_value < 0) {
|
||||
strlcpy(result, "-", sizeof(s));
|
||||
new_value *= -1; // convert to positive
|
||||
} else {
|
||||
strlcpy(result, "", sizeof(s));
|
||||
}
|
||||
|
||||
// do floating point
|
||||
if (format == 2) {
|
||||
// divide by 2
|
||||
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) * 10) / format, s, 10), sizeof(s));
|
||||
} else {
|
||||
strlcat(result, itoa(new_value * format * -1, s, 10), sizeof(s));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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, (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
|
||||
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, (int32_t)value, format, fahrenheit)); // use same code, force it to a signed int
|
||||
}
|
||||
|
||||
// uint32: render long (4 byte) unsigned values
|
||||
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';
|
||||
uint32_t new_value = fahrenheit ? format ? value * 1.8 + 32 * format * (fahrenheit - 1) : value * 1.8 + 32 * (fahrenheit - 1) : value;
|
||||
char s[14] = {0};
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!format) {
|
||||
strlcpy(result, lltoa(new_value, s, 10), sizeof(s)); // format is 0
|
||||
} else if (format > 0) {
|
||||
strlcpy(result, lltoa(new_value / format, s, 10), sizeof(s));
|
||||
strlcat(result, ".", sizeof(s));
|
||||
strlcat(result, itoa(((new_value % format) * 10) / format, s, 10), sizeof(s));
|
||||
if (format == 100) {
|
||||
strlcat(result, itoa(new_value % 10, s, 10), sizeof(s));
|
||||
}
|
||||
} else {
|
||||
strlcpy(result, lltoa(new_value * format * -1, s, 10), sizeof(s));
|
||||
}
|
||||
#else
|
||||
if (!format) {
|
||||
strlcpy(result, ultostr(s, new_value, 10), sizeof(s)); // format is 0
|
||||
} else {
|
||||
strlcpy(result, ultostr(s, new_value / format, 10), sizeof(s));
|
||||
strlcat(result, ".", sizeof(s));
|
||||
strncat(result, ultostr(s, new_value % format, 10), sizeof(s));
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// creates string of hex values from an array of bytes
|
||||
std::string Helpers::data_to_hex(const uint8_t * data, const uint8_t length) {
|
||||
if (length == 0) {
|
||||
return "<empty>";
|
||||
}
|
||||
|
||||
char str[length * 3];
|
||||
memset(str, 0, sizeof(str));
|
||||
|
||||
char buffer[4];
|
||||
char * p = &str[0];
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
Helpers::hextoa(buffer, data[i]);
|
||||
*p++ = buffer[0];
|
||||
*p++ = buffer[1];
|
||||
*p++ = ' '; // space
|
||||
}
|
||||
*--p = '\0'; // null terminate just in case, loosing the trailing space
|
||||
|
||||
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++;
|
||||
// transform hex character to the 4bit equivalent number, using the ascii table indexes
|
||||
if (byte == ' ')
|
||||
byte = *hex++; // skip spaces
|
||||
if (byte >= '0' && byte <= '9')
|
||||
byte = byte - '0';
|
||||
else if (byte >= 'a' && byte <= 'f')
|
||||
byte = byte - 'a' + 10;
|
||||
else if (byte >= 'A' && byte <= 'F')
|
||||
byte = byte - 'A' + 10;
|
||||
else
|
||||
return 0; // error
|
||||
// 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
|
||||
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
|
||||
// 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)
|
||||
double Helpers::transformNumFloat(double value, const int8_t numeric_operator, const uint8_t fahrenheit) {
|
||||
double val;
|
||||
|
||||
if (numeric_operator == 0) { // DV_NUMOP_NONE
|
||||
val = value * 100;
|
||||
} else if (numeric_operator > 0) { // DV_NUMOP_DIVxx
|
||||
val = value * 100 / numeric_operator;
|
||||
} else { // DV_NUMOP_MULxx
|
||||
val = value * -100 * numeric_operator;
|
||||
}
|
||||
|
||||
if (fahrenheit) {
|
||||
val = val * 1.8 + 3200 * (fahrenheit - 1);
|
||||
}
|
||||
|
||||
return (round(val)) / 100.0;
|
||||
}
|
||||
|
||||
// abs of a signed 32-bit integer
|
||||
uint32_t Helpers::abs(const int32_t i) {
|
||||
return (i < 0 ? -i : i);
|
||||
}
|
||||
|
||||
// for booleans, use isBool true (EMS_VALUE_BOOL)
|
||||
bool Helpers::hasValue(const uint8_t & value, const uint8_t isBool) {
|
||||
if (isBool == EMS_VALUE_BOOL) {
|
||||
return (value != EMS_VALUE_BOOL_NOTSET);
|
||||
}
|
||||
return (value != EMS_VALUE_UINT8_NOTSET);
|
||||
}
|
||||
|
||||
bool Helpers::hasValue(const int8_t & value) {
|
||||
return (value != EMS_VALUE_INT8_NOTSET);
|
||||
}
|
||||
|
||||
bool Helpers::hasValue(const char * value) {
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (value[0] != '\0');
|
||||
}
|
||||
|
||||
// for short these are typically 0x8300, 0x7D00 and sometimes 0x8000
|
||||
bool Helpers::hasValue(const int16_t & value) {
|
||||
return (abs(value) < EMS_VALUE_UINT16_NOTSET);
|
||||
}
|
||||
|
||||
bool Helpers::hasValue(const uint16_t & value) {
|
||||
return (value < EMS_VALUE_UINT16_NOTSET);
|
||||
}
|
||||
|
||||
bool Helpers::hasValue(const uint32_t & value) {
|
||||
return (value != EMS_VALUE_UINT24_NOTSET && value != EMS_VALUE_UINT32_NOTSET);
|
||||
}
|
||||
|
||||
// checks if we can convert a char string to an int value
|
||||
bool Helpers::value2number(const char * value, int & value_i, const int min, const int max) {
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
value_i = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value_i = atoi(value);
|
||||
if (value_i >= min && value_i <= max) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if we can convert a char string to a float value
|
||||
bool Helpers::value2float(const char * value, float & value_f) {
|
||||
value_f = 0;
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value[0] == '-' || value[0] == '.' || (value[0] >= '0' && value[0] <= '9')) {
|
||||
value_f = atof(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value[0] == '+' && (value[1] == '.' || (value[1] >= '0' && value[1] <= '9'))) {
|
||||
value_f = atof(value + 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Helpers::value2temperature(const char * value, float & value_f, bool relative) {
|
||||
if (value2float(value, value_f)) {
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
value_f = relative ? (value_f / 1.8) : (value_f - 32) / 1.8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Helpers::value2temperature(const char * value, int & value_i, const bool relative, const int min, const int max) {
|
||||
if (value2number(value, value_i, min, max)) {
|
||||
if (EMSESP::system_.fahrenheit()) {
|
||||
value_i = relative ? (value_i / 1.8) : (value_i - 32) / 1.8;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if we can convert a char string to a lowercase string
|
||||
bool Helpers::value2string(const char * value, std::string & value_s) {
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
value_s = std::string{};
|
||||
return false;
|
||||
}
|
||||
|
||||
value_s = toLower(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// checks to see if a string (usually a command or payload cmd) looks like a boolean
|
||||
// on, off, true, false, 1, 0
|
||||
bool Helpers::value2bool(const char * value, bool & value_b) {
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string bool_str = toLower(value);
|
||||
|
||||
if ((bool_str == std::string(Helpers::translated_word(FL_(on)))) || (bool_str == toLower(Helpers::translated_word(FL_(ON)))) || (bool_str == "on")
|
||||
|| (bool_str == "1") || (bool_str == "true")) {
|
||||
value_b = true;
|
||||
return true; // is a bool
|
||||
}
|
||||
|
||||
if ((bool_str == std::string(Helpers::translated_word(FL_(off)))) || (bool_str == toLower(Helpers::translated_word(FL_(OFF)))) || (bool_str == "off")
|
||||
|| (bool_str == "0") || (bool_str == "false")) {
|
||||
value_b = false;
|
||||
return true; // is a bool
|
||||
}
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
emsesp::EMSESP::logger().debug("Error. value2bool: %s is not a boolean", value);
|
||||
#endif
|
||||
|
||||
return false; // not a bool
|
||||
}
|
||||
|
||||
// checks to see if a string is member of a vector and return the index, also allow true/false for on/off
|
||||
// this for a list of lists, when using translated strings
|
||||
bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * const ** strs) {
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
return false;
|
||||
}
|
||||
std::string str = toLower(value);
|
||||
|
||||
for (value_ui = 0; strs[value_ui]; value_ui++) {
|
||||
std::string str1 = toLower(std::string(Helpers::translated_word(strs[value_ui])));
|
||||
std::string str2 = toLower((strs[value_ui][0])); // also check for default language
|
||||
if ((str1 != "")
|
||||
&& ((str2 == "off" && str == "false") || (str2 == "on" && str == "true") || (str == str1) || (str == str2)
|
||||
|| (value[0] == ('0' + value_ui) && value[1] == '\0'))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
value_ui = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// finds the string (value) of a list vector (strs)
|
||||
// returns true if found, and sets the value_ui to the index, else false
|
||||
// also allow true/false for on/off
|
||||
bool Helpers::value2enum(const char * value, uint8_t & value_ui, const char * const * strs) {
|
||||
if ((value == nullptr) || (strlen(value) == 0)) {
|
||||
return false;
|
||||
}
|
||||
std::string str = toLower(value);
|
||||
|
||||
std::string s_on = Helpers::translated_word(FL_(on));
|
||||
std::string s_off = Helpers::translated_word(FL_(off));
|
||||
|
||||
// stops when a nullptr is found, which is the end delimeter of a MAKE_TRANSLATION()
|
||||
// could use count_items() to avoid buffer over-run but this works
|
||||
for (value_ui = 0; strs[value_ui]; value_ui++) {
|
||||
std::string enum_str = toLower((strs[value_ui]));
|
||||
|
||||
if ((enum_str != "")
|
||||
&& ((enum_str == "off" && (str == s_off || str == "false")) || (enum_str == "on" && (str == s_on || str == "true")) || (str == enum_str)
|
||||
|| (value[0] == ('0' + value_ui) && value[1] == '\0'))) {
|
||||
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;
|
||||
std::transform(lc.begin(), lc.end(), lc.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
return lc;
|
||||
}
|
||||
|
||||
std::string Helpers::toLower(const char * s) {
|
||||
return toLower(std::string(s));
|
||||
}
|
||||
|
||||
std::string Helpers::toUpper(std::string const & s) {
|
||||
std::string lc = s;
|
||||
std::transform(lc.begin(), lc.end(), lc.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||
return lc;
|
||||
}
|
||||
|
||||
// capitalizes one UTF-8 character in char array
|
||||
// works with Latin1 (1 byte), Polish amd some other (2 bytes) characters
|
||||
// TODO add special characters that occur in other supported languages
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#endif
|
||||
void Helpers::CharToUpperUTF8(char * c) {
|
||||
auto p = (c + 1); // pointer to 2nd char of 2-byte unicode char
|
||||
char p_v = *p; // value of 2nd char in 2-byte unicode char
|
||||
|
||||
switch (*c) {
|
||||
case (char)0xC3:
|
||||
// grave, acute, circumflex, diaeresis, etc.
|
||||
if ((p_v >= (char)0xA0) && (p_v <= (char)0xBE)) {
|
||||
*p -= 0x20;
|
||||
}
|
||||
break;
|
||||
case (char)0xC4:
|
||||
switch (p_v) {
|
||||
case (char)0x85: //ą (0xC4,0x85) -> Ą (0xC4,0x84)
|
||||
case (char)0x87: //ć (0xC4,0x87) -> Ć (0xC4,0x86)
|
||||
case (char)0x99: //ę (0xC4,0x99) -> Ę (0xC4,0x98)
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case (char)0xC5:
|
||||
switch (p_v) {
|
||||
case (char)0x82: //ł (0xC5,0x82) -> Ł (0xC5,0x81)
|
||||
case (char)0x84: //ń (0xC5,0x84) -> Ń (0xC5,0x83)
|
||||
case (char)0x9B: //ś (0xC5,0x9B) -> Ś (0xC5,0x9A)
|
||||
case (char)0xBA: //ź (0xC5,0xBA) -> Ź (0xC5,0xB9)
|
||||
case (char)0xBC: //ż (0xC5,0xBC) -> Ż (0xC5,0xBB)
|
||||
*p -= 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*c = toupper(*c); // works on Latin1 letters
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// replace char in char string
|
||||
void Helpers::replace_char(char * str, char find, char replace) {
|
||||
if (str == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
while (str[i] != '\0') {
|
||||
// Replace the matched character...
|
||||
if (str[i] == find)
|
||||
str[i] = replace;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// count number of items in a list
|
||||
// the end of a list has a nullptr
|
||||
uint8_t Helpers::count_items(const char * const * list) {
|
||||
uint8_t list_size = 0;
|
||||
if (list != nullptr) {
|
||||
while (list[list_size]) {
|
||||
list_size++;
|
||||
}
|
||||
}
|
||||
return list_size;
|
||||
}
|
||||
|
||||
// count number of items in a list of lists
|
||||
// the end of a list has a nullptr
|
||||
uint8_t Helpers::count_items(const char * const ** list) {
|
||||
uint8_t list_size = 0;
|
||||
if (list != nullptr) {
|
||||
while (list[list_size]) {
|
||||
list_size++;
|
||||
}
|
||||
}
|
||||
return list_size;
|
||||
}
|
||||
|
||||
// returns char pointer to translated description or fullname
|
||||
// if force_en is true always take the EN non-translated word
|
||||
const char * Helpers::translated_word(const char * const * strings, const bool force_en) {
|
||||
uint8_t language_index = EMSESP::system_.language_index();
|
||||
uint8_t index = 0; // default en
|
||||
|
||||
if (!strings) {
|
||||
return ""; // no translations
|
||||
}
|
||||
|
||||
// see how many translations we have for this entity. if there is no translation for this, revert to EN
|
||||
if (!force_en && (Helpers::count_items(strings) >= language_index + 1 && strlen(strings[language_index]))) {
|
||||
index = language_index;
|
||||
}
|
||||
|
||||
return strings[index];
|
||||
}
|
||||
|
||||
uint16_t Helpers::string2minutes(const std::string & str) {
|
||||
uint8_t i = 0;
|
||||
uint16_t res = 0;
|
||||
uint16_t tmp = 0;
|
||||
uint8_t state = 0;
|
||||
|
||||
while (str[i] != '\0') {
|
||||
// If we got a digit
|
||||
if (str[i] >= '0' && str[i] <= '9') {
|
||||
tmp = tmp * 10 + str[i] - '0';
|
||||
}
|
||||
// Or if we got a colon
|
||||
else if (str[i] == ':') {
|
||||
// If we were reading the hours
|
||||
if (state == 0) {
|
||||
res = 60 * tmp;
|
||||
}
|
||||
// Or if we were reading the minutes
|
||||
else if (state == 1) {
|
||||
if (tmp > 60) {
|
||||
return 0;
|
||||
}
|
||||
res += tmp;
|
||||
}
|
||||
// Or we got an extra colon
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
state++;
|
||||
tmp = 0;
|
||||
}
|
||||
|
||||
// Or we got something wrong
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (state == 1 && tmp < 60) {
|
||||
return res + tmp;
|
||||
} else if (state == 0) { // without : it's only minutes
|
||||
return tmp;
|
||||
} else {
|
||||
return 0; // Or if we were not, something is wrong in the given string
|
||||
}
|
||||
}
|
||||
|
||||
float Helpers::numericoperator2scalefactor(int8_t numeric_operator) {
|
||||
if (numeric_operator == 0)
|
||||
return 1.0f;
|
||||
else if (numeric_operator > 0)
|
||||
return 1.0f / numeric_operator;
|
||||
else
|
||||
return -numeric_operator;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
91
src/core/helpers.h
Normal file
91
src/core/helpers.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_HELPERS_H
|
||||
#define EMSESP_HELPERS_H
|
||||
|
||||
#include "telegram.h" // for EMS_VALUE_* settings
|
||||
#include "common.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Helpers {
|
||||
public:
|
||||
static char * render_value(char * result, const double 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_boolean(char * result, const bool value, const bool dashboard = false);
|
||||
|
||||
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(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 int atoint(const char * value);
|
||||
static bool check_abs(const int32_t i);
|
||||
static uint32_t abs(const int32_t i);
|
||||
static uint16_t string2minutes(const std::string & str);
|
||||
static float numericoperator2scalefactor(int8_t numeric_operator);
|
||||
|
||||
static double transformNumFloat(double value, const int8_t numeric_operator, const uint8_t fahrenheit = 0);
|
||||
|
||||
static std::string toLower(std::string const & s);
|
||||
static std::string toUpper(std::string const & s);
|
||||
static std::string toLower(const char * s);
|
||||
static void CharToUpperUTF8(char * c);
|
||||
|
||||
static void replace_char(char * str, char find, char replace);
|
||||
|
||||
static bool hasValue(const uint8_t & value, const uint8_t isBool = 0);
|
||||
static bool hasValue(const int8_t & value);
|
||||
static bool hasValue(const int16_t & value);
|
||||
static bool hasValue(const uint16_t & value);
|
||||
static bool hasValue(const uint32_t & value);
|
||||
static bool hasValue(const char * value);
|
||||
|
||||
static bool value2number(const char * value, int & value_i, const int min = -2147483648, const int max = 2147483647);
|
||||
static bool value2float(const char * value, float & value_f);
|
||||
static bool value2bool(const char * value, bool & value_b);
|
||||
static bool value2string(const char * value, std::string & value_s);
|
||||
static bool value2enum(const char * value, uint8_t & value_ui, const char * const ** strs);
|
||||
static bool value2enum(const char * value, uint8_t & value_ui, const char * const * strs);
|
||||
static bool value2temperature(const char * value, float & value_f, bool relative = false);
|
||||
static bool value2temperature(const char * value, int & value_i, const bool relative = false, const int min = -2147483648, const int max = 2147483647);
|
||||
|
||||
static uint8_t count_items(const char * const ** list);
|
||||
static uint8_t count_items(const char * const * list);
|
||||
|
||||
static const char * translated_word(const char * const * strings, const bool force_en = false);
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
static char * ultostr(char * ptr, uint32_t value, const uint8_t base);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
396
src/core/locale_common.h
Normal file
396
src/core/locale_common.h
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
|
||||
// clang-format off
|
||||
|
||||
/*
|
||||
* THIS FILE CONTAINS STANDARD STRING LITERALS THAT DON'T NEED LANGUAGE TRANSLATIONS
|
||||
*/
|
||||
|
||||
// common words
|
||||
MAKE_WORD(exit)
|
||||
MAKE_WORD(help)
|
||||
MAKE_WORD(log)
|
||||
MAKE_WORD(set)
|
||||
MAKE_WORD(show)
|
||||
MAKE_WORD(su)
|
||||
MAKE_WORD(scan)
|
||||
MAKE_WORD(password)
|
||||
MAKE_WORD(read)
|
||||
MAKE_WORD(values)
|
||||
MAKE_WORD(system)
|
||||
MAKE_WORD(fetch)
|
||||
MAKE_WORD(restart)
|
||||
MAKE_WORD(format)
|
||||
MAKE_WORD(raw)
|
||||
MAKE_WORD(watch)
|
||||
MAKE_WORD(syslog)
|
||||
MAKE_WORD(send)
|
||||
MAKE_WORD(telegram)
|
||||
MAKE_WORD(bus_id)
|
||||
MAKE_WORD(tx_mode)
|
||||
MAKE_WORD(showertimer)
|
||||
MAKE_WORD(showeralert)
|
||||
MAKE_WORD(ems)
|
||||
MAKE_WORD(devices)
|
||||
MAKE_WORD(shower)
|
||||
MAKE_WORD(mqtt)
|
||||
MAKE_WORD(modbus)
|
||||
MAKE_WORD(emsesp)
|
||||
MAKE_WORD(connected)
|
||||
MAKE_WORD(disconnected)
|
||||
MAKE_WORD(passwd)
|
||||
MAKE_WORD(hostname)
|
||||
MAKE_WORD(wifi)
|
||||
MAKE_WORD(reconnect)
|
||||
MAKE_WORD(ssid)
|
||||
MAKE_WORD(heartbeat)
|
||||
MAKE_WORD(users)
|
||||
MAKE_WORD(publish)
|
||||
MAKE_WORD(board_profile)
|
||||
MAKE_WORD(setvalue)
|
||||
MAKE_WORD(service)
|
||||
MAKE_WORD(message)
|
||||
|
||||
// for commands
|
||||
MAKE_WORD(call)
|
||||
MAKE_WORD(cmd)
|
||||
MAKE_WORD(id)
|
||||
MAKE_WORD(hc)
|
||||
MAKE_WORD(dhw)
|
||||
MAKE_WORD(device)
|
||||
MAKE_WORD(data)
|
||||
MAKE_WORD(command)
|
||||
MAKE_WORD(commands)
|
||||
MAKE_WORD(info)
|
||||
MAKE_WORD(settings)
|
||||
MAKE_WORD(value)
|
||||
MAKE_WORD(entities)
|
||||
MAKE_WORD(coldshot)
|
||||
|
||||
// device types - lowercase, used in MQTT
|
||||
MAKE_WORD(boiler)
|
||||
MAKE_WORD(thermostat)
|
||||
MAKE_WORD_CUSTOM(switcher, "switch")
|
||||
MAKE_WORD(solar)
|
||||
MAKE_WORD(mixer)
|
||||
MAKE_WORD(gateway)
|
||||
MAKE_WORD(controller)
|
||||
MAKE_WORD(connect)
|
||||
MAKE_WORD(heatpump)
|
||||
MAKE_WORD(generic)
|
||||
MAKE_WORD(analogsensor)
|
||||
MAKE_WORD(temperaturesensor)
|
||||
MAKE_WORD(alert)
|
||||
MAKE_WORD(pump)
|
||||
MAKE_WORD(extension)
|
||||
MAKE_WORD(heatsource)
|
||||
MAKE_WORD(scheduler)
|
||||
MAKE_WORD(custom)
|
||||
MAKE_WORD(ventilation)
|
||||
MAKE_WORD(water)
|
||||
MAKE_WORD(pool)
|
||||
|
||||
// brands
|
||||
MAKE_WORD_CUSTOM(bosch, "Bosch")
|
||||
MAKE_WORD_CUSTOM(junkers, "Junkers")
|
||||
MAKE_WORD_CUSTOM(buderus, "Buderus")
|
||||
MAKE_WORD_CUSTOM(nefit, "Nefit")
|
||||
MAKE_WORD_CUSTOM(sieger, "Sieger")
|
||||
MAKE_WORD_CUSTOM(worcester, "Worcester")
|
||||
MAKE_WORD_CUSTOM(ivt, "IVT")
|
||||
MAKE_WORD_CUSTOM(no_brand, "")
|
||||
|
||||
// types
|
||||
MAKE_WORD_CUSTOM(number, "number")
|
||||
MAKE_WORD_CUSTOM(enum, "enum")
|
||||
MAKE_WORD_CUSTOM(text, "text")
|
||||
|
||||
// Console
|
||||
MAKE_WORD_CUSTOM(EMSESP, "EMS-ESP")
|
||||
MAKE_WORD_CUSTOM(host_fmt, "Host: %s")
|
||||
MAKE_WORD_CUSTOM(port_fmt, "Port: %d")
|
||||
MAKE_WORD_CUSTOM(hostname_fmt, "Hostname: %s")
|
||||
MAKE_WORD_CUSTOM(mark_interval_fmt, "Mark interval: %lus")
|
||||
MAKE_WORD_CUSTOM(wifi_ssid_fmt, "WiFi SSID: %s")
|
||||
MAKE_WORD_CUSTOM(wifi_password_fmt, "WiFi Password: %S")
|
||||
MAKE_WORD_CUSTOM(tx_mode_fmt, "Tx mode: %d")
|
||||
MAKE_WORD_CUSTOM(bus_id_fmt, "Bus ID: %02X")
|
||||
MAKE_WORD_CUSTOM(log_level_fmt, "Log level: %s")
|
||||
MAKE_WORD_CUSTOM(cmd_optional, "[cmd]")
|
||||
MAKE_WORD_CUSTOM(ha_optional, "[ha]")
|
||||
MAKE_WORD_CUSTOM(deep_optional, "[deep]")
|
||||
MAKE_WORD_CUSTOM(watchid_optional, "[ID]")
|
||||
MAKE_WORD_CUSTOM(watch_format_optional, "[off | on | raw | unknown]")
|
||||
MAKE_WORD_CUSTOM(invalid_watch, "Invalid watch type")
|
||||
MAKE_WORD_CUSTOM(data_mandatory, "\"XX XX ...\"")
|
||||
MAKE_WORD_CUSTOM(asterisks, "********")
|
||||
MAKE_WORD_CUSTOM(n_mandatory, "<n>")
|
||||
MAKE_WORD_CUSTOM(sensorid_optional, "[sensor ID]")
|
||||
MAKE_WORD_CUSTOM(id_optional, "[id|hc]")
|
||||
MAKE_WORD_CUSTOM(partitionname_optional, "[partitionname]")
|
||||
MAKE_WORD_CUSTOM(data_optional, "[data]")
|
||||
MAKE_WORD_CUSTOM(offset_optional, "[offset]")
|
||||
MAKE_WORD_CUSTOM(length_optional, "[length]")
|
||||
MAKE_WORD_CUSTOM(typeid_mandatory, "<type ID>")
|
||||
MAKE_WORD_CUSTOM(deviceid_mandatory, "<deviceID>")
|
||||
MAKE_WORD_CUSTOM(device_type_optional, "[device]")
|
||||
MAKE_WORD_CUSTOM(invalid_log_level, "Invalid log level")
|
||||
MAKE_WORD_CUSTOM(log_level_optional, "[level]")
|
||||
MAKE_WORD_CUSTOM(show_commands, "[system | users | devices | log | ems | values | mqtt | commands")
|
||||
MAKE_WORD_CUSTOM(name_mandatory, "<name>")
|
||||
MAKE_WORD_CUSTOM(name_optional, "[name]")
|
||||
MAKE_WORD_CUSTOM(new_password_prompt1, "Enter new password: ")
|
||||
MAKE_WORD_CUSTOM(new_password_prompt2, "Retype new password: ")
|
||||
MAKE_WORD_CUSTOM(password_prompt, "Password: ")
|
||||
MAKE_WORD_CUSTOM(unset, "<unset>")
|
||||
MAKE_WORD_CUSTOM(enable_mandatory, "<enable | disable>")
|
||||
MAKE_WORD_CUSTOM(service_mandatory, "<ap | mqtt | ntp>")
|
||||
|
||||
// more common names that don't need translations
|
||||
MAKE_NOTRANSLATION(1x3min, "1x3min")
|
||||
MAKE_NOTRANSLATION(2x3min, "2x3min")
|
||||
MAKE_NOTRANSLATION(3x3min, "3x3min")
|
||||
MAKE_NOTRANSLATION(4x3min, "4x3min")
|
||||
MAKE_NOTRANSLATION(5x3min, "5x3min")
|
||||
MAKE_NOTRANSLATION(6x3min, "6x3min")
|
||||
MAKE_NOTRANSLATION(auto, "auto")
|
||||
MAKE_NOTRANSLATION(rc3x, "RC3x")
|
||||
MAKE_NOTRANSLATION(rc20, "RC20")
|
||||
MAKE_NOTRANSLATION(fb10, "FB10")
|
||||
MAKE_NOTRANSLATION(fb100, "FB100")
|
||||
MAKE_NOTRANSLATION(rc310, "RC310")
|
||||
MAKE_NOTRANSLATION(rc200, "RC200")
|
||||
MAKE_NOTRANSLATION(rc100, "RC100")
|
||||
MAKE_NOTRANSLATION(rc100h, "RC100H")
|
||||
MAKE_NOTRANSLATION(tc100, "TC100")
|
||||
MAKE_NOTRANSLATION(rc120rf, "RC120RF")
|
||||
MAKE_NOTRANSLATION(rc220, "RC220/RT800")
|
||||
MAKE_NOTRANSLATION(single, "single")
|
||||
MAKE_NOTRANSLATION(dash, "-")
|
||||
MAKE_NOTRANSLATION(BLANK, "")
|
||||
MAKE_NOTRANSLATION(pwm, "pwm")
|
||||
MAKE_NOTRANSLATION(pwm_invers, "pwm inverse")
|
||||
MAKE_NOTRANSLATION(mpc, "mpc")
|
||||
MAKE_NOTRANSLATION(tempauto, "temp auto")
|
||||
MAKE_NOTRANSLATION(bypass, "bypass")
|
||||
MAKE_NOTRANSLATION(mixer, "mixer")
|
||||
MAKE_NOTRANSLATION(monovalent, "monovalent")
|
||||
MAKE_NOTRANSLATION(bivalent, "bivalent")
|
||||
MAKE_NOTRANSLATION(n_o, "n_o")
|
||||
MAKE_NOTRANSLATION(n_c, "n_c")
|
||||
MAKE_NOTRANSLATION(prog1, "prog 1")
|
||||
MAKE_NOTRANSLATION(prog2, "prog 2")
|
||||
MAKE_NOTRANSLATION(proga, "prog a")
|
||||
MAKE_NOTRANSLATION(progb, "prog b")
|
||||
MAKE_NOTRANSLATION(progc, "prog c")
|
||||
MAKE_NOTRANSLATION(progd, "prog d")
|
||||
MAKE_NOTRANSLATION(proge, "prog e")
|
||||
MAKE_NOTRANSLATION(progf, "prog f")
|
||||
MAKE_NOTRANSLATION(rc35, "RC35")
|
||||
MAKE_NOTRANSLATION(0kW, "0 kW")
|
||||
MAKE_NOTRANSLATION(2kW, "2 kW")
|
||||
MAKE_NOTRANSLATION(3kW, "3 kW")
|
||||
MAKE_NOTRANSLATION(4kW, "4 kW")
|
||||
MAKE_NOTRANSLATION(6kW, "6 kW")
|
||||
MAKE_NOTRANSLATION(9kW, "9 kW")
|
||||
MAKE_NOTRANSLATION(L1, "L1")
|
||||
MAKE_NOTRANSLATION(L2, "L2")
|
||||
MAKE_NOTRANSLATION(L3, "L3")
|
||||
MAKE_NOTRANSLATION(L4, "L4")
|
||||
|
||||
// templates - this are not translated and will be saved under options_single
|
||||
MAKE_NOTRANSLATION(tpl_datetime, "NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1)")
|
||||
MAKE_NOTRANSLATION(tpl_switchtime, "<nn> [ not_set | day hh:mm on|off ]")
|
||||
MAKE_NOTRANSLATION(tpl_switchtime1, "<nn> [ not_set | day hh:mm Tn ]")
|
||||
MAKE_NOTRANSLATION(tpl_holidays, "dd.mm.yyyy-dd.mm.yyyy")
|
||||
MAKE_NOTRANSLATION(tpl_date, "dd.mm.yyyy")
|
||||
MAKE_NOTRANSLATION(tpl_input, "<inv>[<evu1><evu2><evu3><comp><aux><cool><heat><dhw><pv>]")
|
||||
MAKE_NOTRANSLATION(tpl_input4, "<inv>[<comp><aux><cool><heat><dhw><pv>]")
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
MAKE_NOTRANSLATION(test_cmd, "run a test")
|
||||
#endif
|
||||
|
||||
// TAG mapping - maps to DeviceValueTAG_s in emsdevicevalue.cpp
|
||||
// use empty string if want to suppress showing tags
|
||||
// mqtt tags must not have spaces
|
||||
MAKE_NOTRANSLATION(tag_none, "")
|
||||
MAKE_NOTRANSLATION(tag_heartbeat, "")
|
||||
MAKE_NOTRANSLATION(tag_device_data, "")
|
||||
|
||||
// Unit Of Measurement mapping - maps to DeviceValueUOM_s in emsdevice.cpp
|
||||
// Translating hours/minute/seconds are done in emsdevice.cpp (uom_to_string())
|
||||
MAKE_WORD_CUSTOM(uom_blank, " ")
|
||||
MAKE_WORD_CUSTOM(uom_percent, "%")
|
||||
MAKE_WORD_CUSTOM(uom_degrees, "°C")
|
||||
MAKE_WORD_CUSTOM(uom_kwh, "kWh")
|
||||
MAKE_WORD_CUSTOM(uom_wh, "Wh")
|
||||
MAKE_WORD_CUSTOM(uom_bar, "bar")
|
||||
MAKE_WORD_CUSTOM(uom_ua, "µA")
|
||||
MAKE_WORD_CUSTOM(uom_lmin, "l/min")
|
||||
MAKE_WORD_CUSTOM(uom_kw, "kW")
|
||||
MAKE_WORD_CUSTOM(uom_w, "W")
|
||||
MAKE_WORD_CUSTOM(uom_kb, "KB")
|
||||
MAKE_WORD_CUSTOM(uom_dbm, "dBm")
|
||||
MAKE_WORD_CUSTOM(uom_fahrenheit, "°F")
|
||||
MAKE_WORD_CUSTOM(uom_mv, "mV")
|
||||
MAKE_WORD_CUSTOM(uom_sqm, "m²")
|
||||
MAKE_WORD_CUSTOM(uom_m3, "m³")
|
||||
MAKE_WORD_CUSTOM(uom_l, "l")
|
||||
MAKE_WORD_CUSTOM(uom_kmin, "K*min")
|
||||
MAKE_WORD_CUSTOM(uom_k, "K")
|
||||
MAKE_WORD_CUSTOM(uom_volts, "V")
|
||||
MAKE_WORD_CUSTOM(uom_mbar, "mbar")
|
||||
MAKE_WORD_CUSTOM(uom_lh, "l/h")
|
||||
MAKE_WORD_CUSTOM(uom_ctkwh, "ct/kWh")
|
||||
|
||||
// MQTT topics and prefixes
|
||||
MAKE_WORD_CUSTOM(heating_active, "heating_active")
|
||||
MAKE_WORD_CUSTOM(tapwater_active, "tapwater_active")
|
||||
MAKE_WORD_CUSTOM(response, "response")
|
||||
|
||||
// syslog
|
||||
MAKE_ENUM_FIXED(list_syslog_level, "off", "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", "trace", "all")
|
||||
|
||||
// sensors
|
||||
MAKE_ENUM_FIXED(counter, "counter")
|
||||
MAKE_ENUM_FIXED(digital_out, "digital_out")
|
||||
MAKE_ENUM_FIXED(list_sensortype, "disabled", "digital in", "counter", "adc", "timer", "rate", "digital out", "pwm 0", "pwm 1", "pwm 2")
|
||||
|
||||
// watch
|
||||
MAKE_ENUM_FIXED(list_watch, "off", "on", "raw", "unknown")
|
||||
|
||||
/*
|
||||
* The rest below are Enums and generated from translations lists
|
||||
*/
|
||||
MAKE_ENUM(enum_cylprio, FL_(cyl1), FL_(cyl2))
|
||||
MAKE_ENUM(enum_progMode, FL_(prog1), FL_(prog2))
|
||||
MAKE_ENUM(enum_progMode4, FL_(proga), FL_(progb), FL_(progc), FL_(progd), FL_(proge), FL_(progf))
|
||||
MAKE_ENUM(enum_climate, FL_(seltemp), FL_(roomtemp))
|
||||
MAKE_ENUM(enum_charge, FL_(chargepump), FL_(3wayvalve))
|
||||
MAKE_ENUM(enum_freq, FL_(off), FL_(1x3min), FL_(2x3min), FL_(3x3min), FL_(4x3min), FL_(5x3min), FL_(6x3min), FL_(continuous))
|
||||
MAKE_ENUM(enum_off_time_date_manual, FL_(off), FL_(time), FL_(date), FL_(manual))
|
||||
MAKE_ENUM(enum_comfort, FL_(hot), FL_(eco), FL_(intelligent))
|
||||
MAKE_ENUM(enum_comfort1, FL_(high_comfort), FL_(eco))
|
||||
MAKE_ENUM(enum_comfort2, FL_(eco), FL_(high_comfort))
|
||||
MAKE_ENUM(enum_flow, FL_(off), FL_(flow), FL_(bufferedflow), FL_(buffer), FL_(layeredbuffer))
|
||||
MAKE_ENUM(enum_reset, FL_(dash), FL_(maintenance), FL_(error), FL_(history), FL_(message))
|
||||
MAKE_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
|
||||
MAKE_ENUM(enum_pumpMode, FL_(proportional), FL_(deltaP1), FL_(deltaP2), FL_(deltaP3), FL_(deltaP4))
|
||||
MAKE_ENUM(enum_pumpCharacter, FL_(proportional), FL_(pressure1), FL_(pressure2), FL_(pressure3), FL_(pressure4), FL_(pressure5), FL_(pressure6))
|
||||
MAKE_ENUM(enum_hpPumpMode, FL_(auto), FL_(continuous))
|
||||
|
||||
// thermostat lists
|
||||
MAKE_ENUM(enum_ibaMainDisplay, FL_(internal_temperature), FL_(internal_setpoint), FL_(external_temperature), FL_(burner_temperature), FL_(ww_temperature), FL_(functioning_mode), FL_(time), FL_(date), FL_(smoke_temperature))
|
||||
MAKE_ENUM(enum_ibaLanguage, FL_(german), FL_(dutch), FL_(french), FL_(italian))
|
||||
MAKE_ENUM(enum_ibaLanguage_RC30, FL_(german), FL_(dutch))
|
||||
MAKE_ENUM(enum_floordrystatus, FL_(off), FL_(start), FL_(heat), FL_(hold), FL_(cool), FL_(end))
|
||||
MAKE_ENUM(enum_ibaBuildingType, FL_(light), FL_(medium), FL_(heavy))
|
||||
MAKE_ENUM(enum_PID, FL_(fast), FL_(medium), FL_(slow))
|
||||
MAKE_ENUM(enum_heatup, FL_(slow), FL_(medium), FL_(fast))
|
||||
MAKE_ENUM(enum_wwMode, FL_(off), FL_(normal), FL_(comfort), FL_(auto), FL_(own_prog))
|
||||
MAKE_ENUM(enum_wwCircMode, FL_(off), FL_(on), FL_(auto), FL_(own_prog))
|
||||
MAKE_ENUM(enum_wwMode2, FL_(off), FL_(on), FL_(auto))
|
||||
MAKE_ENUM(enum_wwMode3, FL_(on), FL_(off), FL_(auto))
|
||||
MAKE_ENUM(enum_wwMode4, FL_(off), FL_(ecoplus), FL_(eco), FL_(comfort), FL_(auto))
|
||||
MAKE_ENUM(enum_wwMode5, FL_(normal), FL_(comfort), FL_(ecoplus)) // Rego3000
|
||||
MAKE_ENUM(enum_wwMode6, FL_(off), FL_(comfort), FL_(auto)) // CR120
|
||||
MAKE_ENUM(enum_heatingtype, FL_(off), FL_(radiator), FL_(convector), FL_(floor))
|
||||
MAKE_ENUM(enum_heatingtype1, FL_(off), FL_(curve), FL_(radiator), FL_(convector), FL_(floor))
|
||||
MAKE_ENUM(enum_heatingtype2, FL_(off), FL_(radiator), FL_(convector), FL_(floor), FL_(roomflow), FL_(roomload))
|
||||
MAKE_ENUM(enum_summermode, FL_(summer), FL_(auto), FL_(winter))
|
||||
MAKE_ENUM(enum_hpoperatingmode, FL_(off), FL_(auto), FL_(heating), FL_(cooling))
|
||||
MAKE_ENUM(enum_summer, FL_(winter), FL_(summer))
|
||||
MAKE_ENUM(enum_operatingstate, FL_(heating), FL_(off), FL_(cooling))
|
||||
MAKE_ENUM(enum_hpmode, FL_(heating), FL_(cooling), FL_(heatandcool))
|
||||
|
||||
MAKE_ENUM(enum_mode, FL_(manual), FL_(auto)) // RC100, RC300, RC310
|
||||
MAKE_ENUM(enum_mode2, FL_(off), FL_(manual), FL_(auto)) // RC20, RC30, BC400
|
||||
MAKE_ENUM(enum_mode3, FL_(night), FL_(day), FL_(auto)) // RC35, RC30_N, RC25, RC20_N
|
||||
MAKE_ENUM(enum_mode4, FL_(nofrost), FL_(eco), FL_(heat), FL_(auto)) // JUNKERS
|
||||
MAKE_ENUM(enum_mode5, FL_(auto), FL_(off)) // CRF
|
||||
MAKE_ENUM(enum_mode6, FL_(nofrost), FL_(night), FL_(day)) // RC10
|
||||
MAKE_ENUM(enum_mode_ha, FL_(off), FL_(heat), FL_(auto)) // HA climate
|
||||
|
||||
MAKE_ENUM(enum_modetype, FL_(eco), FL_(comfort))
|
||||
MAKE_ENUM(enum_modetype3, FL_(night), FL_(day))
|
||||
MAKE_ENUM(enum_modetype4, FL_(nofrost), FL_(eco), FL_(heat))
|
||||
MAKE_ENUM(enum_modetype5, FL_(off), FL_(on))
|
||||
|
||||
MAKE_ENUM(enum_reducemode, FL_(nofrost), FL_(reduce), FL_(room), FL_(outdoor))
|
||||
MAKE_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 values: 1-3
|
||||
MAKE_ENUM(enum_nofrostmode, FL_(off), FL_(outdoor), FL_(room))
|
||||
MAKE_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor))
|
||||
|
||||
MAKE_ENUM(enum_controlmode, FL_(optimized), FL_(simple), FL_(na), FL_(room), FL_(power))
|
||||
MAKE_ENUM(enum_controlmode1, FL_(weather_compensated), FL_(outside_basepoint), FL_(na), FL_(room), FL_(power), FL_(constant)) // RC310 1-4
|
||||
MAKE_ENUM(enum_controlmode2, FL_(outdoor), FL_(room))
|
||||
MAKE_ENUM(enum_controlmode3, FL_(off), FL_(unmixed), FL_(unmixedIPM), FL_(mixed))
|
||||
MAKE_ENUM(enum_control, FL_(off), FL_(rc20), FL_(rc3x))
|
||||
MAKE_ENUM(enum_j_control, FL_(off), FL_(fb10), FL_(fb100))
|
||||
MAKE_ENUM(enum_roomsensor, FL_(extern), FL_(intern), FL_(auto))
|
||||
MAKE_ENUM(enum_roominfluence, FL_(off), FL_(intern), FL_(extern), FL_(auto))
|
||||
MAKE_ENUM(enum_control1, FL_(rc310), FL_(rc200), FL_(rc100), FL_(rc100h), FL_(tc100))
|
||||
MAKE_ENUM(enum_control2, FL_(off), FL_(dash), FL_(rc100), FL_(rc100h), FL_(dash), FL_(rc120rf), FL_(rc220), FL_(single)) // BC400
|
||||
|
||||
MAKE_ENUM(enum_switchmode, FL_(off), FL_(eco), FL_(comfort), FL_(heat))
|
||||
MAKE_ENUM(enum_switchProgMode, FL_(level), FL_(absolute))
|
||||
|
||||
MAKE_ENUM(enum_dayOfWeek, FL_(day_mo), FL_(day_tu), FL_(day_we), FL_(day_th), FL_(day_fr), FL_(day_sa), FL_(day_su), FL_(all))
|
||||
MAKE_ENUM(enum_progMode2, FL_(own_1), FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors), FL_(new), FL_(own_2))
|
||||
MAKE_ENUM(enum_progMode3, FL_(family), FL_(morning), FL_(evening), FL_(am), FL_(pm), FL_(midday), FL_(singles), FL_(seniors))
|
||||
MAKE_ENUM(enum_hybridStrategy, FL_(co2_optimized), FL_(cost_optimized), FL_(outside_temp_switched), FL_(co2_cost_mix))
|
||||
MAKE_ENUM(enum_hybridStrategy1, FL_(cost_optimized), FL_(co2_optimized), FL_(outside_temp_alt), FL_(outside_temp_par), FL_(hp_preferred), FL_(boiler_only))
|
||||
MAKE_ENUM(enum_lowNoiseMode, FL_(off), FL_(reduced_output), FL_(switchoff), FL_(perm))
|
||||
|
||||
// heat pump
|
||||
MAKE_ENUM(enum_hpactivity, FL_(off), FL_(heating), FL_(cooling), FL_(hot_water), FL_(pool), FL_(pool_heating), FL_(defrost), FL_(compressor_alarm)) // BV name COMPRESSOR_E21_STATUS
|
||||
MAKE_ENUM(enum_silentMode, FL_(off), FL_(auto), FL_(on))
|
||||
MAKE_ENUM(enum_4way, FL_(cool_defrost), FL_(heat_ww))
|
||||
|
||||
// solar
|
||||
MAKE_ENUM(enum_solarmode, FL_(constant), FL_(pwm), FL_(analog))
|
||||
MAKE_ENUM(enum_collectortype, FL_(flat), FL_(vacuum))
|
||||
MAKE_ENUM(enum_wwStatus2, FL_(BLANK), FL_(BLANK), FL_(BLANK), FL_(no_heat), FL_(BLANK), FL_(BLANK), FL_(heatrequest), FL_(BLANK), FL_(disinfecting), FL_(hold))
|
||||
|
||||
// mixer
|
||||
MAKE_ENUM(enum_shunt, FL_(stopped), FL_(opening), FL_(closing), FL_(open), FL_(close))
|
||||
MAKE_ENUM(enum_wwProgMode, FL_(std_prog), FL_(own_prog))
|
||||
|
||||
// AM200 lists
|
||||
MAKE_ENUM(enum_vr2Config, FL_(off), FL_(bypass))
|
||||
MAKE_ENUM(enum_aPumpSignal, FL_(off), FL_(pwm), FL_(pwm_invers))
|
||||
MAKE_ENUM(enum_bufBypass, FL_(no), FL_(mixer), FL_(valve))
|
||||
MAKE_ENUM(enum_blockMode, FL_(off), FL_(auto), FL_(blocking))
|
||||
MAKE_ENUM(enum_bufConfig, FL_(off), FL_(monovalent), FL_(bivalent))
|
||||
MAKE_ENUM(enum_blockTerm, FL_(n_o), FL_(n_c))
|
||||
|
||||
// Ventilation
|
||||
MAKE_ENUM(enum_ventMode, FL_(auto), FL_(off), FL_(L1), FL_(L2), FL_(L3), FL_(L4), FL_(demand), FL_(sleep), FL_(intense), FL_(bypass), FL_(partymode), FL_(fireplace))
|
||||
|
||||
// water
|
||||
MAKE_ENUM(enum_errorDisp, FL_(off), FL_(normal), FL_(inverted))
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// clang-format on
|
||||
941
src/core/locale_translations.h
Normal file
941
src/core/locale_translations.h
Normal file
@@ -0,0 +1,941 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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
|
||||
|
||||
// clang-format off
|
||||
|
||||
// Define language codes here and add to languages[] in system.cpp
|
||||
#define EMSESP_LOCALE_EN "en"
|
||||
#define EMSESP_LOCALE_DE "de"
|
||||
#define EMSESP_LOCALE_NL "nl"
|
||||
#define EMSESP_LOCALE_SV "sv"
|
||||
#define EMSESP_LOCALE_PL "pl"
|
||||
#define EMSESP_LOCALE_NO "no"
|
||||
#define EMSESP_LOCALE_FR "fr"
|
||||
#define EMSESP_LOCALE_TR "tr"
|
||||
#define EMSESP_LOCALE_IT "it"
|
||||
#define EMSESP_LOCALE_SK "sk"
|
||||
#define EMSESP_LOCALE_CZ "cz"
|
||||
|
||||
// IMPORTANT! translations are in the order:,en, de, nl, sv, pl, no, fr, tr, it, sk, cz
|
||||
//
|
||||
// if there is no translation, it will default to en
|
||||
//
|
||||
// device types, as display in Web and Console
|
||||
MAKE_WORD_TRANSLATION(boiler_hp_device, "Boiler/HP", "Kessel/WP", "CV ketel/WP", "Värmepanna/VP", "Kocioł/PC", "Varmekjele/VP", "", "Kazan/IP", "Caldaia/PC", "Kotol/TČ", "Kotel/TČ") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(boiler_device, "Boiler", "Kessel", "CV ketel", "Värmepanna", "Kocioł", "Varmekjele", "", "Kazan", "Caldaia", "Kotol", "Kotel") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(thermostat_device, "Thermostat", "Thermostat", "Thermostaat", "Termostat", "Termostat", "Termostat", "", "Termostat", "Termostato", "Termostat", "Termostat") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(heatpump_device, "Heat Pump", "Wärmepumpe", "Warmtepomp", "Värmepump", "Pompa ciepła", "Varmepumpe", "", "Isı Pompası", "Pompa di Calore", "Tepelné čerpadlo", "Tepelné čerpadlo") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(solar_device, "Solar Module", "Solarmodul", "Solar Module", "Solmodul", "Moduł solarny", "Solmodul", "", "Güneş Enerjisi Cihazı", "Modulo Solare", "Solárny modul", "Solární modul") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(connect_device, "Connect Module", "Verbindungsmodul", "Connect Module", "Uppkopplingsmodul", "Moduł przyłączeń", "Sammenkoblingsmodul", "", "Güneş Enerjisi Cihazı", "Modulo connessione", "Pripojte modul", "Modul připojení") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(mixer_device, "Mixer Module", "Mischermodul", "Mixer Module", "Blandarmodul", "Moduł mieszacza", "Miksermodul", "", "Karışım Cihazı", "Modulo Miscela", "Modul mixera", "Směšovací modul") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(controller_device, "Controller Module", "Regelmodul", "Controller Module", "Styrmodul", "Moduł sterujący", "Styremodul", "", "Kontrol Ünitesi", "Modulo Controllo", "Modul ovládača", "Řídicí modul") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(switch_device, "Switch Module", "Schaltmodul", "Switch Module", "Relämodul", "Moduł przełączający", "Switch modul", "", "Anahtar", "Modulo Switch", "Spínací modul", "Spínací modul") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(gateway_device, "Gateway Module", "Gateway-Modul", "Gateway Module", "Gateway", "Moduł IP", "Gateway", "", "Ağ Geçidi", "Modulo Gateway", "Modul brány", "Modul brány") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(alert_device, "Alert Module", "Alarmmodul", "Alert Module", "Larmmodul", "Moduł alarmowy", "Alarmmodul", "", "Alarm Cihazı", "Module Avviso", "Modul upozornení", "Modul upozornění") // TODO translate
|
||||
//MAKE_WORD_TRANSLATION(pump_device, "Pump Module", "Pumpenmodul", "Pump Module", "Pumpmodul", "Moduł pompy", "Pumpemodul", "", "Pompa", "Module Pompa", "Modul čerpadla", "Modul čerpadla") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(extension_device, "Extension Module", "Erweiterungsnmodul", "Module", "Utökningsmodul", "Moduł rozszerzeń", "Modul", "", "", "Module", "Rozširujúci modul", "Rozšiřující modul") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(heatsource_device, "Heatsource", "Wärmequelle", "Heatsource", "Värmekälla", "Źródło ciepła", "Varmekilde", "", "Isı Kaynağı", "Fonte di calore", "Tepelný zdroj", "Zdroj tepla") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(sensors_device, "Sensors", "Sensoren", "Sensoren", "Sensorer", "Czujniki", "Sensorer", "Capteurs", "Sensör Cihazı", "Sensori", "Snímače", "Senzory")
|
||||
MAKE_WORD_TRANSLATION(unknown_device, "Unknown", "Unbekannt", "Onbekend", "Okänt", "Nieznane urządzenie", "Ukjent", "Inconnu", "Bilinmeyen", "Sconosciuto", "Neznámy", "Neznámé")
|
||||
MAKE_WORD_TRANSLATION(custom_device, "Custom", "Nutzerdefiniert", "Aangepast", "Anpassad", "Niestandardowe", "", "", "Özel", "Personalizzato", "Vlastné", "Vlastní") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(custom_device_name, "Custom Entities", "Nutzer deklarierte Entitäten", "Gebruiker gedefineerd", "Egendefinierade entiteter", "Encje zdefiniowane przez użytkownika", "", "", "Kullanıcı tarafından tanımlanmış varlıklar", "Entità definita da utente", "Používateľom definované entity", "Vlastní entity") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(ventilation_device, "Ventilation", "Belüftung", "Ventilatie", "Ventilation", "Wentylacja", "", "", "Havalandırma", "Ventilazione", "Vetranie", "Větrání") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(water_device, "Water Module", "Wassermodul", "", "Vattenmodul", "Moduł wodny", "", "", "", "", "Modul vody", "Modul vody") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(pool_device, "Pool Module", "Poolmodul", "", "Poolmodul", "Moduł basenu", "", "", "", "", "Modul bazéna", "Modul bazénu") // TODO translate
|
||||
|
||||
// commands
|
||||
MAKE_WORD_TRANSLATION(info_cmd, "list all values (verbose)", "Liste aller Werte", "lijst van alle waardes", "lista alla värden", "wyświetl wszystkie wartości", "Viser alle verdier", "", "Tüm değerleri listele", "elenca tutti i valori", "zobraziť všetky hodnoty", "vypsat všechny hodnoty (podrobně)") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(commands_cmd, "list all commands", "Liste aller Kommandos", "lijst van alle commando's", "lista alla kommandon", "wyświetl wszystkie komendy", "Viser alle kommandoer", "", "Tüm komutları listele", "elencaa tutti i comandi", "zobraziť všetky príkazy", "vypsat všechny příkazy") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(entities_cmd, "list all entities", "Liste aller Entitäten", "lijst van alle entiteiten", "lista all entiteter", "wyświetl wszsytkie encje", "Viser alle enheter", "", "Tüm varlıkları listele", "elenca tutte le entità", "zobraziť všetky entity", "vypsat všechny entity") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(send_cmd, "send a telegram", "Sende EMS-Telegramm", "stuur een telegram", "skicka ett telegram", "wyślij telegram", "send et telegram", "", "Bir telegram gönder", "invia un telegramma", "poslať telegram", "odeslat telegram") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(read_cmd, "send read request", "", "", "skicka en läsförfrågan", "", "", "", "", "", "odoslať žiadosť o prečítanie", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(setiovalue_cmd, "set I/O value", "Setze Werte E/A", "instellen standaardwaarde", "sätt ett I/O-värde", "ustaw wartość", "sett en io verdi", "", "Giriş/Çıkış değerlerini ayarla", "imposta valore io", "nastaviť hodnotu io", "nastavit hodnotu I/O") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(changeloglevel_cmd, "change log level", "Ändere Protokollebene", "aanpassen log niveau", "ändra logg-nivå", "zmień poziom log-u", "endre loggnivå", "", "Kayıt seviyesini değiştir", "cambia livello registrazione", "zmeniť úroveň protokolu", "změnit úroveň protokolování") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(fetch_cmd, "refresh all EMS values", "Aktualisiere alle EMS-Werte", "Verversen alle EMS waardes", "uppdatera alla EMS-värden", "odśwież wszystkie wartości EMS", "oppfrisk alle EMS verdier", "", "Bütün EMS değerlerini yenile", "aggiornare tutti i valori EMS", "obnoviť všetky hodnoty EMS", "aktualizovat všechny EMS hodnoty") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(restart_cmd, "restart EMS-ESP", "Neustart", "opnieuw opstarten", "starta om EMS-ESP", "uruchom ponownie EMS-ESP", "restart EMS-ESP", "redémarrer EMS-ESP", "EMS-ESPyi yeniden başlat", "riavvia EMS-ESP", "reštart EMS-ESP", "restartovat EMS-ESP") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(format_cmd, "factory reset EMS-ESP", "EMS-ESP auf Werkseinstellungen zurücksetzen", "fabriksåterställ EMS-ESP", "", "", "", "", "", "", "továrenske nastavenie EMS-ESP", "tovární nastavení EMS-ESP") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(watch_cmd, "watch incoming telegrams", "Beobachte eingehende Telegramme", "inkomende telegrammen bekijken", "visa inkommande telegram", "obserwuj przyczodzące telegramy", "se innkommende telegrammer", "", "Gelen telegramları", "guardare i telegrammi in arrivo", "sledovať prichádzajúce telegramy", "sledovat příchozí telegramy") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(publish_cmd, "publish all to MQTT", "Publiziere MQTT", "publiceer alles naar MQTT", "publicera allt till MQTT", "opublikuj wszystko na MQTT", "Publiser alt til MQTT", "", "Hepsini MQTTye gönder", "pubblica tutto su MQTT", "zverejniť všetko na MQTT", "publikovat vše do MQTT") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(system_info_cmd, "show system info", "Zeige Systeminformationen", "toon systeemstatus", "visa systeminformation", "pokaż status systemu", "vis system status", "", "Sistem Durumunu Göster", "visualizza stati di sistema", "zobraziť stav systému", "zobrazit informace o systému") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(schedule_cmd, "enable schedule item", "Aktiviere Zeitplanelemente", "activeer tijdschema item", "aktivera schemalagt objekt", "aktywuj wybrany harmonogram", "", "", "program öğesini etkinleştir", "abilitare l'elemento programmato", "povoliť položku plánovania", "povolit položku plánování") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(entity_cmd, "set custom value on ems", "Sende eigene Entitäten zu EMS", "verstuur custom waarde naar EMS", "sätt ett eget värde i EMS", "wyślij własną wartość na EMS", "", "", "emp üzerinde özel değer ayarla", "imposta valori personalizzati su EMS", "nastaviť vlastnú hodnotu na ems", "nastavit vlastní hodnotu na ems") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(commands_response, "get response", "Hole Antwort", "Verzoek om antwoord", "hämta svar", "uzyskaj odpowiedź", "", "", "gelen cevap", "", "získať odpoveď", "získat odpověď") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(coldshot_cmd, "send a cold shot of water", "Zugabe einer Menge kalten Wassers", "", "sckicka en liten mängd kallvatten", "uruchom tryśnięcie zimnej wody", "", "", "soğuk su gönder", "", "pošlite studenú dávku vody", "poslat studenou vodu") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(message_cmd, "send a message", "Eine Nachricht senden", "", "skicka ett meddelande", "", "", "", "", "", "poslať správu", "odeslat zprávu") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(values_cmd, "list all values", "Liste alle Werte auf", "", "lista alla värden", "", "", "", "", "", "vypísať všetky hodnoty", "vypsat všechny hodnoty") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(system_cmd, "system setting", "System Einstellung", "", "systeminställning", "", "", "", "", "", "vypísať všetky hodnoty", "vypsat všechny hodnoty") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(showertimer_cmd, "enable shower timer", "aktiviere Duschzeitmessung", "", "aktivera duschtimer", "", "", "", "", "", "povoliť časovač sprchovania", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(showeralert_cmd, "enable shower alert", "aktiviere Duschzeitwarnung", "", "aktivera duschvarning", "", "", "", "", "", "povoliť upozornenie na sprchu", "") // TODO translate
|
||||
|
||||
// tags
|
||||
MAKE_WORD_TRANSLATION(tag_hc1, "hc1", "HK1", "hc1", "VK1", "OG1", "hc1", "hc1", "ID1", "hc1", "hc1", "hc1")
|
||||
MAKE_WORD_TRANSLATION(tag_hc2, "hc2", "HK2", "hc2", "VK2", "OG2", "hc2", "hc2", "ID2", "hc2", "hc2", "hc2")
|
||||
MAKE_WORD_TRANSLATION(tag_hc3, "hc3", "HK3", "hc3", "VK3", "OG3", "hc3", "hc3", "ID3", "hc3", "hc3", "hc3")
|
||||
MAKE_WORD_TRANSLATION(tag_hc4, "hc4", "HK4", "hc4", "VK4", "OG4", "hc4", "hc4", "ID4", "hc4", "hc4", "hc4")
|
||||
MAKE_WORD_TRANSLATION(tag_hc5, "hc5", "HK5", "hc5", "VK5", "OG5", "hc5", "hc5", "ID5", "hc5", "hc5", "hc5")
|
||||
MAKE_WORD_TRANSLATION(tag_hc6, "hc6", "HK6", "hc6", "VK6", "OG6", "hc6", "hc6", "ID6", "hc6", "hc6", "hc6")
|
||||
MAKE_WORD_TRANSLATION(tag_hc7, "hc7", "HK7", "hc7", "VK7", "OG7", "hc7", "hc7", "ID7", "hc7", "hc7", "hc7")
|
||||
MAKE_WORD_TRANSLATION(tag_hc8, "hc8", "HK8", "hc8", "VK8", "OG8", "hc8", "hc8", "ID8", "hc8", "hc8", "hc8")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw1, "dhw", "WWK", "dhw", "VVK", "CWU", "dhw", "ecs", "SKS", "dhw", "TÚV", "TUV")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw2, "dhw2", "WWK2", "dhw2", "VVK2", "CWU2", "dhw2", "ecs2", "SKS2", "dhw2", "TÚV2", "TUV2")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw3, "dhw3", "WWK3", "dhw3", "VVK3", "CWU3", "dhw3", "ecs3", "SKS3", "dhw3", "TÚV3", "TUV3")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw4, "dhw4", "WWK4", "dhw4", "VVK4", "CWU4", "dhw4", "ecs4", "SKS4", "dhw4", "TÚV4", "TUV4")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw5, "dhw5", "WWK5", "dhw5", "VVK5", "CWU5", "dhw5", "ecs5", "SKS5", "dhw5", "TÚV5", "TUV5")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw6, "dhw6", "WWK6", "dhw6", "VVK6", "CWU6", "dhw6", "ecs6", "SKS6", "dhw6", "TÚV6", "TUV6")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw7, "dhw7", "WWK7", "dhw7", "VVK7", "CWU7", "dhw7", "ecs7", "SKS7", "dhw7", "TÚV7", "TUV7")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw8, "dhw8", "WWK8", "dhw8", "VVK8", "CWU8", "dhw8", "ecs8", "SKS8", "dhw8", "TÚV8", "TUV8")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw9, "dhw9", "WWK9", "dhw9", "VVK9", "CWU9", "dhw9", "ecs9", "SKS9", "dhw9", "TÚV9", "TUV9")
|
||||
MAKE_WORD_TRANSLATION(tag_dhw10, "dhw10", "WWK10", "dhw10", "VVK10", "CWU10", "dhw10", "ecs10", "SKS10", "dhw10", "TÚV10", "TUV10")
|
||||
|
||||
MAKE_WORD_TRANSLATION(tag_ahs1, "ahs1", "AHQ1", "ahs1", "AVK1", "AŹC1", "ahs1", "ahs1", "ahs1", "ahs1", "ahs1", "ahs1")
|
||||
MAKE_WORD_TRANSLATION(tag_hs1, "hs1", "hs1", "hs1", "VK1", "ŹC1", "hs1", "hs1", "hs1", "hs1", "hs1", "hs1")
|
||||
MAKE_WORD_TRANSLATION(tag_hs2, "hs2", "hs2", "hs2", "VK2", "ŹC2", "hs2", "hs2", "hs2", "hs2", "hs2", "hs2")
|
||||
MAKE_WORD_TRANSLATION(tag_hs3, "hs3", "hs3", "hs3", "VK3", "ŹC3", "hs3", "hs3", "hs3", "hs3", "hs3", "hs3")
|
||||
MAKE_WORD_TRANSLATION(tag_hs4, "hs4", "hs4", "hs4", "VK4", "ŹC4", "hs4", "hs4", "hs4", "hs4", "hs4", "hs4")
|
||||
MAKE_WORD_TRANSLATION(tag_hs5, "hs5", "hs5", "hs5", "VK5", "ŹC5", "hs5", "hs5", "hs5", "hs5", "hs5", "hs5")
|
||||
MAKE_WORD_TRANSLATION(tag_hs6, "hs6", "hs6", "hs6", "VK6", "ŹC6", "hs6", "hs6", "hs6", "hs6", "hs6", "hs6")
|
||||
MAKE_WORD_TRANSLATION(tag_hs7, "hs7", "hs7", "hs7", "VK7", "ŹC7", "hs7", "hs7", "hs7", "hs7", "hs7", "hs7")
|
||||
MAKE_WORD_TRANSLATION(tag_hs8, "hs8", "hs8", "hs8", "VK8", "ŹC8", "hs8", "hs8", "hs8", "hs8", "hs8", "hs8")
|
||||
MAKE_WORD_TRANSLATION(tag_hs9, "hs9", "hs9", "hs9", "VK9", "ŹC9", "hs9", "hs9", "hs9", "hs9", "hs9", "hs9")
|
||||
MAKE_WORD_TRANSLATION(tag_hs10, "hs10", "hs10", "hs10", "VK10", "ŹC10", "hs10", "hs10", "hs10", "hs10", "hs10", "hs10")
|
||||
MAKE_WORD_TRANSLATION(tag_hs11, "hs11", "hs11", "hs11", "VK11", "ŹC11", "hs11", "hs11", "hs11", "hs11", "hs11", "hs11")
|
||||
MAKE_WORD_TRANSLATION(tag_hs12, "hs12", "hs12", "hs12", "VK12", "ŹC12", "hs12", "hs12", "hs12", "hs12", "hs12", "hs12")
|
||||
MAKE_WORD_TRANSLATION(tag_hs13, "hs13", "hs13", "hs13", "VK13", "ŹC13", "hs13", "hs13", "hs13", "hs13", "hs13", "hs13")
|
||||
MAKE_WORD_TRANSLATION(tag_hs14, "hs14", "hs14", "hs14", "VK14", "ŹC14", "hs14", "hs14", "hs14", "hs14", "hs14", "hs14")
|
||||
MAKE_WORD_TRANSLATION(tag_hs15, "hs15", "hs15", "hs15", "VK15", "ŹC15", "hs15", "hs15", "hs15", "hs15", "hs15", "hs15")
|
||||
MAKE_WORD_TRANSLATION(tag_hs16, "hs16", "hs16", "hs16", "VK16", "ŹC16", "hs16", "hs16", "hs16", "hs16", "hs16", "hs16")
|
||||
|
||||
MAKE_WORD_TRANSLATION(tag_hcx, "hc<n>", "HK<n>", "hc<n>", "VK<n>", "OG<n>", "hc<n>", "hc<n>", "ID<n>", "hc<n>", "hc<n>", "hc<n>")
|
||||
MAKE_WORD_TRANSLATION(tag_dhwx, "dhw[n]", "WWK[n]", "dhw[n]", "VVK[n]", "CWU[n]", "dhw[n]", "ecs[n]", "SKS[n]", "dhw[n]", "TÚV[n]", "TUV[n]")
|
||||
MAKE_WORD_TRANSLATION(tag_ahsx, "ahs<n>", "AHQ<n>", "ahs<n>", "AVK<n>", "AŹC<n>", "ahs<n>", "ahs<n>", "ahs<n>", "ahs<n>", "ahs<n>", "ahs<n>")
|
||||
MAKE_WORD_TRANSLATION(tag_hsx, "hs<n>", "HQ<n>", "hs<n>", "VK<n>", "ŹC<n>", "hs<n>", "hs<n>", "hs<n>", "hs<n>", "hs<n>", "hs<n>")
|
||||
|
||||
// General
|
||||
MAKE_WORD_TRANSLATION(on, "on", "an", "aan", "på", "włączono", "på", "on", "açık", "on", "zap", "zap")
|
||||
MAKE_WORD_TRANSLATION(off, "off", "aus", "uit", "av", "wyłączono", "av", "off", "kapalı", "off", "vyp", "vyp")
|
||||
MAKE_WORD_TRANSLATION(ON, "ON", "AN", "AAN", "PÅ", "wł.", "PÅ", "ON", "AÇIK", "ON", "ZAP", "ZAP")
|
||||
MAKE_WORD_TRANSLATION(OFF, "OFF", "AUS", "UIT", "AV", "wył.", "AV", "OFF", "KAPALI", "OFF", "VYP", "VYP")
|
||||
|
||||
// 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_WORD_TRANSLATION(seconds, "seconds", "Sekunden", "Seconden", "Sekunder", "sekund", "Sekunder", "secondes", "saniye", "Secondi", "sekundy", "sekundy")
|
||||
MAKE_WORD_TRANSLATION(minutes, "minutes", "Minuten", "Minuten", "Minuter", "minut", "Minutter", "minutes", "dakika", "Minuti", "minúty", "minuty")
|
||||
MAKE_WORD_TRANSLATION(hours, "hours", "Stunden", "Uren", "Timmar", "godzin", "Timer", "heures", "saat", "ore", "hodiny", "hodiny")
|
||||
MAKE_WORD_TRANSLATION(days, "days", "Tage", "Dagen", "Dagar", "dni", "Dager", "jours", "gün", "giorni", "dni", "dny")
|
||||
|
||||
// Enum translations
|
||||
// general
|
||||
MAKE_WORD_TRANSLATION(day_mo, "mo", "Mo", "Mo", "Må", "poniedziałek", "ma", "lun", "Pzt", "lu", "po", "po")
|
||||
MAKE_WORD_TRANSLATION(day_tu, "tu", "Di", "Di", "Ti", "wtorek", "ti", "mar", "Sal", "ma", "ut", "út")
|
||||
MAKE_WORD_TRANSLATION(day_we, "we", "Mi", "Wo", "On", "środa", "on", "mer", "Çar", "me", "st", "st")
|
||||
MAKE_WORD_TRANSLATION(day_th, "th", "Do", "Do", "To", "czwartek", "to", "jeu", "Per", "gio", "št", "čt")
|
||||
MAKE_WORD_TRANSLATION(day_fr, "fr", "Fr", "Vr", "Fr", "piątek", "fr", "ven", "Cum", "ve", "pi", "pá")
|
||||
MAKE_WORD_TRANSLATION(day_sa, "sa", "Sa", "Za", "Lö", "sobota", "lø", "sam", "Cts", "sa", "so", "so")
|
||||
MAKE_WORD_TRANSLATION(day_su, "su", "So", "Zo", "Sö", "niedziela", "sø", "dim", "Paz", "do", "ne", "ne")
|
||||
MAKE_WORD_TRANSLATION(all, "all", "Alle", "Alle", "Alla", "codziennie", "alle", "tous", "tüm", "tutti", "všetko", "vše")
|
||||
MAKE_WORD_TRANSLATION(own_1, "own 1", "Eigen 1", "Eigen 1", "Egen 1", "własny 1", "egen 1", "propre 1", "kendi 1", "proprio 1", "vlastné 1", "vlastní 1")
|
||||
MAKE_WORD_TRANSLATION(family, "family", "Familie", "Familie", "Familj", "rodzina", "familie", "famille", "aile", "famiglia", "rodina", "rodina")
|
||||
MAKE_WORD_TRANSLATION(morning, "morning", "Morgens", "'s ochtends", "Morgon", "zmiana 1", "morgen", "matin", "sabah", "mattina", "ráno", "ráno")
|
||||
MAKE_WORD_TRANSLATION(evening, "evening", "Abends", "'s avonds", "Kväll", "zmiana 2", "kveld", "soir", "akşam", "sera", "večer", "večer")
|
||||
MAKE_WORD_TRANSLATION(seniors, "seniors", "Senioren", "senioren", "Seniorer", "senior", "seniorer", "séniors", "yaşlılar", "vecchi", "seniori", "senioři")
|
||||
MAKE_WORD_TRANSLATION(no, "no", "nein", "nee", "nej", "nie", "nei", "non", "hayır", "no", "nie", "ne")
|
||||
MAKE_WORD_TRANSLATION(new, "new", "Neu", "Nieuw", "Ny", "nowy", "ny", "nouveau", "yeni", "nuovo", "nové", "nový")
|
||||
MAKE_WORD_TRANSLATION(own_2, "own 2", "Eigen 2", "Eigen 2", "Egen 2", "własny 2", "egen 2", "propre 2", "kendi 2", "proprio 2", "vlastné 2", "vlastní 2")
|
||||
MAKE_WORD_TRANSLATION(singles, "singles", "Einzeln", "singles", "Singlar", "osoba samotna", "single", "seuls", "tekliler", "singoli", "slobodní", "singl")
|
||||
MAKE_WORD_TRANSLATION(am, "am", "Vormittag", "ochtend", "Förmiddag", "do południa", "formiddag", "matin", "sabah", "mattina", "doobeda", "dopoledne")
|
||||
MAKE_WORD_TRANSLATION(pm, "pm", "Nachmittag", "namiddag", "Eftermiddag", "po południu", "ettermiddag", "après-midi", "akşam", "pomeriggio", "poobede", "odpoledne")
|
||||
MAKE_WORD_TRANSLATION(midday, "midday", "Mittag", "middag", "Middag", "południe", "middag", "midi", "öğlen", "mezzogiorno", "poludnie", "poledne")
|
||||
MAKE_WORD_TRANSLATION(unknown, "unknown", "Unbekannt", "onbekend", "Okänt", "nieznany", "ukjent", "inconnu", "bilinmeyen", "sconosciuto", "neznáme", "neznámé")
|
||||
MAKE_WORD_TRANSLATION(flat, "flat", "flach", "vlak", "Platt", "płaski", "flat", "plat", "düz", "piatto", "plochý", "plochý")
|
||||
MAKE_WORD_TRANSLATION(vacuum, "vacuum", "Vakuum", "vacuum", "Vakuum", "próżnia", "vakum", "vide", "vakum", "vacuum", "vákuum", "vakuum")
|
||||
MAKE_WORD_TRANSLATION(co2_optimized, "co2 optimized", "CO2 optimiert", "CO2 geoptimaliseerd", "CO2-optimerad", "optymalizacja CO2", "co2 optimalisert", "optimisé en CO2", "CO2 verimli", "CO2 ottimizzato", "co2 optimalizované", "optimalizace CO2")
|
||||
MAKE_WORD_TRANSLATION(cost_optimized, "cost optimized", "kostenoptimiert", "kosten geoptimaliseerd", "kostnadsoptimerad", "optymalizacja kosztów", "kostnadsoptimalisert", "optimisé en coût", "maliyet odaklı", "costo ottimizzato", "nákladovo optimalizované", "optimalizace nákladů")
|
||||
MAKE_WORD_TRANSLATION(outside_temp_switched, "outside temp switched", "Außentemp. gesteuert", "buitentemp. gestuurd", "Utomhustemp korrigerad", "temperatura zewn. przeł.", "utetemp optimalisert", "contrôle par temp. ext.", "dış hava sıcaklığına bağlı", "temperatura esterna cambiata", "prepnuta vonkajsia teplota", "venkovní teplota přepnuta")
|
||||
MAKE_WORD_TRANSLATION(co2_cost_mix, "co2 cost mix", "CO2-Kostenmix", "CO2-kostenmix", "CO2-Kostnadsmix", "mieszany koszt CO2", "", "coût mixte CO2", "karışık maliyet", "co2 cost mix", "co2 náklady mix", "mix nákladů CO2") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(analog, "analog", "analog", "analoog", "analog", "analogowy", "analog", "analogique", "analog", "analogico", "analógový", "analogový")
|
||||
MAKE_WORD_TRANSLATION(normal, "normal", "normal", "normaal", "normal", "normalny", "normal", "normal", "normal", "normale", "normálny", "normální")
|
||||
MAKE_WORD_TRANSLATION(blocking, "blocking", "Blockierung", "blokkering", "Blockering", "blokowanie", "blokkering", "bloquant", "engelleme", "bloccaggio", "blokovanie", "blokování")
|
||||
MAKE_WORD_TRANSLATION(extern, "extern", "extern", "extern", "extern", "zewnętrzny", "ekstern", "externe", "dış", "eesterno", "externý", "externí")
|
||||
MAKE_WORD_TRANSLATION(intern, "intern", "intern", "intern", "intern", "wewnętrzny", "intern", "interne", "iç", "interno", "interný", "interní")
|
||||
MAKE_WORD_TRANSLATION(lower, "lower", "niedriger", "lager", "lägre", "mniejszy", "nedre", "inférieur", "daha düşük", "basso", "nízky", "nízký")
|
||||
MAKE_WORD_TRANSLATION(error, "error", "Fehler", "error", "Fel", "błąd", "feil", "erreur", "Hata", "errore", "error", "chyba")
|
||||
MAKE_WORD_TRANSLATION(history, "history", "Fehlerspeicher", "", "historik", "historia", "", "", "", "", "história", "historie") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(message, "message", "Meldung", "melding", "meddelande", "komunikat", "melding", "message", "mesajı", "messaggio", "správa", "zpráva")
|
||||
MAKE_WORD_TRANSLATION(na, "n/a", "n/a", "n/a", "n/a", "nd.", "n/a", "n/c", "mevcut değil", "n/a", "n/a", "n/a")
|
||||
MAKE_WORD_TRANSLATION(inverted, "inverted", "invertiert", "", "inverterad", "odwrócony", "", "", "", "", "invertovaný", "invertovaný") // TODO translate
|
||||
|
||||
// boiler
|
||||
MAKE_WORD_TRANSLATION(time, "time", "Zeit", "tijd", "Tid", "godzina", "tid", "heure", "zaman", "ora", "čas", "čas")
|
||||
MAKE_WORD_TRANSLATION(date, "date", "Datum", "datum", "Datum", "data", "dato", "date", "tarih", "data", "dátum", "datum")
|
||||
MAKE_WORD_TRANSLATION(continuous, "continuous", "kontinuierlich", "continue", "kontinuerlig", "ciągły", "kontinuerlig", "continu", "devam eden", "continuo", "nepretržité", "nepretržitě")
|
||||
MAKE_WORD_TRANSLATION(3wayvalve, "3-way valve", "3-Wege-Ventil", "3-weg klep", "trevägsventil", "zawór 3-drogowy", "treveisventil", "vanne 3 voies", "3 yollu vana", "valvola 3 vie", "3-cestný ventil", "3-cestný ventil")
|
||||
MAKE_WORD_TRANSLATION(chargepump, "chargepump", "Ladepumpe", "laadpomp", "laddpump", "pompa ładująca", "ladepumpe", "pompe de charge", "besleme pompası", "pompa di carica", "nabíjacie čerpadlo", "nabíjecí čerpadlo")
|
||||
MAKE_WORD_TRANSLATION(hot, "hot", "Heiß", "heet", "Het", "gorący", "het", "chaud", "sıcak", "caldo", "horúci", "horký")
|
||||
MAKE_WORD_TRANSLATION(high_comfort, "high comfort", "gehobener Komfort", "verhoogd comfort", "Förhöjd komfort", "wysoki komfort", "høy komfort", "comfort", "komfor", "comfort alto", "vysoký komfort", "vysoký komfort")
|
||||
MAKE_WORD_TRANSLATION(eco, "eco", "Eco", "Eco", "Eko", "eko", "øko", "éco", "eko", "Eco", "eko", "eko")
|
||||
MAKE_WORD_TRANSLATION(ecoplus, "eco+", "Eco+", "Eco+", "Eko+", "eko+", "øko+", "éco+", "eko+", "Eco+", "eko+")
|
||||
MAKE_WORD_TRANSLATION(intelligent, "intelligent", "Intelligent", "intelligent", "Intelligent", "inteligentny", "intelligent", "intelligent", "akıllı", "intelligente", "inteligentný", "inteligentní")
|
||||
MAKE_WORD_TRANSLATION(flow, "flow", "Durchfluss", "volumestroom", "Flöde", "przepływ", "strømme", "débit", "akım", "flusso", "tok", "průtok")
|
||||
MAKE_WORD_TRANSLATION(manual, "manual", "Manuell", "handmatig", "Manuell", "ręczny", "manuell", "manuel", "manuel", "manuale", "manuálny", "manuální")
|
||||
MAKE_WORD_TRANSLATION(buffer, "buffer", "Speicher", "buffer", "Buffert", "bufor", "buffer", "buffer", "tampon", "Buffer", "zásobník", "zásobník")
|
||||
MAKE_WORD_TRANSLATION(bufferedflow, "buffered flow", "Durchlaufspeicher", "doorstroombuffer", "Buffertflöde", "przepływ buforowany", "bufret strømning", "", "tampon akım", "memoria flusso", "zásobníkový prietok", "zásobníkový průtok") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(layeredbuffer, "layered buffer", "Schichtspeicher", "gelaagde buffer", "Lagrad buffert", "bufor warstwowy", "lagdelt buffer", "", "katmanlı akım", "strato memoria", "vrstvený zásobník", "vrstvený zásobník") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(maintenance, "maintenance", "Wartung", "onderhoud", "Underhåll", "przegląd", "vedlikehold", "maintenance", "bakım", "servizio", "údržba", "údržba")
|
||||
MAKE_WORD_TRANSLATION(heating, "heating", "Heizen", "verwarmen", "Uppvärmning", "ogrzewanie", "oppvarming", "chauffage", "ısıtma", "riscaldamento", "kúrenie", "topení")
|
||||
MAKE_WORD_TRANSLATION(cooling, "cooling", "Kühlen", "koelen", "Kyler", "chłodzenie", "kjøling", "refroidissement", "soğuma", "raffreddamento", "chladenie", "chlazení")
|
||||
MAKE_WORD_TRANSLATION(heatandcool, "heating & cooling", "Heizen & Kühlen", "verwarmen & koelen", "Uppvärmning & Kyla", "ogrzewanie i chłodzenie", "", "", "ısıtma & soğutma", "", "kúrenie a chladenie", "topení a chlazení") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(disinfecting, "disinfecting", "Desinfizieren", "desinfecteren", "Desinficerar", "dezynfekcja termiczna", "desinfisering", "désinfection", "dezenfeksiyon", "disinfezione", "dezinfekcia", "dezinfekce")
|
||||
MAKE_WORD_TRANSLATION(no_heat, "no heat", "keine Wärme", "geen warmte", "Ingen värme", "brak ciepła", "ingen varme", "pas de chauffage", "ısınma yok", "nessun calore", "žiadne teplo", "bez tepla")
|
||||
MAKE_WORD_TRANSLATION(heatrequest, "heat request", "Wärmeanforderung", "verwarmingsverzoek", "Värmeförfrågan", "zapotrzebowanie na ciepło", "varmeforespørsel", "demande de chauffage", "ısınma ihtiyacı", "richiesta calore", "požiadavka na teplo", "požadavek na teplo")
|
||||
MAKE_WORD_TRANSLATION(valve, "valve", "Ventil", "klep", "Ventil", "zawór", "ventil", "valve", "vana", "valvola", "ventil", "ventil")
|
||||
MAKE_WORD_TRANSLATION(proportional, "proportional", "proportional", "proportioneel", "proportionell", "proporcjonalny", "proposjonal", "", "oransal", "proporzionale", "proporcionálne", "proporcionální") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(deltaP1, "deltaP-1", "deltaP-1", "deltaP-1", "deltaP-1", "delta P-1", "deltaP-1", "", "deltaP-1", "deltaP-1", "deltaP-1", "deltaP-1") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(deltaP2, "deltaP-2", "deltaP-2", "deltaP-2", "deltaP-2", "delta P-2", "deltaP-2", "", "deltaP-2", "deltaP-2", "deltaP-2", "deltaP-2") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(deltaP3, "deltaP-3", "deltaP-3", "deltaP-3", "deltaP-3", "delta P-3", "deltaP-3", "", "deltaP-3", "deltaP-3", "deltaP-3", "deltaP-3") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(deltaP4, "deltaP-4", "deltaP-4", "deltaP-4", "deltaP-4", "delta P-4", "deltaP-4", "", "deltaP-4", "deltaP-4", "deltaP-4", "deltaP-4") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(pressure1, "150mbar", "150mbar", "150mbar", "150mbar", "150mbar", "150mbar", "150mbar", "150mbar", "150mbar", "150mbar", "150mbar") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(pressure2, "200mbar", "200mbar", "200mbar", "200mbar", "200mbar", "200mbar", "200mbar", "200mbar", "200mbar", "200mbar", "200mbar") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(pressure3, "250mbar", "250mbar", "250mbar", "250mbar", "250mbar", "250mbar", "250mbar", "250mbar", "250mbar", "250mbar", "250mbar") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(pressure4, "300mbar", "300mbar", "300mbar", "300mbar", "300mbar", "300mbar", "300mbar", "300mbar", "300mbar", "300mbar", "300mbar") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(pressure5, "350mbar", "350mbar", "350mbar", "350mbar", "350mbar", "350mbar", "350mbar", "350mbar", "350mbar", "350mbar", "350mbar") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(pressure6, "400mbar", "400mbar", "400mbar", "400mbar", "400mbar", "400mbar", "400mbar", "400mbar", "400mbar", "400mbar", "400mbar") // TODO translate
|
||||
|
||||
// heatpump
|
||||
MAKE_WORD_TRANSLATION(none, "none", "keine", "geen", "ingen", "brak", "ingen", "aucun", "hiçbiri", "nessuno", "žiadny", "žádné")
|
||||
MAKE_WORD_TRANSLATION(hot_water, "hot water", "Warmwasser", "warm water", "varmvatten", "c.w.u.", "varmtvann", "eau chaude", "sıcak su", "acqua calda", "horúca voda", "horká voda")
|
||||
MAKE_WORD_TRANSLATION(pool, "pool", "Pool", "zwembad", "pool", "basen", "basseng", "piscine", "havuz", "piscina", "bazén", "bazén")
|
||||
MAKE_WORD_TRANSLATION(pool_heating, "pool heating", "Pool beheizen", "", "", "", "", "", "", "", "ohrev bazéna", "")
|
||||
MAKE_WORD_TRANSLATION(outside_temp_alt, "outside temperature alt.", "Außentemp. alternativ", "alternatieve buitentemperatuur", "Alternativ utomhustemp.", "temp. zewn. alternat.", "alternativ utendørstemp.", "température extérieure alternative", "alternatif dış sıcaklık", "temperatura esterna alternativa", "vonkajšia teplota altern.", "venkovní teplota alt.")
|
||||
MAKE_WORD_TRANSLATION(outside_temp_par, "outside temperature parallel", "Außentemp. parallel", "buitentemperatuur parallel", "Parallell utomhustemp.", "temp. zewn. równoległa", "parallell utendørstemp.", "température extérieure parallèle", "paralel dış sıcaklık", "temperatura esterna parallela", "paralelne s vonkajšou teplotou", "paralelně s venkovní teplotou")
|
||||
MAKE_WORD_TRANSLATION(hp_preferred, "heatpump preferred", "Wärmepumpe bevorzugt", "voorkeur warmtepomp", "Värmepump föredraget", "preferowana pompa ciepła", "varmepumpe prioritert", "pompe à chaleur préférée", "tercih edilen pompa", "pompa di calore preferita", "preferované tepelné čerpadlo", "preferované tepelné čerpadlo")
|
||||
MAKE_WORD_TRANSLATION(boiler_only, "boiler only", "nur Kessel", "uitsluitend cv ketel", "Värmepanna enbart", "tylko kocioł", "kun kjele", "chaudière uniquement", "sadece kazan", "solo caldaia", "len kotol", "pouze kotel")
|
||||
MAKE_WORD_TRANSLATION(reduced_output, "reduced output", "Reduzierte Leistung", "gereduceerde output", "Reducerad produktion", "zmniejszona wydajność", "redusert ytelse", "sortie réduite", "düşürülmüş çıkış", "riduzione uscita", "znížený výkon", "snížený výkon")
|
||||
MAKE_WORD_TRANSLATION(switchoff, "switch off hp", "WP ausschalten", "WP uitschakelen", "Värmepump avstängd", "wyłącz pompę ciepła", "slå av varmepumpe", "éteindre la PAC", "ısı pompasını kapat", "spegnimento pompa calore", "vypnúť tep. čerpadlo", "vypnout tepelné čerpadlo")
|
||||
MAKE_WORD_TRANSLATION(perm, "perm. reduced", "perm. reduziert", "permanent gereduceerd", "Permanent reducerad", "stale zmniejszona wydajność", "permanent redusert", "réduction permanente", "sürekli azaltılmış", "riduzione permanente", "trvalo znížené", "trvale sníženo")
|
||||
MAKE_WORD_TRANSLATION(heat_ww, "heating & dhw", "Heizen & Warmwasser", "", "Uppvärmning & Varmvatten", "ogrzewanie i c.w.u.", "", "", "", "", "kúrenie a TÚV", "topení a TUV")
|
||||
MAKE_WORD_TRANSLATION(cool_defrost, "cooling & defrost", "Kühlen & Abtauen", "", "Kyla & Avfrostning", "chłodzenie i odladzanie", "", "", "", "", "chladenie a rozmrazovanie", "chlazení a odmrazování")
|
||||
MAKE_WORD_TRANSLATION(compressor_alarm, "compressor alarm", "Alarm Kompressor", "", "", "", "", "", "", "", "alarm kompresora", "")
|
||||
|
||||
// thermostat
|
||||
MAKE_WORD_TRANSLATION(seltemp, "selTemp", "Solltemperatur", "doeltemperatuur", "Börtemperatur", "temperatura zadana", "innstilt temperatur", "consigne température", "ayarlanmış sıcaklık", "temperatura di consegna", "zadaná teplota", "zvolená teplota")
|
||||
MAKE_WORD_TRANSLATION(roomtemp, "roomTemp", "Raumtemperatur", "kamertemperatuur", "Rumstemperatur", "temperatura w pomieszczeniu", "romstemperatur", "température de la pièce", "oda sıcaklığı", "temperatura camera", "izbová teplota", "teplota místnosti")
|
||||
MAKE_WORD_TRANSLATION(own_prog, "own prog", "Eigenprog.", "eigen prog.", "Egen prog.", "program własny", "eget prog.", "programme propre", "isteğe göre ayarlanmış program", "proprio prog.", "vlastný prog.", "vlastní program")
|
||||
MAKE_WORD_TRANSLATION(std_prog, "std prog", "Standardprog.", "standaard prog.", "Standardprog.", "program standardowy", "standardprog.", "programme standard", "sandart pogram", "programma standard", "Štandar.prog.", "standardní program")
|
||||
MAKE_WORD_TRANSLATION(light, "light", "Leicht", "licht", "Lätt", "lekki", "lett", "léger", "düşük", "luce", "ľahký", "lehký")
|
||||
MAKE_WORD_TRANSLATION(medium, "medium", "Mittel", "middel", "Medel", "średni", "medium", "medium", "orta", "medio", "stredný", "střední")
|
||||
MAKE_WORD_TRANSLATION(heavy, "heavy", "Schwer", "zwaar", "Tung", "ciężki", "tung", "lourd", "yüksek", "pesante", "ťažký", "těžký")
|
||||
MAKE_WORD_TRANSLATION(start, "start", "Start", "start", "Start", "start", "start", "début", "başlat", "avvia", "štart", "start")
|
||||
MAKE_WORD_TRANSLATION(heat, "heat", "Heizen", "verwarmen", "Värme", "ciepło", "varmer", "chaleur", "ısıtma", "caldo", "kúrenie", "topení")
|
||||
MAKE_WORD_TRANSLATION(hold, "hold", "Halten", "pauzeren", "Paus", "pauza", "pause", "pause", "durdur", "pausa", "pauza", "pausa")
|
||||
MAKE_WORD_TRANSLATION(cool, "cool", "Kühlen", "koelen", "Kyla", "zimno", "kjøler", "froid", "soğutma", "freddo", "chladenie", "chladit")
|
||||
MAKE_WORD_TRANSLATION(end, "end", "Ende", "einde", "Slut", "koniec", "slutt", "fin", "bitti", "fine", "koniec", "konec")
|
||||
MAKE_WORD_TRANSLATION(german, "german", "Deutsch", "Duits", "Tyska", "niemiecki", "tysk", "allemand", "Almanca", "Tedesco", "nemecky", "němčina")
|
||||
MAKE_WORD_TRANSLATION(dutch, "dutch", "Niederländisch", "Nederlands", "Nederländska", "niderlandzki", "nederlandsk", "néerlandais", "Flemenkçe", "Olandese", "holandsky", "nizozemština")
|
||||
MAKE_WORD_TRANSLATION(french, "french", "Französisch", "Frans", "Franska", "francuski", "fransk", "français", "Fransızca", "Francese", "francúzsky", "francouzština")
|
||||
MAKE_WORD_TRANSLATION(italian, "italian", "Italienisch", "Italiaans", "Italienska", "włoski", "italiensk", "italien", "İtalyanca", "Italiano", "taliansky", "italština")
|
||||
MAKE_WORD_TRANSLATION(high, "high", "hoch", "hoog", "Hög", "wysoki", "høy", "haut", "yüksek", "alto", "vysoký", "vysoký")
|
||||
MAKE_WORD_TRANSLATION(low, "low", "niedrig", "laag", "Låg", "niski", "lav", "bas", "düşük", "basso", "nízky", "nízký")
|
||||
MAKE_WORD_TRANSLATION(curve, "heatingcurve", "Heizkurve", "", "värmekurva", "krzywa grzania", "", "", "", "", "vykurovacia krivka", "křivka topení") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(radiator, "radiator", "Heizkörper", "radiator", "Radiator", "grzejniki", "radiator", "radiateur", "radyatör", "radiatore", "radiátor", "radiátor")
|
||||
MAKE_WORD_TRANSLATION(convector, "convector", "Konvektor", "convector", "Konvektor", "konwektory", "konvektor", "convecteur", "convector", "convettore", "konvektor", "konvektor")
|
||||
MAKE_WORD_TRANSLATION(floor, "floor", "Fussboden", "vloer", "Golv", "podłoga", "gulv", "sol", "yer", "pavimento", "podlaha", "podlaha")
|
||||
MAKE_WORD_TRANSLATION(roomflow, "roomflow", "Raum Fluß", "", "Rumsflöde", "", "", "", "", "", "prúdenie miestnosti", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(roomload, "roomload", "Raum Bedarf", "", "Rumsbehov", "", "", "", "", "", "izbová zaťaž", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(summer, "summer", "Sommer", "zomer", "Sommar", "lato", "sommer", "été", "yaz", "estate", "leto", "léto")
|
||||
MAKE_WORD_TRANSLATION(winter, "winter", "Winter", "winter", "Vinter", "zima", "vinter", "hiver", "kış", "inverno", "zima", "zima")
|
||||
MAKE_WORD_TRANSLATION(outdoor, "outdoor", "Außen", "buiten", "Utomhus", "temp. zewnętrzna", "utendørs", "extérieur", "dış", "esterno", "vonku", "venkovní")
|
||||
MAKE_WORD_TRANSLATION(room, "room", "Raum", "kamer", "Rum", "temp. w pomieszczeniu", "", "pièce", "oda", "camera", "izba", "dle teploty prostoru") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(room_outdoor, "room outdoor", "Raum + Außen", "kamer + buiten", "Rum + Ute", "temp. w pom. i zewn.", "rom utendørs", "pièce extérieure", "oda ve dış", "camera esterna", "miestnosť vonku", "místnost venku")
|
||||
MAKE_WORD_TRANSLATION(power, "power", "Leistung", "vermogen", "Effekt", "moc", "effekt", "puissance", "güç", "potenza", "výkon", "výkon")
|
||||
MAKE_WORD_TRANSLATION(constant, "constant", "konstant", "constant", "Konstant", "stały", "konstant", "constant", "sabit", "costante", "konštantný", "konstantní")
|
||||
MAKE_WORD_TRANSLATION(simple, "simple", "einfach", "simpel", "enkel", "prosty", "enkel", "simple", "basit", "semplice", "jednoduchý", "jednoduchý")
|
||||
MAKE_WORD_TRANSLATION(optimized, "optimized", "optimiert", "geoptimaliseerd", "optimerad", "zoptymalizowany", "optimalisert", "optimisé", "optimize", "ottimizzato", "optimalizovaný", "optimalizovaný")
|
||||
MAKE_WORD_TRANSLATION(nofrost, "nofrost", "Frostschutz", "vorstbescherming", "Frostskydd", "ochrona przed zamarzaniem", "frostsikring", "protection gel", "Donma koruması", "protezione gelo", "bez námrazy", "bez mrazu")
|
||||
MAKE_WORD_TRANSLATION(defrost, "defrost", "Abtauen", "ontdooien", "avfrostning", "odladzanie", "tine", "dégivrage", "buz çözücü", "scongelamento", "odmrazenie", "odmrazování")
|
||||
MAKE_WORD_TRANSLATION(comfort, "comfort", "Komfort", "comfort", "Komfort", "komfort", "komfort", "comfort", "konfor", "comfort", "komfortný", "komfort")
|
||||
MAKE_WORD_TRANSLATION(night, "night", "Nacht", "nacht", "Natt", "noc", "natt", "nuit", "gece", "notte", "noc", "noc")
|
||||
MAKE_WORD_TRANSLATION(day, "day", "Tag", "dag", "Dag", "dzień", "dag", "jour", "gün", "giorno", "deň", "den")
|
||||
MAKE_WORD_TRANSLATION(holiday, "holiday", "Urlaub", "vakantie", "Helgdag", "urlop", "ferie", "vacances", "tatil", "vacanza", "dovolenka", "dovolená")
|
||||
MAKE_WORD_TRANSLATION(reduce, "reduce", "reduziert", "gereduceerd", "Reducera", "zredukowany", "redusere", "réduit", "düşür", "riduzione", "znížený", "snížený")
|
||||
MAKE_WORD_TRANSLATION(noreduce, "no reduce", "unreduziert", "niet gereduceerd", "oreducerad", "bez redukcji", "ingen reduksjon", "pas de réduction", "düşürme", "non ridurre", "bez zníženia", "nesnížený")
|
||||
MAKE_WORD_TRANSLATION(offset, "offset", "Anhebung", "offset", "Förskjutning", "przesunięcie", "kompensasjon", "offset", "kompansasyon", "offset", "ofset", "offset")
|
||||
MAKE_WORD_TRANSLATION(design, "design", "Auslegung", "ontwerp", "Design", "projekt", "design", "design", "tasarım", "disegno", "design", "design")
|
||||
MAKE_WORD_TRANSLATION(minflow, "min flow", "min. Durchfluss", "min. doorstroom", "Min. flöde", "minimalny przepływ", "min. strømming", "flux min.", "minimum akış", "flusso minimo", "min. prietok", "minimální průtok")
|
||||
MAKE_WORD_TRANSLATION(maxflow, "max flow", "max. Durchfluss", "max. doorstroom", "Max. flöde", "maksymalny przepływ", "maks. strømming", "flux max.", "maksimum akış", "flusso massimo", "max. prietok", "maximální průtok")
|
||||
MAKE_WORD_TRANSLATION(fast, "fast", "schnell", "snel", "snabb", "szybkie", "hurtig", "rapide", "hızlı", "veloce", "rýchly", "rychlý")
|
||||
MAKE_WORD_TRANSLATION(slow, "slow", "langsam", "langzaam", "långsam", "powolne", "langsom", "lent", "yavaş", "lento", "pomalý", "pomalý")
|
||||
MAKE_WORD_TRANSLATION(internal_temperature, "internal temperature", "Interne Temperatur", "interne temperatuur", "Interntemperatur", "temperatura wewnętrzna", "interntemperatur", "température interne", "oda sıcaklığı", "temperatura interna", "vnútorná teplota", "vnitřní teplota")
|
||||
MAKE_WORD_TRANSLATION(internal_setpoint, "internal setpoint", "Interner Sollwert", "interne streeftemperatuur", "Internt börvärde", "nastawa wewnętrzna", "internt settpunkt", "consigne interne", "istenen oda sıcaklığı", "setpoint interno", "vnútorná pož. hodnota", "vnitřní požadovaná hodnota")
|
||||
MAKE_WORD_TRANSLATION(external_temperature, "external temperature", "Externe Temperatur", "externe temperatuur", "Extern temperatur", "temperatura zewnętrzna", "ekstern temperatur", "température externe", "dış sıcaklık", "temperatura esterna", "vonkajšie teplota", "vnější teplota")
|
||||
MAKE_WORD_TRANSLATION(burner_temperature, "burner temperature", "Brennertemperatur", "brander temperatuur", "Brännartemperatur", "temperatura palnika", "brennertemperatur", "température du brûleur", "kazan sıcaklığı", "temperatura bruciatore", "teplota horáka", "teplota hořáku")
|
||||
MAKE_WORD_TRANSLATION(ww_temperature, "dhw temperature", "Wassertemperatur", "watertemperatuur", "Vattentemperatur", "temperatura c.w.u.", "vanntemperatur", "température de l'eau", "Kullanım suyu sıcaklığı", "temperatura acqua", "teplota vody", "teplota TUV")
|
||||
MAKE_WORD_TRANSLATION(smoke_temperature, "smoke temperature", "Abgastemperatur", "rookgastemperatuur", "Rökgastemperatur", "temperatura dymu", "røykgasstemperatur", "température des gaz d'échappement", "baca gazı sıcaklığı", "temperatura fumo", "teplota dymu", "teplota spalin")
|
||||
MAKE_WORD_TRANSLATION(weather_compensated, "weather compensated", "Wetter kompensiert", "weer gecompenseerd", "Väderkompenserad", "skompensow. pogodą", "værkompensert", "compensation par l'extérieur", "hava durumuna göre dengelenmiş", "acqua compensata", "kompenzácia počasia", "dle venkovní teploty")
|
||||
MAKE_WORD_TRANSLATION(outside_basepoint, "outside basepoint", "Basispunkt Außentemp.", "buiten basispunt", "Utomhus baspunkt", "temp. zewn. z pkt. pocz.", "utendørs basispunkt", "point de base temp. ext.", "dış hava sıcaklığı taban noktası", "basepoint esterno", "vonkajší základný bod", "venkovní teplota s patním bodem")
|
||||
MAKE_WORD_TRANSLATION(functioning_mode, "functioning mode", "Funktionsweise", "functiemodus", "Driftläge", "tryb pracy", "driftsmodus", "mode de fonctionnement", "işletme konumu", "modalità di funzionamento", "funkčný režim", "provozní režim")
|
||||
MAKE_WORD_TRANSLATION(unmixed, "unmixed", "ungemischt", "", "oshuntad", "niezmieszany", "", "", "", "", "nezmiešaný", "nemíchaný") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(unmixedIPM, "unmixed IPM", "ungemischt IPM", "", "oshuntad IPM", "niezmieszany IPM", "", "", "", "", "nezmiešaný IPM", "nemíchaný IPM") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(mixed, "mixed IPM", "gemischt IPM", "", "shuntad IPM", "zmieszany IPM", "", "", "", "", "zmiešaný IPM", "míchaný IPM") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(level, "level", "Level", "", "nivå", "", "", "", "", "", "úroveň", "úroveň") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(absolute, "absolute", "Absolut", "", "absolut", "", "", "", "", "", "absolútny", "absolutní") // TODO translate
|
||||
|
||||
// mixer
|
||||
MAKE_WORD_TRANSLATION(stopped, "stopped", "gestoppt", "gestopt", "stoppad", "zatrzymany", "stoppet", "arrêté", "durdu", "fermato", "zastavený", "zastavené")
|
||||
MAKE_WORD_TRANSLATION(opening, "opening", "öffnen", "openen", "öppnar", "otwieranie", "åpner", "ouverture", "açılıyor", "aperto", "otvorenie", "otevřené")
|
||||
MAKE_WORD_TRANSLATION(closing, "closing", "schließen", "sluiten", "stänger", "zamykanie", "stenger", "fermeture", "kapanıyor", "chiuso", "zatvorenie", "zavřené")
|
||||
MAKE_WORD_TRANSLATION(open, "open", "offen", "open", "Öppen", "otwórz", "åpen", "ouvert", "açık", "aprire", "otvoriť", "otevřít")
|
||||
MAKE_WORD_TRANSLATION(close, "close", "geschlossen", "Gesloten", "Stängd", "zamknij", "stengt", "fermé", "kapalı", "chiudere", "zatvoriť", "zavřít")
|
||||
|
||||
// solar dhw
|
||||
MAKE_WORD_TRANSLATION(cyl1, "cyl 1", "Zyl_1", "Cil 1", "Cyl 1", "cyl 1", "cyl 1", "cyl 1", "cly 1", "Cil 1", "cyl 1", "")
|
||||
MAKE_WORD_TRANSLATION(cyl2, "cyl 2", "Zyl_2", "Cil 2", "Cyl 2", "cyl 2", "cyl 2", "cyl 2", "cly 1", "Cil 2", "cyl 2", "")
|
||||
|
||||
// ventilation
|
||||
MAKE_WORD_TRANSLATION(demand, "demand", "Bedarf", "vereist", "behov", "zapotrzebowanie", "", "", "talep", "richiesta", "požiadavka", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(intense, "intense", "Intensiv", "intensief", "intensiv", "intensywne", "", "", "yoğun", "intensivo", "intenzívne", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(sleep, "sleep", "Einschlafen", "slaapmodus", "sova", "sen", "", "", "uyku", "notturno", "spiace", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(partymode, "party", "Party", "party", "party", "impreza", "", "", "parti", "festa", "párty režim", "") // TODO translate
|
||||
MAKE_WORD_TRANSLATION(fireplace, "fireplace", "Kamin", "haard", "Kamin", "kominek", "", "", "şömine", "camino", "krb", "") // TODO translate
|
||||
|
||||
// MQTT Discovery - this is special device entity for 'climate'
|
||||
MAKE_TRANSLATION(haclimate, "haclimate", "mqtt discovery current room temperature", "Discovery aktuelle Raumtemperatur", "Discovery huidige kamertemperatuur", "MQTT Discovery för aktuell rumstemperatur", "termostat w HA", "HA Avlest temp", "", "Güncel osa sıcaklığı", "verifica temperatura ambiente attuale", "mqtt discovery aktuálna teplota v miestnosti", "mqtt discovery aktuální pokojová teplota") // TODO translate
|
||||
|
||||
// Entity translations: tag, mqtt, en, de, nl, sv, pl, no, fr, tr, it, sk, cz
|
||||
// Boiler
|
||||
MAKE_TRANSLATION(reset, "reset", "reset", "Reset", "Reset", "Återställ", "kasowanie komunikatu", "nullstill", "reset", "Sıfırla", "Reset", "reset", "reset")
|
||||
MAKE_TRANSLATION(forceHeatingOff, "heatingoff", "force heating off", "Heizen abschalten", "", "Uppvärmning avstängd", "wymuś wyłączenie grzania", "", "", "", "", "vynútené vypnutie kúrenia", "vynutit vypnutí vytápění") // TODO translate
|
||||
MAKE_TRANSLATION(wwtapactivated, "tapactivated", "turn on/off", "Durchlauferhitzer aktiv", "zet aan/uit", "på/av", "system przygotowywania", "Varmtvann active", "ecs activée", "aç/kapa", "commuta on/off", "zapnúť/vypnúť", "zapnout/vypnout")
|
||||
MAKE_TRANSLATION(oilPreHeat, "oilpreheat", "oil preheating", "Ölvorwärmung", "Olie voorverwarming", "Förvärmning olja", "podgrzewanie oleju", "oljeforvarming", "préchauffage de l'huile", "Yakıt Ön ısıtma devrede", "preriscaldamento olio", "predohrev oleja", "předehřev oleje")
|
||||
MAKE_TRANSLATION(heatingActive, "heatingactive", "heating active", "Heizen aktiv", "Verwarming actief", "Uppvärmning aktiv", "c.o. aktywne", "oppvarming aktiv", "chauffage actif", "ısıtma devrede", "riscaldamento attivo", "vykurovanie aktívne", "Vytápění aktivní")
|
||||
MAKE_TRANSLATION(heatingOn, "heating", "heating", "Heizen", "verwarmen", "Uppvärmning", "ogrzewanie", "oppvarming", "chauffage", "ısıtma", "riscaldamento", "vykurovanie", "Vytápění")
|
||||
MAKE_TRANSLATION(tapwaterActive, "tapwateractive", "tapwater active", "Warmwasser aktiv", "Warm water actief", "Varmvatten aktiv", "c.w.u. aktywne", "varmtvann aktiv", "eau chaude active", "sıcak kullanım suyu devrede", "acqua calda attiva", "TÚV aktívna", "teplá voda aktivní")
|
||||
MAKE_TRANSLATION(selBurnPow, "selburnpow", "burner selected max power", "Eingestellte maximale Brennerleistung", "Ingestelde maximale brandervermogen", "Brännare vald maxeffekt", "wybrana moc źródła ciepła", "settpunkt brennerkapasitet", "puissance max du brûleur selectionnée", "seçili kazan maksimum güç", "Setpoint potenza bruciatore", "horák zvolený maximálny výkon", "zvolený maximální výkon hořáku")
|
||||
MAKE_TRANSLATION(absBurnPow, "absburnpow", "burner current power (absolute)", "Aktuelle Brennerleistung (absolut)", "Brandervermogen (abs)", "Värmepanna aktuell effekt (abs)", "aktualna moc źródła ciepła (absolutna)", "brennereffekt", "puissance du brûleur actuelle (abs)", "kazan anlık akış gücü(mutlak)", "potenza attuale del bruciatore (assoluta)", "aktuálny výkon horáka (absolútny)", "aktuální výkon hořáku (absolutní)")
|
||||
MAKE_TRANSLATION(heatingPumpMod, "heatingpumpmod", "heating pump modulation", "Modulation Heizungspumpe", "Modulatie verwarmingspomp", "Modulering Värmepump", "wysterowanie pompy c.o.", "varmepumpemodulering", "modulation de la pompe à chaleur", "ısı pompası modülasyonu", "modulazione pompa di calore", "modulácia tepelného čerpadla", "modulace čerpadla topení")
|
||||
MAKE_TRANSLATION(outdoorTemp, "outdoortemp", "outside temperature", "Außentemperatur", "Buitentemperatuur", "Utomhustemperatur", "temperatura zewnętrzna", "utetemperatur", "température extérieure", "dış ortam sıcaklığı", "tempertura esterna", "vonkajšia teplota", "venkovní teplota")
|
||||
MAKE_TRANSLATION(selFlowTemp, "selflowtemp", "selected flow temperature", "Gewählte Vorlauftemperatur", "Ingestelde aanvoertemperatuur", "Börvärde Flödestemperatur", "wybrana temperatura zasilania", "valgt turtemperatur", "température de flux selectionnée", "seçili akış sıcaklığı", "flusso temperatura selezionato", "zvolená teplota na výstupe", "zvolená výstupní teplota ")
|
||||
MAKE_TRANSLATION(curFlowTemp, "curflowtemp", "current flow temperature", "Aktuelle Vorlauftemperatur", "Huidige aanvoertemperatuur", "Flödestemperatur", "temperatura zasilania", "aktuell strømmetemperatur", "température actuelle du flux", "akış sıcaklığı", "temperatura di mandata attuale", "aktuálna výstupná teplota", "naměřená výstupní teplota")
|
||||
MAKE_TRANSLATION(retTemp, "rettemp", "return temperature", "Rücklauftemperatur", "Retourtemperatuur", "Returtemperatur", "temperatura powrotu", "returtemperatur", "température de retour", "dönüş sıcaklığı", "temperatura di ritorno attuale", "teplota spiatočky", "teplota zpátečky")
|
||||
MAKE_TRANSLATION(switchTemp, "switchtemp", "mixing switch temperature", "Mischerschalttemperatur", "Mixer temperatuur", "Blandartemperatur", "temperatura przełączania mieszacza", "Blandertemperatur", "température de bascule du mélangeur", "karışım hücresi sıcaklığı", "Temperatura di commutazione del miscelatore", "teplota zmiešavacieho spínača", "teplota směšovacího rozdělovače")
|
||||
MAKE_TRANSLATION(sysPress, "syspress", "system pressure", "Systemdruck", "Systeemdruk", "Systemtryck", "ciśnienie w systemie", "systemtrykk", "pression du système", "sistem basıncı", "pressione sistema", "tlak v systéme", "tlak v systému")
|
||||
MAKE_TRANSLATION(boilTemp, "boiltemp", "actual boiler temperature", "Kesseltemperatur", "Keteltemperatuur", "Temperatur Värmepanna", "temperatura zasobnika", "varmepumpetemp.", "température de la chaudière", "gerçek boyler sıcaklığı", "temperatura attuale caldaia", "skutočná teplota kotla", "aktuální teplota kotle")
|
||||
MAKE_TRANSLATION(exhaustTemp, "exhausttemp", "exhaust temperature", "Abgastemperatur", "Uitlaattemperatuur", "Avgastemperatur", "temperatura spalin", "røykgasstemp", "température des gaz d'échappement", "baca sıcaklığı", "temperatura di scarico", "teplota výfukových plynov", "teplota spalin")
|
||||
MAKE_TRANSLATION(burnGas, "burngas", "gas", "Gas", "Gas", "Gas", "gaz", "gass", "gaz", "gaz", "Gas", "plyn", "plyn")
|
||||
MAKE_TRANSLATION(burnGas2, "burngas2", "gas stage 2", "Gas Stufe 2", "gas fase 2", "Gas Fas 2", "gaz 2 stopień", "gass fase 2", "gaz état 2", "gaz 2.seviye", "gas fase 2", "plynový stupeň 2", "plyn stupeň 2")
|
||||
MAKE_TRANSLATION(flameCurr, "flamecurr", "flame current", "Flammenstrom", "Vlammenstroom", "Lågström", "prąd palnika", "flammetemp", "courrant de flamme", "alev akımı", "corrente di fiamma", "prúd plameňa", "proud plamene")
|
||||
MAKE_TRANSLATION(heatingPump, "heatingpump", "heating pump", "Heizungspumpe", "Verwarmingspomp", "Värmepump", "pompa ciepła", "varmepumpe", "pompe à chaleur", "ısı pompası", "pompa di calore", "tepelné čerpadlo", "Čerpadlo topení")
|
||||
MAKE_TRANSLATION(fanWork, "fanwork", "fan", "Gebläse", "Ventilator", "Fläkt", "wentylator", "vifte", "ventilateur", "fan", "Ventilatore", "ventilátor", "ventilátor")
|
||||
MAKE_TRANSLATION(ignWork, "ignwork", "ignition", "Zündung", "Ontsteking", "Tändning", "zapłon", "tenning", "ignition", "ateşleme", "accensione", "zapálenie", "zapalování")
|
||||
MAKE_TRANSLATION(heatingActivated, "heatingactivated", "heating activated", "Heizbetrieb aktiviert", "Verwarmen geactiveerd", "Uppvärmning aktiv", "system c.o.", "oppvarming aktivert", "chauffage activé", "ısıtma başladı", "riscaldamento attivato", "kúrenie aktivované", "topení aktivováno")
|
||||
MAKE_TRANSLATION(heatingTemp, "heatingtemp", "heating temperature", "Heiztemperatur", "Verwarmingstemperatuur", "Uppvärmningstemperatur", "temperatura grzania", "oppvarmingstemperatur", "température de chauffage", "ısıtma sıcaklığı", "temperatura riscaldamento", "teplota vykurovania", "teplota topení")
|
||||
MAKE_TRANSLATION(pumpModMax, "pumpmodmax", "boiler pump max power", "Maximale Kesselpumpenleistung", "Ketelpomp max vermogen", "Värmepannepump max effekt", "maksymalna moc pompy zasobnika", "varmepumpe maks effekt", "puissance max pompe à chaleur", "boyler pompası maksimum güç", "max potenza pompa caldaia", "maximálny výkon čerpadla kotla", "maximální výkon čerpadla kotle")
|
||||
MAKE_TRANSLATION(pumpModMin, "pumpmodmin", "boiler pump min power", "Minimale Kesselpumpenleistung", "Ketelpomp min vermogen", "Värmepannepump min effekt", "minimalna moc pompy zasobnika", "varmepumpe min effekt", "puissance min pompe à chaleur", "boyler pompası minimum güç", "min potenza pompa caldaia", "min. výkon čerpadla kotla", "minimální výkon čerpadla kotle")
|
||||
MAKE_TRANSLATION(pumpDelay, "pumpdelay", "pump delay", "Pumpennachlaufzeit", "Pomp nalooptijd", "Pumpfördröjning", "opóźnienie pompy", "pumpeforsinkelse", "délai d'attente pompe", "pompa gecikmesi", "ritardo pompa", "oneskorenie čerpadla", "zpoždění čerpadla")
|
||||
MAKE_TRANSLATION(burnMinPeriod, "burnminperiod", "burner min period", "Antipendelzeit", "Antipendeltijd", "Värmepanna Min Period", "minimalny czas pracy palnika", "varmekjele min periode", "délai d'attente du brûleur", "kazan minmum periyod", "periodo minimo del bruciatore", "minimálne obdobie horáka", "minimální doba hoření")
|
||||
MAKE_TRANSLATION(burnMinPower, "burnminpower", "burner min power", "Minimale Brennerleistung", "Minimaal brandervermogen", "Värmepanna Min Effekt", "minimalna moc źródła ciepła", "varmekjele min effekt", "puissance min brûleur", "kazan minimum güç", "potenza minima bruciatore", "min. výkon horáka", "minimální výkon hořáku")
|
||||
MAKE_TRANSLATION(burnMaxPower, "burnmaxpower", "burner max power", "Maximale Brennerleistung", "Maximaal brandervermogen", "Värmepanna Max Effekt", "maksymalna moc źródła ciepła", "varmekjele maks effekt", "puissance max brûleur", "kazan maksimum güç", "potenza massima bruciatore", "max. výkon horáka", "maximální výkon hořáku")
|
||||
MAKE_TRANSLATION(boilHystOn, "boilhyston", "hysteresis on temperature", "Einschaltdifferenz", "ketel aan hysterese verschil", "Hysteres aktiveringstemperatur", "histereza załączania", "hysterese på temperatur", "hysteresis température d'allumage", "gecikme sıcaklığı devrede", "isteresi sulla temperatura", "hysterézia teploty pri zapnutí", "hystereze teploty při zapnutí")
|
||||
MAKE_TRANSLATION(boilHystOff, "boilhystoff", "hysteresis off temperature", "Ausschaltdifferenz", "ketel uit hysterese verschil", "Hysteres inaktiveringstemperatur", "histereza wyłączania", "hysterese av temperatur", "hysteresis température d'extinction", "gecikme sıcaklığı kapalı", "isteresi fuori temperatura", "hysterézia teploty pri vypnutí", "hystereze teploty při vypnutí")
|
||||
MAKE_TRANSLATION(boil2HystOn, "boil2hyston", "hysteresis stage 2 on temperature", "Einschaltdifferenz Stufe 2", "ketel aan hysterese verschil 2", "Hysteres aktiveringstemperatur 2", "histereza załączania stopnia 2", "", "hysteresis état 2 température d'allumage", "2. seviye gecikme sıcaklığı devrede", "stadio di isteresi 2 sulla temperatura", "2. stupeň hysterézie pri teplote", "hystereze teploty při zapnutí 2.stupeň") // TODO translate
|
||||
MAKE_TRANSLATION(boil2HystOff, "boil2hystoff", "hysteresis stage 2 off temperature", "Ausschaltdifferenz Stufe 2", "ketel uit hysterese verschil 2", "Hysteres inaktiveringstemperatur 2", "histereza wyłączania stopnia 2", "hysterese inaktiveringstemperatur 2", "hysteresis état 2 température d'extinction", "2. seviye gecikme sıcaklığı kapalı", "isteresi stadio 2 fuori temperatura", "teplota vypnutia hysterézneho stupňa 2", "hystereze teploty při vypnutí 2.stupeň")
|
||||
MAKE_TRANSLATION(setFlowTemp, "setflowtemp", "set flow temperature", "Sollwert Vorlauftemperatur", "Ingestelde aanvoertemperatuur", "Börvärde Flödestemperatur", "zadana temperatura zasilania", "innstilt turtemperatur", "température du flux définie", "akış sıcaklığını ayarla", "impostare temperatura di mandata", "nastavená teplota výstupu", "nastavená teplota výstupu")
|
||||
MAKE_TRANSLATION(setBurnPow, "setburnpow", "burner set power", "Sollwert Brennerleistung", "Ingesteld brandervermogen", "Värmepanna vald Effekt", "zadana moc palnika", "varmekjele valgt effekt", "puissance du brûleur définie", "kazan gücünü ayarla", "impostare potenza bruciatore", "nastavenie výkonu horáka", "nastavený výkon hořáku")
|
||||
MAKE_TRANSLATION(curBurnPow, "curburnpow", "burner current power", "Aktuelle Brennerleistung", "Brandervermogen", "Värmepanna aktuell effekt", "aktualna moc źródła ciepła", "brennereffekt", "puissance du brûleur actuelle", "kazan güncel gücü", "potenza attuale bruciatore", "aktuálny výkon horáka", "aktuální výkon hořáku")
|
||||
MAKE_TRANSLATION(burnStarts, "burnstarts", "burner starts", "Brennerstarts", "Aantal brander starts", "Värmepanna antal starter", "liczba uruchomień palnika", "antall varmepumpe starter", "démarrages du brûleur", "kazan başlıyor", "avvii bruciatore", "štarty horáka", "starty hořáku")
|
||||
MAKE_TRANSLATION(burnWorkMin, "burnworkmin", "total burner operating time", "Brennerlaufzeit", "Totale branderlooptijd", "Värmepanna aktiva timmar", "łączny czas pracy palnika", "brennersteg tid i min", "durée de fonctionnement totale du brûleur", "toplam kazan çalışma süresi", "tempo totale di funzionamento del bruciatore", "celkový prevádzkový čas horáka", "celkový provozní čas hořáku")
|
||||
MAKE_TRANSLATION(burn2WorkMin, "burn2workmin", "burner stage 2 operating time", "Brennerlaufzeit Stufe 2", "Totale looptijd brander fase 2", "Värmepanna steg 2 aktiva timmar", "łączny czas pracy palnika 2 stopnia", "brennersteg2 tid i min", "durée de fonctionnement totale du brûleur état 2", "2. seviye toplam kazan çalışma süresi", "tempo di funzionamento del bruciatore 2° stadio", "doba prevádzky 2. stupňa horáka", "provozní čas hořáku 2.stupeň")
|
||||
MAKE_TRANSLATION(heatWorkMin, "heatworkmin", "total heat operating time", "Heizlaufzeit", "Totale looptijd verwarming", "Uppvärmning aktiva timmar", "łączny czas grzania", "varmetid i min", "durée de fonctionnement du chauffage", "toplam ısıtma çalışma süresi", "tempo totale di funzionamento in riscaldamento", "celkový prevádzkový čas kúrenia", "celkový provozní čas topení")
|
||||
MAKE_TRANSLATION(heatStarts, "heatstarts", "burner starts heating", "Brennerstarts Heizen", "Aantal brander starts verwarming", "Uppvärmning antal starter", "liczba uruchomień palnika na ogrzewanie", "antall oppvarmninger", "démarrages du chauffage", "kazan ısıtmaya başlıyor", "preriscaldamento bruciatore", "štarty horáka na kúrenie", "starty hořáku topení")
|
||||
MAKE_TRANSLATION(UBAuptime, "ubauptime", "total UBA operating time", "Anlagengesamtlaufzeit", "totale looptijd branderautomaat (UBA)", "Total Tid", "łączny czas pracy układu sterowania", "totaltid", "durée de fonctionnement totale de l'appareil (UBA)", "kazanın toplam işletme süresi", "Tempo di funzionamento totale del sistema", "Celkový čas chodu systému", "celkový provozní čas UBA")
|
||||
MAKE_TRANSLATION(lastCode, "lastcode", "last error code", "Letzter Fehler", "Laatste foutcode", "Senaste Felkod", "ostatni błąd", "siste feilkode", "dernier code d'erreur", "son hata kodu", "ultimo codice errore", "posledný chybový kód", "poslední kód chyby")
|
||||
MAKE_TRANSLATION(serviceCode, "servicecode", "service code", "Statusmeldung", "Statuscode", "Servicekod", "kod serwisowy", "servicekode", "code de service", "servis kodu", "codice messaggio di stato", "servisný kód", "servisní kód")
|
||||
MAKE_TRANSLATION(serviceCodeNumber, "servicecodenumber", "service code number", "Statusmeldungsnummer", "Status codenummer", "Servicekodnummer", "numer kodu serwisowego", "servicekodenummer", "numéro du code de service", "servis kod numarası", "numero del messaggio di stato", "servisný kód - číslo", "číslo servisního kódu")
|
||||
MAKE_TRANSLATION(maintenanceMessage, "maintenancemessage", "maintenance message", "Wartungsmeldung", "Onderhoudsmelding", "Servicemeddelande", "komunikat przeglądu", "vedlikeholdsmelding", "message de maintenance", "bakım mesajı", "messaggio di manutenzione", "správa o údržbe", "zpráva o údržbě")
|
||||
MAKE_TRANSLATION(maintenanceDate, "maintenancedate", "next maintenance date", "Wartungsdatum", "Onderhoudsdatum", "Datum nästa Service", "termin następnego przeglądu", "vedlikeholdsdato", "prochaine date de maintenance", "bakım tarihi", "prossima data di manutenzione", "dátum ďalšej údržby", "datum další údržby")
|
||||
MAKE_TRANSLATION(maintenanceType, "maintenance", "maintenance scheduled", "Wartungsplan", "Onderhoud gepland", "Underhall schemlagt", "rodzaj przeglądu", "vedlikeholdstype", "maintenance prévue", "planlı bakım", "manutenzione programmata", "plánovaná údržba", "naplánovaná údržba")
|
||||
MAKE_TRANSLATION(maintenanceTime, "maintenancetime", "time to next maintenance", "Wartung in", "Onderhoud in", "Tid till nästa underhall", "czas do kolejnego przeglądu", "vedlikeholdstid", "durée avant la prochaine maintenance", "bakıma kalan süre", "tempo alla prossima manutenzione", "čas do ďalšej údržby", "čas do další údržby")
|
||||
MAKE_TRANSLATION(emergencyOps, "emergencyops", "emergency operation", "Notbetrieb", "Noodoperatie", "Nöddrift", "praca w trybie awaryjnym", "nøddrift", "opération d'urgence", "acil durum çalışması", "operazione di emergenza", "núdzová prevádzka", "nouzový provoz")
|
||||
MAKE_TRANSLATION(emergencyTemp, "emergencytemp", "emergency temperature", "Notfalltemperatur", "Noodtemperatuur", "Nöddrift temperatur", "temperatura w trybie awaryjnym", "nødtemperatur", "température d'urgence", "acil durum sıcaklığı", "temperatura di emergenza", "núdzová teplota", "nouzová teplota")
|
||||
MAKE_TRANSLATION(pumpMode, "pumpmode", "boiler pump mode", "Kesselpumpenmodus", "Ketelpomp modus", "", "tryb pracy pompy kotła", "pumpemodus", "", "pompa modu", "modalità pompa caldaia", "režim kotlového čerpadla", "režim čerpadla kotle") // TODO translate
|
||||
MAKE_TRANSLATION(pumpCharacter, "pumpcharacter", "boiler pump characteristic", "Charakteristik der Kesselpumpe", "karakteristiek ketelpomp", "pannpumpsegenskaper", "charakterystyka pompy kotłowej", "kjelepumpekarakteristikk", "caractéristique de la pompe de la chaudière", "gazan nasosy", "caratteristica della pompa della caldaia", "charakteristika kotlového čerpadla", "charakteristika čerpadla kotle") // TODO translate
|
||||
MAKE_TRANSLATION(pumpOnTemp, "pumpontemp", "pump logic temperature", "Pumpenlogiktemperatur", "", "Pumplogiktemperatur", "", "", "", "", "", "teplota logiky čerpadla", "") // TODO translate
|
||||
MAKE_TRANSLATION(headertemp, "headertemp", "low loss header", "Hydr. Weiche", "open verdeler", "Fördelare", "sprzęgło hydrauliczne", "", "bouteille de déc. hydr.", "isı bloğu gidiş suyu sıc.", "comp. idr.", "nízkostratová hlavica", "") // TODO translate
|
||||
MAKE_TRANSLATION(heatblock, "heatblock", "heating block", "Wärmezelle", "Aanvoertemp. warmtecel", "Värmeblock", "blok grzewczy", "", "départ corps de chauffe", "Hid.denge kabı sıcaklığı", "mandata scamb. pr.", "vykurovací blok", "blok topení") // TODO translate
|
||||
|
||||
MAKE_TRANSLATION(curveOn, "curveon", "heatingcurve on", "Heizkurve an", "", "Värmekurva På", "", "", "", "", "", "vykurovacia krivka zapnutá", "topná křivka zapnutá") // TODO translate
|
||||
MAKE_TRANSLATION(curveBase, "curvebase", "heatingcurve base", "Heizkurve Basis", "", "Värmekurva Bas", "", "", "", "", "", "základňa vykurovacej krivky", "základ topné křivky") // TODO translate
|
||||
MAKE_TRANSLATION(curveEnd, "curveend", "heatingcurve end", "Heizkurve Ende", "", "Värmekurva Slut", "", "", "", "", "", "koniec vykurovacej krivky", "konec topné křivky") // TODO translate
|
||||
|
||||
// heatpump/compress specific
|
||||
MAKE_TRANSLATION(upTimeTotal, "uptimetotal", "heatpump total uptime", "Gesamtbetriebszeit Wärmepumpe", "", "Total tid värmepump", "łączny czas pracy pompy ciepła", "", "", "", "", "celková doba prevádzky tepelného čerpadla", "celková doba provozu tepelného čerpadla") // TODO translate
|
||||
MAKE_TRANSLATION(upTimeControl, "uptimecontrol", "total operating time heat", "Gesamtbetriebszeit Heizen", "Totale bedrijfstijd", "Total tid uppvärmning", "łączny czas generowania ciepła", "total driftstid", "durée totale de fonctionnement chauffage", "ısınma toplam işletme süresi", "Tempo di funzionamento totale riscaldamento", "celkový prevádzkový čas tepla", "celková provozní doba topení")
|
||||
MAKE_TRANSLATION(upTimeCompHeating, "uptimecompheating", "operating time compressor heating", "Betriebszeit Kompressor heizen", "Bedrijfstijd compressor verwarmingsbedrijf", "Total tid kompressor uppvärmning", "łączny czas ogrzewania (sprężarka)", "totaltid kompressor", "durée de fonctionnement compresseur chauffage", "ısı pompası ısınma işletme süresi", "tempo di funzionamento del compressore riscaldamento", "prevádzková doba vykurovania kompresora", "provozní doba kompresoru pro topení")
|
||||
MAKE_TRANSLATION(upTimeCompCooling, "uptimecompcooling", "operating time compressor cooling", "Betriebszeit Kompressor kühlen", "Bedrijfstijd compressor koelbedrijf", "Total tid kompressor kyla", "łączny czas chłodzenia (sprężarka)", "Total tid kompressor kjøling", "durée de fonctionnement compresseur refroidissement", "ısı pompası soğuma işletme süresi", "tempo di funzionamento del compressore raffreddamento", "doba prevádzky chladenia kompresora", "provozní doba kompresoru pro chlazení")
|
||||
MAKE_TRANSLATION(upTimeCompWw, "uptimecomp", "operating time compressor", "Betriebszeit Kompressor", "Bedrijfstijd compressor", "Total tid kompressor", "łączny czas grzania c.w.u. (sprężarka)", "Total tid kompressor varmvatten", "durée de fonctionnement compresseur", "ısı pompası sıcak kullanım suyu işletme süresi", "tempo di funzionamento del compressore", "prevádzková doba kompresora", "provozní doba kompresoru")
|
||||
MAKE_TRANSLATION(upTimeCompPool, "uptimecomppool", "operating time compressor pool", "Betriebszeit Kompressor Pool", "Bedrijfstijd compressor voor zwembadbedrijf", "Total tid kompressor pool", "łączny czas podgrzewania basenu (sprężarka)", "Total tid kompressor basseng", "durée de fonctionnement compresseur piscine", "ısı pompası havuz işletme süresi", "tempo di funzionamento del compressore piscina", "prevádzková doba kompresorového bazéna", "provozní doba kompresoru pro bazén")
|
||||
MAKE_TRANSLATION(totalCompStarts, "totalcompstarts", "total compressor control starts", "Gesamtkompressorstarts ", "Totaal compressorstarts", "Kompressorstarter Totalt", "liczba załączeń sprężarki", "kompressorstarter totalt", "nombre démarrages total contrôle compresseur", "ısı pompası kontrolü toplam başlatma", "avvii totali del compressore", "spustí sa celkové riadenie kompresora", "celkový počet startů řízení kompresoru")
|
||||
MAKE_TRANSLATION(heatingStarts, "heatingstarts", "heating control starts", "Heizungsregelungstarts", "Starts verwarmingsbedrijf", "Kompressorstarter Uppvärmning", "liczba załączeń ogrzewania", "kompressorstarter oppvarming", "démarrages contrôle chauffage", "ısıtma kontrolü toplam başlatma", "avvii riscaldamento", "ovládanie vykurovania sa spustí", "počet startů řízení topení")
|
||||
MAKE_TRANSLATION(coolingStarts, "coolingstarts", "cooling control starts", "Kühlregelungstarts", "Starts koelbedrijf", "Kompressorstarter Kyla", "liczba załączeń chłodzenia", "kompressorstarter kjøling", "démarrages contrôle refroidissement", "soğutma kontrolü toplam başlatma", "avvii raffreddamento", "ovládanie chladenia sa spustí", "počet startů řízení chlazení")
|
||||
MAKE_TRANSLATION(poolStarts, "poolstarts", "pool control starts", "Poolsteuerungstarts", "Starts zwembadbedrijf", "Kompressorstarter Pool", "liczba załączeń podgrzewania basenu", "kompressorstarter basseng", "démarrages contrôle piscine", "havuz kontrolü toplam başlatma", "avvio controllato piscina", "riadenie bazéna sa spustí", "počet startů řízení bazénu")
|
||||
MAKE_TRANSLATION(nrgConsTotal, "nrgconstotal", "total energy consumption", "Gesamtenergieverbrauch", "Energieverbrauch gesamt", "Energiförbrukning totalt", "energia pobrana (sumarycznie)", "energiforbruk totalt", "consommation totale énergie", "toplam enerji tüketimi", "totale energia consumata", "celková spotreba energie", "celková spotřeba energie")
|
||||
MAKE_TRANSLATION(nrgConsCompTotal, "nrgconscomptotal", "total energy consumption compressor", "Gesamtenergieverbrauch Kompressor", "Energieverbruik compressor totaal", "Energiförbrukning kompressor", "energia pobrana przez sprężarkę", "energiforbruk kompressor", "consommation totale énergie compresseur", "ısı pompası toplam enerji tüketimi", "totale energia consumata compressore", "kompresor s celkovou spotrebou energie", "celková spotřeba energie kompresoru")
|
||||
MAKE_TRANSLATION(nrgConsCompHeating, "nrgconscompheating", "energy consumption compressor heating", "Energieverbrauch Kompressor heizen", "Energieverbruik compressor verwarmingsbedrijf", "Energiförbrukning uppvärmning", "energia pobrana przez sprężarkę na ogrzewanie", "energiforbruk oppvarming", "consommation énergie compresseur chauffage", "ısı pompası ısıtma toplam enerji tüketimi", "consumo energia compressore riscaldamento", "spotreba energie vykurovanie kompresorom", "spotřeba energie kompresoru pro topení")
|
||||
MAKE_TRANSLATION(nrgConsCompWw, "nrgconscomp", "energy consumption compressor", "Energieverbrauch Kompressor", "Energieverbruik compressor", "Energiförbrukning varmvatten", "energia pobrana przez sprężarkę na c.w.u.", "energiforbruk", "consommation énergie compresseur", "ısı pompası sıcak kullanım suyu toplam enerji tüketimi", "consumo energia compressore", "kompresor spotreby energie", "spotřeba energie kompresoru")
|
||||
MAKE_TRANSLATION(nrgConsCompCooling, "nrgconscompcooling", "energy consumption compressor cooling", "Energieverbrauch Kompressor kühlen", "Energieverbruik compressor koelbedrijf", "Energiförbrukning kyla", "energia pobrana przez sprężarkę na chłodzenie", "energiforbruk kjøling", "consommation énergie compresseur refroidissement", "ısı pompası soğutma toplam enerji tüketimi", "consumo energia compressore raffreddamento", "spotreba energie kompresorové chladenie", "spotřeba energie kompresoru pro chlazení")
|
||||
MAKE_TRANSLATION(nrgConsCompPool, "nrgconscomppool", "energy consumption compressor pool", "Energieverbrauch Kompressor Pool", "Energiebedrijf compressor zwembadbedrijf", "Energiförbrukning pool", "energia pobrana przez sprężarkę na podgrzewanie basenu", "energiforbruk basseng", "consommation énergie compresseur piscine", "ısı pompası havuz toplam enerji tüketimi", "consumo energia compressore piscina", "spotreba energie kompresorový bazén", "spotřeba energie kompresoru pro bazén")
|
||||
MAKE_TRANSLATION(nrgSuppTotal, "nrgsupptotal", "total energy supplied", "gesamte Energieabgabe", "Totaal opgewekte energie", "Genererad energi", "energia oddana (sumarycznie)", "tilført energi", "énergie totale fournie", "sağlanan toplam enerji", "totale energia fornita", "celková dodaná energia", "celková dodaná energie")
|
||||
MAKE_TRANSLATION(nrgSuppHeating, "nrgsuppheating", "total energy supplied heating", "gesamte Energieabgabe heizen", "Opgewekte energie verwarmingsbedrijf", "Genererad energi Uppvärmning", "energia oddana na ogrzewanie", "tilført energi oppvarming", "énergie totale fournie chauffage", "ısıtma sağlanan toplam enerji", "energia totale fornita - riscaldamento", "celková dodaná energia na vykurovanie", "celková dodaná energie pro topení")
|
||||
MAKE_TRANSLATION(nrgSuppWw, "nrgsupp", "total energy warm supplied", "gesamte Energieabgabe Wärme", "Opgewekte energie", "Genererad energi varmvatten", "energia oddana na c.w.u.", "tilført energi", "énergie chaude totale fournie", "sıcak kullanım suyu sağlanan toplam enerji", "totale energia calorica fornita", "celková dodaná teplá energia", "celková dodaná teplá energie")
|
||||
MAKE_TRANSLATION(nrgSuppCooling, "nrgsuppcooling", "total energy supplied cooling", "gesamte Energieabgabe kühlen", "Opgewekte energie koelbedrijf", "Genererad energi Kyla", "energia oddana na chłodzenie", "Tillført energi kjøling", "énergie totale fournie refroidissement", "soğutma sağlanan toplam enerji", "energia totale fornita - raffreddamento", "chladenie s celkovou dodanou energiou", "celková dodaná energie pro chlazení")
|
||||
MAKE_TRANSLATION(nrgSuppPool, "nrgsupppool", "total energy supplied pool", "gesamte Energieabgabe Pool", "Opgewekte energie zwembadbedrijf", "Genererad energi Pool", "energia oddana na podgrzewanie basenu", "tilført energi basseng", "énergie totale fournie piscine", "havuz sağlanan toplam enerji", "totale di energia fornita- piscina", "celkový bazén dodanej energie", "celková dodaná energie pro bazén")
|
||||
MAKE_TRANSLATION(auxElecHeatNrgConsTotal, "auxelecheatnrgconstotal", "total aux elec. heater energy consumption", "Energieverbrauch el. Zusatzheizung", "Totaal energieverbruik electrisch verwarmingselement", "Energiförbrukning Eltillkott", "energia pobrana przez grzałki", "energiforbruk varmekolbe", "consommation totale énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı toplam enerji tüketimi", "consumo energetico riscaldamento elettrico supplementare", "celková spotreba energie prídavného elektrického ohrievača", "celková spotřeba energie pomocného el. ohřívače")
|
||||
MAKE_TRANSLATION(auxElecHeatNrgConsHeating, "auxelecheatnrgconsheating", "aux elec. heater energy consumption heating", "Energieverbrauch el. Zusatzheizung Heizen", "Energieverbruik electrisch verwarmingselement voor verwarmingsbedrijf", "Energiförbrukning Eltillskott Uppvärmning", "energia pobrana przez grzałki na ogrzewanie", "energiforbruk varmekolbe oppvarming", "consommation énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı ısınma toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario", "pomocný elektrický ohrievač spotreba energie vykurovanie", "spotřeba energie pomocného el. ohřívače pro topení")
|
||||
MAKE_TRANSLATION(auxElecHeatNrgConsWw, "auxelecheatnrgcons", "aux elec. heater energy consumption", "Energieverbrauch el. Zusatzheizung", "Energieverbruik electrisch verwarmingselement voor", "Energiförbrukning Eltillskott Varmvatten", "energia pobrana przez grzałki na c.w.u.", "energiförbruk varmekolbe", "consommation énergie electrique auxiliaire chauffage", "ilave elektrikli ısıtıcı sıcak kullanım suyu toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario", "spotreba energie pomocného elektrického ohrievača", "spotřeba energie pomocného el. ohřívače")
|
||||
MAKE_TRANSLATION(auxElecHeatNrgConsPool, "auxelecheatnrgconspool", "aux elec. heater energy consumption pool", "Energieverbrauch el. Zusatzheizung Pool", "Energieverbruik electrisch verwarmingselement voor zwembadbedrijf", "Energiförbrukning Eltillskott Pool", "energia pobrana przez grzałki na podgrzewanie basenu", "energiforbruk el. tilleggsvarme basseng", "consommation énergie electrique auxiliaire chauffage piscine", "ilave elektrikli ısıtıcı havuz toplam enerji tüketimi", "consumo di energia riscaldamento elettrico ausiliario piscina", "bazén spotreby energie pomocného elektrického ohrievača", "spotřeba energie pomocného el. ohřívače pro bazén")
|
||||
|
||||
MAKE_TRANSLATION(hpCompOn, "hpcompon", "hp compressor", "WP Kompressor", "WP compressor", "VP Kompressor", "sprężarka pompy ciepła", "vp kompressor", "compresseur pompe à chaleur", "hp ısı pompası", "compressore pompa calore", "tč kompresor", "tč kompresor")
|
||||
MAKE_TRANSLATION(coolingOn, "coolingon", "cooling on", "Kühlung an", "koelbedrijf", "Kyla", "chłodzenie włączone", "kjøling", "refroidissement", "soğutma", "", "chladenie", "chlazení zapnuto") // TODO translate
|
||||
// MAKE_TRANSLATION(hpHeatingOn, "hpheatingon", "hp heating", "WP Heizen", "WP verwarmingsbedrijf", "VP Uppvärmning", "pompa ciepła, ogrzewanie", "vp oppvarmning", "hp ısınıyor", "riscaldamento pompa calore", "vykurovanie hp", "topení hp")
|
||||
// MAKE_TRANSLATION(hpCoolingOn, "hpcoolingon", "hp cooling", "WP Kühlen", "WP koelbedrijf", "VP Kyla", "pompa ciepła, chłodzenie", "vp kjøling", "hp soğuyor", "raffreddamento pompa calore", "chladenie hp", "chlazení hp")
|
||||
// MAKE_TRANSLATION(hpWwOn, "hpdhwon", "hp", "WP", "WP", "VP Varmvatten", "pompa ciepła", "vp", "pompe à chaleur", "hp", "pompa calore", "hp", "hp")
|
||||
// MAKE_TRANSLATION(hpPoolOn, "hppoolon", "hp pool", "WP Pool", "WP zwembadbedrijf", "VP Pool", "pompa ciepła, podgrzewanie basenu", "vp basseng", "tuzlu su pompası hızı", "pompa calore piscina", "hp bazén", "hp bazén")
|
||||
MAKE_TRANSLATION(hpBrinePumpSpd, "hpbrinepumpspd", "brine pump speed", "Solepumpendrehzahl", "Snelheid pekelpomp", "Hastighet Brine-pump", "wysterowanie pompy glikolu", "hastighet brine-pumpe", "vitesse pompe à saumure", "ısı pompası hızı", "velocità pompa sbrinamento", "rýchlosť čerpadla soľanky", "rychlost čerpadla solanky")
|
||||
MAKE_TRANSLATION(hpCompSpd, "hpcompspd", "compressor speed", "Kompressordrehzahl", "Snelheid compressor", "Kompressorhastighet", "wysterowanie sprężarki", "kompressorhastighet", "vitesse du compresseur", "sirkülasyon pompası hızı", "velocità compressore", "rýchlosť kompresora", "rychlost kompresoru")
|
||||
MAKE_TRANSLATION(hpCircSpd, "hpcircspd", "circulation pump speed", "Zirkulationspumpendrehzahl", "Snelheid circulatiepomp", "Hastighet Cirkulationspump", "wysterowanie pompy obiegu grzewczego", "hastighet sirkulationspumpe", "vitesse pompe à circulation", "evaporatör tuzlu su giişi", "velocità pompa circolazione", "otáčky obehového čerpadla", "rychlost oběhového čerpadla")
|
||||
MAKE_TRANSLATION(hpBrineIn, "hpbrinein", "brine in/evaporator", "Sole in/Verdampfer", "pekel in/verdamper", "Brine in (förångare)", "temperatura glikolu na wejściu kolektora (TB0)", "brine in/fordamper", "entrée saumure/évaporateur", "kondenser tuzlu su çıkışı", "salamoia nell evaporatore", "soľanka v/výparník", "solanka in/evaporátor")
|
||||
MAKE_TRANSLATION(hpBrineOut, "hpbrineout", "brine out/condenser", "Sole aus/Kondensator", "pekel uit/condensor", "Brine ut (kondensor)", "temperatura glikolu na wyjściu kolektora (TB1)", "Brine ut/kondensor", "sortie saumure/condenseur", "anahtar valfi", "salamoia nell uscita evaporatore", "výstup soľanky/kondenzátor", "solanka out/kondenzátor")
|
||||
MAKE_TRANSLATION(hpSwitchValve, "hpswitchvalve", "switch valve", "Schaltventil", "schakelklep", "Växelventil", "zawór przełączający", "skifteventil", "valve de commutation", "ısı pompası aktivitesi", "valvola commutazione pompa di calore", "prepínací ventil", "přepínací ventil")
|
||||
MAKE_TRANSLATION(hpActivity, "hpactivity", "compressor activity", "Kompressoraktivität", "Compressoractiviteit", "Kompressoraktivitet", "pompa ciepła, aktywność sprężarki", "kompressoraktivitet", "hp ısı pompası", "attività compressore", "činnosť kompresora", "činnost kompresoru")
|
||||
|
||||
MAKE_TRANSLATION(hpMaxPower, "hpmaxpower", "compressor max power", "max. Kompressorleistung", "", "Max. Kompressoreffekt", "maksymalna wydajność sprężarki", "", "", "", "", "max výkon kompresora", "maximální výkon kompresoru") // TODO translate
|
||||
MAKE_TRANSLATION(pvMaxComp, "pvmaxcomp", "pv compressor max power", "PV max. Kompressorleistung", "", "PV Max. Kompressoreffekt", "maksymalna wydajność sprężarki", "", "", "", "", "pv max výkon kompresora", "maximální výkon PV kompresoru") // TODO translate
|
||||
MAKE_TRANSLATION(hpPower, "hppower", "compressor power output", "Kompressorleistung", "Compressorvermogen", "Kompressoreffekt", "moc wyjściowa sprężarki", "kompressoreffekt", "puissance de sortie compresseur", "ısı pompası güç çıkışı", "potenza uscita compressore", "výkon kompresora", "výstupní výkon kompresoru")
|
||||
MAKE_TRANSLATION(hpTc0, "hptc0", "heat carrier return (TC0)", "Kältemittelrücklauf (TC0)", "Koudemiddel retour (TC0)", "Värmebärare Retur (TC0)", "temperatura nośnika ciepła na powrocie (TC0)", "kjølemiddel retur (TC0)", "retour caloporteur (TC0)", "sıcak su dönüşü (TC0)", "ritorno del refrigerante (TC0)", "návrat nosiča tepla (TC0)", "návrat teplonosné látky (TC0)")
|
||||
MAKE_TRANSLATION(hpTc1, "hptc1", "heat carrier forward (TC1)", "Kältemittelvorlauf (TC1)", "Koudemiddel aanvoer (TC1)", "Värmebärare Framledning (TC1)", "temperatura nośnika ciepła pierwotna (TC1)", "kjølemiddel tur (TC1)", "avance caloporteur (TC1)", "sıcak su çıkışı (TC1)", "flusso di refrigerante (TC1)", "nosič tepla vpred (TC1)", "předání teplonosné látky (TC1)")
|
||||
MAKE_TRANSLATION(hpTc3, "hptc3", "condenser temperature (TC3)", "Kondensatortemperatur (TC3)", "Condensortemperatuur (TC3)", "Kondensortemperatur (TC3)", "temperatura skraplacza/na wyjściu sprężarki (TC3)", "kondensortemperatur (TC3)", "température condensateur (TC3)", "kondenser sıcaklığı (TC3)", "temperatura condensatore (TC3)", "teplota kondenzátora (TC3)", "teplota kondenzátoru (TC3)")
|
||||
MAKE_TRANSLATION(hpTr1, "hptr1", "compressor temperature (TR1)", "Kompressortemperatur (TR1)", "Compressor temperatuur (TR1)", "Kompressor temp (TR1)", "temperatura sprężarki (TR1)", "kompressor temperatur (TR1)", "température compresseur (TR1)", "ısı pompası sıcaklığı (TR1)", "temperatura compressore (TR1)", "teplota kompresora (TR1)", "teplota kompresoru (TR1)")
|
||||
MAKE_TRANSLATION(hpTr3, "hptr3", "refrigerant temperature liquid side (condenser output) (TR3)", "Kältemittel (flüssig) (TR3)", "Temperatuur koudemiddel vloeibare zijde (TR3)", "Köldmedium temperatur (kondensorutlopp) (TR3)", "temperatura skraplacza ogrzew. (TR3)", "kjølemiddeltemperatur på væskesiden (TR3)", "température réfrigérant côté liquide (sortie condensateur) (TR3)", "ısı pompası çıkışı (TR3)", "temperatura refrigerante lato liquido (uscita condensatore) (TR3)", "teplota chladiva na strane kvapaliny (výstup kondenzátora) (TR3)", "teplota chladiva na kapalné straně (výstup kondenzátoru) (TR3)")
|
||||
MAKE_TRANSLATION(hpTr4, "hptr4", "evaporator inlet temperature (TR4)", "Verdampfereingang (TR4)", "Verdamper ingangstemperatuur (TR4)", "Förångare inloppstemp (TR4)", "temperatura na wejściu parownika (TR4)", "innløpstemperatur for fordamperen (TR4)", "température entrée évaporateur (TR4)", "evaporatör giriş sıcaklığı (TR4)", "temperatura di ingresso dell'evaporatore (TR4)", "Vstupná teplota výparníka (TR4)", "teplota vstupu do evaporátoru (TR4)")
|
||||
MAKE_TRANSLATION(hpTr5, "hptr5", "compressor inlet temperature (TR5)", "Kompressoreingang (TR5)", "Compressor ingangstemperatuur (TR5)", "Kompressor inloppstemp (TR5)", "temperatura na wejściu sprężarki (TR5)", "kompressor innløpstemp (TR5)", "température entrée compresseur (TR5)", "ısı pompası giriş sıcaklığı (TR5)", "temperatura di ingresso del compressore (TR5)", "vstupná teplota kompresora (TR5)", "teplota vstupu do kompresoru (TR5)")
|
||||
MAKE_TRANSLATION(hpTr6, "hptr6", "compressor outlet temperature (TR6)", "Kompressorausgang (TR6)", "Compressor uitgangstemperatuur (TR6)", "Kompressor utloppstemp (TR6)", "temperatura na wyjściu sprężarki (TR6)", "kompressor utløpstemp (TR6)", "température sortie compresseur (TR6)", "ısı pompası çıkış sıcaklığı (TR6)", "temperatura di uscita del compressore (TR6)", "výstupná teplota kompresora (TR6)", "teplota výstupu z kompresoru (TR6)")
|
||||
MAKE_TRANSLATION(hpTr7, "hptr7", "refrigerant temperature gas side (condenser input) (TR7)", "Kältemittel (gasförmig) (TR7)", "Temperatuur koudemiddel gasvormig (TR7)", "Köldmedium temperatur gassida (kondensorinlopp) (TR7)", "temperatura czynnika chłodniczego po stronie gazu (wejście skraplacza) (TR7)", "kjølemedium temperatur gassida (kondensatorinløp) (TR7)", "température réfrigérant côté gaz (sortie condensateur) (TR7)", "kondenser giriş sıcaklığı (TR7)", "temperatura refrigerante lato gas (ingresso condensatore) (TR7)", "teplota chladiva na strane plynu (vstup kondenzátora) (TR7)", "teplota chladiva na plynové straně (vstup kondenzátoru) (TR7)")
|
||||
MAKE_TRANSLATION(hpTl2, "hptl2", "air inlet temperature (TL2)", "Außenlufteintrittstemperatur (TL2)", "Temperatuur luchtinlaat (TL2)", "Luftintagstemperatur (TL2)", "temperatura wlotu powietrza (TL2)", "luftinntakstemperatur (TL2)", "température entrée air (TL2)", "hava giriş sıcaklığı (TL2)", "temperatura ingresso aria (TL2)", "teplota prívodu vzduchu (TL2)", "teplota vzduchu na vstupu (TL2)")
|
||||
MAKE_TRANSLATION(hpPl1, "hppl1", "low pressure side temperature (PL1)", "Niederdrucktemperatur (PL1)", "Temperatuur lage drukzijde (PL1)", "Temperatur Lågtryckssidan (PL1)", "temperatura po stronie niskiego ciśnienia (PL1)", "temperatur lavtrykksiden (PL1)", "température côté basse pression (PL1)", "düşük basınç tarafı sıcaklığı (PL1)", "temperatura lato bassa pressione (PL1)", "teplota na strane nízkeho tlaku (PL1)", "teplota na nízkotlaké straně (PL1)")
|
||||
MAKE_TRANSLATION(hpPh1, "hpph1", "high pressure side temperature (PH1)", "Hochdrucktemperatur (PH1)", "Temperatuur hoge drukzijde (PH1)", "Temperatur Högtryckssidan (PH1)", "temperatura po stronie wysokiego ciśnienia (PH1)", "Temperatur Høytrykksiden (PH1)", "température côté bhauteasse pression (PH1)", "yüksek basınç tarafı sıcaklığı (PH1)", "temperatura lato alta pressione (PH1)", "teplota na strane vysokého tlaku (PH1)", "teplota na vysokotlaké straně (PH1)")
|
||||
MAKE_TRANSLATION(hpTa4, "hpta4", "drain pan temp (TA4)", "Kondensatorwanne (TA4)", "Temperatuur condensorafvoerbak (TA4)", " (TA4)", "temperatura ociekacza (TA4)", "kondenstråg temperatur (TA4)", " (TA4)", "tahliye sıcaklığı (TA4)", "temperatura condensatore (TA4)", "teplota vypúšťacej misky (TA4)", "teplota odvodňovací vany (TA4)") // TODO translate
|
||||
MAKE_TRANSLATION(hpTw1, "hptw1", "reservoir temp (TW1)", "DHW Reservoir (TW1)", "(TW1)", "Varmvattentank Temperatur (TW1)", "temperatura zbiornika (TW1)", "(TW1)", "(TW1)", "(TW1)", "(TW1)", "teplota zásobníka (TW1)", "teplota v nádrži (TW1)") // TODO translate
|
||||
|
||||
MAKE_TRANSLATION(hpInput1, "hpin1", "input 1 state", "Status Eingang 1", "Status input 1", "Status Ingång 1", "stan wejścia 1", "status inggang 1", "état entrée 1", "giriş 1 durumu", "stato ingresso 1", "stav vstupu 1", "stav vstupu 1")
|
||||
MAKE_TRANSLATION(hpInput2, "hpin2", "input 2 state", "Status Eingang 2", "Status input 2", "Status Ingång 2", "stan wejścia 2", "status inggang 2", "état entrée 2", "giriş 2 durumu", "stato ingresso 2", "stav vstupu 2", "stav vstupu 2")
|
||||
MAKE_TRANSLATION(hpInput3, "hpin3", "input 3 state", "Status Eingang 3", "Status input 3", "Status Ingång 3", "stan wejścia 3", "status inggang 3", "état entrée 3", "giriş 3 durumu", "stato ingresso 3", "stav vstupu 3", "stav vstupu 3")
|
||||
MAKE_TRANSLATION(hpInput4, "hpin4", "input 4 state", "Status Eingang 4", "Status input 4", "Status Ingång 4", "stan wejścia 4", "status inggang 4", "état entrée 4", "giriş 4 durumu", "stato ingresso 4", "stav vstupu 4", "stav vstupu 4")
|
||||
MAKE_TRANSLATION(hpIn1Opt, "hpin1opt", "input 1 options", "Einstellung Eingang 1", "Instelling input 1", "Inställningar Ingång 1", "opcje wejścia 1", "innstillinger inngang 1", "options entrée 1", "giriş 1 seçenekleri", "impostazioni ingresso 1", "možnosti vstupu 1", "možnosti vstupu 1")
|
||||
MAKE_TRANSLATION(hpIn2Opt, "hpin2opt", "input 2 options", "Einstellung Eingang 2", "Instelling input 2", "Inställningar Ingång 2", "opcje wejścia 2", "innstillinger inngang 2", "options entrée 2", "giriş 2 seçenekleri", "impostazioni ingresso 2", "možnosti vstupu 2", "možnosti vstupu 2")
|
||||
MAKE_TRANSLATION(hpIn3Opt, "hpin3opt", "input 3 options", "Einstellung Eingang 3", "Instelling input 3", "Inställningar Ingång 3", "opcje wejścia 3", "innstillinger inngang 3", "options entrée 3", "giriş 3 seçenekleri", "impostazioni ingresso 3", "možnosti vstupu 3", "možnosti vstupu 3")
|
||||
MAKE_TRANSLATION(hpIn4Opt, "hpin4opt", "input 4 options", "Einstellung Eingang 4", "Instelling input 4", "Inställningar Ingång 4", "opcje wejścia 4", "innstillinger inngang 4", "options entrée 4", "giriş 4 seçenekleri", "impostazioni ingresso 4", "možnosti vstupu 4", "možnosti vstupu 4")
|
||||
MAKE_TRANSLATION(maxHeatComp, "maxheatcomp", "heat limit compressor", "Heizstab Limit mit Kompressor", "heat limit compressor", "Max. Värmegräns Kompressor", "ograniczenie mocy sprężarki", "max varmegrense kompressor", "limite chaleur compresseur", "ısı pompası ısıtma sınırı", "limite riscaldamento compressore", "tepelný limit kompresora", "tepelný limit kompresoru")
|
||||
MAKE_TRANSLATION(maxHeatHeat, "maxheatheat", "heat limit heating", "Heizstab Limit Leistung", "Max, Värmegräns Uppvärmning", "heat limit heating", "ograniczenie mocy w trybie ogrzewania", "maks varmegrense oppvarming", "limite chaleur chauffage", "ısınma ısıtma sınırı", "limite calore riscaldamento", "vyhrievanie limitu tepla", "tepelný limit topení")
|
||||
MAKE_TRANSLATION(maxHeatDhw, "maxheat", "heat limit", "Heizstab Limit für WW", "heat limit", "Max. Värmegräns Varmvatten", "ograniczenie mocy w trybie c.w.u.", "varmegrense", "limite chaleur", "sıcak kullanım suyu ısınma sınırı", "limite calore", "tepelný limit", "tepelný limit")
|
||||
|
||||
MAKE_TRANSLATION(auxHeaterOff, "auxheateroff", "disable aux heater", "Zusatzheizer deaktivieren", "Bijverwarming uitsc", "Blockera eltillskott", "wyłącz dogrzewacz", "deaktiver tilleggsvarme", "Désactiver chauff. d'app", "ilave ısıtıcıyı kapat", "disattivare i riscaldatori addizionali", "vypnúť pomocný ohrievač", "zakázat pomocné topení")
|
||||
MAKE_TRANSLATION(auxHeaterStatus, "auxheaterstatus", "aux heater status", "Zusatzheizerstatus", "Bijverwarming", "Eltillskott Status", "status dogrzewacza", "status el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "stato riscaldatori addizionali", "stav pomocného ohrievača", "stav pomocného topení")
|
||||
MAKE_TRANSLATION(auxHeaterLevel, "auxheaterlevel", "aux heater level", "Zusatzheizer", "Bijverwarming", "Eltillskott", "dogrzewacza", "el. tillegsvarme", "Chauffage auxiliaire", "ilave ısıtıcı durumu", "riscaldatori addizionali", "pomocného ohrievača", "pomocného topení")
|
||||
MAKE_TRANSLATION(auxHeaterOnly, "auxheateronly", "aux heater only", "nur Zusatzheizer", "Alleen bijverwarming", "Eltillskott Enbart", "tylko dogrzewacz", "kun el tilleggsvarme", "Que chauffage auxiliaire", "sadece ilave ısıtıvcı", "solo riscaldatori addizionali", "iba pomocný ohrievač", "pouze pomocné topení")
|
||||
MAKE_TRANSLATION(auxHeaterDelay, "auxheaterdelay", "aux heater on delay", "Zusatzheizer verzögert ein", "Bijverw. vertraagd aan", "Eltillskottfördröjning på", "opóźnienie włączenia dogrzewacza", "Tilleggsvarmer forsinket på", "Chauff app tempo marche", "ilave ısıtıcı beklemede", "ritardo riscaldatori addizionali", "oneskorenie prídavného ohrievača", "zpoždění zapnutí pomocného topení")
|
||||
MAKE_TRANSLATION(silentMode, "silentmode", "silent mode", "Silentmodus", "Stiller gebruik", "Tyst drift", "tryb cichy", "stille modus", "Fct silencieux", "sessiz mod", "modalità silenziosa", "tichý režim", "tichý režim")
|
||||
MAKE_TRANSLATION(minTempSilent, "mintempsilent", "min outside temp for silent mode", "Minimale Außentemperatur Silentmodus", "Stiller gebruik min. buitentemp", "Tyst drift min temp", "minimalna temperatura zewnętrzna dla trybu cichego", "atille modus min temp", "Fct silencieux: Temp. extérieure min.", "sessiz mod için min. dış ortam sıcaklığı", "modalità silenziosa temperatura esterna minima", "min. vonkajšia teplota pre tichý režim", "minimální venkovní teplota pro tichý režim")
|
||||
MAKE_TRANSLATION(tempParMode, "tempparmode", "outside temp parallel mode", "Heizstab Parallelbetrieb", "Buitentemp. parallelbedr", "Parallelläge Utomhustemp.", "maksymalna temperatura zewnętrzna dla dogrzewacza", "", "Temp. ext. fct parallèle", "paralel mod dış ortam sıcaklığı", "modalità parallela temperatura esterna", "paralelný režim mimo teploty", "venkovní teplota pro paralelní režim") // TODO translate
|
||||
MAKE_TRANSLATION(auxHeatMixValve, "auxheatmix", "aux heater mixing valve", "Mischventil Zusatzheizer", "Bijverwarming menger", "Eltillskott Blandarventil", "mieszacz dogrzewacza", "eltilskudd blandeventil", "Chauffage auxiliaire mélangeur", "ilave ısıtıcı karışım vanası", "miscela riscaldatori addizionali", "zmiešavací ventil pomocného ohrievača", "směšovací ventil pomocného topení")
|
||||
MAKE_TRANSLATION(hpHystHeat, "hphystheat", "on/off hyst heat", "Schalthysterese Heizen", "Aan/uit-hysteresis in verw. bedrijf", "Hysteres Uppvärmning", "histereza wł./wył. ogrzewania", "På/av hysterese Oppvar.", "Hystérésis Marche en mode chauffage", "ısıtma gecikmesi", "isteresi di commutazione riscaldamento", "zapnutie/vypnutie hyst ohrevu", "hystereze zapnutí/vypnutí pro topení")
|
||||
MAKE_TRANSLATION(hpHystCool, "hphystcool", "on/off hyst cool", "Schalthysterese Kühlen", "Aan/uit-hysteresis in koelbedrijf", "Hysteres Kyla", "histereza wł./wył. chłodzenia", "hystrese kjøling", "Hystérésis Marche en mode refroidissement", "soğutma gecikmesi", "isteresi di commutazione raffreddamento", "zapnutie/vypnutie hyst chladenia", "hystereze zapnutí/vypnutí pro chlazení")
|
||||
MAKE_TRANSLATION(hpHystPool, "hphystpool", "on/off hyst pool", "Schalthysterese Pool", "an/uit-hysteresis in zwembadbedri", "Hysteres Pool", "histereza wł./wył. podgrzewania basenu", "hystrese basseng", "Hystérésis Marche en mode piscine", "havuz gecikmesi", "isteresi di commutazione piscina", "zapnutie/vypnutie hyst bazénu", "hystereze zapnutí/vypnutí pro bazén")
|
||||
MAKE_TRANSLATION(tempDiffHeat, "tempdiffheat", "temp diff TC3/TC0 heat", "Temp.diff. TC3/TC0 Heizen", "Temp.vers. TC3/TC0 verw", "Temperaturskillnad TC3/TC0 Uppvärmning", "różnica temperatur TC3/TC0 w trakcie ogrzewania", "temp. diff. TC3/TC0 oppvarm", "Delta T TC3/TC0 Chauff", "TC3-TC0 ısıtma sıcaklık farkı", "Delta T riscaldamento TC3/TC0", "teplotný rozdiel TC3/TC0 tepla", "rozdíl teplot TC3/TC0 pro topení")
|
||||
MAKE_TRANSLATION(tempDiffCool, "tempdiffcool", "temp diff TC3/TC0 cool", "Temp.diff. TC3/TC0 Kühlen", "Temp.vers. TC3/TC0 koel.", "Temperaturskillnad TC3/TC0 Kyla", "różnica temperatur TC3/TC0 w trakcie chłodzenia", "temp. diff. TC3/TC0 kjøling", "Delta T TC3/TC0 Refroid.", "TC3-TC0 soğutma sıcaklık farkı", "Delta T raffreddamento TC3/TC0", "teplotný rozdiel TC3/TC0 chladenie", "rozdíl teplot TC3/TC0 pro chlazení")
|
||||
MAKE_TRANSLATION(silentFrom, "silentfrom", "silent mode from", "Silentmodus Start", "Start stille modus", "Tyst drift starttid", "początek trybu cichego", "stillemodus starter", "", "sessiz mod başlangıcı", "avvio della modalità silenziosa", "tichý režim od", "tichý režim od") // TODO translate
|
||||
MAKE_TRANSLATION(silentTo, "silentto", "silent mode to", "Silentmodus Ende", "Einde stille modus", "Tyst drift stopptid", "koniec trybu cichego", "komfortmodus av", "", "sessiz mod bitişi", "spegnere modalità silenziosa", "tichý režim do", "tichý režim do") // TODO translate
|
||||
|
||||
MAKE_TRANSLATION(wwComfOffTemp, "comfoff", "comfort switch off", "Komfort Ausschalttemp.", "Comfort Uitschakeltemp.", "Komfort frånkopplingstemperatur", "temperatura wyłączania w trybie komfort", "eco modus utkoblingstem", "Confort Temp. d'arrêt", "konfor kapalı", "spegnimento modalità comfort", "komfortné vypnutie", "komfortní vypnutí")
|
||||
MAKE_TRANSLATION(wwEcoOffTemp, "ecooff", "eco switch off", "ECO Ausschalttemp.", "Eco Uitschakeltemp.", "Eko frånkopplingstemperatur", "temperatura wyłączania w trybie eko", "Øko avstengningstemp.", "Eco Temp. d'arrêt", "eko kapalı", "spegnimento modalità ECO", "eko vypínač", "eko vypnutí")
|
||||
MAKE_TRANSLATION(wwEcoPlusOffTemp, "ecoplusoff", "eco+ switch off", "ECO+ Ausschalttemp.", "Eco+ Uitschakeltemp.", "Eko+ frånkopplingstemperatur", "temperatura wyłączania w trybie eko+", "Øko+ avstengningstemp.", "Eco+ Temp. d'arrêt", "eko+ kapalı", "spegnimento modalità ECO+", "eko+ vypnutie", "eko+ vypnutí")
|
||||
MAKE_TRANSLATION(wwComfDiffTemp, "comfdiff", "comfort diff", "Komfort Differenztemp.", "", "Komfort temperaturskillnad", "", "", "", "", "", "Komfortný rozdiel teploty", "komfortní rozdíl") // TODO translate
|
||||
MAKE_TRANSLATION(wwEcoDiffTemp, "ecodiff", "eco diff", "ECO Differenztemp.", "", "Eko temperaturskillnad", "", "", "", "", "", "ECO rozdiel teploty", "eko rozdíl") // TODO translate
|
||||
MAKE_TRANSLATION(wwEcoPlusDiffTemp, "ecoplusdiff", "eco+ diff", "ECO+ Differenztemp.", "", "Eko+ temperaturskillnad", "", "", "", "", "", "ECO+ rozdiel teploty", "eko+ rozdíl") // TODO translate
|
||||
MAKE_TRANSLATION(wwComfStopTemp, "comfstop", "comfort stop temp", "Komfort Stopptemp.", "", "Komfort stopptemperatur", "", "", "", "", "", "komfortná stop teplota", "komfortní teplota vypnutí") // TODO translate
|
||||
MAKE_TRANSLATION(wwEcoStopTemp, "ecostop", "eco stop temp", "ECO Stopptemp.", "", "Eko stopptemperatur", "", "", "", "", "", "ECO stop teplota", "eko teplota vypnutí") // TODO translate
|
||||
MAKE_TRANSLATION(wwEcoPlusStopTemp, "ecoplusstop", "eco+ stop temp", "ECO+ Stopptemp.", "", "Eko+ stopptemperatur", "", "", "", "", "", "ECO+ stop teplota", "eko+ teplota vypnutí") // TODO translate
|
||||
|
||||
MAKE_TRANSLATION(auxHeatMode, "auxheatrmode", "aux heater mode", "Zusatzheizungsmodus", "Modus bijverwarmer", "Eltillskott Läge", "tryb pracy dogrzewacza po blokadzie z Zakładu Energetycznego", "tilleggsvarmer modus", "", "ilave ısıtıcı modu", "modalità riscaldatore addizionale", "režim pomocného ohrievača", "režim pomocného topení") // TODO translate
|
||||
MAKE_TRANSLATION(auxMaxLimit, "auxmaxlimit", "aux heater max limit", "Zusatzheizer max. Grenze", "Bijverwarmer grensinstelling maximaal", "Eltillskott max begränsning", "dogrzewacz, maksymalny limit", "tillegsvarme maksgrense", "ilave ısıtıcı maks limit", "limite massimo riscaldatore addizionale", "maximálny limit pomocného ohrievača", "maximální limit pomocného topení")
|
||||
MAKE_TRANSLATION(auxLimitStart, "auxlimitstart", "aux heater limit start", "Zusatzheizer Grenze Start", "Bijverwarmer grens voor start", "Eltillskott begränsningsstart", "dogrzewacz, początek ograniczenia", "tillegsvarme startgrense", "ilave ısıtıcı limir başlangıcı", "avvio limite massimo riscaldatore addizionale", "spustenie limitu pomocného ohrievača", "startovací limit pomocného topení")
|
||||
MAKE_TRANSLATION(manDefrost, "mandefrost", "manual defrost", "Manuelle Enteisung", "Handmatige ontdooicyclus", "Manuell avfrostning", "ręczne odladzanie", "manuell avisning", "", "manuel buz çözme", "sbrinamento manuale", "manuálne odmrazovanie", "ruční odmrazování") // TODO translate
|
||||
MAKE_TRANSLATION(pvCooling, "pvcooling", "cooling only with PV", "Kühlen nur mit PV", "Koelen alleen met solar PV", "Kyla endast med solpanel", "chłodzenie tylko z PV", "kjøling med solpanel", "", "sadece PV ile soğutma", "solo raffrescamento con solare", "Chladenie len s FV", "chlazení pouze s FV") // TODO translate
|
||||
MAKE_TRANSLATION(hpCircPumpWw, "hpcircpump", "circulation pump available during dhw", "Zirkulation möglich bei WW-Bereitung", "Circulatiepomp WP beschikbaar tijdens ww", "Värmebärarpump på vid varmvattenberedning", "pompa cyrkulacji dostępna w trakcie c.w.u.", "sirkulasjonspumpe tilgjengelig under varmtvann", "", "SKS esnasında sirkülasyon pompasu uygun", "pompa di circolazione disponibile durante ACS", "obehové čerpadlo k dispozícii počas TÚV", "oběhové čerpadlo dostupné během TUV") // TODO translate
|
||||
MAKE_TRANSLATION(vp_cooling, "vpcooling", "valve/pump cooling", "Ventil/Pumpe für Kühlen", "Klep koeling", "Ventil/Pump kyla", "zawór/pompa chłodzenia", "varmepumpe kjøling", "", "vana/pompa soğuyor", "valvola/pompa raffrescamento", "chladenie ventilu/čerpadla", "ventil/čerpadlo chlazení") // TODO translate
|
||||
MAKE_TRANSLATION(VC0valve, "vc0valve", "VC0 valve", "VC0 Ventil", "Klep VC0", "VC0 Ventil", "zawór VC0", "vc0 ventil", "", "VC0 vana", "valvola VC0", "VC0 ventil", "ventil VC0") // TODO translate
|
||||
MAKE_TRANSLATION(primePump, "primepump", "primary heatpump", "Hauptpumpe", "Hoofdpomp", "Primär Pump", "główna pompa ciepła", "primærpumpe", "", "ana ısı pompası", "pompa principale riscaldamento", "primárne tepelné čerpadlo", "primární tepelný čerpadlo") // TODO translate
|
||||
MAKE_TRANSLATION(primePumpMod, "primepumpmod", "primary heatpump modulation", "Modulation Hauptpumpe", "Modulatie hoofdpomp", "Modulation Primär Pump", "wysterowanie głównej pompy ciepła", "primærpumpelast", "", "ana ısı pompası modülasyon", "pompa principale modulazione riscaldamento", "primárna modulácia tepelného čerpadla", "modulace primárního tepelného čerpadla") // TODO translate
|
||||
MAKE_TRANSLATION(hp3wayValve, "hp3way", "3-way valve", "3-Wege-Ventil", "3-weg klep", "3-vägsventil", "zawór 3-drogowy pompy ciepła", "3-veisventil", "", "3 yollu vana", "valvola 3-vie", "3-cestný ventil", "3-cestný ventil") // TODO translate
|
||||
MAKE_TRANSLATION(hp4wayValve, "hp4way", "4-way valve (VR4)", "4-Wege-Ventil (VR4)", "4-weg klep (VR4)", "4-vägsventil (VR4)", "zawór 4-drogowy pompy ciepła (VR4)", "4-veisventil (VR4)", "(VR4)", "4 yollu vana (VR4)", "valvola 4-vie (VR4)", "4-cestný ventil (VR4)", "4-cestný ventil (VR4)") // TODO translate
|
||||
MAKE_TRANSLATION(elHeatStep1, "elheatstep1", "el. heater step 1", "El. Heizer Stufe 1", "Electrische bijverwarmer niveau 1", "Eltillskott Steg 1", "dogrzewacz poziom 1", "el-kolbe steg 1", "", "el.ısıtıcı adım 1", "riscaldatore elettrico livello 1", "krok 1 elektrického ohrievača", "elektrický ohřívač stupeň 1") // TODO translate
|
||||
MAKE_TRANSLATION(elHeatStep2, "elheatstep2", "el. heater step 2", "El. Heizer Stufe 2", "Electrische bijverwarmer niveau 2", "Eltillskott Steg 2", "dogrzewacz poziom 2", "el-kolbe steg 2", "", "el.ısıtıcı adım 2", "riscaldatore elettrico livello 2", "krok 2 elektrického ohrievača", "elektrický ohřívač stupeň 2") // TODO translate
|
||||
MAKE_TRANSLATION(elHeatStep3, "elheatstep3", "el. heater step 3", "El. Heizer Stufe 3", "Electrische bijverwarmer niveau 3", "Eltillskott Steg 3", "dogrzewacz poziom 3", "el-kolbe steg 3", "", "el.ısıtıcı adım 3", "riscaldatore elettrico livello 3", "krok 3 elektrického ohrievača", "elektrický ohřívač stupeň 3") // TODO translate
|
||||
MAKE_TRANSLATION(wwAlternatingOper, "alternatingop", "alternating operation", "Wechselbetrieb", "Wisselbedrijf ww", "Växeldrift", "praca naprzemienna", "alternativ drift", "", "sıcak kullanım suyu alternatif işletim", "funzionamento alternato", "striedavá prevádzka", "střídavý provoz") // TODO translate
|
||||
MAKE_TRANSLATION(wwAltOpPrioHeat, "altopprioheat", "prioritise heating during dhw", "Heizen bevorzugt vor WW", "Proriteit verwarming boven ww", "Varmvattenprioritet", "czas na ogrzewanie w trakcie c.w.u", "prioritert oppvarmning", "", "sıcak kullanım suyu esnasında ısıtmayı öne al", "dare la priorità al riscaldamento durante l'ACS", "Uprednostniť ohrev počas TÚV", "prioritizace vytápění během TUV") // TODO translate
|
||||
MAKE_TRANSLATION(wwAltOpPrioWw, "altopprio", "prioritise dhw during heating", "bevorzugt vor Heizen", "Prioriteit ww boven verwarming", "Värmeprioritet", "czas na c.w.u w trakcie ogrzewania", "prioritert varmtvann", "", "ısıtma esnasında sıcak kullanım suyunu öne al", "dare priorità all'acqua calda durante il riscaldamento", "uprednostniť TÚV počas ohrevu", "prioritizace TUV během vytápění") // TODO translate
|
||||
MAKE_TRANSLATION(hpEA0, "hpea0", "condensate reservoir heating (EA0)", "Heizung Kondensatwanne (EA0)", "", "Värme kondenstråg (EA0)", "ogrzewanie zbiornika kondensatu (EA0)", "", "", "", "", "ohrievanie zásobníka kondenzátu (EA0)", "ohřev kondenzátní nádrže (EA0)") // TODO translate
|
||||
MAKE_TRANSLATION(boost, "boost", "boost mode", "Boost", "", "Boost-läge", "tryb wzmocnienia (boost)", "", "", "", "", "boost režim", "režim boost") // TODO translate
|
||||
MAKE_TRANSLATION(boosttime, "boosttime", "boost time", "Boost-Dauer", "", "Boost-tid", "czas trwania wzmocnienia", "", "", "", "", "čas trvania posilnenia", "čas trvání režimu boost") // TODO translate
|
||||
MAKE_TRANSLATION(hpPumpMode, "hppumpmode", "primary heatpump mode", "primärer Wärmepumpenmodus", "", "Driftläge värmebärarpump", "tryb pracy głównej pompy ciepła", "", "", "", "", "režim primárneho tepelného čerpadla", "režim primárního tepelného čerpadla") // TODO translate
|
||||
MAKE_TRANSLATION(instantstart, "instantstart", "instant start", "Sofortstart", "", "Gränsvärde direktstart värme", "natychmiastowy start", "", "", "", "", "okamžité spustenie", "okamžité spuštění") // TODO translate
|
||||
MAKE_TRANSLATION(heatondelay, "heatondelay", "heat-on delay", "Einschaltverzögerung Heizen", "", "Inkopplingsfördröjning värme", "opóźnienie włączania ogrzewania", "", "", "", "", "Oneskorenie zapnutia kúreni", "zpoždění zapnutí topení") // TODO translate
|
||||
MAKE_TRANSLATION(heatoffdelay, "heatoffdelay", "heat-off delay", "Ausschaltverzögerung Heizen", "", "Frånkopplingsfördröjning värme", "opóźnienie włączania ogrzewania", "", "", "", "", "Oneskorenie vypnutia kúrenia", "zpoždění vypnutí topení") // TODO translate
|
||||
MAKE_TRANSLATION(hpSetDiffPress, "hpsetdiffpress", "set differential pressure", "Pumpensolldruck", "", "VP Tryckskillnad", "różnica ciśnień", "", "", "", "", "nastaviť diferenčný tlak", "nastavení rozdílového tlaku") // TODO translate
|
||||
MAKE_TRANSLATION(hpFan, "fan", "fan", "Lüfter", "", "Fläkt", "wentylator", "", "", "", "", "ventilátor", "ventilátor") // TODO translate
|
||||
MAKE_TRANSLATION(hpShutdown, "shutdown", "shutdown", "Abschalten", "", "Stäng av", "wyłączenie", "", "", "", "", "vypnutie", "vypnutí") // TODO translate
|
||||
MAKE_TRANSLATION(pc0Flow, "pc0flow", "Flow PC0", "Durchfluss PC0", "", "Flöde värmebärarpump", "", "", "", "", "", "prietok PC0", "průtok PC0") // TODO translate
|
||||
MAKE_TRANSLATION(pc1Flow, "pc1flow", "Flow PC1", "Durchfluss PC1", "", "Flöde cirkulationspump", "", "", "", "", "", "prietok PC1", "průtok PC1") // TODO translate
|
||||
MAKE_TRANSLATION(pc1On, "pc1on", "PC1", "PC1", "", "Cirkulationspump", "", "", "", "", "", "PC1", "PC1") // TODO translate
|
||||
MAKE_TRANSLATION(pc1Rate, "pc1rate", "PC1 rate", "PC1 Rate", "", "Hastighet cirkulationspump", "", "", "", "", "", "sadzba PC1", "míra PC1") // TODO translate
|
||||
|
||||
// hybrid heatpump
|
||||
MAKE_TRANSLATION(hybridStrategy, "hybridstrategy", "hybrid control strategy", "Hybrid-Steuerungsstrategie", "Hybride strategie", "Hybrid kontrollstrategi", "strategia sterowania hybrydowego", "hybrid kontrollstrategi", "stratégie contrôle hybride", "hibrit kontrol stratejisi", "strategia comtrollo ibrido", "hybridná stratégia riadenia", "strategie hybridního řízení")
|
||||
MAKE_TRANSLATION(switchOverTemp, "switchovertemp", "outside switchover temperature", "Außentemperatur für Umschaltung", "Schakeltemperatuur buitentemperatuur", "Utomhus Omställningstemperatur", "zewnętrzna temperatura przełączania", "utendørstemp styring", "basculement par température extérieure", "geçiş için dış sıcaklık", "temperatura esterna per commutazione", "vonkajšia prepínacia teplota", "teplota přepnutí venku")
|
||||
MAKE_TRANSLATION(energyCostRatio, "energycostratio", "energy cost ratio", "Energie-/Kostenverhältnis", "Energiekostenratio", "Energi/Kostnads-förhållande", "współczynnik energia/koszt", "energi/kostnads forhold", "ratio coût énergie", "enerji maliyet oranı", "rapporto energia/costo", "pomer nákladov na energiu", "poměr nákladů na energii")
|
||||
MAKE_TRANSLATION(fossileFactor, "fossilefactor", "fossile energy factor", "Energiefaktor Fossil", "Energiefactor fossiele brandstof", "Faktor fossilenergi", "udział energii z paliw kopalnych", "energifaktor fossilenergi", "facteur énergie fossile", "fosil yakıt faktörü", "fattore energia fossile", "faktor fosílnej energie", "faktor fosilní energie")
|
||||
MAKE_TRANSLATION(electricFactor, "electricfactor", "electric energy factor", "Energiefaktor elektrisch", "Energiefactor electrisch", "Faktor elenergi", "udział energii elektrycznej", "elektrisk energifaktor", "facteur énergie électrique", "elektrik enerjisi faktörü", "fattore energia elettrica", "faktor elektrickej energie", "faktor elektrické energie")
|
||||
MAKE_TRANSLATION(delayBoiler, "delayboiler", "delay boiler support", "Verzögerungsoption", "Vertragingsoptie", "Fördröjningsoption", "opcja opóźnienia", "Fördörjningsoption", "option retardement chaudière", "kazan desteğini ötele", "opzione ritardo caldaia", "oneskorená podpora kotla", "zpoždění podpory kotle")
|
||||
MAKE_TRANSLATION(tempDiffBoiler, "tempdiffboiler", "temp diff boiler support", "Temperaturdifferenzoption", "Verschiltemperatuuroptie", "Temperaturskillnadsoption", "opcja różnicy temperatur", "temperatursforskjell kjele", "option différence température", "sıcaklık farkı kazan desteği", "opzione differenza temperatura", "možnosť rozdielu teplôt", "teplotní rozdíl pro podporu kotle")
|
||||
MAKE_TRANSLATION(lowNoiseMode, "lownoisemode", "low noise mode", "Geräuscharmer Betrieb", "Stil bedrijf", "Tyst drift", "tryb cichy", "stillemodus", "mode faible bruit", "düşük ses modu", "modalità a basso rumore", "režim nízkej hlučnosti", "režim nízkého hluku")
|
||||
MAKE_TRANSLATION(lowNoiseStart, "lownoisestart", "low noise starttime", "Start geräuscharmer Betrieb", "Start stil bedrijf", "Tyst drift starttid", "początek trybu cichego", "stille modu starttid", "heure démarrage faible bruit", "düşük ses başlangıç", "ora di avvio a basso rumore", "nízka hlučnosť spustenia", "čas začátku nízkého hluku")
|
||||
MAKE_TRANSLATION(lowNoiseStop, "lownoisestop", "low noise stoptime", "Stopp geräuscharmer Betrieb", "Stop stil bedrijf", "Tyst drift stopptid", "koniec trybu cichego", "stille modus stopptid", "heure arrêt faible bruit", "düşük ses bitiş", "ora di arresto funzionamento silenzioso", "doba zastavenia s nízkou hlučnosťou", "čas konce nízkého hluku")
|
||||
MAKE_TRANSLATION(energyPriceGas, "energypricegas", "energy price gas", "Energiepreis Gas", "Energieprijs gas", "Gaspris", "cena energii z gazu", "energipris gass", "prix énergie gaz", "gaz enerjisi fiyatı", "prezzo energia gas", "cena energie plyn", "cena energie plyn")
|
||||
MAKE_TRANSLATION(energyPriceEl, "energypriceel", "energy price electric", "Energiepreis Eletrizität", "energieprijs electriciteit", "Elpris", "cena energii elektrycznej", "strømpris", "prix énergie électrique", "elektrik enerjisi fiyatı", "prezzo energia elettrica", "cena elektrickej energie", "cena energie elektřina")
|
||||
MAKE_TRANSLATION(energyPricePV, "energyfeedpv", "feed in PV", "PV-Einspeisevergütung", "PV teruglevertarief", "PV Energi", "cena energii PV", "strømpris PV", "alimentation PV", "giren güneş enerjisi", "energia fotovoltaico", "Výkupná cena FV", "přetok FV")
|
||||
MAKE_TRANSLATION(hybridDHW, "hybriddhw", "hybrid DHW", "Hybrid-Warmwasser", "Hybride ww", "Hybridläge varmvatten", "hybrydowa c.w.u.", "hybridmodus varmtvann", "ecs hybride", "hibrit SKS", "ACS ibrida", "hybridná TÚV", "hybridní TUV")
|
||||
MAKE_TRANSLATION(airPurgeMode, "airpurgemode", "air purge mode", "Luftspülung", "Luchtzuivering", "Luftreningsläge", "tryb oczyszczania powietrza", "luftsrensningsmodus", "mode purge air", "hava temizleme modu", "modalita spurgo aria", "režim čistenia vzduchu", "režim odvzdušnění")
|
||||
MAKE_TRANSLATION(heatPumpOutput, "heatpumpoutput", "heatpump output", "WP-Leistung", "WP output", "Värmepumpseffekt", "moc wyjściowa pompy ciepła", "varmepumpeeffekt", "sortie pompe à chaleur", "ısı pompası çıkışı", "prestazione pompa calore", "Výkon tepelného čerpadla", "výstup tepelného čerpadla")
|
||||
MAKE_TRANSLATION(coolingCircuit, "coolingcircuit", "cooling circuit", "Kühlkreislauf", "Koelcircuit", "Kylkrets", "obwód chłodzący", "kjølekrets", "circuit refroidissement", "soğutma devresi", "circuito raffreddante", "chladiaci okruh", "chlazení okruhu")
|
||||
MAKE_TRANSLATION(compStartMod, "compstartmod", "compressor start modulation", "Kompressorstartmodulation", "Beginvermogen compressor", "Kompressor startmodulering", "początkowa modulacja sprężarki", "kompressor startmodulering", "modulation démarrage compresseur", "kazan başlangıç modülasyonu", "avvio modulazione compressore", "modulácia štartu kompresora", "modulace startu kompresoru")
|
||||
MAKE_TRANSLATION(heatDrainPan, "heatdrainpan", "heat drain pan", "Wärmeausgleichsgefäß", "Vereffeningsvat", "Uppvärm. dränering", "zbiornik wyrównawczy ciepła", "oppvarming drenering", "bac récupération chaleur", "ısı tahliye tablası", "serbatoio scarico condensa", "odkvapkávacia nádoba na teplo", "ohřev odtokové pánve")
|
||||
MAKE_TRANSLATION(heatCable, "heatcable", "heating cable", "Heizband", "heating cable", "värmekabel", "przewód grzejny", "varmekabel", "câble chauffant", "ısıtma kablosu", "cavo riscaldante", "vykurovací kábel", "topný kabel")
|
||||
|
||||
// alternative heatsource AM200
|
||||
MAKE_TRANSLATION(aCylTopTemp, "cyltoptemp", "cylinder top temperature", "Speichertemperatur Oben", "Buffer temperatuur boven", "Cylindertemperatur Toppen", "temperatura na górze cylindra", "beredertemperatur topp", "température haut cylindre", "silindir üst yüzey sıcaklığı", "temperatura superiore accumulo", "vrchná teplota valca", "teplota horní části zásobníku")
|
||||
MAKE_TRANSLATION(aCylCenterTemp, "cylcentertemp", "cylinder center temperature", "Speichertemperatur Mitte", "Buffer temperatuur midden", "Cylindertemperatur Mitten", "temperatura na środku cylindra", "beredertemperatur midten", "température centre cylindre", "silindir merkez sıcaklığı", "temperatura centrale accumulo", "stredná teplota valca", "teplota středu zásobníku")
|
||||
MAKE_TRANSLATION(aCylBottomTemp, "cylbottomtemp", "cylinder bottom temperature", "Speichertemperatur Unten", "Buffer temperatuur onder", "Cylindertemperatur Botten", "temperatura na dole cylindra", "beredertemperatur nederst", "température fond cylindre", "silindir taban sıcaklığı", "temperatura inferiore accumulo", "teplota dna valca", "teplota dolní části zásobníku")
|
||||
MAKE_TRANSLATION(aFlowTemp, "altflowtemp", "alternative hs flow temperature", "Alternative WE-Vorlauftemperatur", "Alternatieve warmtebron aanvoertemperatuur", "Alternativ flödestemp värmekälla", "temperatura zasilania z alternatywnego źródła", "alternativ varmekilde tilførselstemperatur", "température flux hs alternative", "alternatif ısı kaynağı besleme sıcaklığı", "temperatura alternativa mandata hs", "alternatívna výstupná teplota hs", "alternativní výstupný teplota hs")
|
||||
MAKE_TRANSLATION(aRetTemp, "altrettemp", "alternative hs return temperature", "Alternative WE-Rücklauftemperatur", "Alternatieve warmtebron retourtemperatuur", "Alternativ returtemp värmekälla", "temperatura powrotu z alternatywnego źródła", "alternativ varmekilde returtemperatur", "température retour hs alternative", "alternatif ısı kaynağı dönüş sıcaklığı", "temperatura alternativa ritorno hs", "alternatívna teplota spiatočky hs", "alternatívna teplota zpátečky hs")
|
||||
MAKE_TRANSLATION(sysFlowTemp, "sysflowtemp", "system flow temperature", "Systemvorlauftemperatur", "Systeem aanvoertemperatuur", "Systemflödestemperatur", "temperatura zasilania systemu", "systemturtemperatur", "température flux système", "sistem besleme sıcaklığı", "temperatura di mandata impianto", "teplota prívodu systému", "teplota přívodu systému")
|
||||
MAKE_TRANSLATION(sysRetTemp, "sysrettemp", "system return temperature", "Systemrücklauftemperatur", "Systeem retourtemperatuur", "Systemreturtemperatur", "temperatura powrotu z systemu", "systemreturtemperatur", "température retour système", "sistem dönüş sıcaklığı", "temperatura di ritorno impianto", "teplota spiatočky systému", "teplota zpátečky systému")
|
||||
MAKE_TRANSLATION(valveByPass, "valvebypass", "bypass valve", "Bypassventil", "Bypass klep", "Bypassventil", "zawór obejścia", "bypassventil", "vanne dérivation", "baypas vanası", "valvola Bypass", "obtokový ventil", "bypassový ventil")
|
||||
MAKE_TRANSLATION(valveBuffer, "valvebuffer", "buffer valve", "Pufferventil", "Bufferklep", "Buffertventil", "zawór bufora", "buffertventil", "vanne tampon", "tampon vanası", "valvola tampone", "nárazový ventil", "ventil vyrovnávací nádrže")
|
||||
MAKE_TRANSLATION(valveReturn, "valvereturn", "return valve", "Rückflussventil", "Retourklep", "Returventil", "zawór powrotu", "returventil", "vanne retour", "dönüş vanası", "valvola ritorno", "spätný ventil", "návratový ventil")
|
||||
MAKE_TRANSLATION(aPumpMod, "apumpmod", "alternative hs pump modulation", "Alternative WE-Pumpenmodulation", "Alternatieve warmtebron pomp modulatie", "Alternativ Pumpmodulering Värmekälla", "modulacja pompy alternatywnego źródła ciepła", "alternativ pumpemodulering varmekilde", "modulation alternative pompe hs", "alternatif ısı kaynağı pompa modülasyonu", "pompa modulazione alternativa hs", "alternatívna modulácia čerpadla hs", "alternativní modulace čerpadla hs")
|
||||
MAKE_TRANSLATION(heatSource, "heatsource", "alternative heating active", "Alternativer Wärmeerzeuger aktiv", "Alternatieve warmtebron aktief", "Alternativ Värmekälla aktiv", "aktywne alternatywne źródło ciepła", "alternativ varmekilde aktiv", "chauffage alternatif actif", "alternatif ısınma devrede", "riscaldamento alternativo attivo", "alternatívne kúrenie aktívne", "alternativní vytápění aktivní")
|
||||
MAKE_TRANSLATION(aPump, "apump", "alternative hs pump", "Alternative WE-Pumpe", "Alternatieve warmtebron pomp", "Alternativ Pump Värmekälla", "pompy alternatywnego źródła ciepła", "alternativ pumpe varmekilde", "alternative pompe hs", "alternatif ısı kaynağı pompası", "pompa alternativa hs", "alternatívne čerpadlo hs", "alternativní čerpadlo hs")
|
||||
MAKE_TRANSLATION(burner, "burner", "burner", "Brenner", "Brander", "Brännare", "palnik", "", "", "kazan", "bruciatore", "horák", "hořák") // TODO translate
|
||||
MAKE_TRANSLATION(heatRequest, "heatrequest", "heat request", "Wärmeanforderung", "Warmtevraag", "Värmebehov", "zapotrzebowanie na ciepło", "varmeforespørsel", "", "ısı talebi", "richiesta calore", "požiadavka na teplo", "požadavek na teplo") // TODO translate
|
||||
MAKE_TRANSLATION(blockRemain, "blockremain", "remaining blocktime", "verbleibende Sperrzeit", "Resterende bloktijd", "Återstående blockeringstid", "czas do końca blokady", "gjenstående blokkeringstid", "", "kalan blok süresi", "tempo di blocco rimanente", "zostávajúci čas blokovania", "zbývající doba blokace") // TODO translate
|
||||
MAKE_TRANSLATION(blockRemainWw, "blockremaindhw", "remaining blocktime dhw", "verbleibende Sperrzeit WW", "Resterende bloktijd ww", "Återstående blockeringstid varmvatten", "czas do końca blokady c.w.u.", "gjenværende blokkeringstid bereder", "", "kalan sıcak kullanım suyu blok süresi", "tempo di blocco rimanente ACS", "zostávajúci čas blokovania TÚV", "zbývající doba blokace TUV") // TODO translate
|
||||
MAKE_TRANSLATION(flueGasTemp, "fluegastemp", "flue gas temperature", "Abgastemperatur", "Rookafvoertemperatuur", "Rökgastemperatur", "temperatura spalin", "røykgasstemperatur", "", "baca gazı sıcaklığı", "temperatura gas di scarico", "teplota spalín", "teplota spalin") // TODO translate
|
||||
|
||||
MAKE_TRANSLATION(vr2Config, "vr2config", "vr2 configuration", "VR2 Konfiguration", "VR2 configuratie", "VR2 Konfiguration", "konfiguracja VR2", "vr2 konfigurasjon", "configuration vr2", "vr2 ayarı", "configurazione VR2", "konfigurácia vr2", "konfigurace VR2")
|
||||
MAKE_TRANSLATION(ahsActivated, "ahsactivated", "alternate heat source activation", "Alt. Wärmeerzeuger aktiviert", "Altenatieve warmtebron geactiveerd", "Alternativ värmekälla aktivering", "aktywacja alternatywnego źródła ciepła", "alternativ varmekilde aktivering", "activation source chaleur alternative", "alternatif ısı kaynağı devrede", "attivazione fonte di calore alternativa", "aktivácia alternatívneho zdroja tepla", "aktivace alternativního zdroje tepla")
|
||||
MAKE_TRANSLATION(aPumpConfig, "apumpconfig", "primary pump config", "Konfig. Hauptpumpe", "Primaire pomp configuratie", "Konfiguration Primärpump", "konfiguracja pompy głównej", "konfiguration primærpumpe", "configuration pompe primaire", "ana pompa ayarı", "configurazione pompa primaria", "konfigurácia primárneho čerpadla", "konfigurace primárního čerpadla")
|
||||
MAKE_TRANSLATION(aPumpSignal, "apumpsignal", "output for pr1 pump", "Ausgang Pumpe PR1", "Output voor pomp PR1", "Utgång från pump PR1", "wyjście pompy PR1", "utgang fra pumpe PR1", "sortie pompe pr1", "p1 pompa çıkışı", "uscita per pompa PR1", "výstup pre čerpadlo pr1", "výstup pro čerpadlo PR1")
|
||||
MAKE_TRANSLATION(aPumpMin, "apumpmin", "min output pump pr1", "Minimale Pumpenansteuerung PR1", "Minimale output pomp PR1", "Min Output Pump PR1", "minimalne wysterowanie pompy PR1", "minimal output pumpe PR1", "sortie min pompe pr1", "p1 pompa minimum çıkış", "uscita minima pompa PR1", "min. výstupné čerpadlo pr1", "minimální výstup čerpadla PR1")
|
||||
MAKE_TRANSLATION(tempRise, "temprise", "ahs return temp rise", "Rücklauftemperaturerhöhung", "Verhoging retourtemperatuur", "Förhöjd returtemperatur", "wzrost temperatury powrotu", "forhøyd returtemperatur", "augmentation température retour ahs", "alternatif ısı kaynağı dönüş sıcaklığı yükseldi", "aumento della temperatura di ritorno", "Zvýšenie teploty spiatočky", "zvýšení teploty zpátečky")
|
||||
MAKE_TRANSLATION(setReturnTemp, "setreturntemp", "set temp return", "Soll-Rücklauftemperatur", "Streeftemperatuur retour", "Vald returtemperatur", "zadana temperatura powrotu", "valgt returtemperatur", "régler température retour", "hedef dönüş sıcaklığı", "imposta temperatura di ritorno", "cieľová teplota spiatočky", "nastavení teploty zpátečky")
|
||||
MAKE_TRANSLATION(mixRuntime, "mixruntime", "mixer run time", "Mischerlaufzeit", "Mixer looptijd", "Blandningsventil drifttid", "czas pracy miksera", "blandingsventil drifttid", "durée fonctionnement mélangeur", "karışım çalışma süresi", "tempo di funzionamento del miscelatore", "doba chodu mixéra", "doba běhu mixéru")
|
||||
MAKE_TRANSLATION(bufBypass, "bufbypass", "buffer bypass config", "Puffer-Bypass-Konfig.", "Buffer bypass configuratie", "Konfiguration Buffer bypass", "konfiguracja z obejściem bufora", "konfigurasjon buffer bypass", "configuration contournement buffer", "tampon baypas ayarı", "configurazione bypass del tampone", "konfigurácia vynechania vyrovnávacej pamäte", "konfigurace vyrovnávacího bypassu")
|
||||
MAKE_TRANSLATION(bufMixRuntime, "bufmixruntime", "bypass mixer run time", "Speicher-Mischer-Laufzeit", "Buffer mixer looptijd", "Blandningsventil Bypass drifttid", "czas pracy mieszacza obejścia", "blandningsventil bypass drifttid", "durée fonctionnement contournement mélangeur", "baypas karıştırıcı çalışma süresi", "tempo funzionamento bypass miscelatore", "doba chodu obtokového mixéra", "doba běhu bypassového mixéru")
|
||||
MAKE_TRANSLATION(bufConfig, "bufconfig", "dhw buffer config", "Konfig. Warmwasserspeicher", "Warmwater boiler configuratie", "Konfiguration Varmvattentank", "konfiguracja bufora c.w.u.", "konfigurasjon varmvannstank", "configuration buffer ecs", "sıcak su tampon ayarı", "configurazione tampone ACS", "konfigurácia zásobníka TÚV", "konfigurace vyrovnávacího zásobníku TUV")
|
||||
MAKE_TRANSLATION(blockMode, "blockmode", "config htg. blocking mode", "Konfig. Sperrmodus", "Configuratie blokeermodus", "Konfiguration Blockeringsläge", "konfiguracja trybu blokady", "konfigurasjon blokkeringsmodus", "config mode blocage htg.", "blok modu yapılandırması", "configurazione modalità di blocco", "Režim uzamknutia konfigurácie", "konfigurace blokovacího režimu vytápění")
|
||||
MAKE_TRANSLATION(blockTerm, "blockterm", "config of block terminal", "Konfig. Sperrterminal", "Configuratie blookerterminal", "Konfiguration Blockeringsterminal", "konfiguracja terminala blokującego", "konfigurasjon blokkeringsterminal", "config. du bloque terminal", "blok terminal yapılandırması", "configurazione terminale di blocco", "Konfiguračný blokovací terminál", "konfigurace blokovacího terminálu")
|
||||
MAKE_TRANSLATION(blockHyst, "blockhyst", "hyst. for boiler block", "Hysterese Sperrmodus", "Hysterese blokeerterminal", "Hysteres Blockeringsmodul", "tryb blokowania histerezy", "hystrese blokkeringsmodus", "hyst. Blocage chaudière", "kazan blok geçikmesi", "modalità blocco isteresi", "Režim hysterézneho zámku", "hystereze pro blokaci kotle")
|
||||
MAKE_TRANSLATION(releaseWait, "releasewait", "boiler release wait time", "Wartezeit Kesselfreigabe", "Wachttijd ketel vrijgave", "Väntetid Frisläppning", "czas oczekiwania na zwolnienie kotła", "kjele frigjøringsventetid", "temps attente libération chaudière", "kazan tahliyesi bekleme süresi", "tempo di attesa sblocco caldaia", "doba čakania na uvoľnenie kotla", "doba čekání na uvolnění kotle")
|
||||
|
||||
// energy
|
||||
MAKE_TRANSLATION(nrgTotal, "nrgtotal", "total energy", "Gesamtenergie", "", "Avgiven energi totalt", "całkowita energia", "", "", "", "", "celková energia", "celková energie") // TODO translate
|
||||
MAKE_TRANSLATION(nrgHeat, "nrgheat", "energy heating", "Energie Heizen", "", "Avgiven energi värme", "energia na ogrzewanie", "", "", "ısıtma enerjisi", "energia vykurovania", "energetické vykurovanie", "energie pro vytápění") // TODO translate
|
||||
MAKE_TRANSLATION(nrgCool, "nrgcool", "energy cooling", "Energie Kühlen", "", "Avgiven energi kyla", "", "", "", "", "", "energia chladenia", "energie pro chlazení") // TODO translate
|
||||
MAKE_TRANSLATION(nrgWw, "nrg", "energy", "Energie", "", "Avgiven energi varmvatten", "energia", "", "", "sıcak kullanım suyu enerjisi", "", "energia", "energie") // TODO translate
|
||||
MAKE_TRANSLATION(nrgHeat2, "nrgheat2", "energy heating 2", "Energie Heizen 2", "", "Avgiven energi värme 2", "energia na ogrzewanie 2", "", "", "ısıtma enerjisi 2", "", "energia vykurovania 2", "energie pro vytápění 2") // TODO translate
|
||||
MAKE_TRANSLATION(nrgWw2, "nrg2", "energy 2", "Energie 2", "", "", "energia 2", "", "Avgiven energi varmvatten 2", "sıcak kullanım suyu enerjisi 2", "", "energia 2", "energie 2") // TODO translate
|
||||
MAKE_TRANSLATION(nomPower, "nompower", "nominal Power", "Brennerleistung", "", "Nominell effekt", "moc nominalna", "", "", "nominal güç", "", "nominálny výkon", "nominální výkon") // TODO translate
|
||||
MAKE_TRANSLATION(meterTotal, "metertotal", "meter total", "Gesamtmessung", "", "Förbrukad energi totalt", "licznik całkowity", "", "", "", "", "počítadlo celkom", "počítadlo celkem") // TODO translate
|
||||
MAKE_TRANSLATION(meterComp, "metercomp", "meter compressor", "Messung Kompressor", "", "Förbrukad energi kompressor", "licznik sprężarki", "", "", "", "", "počítadlo kompresor", "počítadlo kompresoru") // TODO translate
|
||||
MAKE_TRANSLATION(meterEHeat, "metereheat", "meter e-heater", "Messung E-Heizer", "", "Förbrukad energi eltillskott", "licznik dogrzewacza", "", "", "", "", "počítadlo e-ohrievača", "počítadlo elektrického topení") // TODO translate
|
||||
MAKE_TRANSLATION(meterHeat, "meterheat", "meter heating", "Messung Heizen", "", "Förbrukad energi värme", "licznik ogrzewania", "", "", "", "", "počítadlo kúrenia", "počítadlo vytápění") // TODO translate
|
||||
MAKE_TRANSLATION(meterCool, "metercool", "meter cooling", "Messung Kühlen", "", "Förbrukad energi kyla", "", "", "", "", "", "počítadlo chladenia", "počítadlo chlazení") // TODO translate
|
||||
MAKE_TRANSLATION(meterWw, "meter", "meter", "Messung", "", "", "licznik", "", "Förbrukad energi varmvatten", "", "", "počítadlo", "počítadlo") // TODO translate
|
||||
MAKE_TRANSLATION(gasMeterHeat, "gasmeterheat", "gas meter heating", "Gaszähler Heizen", "", "Gasförbrukning värme", "licznik gazu na ogrzewanie", "", "", "", "", "počítadlo plynu kúrenia", "počítadlo plynu pro vytápění") // TODO translate
|
||||
MAKE_TRANSLATION(gasMeterWw, "gasmeter", "gas meter", "Gaszähler", "", "Gasförbrukning varmvatten", "licznik gazu", "", "", "", "", "počítadlo plynu", "počítadlo plynu") // TODO translate
|
||||
MAKE_TRANSLATION(hpCurrPower, "hpcurrpower", "compressor current power", "akt. Kompressorleistung", "", "Kompressoreffekt", "", "", "", "", "", "aktuálny výkon kompresoru", "aktuální výkon kompresoru") // TODO translate
|
||||
MAKE_TRANSLATION(hpPowerLimit, "hppowerlimit", "power limit", "Leistungsgrenze", "", "Begränsning kompressoreffekt", "", "", "", "", "", "obmedzenie výkonu", "omezení výkonu") // TODO translate
|
||||
MAKE_TRANSLATION(powerReduction, "powerreduction", "power reduction", "Leistungsverringerung", "", "Reducerad kompressoreffekt", "", "", "", "", "", "obmedzenie výkonu", "omezení výkonu") // TODO translate
|
||||
MAKE_TRANSLATION(fuelHeat, "fuelheat", "fuel consumption heating", "Verbrauch Heizen", "", "Bränsleförbrukning värme", "", "", "", "", "", "obmedzenie výkonu", "omezení výkonu") // TODO translate
|
||||
MAKE_TRANSLATION(fuelDhw, "fueldhw", "fuel consumption", "Verbrauch", "", "Bränsleförbrukning varmvatten", "", "", "", "", "", "spotreba paliva", "") // TODO translate
|
||||
MAKE_TRANSLATION(elHeat, "elheat", "el. consumption heating", "el. Verbrauch Heizen", "", "Elförbrukning värme", "", "", "", "", "", "el. spotreba kúrenie", "") // TODO translate
|
||||
MAKE_TRANSLATION(elDhw, "eldhw", "el. consumption", "el. Verbrauch", "", "Elförbrukning varmvatten", "", "", "", "", "", "el. spotreba", "") // TODO translate
|
||||
MAKE_TRANSLATION(elGenHeat, "elgenheat", "el. generation heating", "el. Erzeugung Heizen", "", "Elgenerering värme", "", "", "", "", "", "el. generovanie kúrenia", "") // TODO translate
|
||||
MAKE_TRANSLATION(elGenDhw, "elgendhw", "el generation", "el. Erzeugung", "", "Elgenerering varmvatten", "", "", "", "", "", "el. generovanie", "") // TODO translate
|
||||
|
||||
// HIU
|
||||
MAKE_TRANSLATION(netFlowTemp, "netflowtemp", "heat network flow temp", "Systemvorlauftemperatur", "Netto aanvoertemperatuur", "Temperatur fjärrvärmenät", "temp. zasilania sieci cieplnej", "", "", "ısıtma şebekesi akış derecesi", "temperatura di mandata della rete di riscaldamento", "teplota prívodu tepelnej siete", "teplota přívodu tepelné sítě") // TODO translate
|
||||
// MAKE_TRANSLATION(cwFlowRate, "cwflowrate", "cold water flow rate", "Kaltwasser Durchfluss", "Stroomsnelheid koud water", "Kallvattensflöde", "przepływ zimnej wody", "", "", "soğuk su akış hızı", "portata acqua fredda", "prietok studenej vody", "průtok studené vody") // TODO translate
|
||||
MAKE_TRANSLATION(keepWarmTemp, "keepwarmtemp", "keep warm temperature", "Warmhaltetemperatur", "Warmhoudtemperatuur", "", "Varmhållningstemperatur", "temperatura podtrzymywania ciepła", "", "sıcaklığı koruma derecesi", "mantenere la temperatura calda", "udržať teplú teplotu", "udržovací teplota") // TODO translate
|
||||
MAKE_TRANSLATION(heatValve, "heatvalve", "heating valve", "Heizungsventil", "", "Ventil uppvärmning", "zawór ogrzewania", "", "", "", "", "vykurovací ventil", "ventil pro vytápění") // TODO translate
|
||||
MAKE_TRANSLATION(wwValve, "dhwvalve", "valve", "Ventil", "", "Ventil varmvatten", "zawór", "", "", "", "", "ventil", "ventil") // TODO translate
|
||||
|
||||
// the following are dhw for the boiler and automatically tagged with 'dhw'
|
||||
MAKE_TRANSLATION(wwSelTemp, "seltemp", "selected temperature", "gewählte Temperatur", "Geselecteerd temperatuur", "Vald Temperatur", "temperatura wyższa/komfort", "valgt temperatur", "température sélectionnée", "seçili sıcaklık", "temperatura selezionata", "zvolená teplota", "nastavená teplota")
|
||||
MAKE_TRANSLATION(wwSelTempLow, "seltemplow", "selected lower temperature", "ausgewählte untere Temperatur", "Onderste streeftemperatuur", "Vald lägstatemperatur", "temperatura niższa/eko", "valgt nedre temperatur", "température basse sélectionnée", "seçili düşük sıcaklık", "bassa temperatura selezionata", "zvolená nižšia teplota", "redukovaná teplota")
|
||||
MAKE_TRANSLATION(wwSelTempEco, "tempecoplus", "selected eco+ temperature", "ausgewählte ECO+ Temperatur", "eco+ streeftemperatuur", "Eco+ lägstatemperatur", "temperatura niższa/eko+", "valgt eco+ temperatur", "température eco+ sélectionnée", "seçili eco+ sıcaklık", "eco+ temperatura selezionata", "zvolená teplota eco+", "Eco+ teplota")
|
||||
MAKE_TRANSLATION(wwSelTempOff, "seltempoff", "selected temperature for off", "ausgewählte Temperatur bei AUS", "Streeftemperatuur bij UIT", "Vald temperatur för AV", "temperatura gdy grzanie wyłączone", "valgt tempereatur for av", "température sélectionnée pour arrêt", "kapanma için seçili sıcaklık", "temperatura selezionata per spegnimento", "zvolená teplota pre vypnutie", "teplota pro vypnutí")
|
||||
MAKE_TRANSLATION(wwOneTime, "onetime", "one time charging", "Einmalladung", "Buffer eenmalig laden", "Engångsladdning", "jednorazowa dodatkowa ciepła woda", "engangsoppvarming", "charge unique", "tek seferlik doldurma", "carica singola", "jednorazové nabíjanie", "jednorázové nabíjení")
|
||||
MAKE_TRANSLATION(wwSelTempSingle, "seltempsingle", "single charge temperature", "Einmalladungstemperatur", "Streeftemperatuur enkele lading", "Temperatur Engångsladdning", "temperatura dodatkowej ciepłej wody", "temp engangsoppvarming", "température charge unique", "tek şarj sıcaklığı", "temperatura singolaa carica", "teplota na jedno nabitie", "teplota jednorázového nabíjení")
|
||||
MAKE_TRANSLATION(wwOneTimeKey, "onetimekey", "one time key function", "Einmalladungstaste", "Knop voor eenmalig laden buffer", "Engångsfunktion", "przycisk jednorazowego ogrzania", "engangsknapp varme", "fonction touche unique", "tek seferlik doldurma fonksiyonu", "pulsante funzione singola", "jednorazová kľúčová funkcia", "jednorázová funkce klíče")
|
||||
MAKE_TRANSLATION(wwCylMiddleTemp, "cylmiddletemp", "cylinder middle temperature (TS3)", "Speichertemperatur Mitte", "Buffer temperatuur midden", "Cylinder Temperatur Mitten (TS3)", "temperatura środka cylindra (TS3)", "vanntank midten temperatur (TS3)", "température moyenne ballon (TS3)", "Silindir orta sıcaklığı", "temperatura centrale accumulo (TS3)", "stredná teplota valca (TS3)", "teplota středu zásobníku (TS3)")
|
||||
MAKE_TRANSLATION(wwSetTemp, "settemp", "set temperature", "Solltemperatur", "Streeftemperatuut", "Börtemperatur", "temperatura zadana", "innstilt temperatur", "régler température", "hedef sıcaklık", "imposta temperatura", "nastavená teplota", "nastavená teplota")
|
||||
MAKE_TRANSLATION(wwType, "type", "type", "Typ", "type", "Typ", "typ", "type", "type", "tip", "tipo", "typ", "typ")
|
||||
MAKE_TRANSLATION(wwComfort, "comfort", "comfort", "Komfort", "Comfort", "Komfort", "komfort", "komfort", "confort", "konfor", "Comfort", "komfort", "komfort")
|
||||
MAKE_TRANSLATION(wwComfort1, "comfort1", "comfort mode", "Komfort-Modus", "Comfort modus", "Komfortläge", "tryb komfortu", "komfort modus", "mode confort", "konfor modu", "modalità comfort", "komfortný režim", "komfortní režim")
|
||||
MAKE_TRANSLATION(wwFlowTempOffset, "flowtempoffset", "flow temperature offset", "Anhebung Vorlauftemperatur", "Aanvoertemperatuur offset", "Flödestemperatur förskjutning", "korekta temperatury wypływu", "turtemperaturforskyvning", "offset température flux", "akış sıcaklığı artışı", "aumento della temperatura di ritorno", "Posun teploty prívodu", "kompenzace teploty přívodu")
|
||||
MAKE_TRANSLATION(wwMaxPower, "maxpower", "max power", "max. Leistung", "Maximaal vermogen", "Max. Effekt", "moc. maksymalna", "maks. effekt", "puissance max.", "maksimum güç", "potenza massima", "maximálny výkon", "maximální výkon")
|
||||
MAKE_TRANSLATION(wwCircPump, "circpump", "circulation pump available", "Zirkulationspumpe vorhanden", "Circulatiepomp aanwezig", "Cirkulationspump tillgänglig", "pompa cyrkulacji zainstalowana", "sirkulasjonspumpe tilgjengelig", "pompe circulation disponible", "sikülasyon pompası müsait", "pompa circolazione disponibile", "dostupné obehové čerpadlo", "oběhové čerpadlo k dispozici")
|
||||
MAKE_TRANSLATION(wwChargeType, "chargetype", "charging type", "Speicherladungstyp", "Buffer laadtype", "Laddningstyp", "sposób grzania zasobnika", "varmetype", "type chargement", "şarj tipi", "tipo caricamento", "typ nabíjania", "typ nabíjení")
|
||||
MAKE_TRANSLATION(wwCircMode, "circmode", "circulation pump mode", "Zirkulationspumpenmodus", "Modus circulatiepomp", "Läge Cirkulationspump", "tryb pracy cyrkulacji", "sikulasjonspumpemodus", "mode pompe circulation", "sirkülasyon pompa modu", "modalità pompa circolazione", "režim obehového čerpadla", "režim oběhového čerpadla")
|
||||
MAKE_TRANSLATION(wwCirc, "circ", "circulation active", "Zirkulation aktiv", "Circulatiepomp actief", "Cirkulation aktiv", "pompa cyrkulacji", "sirkulasjon aktiv", "circulation active", "sirkülasyon devrede", "circolazione attiva", "obeh aktívny", "oběh aktivní")
|
||||
MAKE_TRANSLATION(wwCurTemp, "curtemp", "current intern temperature", "aktuelle interne Temperatur", "Huidige interne temperatuur", "Intern Temperatur", "temperatura zasobnika", "gjeldende intern temperatur", "température interne actuelle", "güncel iç sıcaklık", "temperatura interna attuale", "aktuálna vnútorná teplota", "aktuální vnitřní teplota")
|
||||
MAKE_TRANSLATION(wwCurTemp2, "curtemp2", "current extern temperature", "aktuelle externe Temperatur", "Huidige externe temperatuur", "Extern Temperatur", "temperatura wypływu", "gjeldende ekstern temperaur", "température externe actuelle", "güncel dış sıcaklık", "temperatura esterna attuale", "aktuálna vonkajšia teplota", "aktuální venkovní teplota")
|
||||
MAKE_TRANSLATION(wwCurFlow, "curflow", "current tap water flow", "aktueller Durchfluss", "Hudige warmwater doorstroming", "Aktuellt tappvattenflöde", "aktualny przepływ", "gjeldende tappevannshastighet", "débit actuel eau robinet", "güncel musluk suyu akışı", "portata corrente dell'acqua del rubinetto", "aktuálny prietok vody z vodovodu", "aktuální průtok TUV")
|
||||
MAKE_TRANSLATION(wwStorageTemp1, "storagetemp1", "storage intern temperature", "interne Speichertemperatur", "Interne buffertemperatuur", "Beredare Intern Temperatur", "temperatura wewnątrz zasobnika", "intern temperatur bereder", "température interne stockage", "depo iç sıcaklığı", "temperatura di conservazione interna", "interná teplota skladovania", "vnitřní teplota zásobníku")
|
||||
MAKE_TRANSLATION(wwStorageTemp2, "storagetemp2", "storage extern temperature", "externe Speichertemperatur", "Externe buffertemperatuur", "Beredare Extern Temperatur", "temperatura na wyjściu zasobnika", "ekstern temperatur bereder", "température externe stockage", "depo dış sıcaklığı", "temperatura di conservazione esterna", "vonkajšia teplota skladovania", "venkovní teplota zásobníku")
|
||||
MAKE_TRANSLATION(wwActivated, "activated", "activated", "aktiviert", "geactiveerd", "Aktiverad", "system przygotowywania c.w.u.", "aktivert", "activé", "devreye girdi", "attivato", "aktivovaný", "aktivováno")
|
||||
MAKE_TRANSLATION(wwDisinfecting, "disinfecting", "disinfecting", "Desinfizieren", "Desinfectie", "Desinficerar", "dezynfekcja termiczna", "desinfiserer", "désinfection", "dezenfekte ediliyor", "disinfezione", "dezinfekcia", "dezinfekce")
|
||||
MAKE_TRANSLATION(wwDisinfectionTemp, "disinfectiontemp", "disinfection temperature", "Desinfektionstemperatur", "Desinfectietemperatuur", "Desinfektionstemperatur", "temperatura dezynfekcji termicznej", "desinfeksjonstemperatur", "température désinfection", "dezenfeksiyon sıcaklığı", "temperatura disinfezione", "teplota dezinfekcie", "dezinfekční teplota")
|
||||
MAKE_TRANSLATION(wwCharging, "charging", "charging", "Laden", "Laden", "Värmer", "grzanie", "varmer", "chargement", "dolduruluyor", "caricamento", "nabíjanie", "nabíjení")
|
||||
MAKE_TRANSLATION(wwChargeOptimization, "chargeoptimization", "charge optimization", "Ladungsoptimierung", "laadoptimalisatie", "Laddningsoptimering", "optymalizacja grzania", "oppvarmingsoptimalisering", "optimisation charge", "dolum optimizasyonu", "ottimizzazione carica", "optimalizácia poplatkov", "optimalizace nabíjení")
|
||||
MAKE_TRANSLATION(wwRecharging, "recharging", "recharging", "Nachladen", "herladen", "Laddar om", "ponowne grzanie", "varm på nytt", "en recharge", "tekrar dolduruluyor", "in ricarica", "nabíjanie", "dobíjení")
|
||||
MAKE_TRANSLATION(wwTempOK, "tempok", "temperature ok", "Temperatur ok", "Temperatuur OK", "Temperatur OK", "temperatura OK", "temperatur ok!", "température ok", "sıcaklık tamam", "Temperatura OK", "teplota ok", "teplota v pořádku")
|
||||
MAKE_TRANSLATION(wwActive, "active", "active", "aktiv", "Actief", "Aktiv", "aktywna", "aktiv", "actif", "devrede", "attivo", "aktívny", "aktivní")
|
||||
MAKE_TRANSLATION(ww3wayValve, "3wayvalve", "3-way valve active", "3-Wege-Ventil aktiv", "3-wegklep actief", "Trevägsventil aktiv", "zawór 3-drogowy aktywny", "aktiv trevisventil", "vanne 3 voies active", "3 yollu vana", "valvola 3-vie", "3-cestný ventil aktívny", "aktivní 3-cestný ventil")
|
||||
MAKE_TRANSLATION(wwMixerTemp, "mixertemp", "mixer temperature", "Mischertemperatur", "Mixertemperatuur", "Blandningsventil tempertur", "temperatura mieszacza", "temperatur blandeventil", "température mélangeur", "karıştırıcı sıcaklığı", "temperatura miscelatore", "teplota mixéra", "teplota směšovače")
|
||||
MAKE_TRANSLATION(wwStarts, "starts", "starts", "Anzahl Starts", "Aantal starts", "Antal starter", "liczba załączeń", "antall starter", "démarrages", "başlıyor", "avvii", "Počet štartov", "Počet startů")
|
||||
MAKE_TRANSLATION(wwStartsHp, "startshp", "starts hp", "Anzahl Starts WP", "", "Antal starter VP", "", "", "", "", "", "Počet spustení TČ", "") // TODO translate
|
||||
MAKE_TRANSLATION(wwWorkM, "workm", "active time", "aktive Zeit", "Actieve tijd", "Aktiv Tid", "czas aktywności", "driftstid", "temps actif", "aktif zaman", "tempo attivo", "aktívny čas", "aktivní čas")
|
||||
MAKE_TRANSLATION(wwHystOn, "hyston", "hysteresis on temperature", "Einschalttemperaturdifferenz", "Inschakeltemperatuurverschil", "Hysteres PÅ-temperatur", "histereza załączania", "innkoblingstemperaturforskjell", "hystérésis température allumage", "çalışma sıcaklığı farkı", "differenza di temperatura di accensione", "hysterézia teploty", "hystereze zapnutí")
|
||||
MAKE_TRANSLATION(wwHystOff, "hystoff", "hysteresis off temperature", "Ausschalttemperaturdifferenz", "Uitschakeltemperatuurverschil", "Hysteres AV-temperatur", "histereza wyłączania", "utkoblingstemperaturforskjell", "hystérésis température extinction", "kapatma sıcaklığı farkı", "differenza di temperatura di spegnimento", "teplota hysterézie", "hystereze vypnutí")
|
||||
MAKE_TRANSLATION(wwProgMode, "progmode", "program", "Programmmodus", "Programma", "Program", "program", "program", "programme", "program", "Programma", "program", "program")
|
||||
MAKE_TRANSLATION(wwCircProg, "circprog", "circulation program", "Zirkulationsprogramm", "Circulatieprogramma", "Cirkulationsprogram", "program cyrkulacji c.w.u.", "sirkulationsprogram", "programme circulation", "sirkülasyon programı", "programma circolazione", "obehový program", "program oběhu")
|
||||
MAKE_TRANSLATION(wwMaxTemp, "maxtemp", "maximum temperature", "maximale Temperatur", "maximale temperatuur", "maximal Temperatur", "temperatura maksymalna", "maksimal temperatur", "température max", "maksimum sıcaklık", "temperatura massima", "maximálna teplota", "maximální teplota")
|
||||
MAKE_TRANSLATION(wwSolarTemp, "solartemp", "solar boiler temperature", "Solarkesseltemperatur", "Zonneboiler temperatuur", "Solpanel Temperatur", "temperatura zasobnika solarnego", "solpaneltemp", "température chaudière solaire", "güneş enerjisi kazan sıcaklığı", "temperatura pannello solare", "teplota solárneho kotla", "teplota solárního kotle")
|
||||
|
||||
// mqtt values / commands
|
||||
MAKE_TRANSLATION(switchtime, "switchtime", "program switchtime", "Programmschaltzeit", "Programma schakeltijd", "Program Bytestid", "program czasowy", "programbyttetid", "heure commutation programme", "program değiştirme süresi", "ora commutazione programmata", "čas prepnutia programu", "čas přepnutí programu")
|
||||
MAKE_TRANSLATION(switchtime1, "switchtime1", "own1 program switchtime", "Programmschaltzeit 1", "Schakeltijd programma 1", "Program 1 Bytestid", "program przełączania 1", "byttetidprogram 1", "heure de commutation programme 1", "program1 değiştirme süresi", "ora commutazione programma 1", "vlastný 1 program prepnutia", "vlastní program 1 přepnutí")
|
||||
MAKE_TRANSLATION(switchtime2, "switchtime2", "own2 program switchtime", "Programmschaltzeit 2", "Schakeltijd programma 2", "Program 2 Bytestid", "program przełączania 2", "byttetid program 2", "heure de changement programme 2", "program1 değiştirme süresi", "ora commutazione programma 2", "vlastný 2 program prepnutia", "vlastní program 2 přepnutí")
|
||||
MAKE_TRANSLATION(wwswitchtime, "switchtimeWW", "program switchtime warm water", "Programmschaltzeit Warmwasser", "Warm water programma schakeltijd", "Varmvattenprogram Bytestid", "program czasowy", "byttetid varmtvannsprogram", "heure commutation programme", "sıcak kullanıom suyu program değiştirme süresi", "Tempo di commutazione del programma", "čas prepnutia programu", "čas přepnutí program TUV")
|
||||
MAKE_TRANSLATION(wwcircswitchtime, "circswitchtime", "circulation program switchtime", "Zirculationsprogramm Schaltzeit", "Schakeltijd circulatieprogramma", "Cirkulationsprogram Bytestid", "program cyrkulacji", "byttetid sirkulasjonsprogram", "heure commutation programme circulation", "sirkülasyon program değiştirme süresi", "ora commutazione programma circolazione", "čas prepnutia cirkulačného programu", "čas přepnutí programu cirkulace")
|
||||
MAKE_TRANSLATION(dateTime, "datetime", "date/time", "Datum/Zeit", "Datum/Tijd", "Datum/Tid", "data i godzina", "dato/tid", "date/heure", "zaman/saat", "Data/Ora", "dátum/čas", "datum/čas")
|
||||
MAKE_TRANSLATION(errorCode, "errorcode", "error code", "Fehlercode", "Foutmeldingscode", "Felkod", "kod błędu", "feikode", "code erreur", "hata kodu", "codice errore", "error kód", "chybový kód")
|
||||
MAKE_TRANSLATION(ibaMainDisplay, "display", "display", "Anzeige", "Display", "Display", "wyświetlacz", "skjerm", "affichage", "ekran", "Display", "display", "displej")
|
||||
MAKE_TRANSLATION(ibaLanguage, "language", "language", "Sprache", "Taal", "Sprak", "język", "språk", "langue", "dil", "Lingua", "jazyk", "jazyk")
|
||||
MAKE_TRANSLATION(ibaClockOffset, "clockoffset", "clock offset", "Uhrkorrektur", "Klokcorrectie", "Tidskorrigering", "korekta zegara", "tidskorrigering", "offset horloge", "saat farkı", "correzione orario", "korekcia času", "posun hodin")
|
||||
MAKE_TRANSLATION(ibaBuildingType, "building", "building type", "Gebäudetyp", "Type gebouw", "Byggnadstyp", "typ budynku", "bygningstype", "type bâtiment", "bina tipi", "tipo di edificio", "typ budovy", "typ budovy")
|
||||
MAKE_TRANSLATION(heatingPID, "heatingpid", "heating PID", "Heizungs-PID", "PID verwarming", "Uppvärmning PID", "PID ogrzewania", "oppvarmings PID", "PID chauffage", "PID ısınıyor", "PID-riscaldamento", "PID kúrenia", "PID regulace vytápění")
|
||||
MAKE_TRANSLATION(ibaCalIntTemperature, "intoffset", "internal temperature offset", "Korrektur interner Temperatur", "Offset interne temperatuur", "Korrigering interntemperatur", "korekta temperatury w pomieszczeniu", "Korrigering interntemperatur", "offset température interne", "iç sıcaklık artışı", "scostamento della temperatura interna", "odchýlka vnútornej teploty", "kompenzace vnitřní teploty")
|
||||
MAKE_TRANSLATION(ibaMinExtTemperature, "minexttemp", "minimal external temperature", "Min. Außentemperatur", "Min. buitentemperatuur", "Min Extern Temperatur", "minimalna miejscowa temperatura zewnętrzna", "minimal eksterntemperatur", "température extérieure minimale", "en düşük sış sıcaklık", "temperatura esterna minima", "minimálna vonkajšia teplota", "minimální venkovní teplota")
|
||||
MAKE_TRANSLATION(backlight, "backlight", "key backlight", "Tastenbeleuchtung", "Toetsverlichting", "Bakgrundsbelysning", "podświetlenie klawiatury", "bakgrunnsbelysning", "rétroéclairage touches", "tuş takımı aydınlatması", "retroilluminazione dei tasti", "podsvietenie kláves", "podsvícení kláves")
|
||||
MAKE_TRANSLATION(damping, "damping", "damping outdoor temperature", "Dämpfung der Außentemperatur", "Demping buitentemperatuur", "Utomhustemperatur dämpning", "tłumienie temperatury zewnętrznej", "demping av utetemperatur", "température extérieure minimale", "dış sıcaklığın sönümlenmesi", "smorzamento della temperatura esterna", "tlmenie vonkajšej teploty", "tlumení venkovní teploty")
|
||||
MAKE_TRANSLATION(tempsensor1, "inttemp1", "temperature sensor 1", "Temperatursensor 1", "Temperatuursensor 1", "Temperatursensor 1", "czujnik temperatury 1", "temperatursensor 1", "sonde température 1", "sıcaklık sensörü 1", "sensore temperatura 1", "snímač teploty 1", "snímač teploty 1")
|
||||
MAKE_TRANSLATION(tempsensor2, "inttemp2", "temperature sensor 2", "Temperatursensor 2", "Temperatuursensor 2", "Temperatursensor 2", "czujnik temperatury 2", "temperatursensor 2", "capteur température 2", "sıcaklık sensörü 2", "sensore temperatura 2", "snímač teploty 2", "snímač teploty 2")
|
||||
MAKE_TRANSLATION(dampedoutdoortemp, "dampedoutdoortemp", "damped outdoor temperature", "Gedämpfte Außentemperatur", "Gedempte buitentemperatuur", "Utomhustemperatur dämpad", "tłumiona temperatura zewnętrzna", "dempet utetemperatur", "température extérieure amortie", "sönümlenmiş dış sıcaklık", "temperatura esterna smorzata", "tlmená vonkajšia teplota", "tlumená venkovní teplota")
|
||||
MAKE_TRANSLATION(floordrystatus, "floordry", "floor drying", "Estrichtrocknung", "Vloerdroogprogramma", "Golvtorkning", "suszenie jastrychu", "gulvtørkeprogram", "séchage sol", "yerden ısıtma", "asciugatura pavimento", "sušenie podlahy", "sušení podlahy")
|
||||
MAKE_TRANSLATION(floordrytemp, "floordrytemp", "floor drying temperature", "Estrichtrocknungstemperatur", "Temperatuur vloerdroogprogramma", "Golvtorkning Temperatur", "temperatura suszenia jastrychu", "gulvtørketemperatur", "température séchage sol", "yerden ısıtma sıcaklığı", "Temperatura asciugatura pavimento", "teplota sušenia podlahy", "teplota sušení podlah")
|
||||
MAKE_TRANSLATION(brightness, "brightness", "screen brightness", "Bildschirmhelligkeit", "Schermhelderheid", "Ljusstyrka", "jasność", "lysstyrke", "luminosité écran", "ekran parlaklığı", "luminosita display", "jas obrazovky", "jas obrazovky")
|
||||
MAKE_TRANSLATION(autodst, "autodst", "automatic change daylight saving time", "Automatische Sommerzeit Umstellung", "Automatische omschakeling zomer-wintertijd", "Automatisk växling sommar/vinter-tid", "automatycznie przełączaj na czas letni/zimowy", "automatisk skifte av sommer/vinter-tid", "changement automatique heure d'été", "gün ışığından yararlanma saatini otomatik olarak değiştir", "cambio automatico dell'ora legale", "automatická zmena letného času", "automatická změna letního času")
|
||||
MAKE_TRANSLATION(preheating, "preheating", "preheating in the clock program", "Vorheizen im Zeitprogramm", "Voorverwarming in het klokprogramma", "Förvärmning i tidsprogram", "podgrzewanie w programie czasowym", "forvarming i tidsprogram", "préchauffage dans programme horloge", "saat programında ön ısıtma", "preriscaldamento nel programma orologio", "predohrev v programe hodín", "předehřev v programovaném režimu")
|
||||
MAKE_TRANSLATION(offtemp, "offtemp", "temperature when mode is off", "Temperatur bei AUS", "Temperatuur bij UIT", "Temperatur Avslagen", "temperatura w trybie \"wył.\"", "temperatur avslått", "température lorsque mode désactivé", "mod kapalı iken sıcaklık", "temperatura quando la modalità è disattivata", "teplota, keď je režim vypnutý", "teplota při vypnutém režimu")
|
||||
MAKE_TRANSLATION(mixingvalves, "mixingvalves", "mixing valves", "Mischventile", "Mengkleppen", "Blandningsventiler", "zawory mieszające", "blandeventiler", "vannes mélange", "karışım vanaları", "valvole miscela", "zmiešavacie ventily", "směšovací ventily")
|
||||
MAKE_TRANSLATION(pvEnableWw, "pvenabledhw", "enable raise dhw", "aktiviere WW-Anhebung", "Verhoging WW activeren", "Höj varmvatten med solpanel", "podwyższenie c.w.u. z PV", "aktivere hevet temperatur bereder", "", "sıcak kullanım suyu yükseltmeyi etkinleştir", "abilitare aumento ACS", "povoliť zvýšenie TÚV", "povolit zvýšení TUV") // TODO translate
|
||||
MAKE_TRANSLATION(pvRaiseHeat, "pvraiseheat", "raise heating with PV", "Anhebung Heizen mit PV", "Verwarmen met PV activeren", "Höj värmen med solpanel", "podwyższenie grzania z PV", "heve varmen med solpanel", "", "ısıtmayı G.E. İle yükselt", "Aumentare il riscaldamento con il solare", "zvýšiť kúrenie s FV", "zvýšení vytápění s FV") // TODO translate
|
||||
MAKE_TRANSLATION(pvLowerCool, "pvlowercool", "lower cooling with PV", "Absenkung Kühlen mit PV", "Verlagen koeling met PV activeren", "Sänk kylning med solpanel", "obniżenie chłodzenia z PV", "nedre kjøling solpanel", "", "soğutmayı G.E. İle düşür", "Riduzione del raffreddamento con il solare", "nižšie chladenie s PV", "snížení chlazení s FV") // TODO translate
|
||||
MAKE_TRANSLATION(hasSolar, "solar", "solar", "Solar", "", "Solpaneler", "", "", "", "", "", "solár", "") // TODO translate
|
||||
|
||||
// thermostat dhw
|
||||
MAKE_TRANSLATION(wwMode, "mode", "operating mode", "Betriebsart", "Modus", "Läge", "tryb pracy", "modus", "mode", "mod", "modalità", "režim", "provozní režim")
|
||||
MAKE_TRANSLATION(wwSetTempLow, "settemplow", "set low temperature", "untere Solltemperatur", "Onderste streeftemperatuur", "Nedre Börvärde", "zadana temperatura obniżona", "nedre settverdi", "réglage température basse", "hedef düşük sıcaklık", "imposta bassa temperatura", "nastaviť nízku teplotu", "nastavit nízkou teplotu")
|
||||
MAKE_TRANSLATION(wwWhenModeOff, "whenmodeoff", "when thermostat mode off", "bei Thermostatmodus AUS", "Als Thermostaat op UIT", "när Termostatläge är AV", "gdy wyłączono na termostacie", "når modus er av", "lorsque mode thermostat off", "termostat modu kapalı olduğunda", "quando termostato modalita OFF", "keď je režim termostatu vypnutý", "při vypnutém termostatu")
|
||||
MAKE_TRANSLATION(wwExtra, "extra", "extra", "Extra", "extra", "Extra", "obieg", "ekstra", "extra", "ekstra", "extra", "extra", "extra")
|
||||
MAKE_TRANSLATION(wwCharge, "charge", "charge", "Laden", "Laden", "Ladda", "grzanie", "lade", "charge", "doldurma", "carica", "nabiť", "nabíjení")
|
||||
MAKE_TRANSLATION(wwChargeDuration, "chargeduration", "charge duration", "Ladedauer", "Laadtijd", "Laddtid", "czas grzania dodatkowej ciepłej wody", "ladetid", "durée charge", "doldurma süresi", "durata carica", "doba nabíjania", "doba nabíjení")
|
||||
MAKE_TRANSLATION(wwDisinfect, "disinfect", "disinfection", "Desinfektion", "Desinfectie", "Desinfektion", "dezynfekcja termiczna", "desinfeksjon", "désinfection", "dezenfeksiyon", "disinfezione", "dezinfekcia", "dezinfekce")
|
||||
MAKE_TRANSLATION(wwDisinfectDay, "disinfectday", "disinfection day", "Desinfektionstag", "Desinfectiedag", "Desinfektionsdag", "dzień dezynfekcji termicznej", "desinfeksjonsdag", "jour désinfection", "dezenfeksiyon günü", "giorno disinfezione", "deň dezinfekcie", "den dezinfekce")
|
||||
MAKE_TRANSLATION(wwDisinfectHour, "disinfecthour", "disinfection hour", "Desinfektionsstunde", "Desinfectieuur", "Desinfektionstimme", "godzina dezynfekcji termicznej", "desinfeksjonstime", "heure désinfection", "dezenfeksiyon saati", "ora disinfezione", "hodina dezinfekcie", "hodina dezinfekce")
|
||||
MAKE_TRANSLATION(wwDisinfectTime, "disinfecttime", "disinfection time", "Desinfektionszeit", "Desinfectietijd", "Desinfektionstid", "maksymalny czas trwania dezynfekcji termicznej", "desinfeksjonstid", "durée désinfection", "dezenfeksiyon zamanı", "orario disinfezione", "čas na dezinfekciu", "čas dezinfekce")
|
||||
MAKE_TRANSLATION(wwDailyHeating, "dailyheating", "daily heating", "täglich Heizen", "Dagelijks opwarmen", "Daglig Uppvärmning", "codzienne nagrzewanie", "daglig oppvarming", "chauffage quotidien", "günlük ısıtma", "riscaldamento giornaliero", "denné kúrenie", "denní ohřev")
|
||||
MAKE_TRANSLATION(wwDailyHeatTime, "dailyheattime", "daily heating time", "tägliche Heizzeit", "Tijd dagelijkse opwarming", "Daglig Uppvärmningstid", "czas trwania codziennego nagrzewania", "daglig oppvarmingstid", "heure chauffage quotidien", "günlük ısıtma süresi", "orario riscaldamento giornaliero", "denný čas vykurovania", "čas denního ohřevu")
|
||||
|
||||
// thermostat hc
|
||||
MAKE_TRANSLATION(selRoomTemp, "seltemp", "selected room temperature", "gewählte Raumtemperatur", "Streeftemperatuur kamer", "Vald Rumstemperatur", "zadana temperatura w pomieszczeniu", "valgt rumstemperatur", "température ambiante sélectionnée", "seçili oda sıcaklığı", "temperatura ambiente selezionata", "zvolená izbová teplota", "zvolená teplota místnosti")
|
||||
MAKE_TRANSLATION(roomTemp, "currtemp", "current room temperature", "aktuelle Raumtemperatur", "Huidige kamertemperatuur", "Aktuell Rumstemperatur", "temperatura w pomieszczeniu", "gjeldende romstemperatur", "température ambiante actuelle", "güncel oda sıcaklığı", "temperatura ambiente attuale", "aktuálna izbová teplota", "aktuální teplota místnosti")
|
||||
MAKE_TRANSLATION(mode, "mode", "operating mode", "Betriebsart", "Modus", "Läge", "sposób sterowania", "modus", "mode", "mod", "modalità", "režim", "provozní režim")
|
||||
MAKE_TRANSLATION(modetype, "modetype", "mode type", "Modustyp", "Type modus", "Typ av läge", "aktualny tryb pracy", "modusrype", "type mode", "mod tipi", "tipo di modalita", "typ režimu", "typ režimu")
|
||||
MAKE_TRANSLATION(fastheatup, "fastheatup", "fast heatup", "schnelles Aufheizen", "Snel opwarmen", "Snabb Uppvärmning", "szybkie nagrzewanie", "rask oppvarming", "chauffage rapide", "hızlı ısıtma", "riscaldamento rapido", "rýchle zahriatie", "rychlé předehřátí")
|
||||
MAKE_TRANSLATION(heatup, "heatup", "heatup", "Aufheizen", "opwarmen", "Uppvärmning", "nagrzewanie", "oppvarming", "chauffage", "hızlı", "riscaldamento", "rýchle zahriatie", "předehřev")
|
||||
MAKE_TRANSLATION(daytemp, "daytemp", "day temperature", "Tagestemperatur", "temperatuur dag", "Dagstemperatur", "temperatura w dzień", "dagtemperatur", "température jour", "gündüz sıcaklığı", "temperatura giornaliera", "denná teplota", "denní teplota")
|
||||
MAKE_TRANSLATION(daylowtemp, "daytemp2", "day temperature T2", "Tagestemperatur T2", "Temperatuur dag T2", "Dagstemperatur T2", "temperatura w dzień T2", "dagtemperatur T2", "température jour T2", "gündüz sıcaklığı T2", "temperatura giornaliera T2", "denná teplota T2", "denní teplota T2")
|
||||
MAKE_TRANSLATION(daymidtemp, "daytemp3", "day temperature T3", "Tagestemperatur T3", "Temperatuur dag T3", "Dagstemperatur T3", "temperatura w dzień T3", "dagtemperatur T3", "température jour T3", "gündüz sıcaklığı T3", "temperatura giornaliera T3", "denná teplota T3", "denní teplota T3")
|
||||
MAKE_TRANSLATION(dayhightemp, "daytemp4", "day temperature T4", "Tagestemperatur T4", "Temperatuur dag T4", "Dagstemperatur T4", "temperatura w dzień T4", "dagtemperatur T4", "température jour T4", "gündüz sıcaklığı T4", "temperatura giornaliera T4", "denná teplota T4", "denní teplota T4")
|
||||
MAKE_TRANSLATION(heattemp, "heattemp", "heat temperature", "Heiztemperatur", "Temperatuur verwarming", "Temperatur Uppvärmning", "temperatura ogrzewania", "oppvarmingstemperatur", "température chauffage", "ısıtma sıcaklığı", "temperatura riscaldamento", "teplota ohrevu", "teplota topení")
|
||||
MAKE_TRANSLATION(nighttemp, "nighttemp", "night temperature", "Nachttemperatur", "Nachttemperatuur", "Nattemperatur", "temperatura w nocy", "nattemperatur", "température de nuit", "gece sıcaklığı", "temperatura notturna", "nočná teplota", "noční teplota")
|
||||
MAKE_TRANSLATION(nighttemp2, "nighttemp", "night temperature T1", "Nachttemperatur T1", "Nachttemperatuur T1", "Nattemperatur T1", "temperatura w nocy T1", "nattemperatur T1", "température nuit T1", "gece sıcaklığı T1", "temperatura notturna T1", "nočná teplota T1", "noční teplota T1")
|
||||
MAKE_TRANSLATION(ecotemp, "ecotemp", "eco temperature", "eco Temperatur", "Temperatuur eco", "Eko-temperatur", "temperatura w trybie eko", "øko temperatur", "température éco", "eko sıcaklık", "Temperatura eco", "eko teplota", "eko teplota")
|
||||
MAKE_TRANSLATION(manualtemp, "manualtemp", "manual temperature", "manuelle Temperatur", "Temperatuur handmatig", "Temperatur Manuell", "temperatura ustawiona ręcznie", "manuell temperatur", "température manuelle", "manuel sıcaklık", "temperatura manuale", "manuálna teplota", "manuální teplota")
|
||||
MAKE_TRANSLATION(tempautotemp, "tempautotemp", "temporary set temperature automode", "temporäre Solltemperatur Automatikmodus", "Streeftemperatuur automodus tijdelijk", "Temporär Aktivering av Auto-läge", "zadana temperatura w pomieszczeniu w trybie \"auto\" (tymczasowa)", "temporær valgt temp i automodus", "température temporaire mode automatique", "geçici ayarlı sıcaklık otomatik mod", "impostare temporaneamente temperatura automatica", "automatický režim dočasnej nastavenej teploty", "dočasné nastavení teploty v automatickém režimu")
|
||||
MAKE_TRANSLATION(remoteseltemp, "remoteseltemp", "temporary set temperature from remote", "temporäre Solltemperatur Remote", "Temperatuur van afstandsbedieding", "Temperatur från fjärruppkoppling", "zadana zdalnie temperatura a pomieszczeniu (tymczasowa)", "temporær valgt temp fra fjernbetjening", "température temporaire depuis télécommande", "geçici ayarlı sıcaklık uzaktan", "Temperatura temporanea da remoto", "dočasne nastavená teplota z diaľkového ovládania", "dočasné nastavení teploty z dálkového ovladače")
|
||||
MAKE_TRANSLATION(comforttemp, "comforttemp", "comfort temperature", "Komforttemperatur", "Comforttemperatuur", "Komforttemperatur", "temperatura w trybie komfort", "komforttemperatur", "température confort", "konfor sıcaklığı", "temperatura comfort", "komfortná teplota", "komfortní teplota")
|
||||
MAKE_TRANSLATION(summertemp, "summertemp", "summer temperature", "Sommertemperatur", "Zomertemperatuur", "Sommartemperatur", "temperatura przełączania lato/zima", "Sommertemperatur", "température été", "yaz sıcaklığı", "temperatura estiva", "letná teplota", "letní teplota")
|
||||
MAKE_TRANSLATION(designtemp, "designtemp", "design temperature", "Auslegungstemperatur", "Ontwerptemperatuur", "Design-temperatur", "temperatura projektowa", "designtemperatur", "température conception", "özel sıcaklık", "temperatura predefinita", "návrhová teplota", "dimenzovaná teplota")
|
||||
MAKE_TRANSLATION(offsettemp, "offsettemp", "offset temperature", "Temperaturanhebung", "Temperatuur offset", "Temperaturkorrigering", "korekta temperatury", "temperaturkorrigering", "température offset", "artış sıcaklığı", "aumento della temperatura", "offsetová teplota", "offset teploty")
|
||||
MAKE_TRANSLATION(minflowtemp, "minflowtemp", "min flow temperature", "min. Vorlauftemperatur", "Minimale aanvoertemperatuur", "Min. Flödestemperatur", "minimalna temperatura zasilania", "min. turtemperatur", "température min. flux", "minimun akış sıcaklığı", "temperatura minima di mandata", "min. výstupná teplota", "vytápění minimální teplota")
|
||||
MAKE_TRANSLATION(maxflowtemp, "maxflowtemp", "max flow temperature", "max. Vorlauftemperatur", "Maximale aanvoertemperatuur", "Max. Flödestemperatur", "maksymalna temperatura zasilania", "maks turtemperatur", "température max flux", "maksimum akış sıcaklığı", "temperatura massima di mandata", "maximálna teplota prívodu", "vytápění maximální teplota")
|
||||
MAKE_TRANSLATION(roominfluence, "roominfluence", "room influence", "Raumeinfluss", "Ruimteinvloed", "Rumspåverkan", "wpływ pomieszczenia", "rominnflytelse", "influence pièce", "oda etkisi", "influenza della camera", "vplyv miestnosti", "vliv prostoru")
|
||||
MAKE_TRANSLATION(roominfl_factor, "roominflfactor", "room influence factor", "Raumeinflussfaktor", "Factor ruimteinvloed", "Rumspåverkansfaktor", "współczynnik wpływu pomieszczenia", "rominnflytelsesfaktor", "facteur d'influence pièce", "oda etkisi faktörü", "fattore influenza camera", "faktor vplyvu miestnosti", "ofset teploty prostoru")
|
||||
MAKE_TRANSLATION(curroominfl, "curroominfl", "current room influence", "aktueller Raumeinfluss", "Huidige ruimteinvloed", "Aktuell Rumspåverkan", "aktualny wpływ pomieszczenia", "gjeldende rominnflytelse", "influence actuelle pièce", "güncel oda etkisi", "fattore corrente influenza camera", "aktuálny vplyv miestnosti", "aktuální vliv místnosti")
|
||||
MAKE_TRANSLATION(nofrosttemp, "nofrosttemp", "nofrost temperature", "Frostschutztemperatur", "Temperatuur vorstbeveiliging", "Temperatur Frostskydd", "temperatura ochrony przed zamarzaniem", "frostbeskyttelsestemperatur", "température protection gel", "donma koruması sıcaklığı", "temperatura protezione antigelo", "nofrost teplota", "teplota proti zamrznutí")
|
||||
MAKE_TRANSLATION(targetflowtemp, "targetflowtemp", "target flow temperature", "berechnete Vorlauftemperatur", "Berekende aanvoertemperatuur", "Målvärde Flödestemperatur", "zadana temperatura zasilania", "målverdi turtemperatur", "température cible flux", "hedef akış sıcaklığı", "temperatura di mandata calcolata", "cieľová teplota prívodu", "cílová teplota přívodu")
|
||||
MAKE_TRANSLATION(heatingtype, "heatingtype", "heating type", "Heizungstyp", "Verwarmingstype", "Värmesystem", "system grzewczy", "varmesystem", "type chauffage", "ısıtma tipi", "tipo riscaldamento", "typ vykurovania", "typ vytápění")
|
||||
MAKE_TRANSLATION(summersetmode, "summersetmode", "set summer mode", "Einstellung Sommerbetrieb", "Instelling zomerbedrijf", "Aktivera Sommarläge", "tryb lato/zima", "aktiver sommermodus", "activer mode été", "yaz modu ayarı", "Impostazione della modalità estiva", "nastaviť letný režim", "nastavit letní režim")
|
||||
MAKE_TRANSLATION(hpoperatingmode, "hpoperatingmode", "heatpump operating mode", "WP Betriebsmodus", "Bedrijfsmodus warmtepomp", "Värmepump Driftläge", "tryb pracy pompy ciepła", "driftsmodus varmepumpe", "mode fonctionnement pompe à chaleur", "ısı pompası çalışma modu", "Modalità di funzionamento della pompa di calore", "prevádzkový režim tepelného čerpadla", "provozní režim tepelného čerpadla")
|
||||
MAKE_TRANSLATION(hpoperatingstate, "hpoperatingstate", "heatpump operating state", "WP Betriebszustand", "Huidige modus warmtepomp", "Värmepump drifttillstånd", "aktualny tryb pracy pompy ciepła", "driftstatus varmepumpe", "état fonctionnement pompe à chaleur", "ısı pompası çalışma durumu", "stato funzionamento pompa di calore", "prevádzkový stav tepelného čerpadla", "stav provozu tepelného čerpadla")
|
||||
MAKE_TRANSLATION(controlmode, "controlmode", "control mode", "Steuermodus", "Comtrolemodus", "Kontrolläge", "tryb sterowania", "kontrollmodus", "mode régulation", "kontrol modu", "modalità di controllo", "kontrolný režim", "způsob regulace")
|
||||
MAKE_TRANSLATION(control, "control", "control device", "Fernsteuerung", "Afstandsbedieding", "Kontrollenhet", "sterownik", "kontrollenhet", "dispositif régulation", "kontrol cihazı", "dispositivo di comando", "ovládacie zariadenie", "řídící zařízení")
|
||||
MAKE_TRANSLATION(roomsensor, "roomsensor", "room sensor", "Raumsensor", "Ruimtesensor", "Rumssensor", "czujnik temperatury pomieszczenia", "romsensor", "capteur pièce", "oda sensörü", "sensore ambiente", "izbový snímač", "čidlo místnosti")
|
||||
MAKE_TRANSLATION(program, "program", "program", "Programm", "Programma", "Program", "program", "program", "programme", "program", "Programma", "program", "program")
|
||||
MAKE_TRANSLATION(pause, "pause", "pause time", "Pausenzeit", "Pausetijd", "Paustid", "czas przerwy", "pausetid", "temps de pause", "süreyi durdur", "pausa", "prestávka", "doba pauzy")
|
||||
MAKE_TRANSLATION(party, "party", "party time", "Partyzeit", "Partytijd", "Partytid", "czas przyjęcia", "partytid", "temps de fête", "parti zamanı", "festivo", "čas na párty", "doba party")
|
||||
MAKE_TRANSLATION(holidaytemp, "holidaytemp", "holiday temperature", "Urlaubstemperatur", "Vakantietemperatuur", "Helgtemperatur", "temperatura w trybie urlopowym", "ferietemperatur", "température vacances", "tatil sıcaklığı", "temperatura festiva", "prázdninová teplota", "teplota během dovolené")
|
||||
MAKE_TRANSLATION(summermode, "summermode", "summer mode", "Sommerbetrieb", "Zomerbedrijf", "Sommarläge", "aktualny tryb lato/zima", "sommermodus", "mode été", "yaz modu", "funzionamento estivo", "letný režim", "letní režim")
|
||||
MAKE_TRANSLATION(holidaymode, "holidaymode", "holiday mode", "Urlaubsbetrieb", "Vakantiebedrijf", "Helgläge", "tryb urlopowy", "feriemodus", "mode vacances", "tatil modu", "modalita vacanze", "dovolenkový režim", "režim dovolené")
|
||||
MAKE_TRANSLATION(flowtempoffset, "flowtempoffset", "flow temperature offset for mixer", "Anhebung Vorlauftemperatur Mischer", "Mixer aanvoertemperatuur offset", "Temperaturkorrigering Flödestemp. Blandningsventil", "korekta temperatury przepływu dla miksera", "temperaturkorrigering av blandingsventil", "décalage température de bascule pour mélangeur", "karıştırıcı için akış sıcaklığı farkı", "aumento della temperatura di ritorno", "Posun teploty prívodu pre zmiešavač", "offset teploty přívodu pro směšovač")
|
||||
MAKE_TRANSLATION(reducemode, "reducemode", "reduce mode", "Absenkmodus", "Gereduceerde modus", "Reducerat Läge", "tryb zredukowany/obniżony", "redusert modus", "mode réduction", "düşürme modu", "modalità assente", "znížený režim", "redukční režim")
|
||||
MAKE_TRANSLATION(noreducetemp, "noreducetemp", "no reduce below temperature", "Durchheizen unter", "Reduceermodus onderbreken onder", "Inaktivera reducering under", "bez redukcji poniżej temperatury", "inaktiver redusert nedre temp", "pas de réduction en dessous température", "bu sıcaklığın altına düşürme", "non ridurre temperatura sotto", "žiadne zníženie teploty pod teplotu", "nesnižovat pod teplotu")
|
||||
MAKE_TRANSLATION(reducetemp, "reducetemp", "off/reduce switch temperature", "Absenkmodus unter", "Onderste afschakeltemperatuur", "Avslag/Reducera under", "tryb zredukowany poniżej temperatury", "nedre avstengningstemperatur", "arrêt/réduction température bascule", "sıcaklık kapama/düşürme modu", "interruttore riduzione temperatura", "vypnúť/znížiť teplotu spínača", "teplota přepnutí na redukci/vypnutí")
|
||||
MAKE_TRANSLATION(vacreducetemp, "vacreducetemp", "vacations off/reduce switch temperature", "Urlaub Absenkmodus unter", "Vakantiemodus onderste afschakeltemperatuur", "Helg Avslag/Reducering under", "tryb urlopowy poniżej temperatury", "feriemodus nedre utkoblingstemperatur", "vacances – arrêt/réduction température bascule", "tatil sıcaklık kapama/düşürme modu", "interruttore riduzione temperatura vacanze", "dovolenky vypnúť/znížiť teplotu spínača", "teplota přepnutí na redukci/vypnutí během dovolené")
|
||||
MAKE_TRANSLATION(vacreducemode, "vacreducemode", "vacations reduce mode", "Urlaub Absenkmodus", "Vakantie afschakelmodus", "Helg reduceringsläge", "redukcja w trakcie urlopu", "ferieavstengningsmodus", "mode réduction vacances", "tail düşürme modu", "modalita riduzione vacanze", "režim zníženia dovoleniek", "redukční režim během dovolené")
|
||||
MAKE_TRANSLATION(nofrostmode, "nofrostmode", "nofrost mode", "Frostschutzmodus", "Vorstbeveiligingsmodus", "Frostskyddsläge", "temperatura wiodąca dla ochrony przed zamarzaniem", "frostbeskyttelsesmodus", "mode protection gel", "donma koruması modu", "Modalità protezione antigelo", "nofrost režim", "režim proti zamrznutí")
|
||||
MAKE_TRANSLATION(remotetemp, "remotetemp", "room temperature from remote", "Raumtemperatur Remote", "Ruimtetemperatuur van afstandsbediening", "Rumstemperatur från fjärr", "temperatura w pomieszczeniu (z termostatu)", "romstemperatur fra fjernbetjening", "température pièce depuis télécommande", "uzaktan oda sıcaklığı", "temperatura ambiente da remoto", "izbová teplota z diaľkového ovládania", "teplota místnosti z dálkového ovladače")
|
||||
MAKE_TRANSLATION(remotehum, "remotehum", "room humidity from remote", "Raumfeuchte Remote", "", "Rumsluftfuktighet från fjärr", "wilgotność w pomieszczeniu (z termostatu)", "", "", "uzaktan kumandadan oda nemi", "", "Vlhkosť v miestnosti z diaľkového ovládania", "vlhkost místnosti z dálkového ovladače") // TODO translate
|
||||
MAKE_TRANSLATION(wwHolidays, "holidays", "holiday dates", "Feiertage", "Feestdagen", "Helgdagar", "dni świąteczne", "feriedager varmtvann", "dates vacances", "tatil günleri", "feste pubbliche", "sviatočné termíny", "data pro dovolenou")
|
||||
MAKE_TRANSLATION(wwVacations, "vacations", "vacation dates", "Urlaubstage", "Vakantiedagen", "Semesterdatum Varmvatten", "dni urlopowe", "ferie dato varmtvann", "dates vacances", "izin günleri", "date vacanze", "termíny dovolenky", "data pro prázdniny")
|
||||
MAKE_TRANSLATION(holidays, "holidays", "holiday dates", "Feiertage", "Feestdagen", "Helgdatum", "święta", "helligdager", "dates vacances", "tatil günleri", "date feste pubbliche", "sviatočné termíny", "data pro dovolenou")
|
||||
MAKE_TRANSLATION(vacations, "vacations", "vacation dates", "Urlaubstage", "Vakantiedagen", "Semesterdatum", "urlop", "feriedager", "dates vacances", "izin günleri", "date vacanze", "termíny dovolenky", "data pro prázdniny")
|
||||
MAKE_TRANSLATION(wwprio, "dhwprio", "dhw priority", "WW-Vorrang", "Prioriteit warm water", "Prioritera Varmvatten", "priorytet dla c.w.u.", "prioroter varmtvann", "priorité ecs", "sıcak kullanım suyu önceliği", "priorita acqua calda", "Priorita TÚV", "přednost ohřevu TUV")
|
||||
MAKE_TRANSLATION(nofrostmode1, "nofrostmode1", "nofrost mode", "Frostschutz", "Vorstbeveiligingsmodus", "Frostskyddsläge", "ochrona przed zamarzaniem", "frostbeskyttelse", "mode protection gel", "donma koruması modu 1", "modalita protezione antigelo", "nofrost režim", "režim proti zamrznutí")
|
||||
MAKE_TRANSLATION(reducehours, "reducehours", "duration for nighttemp", "Dauer Nachttemp.", "Duur nachtverlaging", "Timmar Nattsänkning", "czas trwania trybu nocnego", "timer nattsenkning", "durée température nuit", "gece sıcaklığı süresi", "durata temperatura notturna", "trvanie nočnej teploty", "délka trvání noční teploty")
|
||||
MAKE_TRANSLATION(reduceminutes, "reduceminutes", "remaining time for nightmode", "Restzeit Nachttemp.", "Resterende tijd nachtverlaging", "Återstående Tid Nattläge", "czas do końca trybu nocnego", "gjenværende tid i nattstilling", "temps restant mode nuit", "gece modu için kalan süre", "temperatura notturna residua", "zostávajúci čas pre nočný režim", "zbývající čas nočního režimu")
|
||||
MAKE_TRANSLATION(switchonoptimization, "switchonoptimization", "switch-on optimization", "Einschaltoptimierung", "Inschakeloptimalisering", "Växlingsoptimering", "optymalizacja załączania", "slå på optimalisering", "optimisation mise en marche", "optimizasyonu aç", "ottimizzazione all'accensione", "optimalizácia pri zapnutí", "optim. zapínání čas.prog.")
|
||||
MAKE_TRANSLATION(vacations1, "vacations1", "vacation dates 1", "Urlaubstage 1", "Vakantiedagen 1", "Semesterdatum 1", "urlop 1", "feriedager 1", "dates vacances 1", "izin günleri 1", "date vacanze 1", "termíny dovolenky 1", "data prázdnin 1")
|
||||
MAKE_TRANSLATION(vacations2, "vacations2", "vacation dates 2", "Urlaubstage 2", "Vakantiedagen 2", "Semesterdatum 2", "urlop 2", "feriedager 2", "dates vacances 2", "izin günleri 2", "date vacanze 2", "termíny dovolenky 2", "data prázdnin 2")
|
||||
MAKE_TRANSLATION(vacations3, "vacations3", "vacation dates 3", "Urlaubstage 3", "Vakantiedagen 3", "Semesterdatum 3", "urlop 3", "feriedager 3", "dates vacances 3", "izin günleri 3", "date vacanze 3", "termíny dovolenky 3", "data prázdnin 3")
|
||||
MAKE_TRANSLATION(vacations4, "vacations4", "vacation dates 4", "Urlaubstage 4", "Vakantiedagen 4", "Semesterdatum 4", "urlop 4", "feriedager 4", "dates vacances 4", "izin günleri 4", "date vacanze 4", "termíny dovolenky 4", "data prázdnin 4")
|
||||
MAKE_TRANSLATION(vacations5, "vacations5", "vacation dates 5", "Urlaubstage 5", "Vakantiedagen 5", "Semesterdatum 5", "urlop 5", "feriedager 5", "dates vacances 5", "izin günleri 5", "date vacanze 5", "termíny dovolenky 5", "data prázdnin 5")
|
||||
MAKE_TRANSLATION(vacations6, "vacations6", "vacation dates 6", "Urlaubstage 6", "Vakantiedagen 6", "Semesterdatum 6", "urlop 6", "feriedager 6", "dates vacances 6", "izin günleri 6", "date vacanze 6", "termíny dovolenky 6", "data prázdnin 6")
|
||||
MAKE_TRANSLATION(vacations7, "vacations7", "vacation dates 7", "Urlaubstage 7", "Vakantiedagen 7", "Semesterdatum 7", "urlop 7", "feriedager 7", "dates vacances 7", "izin günleri 7", "date vacanze 7", "termíny dovolenky 7", "data prázdnin 7")
|
||||
MAKE_TRANSLATION(vacations8, "vacations8", "vacation dates 8", "Urlaubstage 8", "Vakantiedagen 8", "Semesterdatum 8", "urlop 8", "feriedager 8", "dates vacances 8", "izin günleri 8", "date vacanze 8", "termíny dovolenky 8", "data prázdnin 8")
|
||||
MAKE_TRANSLATION(absent, "absent", "absent", "Abwesend", "", "Frånvarande", "", "", "", "", "", "chýnajúci", "") // TODO translate
|
||||
MAKE_TRANSLATION(redthreshold, "redthreshold", "reduction threshold", "Absenkschwelle", "", "Tröskel för sänkning", "", "", "", "", "", "zníženie tresholdu", "") // TODO translate
|
||||
|
||||
MAKE_TRANSLATION(hpmode, "hpmode", "HP Mode", "WP-Modus", "Modus warmtepomp", "Värmepumpsläge", "tryb pracy pompy ciepła", "", "", "yüksek güç modu", "Modalità Termopompa", "Režim TČ", "režim tepelného čerpadla") // TODO translate
|
||||
MAKE_TRANSLATION(dewoffset, "dewoffset", "dew point offset", "Taupunktdifferenz", "Offset dauwpunt", "Daggpunktsförskjutning", "przesunięcie punktu rosy", "", "", "çiğ noktası göreli", "differenza del punto di rugiada", "posun rosného bodu", "offset rosného bodu") // TODO translate
|
||||
MAKE_TRANSLATION(roomtempdiff, "roomtempdiff", "room temp difference", "Raumtemperaturdifferenz", "Verschiltemperatuur kamertemp", "Rumstemperaturskillnad", "różnica temp. pomieszczenia", "", "", "oda sıcaklığı farkı", "differenza temperatura ambiente", "rozdiel izbovej teploty", "rozdíl teploty místnosti") // TODO translate
|
||||
MAKE_TRANSLATION(hpminflowtemp, "hpminflowtemp", "HP min. flow temp.", "WP minimale Vorlauftemperatur", "Minimale aanvoertemperatuur WP", "VP min flödestemperatur", "pompa ciepła, min. temperatura przepływu", "", "yüksek güç minimum akış sıcaklığı", "temperatura minima di mandata", "VT min. teplota prietoku.", "minimální teplota přívodu tepelného čerpadla") // TODO translate
|
||||
MAKE_TRANSLATION(hpcooling, "hpcooling", "hp cooling", "WP Kühlen", "WP koelbedrijf", "VP Kyla", "pompa ciepła, chłodzenie", "vp kjøling", "hp soğuyor", "raffreddamento pompa calore", "chladenie TČ", "chlazení tepelného čerpadla")
|
||||
MAKE_TRANSLATION(coolstart, "coolstart", "cooling starttemp", "Kühlbetrieb ab", "", "Kyla starttemperatur", "", "", "", "", "", "teplota spustenia chladenia", "teplota spuštění chlazení") // TODO translate
|
||||
MAKE_TRANSLATION(coolondelay, "coolondelay", "cooling on delay", "Einschaltverzögerung Kühlen", "", "Inkopplingsfördröjning Kyla", "", "", "", "", "", "oneskorenie zapnutie chladenia", "zpoždění zapnutí chlazení") // TODO translate
|
||||
MAKE_TRANSLATION(cooloffdelay, "cooloffdelay", "cooling off delay", "Ausschaltverzögerung Kühlen", "", "Frånkopplingsfördröjning Kyla", "", "", "", "", "", "oneskorenie vypnutie chladenia", "zpoždění vypnutí chlazení") // TODO translate
|
||||
MAKE_TRANSLATION(switchProgMode, "switchprogmode", "switch program mode", "Schaltprogrammmodus", "", "Byt programläge", "", "", "", "", "", "prepnúť režim programu", "přepínací režim programu") // TODO translate
|
||||
|
||||
// heatpump and RC100H
|
||||
MAKE_TRANSLATION(airHumidity, "airhumidity", "relative air humidity", "relative Luftfeuchte", "Relatieve luchtvochtigheid", "Relativ Luftfuktighet", "wilgotność względna w pomieszczeniu", "luftfuktighet", "humidité relative air", "havadaki bağıl nem", "umidità relativa aria", "relatívna vlhkosť vzduchu", "relativní vlhkost vzduchu")
|
||||
MAKE_TRANSLATION(dewTemperature, "dewtemperature", "dew point temperature", "Taupunkttemperatur", "Dauwpunttemperatuur", "Daggpunkt", "punkt rosy w pomieszczeniu", "duggtemperatur", "température point rosée", "çiğ noktası sıcaklığı", "temperatura del punto di rugiada", "teplota rosného bodu", "teplota rosného bodu")
|
||||
MAKE_TRANSLATION(battery, "battery", "battery", "Batterie", "", "Batteri", "bateria", "", "", "", "", "batéria", "baterie") // TODO translate
|
||||
|
||||
// mixer
|
||||
MAKE_TRANSLATION(flowSetTemp, "flowsettemp", "setpoint flow temperature", "Sollwert Vorlauftemperatur", "Streefwaarde aanvoertemperatuur", "Vald flödestemperatur", "zadana temperatura zasilania", "valgt turtemperatur", "consigne température flux", "akış sıcaklığı ayarı", "Setpoint temperatura di mandata", "požadovaná hodnota výstupnej teploty", "cílová teplota vstupní vody")
|
||||
MAKE_TRANSLATION(flowTempHc, "flowtemphc", "flow temperature (TC1)", "Vorlauftemperatur HK (TC1)", "Aanvoertemperatuut circuit (TC1)", "Flödestemperatur (TC1)", "temperatura zasilania (TC1)", "turtemperatur (TC1)", "température flux (TC1)", "akış sıcaklığı (TC1)", "temperatura di mandata (TC1)", "teplota prívodu (TC1)", "teplota přívodu (TC1)")
|
||||
MAKE_TRANSLATION(pumpStatus, "pumpstatus", "pump status (PC1)", "Pumpenstatus HK (PC1)", "pompstatus circuit (PC1)", "Pumpstatus (PC1)", "status pompy (PC1)", "pumpestatus (PC1)", "état pompe (PC1)", "pompa durumu (PC1)", "stato pompa (PC1)", "stav čerpadla (PC1)", "stav čerpadla (PC1)")
|
||||
MAKE_TRANSLATION(mixerStatus, "valvestatus", "mixing valve actuator (VC1)", "Mischerventilposition (VC1)", "positie mixerklep (VC1)", "Shuntventil Status (VC1)", "siłownik zaworu mieszającego (VC1)", "shuntventil status (VC1)", "actionnement vanne mélangeur (VC1)", "karışım vanası aktüatörü (VC1)", "posizione valvola miscela (VC1)", "pohon zmiešavacieho ventilu (VC1)", "pohon směšovacího ventilu (VC1)")
|
||||
MAKE_TRANSLATION(flowTempVf, "flowtempvf", "flow temperature in header (T0/Vf)", "Vorlauftemperatur am Verteiler (T0/Vf)", "aanvoertemperatuur verdeler (T0/Vf)", "Flödestemperatur Fördelare (T0/Vf)", "temperatura zasilania na rozdzielaczu (T0/Vf)", "turtemperatur ved fordeleren (T0/Vf)", "température départ collecteur (T0/Vf)", "başlıkta akış sıcaklığı", "Temperatura di mandata al distributore (T0/Vf)", "teplota prívodu v zberači (T0/Vf)", "teplota přívodu v hlavici (T0/Vf)")
|
||||
MAKE_TRANSLATION(mixerSetTime, "valvesettime", "time to set valve", "Zeit zum einstellen des Ventils", "Inschakeltijd mengklep", "Inställningstid Shuntventil", "czas na ustawienie zaworu", "instillningstid ventil", "délai activation vanne", "vana ayar zamanı", "ritardo attivazione valvola", "čas na nastavenie ventilu", "čas pro nastavení ventilu")
|
||||
|
||||
// mixer pool
|
||||
MAKE_TRANSLATION(poolSetTemp, "poolsettemp", "pool set temperature", "Sollwert Pooltemperatur", "Streeftemperatuur zwembad", "Pool Temperatur Börvärde", "zadana temperatura basenu", "valgt temp basseng", "température consigne piscine", "hedef havuz sıcaklığı", "temperatura nominale piscina", "nastavená teplota bazéna", "cílová teplota bazénu")
|
||||
MAKE_TRANSLATION(poolTemp, "pooltemp", "pool temperature", "Pooltemperatur", "Zwembadtemperatuur", "Pooltemperatur", "temperatura basenu", "bassengtemperatur", "température piscine", "havuz sıcaklığı", "temperatura piscina", "teplota bazéna", "teplota bazénu")
|
||||
MAKE_TRANSLATION(poolShuntStatus, "poolshuntstatus", "pool shunt status opening/closing", "Poolventil öffnen/schließen", "Zwembadklep status openen/sluiten", "Pool Shunt-status öppen/stängd", "status bocznika basenu", "bassengshunt-status åpen/stengt", "état shunt piscine ouvert/fermé", "havuz şant durumu açılıyor/kapanıyor", "aprire/chiudere valvola regolazione piscina", "stav bazénového bočníka otváranie/zatváranie", "stav šoupátka bazénu (otevírání/zavírání)")
|
||||
MAKE_TRANSLATION(poolShunt, "poolshunt", "pool shunt open/close (0% = pool / 100% = heat)", "Poolventil Öffnung", "Mengklep zwembad stand", "Pool Shunt Öppen/Stängd", "bocznik basenu (0% = basen / 100% = grzanie)", "bassengshunt åpen/stengt (0% = basseng / 100% = varme)", "ouverture/fermeture shunt piscine (0% = piscine / 100% = chaleur).", "havuz şant açık/kapalı (0% = havuz / 100% = ısıtma)", "valvola regolazione piscina (0% = piscina / 100% = caldo)", "pohyb bazéna otvoriť/zatvoriť (0 % = bazén / 100 % = teplo)", "šoupátko bazénu (0% = bazén / 100% = vytápění)")
|
||||
MAKE_TRANSLATION(hydrTemp, "hydrTemp", "hydraulic header temperature", "Verteilertemperatur", "Temperatuur open verdeler", "Fördelartemperatur", "temperatura kolektora hydraulicznego", "Fordelertemperatur", "température collecteur hydraulique", "hidrolik başlık sıcaklığı", "temperatura del collettore", "teplota hydraulickej hlavice", "teplota hydraulické hlavice")
|
||||
|
||||
// solar
|
||||
MAKE_TRANSLATION(cylMiddleTemp, "cylmiddletemp", "cylinder middle temperature (TS3)", "Speichertemperatur Mitte (TS3)", "Zonneboilertemperatuur midden (TS3)", "Cylindertemperatur Mitten (TS3)", "temperatura w środku zasobnika (TS3)", "beredertemperatur i midten (TS3)", "température moyenne cylindre (TS3)", "orta depolama sıcaklığı (TS3)", "temperatura di conservazione media accumulo (TS3)", "stredná teplota valca (TS3)", "teplota středu válce (TS3)")
|
||||
MAKE_TRANSLATION(retHeatAssist, "retheatassist", "return temperature heat assistance (TS4)", "Anhebung Rücklauftemp. (TS4)", "Retourtemperatuur verwarmingsassistentie (TS4)", "Returtemperatur värmestöd (TS4)", "temperatura powrotu wspomagania grzania (TS4)", "returtemperatur varmestøtte (TS4)", "température retour de assistance thermique (TS4)", "geri dönüş sıcaklığı artışı (TS4)", "temperatura ritorno scambiatore (TS4)", "pomoc pri teplote spiatočky (TS4)", "teplota zpátečky pomocného topení (TS4)")
|
||||
MAKE_TRANSLATION(ts8, "ts8", "return temperature heat assistance (TS8)", "Anhebung Rücklauftemp. (TS8)", "Retourtemperatuur verwarmingsassistentie (TS8)", "Returtemperatur värmestöd (TS8)", "temperatura powrotu wspomagania grzania (TS8)", "returtemperatur varmestøtte (TS8)", "température retour de assistance thermique (TS8)", "geri dönüş sıcaklığı artışı (TS8)", "temperatura ritorno scambiatore (TS8)", "pomoc pri teplote spiatočky (TS8)", "teplota zpátečky pomocného topení (TS8)")
|
||||
MAKE_TRANSLATION(m1Valve, "heatassistvalve", "heat assistance valve (M1)", "Ventil Heizungsunterstützung (M1)", "Klep verwarmingsassistentie (M1)", "Uppvärmningsstöd Ventil (M1)", "zawór wspomagania grzania (M1)", "varmehjelpsventil (M1)", "vanne assistance thermique (M1)", "ısıtma yardım vanası (M1)", "valvola scambiatore (M1)", "tepelný asistenčný ventil (M1)", "ventil pomocného topení (M1)")
|
||||
MAKE_TRANSLATION(m1Power, "heatassistpower", "heat assistance valve power (M1)", "Ventilleistung Heizungsunterstützung (M1)", "Vermogen klep verwarmingsassistentie (M1)", "Uppvärmningsstöd Ventil Effekt (M1)", "moc zaworu wspomagania grzania (M1)", "varmehjelpsventileffekt (M1)", "puissance vanne assistance thermique (M1)", "ısıtma yardım vanası gücü (M1)", "potenza valvola scambiatore (M1)", "výkon ventilu tepelného asistenta (M1)", "výkon ventilu pomocného topení (M1)")
|
||||
MAKE_TRANSLATION(pumpMinMod, "pumpminmod", "minimum pump modulation", "minimale Pumpenmodulation", "Minimale pompmodulatie", "Min Pumpmodulering", "minimalna modulacja pompy", "minimum pumpmodulering", "modulation minimale pompe", "minimum pompa modülasyonu", "modulazione minima pompa", "minimálna modulácia čerpadla", "minimální modulace čerpadla")
|
||||
MAKE_TRANSLATION(maxFlow, "maxflow", "maximum solar flow", "maximaler Durchfluss", "Maximale doorstroom solar", "Max Flöde Solpanel", "maksymalny przepływ solarów", "maks strømming solpanel", "débit solaire maximum", "minimum G.E. akışı", "portata massima solare", "maximálny solárny prietok", "maximální průtok soláru")
|
||||
MAKE_TRANSLATION(solarPower, "solarpower", "actual solar power", "aktuelle Solarleistung", "Huidig solar vermogen", "Aktuellt Sol-effekt", "aktualna moc solarów", "aktuell soleffekt", "puissance solaire réelle", "gerçek G.E. gücü", "potenza attuale solare", "skutočná slnečná energia", "aktuální solární výkon")
|
||||
MAKE_TRANSLATION(solarPumpTurnonDiff, "turnondiff", "pump turn on difference", "Einschalthysterese Pumpe", "Inschakelhysterese pomp", "Aktiveringshysteres Pump", "histereza załączenia pompy", "slå på hysteresepumpe", "différence activation pompe", "pompa devreye alma farkı", "isteresi di accensione pompa", "rozdiel v zapnutí čerpadla", "rozdíl pro zapnutí čerpadla")
|
||||
MAKE_TRANSLATION(solarPumpTurnoffDiff, "turnoffdiff", "pump turn off difference", "Ausschalthysterese Pumpe", "Uitschakelhysterese pomp", "Avslagshysteres Pump", "histereza włączenia pompy", "slå av hysteresepumpe", "différence arrêt pompe", "pompa kapama farkı", "isteresi di spegnimento pompa", "rozdiel vypnutia čerpadla", "rozdíl pro vypnutí čerpadla")
|
||||
MAKE_TRANSLATION(pump2MinMod, "pump2minmod", "minimum pump 2 modulation", "minimale Modulation Pumpe 2", "Minimale modulatie pomp 2", "Min Modulering Pump 2", "minimalna modulacja pompy 2", "minimum pumpmodulering 2", "modulation minimale pompe 2", "minimum pompa 2 modülasyonu", "modulazione minima pompa 2", "minimálna modulácia čerpadla 2", "minimální modulace čerpadla 2")
|
||||
MAKE_TRANSLATION(solarPump2TurnonDiff, "turnondiff2", "pump 2 turn on difference", "Einschalthysterese Pumpe 2", "Inschakelhysterese pomp 2", "Aktiveringshysteres Pump 2", "histereza załączenia pompy 2", "slå på hysteresepumpe 2", "différence activation pompe 2", "pompa 2 devreye alma farkı", "isteresi di accensione pompa 2", "rozdiel v zapnutí čerpadla 2", "rozdíl pro zapnutí čerpadla 2")
|
||||
MAKE_TRANSLATION(solarPump2TurnoffDiff, "turnoffdiff2", "pump 2 turn off difference", "Ausschalthysterese Pumpe 2", "Uitschakelhysterese pomp 2", "Avslagshysteres Pump 2", "histereza wyłączenia pompy 2", "slå av hysteresepumpe 2", "différence arrêt pompe 2", "pompa 2 kapama farkı", "isteresi di spegnimento pompa", "rozdiel vypnutia čerpadla 2", "rozdíl pro vypnutí čerpadla 2")
|
||||
MAKE_TRANSLATION(collectorTemp, "collectortemp", "collector temperature (TS1)", "Kollektortemperatur (TS1)", "Collectortemperatuur (TS1)", "Kollektor Temperatur (TS1)", "temperatura kolektora (TS1)", "kollektor temperatur (TS1)", "température collecteur (TS1)", "kollektör sıcaklığı (TS1)", "temperatura collettore (TS1)", "teplota kolektora (TS1)", "teplota kolektoru (TS1)")
|
||||
MAKE_TRANSLATION(collector2Temp, "collector2temp", "collector 2 temperature (TS7)", "Kollectortemperatur 2 (TS7)", "Collector 2 temperatuur (TS7)", "Kollektor 2 Temperatur (TS7)", "temperatura kolektora 2 (TS7)", "kollektor 2 temperatur (TS7)", "température collecteur 2 (TS7)", "kollektör 2 sıcaklığı (TS2)", "temperatura collettore 2 (TS7)", "teplota kolektora 2 (TS7)", "teplota kolektoru 2 (TS7)")
|
||||
MAKE_TRANSLATION(cylBottomTemp, "cylbottomtemp", "cylinder bottom temperature (TS2)", "Speicherbodentemperatur (TS2)", "Bodemtemperatuur zonneboiler (TS2)", "Cylindertemperatur Botten (TS2)", "temperatura na spodzie zasobnika (TS2)", "beredertemp i bunn (TS2)", "température fond de cylindre (TS2)", "alt depolama sıcaklığıc(TS2)", "temperatura inferiore accumulo (TS2)", "teplota dna valca (TS2)", "teplota dna válce (TS2)")
|
||||
MAKE_TRANSLATION(cyl2BottomTemp, "cyl2bottomtemp", "second cylinder bottom temperature (TS5)", "Speicherbodentemperatur 2 (TS5)", "Bodemtemperatuur 2e boiler", "Sekundär Cylindertemperatur Botten (TS5)", "temperatura na spodzie drugiego zasobnika (TS5)", "skundær beredertemp i bunn (TS5)", "température fond de cylindre (TS5)", "ikinci alt depolama sıcaklığıc(TS5)", "temperatura inferiore 2° accumulo (TS5)", "teplota dna druhého valca (TS5)", "teplota dna druhého válce (TS5)")
|
||||
MAKE_TRANSLATION(heatExchangerTemp, "heatexchangertemp", "heat exchanger temperature (TS6)", "Wärmetauschertemperatur (TS6)", "Temperatuur warmtewisselaar (TS6)", "Värmeväxlare Temperatur (TS6)", "temperatura wymiennika ciepła (TS6)", "Varmeveksler temperatur (TS6)", "température échangeur de chaleur (TS6)", "eşanjör sıcaklığı (TS6)", "temperatura scambiatore calore (TS6)", "teplota výmenníka tepla (TS6)", "teplota výměníku tepla (TS6)")
|
||||
MAKE_TRANSLATION(collectorMaxTemp, "collectormaxtemp", "maximum collector temperature", "maximale Kollektortemperatur", "Maximale collectortemperatuur", "Max. Kollektortemperatur", "maksymalna temperatura kolektora", "maks kollektortemperatur", "température max. collecteur", "maksimum kollektör sıcaklığı", " temperatura massima scambiatore calore", "maximálna teplota kolektora", "maximální teplota kolektoru")
|
||||
MAKE_TRANSLATION(collectorMinTemp, "collectormintemp", "minimum collector temperature", "minimale Kollektortemperatur", "Minimale collectortemperatuur", "Min. Kollektortemperatur", "minimalna temperatura kolektora", "min. kollektortemperatur", "température min. collecteur", "minimum kollektör sıcaklığı", "temperatura minima scambiatore calore", "minimálna teplota kolektora", "minimální teplota kolektoru")
|
||||
MAKE_TRANSLATION(cylMaxTemp, "cylmaxtemp", "maximum cylinder temperature", "maximale Speichertemperatur", "maximale temperatuur zonneboiler", "Max Cylindertemperatur", "maksymalna temperatura zasobnika", "maks beredertemperatur", "température max. cylindre", "maksimum silindir sıcaklığı", "temperatura massima vaso accumulo", "maximálna teplota valca", "maximální teplota válce")
|
||||
MAKE_TRANSLATION(solarPumpMod, "solarpumpmod", "pump modulation (PS1)", "Pumpenmodulation (PS1)", "Pompmodulatie (PS1)", "Pumpmodulering (PS1)", "modulacja pompy solarnej (PS1)", "solpumpmodulering (PS1)", "modulation pompe (PS1)", "pompa modülasyonu (PS1)", "modulazione pompa (PS1)", "modulácia čerpadla (PS1)", "modulace čerpadla (PS1)")
|
||||
MAKE_TRANSLATION(cylPumpMod, "cylpumpmod", "cylinder pump modulation (PS5)", "Speicherpumpenmodulation (PS5)", "Modulatie zonneboilerpomp (PS5)", "Cylinderpumpmodulering (PS5)", "modulacja pompy zasobnika (PS5)", "sylinderpumpemodulering (P55)", "modulation pompe cylindre (PS5)", "silindir pompa modülasyonu (PS5)", "pompa modulazione accumulo (PS5)", "modulácia čerpadla valca (PS5)", "modulace čerpadla válce (PS5)")
|
||||
MAKE_TRANSLATION(solarPump, "solarpump", "pump (PS1)", "Pumpe (PS1)", "Pomp (PS1)", "Pump (PS1)", "pompa solarna (PS1)", "solpumpe (PS1)", "pompe solaire (PS1)", "pompa (PS1)", "pompa solare (PS1)", "čerpadlo (PS1)", "čerpadlo (PS1)")
|
||||
MAKE_TRANSLATION(solarPump2, "solarpump2", "pump 2 (PS4)", "Pumpe 2 (PS4)", "Pomp 2 (PS4)", "Pump 2 (PS4)", "pompa solarna 2 (PS4)", "solpumpe 2 (PS4)", "pompe 2 (PS4)", "pompa 2 (PS4)", "pompa solare 2 (PS4)", "čerpadlo 2 (PS4)", "čerpadlo 2 (PS4)")
|
||||
MAKE_TRANSLATION(solarPump2Mod, "solarpump2mod", "pump 2 modulation (PS4)", "Modulation Pumpe 2 (PS4)", "Modulatie pomp 2 (PS4)", "Pump 2 Modulering (PS4)", "modulacja pompy solarnej 2 (PS4)", "solpumpe2modulering (PS4)", "modulation pompe solaire 2 (PS4)", "pompa2 modülasyonu(PS1)", "pompa modulazione 2 (PS4)", "modulácia pumpy 2 (PS4)", "modulace čerpadla 2 (PS4)")
|
||||
MAKE_TRANSLATION(valveStatus, "valvestatus", "valve status", "Ventilstatus", "Klepstatus", "Ventilstatus", "stan zaworu", "ventilstatus", "statut valve", "vana durumu", "stato valvola", "stav ventilu", "stav ventilu")
|
||||
MAKE_TRANSLATION(vs1Status, "vs1status", "valve status VS1", "Ventilstatus VS1", "Klepstatus VS1", "Ventilstatus VS1", "stan zaworu VS1", "ventilstatus VS1", "statut valve VS1", "vana durumu VS1", "stato valvola VS1", "stav ventilu VS1", "stav ventilu VS1")
|
||||
MAKE_TRANSLATION(vs3Status, "vs3status", "valve status VS3", "Ventilstatus VS3", "Klepstatus VS3", "Ventilstatus VS3", "stan zaworu VS3", "ventilstatus VS3", "statut valve VS3", "vana durumu VS3", "stato valvola VS3", "stav ventilu VS3", "stav ventilu VS3")
|
||||
MAKE_TRANSLATION(cylHeated, "cylheated", "cyl heated", "Speichertemperatur erreicht", "Boilertemperatuur behaald", "Värmepanna Uppvärmd", "zasobnik został nagrzany", "bereder oppvarmt", "cylindre chauffé", "depolama sıcakllığına ulaşıldı", "temperatura richiesta vaso accumulo raggiunta", "Dosiahnutá teplota zásobníka", "ohřívání válce")
|
||||
MAKE_TRANSLATION(collectorShutdown, "collectorshutdown", "collector shutdown", "Kollektorabschaltung", "Collector afschakeling", "Kollektor Avstängning", "wyłączenie kolektora", "kollektor stengt", "arrêt collecteur", "kollektör kapalı", "spegnimento del collettore", "vypnutie kolektora", "vypnutí kolektoru")
|
||||
MAKE_TRANSLATION(pumpWorkTime, "pumpworktime", "pump working time", "Pumpenlaufzeit", "Pomplooptijd", "Pump Drifttid", "czas pracy pompy", "driftstid pumpe", "durée fonctionnement pompe", "pompa çalışma süresi", "tempo funzionamento pompa", "pracovný čas čerpadla", "pracovní doba čerpadla")
|
||||
MAKE_TRANSLATION(pump2WorkTime, "pump2worktime", "pump 2 working time", "Laufzeit Pumpe 2", "Looptijd pomp 2", "Pump 2 Drifttid", "czas pracy pompy 2", "driftstid pumpe2", "durée fonctionnement pompe 2", "pompa 2 çalışma süresi", "tempo funzionamento pompa 2", "pracovný čas čerpadla 2", "pracovní doba čerpadla 2")
|
||||
MAKE_TRANSLATION(m1WorkTime, "m1worktime", "differential control working time", "Differenzregelung Arbeitszeit", "Verschilregeling arbeidstijd", "Differentialreglering Drifttid", "czas pracy regulacji różnicowej", "differentialreguleringssrifttid", "durée fonctionnement contrôle différentiel", "çalışma saatlerinin farklı düzenlenmesi", "controllo differenziale durata funzionamento", "pracovný čas diferenciálnej kontroly", "pracovní doba diferenciální regulace")
|
||||
MAKE_TRANSLATION(energyLastHour, "energylasthour", "energy last hour", "Energie letzte Std", "Energie laatste uur", "Energi Senaste Timmen", "energia w ciągu ostatniej godziny", "energi siste time", "énergie dernière heure", "son saat enerji", "Eenergia ultima ora", "energia za poslednú hodinu", "energie za poslední hodinu")
|
||||
MAKE_TRANSLATION(energyTotal, "energytotal", "total energy", "Gesamtenergie", "Totale energie", "Total Energi", "energia całkowita", "total energi", "énergie totale", "toplam enerji", "energia totale", "celková energia", "celková energie")
|
||||
MAKE_TRANSLATION(energyToday, "energytoday", "total energy today", "Energie heute", "Energie vandaag", "Total Energi Idag", "energia całkowita dzisiaj", "total energi i dag", "énergie totale aujourd'hui", "bugün toplam enerji", "totale energia giornaliera", "celková energia dnes", "celková energie dnes")
|
||||
MAKE_TRANSLATION(cyl3BottomTemp, "cyl3bottomtemp", "third cylinder bottom temperature (TS11)", "Speichertemperatur unten (TS11)", "", "Tredje Cylindertemperatur Botten (TS11)", "", "", "", "", "", "spodná teplota tretieho valca (TS11)", "") // TODO translate
|
||||
MAKE_TRANSLATION(cylTopTemp, "cyltoptemp", "cylinder top temperature (TS10)", "Speichertemperatur oben (TS10)", "", "Cylindertemperatur Toppen (TS10)", "", "", "", "", "", "horná teplota valca (TS10)", "") // TODO translate
|
||||
MAKE_TRANSLATION(transferPumpMod, "transferpumpmod", "transfer pump modulation", "Transferpumpenmodulation", "", "Överföringspumpmodulering", "", "", "", "", "", "modulácia prenosového čerpadla", "") // TODO translate
|
||||
MAKE_TRANSLATION(transferPump, "transferpump", "transfer pump", "Transferpumpe", "", "Överföringspump", "", "", "", "", "", "prenosové čerpadlo", "") // TODO translate
|
||||
|
||||
// solar dhw
|
||||
MAKE_TRANSLATION(wwColdTemp, "coldtemp", "cold water", "Kaltwasser", "", "kallvatten", "zimna woda", "", "", "", "", "studená voda", "studená voda") // TODO translate
|
||||
MAKE_TRANSLATION(wwTemp5, "temp5", "temperature 5", "Temperatur 5", "Temperatuur 5", "Temperatur 5", "temperatura 5", "Temperatur 5", "température 5", "sıcaklık 5", "Temperatura 5", "teplota 5", "teplota 5")
|
||||
MAKE_TRANSLATION(wwTemp6, "temp6", "temperature 6", "Temperatur 6", "Temperatuur 6", "Temperatur 6", "temperatura 6", "temperatur 6", "température 6", "sıcaklık 6", "Temperatura 6", "teplota 6", "teplota 6")
|
||||
// MAKE_TRANSLATION(wwTemp7, "temp7", "temperature 7", "Temperatur 7", "Temperatuur 7", "Temperatur 7", "temperatura 7", "Temperatur 7", "température 7", "sıcaklık 7", "Temperatura 7", "teplota 7", "teplota 7")
|
||||
MAKE_TRANSLATION(wwPump, "pump", "pump", "Pumpe", "Pomp", "Pump", "pompa", "pumpe", "pompe", "pompa", "Pompa", "čerpadlo", "čerpadlo")
|
||||
MAKE_TRANSLATION(wwCircTc, "circtc", "circulation time controled", "zeitgesteuerte Zirkulation", "", "Tidsstyrd cirkulation", "", "", "", "", "", "riadená doba cirkulácie", "řízený čas cirkulace") // TODO translate
|
||||
MAKE_TRANSLATION(errorDisp, "errordisp", "error display", "Fehleranzeige", "", "Feldisplay", "wyświetlanie błędów", "", "", "", "", "zobrazenie chyby", "zobrazení chyb") // TODO translate
|
||||
MAKE_TRANSLATION(deltaTRet, "deltatret", "temp. diff. return valve", "Temperaturdifferenz Rücklaufventil", "", "Temperaturskillnad returventil", "różnica temp. zaworu powrotnego", "", "", "", "", "rozdiel teplôt spätného ventilu", "rozdíl teploty zpátečního ventilu") // TODO translate
|
||||
|
||||
// solar dhw and mixer dhw
|
||||
MAKE_TRANSLATION(wwMinTemp, "mintemp", "minimum temperature", "minimale Temperatur", "Minimale temperatuur", "Min. Temperatur", "temperatura minimalna", "min. temperatur", "température min.", "minimum sıcaklık", "temperatura minima", "minimálna teplota", "minimální teplota")
|
||||
MAKE_TRANSLATION(wwRedTemp, "redtemp", "reduced temperature", "reduzierte Temperatur", "Gereduceerde temperatuur", "Reducerad Temperatur", "temperatura zredukowana", "reducert temperatur", "température réduite", "düşürülmüş sıcaklık", "temperatura ridotta", "znížená teplota", "snížená teplota")
|
||||
MAKE_TRANSLATION(wwDailyTemp, "dailytemp", "daily temperature", "tägl. Temperatur", "Dagelijkse temperatuur", "Daglig temperatur", "temperatura dzienna", "dagtemperatur", "température en journée", "günlük sıcaklık", "temperatura giornaliera", "denná teplota", "denní teplota")
|
||||
MAKE_TRANSLATION(wwHotTemp, "hottemp", "extra hot temperature", "sehr heiße Temperatur", "", "Mycket varm temperatur", "", "", "", "", "", "extra vysoká teplota", "teplota extra horká") // TODO translate
|
||||
MAKE_TRANSLATION(wwKeepWarm, "keepwarm", "keep warm", "Warmhalten", "Warm houde", "Varmhållning", "utrzymywanie ciepła", "holde varmen", "maintenir chaleur", "ılık tut", "mantenimento calore", "udržovať v teple", "udržení tepla")
|
||||
MAKE_TRANSLATION(wwStatus2, "status2", "status 2", "Status 2", "Status 2", "Status 2", "status 2", "status 2", "statut 2", "durum 2", "Status 2", "stav 2", "stav 2")
|
||||
MAKE_TRANSLATION(wwPumpMod, "pumpmod", "pump modulation", "Pumpenmodulation", "Pompmodulatie", "Pumpmodulering", "modulacja pompy", "pumpemodulering", "modulation de pompe", "pompa modülasyonu", "modulazione pompa", "modulácia čerpadla", "modulace čerpadla")
|
||||
MAKE_TRANSLATION(wwFlow, "flow", "flow rate", "Volumenstrom", "Doorstroomsnelheid", "Flöde", "przepływ", "strømningshastighet", "débit", "akış hızı", "portata flusso", "prietok", "průtok")
|
||||
// MAKE_TRANSLATION(wwRetValve, "retvalve", "return valve", "Rücklauf Ventil", "", "Returventil", "", "", "", "", "", "spätný ventil", "zpětný ventil") // TODO translate
|
||||
|
||||
// extra mixer dhw
|
||||
MAKE_TRANSLATION(wwRequiredTemp, "requiredtemp", "required temperature", "benötigte Temperatur", "Benodigde temperatuur", "Nödvändig Temperatur", "temperatura wymagana", "nødvendig temperatur", "température requise", "gerekli sıcaklık", "temperatura richiesta", "požadovaná teplota", "požadovaná teplota")
|
||||
MAKE_TRANSLATION(wwDiffTemp, "difftemp", "start differential temperature", "Start Differenztemperatur", "Start differentiele temperatuur", "Start Skillnadstemperatur", "start temperatury różnicowej", "start differensialtemperatur", "température différentielle de départ", "diferansiyel sıcaklık", "avvia temperatura differenziale", "začiatok diferenciálnej teploty", "rozdílová teplota pro spuštění")
|
||||
MAKE_TRANSLATION(wwPumpStatus, "pumpstatus", "pump status in assigned dhw (PC1)", "Pumpenstatus des WWK (PC1)", "Pompstatus in WW circuit (PC1)", "Pumpstatus i VV-krets (PC1)", "stan pompy w obwodzie c.w.u. (PC1)", "Pumpestatus i VV-krets (PC1)", "état pompe dhw (PC1)", "Kullanım suyu devresindeki(PC1) pompa durumu", "stato pompa assegnato nel ciruito WW (PC1)", "stav čerpadla v pridelenom TÚV (PC1)", "stav čerpadla přiřazeného k TUV (PC1)")
|
||||
MAKE_TRANSLATION(wwTempStatus, "tempstatus", "temperature switch in assigned dhw (MC1)", "Temperaturschalter des WWK (MC1)", "Temperatuurschakeling in WW circuit (MC1)", "Temperaturventil i VV-krets (MC1)", "temperatura w obwodzie c.w.u. (MC1)", "temperaturventil i VV-krets (MC1)", "température bascule dhw (MC1).", "atanmış sıcak su devresinde sıcaklık", "interruttore di temperatura del wwk (MC1)", "teplotný spínač v priradenej TÚV (MC1)", "přepínací teplota v přiřazeném TUV (MC1)")
|
||||
MAKE_TRANSLATION(wwTemp, "temp", "current temperature", "aktuelle Temperatur", "huidige temperatuur", "Aktuell Temperatur", "temperatura c.w.u.", "aktuell temperatur", "température actuelle", "güncel sıcaklık", "temperatura attuale", "aktuálna teplota", "aktuální teplota")
|
||||
|
||||
// SM100
|
||||
MAKE_TRANSLATION(heatTransferSystem, "heattransfersystem", "heattransfer system", "Wärmeübertragungssystem", "Warmteoverdrachtssysteem", "Värmeöverföringssystem", "system wymiany ciepła", "varmeoverføringssystem", "système de transfert de chaleur", "ıs transfer sistemi", "sistema di trasferimento del calore", "systém prenosu tepla", "systém přenosu tepla")
|
||||
MAKE_TRANSLATION(externalCyl, "externalcyl", "external cylinder", "Externer Speicher", "Externe boiler", "Extern Cylinder", "zbiornik zewnętrzny", "ekstern bereder", "cylindre externe", "dış silindir", "vaso accumulo esterno", "vonkajší valec", "vnější válec")
|
||||
MAKE_TRANSLATION(thermalDisinfect, "thermaldisinfect", "thermal disinfection", "Thermische Desinfektion", "Thermische desinfectie", "Termisk Desinfektion", "dezynfekcja termiczna", "termisk desinfeksjon", "désinfection thermique", "ısıl temizlik", "disinfezione termica", "tepelná dezinfekcia", "teplotní dezinfekce")
|
||||
MAKE_TRANSLATION(heatMetering, "heatmetering", "heatmetering", "Wärmemessung", "warmtemeting", "Värmemätning", "pomiar ciepła", "varmemåling", "mesure de chaleur", "ısı ölçümü", "misurazione del calore", "meranie tepla", "měření tepla")
|
||||
MAKE_TRANSLATION(solarIsEnabled, "solarenabled", "solarmodule enabled", "Solarmodul aktiviert", "Solarmodule geactiveerd", "Solmodul Aktiverad", "system solarny", "solmodul aktivert", "module solaire activé", "güneş modu etkinleştirildi", "modulo solare attivato", "solárny modul povolený", "solární modul povolen")
|
||||
|
||||
// telegram 0x035A
|
||||
MAKE_TRANSLATION(solarPumpMode, "solarpumpmode", "solar pump mode", "Solarpumpenmodus", "Modus zonneboilerpomp", "Sol Pumpläge", "tryb pracy pompy", "solpumpemodus", "mode pompe solaire", "pompa modu", "modalità pompa solare", "režim čerpadla", "režim solárního čerpadla")
|
||||
MAKE_TRANSLATION(solarPumpKick, "solarpumpkick", "solar pump kick", "Röhrenkollektorfunktion", "Modus buizencollector", "Sol Kollektorfunktion", "wspomaganie startu pompy", "solkllektorfunksjon", "démarrage boost pompe solaire", "pompa zorunlu çalıştırma", "avvio forzato pompa", "kopnutie pumpy", "kop solárního čerpadla")
|
||||
MAKE_TRANSLATION(plainWaterMode, "plainwatermode", "plain water mode", "Südeuropafunktion", "Modus Zuid-Europa", "Sydeuropa-funktion", "tylko woda (dla Europy Południowej)", "vanlig vannmodus", "mode eau ordinaire", "sadece su modu", "modalità acqua normale", "režim čistej vody", "režim čisté vody")
|
||||
MAKE_TRANSLATION(doubleMatchFlow, "doublematchflow", "doublematchflow", "Double Match Flow", "Double Match Flow", "Dubbelmatchning Flöde", "przepływ podwójnie dopasowany", "dobbelmatch flow", "double match flow", "doublematch akışı", "carico ottimizzato dell'accumulatore ad effetto termosifone", "Tok dvojitej zhody", "dvojitý průtok")
|
||||
MAKE_TRANSLATION(solarPump2Mode, "pump2mode", "pump 2 mode", "Modus Pumpe 2", "Modus pomp 2", "Pump 2 Läge", "tryb pracy pompy 2", "pump 2 modus", "mode pompe 2", "pompa 2 modu", "modalità pompa 2", "režim čerpadla 2", "režim čerpadla 2")
|
||||
MAKE_TRANSLATION(solarPump2Kick, "pump2kick", "pump kick 2", "Startboost Pumpe 2", "Startboost pomp 2", "Pump 2 Kollektorfunktion", "wspomaganie startu pompy 2", "startboost pumpe 2", "démarrage boost pompe 2", "pompa 2 zorunlu çalıştırma", "avvio forzato pompa 2", "nútený štart čerpadla 2", "kop čerpadla 2")
|
||||
|
||||
// telegram 0x035F
|
||||
MAKE_TRANSLATION(cylPriority, "cylpriority", "cylinder priority", "Speicherpriorität", "Prioriteit boiler", "Cylinderprioritering", "priorytet cylindra", "berederprioritering", "priorité de cylindre", "silindir önceliği", "priorità vaso accumulo", "Priorita valca", "přednost válce")
|
||||
|
||||
// telegram 0x380
|
||||
MAKE_TRANSLATION(climateZone, "climatezone", "climate zone", "Klimazone", "klimaatzone", "Klimatzon", "strefa klimatyczna", "klimasone", "zone de climat", "iklim alanı", "zona clima", "klimatická zóna", "klimatická zóna")
|
||||
MAKE_TRANSLATION(collector1Area, "collector1area", "collector 1 area", "Kollektorfläche 1", "oppervlakte collector 1", "Kollektor 1 Area", "powierzchnia kolektora 1", "kollektor 1 område", "zone collecteur 1", "kollektör 1 alan", "area collettore 1", "oblasť kolektora 1", "plocha kolektoru 1")
|
||||
MAKE_TRANSLATION(collector1Type, "collector1type", "collector 1 type", "Kollektortyp 1", "Type collector 1", "Kollektor 1 Typ", "typ kolektora 1", "kollektor 1 type", "type collecteur 1", "kollektör 1 tip", "tipo collettore 1", "kolektor 1 typ", "typ kolektoru 1")
|
||||
MAKE_TRANSLATION(collector2Area, "collector2area", "collector 2 area", "Kollektorfläche 2", "Oppervlakte collector 2", "Kollektor 2 Area", "powierzchnia kolektora 2", "kollektor 2 område", "zone collecteur 2", "kollektör 2 alan", "area collettore 2", "oblasť kolektora 2", "plocha kolektoru 2")
|
||||
MAKE_TRANSLATION(collector2Type, "collector2type", "collector 2 type", "Kollektortyp 2", "Type collector 2", "Kollektor 2 Typ", "typ kolektora 2", "kollektor 2 type", "type collecteur 2", "kollektör 2 tip", "tipo collettore 2", "kolektor 2 typ", "typ kolektoru 2")
|
||||
|
||||
// telegram 0x0363 heatCounter
|
||||
MAKE_TRANSLATION(heatCntFlowTemp, "heatcntflowtemp", "heat counter flow temperature", "Wärmezähler Vorlauftemperatur", "Aanvoertemperatuur warmteenergiemeter", "Värmeräknare Flödestemperatur", "temperatura zasilania ciepłomierza", "varmeenergimåler turtemperatur", "température flux compteur chaleur", "ısı sayacı akış sıcaklığı", "Temperatura di mandata del contatore di calore", "teplota prúdu počítadla tepla", "počítadlo teploty průtoku")
|
||||
MAKE_TRANSLATION(heatCntRetTemp, "heatcntrettemp", "heat counter return temperature", "Wärmezähler Rücklauftemperatur", "Retourtemperatuur warmteenergiemeter", "Värmeräknare Returtemperatur", "temperatura powrotu ciepłomierza", "varmeenergimåler returtemperatur", "température retour compteur chaleur", "ısı sayacı dönüş sıcaklığı", "Temperatura di ritorno del contatore di calore", "teplota spiatočky počítadla tepla", "počítadlo teploty zpátečky")
|
||||
MAKE_TRANSLATION(heatCnt, "heatcnt", "heat counter impulses", "Wärmezähler Impulse", "Warmteenergiemeter pulsen", "Värmeräknare Impuls", "liczba impulsów ciepłomierza", "varmemåler impuls", "impulsions compteur chaleur", "ısı sayacı atış adedi", "contacalore a impulsi", "Impulzy počítadla tepla", "počítadlo impulzů")
|
||||
MAKE_TRANSLATION(swapFlowTemp, "swapflowtemp", "swap flow temperature (TS14)", "Austausch Vorlauftemperatur (TS14)", "Aanvoertemperatuur verwisselaar (TS14)", "Växlingstemperatur Flöde (TS14)", "temperatura zasilania wymiennika", "veksler turledningstemperatur (TS14)", "température flux échangeur (TS14)", "değişim akış sıcaklığı(TS14)", "Scambiare la temperatura di mandata (TS14)", "swap flow temperature (TS14)", "přepínací průtoková teplota (TS14)")
|
||||
MAKE_TRANSLATION(swapRetTemp, "swaprettemp", "swap return temperature (TS15)", "Austausch Rücklauftemperatur (TS15)", "Retourtemperatuur verwisselaar (TS15)", "Växlingstemperatur Returflöde (TS15)", "temperatura powrotu wymiennika", "veksler returledningstemperatur (TS15)", "température retour échangeur (TS15)", "değişim dönüş sıcaklığı(TS15)", "Scambiare la temperatura di ritorno (TS15)", "výmena teploty spiatočky (TS15)", "přepínací teplota zpátečky (TS15)")
|
||||
|
||||
// switch
|
||||
MAKE_TRANSLATION(activated, "activated", "activated", "Aktiviert", "Geactiveerd", "Aktiverad", "aktywowany", "aktivert", "activé", "başladı", "attivato", "aktivovaný", "aktivováno")
|
||||
MAKE_TRANSLATION(status, "status", "status", "Status", "Status", "Status", "status", "status", "statut", "durum", "Stato", "stav", "stav")
|
||||
|
||||
// RF sensor, id 0x40, telegram 0x435
|
||||
MAKE_TRANSLATION(RFTemp, "rftemp", "RF room temperature sensor", "RF Raumtemperatursensor", "RF ruimtetemperatuur sensor", "RF Rumsgivare Temperatur", "bezprzewodowy czujnik temperatury pomieszczenia", "RF romsgiver temp", "capteur de température de pièce RF", "RF oda sıcaklık sensörü", "Sensore di temperatura ambiente RF", "RF snímač izbovej teploty", "RF senzor teploty místnosti")
|
||||
|
||||
// ventilation
|
||||
MAKE_TRANSLATION(outFresh, "outfresh", "outdoor fresh air", "Außenlufttemp.", "temperatuur buitenlucht", "Utelufttemperatur", "świeże powietrze z zewnątrz", "", "", "dış ortam taze hava", "aria fresca esterna", "čerstvý vzduch vonku", "venkovní čerstvý vzduch") // TODO translate
|
||||
MAKE_TRANSLATION(inFresh, "infresh", "indoor fresh air", "Zulufttemp.", "temperatuur aanvoer", "Tillufttemperatur", "nawiew", "", "", "iç ortam taze hava", "aria fresca interna", "čerstvý vzduch v interiéri", "vnitřní čerstvý vzduch") // TODO translate
|
||||
MAKE_TRANSLATION(outEx, "outexhaust", "outdoor exhaust air", "Fortlufttemp.", "uitlaatemperatuur buiten", "Utgående lufttemperatur", "zużyte powietrze z wewnątrz", "", "", "dış ortam egsoz", "aria di scarico esterna", "vonkajší odpadový vzduch", "venkovní odpadní vzduch") // TODO translate
|
||||
MAKE_TRANSLATION(inEx, "inexhaust", "indoor exhaust air", "Ablufttemp.", "uitlaattemperatuur binnen", "Frånlufttemperatur", "wywiew", "", "", "iç ortam egsoz", "aria di scarico interna", "vnútorný odpadový vzduch", "vnitřní odpadní vzduch") // TODO translate
|
||||
MAKE_TRANSLATION(ventMode, "ventmode", "ventilation mode", "Belüftungsmodus", "ventilatiemodus", "ventilationsläge", "tryb wentylacji", "", "", "havalandırma modu", "modalità di ventilazione", "režim vetrania", "režim ventilace") // TODO translate
|
||||
MAKE_TRANSLATION(ventInSpeed, "ventinspeed", "in blower speed", "Zuluftdrehzahl", "toerental aanvoerventilator", "Tilluftflöde", "prędkość wentylatora nawiewu", "", "", "iç fan hızı", "velocità aria di alimentazione", "rýchlosť ventilátora", "rychlost vnitřního ventilátoru") // TODO translate
|
||||
MAKE_TRANSLATION(ventOutSpeed, "ventoutspeed", "out blower speed", "Abluftdrehzahl", "toerental afvoerventilator", "Frånluftflöde", "prędkość wentylatora wywiewu", "", "", "dış fan hızı", "velocità aria di scarico", "rýchlosť výstupného ventilátora", "rychlost venkovního ventilátoru") // TODO translate
|
||||
MAKE_TRANSLATION(airquality, "airquality", "air quality (voc)", "Luftqualität (VOC)", "luchtkwaliteit (VOC)", "Luftkvalitet (VOC)", "jakość powietrza", "", "", "hava kalitesi(voc)", "qualità aria (VOC)", "kvalita vzduchu (voc)", "kvalita vzduchu (VOC)") // TODO translate
|
||||
|
||||
// EM100
|
||||
MAKE_TRANSLATION(minV, "minv", "min volt.", "min. Spannung", "", "min. spänning", "minimalne napięcie", "", "", "", "", "min. napätie", "minimální napětí") // TODO translate
|
||||
MAKE_TRANSLATION(maxV, "maxv", "max volt.", "max. Spannung", "", "max. spänning", "maksymalne napięcie", "", "", "", "", "max. napätie", "maximální napětí") // TODO translate
|
||||
MAKE_TRANSLATION(minT, "mint", "min temp.", "min. Temperatur", "", "min. temperatur", "minimalna temperatura", "", "", "", "", "min. tepl.", "minimální teplota") // TODO translate
|
||||
MAKE_TRANSLATION(maxT, "maxt", "max temp.", "max. Temperatur", "", "max. temperatur", "maksymalna temperatura", "", "", "", "", "max. tepl.", "maximální teplota") // TODO translate
|
||||
MAKE_TRANSLATION(setPoint, "setpoint", "set temp.", "Solltemperatur", "", "Börvärde", "temperatura nastawu", "", "", "", "", "pož. teplota", "nastavená teplota") // TODO translate
|
||||
MAKE_TRANSLATION(setPower, "setpower", "request power", "Sollleistung", "", "Böreffekt", "zadana moc", "", "", "", "", "pož. výkon", "požadovaný výkon") // TODO translate
|
||||
MAKE_TRANSLATION(dip, "dip", "dip switch", "DIP-Schalter", "", "DIP-strömställare", "przełącznik DIP", "", "", "", "", "dip prepínač", "přepínač DIP") // TODO translate
|
||||
MAKE_TRANSLATION(outPower, "outpow", "output IO1", "Ausgang IO1", "", "Utgångsffekt", "wyjście IO1", "", "", "", "", "výstup IO1", "výstup IO1") // TODO translate
|
||||
MAKE_TRANSLATION(input, "input", "input", "Eingang", "", "Ingång", "wejście", "", "", "", "", "vstup", "vstup") // TODO translate
|
||||
|
||||
/*
|
||||
// unknown fields to track (SM10), only for testing
|
||||
// **** NO TRANSLATION NEEDED ****
|
||||
MAKE_TRANSLATION(data11, "data11", "unknown datafield 11", "", "", "okänt datafält 11", "nieznane pole danych 11", "", "", "", "", "neznáme dátové pole 11", "") // TODO translate
|
||||
MAKE_TRANSLATION(data12, "data12", "unknown datafield 12", "", "", "okänt datafält 12", "nieznane pole danych 12", "", "", "", "", "neznáme dátové pole 12", "") // TODO translate
|
||||
MAKE_TRANSLATION(data8, "data8", "unknown datafield 8", "", "", "okänt datafält 8", "nieznane pole danych 8", "", "", "", "", "neznáme dátové pole 8", "") // TODO translate
|
||||
MAKE_TRANSLATION(data0, "data0", "unknown datafield 0", "", "", "okänt datafält 0", "nieznane pole danych 0", "", "", "", "", "neznáme dátové pole 0", "") // TODO translate
|
||||
MAKE_TRANSLATION(data1, "data1", "unknown datafield 1", "", "", "okänt datafält 1", "nieznane pole danych 1", "", "", "", "", "neznáme dátové pole 1", "") // TODO translate
|
||||
MAKE_TRANSLATION(setting3, "setting3", "unknown setting 3", "", "", "okänd inställning 3", "nieznane ustawienie 3", "", "", "", "", "neznáme dátové pole 3", "") // TODO translate
|
||||
MAKE_TRANSLATION(setting4, "setting4", "unknown setting 4", "", "", "okänd inställning 4", "nieznane ustawienie 4", "", "", "", "", "neznáme dátové pole 4", "") // TODO translate
|
||||
*/
|
||||
|
||||
// clang-format on
|
||||
31
src/core/main.cpp
Normal file
31
src/core/main.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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"
|
||||
|
||||
using namespace emsesp;
|
||||
|
||||
static EMSESP application; // the main application
|
||||
|
||||
void setup() {
|
||||
application.start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
application.loop();
|
||||
}
|
||||
531
src/core/modbus.cpp
Normal file
531
src/core/modbus.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
/**
|
||||
* TODO: verwendete libs in readme hinzufügen
|
||||
*/
|
||||
#include "modbus.h"
|
||||
#include "modbus_entity_parameters.hpp"
|
||||
#include "emsesp.h"
|
||||
#include "emsdevice.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
#ifdef EMSESP_STANDALONE
|
||||
// no eModbus lib in standalone build
|
||||
enum FunctionCode : uint8_t { WRITE_HOLD_REGISTER = 0x06, WRITE_MULT_REGISTERS = 0x10 };
|
||||
#endif
|
||||
|
||||
uuid::log::Logger Modbus::logger_{F_(modbus), uuid::log::Facility::DAEMON};
|
||||
|
||||
void Modbus::start(uint8_t systemServerId, uint16_t port, uint8_t max_clients, uint32_t timeout) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!check_parameter_order()) {
|
||||
LOG_ERROR("Unable to enable Modbus - the parameter list order is corrupt. This is a firmware bug.");
|
||||
return;
|
||||
}
|
||||
|
||||
modbusServer_ = new ModbusServerTCPasync();
|
||||
modbusServer_->registerWorker(systemServerId, READ_INPUT_REGISTER, [this](auto && request) { return handleSystemRead(request); });
|
||||
|
||||
for (uint8_t i = EMSdevice::DeviceType::BOILER; i < EMSdevice::DeviceType::UNKNOWN; i++) {
|
||||
if (i != systemServerId) {
|
||||
modbusServer_->registerWorker(i, READ_INPUT_REGISTER, [this](auto && request) { return handleRead(request); });
|
||||
modbusServer_->registerWorker(i, READ_HOLD_REGISTER, [this](auto && request) { return handleRead(request); });
|
||||
modbusServer_->registerWorker(i, WRITE_HOLD_REGISTER, [this](auto && request) { return handleWrite(request); });
|
||||
modbusServer_->registerWorker(i, WRITE_MULT_REGISTERS, [this](auto && request) { return handleWrite(request); });
|
||||
}
|
||||
}
|
||||
modbusServer_->start(port, max_clients, timeout);
|
||||
LOG_INFO("Starting Modbus service (ID %d, port %d)", systemServerId, port);
|
||||
#else
|
||||
if (!check_parameter_order()) {
|
||||
LOG_ERROR("Unable to enable Modbus - the parameter list order is corrupt. This is a firmware bug.");
|
||||
}
|
||||
LOG_INFO("Modbus deactivated in standalone build.");
|
||||
#endif
|
||||
}
|
||||
|
||||
// this is currently never called, just for good measure
|
||||
void Modbus::stop() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
modbusServer_->stop();
|
||||
// virtual ~ModbusServerTCPasync() = default;
|
||||
// delete modbusServer_;
|
||||
modbusServer_ = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check that the Modbus parameters defined in modbus_entity_parameters.cpp are correctly ordered
|
||||
bool Modbus::check_parameter_order() {
|
||||
EntityModbusInfo const * prev = nullptr;
|
||||
bool isFirst = true;
|
||||
for (const auto & mi : modbus_register_mappings) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else if (prev == nullptr) {
|
||||
LOG_ERROR("Error checking modbus parameters %s.", mi.short_name);
|
||||
return false;
|
||||
} else if (!prev->isLessThan(mi)) {
|
||||
LOG_ERROR("Error in modbus parameters: %s must be listed before %s.", mi.short_name, prev->short_name);
|
||||
return false;
|
||||
}
|
||||
prev = &mi;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int8_t Modbus::tag_to_type(int8_t tag) {
|
||||
// this coulod even be an array
|
||||
switch (tag) {
|
||||
case DeviceValue::TAG_NONE:
|
||||
return TAG_TYPE_NONE;
|
||||
|
||||
case DeviceValue::TAG_DEVICE_DATA:
|
||||
return TAG_TYPE_DEVICE_DATA;
|
||||
|
||||
case DeviceValue::TAG_HC1:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_HC2:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_HC3:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_HC4:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_HC5:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_HC6:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_HC7:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_HC8:
|
||||
return TAG_TYPE_HC;
|
||||
|
||||
case DeviceValue::TAG_DHW1:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW2:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW3:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW4:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW5:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW6:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW7:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW8:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW9:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_DHW10:
|
||||
return TAG_TYPE_DHW;
|
||||
|
||||
case DeviceValue::TAG_AHS1:
|
||||
return TAG_TYPE_AHS;
|
||||
|
||||
case DeviceValue::TAG_HS1:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS2:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS3:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS4:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS5:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS6:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS7:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS8:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS9:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS10:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS11:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS12:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS13:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS14:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS15:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
case DeviceValue::TAG_HS16:
|
||||
return TAG_TYPE_HS;
|
||||
|
||||
default:
|
||||
return INVALID_TAG_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
/* DEBUG
|
||||
template <typename TInputIter>
|
||||
std::string make_hex_string(TInputIter first, TInputIter last) {
|
||||
std::ostringstream ss;
|
||||
ss << std::hex << std::setfill('0');
|
||||
|
||||
while (first != last)
|
||||
ss << std::setw(2) << static_cast<int>(*first++);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
ModbusMessage Modbus::handleSystemRead(const ModbusMessage & request) {
|
||||
ModbusMessage response;
|
||||
|
||||
uint16_t start_address = 0;
|
||||
uint16_t num_words = 0;
|
||||
|
||||
request.get(2, start_address);
|
||||
request.get(4, num_words);
|
||||
|
||||
LOG_DEBUG("Got request for serverId %d, startAddress %d, numWords %d", request.getServerID(), start_address, num_words);
|
||||
|
||||
if (start_address < 1000) {
|
||||
switch (start_address) {
|
||||
case 1:
|
||||
response.add(request.getServerID());
|
||||
response.add(request.getFunctionCode());
|
||||
response.add((uint8_t)2);
|
||||
response.add((uint16_t)EMSESP::emsdevices.size());
|
||||
break;
|
||||
|
||||
default:
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
}
|
||||
} else if (start_address < 1000 + EMSESP::emsdevices.size()) {
|
||||
const auto & dev = EMSESP::emsdevices[start_address - 1000];
|
||||
response.add(request.getServerID());
|
||||
response.add(request.getFunctionCode());
|
||||
response.add((uint8_t)2);
|
||||
response.add(dev->device_type());
|
||||
response.add(dev->device_id());
|
||||
} else {
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
ModbusMessage Modbus::handleRead(const ModbusMessage & request) {
|
||||
ModbusMessage response;
|
||||
|
||||
uint8_t device_type = request.getServerID(); // the server ID is the same as the device type
|
||||
uint16_t start_address = 0;
|
||||
uint16_t num_words = 0;
|
||||
|
||||
request.get(2, start_address);
|
||||
request.get(4, num_words);
|
||||
|
||||
LOG_DEBUG("Got read request for serverId %d, startAddress %d, numWords %d", device_type, start_address, num_words);
|
||||
|
||||
// each register block corresponds to a device value tag
|
||||
auto tag = (uint8_t)(start_address / REGISTER_BLOCK_SIZE);
|
||||
auto tag_type = tag_to_type(tag);
|
||||
|
||||
if (tag_type == INVALID_TAG_TYPE) {
|
||||
// invalid register block, does not correspond to an existing tag
|
||||
LOG_ERROR("invalid register block, does not correspond to an existing tag");
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
return response;
|
||||
}
|
||||
|
||||
auto register_offset = start_address - tag * REGISTER_BLOCK_SIZE;
|
||||
|
||||
// binary search in modbus infos
|
||||
auto key = EntityModbusInfoKey(device_type, tag_type, register_offset);
|
||||
|
||||
const auto & modbusInfo = std::lower_bound(std::begin(modbus_register_mappings),
|
||||
std::end(modbus_register_mappings),
|
||||
key,
|
||||
[](const EntityModbusInfo & a, const EntityModbusInfoKey & b) { return a.isLessThan(b); });
|
||||
|
||||
|
||||
if (modbusInfo == std::end(modbus_register_mappings) || !modbusInfo->equals(key)) {
|
||||
// combination of device_type/tag_type/register_offset does not exist
|
||||
LOG_ERROR("combination of device_type/tag_type/register_offset does not exist");
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
return response;
|
||||
}
|
||||
|
||||
// only reading a single value at a time is supported for now
|
||||
if (num_words != modbusInfo->registerCount) {
|
||||
// number of registers requested does not match actual register count for entity
|
||||
LOG_ERROR("number of registers requested does not match actual register count for entity");
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
return response;
|
||||
}
|
||||
|
||||
auto buf = std::vector<uint16_t>(num_words);
|
||||
int error_code = -1;
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->device_type() == device_type) {
|
||||
error_code = emsdevice->get_modbus_value(tag, modbusInfo->short_name, buf);
|
||||
if (!error_code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error_code) {
|
||||
if (uuid::get_uptime_sec() > 60 || error_code < -2) { // suppress not found messages for the first minute
|
||||
LOG_ERROR("Unable to read raw device value %s for tag=%d - error_code = %d", modbusInfo->short_name, (int)tag, error_code);
|
||||
}
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), SERVER_DEVICE_FAILURE);
|
||||
return response;
|
||||
}
|
||||
|
||||
response.add(request.getServerID());
|
||||
response.add(request.getFunctionCode());
|
||||
response.add((uint8_t)(num_words * 2));
|
||||
for (auto & value : buf)
|
||||
response.add(value);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
ModbusMessage Modbus::handleWrite(const ModbusMessage & request) {
|
||||
ModbusMessage response;
|
||||
|
||||
uint8_t device_type = request.getServerID(); // the server ID is the same as the device type
|
||||
uint8_t function_code = request.getFunctionCode();
|
||||
uint16_t start_address = 0;
|
||||
uint16_t num_words = 0;
|
||||
uint8_t byte_count = 0;
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
if (function_code == WRITE_MULT_REGISTERS) {
|
||||
request.get(2, start_address);
|
||||
request.get(4, num_words);
|
||||
request.get(6, byte_count);
|
||||
request.get(7, data, byte_count);
|
||||
} else if (function_code == WRITE_HOLD_REGISTER) {
|
||||
num_words = 1;
|
||||
byte_count = 2;
|
||||
request.get(2, start_address);
|
||||
request.get(4, data, byte_count);
|
||||
} else {
|
||||
LOG_ERROR("Function code %d is not implemented", function_code);
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
|
||||
return response;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Got write request for serverId %d, startAddress %d, numWords %d, byteCount %d", device_type, start_address, num_words, byte_count);
|
||||
|
||||
// each register block corresponds to a device value tag
|
||||
auto tag = (uint8_t)(start_address / REGISTER_BLOCK_SIZE);
|
||||
auto tag_type = tag_to_type(tag);
|
||||
|
||||
if (tag_type == INVALID_TAG_TYPE) {
|
||||
// invalid register block, does not correspond to an existing tag
|
||||
LOG_ERROR("invalid register block (%d), does not correspond to an existing tag", tag);
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
return response;
|
||||
}
|
||||
|
||||
auto register_offset = start_address - tag * REGISTER_BLOCK_SIZE;
|
||||
|
||||
LOG_DEBUG("Tag %d, offset %d", tag, register_offset);
|
||||
|
||||
|
||||
// binary search in modbus infos
|
||||
auto key = EntityModbusInfoKey(device_type, tag_type, register_offset);
|
||||
auto modbusInfo = std::lower_bound(std::begin(modbus_register_mappings),
|
||||
std::end(modbus_register_mappings),
|
||||
key,
|
||||
[](const EntityModbusInfo & mi, const EntityModbusInfoKey & k) { return mi.isLessThan(k); });
|
||||
|
||||
if (modbusInfo == std::end(modbus_register_mappings) || !modbusInfo->equals(key)) {
|
||||
// combination of device_type/tag_type/register_offset does not exist
|
||||
LOG_ERROR("combination of device_type (%d)/tag_type (%d)/register_offset (%d) does not exist",
|
||||
key.device_type,
|
||||
key.device_value_tag_type,
|
||||
key.registerOffset);
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
return response;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Writing to entity %s", modbusInfo->short_name);
|
||||
|
||||
// only writing a single value at a time is supported for now
|
||||
if (num_words != modbusInfo->registerCount) {
|
||||
// number of registers requested does not match actual register count for entity
|
||||
LOG_ERROR("number of registers (%d) requested does not match actual register count (%d) for entity", num_words, modbusInfo->registerCount);
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
return response;
|
||||
}
|
||||
|
||||
JsonDocument input_doc;
|
||||
JsonObject input = input_doc.to<JsonObject>();
|
||||
int error_code = -1;
|
||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||
if (emsdevice->device_type() == device_type) {
|
||||
error_code = emsdevice->modbus_value_to_json(tag, modbusInfo->short_name, data, input);
|
||||
if (!error_code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error_code) {
|
||||
// error getting modbus value as json
|
||||
LOG_ERROR("error getting modbus value as json, error code = %d", error_code);
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_ADDRESS);
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (tag < DeviceValueTAG::TAG_HC1) {
|
||||
path = std::string("api/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + modbusInfo->short_name;
|
||||
} else {
|
||||
path = std::string("api/") + std::string(EMSdevice::device_type_2_device_name(device_type)) + "/" + EMSdevice::tag_to_mqtt(tag) + "/"
|
||||
+ modbusInfo->short_name;
|
||||
}
|
||||
|
||||
LOG_DEBUG("path: %s\n", path.c_str());
|
||||
|
||||
std::string inputStr;
|
||||
serializeJson(input, inputStr);
|
||||
LOG_DEBUG("input: %s\n", inputStr.c_str());
|
||||
|
||||
JsonDocument output_doc;
|
||||
JsonObject output = output_doc.to<JsonObject>();
|
||||
uint8_t return_code = Command::process(path.c_str(), true, input, output); // modbus is always authenticated
|
||||
|
||||
if (return_code != CommandRet::OK) {
|
||||
char error[100];
|
||||
if (output.size()) {
|
||||
snprintf(error,
|
||||
sizeof(error),
|
||||
"Modbus write command failed with error %s (%s)",
|
||||
(const char *)output["message"],
|
||||
Command::return_code_string(return_code));
|
||||
} else {
|
||||
snprintf(error, sizeof(error), "Modbus write command failed with error code (%s)", Command::return_code_string(return_code));
|
||||
}
|
||||
LOG_ERROR(error);
|
||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_DATA_VALUE);
|
||||
} else {
|
||||
// all good
|
||||
response.add(request.getServerID());
|
||||
response.add(request.getFunctionCode());
|
||||
response.add(start_address);
|
||||
response.add(num_words);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
// return the relative register start offset for a DeviceValue, i.e. the address within the
|
||||
// register block corresponding to the value's tag type.
|
||||
int Modbus::getRegisterOffset(const DeviceValue & dv) {
|
||||
auto it = std::find_if(std::begin(modbus_register_mappings), std::end(modbus_register_mappings), [&](const EntityModbusInfo & mi) {
|
||||
return mi.device_type == dv.device_type && mi.device_value_tag_type == tag_to_type(dv.tag) && !strcmp(mi.short_name, dv.short_name);
|
||||
});
|
||||
if (it != std::end(modbus_register_mappings)) {
|
||||
return it->registerOffset;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return the number of registers
|
||||
int Modbus::getRegisterCount(const DeviceValue & dv) {
|
||||
auto it = std::find_if(std::begin(modbus_register_mappings), std::end(modbus_register_mappings), [&](const EntityModbusInfo & mi) {
|
||||
return mi.device_type == dv.device_type && mi.device_value_tag_type == tag_to_type(dv.tag) && !strcmp(mi.short_name, dv.short_name);
|
||||
});
|
||||
if (it != std::end(modbus_register_mappings)) {
|
||||
// look up actual size
|
||||
return it->registerCount;
|
||||
} else {
|
||||
// guess based on type
|
||||
switch (dv.type) {
|
||||
case DeviceValue::BOOL: // 8 bit
|
||||
case DeviceValue::INT8:
|
||||
case DeviceValue::UINT8:
|
||||
case DeviceValue::INT16:
|
||||
case DeviceValue::UINT16:
|
||||
case DeviceValue::ENUM: // 8 bit
|
||||
return 1;
|
||||
|
||||
case DeviceValue::UINT24:
|
||||
case DeviceValue::UINT32:
|
||||
case DeviceValue::TIME: // 32 bit
|
||||
return 2;
|
||||
|
||||
case DeviceValue::CMD: {
|
||||
// calculate a sensible register size from min, max and numeric_operator
|
||||
uint32_t num_values = std::max(dv.max, (uint32_t)abs(dv.min));
|
||||
int num_registers = 0;
|
||||
|
||||
if (num_values <= (1L << 8))
|
||||
num_registers = 1;
|
||||
else if (num_values <= (1L << 16))
|
||||
num_registers = 2;
|
||||
else if (num_values <= (1L << 32))
|
||||
num_registers = 4;
|
||||
else
|
||||
LOG_ERROR("num_registers is too big to be encoded with modbus registers");
|
||||
|
||||
LOG_DEBUG("Value for CMD '%s' can take on %ld values and is encoded in %d registers", dv.short_name, num_values, num_registers);
|
||||
|
||||
return num_registers;
|
||||
}
|
||||
|
||||
case DeviceValue::STRING:
|
||||
break; // impossible to guess, needs to be hardcoded
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return the absolute register start address for a DeviceValue
|
||||
int Modbus::getRegisterStartAddress(const DeviceValue & dv) {
|
||||
return dv.tag * REGISTER_BLOCK_SIZE + getRegisterOffset(dv);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
110
src/core/modbus.h
Normal file
110
src/core/modbus.h
Normal file
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// Created by Michael Heyse on 08.02.24.
|
||||
//
|
||||
|
||||
#ifndef EMSESP_MODBUS_H_
|
||||
#define EMSESP_MODBUS_H_
|
||||
|
||||
#include "helpers.h"
|
||||
#include "emsdevice.h"
|
||||
#include "emsdevicevalue.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#include <../test/test_modbus/modbus_test.h>
|
||||
#endif
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <ModbusServerTCPasync.h>
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Modbus {
|
||||
public:
|
||||
static const int REGISTER_BLOCK_SIZE = 1000;
|
||||
|
||||
void start(uint8_t systemServerId, uint16_t port, uint8_t max_clients, uint32_t timeout);
|
||||
void stop();
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
int getRegisterOffset(const DeviceValue & dv);
|
||||
int getRegisterCount(const DeviceValue & dv);
|
||||
int getRegisterStartAddress(const DeviceValue & dv);
|
||||
#endif
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
struct EntityModbusInfoKey {
|
||||
const uint8_t device_type;
|
||||
const uint8_t device_value_tag_type;
|
||||
const uint16_t registerOffset;
|
||||
|
||||
EntityModbusInfoKey(uint8_t deviceType, uint8_t deviceValueTagType, uint16_t registerOffset)
|
||||
: device_type(deviceType)
|
||||
, device_value_tag_type(deviceValueTagType)
|
||||
, registerOffset(registerOffset) {
|
||||
}
|
||||
|
||||
bool equals(const EntityModbusInfoKey & other) const {
|
||||
return device_type == other.device_type && device_value_tag_type == other.device_value_tag_type && registerOffset == other.registerOffset;
|
||||
}
|
||||
};
|
||||
|
||||
struct EntityModbusInfo {
|
||||
const uint8_t device_type;
|
||||
const uint8_t device_value_tag_type;
|
||||
const char * const short_name;
|
||||
const uint16_t registerOffset;
|
||||
const uint16_t registerCount;
|
||||
|
||||
bool equals(const EntityModbusInfoKey & other) const {
|
||||
return device_type == other.device_type && device_value_tag_type == other.device_value_tag_type && registerOffset == other.registerOffset;
|
||||
}
|
||||
|
||||
bool isLessThan(const EntityModbusInfoKey & other) const {
|
||||
return device_type < other.device_type || ((device_type == other.device_type) && (device_value_tag_type < other.device_value_tag_type))
|
||||
|| ((device_type == other.device_type) && (device_value_tag_type == other.device_value_tag_type) && (registerOffset < other.registerOffset));
|
||||
}
|
||||
|
||||
bool isLessThan(const EntityModbusInfo & other) const {
|
||||
return device_type < other.device_type || ((device_type == other.device_type) && (device_value_tag_type < other.device_value_tag_type))
|
||||
|| ((device_type == other.device_type) && (device_value_tag_type == other.device_value_tag_type) && (registerOffset < other.registerOffset));
|
||||
}
|
||||
};
|
||||
|
||||
enum DeviceValueTAGType : int8_t {
|
||||
TAG_TYPE_NONE = DeviceValue::DeviceValueTAG::TAG_NONE,
|
||||
TAG_TYPE_DEVICE_DATA = DeviceValue::DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
TAG_TYPE_HC = DeviceValue::DeviceValueTAG::TAG_HC1,
|
||||
TAG_TYPE_DHW = DeviceValue::DeviceValueTAG::TAG_DHW1,
|
||||
TAG_TYPE_AHS = DeviceValue::DeviceValueTAG::TAG_AHS1,
|
||||
TAG_TYPE_HS = DeviceValue::DeviceValueTAG::TAG_HS1,
|
||||
INVALID_TAG_TYPE = -2
|
||||
};
|
||||
|
||||
static const std::initializer_list<EntityModbusInfo> modbus_register_mappings;
|
||||
|
||||
static int8_t tag_to_type(int8_t tag);
|
||||
static bool check_parameter_order();
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
ModbusServerTCPasync * modbusServer_;
|
||||
#endif
|
||||
|
||||
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||
public:
|
||||
#endif
|
||||
|
||||
static ModbusMessage handleSystemRead(const ModbusMessage & request);
|
||||
static ModbusMessage handleRead(const ModbusMessage & request);
|
||||
static ModbusMessage handleWrite(const ModbusMessage & request);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif //EMSESP_MODBUS_H_
|
||||
576
src/core/modbus_entity_parameters.hpp
Normal file
576
src/core/modbus_entity_parameters.hpp
Normal file
@@ -0,0 +1,576 @@
|
||||
#include "modbus.h"
|
||||
#include "emsdevice.h"
|
||||
|
||||
/*
|
||||
* This file is auto-generated. Do not modify.
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using dt = EMSdevice::DeviceType;
|
||||
|
||||
#define REGISTER_MAPPING(device_type, device_value_tag_type, long_name, modbus_register_offset, modbus_register_count) \
|
||||
{ device_type, device_value_tag_type, long_name[0], modbus_register_offset, modbus_register_count }
|
||||
|
||||
// IMPORTANT: This list MUST be ordered by keys "device_type", "device_value_tag_type" and "modbus_register_offset" in this order.
|
||||
const std::initializer_list<Modbus::EntityModbusInfo> Modbus::modbus_register_mappings = {
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(reset), 0, 1), // reset
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(forceHeatingOff), 1, 1), // heatingoff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingActive), 2, 1), // heatingactive
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tapwaterActive), 3, 1), // tapwateractive
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(selFlowTemp), 4, 1), // selflowtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingPumpMod), 5, 1), // heatingpumpmod
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(outdoorTemp), 6, 1), // outdoortemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curFlowTemp), 7, 1), // curflowtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(retTemp), 8, 1), // rettemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(switchTemp), 9, 1), // switchtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(sysPress), 10, 1), // syspress
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilTemp), 11, 1), // boiltemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(headertemp), 12, 1), // headertemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingActivated), 13, 1), // heatingactivated
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingTemp), 14, 1), // heatingtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingPump), 15, 1), // heatingpump
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pumpModMax), 16, 1), // pumpmodmax
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pumpModMin), 17, 1), // pumpmodmin
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pumpMode), 18, 1), // pumpmode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pumpCharacter), 19, 1), // pumpcharacter
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pumpDelay), 20, 1), // pumpdelay
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pumpOnTemp), 21, 1), // pumpontemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(selBurnPow), 22, 1), // selburnpow
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curBurnPow), 23, 1), // curburnpow
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnStarts), 24, 2), // burnstarts
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnWorkMin), 26, 2), // burnworkmin
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burn2WorkMin), 28, 2), // burn2workmin
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatWorkMin), 30, 2), // heatworkmin
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatStarts), 32, 2), // heatstarts
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(UBAuptime), 34, 2), // ubauptime
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(lastCode), 36, 28), // lastcode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(serviceCode), 64, 2), // servicecode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(serviceCodeNumber), 66, 1), // servicecodenumber
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maintenanceMessage), 67, 2), // maintenancemessage
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maintenanceType), 69, 1), // maintenance
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maintenanceTime), 70, 1), // maintenancetime
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maintenanceDate), 71, 6), // maintenancedate
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(emergencyOps), 77, 1), // emergencyops
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(emergencyTemp), 78, 1), // emergencytemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgTotal), 79, 2), // nrgtotal
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat), 81, 2), // nrgheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgCool), 83, 2), // nrgcool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(meterTotal), 85, 2), // metertotal
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(meterComp), 87, 2), // metercomp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(meterEHeat), 89, 2), // metereheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(meterHeat), 91, 2), // meterheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(meterCool), 93, 2), // metercool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(upTimeTotal), 95, 2), // uptimetotal
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(upTimeControl), 97, 2), // uptimecontrol
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(upTimeCompHeating), 99, 2), // uptimecompheating
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(upTimeCompCooling), 101, 2), // uptimecompcooling
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(upTimeCompPool), 103, 2), // uptimecomppool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(totalCompStarts), 105, 2), // totalcompstarts
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingStarts), 107, 2), // heatingstarts
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(coolingStarts), 109, 2), // coolingstarts
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(poolStarts), 111, 2), // poolstarts
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgConsTotal), 113, 2), // nrgconstotal
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgConsCompTotal), 115, 2), // nrgconscomptotal
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgConsCompHeating), 117, 2), // nrgconscompheating
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgConsCompCooling), 119, 2), // nrgconscompcooling
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgConsCompPool), 121, 2), // nrgconscomppool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxElecHeatNrgConsTotal), 123, 2), // auxelecheatnrgconstotal
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxElecHeatNrgConsHeating), 125, 2), // auxelecheatnrgconsheating
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxElecHeatNrgConsPool), 127, 2), // auxelecheatnrgconspool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgSuppTotal), 129, 2), // nrgsupptotal
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgSuppHeating), 131, 2), // nrgsuppheating
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgSuppCooling), 133, 2), // nrgsuppcooling
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgSuppPool), 135, 2), // nrgsupppool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPower), 137, 1), // hppower
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpMaxPower), 138, 1), // hpmaxpower
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pvMaxComp), 139, 1), // pvmaxcomp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(powerReduction), 140, 1), // powerreduction
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpSetDiffPress), 141, 1), // hpsetdiffpress
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCompOn), 142, 1), // hpcompon
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpActivity), 143, 1), // hpactivity
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrinePumpSpd), 144, 1), // hpbrinepumpspd
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpSwitchValve), 145, 1), // hpswitchvalve
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCompSpd), 146, 1), // hpcompspd
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCircSpd), 147, 1), // hpcircspd
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrineIn), 148, 1), // hpbrinein
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpBrineOut), 149, 1), // hpbrineout
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc0), 150, 1), // hptc0
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc1), 151, 1), // hptc1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTc3), 152, 1), // hptc3
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr1), 153, 1), // hptr1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr3), 154, 1), // hptr3
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr4), 155, 1), // hptr4
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr5), 156, 1), // hptr5
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr6), 157, 1), // hptr6
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTr7), 158, 1), // hptr7
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTl2), 159, 1), // hptl2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPl1), 160, 1), // hppl1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPh1), 161, 1), // hpph1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTa4), 162, 1), // hpta4
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpTw1), 163, 1), // hptw1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(poolSetTemp), 164, 1), // poolsettemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hp4wayValve), 165, 1), // hp4way
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput1), 166, 1), // hpin1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn1Opt), 167, 8), // hpin1opt
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput2), 175, 1), // hpin2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn2Opt), 176, 8), // hpin2opt
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput3), 184, 1), // hpin3
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn3Opt), 185, 8), // hpin3opt
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpInput4), 193, 1), // hpin4
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpIn4Opt), 194, 8), // hpin4opt
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maxHeatComp), 202, 1), // maxheatcomp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(maxHeatHeat), 203, 1), // maxheatheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(manDefrost), 204, 1), // mandefrost
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pvCooling), 205, 1), // pvcooling
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterOnly), 206, 1), // auxheateronly
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterOff), 207, 1), // auxheateroff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterStatus), 208, 1), // auxheaterstatus
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterLevel), 209, 1), // auxheaterlevel
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeaterDelay), 210, 1), // auxheaterdelay
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxMaxLimit), 211, 1), // auxmaxlimit
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxLimitStart), 212, 1), // auxlimitstart
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeatMode), 213, 1), // auxheatrmode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystHeat), 214, 1), // hphystheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystCool), 215, 1), // hphystcool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpHystPool), 216, 1), // hphystpool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentMode), 217, 1), // silentmode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentFrom), 218, 1), // silentfrom
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(silentTo), 219, 1), // silentto
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(minTempSilent), 220, 1), // mintempsilent
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempParMode), 221, 1), // tempparmode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(auxHeatMixValve), 222, 1), // auxheatmix
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempDiffHeat), 223, 1), // tempdiffheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(tempDiffCool), 224, 1), // tempdiffcool
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(vp_cooling), 225, 1), // vpcooling
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 226, 1), // heatcable
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(VC0valve), 227, 1), // vc0valve
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(primePump), 228, 1), // primepump
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(primePumpMod), 229, 1), // primepumpmod
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hp3wayValve), 230, 1), // hp3way
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep1), 231, 1), // elheatstep1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep2), 232, 1), // elheatstep2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(elHeatStep3), 233, 1), // elheatstep3
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpEA0), 234, 1), // hpea0
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPumpMode), 235, 1), // hppumpmode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpFan), 236, 1), // fan
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpShutdown), 237, 1), // shutdown
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpCurrPower), 238, 1), // hpcurrpower
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(hpPowerLimit), 239, 1), // hppowerlimit
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc0Flow), 240, 1), // pc0flow
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1Flow), 241, 1), // pc1flow
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1On), 242, 1), // pc1on
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(pc1Rate), 243, 1), // pc1rate
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(exhaustTemp), 244, 1), // exhausttemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnGas), 245, 1), // burngas
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnGas2), 246, 1), // burngas2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(flameCurr), 247, 1), // flamecurr
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(fanWork), 248, 1), // fanwork
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(ignWork), 249, 1), // ignwork
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(oilPreHeat), 250, 1), // oilpreheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPower), 251, 1), // burnminpower
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMaxPower), 252, 1), // burnmaxpower
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(burnMinPeriod), 253, 1), // burnminperiod
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(absBurnPow), 254, 1), // absburnpow
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatblock), 255, 1), // heatblock
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOn), 256, 1), // boilhyston
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boilHystOff), 257, 1), // boilhystoff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOn), 258, 1), // boil2hyston
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(boil2HystOff), 259, 1), // boil2hystoff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveOn), 260, 1), // curveon
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveBase), 261, 1), // curvebase
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(curveEnd), 262, 1), // curveend
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(summertemp), 263, 1), // summertemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrostmode), 264, 1), // nofrostmode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nofrosttemp), 265, 1), // nofrosttemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(gasMeterHeat), 266, 2), // gasmeterheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat2), 268, 2), // nrgheat2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(nomPower), 270, 1), // nompower
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(netFlowTemp), 271, 1), // netflowtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatValve), 272, 1), // heatvalve
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(keepWarmTemp), 273, 1), // keepwarmtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(setReturnTemp), 274, 1), // setreturntemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DEVICE_DATA, FL_(heatingOn), 275, 1), // heating
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgWw), 0, 2), // nrg
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(meterWw), 2, 2), // meter
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(upTimeCompWw), 4, 2), // uptimecomp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwStartsHp), 6, 2), // startshp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgConsCompWw), 8, 2), // nrgconscomp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(auxElecHeatNrgConsWw), 10, 2), // auxelecheatnrgcons
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgSuppWw), 12, 2), // nrgsupp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(maxHeatDhw), 14, 1), // maxheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwAlternatingOper), 15, 1), // alternatingop
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwAltOpPrioHeat), 16, 1), // altopprioheat
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwAltOpPrioWw), 17, 1), // altopprio
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwComfOffTemp), 18, 1), // comfoff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwEcoOffTemp), 19, 1), // ecooff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwEcoPlusOffTemp), 20, 1), // ecoplusoff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwComfDiffTemp), 21, 1), // comfdiff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwEcoDiffTemp), 22, 1), // ecodiff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwEcoPlusDiffTemp), 23, 1), // ecoplusdiff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwComfStopTemp), 24, 1), // comfstop
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwEcoStopTemp), 25, 1), // ecostop
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwEcoPlusStopTemp), 26, 1), // ecoplusstop
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(hpCircPumpWw), 27, 1), // hpcircpump
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwtapactivated), 28, 1), // tapactivated
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwSetTemp), 29, 1), // settemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(selRoomTemp), 30, 1), // seltemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwSelTempLow), 31, 1), // seltemplow
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwSelTempEco), 32, 1), // tempecoplus
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwSelTempOff), 33, 1), // seltempoff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwSelTempSingle), 34, 1), // seltempsingle
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwSolarTemp), 35, 1), // solartemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwType), 36, 1), // type
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwComfort), 37, 1), // comfort
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwComfort1), 38, 1), // comfort1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(flowtempoffset), 39, 1), // flowtempoffset
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwChargeOptimization), 40, 1), // chargeoptimization
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwMaxPower), 41, 1), // maxpower
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwMaxTemp), 42, 1), // maxtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwCircPump), 43, 1), // circpump
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwChargeType), 44, 1), // chargetype
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwHystOn), 45, 1), // hyston
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwHystOff), 46, 1), // hystoff
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwDisinfectionTemp), 47, 1), // disinfectiontemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwCircMode), 48, 1), // circmode
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwCirc), 49, 1), // circ
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwCurTemp), 50, 1), // curtemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwCurTemp2), 51, 1), // curtemp2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwCurFlow), 52, 1), // curflow
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwStorageTemp1), 53, 1), // storagetemp1
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwStorageTemp2), 54, 1), // storagetemp2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(activated), 55, 1), // activated
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwOneTime), 56, 1), // onetime
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwDisinfecting), 57, 1), // disinfecting
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwCharging), 58, 1), // charging
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwRecharging), 59, 1), // recharging
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwTempOK), 60, 1), // tempok
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwActive), 61, 1), // active
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(ww3wayValve), 62, 1), // 3wayvalve
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwMixerTemp), 63, 1), // mixertemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(cylMiddleTemp), 64, 1), // cylmiddletemp
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwStarts), 65, 2), // starts
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwWorkM), 67, 2), // workm
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(gasMeterWw), 69, 2), // gasmeter
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(nrgWw2), 71, 2), // nrg2
|
||||
REGISTER_MAPPING(dt::BOILER, TAG_TYPE_DHW, FL_(wwValve), 73, 1), // dhwvalve
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(errorCode), 0, 8), // errorcode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(lastCode), 8, 25), // lastcode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(dateTime), 33, 13), // datetime
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(ibaCalIntTemperature), 46, 1), // intoffset
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(floordrystatus), 47, 1), // floordry
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(dampedoutdoortemp), 48, 1), // dampedoutdoortemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(floordrytemp), 49, 1), // floordrytemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(ibaBuildingType), 50, 1), // building
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(ibaMinExtTemperature), 51, 1), // minexttemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(damping), 52, 1), // damping
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(hasSolar), 53, 1), // solar
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(hybridStrategy), 54, 1), // hybridstrategy
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(switchOverTemp), 55, 1), // switchovertemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(energyCostRatio), 56, 1), // energycostratio
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(fossileFactor), 57, 1), // fossilefactor
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(electricFactor), 58, 1), // electricfactor
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(delayBoiler), 59, 1), // delayboiler
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(tempDiffBoiler), 60, 1), // tempdiffboiler
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(pvEnableWw), 61, 1), // pvenabledhw
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(pvRaiseHeat), 62, 1), // pvraiseheat
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(pvLowerCool), 63, 1), // pvlowercool
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(absent), 64, 1), // absent
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(ibaMainDisplay), 65, 1), // display
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(ibaLanguage), 66, 1), // language
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(ibaClockOffset), 67, 1), // clockoffset
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(tempsensor1), 68, 1), // inttemp1
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(tempsensor2), 69, 1), // inttemp2
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(autodst), 70, 1), // autodst
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(backlight), 71, 1), // backlight
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(brightness), 72, 1), // brightness
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(mixingvalves), 73, 1), // mixingvalves
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(heatingPID), 74, 1), // heatingpid
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(preheating), 75, 1), // preheating
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DEVICE_DATA, FL_(vacations), 76, 13), // vacations
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(selRoomTemp), 0, 1), // seltemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(roomTemp), 1, 1), // currtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(haclimate), 2, 1), // haclimate
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(mode), 3, 1), // mode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(modetype), 4, 1), // modetype
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(ecotemp), 5, 1), // ecotemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(manualtemp), 6, 1), // manualtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(comforttemp), 7, 1), // comforttemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(summertemp), 8, 1), // summertemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(designtemp), 9, 1), // designtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(offsettemp), 10, 1), // offsettemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(minflowtemp), 11, 1), // minflowtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(maxflowtemp), 12, 1), // maxflowtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(roominfluence), 13, 1), // roominfluence
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(roominfl_factor), 14, 1), // roominflfactor
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(curroominfl), 15, 1), // curroominfl
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(nofrostmode), 16, 1), // nofrostmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(nofrosttemp), 17, 1), // nofrosttemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(targetflowtemp), 18, 1), // targetflowtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heatingtype), 19, 1), // heatingtype
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(summersetmode), 20, 1), // summersetmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(hpoperatingmode), 21, 1), // hpoperatingmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(summermode), 22, 1), // summermode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(hpoperatingstate), 23, 1), // hpoperatingstate
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(controlmode), 24, 1), // controlmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(program), 25, 1), // program
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(tempautotemp), 26, 1), // tempautotemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(remoteseltemp), 27, 1), // remoteseltemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(fastheatup), 28, 1), // fastheatup
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(switchonoptimization), 29, 1), // switchonoptimization
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(reducemode), 30, 1), // reducemode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(noreducetemp), 31, 1), // noreducetemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(reducetemp), 32, 1), // reducetemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(wwprio), 33, 1), // dhwprio
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(hpcooling), 34, 1), // hpcooling
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(coolingOn), 35, 1), // coolingon
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(hpmode), 36, 1), // hpmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(dewoffset), 37, 1), // dewoffset
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(roomtempdiff), 38, 1), // roomtempdiff
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(hpminflowtemp), 39, 1), // hpminflowtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(control), 40, 1), // control
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(remotetemp), 41, 1), // remotetemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(remotehum), 42, 1), // remotehum
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heatondelay), 43, 1), // heatondelay
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heatoffdelay), 44, 1), // heatoffdelay
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(instantstart), 45, 1), // instantstart
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(boost), 46, 1), // boost
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(boosttime), 47, 1), // boosttime
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(coolstart), 48, 1), // coolstart
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(coolondelay), 49, 1), // coolondelay
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(cooloffdelay), 50, 1), // cooloffdelay
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(switchProgMode), 51, 1), // switchprogmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(redthreshold), 52, 1), // redthreshold
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(daytemp), 53, 1), // daytemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(nighttemp2), 54, 1), // nighttemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(holidaytemp), 55, 1), // holidaytemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(holidaymode), 56, 1), // holidaymode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(flowtempoffset), 57, 1), // flowtempoffset
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(holidays), 58, 13), // holidays
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations), 71, 13), // vacations
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(pause), 84, 1), // pause
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(party), 85, 1), // party
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacreducetemp), 86, 1), // vacreducetemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacreducemode), 87, 1), // vacreducemode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(switchtime1), 88, 8), // switchtime1
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(switchtime2), 96, 8), // switchtime2
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(offtemp), 104, 1), // offtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(daylowtemp), 105, 1), // daytemp2
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(daymidtemp), 106, 1), // daytemp3
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(dayhightemp), 107, 1), // daytemp4
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(switchtime), 108, 8), // switchtime
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations1), 116, 11), // vacations1
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations2), 127, 11), // vacations2
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations3), 138, 11), // vacations3
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations4), 149, 11), // vacations4
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations5), 160, 11), // vacations5
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations6), 171, 11), // vacations6
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(vacations7), 182, 11), // vacations7
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(reducehours), 193, 1), // reducehours
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(reduceminutes), 194, 1), // reduceminutes
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heattemp), 195, 1), // heattemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(roomsensor), 196, 1), // roomsensor
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_HC, FL_(heatup), 197, 1), // heatup
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(mode), 0, 1), // mode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTemp), 1, 1), // settemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwSetTempLow), 2, 1), // settemplow
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircMode), 3, 1), // circmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwChargeDuration), 4, 1), // chargeduration
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCharge), 5, 1), // charge
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwExtra), 6, 1), // extra
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfecting), 7, 1), // disinfecting
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectDay), 8, 1), // disinfectday
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectTime), 9, 1), // disinfecttime
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeating), 10, 1), // dailyheating
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDailyHeatTime), 11, 1), // dailyheattime
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwProgMode), 12, 1), // progmode
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwCircProg), 13, 1), // circprog
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwDisinfectHour), 14, 1), // disinfecthour
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwMaxTemp), 15, 1), // maxtemp
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwOneTimeKey), 16, 1), // onetimekey
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwswitchtime), 17, 11), // switchtimeWW
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwcircswitchtime), 28, 8), // circswitchtime
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(holidays), 36, 13), // holidays
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(vacations), 49, 13), // vacations
|
||||
REGISTER_MAPPING(dt::THERMOSTAT, TAG_TYPE_DHW, FL_(wwWhenModeOff), 62, 1), // whenmodeoff
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowTempHc), 0, 1), // flowtemphc
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(valveStatus), 1, 1), // valvestatus
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowSetTemp), 2, 1), // flowsettemp
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(wwPumpStatus), 3, 1), // pumpstatus
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(activated), 4, 1), // activated
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(mixerSetTime), 5, 1), // valvesettime
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowTempVf), 6, 1), // flowtempvf
|
||||
REGISTER_MAPPING(dt::MIXER, TAG_TYPE_HC, FL_(flowtempoffset), 7, 1), // flowtempoffset
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorTemp), 0, 1), // collectortemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylBottomTemp), 1, 1), // cylbottomtemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump), 2, 1), // solarpump
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pumpWorkTime), 3, 2), // pumpworktime
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylMaxTemp), 5, 1), // cylmaxtemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorShutdown), 6, 1), // collectorshutdown
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylHeated), 7, 1), // cylheated
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpMod), 8, 1), // solarpumpmod
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pumpMinMod), 9, 1), // pumpminmod
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpTurnonDiff), 10, 1), // turnondiff
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpTurnoffDiff), 11, 1), // turnoffdiff
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPower), 12, 1), // solarpower
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(energyLastHour), 13, 2), // energylasthour
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(maxFlow), 15, 1), // maxflow
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarIsEnabled), 16, 1), // solarenabled
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylMiddleTemp), 17, 1), // cylmiddletemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(retHeatAssist), 18, 1), // retheatassist
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(m1Valve), 19, 1), // heatassistvalve
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector2Temp), 20, 1), // collector2temp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(m1Power), 21, 1), // heatassistpower
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2), 22, 1), // solarpump2
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2Mod), 23, 1), // solarpump2mod
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cyl2BottomTemp), 24, 1), // cyl2bottomtemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cyl3BottomTemp), 25, 1), // cyl3bottomtemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylTopTemp), 26, 1), // cyltoptemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatExchangerTemp), 27, 1), // heatexchangertemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylPumpMod), 28, 1), // cylpumpmod
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(valveStatus), 29, 1), // valvestatus
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(vs1Status), 30, 1), // vs1status
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(vs3Status), 31, 1), // vs3status
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(transferPump), 32, 1), // transferpump
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(transferPumpMod), 33, 1), // transferpumpmod
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorMaxTemp), 34, 1), // collectormaxtemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collectorMinTemp), 35, 1), // collectormintemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(energyToday), 36, 2), // energytoday
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(energyTotal), 38, 2), // energytotal
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pump2WorkTime), 40, 2), // pump2worktime
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(m1WorkTime), 42, 2), // m1worktime
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatTransferSystem), 44, 1), // heattransfersystem
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(externalCyl), 45, 1), // externalcyl
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(thermalDisinfect), 46, 1), // thermaldisinfect
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatMetering), 47, 1), // heatmetering
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(activated), 48, 1), // activated
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpMode), 49, 1), // solarpumpmode
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPumpKick), 50, 1), // solarpumpkick
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(plainWaterMode), 51, 1), // plainwatermode
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(doubleMatchFlow), 52, 1), // doublematchflow
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(pump2MinMod), 53, 1), // pump2minmod
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2TurnonDiff), 54, 1), // turnondiff2
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2TurnoffDiff), 55, 1), // turnoffdiff2
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(solarPump2Kick), 56, 1), // pump2kick
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(climateZone), 57, 1), // climatezone
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector1Area), 58, 1), // collector1area
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector1Type), 59, 1), // collector1type
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector2Area), 60, 1), // collector2area
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(collector2Type), 61, 1), // collector2type
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(cylPriority), 62, 1), // cylpriority
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCntFlowTemp), 63, 1), // heatcntflowtemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCntRetTemp), 64, 1), // heatcntrettemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(heatCnt), 65, 1), // heatcnt
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(swapFlowTemp), 66, 1), // swapflowtemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DEVICE_DATA, FL_(swapRetTemp), 67, 1), // swaprettemp
|
||||
REGISTER_MAPPING(dt::SOLAR, TAG_TYPE_DHW, FL_(wwMinTemp), 0, 1), // mintemp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(airHumidity), 0, 1), // airhumidity
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(dewTemperature), 1, 1), // dewtemperature
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(curFlowTemp), 2, 1), // curflowtemp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(retTemp), 3, 1), // rettemp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(sysRetTemp), 4, 1), // sysrettemp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpTa4), 5, 1), // hpta4
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpTr1), 6, 1), // hptr1
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpTr3), 7, 1), // hptr3
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpTr4), 8, 1), // hptr4
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpTr5), 9, 1), // hptr5
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpTr6), 10, 1), // hptr6
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpTl2), 11, 1), // hptl2
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpPl1), 12, 1), // hppl1
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpPh1), 13, 1), // hpph1
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatingPumpMod), 14, 1), // heatingpumpmod
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hpCompSpd), 15, 1), // hpcompspd
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(hybridStrategy), 16, 1), // hybridstrategy
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseMode), 17, 1), // lownoisemode
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStart), 18, 1), // lownoisestart
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(lowNoiseStop), 19, 1), // lownoisestop
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceGas), 20, 1), // energypricegas
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPriceEl), 21, 1), // energypriceel
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(energyPricePV), 22, 1), // energyfeedpv
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(switchOverTemp), 23, 1), // switchovertemp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(airPurgeMode), 24, 1), // airpurgemode
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatPumpOutput), 25, 1), // heatpumpoutput
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(coolingCircuit), 26, 1), // coolingcircuit
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(compStartMod), 27, 1), // compstartmod
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatDrainPan), 28, 1), // heatdrainpan
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatCable), 29, 1), // heatcable
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgTotal), 30, 2), // nrgtotal
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(nrgHeat), 32, 2), // nrgheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterTotal), 34, 2), // metertotal
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterComp), 36, 2), // metercomp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterEHeat), 38, 2), // metereheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(meterHeat), 40, 2), // meterheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(heatingStarts), 42, 2), // heatingstarts
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(fuelHeat), 44, 2), // fuelheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elHeat), 46, 2), // elheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DEVICE_DATA, FL_(elGenHeat), 48, 2), // elgenheat
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(hybridDHW), 0, 1), // hybriddhw
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(nrgWw), 1, 2), // nrg
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(meterWw), 3, 2), // meter
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(wwStartsHp), 5, 2), // startshp
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(fuelDhw), 7, 2), // fueldhw
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(elDhw), 9, 2), // eldhw
|
||||
REGISTER_MAPPING(dt::HEATPUMP, TAG_TYPE_DHW, FL_(elGenDhw), 11, 2), // elgendhw
|
||||
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(activated), 0, 1), // activated
|
||||
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(flowTempHc), 1, 1), // flowtemphc
|
||||
REGISTER_MAPPING(dt::SWITCH, TAG_TYPE_DEVICE_DATA, FL_(status), 2, 1), // status
|
||||
REGISTER_MAPPING(dt::CONTROLLER, TAG_TYPE_DEVICE_DATA, FL_(dateTime), 0, 13), // datetime
|
||||
REGISTER_MAPPING(dt::ALERT, TAG_TYPE_DEVICE_DATA, FL_(setFlowTemp), 0, 1), // setflowtemp
|
||||
REGISTER_MAPPING(dt::ALERT, TAG_TYPE_DEVICE_DATA, FL_(setBurnPow), 1, 1), // setburnpow
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(flowTempVf), 0, 1), // flowtempvf
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(input), 1, 1), // input
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(outPower), 2, 1), // outpow
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(setPower), 3, 1), // setpower
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(setPoint), 4, 1), // setpoint
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(minV), 5, 1), // minv
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(maxV), 6, 1), // maxv
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(minT), 7, 1), // mint
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(maxT), 8, 1), // maxt
|
||||
REGISTER_MAPPING(dt::EXTENSION, TAG_TYPE_DEVICE_DATA, FL_(mode), 9, 1), // mode
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(outFresh), 0, 1), // outfresh
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(inFresh), 1, 1), // infresh
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(outEx), 2, 1), // outexhaust
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(inEx), 3, 1), // inexhaust
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(ventInSpeed), 4, 1), // ventinspeed
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(ventOutSpeed), 5, 1), // ventoutspeed
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(ventMode), 6, 1), // ventmode
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(airquality), 7, 1), // airquality
|
||||
REGISTER_MAPPING(dt::VENTILATION, TAG_TYPE_DEVICE_DATA, FL_(airHumidity), 8, 1), // airhumidity
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(selRoomTemp), 0, 1), // seltemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwTemp), 1, 1), // temp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCurTemp2), 2, 1), // curtemp2
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(hydrTemp), 3, 1), // hydrTemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwPump), 4, 1), // pump
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(flowtempoffset), 5, 1), // flowtempoffset
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwHystOn), 6, 1), // hyston
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwHystOff), 7, 1), // hystoff
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDisinfectionTemp), 8, 1), // disinfectiontemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCirc), 9, 1), // circ
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCircMode), 10, 1), // circmode
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwTempStatus), 11, 1), // tempstatus
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwMaxTemp), 12, 1), // maxtemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDiffTemp), 13, 1), // difftemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRedTemp), 14, 1), // redtemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwRequiredTemp), 15, 1), // requiredtemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwStorageTemp1), 16, 1), // storagetemp1
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwColdTemp), 17, 1), // coldtemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwTemp5), 18, 1), // temp5
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(retTemp), 19, 1), // rettemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwHotTemp), 20, 1), // hottemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwDailyTemp), 21, 1), // dailytemp
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwCircTc), 22, 1), // circtc
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwKeepWarm), 23, 1), // keepwarm
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwStatus2), 24, 1), // status2
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwPumpMod), 25, 1), // pumpmod
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(wwFlow), 26, 1), // flow
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(valveReturn), 27, 1), // valvereturn
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(deltaTRet), 28, 1), // deltatret
|
||||
REGISTER_MAPPING(dt::WATER, TAG_TYPE_DHW, FL_(errorDisp), 29, 1), // errordisp
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
// clang-format off
|
||||
|
||||
1444
src/core/mqtt.cpp
Normal file
1444
src/core/mqtt.cpp
Normal file
File diff suppressed because it is too large
Load Diff
317
src/core/mqtt.h
Normal file
317
src/core/mqtt.h
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_MQTT_H_
|
||||
#define EMSESP_MQTT_H_
|
||||
|
||||
#include <espMqttClient.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "system.h"
|
||||
#include "console.h"
|
||||
#include "command.h"
|
||||
#include "emsdevicevalue.h"
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
using mqtt_sub_function_p = std::function<bool(const char * message)>;
|
||||
|
||||
class Mqtt {
|
||||
public:
|
||||
enum discoveryType : uint8_t { HOMEASSISTANT = 0, DOMOTICZ, DOMOTICZ_LATEST };
|
||||
|
||||
// SINGLE_SHORT (1) and MULTI_SHORT (2) are the latest. Default is SINGLE_SHORT.
|
||||
// SINGLE_LONG (0) is v3.4 only
|
||||
// SINGLE_OLD (3) and MULTI_OLD (4) are for backwards compatibility with the older v3.6 style. https://github.com/emsesp/EMS-ESP32/issues/1714
|
||||
enum entityFormat : uint8_t { SINGLE_LONG = 0, SINGLE_SHORT, MULTI_SHORT, SINGLE_OLD, MULTI_OLD };
|
||||
|
||||
void loop();
|
||||
void start();
|
||||
|
||||
static void load_settings();
|
||||
|
||||
void set_publish_time_boiler(uint16_t publish_time);
|
||||
void set_publish_time_thermostat(uint16_t publish_time);
|
||||
void set_publish_time_solar(uint16_t publish_time);
|
||||
void set_publish_time_mixer(uint16_t publish_time);
|
||||
void set_publish_time_water(uint16_t publish_time);
|
||||
void set_publish_time_other(uint16_t publish_time);
|
||||
void set_publish_time_sensor(uint16_t publish_time);
|
||||
void set_publish_time_heartbeat(uint16_t publish_time);
|
||||
bool get_publish_onchange(uint8_t device_type);
|
||||
|
||||
enum Operation : uint8_t { PUBLISH, SUBSCRIBE, UNSUBSCRIBE };
|
||||
enum NestedFormat : uint8_t { NESTED = 1, SINGLE };
|
||||
|
||||
static constexpr uint8_t MQTT_TOPIC_MAX_SIZE = 128; // fixed, not a user setting anymore
|
||||
static constexpr uint16_t MQTT_QUEUE_MAX_SIZE = 300;
|
||||
|
||||
static void on_connect();
|
||||
static void on_disconnect(espMqttClientTypes::DisconnectReason reason);
|
||||
static void on_message(const char * topic, const uint8_t * payload, size_t len);
|
||||
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 bool queue_publish(const std::string & topic, const std::string & payload);
|
||||
static bool queue_publish(const char * topic, const char * payload);
|
||||
static bool queue_publish(const std::string & topic, const JsonObjectConst payload);
|
||||
static bool queue_publish(const char * topic, const JsonObjectConst payload);
|
||||
static bool queue_publish(const char * topic, const std::string & payload);
|
||||
static bool queue_publish_retain(const std::string & topic, const JsonObjectConst payload, const bool retain);
|
||||
static bool queue_publish_retain(const char * topic, const std::string & payload, const bool retain);
|
||||
static bool queue_publish_retain(const char * topic, const JsonObjectConst payload, const bool retain);
|
||||
static bool queue_ha(const char * topic, const JsonObjectConst payload);
|
||||
static bool queue_remove_topic(const char * topic);
|
||||
|
||||
static bool publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
|
||||
static bool publish_ha_sensor_config(uint8_t type,
|
||||
int8_t tag,
|
||||
const char * const fullname,
|
||||
const char * const en_name,
|
||||
const uint8_t device_type,
|
||||
const char * const entity,
|
||||
const uint8_t uom,
|
||||
const bool remove,
|
||||
const bool has_cmd,
|
||||
const char * const ** options,
|
||||
uint8_t options_size,
|
||||
const int16_t dv_set_min,
|
||||
const uint32_t dv_set_max,
|
||||
const int8_t num_op,
|
||||
const JsonObjectConst dev_json);
|
||||
|
||||
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
|
||||
static bool publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
|
||||
|
||||
static void show_topic_handlers(uuid::console::Shell & shell, const uint8_t device_type);
|
||||
static void show_mqtt(uuid::console::Shell & shell);
|
||||
|
||||
static void ha_status();
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
void incoming(const char * topic, const char * payload = ""); // for testing only
|
||||
#endif
|
||||
|
||||
static bool connected() {
|
||||
return mqttClient_ ? mqttClient_->connected() : false;
|
||||
}
|
||||
|
||||
static MqttClient * client() {
|
||||
return mqttClient_;
|
||||
}
|
||||
|
||||
static bool enabled() {
|
||||
return mqtt_enabled_;
|
||||
}
|
||||
|
||||
static void enabled(bool mqtt_enabled) {
|
||||
mqtt_enabled_ = mqtt_enabled;
|
||||
}
|
||||
|
||||
static std::string base() {
|
||||
return mqtt_base_;
|
||||
}
|
||||
|
||||
static std::string basename() {
|
||||
return mqtt_basename_;
|
||||
}
|
||||
|
||||
// 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 uint32_t publish_count() {
|
||||
return mqtt_message_id_;
|
||||
}
|
||||
|
||||
static uint32_t publish_fails() {
|
||||
return mqtt_publish_fails_;
|
||||
}
|
||||
|
||||
static uint32_t publish_queued() {
|
||||
return queuecount_;
|
||||
}
|
||||
|
||||
static uint8_t connect_count() {
|
||||
return connectcount_;
|
||||
}
|
||||
|
||||
static void reset_mqtt();
|
||||
|
||||
static bool is_nested() {
|
||||
return nested_format_ == NestedFormat::NESTED;
|
||||
}
|
||||
|
||||
static uint8_t entity_format() {
|
||||
return entity_format_;
|
||||
}
|
||||
|
||||
static void entity_format(uint8_t n) {
|
||||
entity_format_ = n;
|
||||
}
|
||||
|
||||
static uint8_t discovery_type() {
|
||||
return discovery_type_;
|
||||
}
|
||||
|
||||
static void nested_format(uint8_t nested_format) {
|
||||
nested_format_ = nested_format;
|
||||
}
|
||||
|
||||
static bool publish_single() {
|
||||
return mqtt_enabled_ && publish_single_;
|
||||
}
|
||||
|
||||
static bool publish_single2cmd() {
|
||||
return publish_single2cmd_;
|
||||
}
|
||||
|
||||
static void publish_single(bool publish_single) {
|
||||
publish_single_ = publish_single;
|
||||
}
|
||||
|
||||
static bool ha_enabled() {
|
||||
return mqtt_enabled_ && ha_enabled_;
|
||||
}
|
||||
|
||||
static void ha_enabled(bool ha_enabled) {
|
||||
ha_enabled_ = ha_enabled;
|
||||
}
|
||||
|
||||
static bool ha_climate_reset() {
|
||||
return ha_climate_reset_;
|
||||
}
|
||||
|
||||
static void ha_climate_reset(bool reset) {
|
||||
ha_climate_reset_ = reset;
|
||||
}
|
||||
|
||||
static std::string get_response() {
|
||||
return lastresponse_;
|
||||
}
|
||||
|
||||
static void clear_response() {
|
||||
lastresponse_.clear();
|
||||
}
|
||||
|
||||
void set_qos(uint8_t mqtt_qos) const {
|
||||
mqtt_qos_ = mqtt_qos;
|
||||
}
|
||||
|
||||
void set_retain(bool mqtt_retain) const {
|
||||
mqtt_retain_ = mqtt_retain;
|
||||
}
|
||||
|
||||
static std::string tag_to_topic(uint8_t device_type, int8_t tag);
|
||||
|
||||
static void add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, const char * entity = nullptr);
|
||||
|
||||
static void add_ha_sections_to_doc(const char * name,
|
||||
const char * state_t,
|
||||
JsonDocument & config,
|
||||
const bool is_first = false,
|
||||
const char * cond1 = nullptr,
|
||||
const char * cond2 = nullptr,
|
||||
const char * negcond = nullptr);
|
||||
static void add_ha_bool(JsonDocument & config);
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static MqttClient * mqttClient_;
|
||||
static uint32_t mqtt_message_id_;
|
||||
|
||||
static bool queue_message(const uint8_t operation, const std::string & topic, const std::string & payload, const bool retain);
|
||||
static bool queue_publish_message(const std::string & topic, const std::string & payload, const bool retain);
|
||||
static void queue_subscribe_message(const std::string & topic);
|
||||
static void queue_unsubscribe_message(const std::string & topic);
|
||||
|
||||
void on_publish(uint16_t packetId) const;
|
||||
|
||||
// function handlers for MQTT subscriptions
|
||||
struct MQTTSubFunction {
|
||||
uint8_t device_type_; // which device type, from DeviceType::
|
||||
const std::string topic_; // short topic name
|
||||
mqtt_sub_function_p mqtt_subfunction_; // can be empty
|
||||
|
||||
// replaced &&topic with &topic in 3.7.0-dev.43, so we prevent the std:move later
|
||||
MQTTSubFunction(uint8_t device_type, const std::string & topic, mqtt_sub_function_p mqtt_subfunction)
|
||||
: device_type_(device_type)
|
||||
, topic_(topic)
|
||||
, mqtt_subfunction_(mqtt_subfunction) {
|
||||
}
|
||||
};
|
||||
|
||||
static std::vector<MQTTSubFunction> mqtt_subfunctions_; // list of mqtt subscribe callbacks for all devices
|
||||
|
||||
uint32_t last_publish_boiler_ = 0;
|
||||
uint32_t last_publish_thermostat_ = 0;
|
||||
uint32_t last_publish_solar_ = 0;
|
||||
uint32_t last_publish_mixer_ = 0;
|
||||
uint32_t last_publish_water_ = 0;
|
||||
uint32_t last_publish_other_ = 0;
|
||||
uint32_t last_publish_sensor_ = 0;
|
||||
uint32_t last_publish_heartbeat_ = 0;
|
||||
// uint32_t last_publish_queue_ = 0;
|
||||
|
||||
static bool connecting_;
|
||||
static bool initialized_;
|
||||
static uint32_t mqtt_publish_fails_;
|
||||
static uint16_t queuecount_;
|
||||
static uint8_t connectcount_;
|
||||
static bool ha_climate_reset_;
|
||||
|
||||
static std::string lasttopic_;
|
||||
static std::string lastpayload_;
|
||||
static std::string lastresponse_;
|
||||
|
||||
// settings, copied over
|
||||
static std::string mqtt_base_;
|
||||
static std::string mqtt_basename_;
|
||||
static uint8_t mqtt_qos_;
|
||||
static bool mqtt_retain_;
|
||||
static uint32_t publish_time_;
|
||||
static uint32_t publish_time_boiler_;
|
||||
static uint32_t publish_time_thermostat_;
|
||||
static uint32_t publish_time_solar_;
|
||||
static uint32_t publish_time_mixer_;
|
||||
static uint32_t publish_time_water_;
|
||||
static uint32_t publish_time_other_;
|
||||
static uint32_t publish_time_sensor_;
|
||||
static uint32_t publish_time_heartbeat_;
|
||||
static bool mqtt_enabled_;
|
||||
static bool ha_enabled_;
|
||||
static uint8_t nested_format_;
|
||||
static uint8_t entity_format_;
|
||||
static std::string discovery_prefix_;
|
||||
static uint8_t discovery_type_;
|
||||
static bool publish_single_;
|
||||
static bool publish_single2cmd_;
|
||||
static bool send_response_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
426
src/core/roomcontrol.cpp
Normal file
426
src/core/roomcontrol.cpp
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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 "roomcontrol.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// init statics
|
||||
bool Roomctrl::switch_off_[HCS] = {false, false, false, false};
|
||||
uint32_t Roomctrl::send_time_[HCS] = {0, 0, 0, 0};
|
||||
uint32_t Roomctrl::receive_time_[HCS] = {0, 0, 0, 0};
|
||||
int16_t Roomctrl::remotetemp_[HCS] = {EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET, EMS_VALUE_INT16_NOTSET};
|
||||
uint8_t Roomctrl::remotehum_[HCS] = {EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET, EMS_VALUE_UINT8_NOTSET};
|
||||
uint8_t Roomctrl::sendtype_[HCS] = {SendType::TEMP, SendType::TEMP, SendType::TEMP, SendType::TEMP};
|
||||
uint8_t Roomctrl::type_[HCS] = {RemoteType::NONE, RemoteType::NONE, RemoteType::NONE, RemoteType::NONE};
|
||||
uint32_t Roomctrl::timeout_ = 0;
|
||||
|
||||
/**
|
||||
* set the temperature,
|
||||
*/
|
||||
void Roomctrl::set_timeout(uint8_t t) {
|
||||
timeout_ = t * 3600000; // ms
|
||||
}
|
||||
void Roomctrl::set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp) {
|
||||
if (!type_[hc] && !type) {
|
||||
return;
|
||||
}
|
||||
if (remotetemp_[hc] != EMS_VALUE_INT16_NOTSET && temp == EMS_VALUE_INT16_NOTSET) { // switch remote off
|
||||
remotetemp_[hc] = EMS_VALUE_INT16_NOTSET;
|
||||
switch_off_[hc] = true;
|
||||
send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
return;
|
||||
}
|
||||
if (hc >= HCS || !type) {
|
||||
return;
|
||||
}
|
||||
if (remotetemp_[hc] != temp) {
|
||||
send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
}
|
||||
type_[hc] = type;
|
||||
remotetemp_[hc] = temp;
|
||||
receive_time_[hc] = uuid::get_uptime();
|
||||
}
|
||||
|
||||
// set humidity for RC100H emulation
|
||||
void Roomctrl::set_remotehum(const uint8_t type, const uint8_t hc, const int8_t hum) {
|
||||
if (hc >= HCS || type != type_[hc]) {
|
||||
return;
|
||||
}
|
||||
if (remotehum_[hc] != hum) {
|
||||
send_time_[hc] = uuid::get_uptime() - SEND_INTERVAL; // send now
|
||||
sendtype_[hc] = SendType::HUMI;
|
||||
}
|
||||
remotehum_[hc] = hum;
|
||||
}
|
||||
|
||||
uint8_t Roomctrl::get_hc(uint8_t addr) {
|
||||
addr &= 0x7F;
|
||||
if (addr >= 0x40 && addr <= 0x44 && type_[addr - 0x40] == SENSOR) {
|
||||
return addr - 0x40; // SENSOR
|
||||
} else if (addr >= 0x38 && addr <= 0x3B
|
||||
&& (type_[addr - 0x38] == RC100H || type_[addr - 0x38] == RC200 || type_[addr - 0x38] == RC100 || type_[addr - 0x38] == RT800)) {
|
||||
return addr - 0x38; // RC100H, RC200
|
||||
} else if (addr >= 0x18 && addr <= 0x1B && (type_[addr - 0x18] == RC20 || type_[addr - 0x18] == FB10)) {
|
||||
return addr - 0x18; // RC20, FB10
|
||||
}
|
||||
return 0xFF; // invalid
|
||||
}
|
||||
|
||||
/**
|
||||
* if remote control is active send the temperature every 15 seconds
|
||||
*/
|
||||
void Roomctrl::send(uint8_t addr) {
|
||||
if (addr & 0x80) {
|
||||
return;
|
||||
}
|
||||
uint8_t hc = get_hc(addr);
|
||||
// check address, reply only on addresses 0x18..0x1B or 0x40..0x43
|
||||
if (hc >= HCS) {
|
||||
return;
|
||||
}
|
||||
// no reply if the temperature is not set
|
||||
if (!switch_off_[hc] && remotetemp_[hc] == EMS_VALUE_INT16_NOTSET && remotehum_[hc] == EMS_VALUE_UINT8_NOTSET) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!switch_off_[hc] && timeout_ && (uuid::get_uptime() - receive_time_[hc]) > timeout_) {
|
||||
remotetemp_[hc] = EMS_VALUE_INT16_NOTSET;
|
||||
switch_off_[hc] = true;
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
EMSESP::logger().warning("remotetemp timeout hc%d, stop sending roomtemperature to thermostat", hc);
|
||||
}
|
||||
if (switch_off_[hc] || (uuid::get_uptime() - send_time_[hc]) > SEND_INTERVAL) { // check interval
|
||||
if (type_[hc] == RC100H || type_[hc] == RT800) {
|
||||
if (sendtype_[hc] == SendType::HUMI) { // send humidity
|
||||
if (switch_off_[hc]) {
|
||||
remotehum_[hc] = EMS_VALUE_UINT8_NOTSET;
|
||||
}
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
humidity(addr, 0x10, hc);
|
||||
sendtype_[hc] = SendType::TEMP;
|
||||
} else { // temperature telegram
|
||||
if (remotehum_[hc] != EMS_VALUE_UINT8_NOTSET) {
|
||||
sendtype_[hc] = SendType::HUMI;
|
||||
} else {
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
}
|
||||
temperature(addr, 0x10, hc); // send to master-thermostat
|
||||
}
|
||||
} else if (type_[hc] == RC200 || type_[hc] == RC100 || type_[hc] == FB10) {
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
temperature(addr, 0x10, hc); // send to master-thermostat (https://github.com/emsesp/EMS-ESP32/issues/336)
|
||||
} else { // type==RC20 or SENSOR
|
||||
send_time_[hc] = uuid::get_uptime();
|
||||
temperature(addr, 0x00, hc); // send to all
|
||||
}
|
||||
if (remotehum_[hc] == EMS_VALUE_UINT8_NOTSET && switch_off_[hc]) {
|
||||
switch_off_[hc] = false;
|
||||
type_[hc] = RemoteType::NONE;
|
||||
}
|
||||
} else {
|
||||
// acknowledge every poll
|
||||
EMSuart::send_poll(addr | EMSbus::ems_mask());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if there is a message for the remote room controller
|
||||
*/
|
||||
void Roomctrl::check(uint8_t addr, const uint8_t * data, const uint8_t length) {
|
||||
uint8_t hc = get_hc(addr);
|
||||
if (hc >= HCS || length < 5) {
|
||||
return;
|
||||
}
|
||||
if (type_[hc] == SENSOR) {
|
||||
return;
|
||||
}
|
||||
// no reply if the temperature is not set
|
||||
if (remotetemp_[hc] == EMS_VALUE_INT16_NOTSET) {
|
||||
return;
|
||||
}
|
||||
// reply to writes with write nack byte
|
||||
if ((addr & 0x80) == 0) { // it's a write to us
|
||||
ack_write(); // accept writes, don't care.
|
||||
return;
|
||||
}
|
||||
addr &= 0x7F;
|
||||
// reads: for now we only reply to version and remote temperature
|
||||
// empty message back if temperature not set or unknown message type
|
||||
if (data[2] == EMSdevice::EMS_TYPE_VERSION) {
|
||||
version(addr, data[0], hc);
|
||||
} else if (data[2] == 0xAF && data[3] == 0) {
|
||||
temperature(addr, data[0], hc);
|
||||
} else if (length == 6) { // all other ems queries
|
||||
unknown(addr, data[0], data[2], data[3]);
|
||||
} else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 0 && data[6] == 0x23) { // Junkers
|
||||
temperature(addr, data[0], hc);
|
||||
} else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x2B + hc) { // EMS+ temperature
|
||||
temperature(addr, data[0], hc);
|
||||
} else if (length == 8 && data[2] == 0xFF && data[3] == 0 && data[5] == 3 && data[6] == 0x7B + hc && remotehum_[hc] != EMS_VALUE_UINT8_NOTSET) { // EMS+ humidity
|
||||
humidity(addr, data[0], hc);
|
||||
} else if (length == 8 && data[2] == 0xFF) { // ems+ query
|
||||
unknown(addr, data[0], data[3], data[5], data[6]);
|
||||
} else if (data[2] == 0xF7) { // ems+ query with 3 bytes type src dst 7F offset len=FF FF HIGH LOW
|
||||
replyF7(addr, data[0], data[3], data[5], data[6], data[7], hc);
|
||||
} else if (length == 8) {
|
||||
unknown(addr, data[0], data[3], data[5], data[6]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send version info
|
||||
*/
|
||||
void Roomctrl::version(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
uint8_t data[20];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
data[2] = 0x02;
|
||||
data[3] = 0;
|
||||
data[4] = type_[hc]; // set RC20 id 113, Ver 02.01 or Junkers FB10 id 109, Ver 16.05, RC100H id 200 ver 40.04
|
||||
if (type_[hc] == RC20) {
|
||||
data[5] = 2; // version 2.01
|
||||
data[6] = 1;
|
||||
data[7] = EMSbus::calculate_crc(data, 7); // append CRC
|
||||
EMSuart::transmit(data, 8);
|
||||
return;
|
||||
} else if (type_[hc] == FB10) {
|
||||
data[5] = 16; // version 16.05
|
||||
data[6] = 5;
|
||||
data[7] = 0;
|
||||
data[8] = 0;
|
||||
data[9] = 0;
|
||||
data[10] = 0;
|
||||
data[11] = 0;
|
||||
data[12] = 0;
|
||||
data[13] = 0;
|
||||
data[14] = EMSbus::calculate_crc(data, 14); // append CRC
|
||||
EMSuart::transmit(data, 15);
|
||||
return;
|
||||
} else if (type_[hc] == RC200) {
|
||||
data[5] = 32; // version 32.02 see #1611
|
||||
data[6] = 2;
|
||||
data[7] = 0;
|
||||
data[8] = 0xFF;
|
||||
data[9] = 0;
|
||||
data[10] = 0;
|
||||
data[11] = 0;
|
||||
data[12] = 0;
|
||||
data[13] = 0;
|
||||
data[14] = EMSbus::calculate_crc(data, 14); // append CRC
|
||||
EMSuart::transmit(data, 15);
|
||||
return;
|
||||
} else if (type_[hc] == RC100H) {
|
||||
data[5] = 40; // version 40.04
|
||||
data[6] = 4;
|
||||
data[7] = 0;
|
||||
data[8] = 0xFF;
|
||||
data[9] = EMSbus::calculate_crc(data, 9); // append CRC
|
||||
EMSuart::transmit(data, 10);
|
||||
return;
|
||||
} else if (type_[hc] == RC100) {
|
||||
data[5] = 40; // version 40.03
|
||||
data[6] = 3;
|
||||
data[7] = 0;
|
||||
data[8] = 0xFF;
|
||||
data[9] = EMSbus::calculate_crc(data, 9); // append CRC
|
||||
EMSuart::transmit(data, 10);
|
||||
return;
|
||||
} else if (type_[hc] == RT800) {
|
||||
data[5] = 21; // version 21.03
|
||||
data[6] = 3;
|
||||
data[7] = EMSbus::calculate_crc(data, 7); // append CRC
|
||||
EMSuart::transmit(data, 8);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* unknown message id, we reply with empty message
|
||||
*/
|
||||
void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset) {
|
||||
uint8_t data[10];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
data[2] = type;
|
||||
data[3] = offset;
|
||||
data[4] = EMSbus::calculate_crc(data, 4); // append CRC
|
||||
EMSuart::transmit(data, 5);
|
||||
}
|
||||
|
||||
void Roomctrl::unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh, uint8_t typel) {
|
||||
uint8_t data[10];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
data[2] = 0xFF;
|
||||
data[3] = offset;
|
||||
data[4] = typeh;
|
||||
data[5] = typel;
|
||||
data[6] = EMSbus::calculate_crc(data, 6); // append CRC
|
||||
EMSuart::transmit(data, 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* send the room temperature in message 0xAF
|
||||
*/
|
||||
void Roomctrl::temperature(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
uint8_t data[14];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
if (type_[hc] == RC20) { // RC20, telegram 0xAF
|
||||
data[2] = 0xAF;
|
||||
data[3] = 0;
|
||||
data[4] = (uint8_t)(remotetemp_[hc] >> 8);
|
||||
data[5] = (uint8_t)(remotetemp_[hc] & 0xFF);
|
||||
data[6] = 0;
|
||||
data[7] = EMSbus::calculate_crc(data, 7); // append CRC
|
||||
EMSuart::transmit(data, 8);
|
||||
} else if (type_[hc] == FB10) { // Junkers FB10, telegram 0x0123
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 0;
|
||||
data[5] = 0x23; // fixed for all hc
|
||||
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
|
||||
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
|
||||
data[8] = EMSbus::calculate_crc(data, 8); // append CRC
|
||||
EMSuart::transmit(data, 9);
|
||||
} else if (type_[hc] == RC200) { // RC200, telegram 42B, ff
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 3;
|
||||
data[5] = 0x2B + hc;
|
||||
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
|
||||
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
|
||||
uint16_t t1 = remotetemp_[hc] * 10 + 3;
|
||||
data[8] = (uint8_t)(t1 >> 8);
|
||||
data[9] = (uint8_t)(t1 & 0xFF);
|
||||
data[10] = 1; // not sure what this is and if we need it, maybe mode?
|
||||
data[11] = EMSbus::calculate_crc(data, 11); // append CRC
|
||||
EMSuart::transmit(data, 12);
|
||||
} else if (type_[hc] == RC100H || type_[hc] == RC100) { // RC100H, telegram 42B, ff
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 3;
|
||||
data[5] = 0x2B + hc;
|
||||
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
|
||||
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
|
||||
data[8] = EMSbus::calculate_crc(data, 8); // append CRC
|
||||
EMSuart::transmit(data, 9);
|
||||
} else if (type_[hc] == SENSOR) { // wireless sensor, broadcast id 435
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 3;
|
||||
data[5] = 0x35 + hc;
|
||||
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
|
||||
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
|
||||
data[8] = EMSbus::calculate_crc(data, 8); // append CRC
|
||||
EMSuart::transmit(data, 9);
|
||||
} else if (type_[hc] == RT800) { // RC200, telegram 42B, ff
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 3;
|
||||
data[5] = 0x2B + hc;
|
||||
data[6] = (uint8_t)(remotetemp_[hc] >> 8);
|
||||
data[7] = (uint8_t)(remotetemp_[hc] & 0xFF);
|
||||
uint16_t t1 = remotetemp_[hc] * 10 + 3;
|
||||
data[8] = (uint8_t)(t1 >> 8);
|
||||
data[9] = (uint8_t)(t1 & 0xFF);
|
||||
data[10] = 1; // not sure what this is and if we need it, maybe mode?
|
||||
data[11] = 9; // not sure what this is and if we need it, maybe mode?
|
||||
data[12] = EMSbus::calculate_crc(data, 12); // append CRC
|
||||
EMSuart::transmit(data, 13);
|
||||
}
|
||||
}
|
||||
|
||||
// send telegram 0x047B only for RC100H
|
||||
void Roomctrl::humidity(uint8_t addr, uint8_t dst, uint8_t hc) {
|
||||
uint8_t data[11];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
uint16_t dew = calc_dew(remotetemp_[hc], remotehum_[hc]);
|
||||
data[2] = 0xFF;
|
||||
data[3] = 0;
|
||||
data[4] = 3;
|
||||
data[5] = 0x7B + hc;
|
||||
data[6] = dew == EMS_VALUE_INT16_NOTSET ? EMS_VALUE_INT8_NOTSET : (uint8_t)((dew + 5) / 10);
|
||||
data[7] = remotehum_[hc];
|
||||
data[8] = (uint8_t)(dew << 8);
|
||||
data[9] = (uint8_t)(dew & 0xFF);
|
||||
data[10] = EMSbus::calculate_crc(data, 10); // append CRC
|
||||
EMSuart::transmit(data, 11);
|
||||
}
|
||||
|
||||
/**
|
||||
* send a nack if someone want to write to us.
|
||||
*/
|
||||
void Roomctrl::nack_write() {
|
||||
uint8_t data[1];
|
||||
data[0] = TxService::TX_WRITE_FAIL;
|
||||
EMSuart::transmit(data, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* send a ack if someone want to write to us.
|
||||
*/
|
||||
void Roomctrl::ack_write() {
|
||||
uint8_t data[1];
|
||||
data[0] = TxService::TX_WRITE_SUCCESS;
|
||||
EMSuart::transmit(data, 1);
|
||||
}
|
||||
|
||||
void Roomctrl::replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc) {
|
||||
uint8_t data[12];
|
||||
data[0] = addr | EMSbus::ems_mask();
|
||||
data[1] = dst & 0x7F;
|
||||
data[2] = 0xF7;
|
||||
data[3] = offset;
|
||||
data[4] = typehh;
|
||||
data[5] = typeh;
|
||||
data[6] = typel;
|
||||
if (typehh == 0x02) {
|
||||
if (type_[hc] == RC200 || type_[hc] == FB10) {
|
||||
data[7] = 0xFF;
|
||||
data[8] = 0x01;
|
||||
} else {
|
||||
data[7] = 0x0F;
|
||||
data[8] = 0x00;
|
||||
}
|
||||
} else {
|
||||
data[7] = 0;
|
||||
data[8] = 0;
|
||||
}
|
||||
data[9] = EMSbus::calculate_crc(data, 9); // append CRC
|
||||
EMSuart::transmit(data, 10);
|
||||
}
|
||||
|
||||
int16_t Roomctrl::calc_dew(int16_t temp, uint8_t humi) {
|
||||
if (humi == EMS_VALUE_UINT8_NOTSET || temp == EMS_VALUE_INT16_NOTSET) {
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
const float k2 = 17.62;
|
||||
const float k3 = 243.12;
|
||||
const float t = (float)temp / 10;
|
||||
const float h = (float)humi / 100;
|
||||
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
|
||||
return dt;
|
||||
}
|
||||
|
||||
|
||||
} // namespace emsesp
|
||||
67
src/core/roomcontrol.h
Normal file
67
src/core/roomcontrol.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_ROOMCONTROL_H
|
||||
#define EMSESP_ROOMCONTROL_H
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Roomctrl {
|
||||
public:
|
||||
// Product-Id of the remote
|
||||
enum RemoteType : uint8_t { NONE = 0, RC20 = 113, FB10 = 109, RC100H = 200, SENSOR = 0x40, RC200 = 157, RC100 = 165, RT800 = 3 };
|
||||
|
||||
static void send(uint8_t addr);
|
||||
static void check(uint8_t addr, const uint8_t * data, const uint8_t length);
|
||||
static void set_remotetemp(const uint8_t type, const uint8_t hc, const int16_t temp);
|
||||
static void set_remotehum(const uint8_t type, const uint8_t hc, const int8_t hum);
|
||||
static bool is_remote(const uint8_t hc) {
|
||||
return (hc < 4 && remotetemp_[hc] != EMS_VALUE_INT16_NOTSET);
|
||||
}
|
||||
static void set_timeout(uint8_t t);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t SEND_INTERVAL = 15000; // 15 sec
|
||||
static constexpr uint8_t HCS = 4; // max 4 heating circuits
|
||||
enum SendType : uint8_t { TEMP, HUMI };
|
||||
|
||||
static uint8_t get_hc(const uint8_t addr);
|
||||
static void version(uint8_t addr, uint8_t dst, uint8_t hc);
|
||||
static void unknown(uint8_t addr, uint8_t dst, uint8_t type, uint8_t offset);
|
||||
static void unknown(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typeh, uint8_t typel);
|
||||
static void temperature(uint8_t addr, uint8_t dst, uint8_t hc);
|
||||
static void humidity(uint8_t addr, uint8_t dst, uint8_t hc);
|
||||
static void nack_write();
|
||||
static void ack_write();
|
||||
static void replyF7(uint8_t addr, uint8_t dst, uint8_t offset, uint8_t typehh, uint8_t typeh, uint8_t typel, uint8_t hc);
|
||||
static int16_t calc_dew(int16_t temp, uint8_t hum);
|
||||
|
||||
static bool switch_off_[HCS];
|
||||
static uint32_t send_time_[HCS];
|
||||
static uint32_t receive_time_[HCS];
|
||||
static int16_t remotetemp_[HCS];
|
||||
static uint8_t remotehum_[HCS];
|
||||
static uint8_t sendtype_[HCS];
|
||||
static uint8_t type_[HCS]; // type is product-id 113 for RC20 or 109 for Junkers FB10
|
||||
static uint32_t timeout_;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
261
src/core/shower.cpp
Normal file
261
src/core/shower.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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 "shower.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger Shower::logger_{F_(shower), uuid::log::Facility::CONSOLE};
|
||||
|
||||
static bool force_coldshot = false;
|
||||
|
||||
void Shower::start() {
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||
shower_timer_ = settings.shower_timer;
|
||||
shower_alert_ = settings.shower_alert;
|
||||
shower_alert_trigger_ = settings.shower_alert_trigger * 60; // convert from minutes to seconds
|
||||
shower_alert_coldshot_ = settings.shower_alert_coldshot; // in seconds
|
||||
shower_min_duration_ = settings.shower_min_duration; // in seconds
|
||||
});
|
||||
|
||||
Command::add(
|
||||
EMSdevice::DeviceType::BOILER,
|
||||
F_(coldshot),
|
||||
[&](const char * value, const int8_t id, JsonObject output) {
|
||||
LOG_INFO("Forcing coldshot...");
|
||||
if (shower_state_) {
|
||||
output["message"] = "OK";
|
||||
force_coldshot = true;
|
||||
} else {
|
||||
output["message"] = "Coldshot failed. Shower not active";
|
||||
LOG_WARNING("Coldshot failed. Shower not active");
|
||||
force_coldshot = false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
FL_(coldshot_cmd),
|
||||
CommandFlag::ADMIN_ONLY);
|
||||
|
||||
if (shower_timer_) {
|
||||
set_shower_state(false, true); // turns shower to off and creates HA topic if not already done
|
||||
}
|
||||
}
|
||||
|
||||
void Shower::loop() {
|
||||
if (!shower_timer_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto time_now = uuid::get_uptime_sec(); // in sec
|
||||
|
||||
// if already in cold mode, ignore all this logic until we're out of the cold blast
|
||||
if (!doing_cold_shot_) {
|
||||
// is the hot water running?
|
||||
if (EMSESP::tap_water_active()) {
|
||||
// if heater was previously off, start the timer
|
||||
if (timer_start_ == 0) {
|
||||
// hot water just started...
|
||||
timer_start_ = time_now;
|
||||
timer_pause_ = 0; // remove any last pauses
|
||||
doing_cold_shot_ = false;
|
||||
duration_ = 0;
|
||||
shower_state_ = false;
|
||||
next_alert_ = shower_alert_trigger_;
|
||||
} else {
|
||||
// hot water has been on for a while
|
||||
// first check to see if hot water has been on long enough to be recognized as a Shower/Bath
|
||||
if (!shower_state_ && (time_now - timer_start_) > shower_min_duration_) {
|
||||
set_shower_state(true);
|
||||
LOG_DEBUG("hot water still running, starting shower timer");
|
||||
}
|
||||
// check if the shower has been on too long
|
||||
else if ((shower_alert_ && ((time_now - timer_start_) > next_alert_)) || force_coldshot) {
|
||||
shower_alert_start();
|
||||
}
|
||||
}
|
||||
} else { // hot water is off
|
||||
// if it just turned off, record the time as it could be a short pause
|
||||
if (timer_start_ && (timer_pause_ == 0)) {
|
||||
timer_pause_ = time_now;
|
||||
}
|
||||
|
||||
// if shower has been off for longer than the wait time
|
||||
if (timer_pause_ && ((time_now - timer_pause_) > SHOWER_PAUSE_TIME)) {
|
||||
// it is over the wait period, so assume that the shower has finished and calculate the total time and publish
|
||||
// because its unsigned long, can't have negative so check if length is less than OFFSET_TIME
|
||||
if ((timer_pause_ - timer_start_) > SHOWER_OFFSET_TIME) {
|
||||
duration_ = (timer_pause_ - timer_start_ - SHOWER_OFFSET_TIME); // duration in seconds
|
||||
if (duration_ > shower_min_duration_) {
|
||||
JsonDocument doc;
|
||||
|
||||
// duration in seconds
|
||||
doc["duration"] = duration_; // seconds
|
||||
// time_t now = time(nullptr);
|
||||
// // if NTP enabled, publish timestamp
|
||||
// if (now > 1576800000) { // year 2020
|
||||
// // doc["timestamp_s"] = now; // if needed, in seconds
|
||||
// tm * tm_ = localtime(&now);
|
||||
// char dt[25];
|
||||
// strftime(dt, sizeof(dt), "%FT%T%z", tm_);
|
||||
// doc["timestamp"] = dt;
|
||||
// LOG_INFO("Shower finished %s (duration %lus)", dt, duration_);
|
||||
// } else {
|
||||
// LOG_INFO("Shower finished (duration %lus)", duration_);
|
||||
// }
|
||||
LOG_INFO("Shower finished (duration %lus)", duration_);
|
||||
Mqtt::queue_publish("shower_data", doc.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
|
||||
// reset everything
|
||||
timer_start_ = 0;
|
||||
timer_pause_ = 0;
|
||||
doing_cold_shot_ = false;
|
||||
alert_timer_start_ = 0;
|
||||
|
||||
set_shower_state(false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// at this point we're in the shower cold shot (doing_cold_shot_ == true)
|
||||
// keep repeating until the time is up
|
||||
if ((time_now - alert_timer_start_) > shower_alert_coldshot_) {
|
||||
shower_alert_stop();
|
||||
}
|
||||
}
|
||||
|
||||
// turn off hot water to send a shot of cold
|
||||
void Shower::shower_alert_start() {
|
||||
LOG_DEBUG("Shower Alert started");
|
||||
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "false", 9);
|
||||
doing_cold_shot_ = true;
|
||||
force_coldshot = false;
|
||||
alert_timer_start_ = uuid::get_uptime_sec(); // timer starts now
|
||||
}
|
||||
|
||||
// turn back on the hot water for the shower
|
||||
void Shower::shower_alert_stop() {
|
||||
if (doing_cold_shot_) {
|
||||
LOG_DEBUG("Shower Alert stopped");
|
||||
(void)Command::call(EMSdevice::DeviceType::BOILER, "tapactivated", "true", 9);
|
||||
doing_cold_shot_ = false;
|
||||
force_coldshot = false;
|
||||
next_alert_ += shower_alert_trigger_;
|
||||
}
|
||||
}
|
||||
|
||||
// send status of shower to MQTT topic called shower_active - which is determined by the state parameter
|
||||
// and creates the HA config topic if HA enabled
|
||||
// force is used by EMSESP::publish_all_loop()
|
||||
void Shower::set_shower_state(bool state, bool force) {
|
||||
// sets the state
|
||||
shower_state_ = state;
|
||||
|
||||
// only publish if that state has changed
|
||||
static bool old_shower_state_ = false;
|
||||
if ((shower_state_ == old_shower_state_) && !force) {
|
||||
return;
|
||||
}
|
||||
old_shower_state_ = shower_state_; // copy current state
|
||||
|
||||
// always publish as a string
|
||||
char s[12];
|
||||
Mqtt::queue_publish("shower_active", Helpers::render_boolean(s, shower_state_)); // https://github.com/emsesp/EMS-ESP/issues/369
|
||||
|
||||
// send out HA MQTT Discovery config topic
|
||||
if ((Mqtt::ha_enabled()) && (!ha_configdone_ || force)) {
|
||||
JsonDocument doc;
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
char str[70];
|
||||
char stat_t[50];
|
||||
|
||||
//
|
||||
// shower active
|
||||
//
|
||||
doc["name"] = "Shower Active";
|
||||
|
||||
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
|
||||
snprintf(str, sizeof(str), "%s_shower_active", Mqtt::basename().c_str());
|
||||
} else {
|
||||
snprintf(str, sizeof(str), "shower_active"); // v3.4 compatible
|
||||
}
|
||||
doc["uniq_id"] = str;
|
||||
doc["object_id"] = str;
|
||||
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
Mqtt::add_ha_bool(doc);
|
||||
|
||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, true); // create first dev & ids, no conditions
|
||||
|
||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
|
||||
ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
//
|
||||
// shower duration
|
||||
//
|
||||
doc.clear();
|
||||
|
||||
snprintf(str, sizeof(str), "%s_shower_duration", Mqtt::basename().c_str());
|
||||
|
||||
doc["uniq_id"] = str;
|
||||
doc["object_id"] = str;
|
||||
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/shower_data", Mqtt::base().c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
doc["name"] = "Shower Duration";
|
||||
doc["val_tpl"] = "{{value_json.duration if value_json.duration is defined else 0}}";
|
||||
doc["unit_of_meas"] = "s";
|
||||
doc["stat_cla"] = "measurement";
|
||||
doc["dev_cla"] = "duration";
|
||||
// doc["ent_cat"] = "diagnostic";
|
||||
|
||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, false, "value_json.duration is defined");
|
||||
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str());
|
||||
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
|
||||
//
|
||||
// shower timestamp
|
||||
//
|
||||
doc.clear();
|
||||
|
||||
snprintf(str, sizeof(str), "%s_shower_timestamp", Mqtt::basename().c_str());
|
||||
|
||||
doc["uniq_id"] = str;
|
||||
doc["object_id"] = str;
|
||||
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/shower_data", Mqtt::base().c_str());
|
||||
doc["stat_t"] = stat_t;
|
||||
|
||||
doc["name"] = "Shower Timestamp";
|
||||
doc["val_tpl"] = "{{value_json.timestamp if value_json.timestamp is defined else 0}}";
|
||||
// doc["ent_cat"] = "diagnostic";
|
||||
|
||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, false, "value_json.timestamp is defined");
|
||||
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/shower_timestamp/config", Mqtt::basename().c_str());
|
||||
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
73
src/core/shower.h
Normal file
73
src/core/shower.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_SHOWER_H
|
||||
#define EMSESP_SHOWER_H
|
||||
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Shower {
|
||||
public:
|
||||
void start();
|
||||
void loop();
|
||||
|
||||
void set_shower_state(bool state, bool force = false);
|
||||
|
||||
// commands
|
||||
static bool command_coldshot(const char * value, const int8_t id);
|
||||
|
||||
void shower_timer(bool enable) {
|
||||
shower_timer_ = enable;
|
||||
}
|
||||
|
||||
void shower_alert(bool enable) {
|
||||
shower_alert_ = enable;
|
||||
}
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static constexpr uint32_t SHOWER_PAUSE_TIME = 15; // 15 seconds, max time if water is switched off & on during a shower
|
||||
static constexpr uint32_t SHOWER_OFFSET_TIME = 5; // 5 seconds grace time, to calibrate actual time under the shower
|
||||
|
||||
void shower_alert_start();
|
||||
void shower_alert_stop();
|
||||
|
||||
bool shower_timer_; // true if we want to report back on shower times
|
||||
bool shower_alert_; // true if we want the alert of cold water
|
||||
uint32_t shower_alert_trigger_; // default 7 minutes, before trigger a shot of cold water
|
||||
uint32_t shower_alert_coldshot_; // default 10 seconds for cold water before turning back hot water
|
||||
uint32_t shower_min_duration_; // default 3 minutes (180 seconds), before recognizing its a shower
|
||||
uint32_t next_alert_;
|
||||
bool ha_configdone_ = false; // for HA MQTT Discovery
|
||||
bool shower_state_;
|
||||
|
||||
uint32_t timer_start_; // sec
|
||||
uint32_t timer_pause_; // sec
|
||||
uint32_t duration_; // sec
|
||||
|
||||
// cold shot
|
||||
uint32_t alert_timer_start_; // sec
|
||||
bool doing_cold_shot_; // true if we've just sent a jolt of cold water
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
712
src/core/shuntingYard.hpp
Normal file
712
src/core/shuntingYard.hpp
Normal file
@@ -0,0 +1,712 @@
|
||||
// Shunting-yard Algorithm
|
||||
// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
||||
//
|
||||
// Implementation notes for unary operators by Austin Taylor
|
||||
// https://stackoverflow.com/a/5240912
|
||||
//
|
||||
// Example:
|
||||
// https://ideone.com/VocUTq
|
||||
//
|
||||
// License:
|
||||
// This code uses the following materials.
|
||||
// (1) Wikipedia article [Shunting-yard algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm),
|
||||
// which is released under the [Creative Commons Attribution-Share-Alike License 3.0](https://creativecommons.org/licenses/by-sa/3.0/).
|
||||
// (2) [Implementation notes for unary operators in Shunting-Yard algorithm](https://stackoverflow.com/a/5240912) by Austin Taylor
|
||||
// which is released under the [Creative Commons Attribution-Share-Alike License 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
|
||||
//
|
||||
// copy from https://gist.github.com/t-mat/b9f681b7591cdae712f6
|
||||
// modified MDvP, 06.2024
|
||||
//
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <math.h>
|
||||
|
||||
class Token {
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
Unknown,
|
||||
Number,
|
||||
String,
|
||||
Operator,
|
||||
Compare,
|
||||
Logic,
|
||||
Unary,
|
||||
LeftParen,
|
||||
RightParen,
|
||||
};
|
||||
|
||||
Token(Type type, const std::string & s, int8_t precedence = -1, bool rightAssociative = false)
|
||||
: type{type}
|
||||
, str(s)
|
||||
, precedence{precedence}
|
||||
, rightAssociative{rightAssociative} {
|
||||
}
|
||||
|
||||
const Type type;
|
||||
const std::string str;
|
||||
const int8_t precedence;
|
||||
const bool rightAssociative;
|
||||
};
|
||||
|
||||
// find tokens
|
||||
std::deque<Token> exprToTokens(const std::string & expr) {
|
||||
std::deque<Token> tokens;
|
||||
|
||||
for (const auto * p = expr.c_str(); *p; ++p) {
|
||||
if (isblank(*p)) {
|
||||
// do nothing
|
||||
} else if (*p == '{') { // json is stored as string including {}
|
||||
const auto * b = p;
|
||||
++p;
|
||||
uint8_t i = 1;
|
||||
while (*p && i > 0) {
|
||||
i += (*p == '{') ? 1 : (*p == '}') ? -1 : 0;
|
||||
++p;
|
||||
}
|
||||
if (*p) {
|
||||
++p;
|
||||
}
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -3);
|
||||
if (*p == '\0') {
|
||||
--p;
|
||||
}
|
||||
} else if (strncmp(p, "int", 3) == 0) {
|
||||
p += 2;
|
||||
tokens.emplace_back(Token::Type::Unary, "i", 5);
|
||||
} else if (strncmp(p, "round", 5) == 0) {
|
||||
p += 4;
|
||||
tokens.emplace_back(Token::Type::Unary, "r", 5);
|
||||
} else if (strncmp(p, "abs", 3) == 0) {
|
||||
p += 2;
|
||||
tokens.emplace_back(Token::Type::Unary, "a", 5);
|
||||
} else if (strncmp(p, "log", 3) == 0) {
|
||||
p += 2;
|
||||
tokens.emplace_back(Token::Type::Unary, "l", 5);
|
||||
} else if (strncmp(p, "exp", 3) == 0) {
|
||||
p += 2;
|
||||
tokens.emplace_back(Token::Type::Unary, "e", 5);
|
||||
} else if (strncmp(p, "sqrt", 4) == 0) {
|
||||
p += 3;
|
||||
tokens.emplace_back(Token::Type::Unary, "s", 5);
|
||||
} else if (strncmp(p, "pow", 3) == 0) {
|
||||
p += 2;
|
||||
tokens.emplace_back(Token::Type::Unary, "p", 5);
|
||||
} else if (*p >= 'a' && *p <= 'z') {
|
||||
const auto * b = p;
|
||||
while ((*p >= 'a' && *p <= 'z') || (*p == '_')) {
|
||||
++p;
|
||||
}
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -2);
|
||||
--p;
|
||||
} else if (*p == '"') {
|
||||
++p;
|
||||
const auto * b = p;
|
||||
while (*p && *p != '"') {
|
||||
++p;
|
||||
}
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -3);
|
||||
if (*p == '\0') {
|
||||
--p;
|
||||
}
|
||||
} else if (*p == '\'') {
|
||||
++p;
|
||||
const auto * b = p;
|
||||
while (*p && *p != '\'') {
|
||||
++p;
|
||||
}
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::String, s, -3);
|
||||
if (*p == '\0') {
|
||||
--p;
|
||||
}
|
||||
} else if (isdigit(*p)) {
|
||||
const auto * b = p;
|
||||
while (isdigit(*p) || *p == '.') {
|
||||
++p;
|
||||
}
|
||||
const auto s = std::string(b, p);
|
||||
tokens.emplace_back(Token::Type::Number, s, -4);
|
||||
--p;
|
||||
} else {
|
||||
Token::Type token = Token::Type::Operator;
|
||||
int8_t precedence = -1;
|
||||
bool rightAssociative = false;
|
||||
char c = *p;
|
||||
switch (c) {
|
||||
default:
|
||||
token = Token::Type::Unknown;
|
||||
break;
|
||||
case '(':
|
||||
token = Token::Type::LeftParen;
|
||||
break;
|
||||
case ')':
|
||||
token = Token::Type::RightParen;
|
||||
break;
|
||||
case '^':
|
||||
precedence = 4;
|
||||
rightAssociative = true;
|
||||
break;
|
||||
case '*':
|
||||
precedence = 3;
|
||||
break;
|
||||
case '/':
|
||||
precedence = 3;
|
||||
break;
|
||||
case '%':
|
||||
precedence = 3;
|
||||
break;
|
||||
case '+':
|
||||
precedence = 2;
|
||||
break;
|
||||
case '-':
|
||||
// If current token is '-'
|
||||
// and if it is the first token, or preceded by another operator, or left-paren,
|
||||
if (tokens.empty() || tokens.back().type == Token::Type::Operator || tokens.back().type == Token::Type::Compare
|
||||
|| tokens.back().type == Token::Type::Logic || tokens.back().type == Token::Type::Unary || tokens.back().type == Token::Type::LeftParen) {
|
||||
// it's unary '-'
|
||||
// note#1 : 'm' is a special operator name for unary '-'
|
||||
// note#2 : It has highest precedence than any of the infix operators
|
||||
if (!tokens.empty() && tokens.back().str[0] == 'm') { // double unary minus
|
||||
tokens.pop_back();
|
||||
continue;
|
||||
}
|
||||
token = Token::Type::Unary;
|
||||
c = 'm';
|
||||
precedence = 5;
|
||||
} else {
|
||||
// otherwise, it's binary '-'
|
||||
precedence = 2;
|
||||
}
|
||||
break;
|
||||
case '&':
|
||||
if (p[1] == '&')
|
||||
++p;
|
||||
precedence = 0;
|
||||
token = Token::Type::Logic;
|
||||
break;
|
||||
case '|':
|
||||
if (p[1] == '|')
|
||||
++p;
|
||||
precedence = 0;
|
||||
token = Token::Type::Logic;
|
||||
break;
|
||||
case '!':
|
||||
if (p[1] == '=') {
|
||||
++p;
|
||||
precedence = 1;
|
||||
token = Token::Type::Compare;
|
||||
} else {
|
||||
precedence = 1;
|
||||
token = Token::Type::Unary;
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
if (p[1] == '=') {
|
||||
++p;
|
||||
c = '{';
|
||||
}
|
||||
precedence = 1;
|
||||
token = Token::Type::Compare;
|
||||
break;
|
||||
case '>':
|
||||
if (p[1] == '=') {
|
||||
++p;
|
||||
c = '}';
|
||||
}
|
||||
precedence = 1;
|
||||
token = Token::Type::Compare;
|
||||
break;
|
||||
case '=':
|
||||
if (p[1] == '=')
|
||||
++p;
|
||||
precedence = 1;
|
||||
token = Token::Type::Compare;
|
||||
break;
|
||||
}
|
||||
const auto s = std::string(1, c);
|
||||
tokens.emplace_back(token, s, precedence, rightAssociative);
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// sort tokens to RPN form
|
||||
std::deque<Token> shuntingYard(const std::deque<Token> & tokens) {
|
||||
std::deque<Token> queue;
|
||||
std::vector<Token> stack;
|
||||
|
||||
// While there are tokens to be read:
|
||||
for (auto const & token : tokens) {
|
||||
// Read a token
|
||||
switch (token.type) {
|
||||
case Token::Type::Number:
|
||||
case Token::Type::String:
|
||||
// If the token is a number, then add it to the output queue
|
||||
queue.push_back(token);
|
||||
break;
|
||||
|
||||
case Token::Type::Unary:
|
||||
case Token::Type::Compare:
|
||||
case Token::Type::Logic:
|
||||
case Token::Type::Operator: {
|
||||
// If the token is operator, o1, then:
|
||||
const auto o1 = token;
|
||||
|
||||
// while there is an operator token,
|
||||
while (!stack.empty()) {
|
||||
// o2, at the top of stack, and
|
||||
const auto o2 = stack.back();
|
||||
|
||||
// either o1 is left-associative and its precedence is
|
||||
// *less than or equal* to that of o2,
|
||||
// or o1 if right associative, and has precedence
|
||||
// *less than* that of o2,
|
||||
if ((!o1.rightAssociative && o1.precedence <= o2.precedence) || (o1.rightAssociative && o1.precedence < o2.precedence)) {
|
||||
// then pop o2 off the stack,
|
||||
stack.pop_back();
|
||||
// onto the output queue;
|
||||
queue.push_back(o2);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// @@ otherwise, exit.
|
||||
break;
|
||||
}
|
||||
|
||||
// push o1 onto the stack.
|
||||
stack.push_back(o1);
|
||||
} break;
|
||||
|
||||
case Token::Type::LeftParen:
|
||||
// If token is left parenthesis, then push it onto the stack
|
||||
stack.push_back(token);
|
||||
break;
|
||||
|
||||
case Token::Type::RightParen: {
|
||||
// If token is right parenthesis:
|
||||
bool match = false;
|
||||
|
||||
// Until the token at the top of the stack
|
||||
// is a left parenthesis,
|
||||
while (!stack.empty() && stack.back().type != Token::Type::LeftParen) {
|
||||
// pop operators off the stack
|
||||
// onto the output queue.
|
||||
queue.push_back(stack.back());
|
||||
stack.pop_back();
|
||||
match = true;
|
||||
}
|
||||
|
||||
if (!match && stack.empty()) {
|
||||
// If the stack runs out without finding a left parenthesis,
|
||||
// then there are mismatched parentheses.
|
||||
return {};
|
||||
}
|
||||
|
||||
// Pop the left parenthesis from the stack,
|
||||
// but not onto the output queue.
|
||||
stack.pop_back();
|
||||
} break;
|
||||
|
||||
case Token::Type::Unknown:
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// When there are no more tokens to read:
|
||||
// While there are still operator tokens in the stack:
|
||||
while (!stack.empty()) {
|
||||
// If the operator token on the top of the stack is a parenthesis,
|
||||
// then there are mismatched parentheses.
|
||||
if (stack.back().type == Token::Type::LeftParen) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Pop the operator onto the output queue.
|
||||
queue.push_back(std::move(stack.back()));
|
||||
stack.pop_back();
|
||||
}
|
||||
return queue;
|
||||
}
|
||||
|
||||
// check if string is a number
|
||||
bool isnum(const std::string & s) {
|
||||
if (s.find_first_not_of("0123456789.") == std::string::npos || (s[0] == '-' && s.find_first_not_of("0123456789.", 1) == std::string::npos)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// replace commands like "<device>/<hc>/<cmd>" with its value"
|
||||
std::string commands(std::string & expr, bool quotes = true) {
|
||||
for (uint8_t device = 0; device < emsesp::EMSdevice::DeviceType::UNKNOWN; device++) {
|
||||
const char * d = emsesp::EMSdevice::device_type_2_device_name(device);
|
||||
auto f = expr.find(d);
|
||||
while (f != std::string::npos) {
|
||||
// entity names are alphanumeric or _
|
||||
auto e = expr.find_first_not_of("/._abcdefghijklmnopqrstuvwxyz0123456789", f);
|
||||
if (e == std::string::npos) {
|
||||
e = expr.length();
|
||||
}
|
||||
while (e > 0 && expr[e - 1] == ' ') { // remove blanks from end
|
||||
e--;
|
||||
}
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
size_t l = e - f;
|
||||
if (l >= sizeof(cmd) - 1) {
|
||||
break;
|
||||
}
|
||||
expr.copy(cmd, l, f);
|
||||
cmd[l] = '\0';
|
||||
if (strstr(cmd, "/value") == nullptr) {
|
||||
strlcat(cmd, "/value", sizeof(cmd) - 6);
|
||||
}
|
||||
JsonDocument doc_out;
|
||||
JsonDocument doc_in;
|
||||
JsonObject output = doc_out.to<JsonObject>();
|
||||
JsonObject input = doc_in.to<JsonObject>();
|
||||
std::string cmd_s = "api/" + std::string(cmd);
|
||||
emsesp::Command::process(cmd_s.c_str(), true, input, output);
|
||||
if (output["api_data"].is<std::string>()) {
|
||||
std::string data = output["api_data"];
|
||||
if (!isnum(data) && quotes) {
|
||||
data.insert(data.begin(), '"');
|
||||
data.insert(data.end(), '"');
|
||||
}
|
||||
expr.replace(f, l, data);
|
||||
e = f + data.length();
|
||||
} else {
|
||||
return expr = "";
|
||||
}
|
||||
f = expr.find(d, e);
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
// checks for logic value
|
||||
int to_logic(const std::string & s) {
|
||||
if (s[0] == '1' || s == "on" || s == "ON" || s == "true") {
|
||||
return 1;
|
||||
}
|
||||
if (s[0] == '0' || s == "off" || s == "OFF" || s == "false") {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// number to string, remove trailing zeros
|
||||
std::string to_string(double d) {
|
||||
std::string s = std::to_string(d);
|
||||
while (!s.empty() && s.back() == '0') {
|
||||
s.pop_back();
|
||||
}
|
||||
if (!s.empty() && s.back() == '.') {
|
||||
s.pop_back();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// RPN calculator
|
||||
std::string calculate(const std::string & expr) {
|
||||
auto expr_new = emsesp::Helpers::toLower(expr);
|
||||
commands(expr_new);
|
||||
|
||||
const auto tokens = exprToTokens(expr_new);
|
||||
if (tokens.empty()) {
|
||||
return "";
|
||||
}
|
||||
auto queue = shuntingYard(tokens);
|
||||
if (queue.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
// debug only print tokens
|
||||
#ifdef EMSESP_STANDALONE
|
||||
for (const auto & t : queue) {
|
||||
emsesp::EMSESP::logger().debug("shunt token: %s(%d)", t.str.c_str(), t.type);
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
std::vector<std::string> stack;
|
||||
|
||||
while (!queue.empty()) {
|
||||
const auto token = queue.front();
|
||||
queue.pop_front();
|
||||
switch (token.type) {
|
||||
case Token::Type::Number:
|
||||
case Token::Type::String:
|
||||
stack.push_back(token.str);
|
||||
break;
|
||||
case Token::Type::Unary: {
|
||||
if (stack.empty()) {
|
||||
return "";
|
||||
}
|
||||
const auto rhs = stack.back();
|
||||
stack.pop_back();
|
||||
switch (token.str[0]) {
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
case 'm': // Special operator name for unary '-'
|
||||
if (!isnum(rhs)) {
|
||||
return "";
|
||||
}
|
||||
stack.push_back(to_string(-1 * std::stod(rhs)));
|
||||
break;
|
||||
case '!':
|
||||
if (to_logic(rhs) < 0) {
|
||||
return "";
|
||||
}
|
||||
stack.push_back(to_logic(rhs) == 0 ? "1" : "0");
|
||||
break;
|
||||
case 'i':
|
||||
stack.push_back(to_string(std::stoi(rhs)));
|
||||
break;
|
||||
case 'r':
|
||||
stack.push_back(to_string(std::round(std::stod(rhs))));
|
||||
break;
|
||||
case 'a':
|
||||
stack.push_back(to_string(std::abs(std::stod(rhs))));
|
||||
break;
|
||||
case 'e':
|
||||
stack.push_back(to_string(std::exp(std::stod(rhs))));
|
||||
break;
|
||||
case 'l':
|
||||
stack.push_back(to_string(std::log(std::stod(rhs))));
|
||||
break;
|
||||
case 's':
|
||||
stack.push_back(to_string(std::sqrt(std::stod(rhs))));
|
||||
break;
|
||||
case 'p':
|
||||
stack.push_back(to_string(std::pow(std::stod(rhs), 2)));
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case Token::Type::Compare: {
|
||||
if (stack.size() < 2) {
|
||||
return "";
|
||||
}
|
||||
const auto rhs = stack.back();
|
||||
stack.pop_back();
|
||||
const auto lhs = stack.back();
|
||||
stack.pop_back();
|
||||
switch (token.str[0]) {
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
case '<':
|
||||
if (isnum(rhs) && isnum(lhs)) {
|
||||
stack.push_back((std::stod(lhs) < std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
stack.push_back((lhs < rhs) ? "1" : "0");
|
||||
break;
|
||||
case '{':
|
||||
if (isnum(rhs) && isnum(lhs)) {
|
||||
stack.push_back((std::stod(lhs) <= std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
stack.push_back((lhs <= rhs) ? "1" : "0");
|
||||
break;
|
||||
case '>':
|
||||
if (isnum(rhs) && isnum(lhs)) {
|
||||
stack.push_back((std::stod(lhs) > std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
stack.push_back((lhs > rhs) ? "1" : "0");
|
||||
break;
|
||||
case '}':
|
||||
if (isnum(rhs) && isnum(lhs)) {
|
||||
stack.push_back((std::stod(lhs) >= std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
stack.push_back((lhs >= rhs) ? "1" : "0");
|
||||
break;
|
||||
case '=':
|
||||
if (isnum(rhs) && isnum(lhs)) {
|
||||
stack.push_back((std::stod(lhs) == std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
stack.push_back((lhs == rhs) ? "1" : "0");
|
||||
break;
|
||||
case '!':
|
||||
if (isnum(rhs) && isnum(lhs)) {
|
||||
stack.push_back((std::stod(lhs) != std::stod(rhs)) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
stack.push_back((lhs != rhs) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case Token::Type::Logic: {
|
||||
// binary operators
|
||||
if (stack.size() < 2) {
|
||||
return "";
|
||||
}
|
||||
const auto rhs = to_logic(stack.back());
|
||||
stack.pop_back();
|
||||
const auto lhs = to_logic(stack.back());
|
||||
stack.pop_back();
|
||||
if (rhs < 0 || lhs < 0) {
|
||||
return "";
|
||||
}
|
||||
switch (token.str[0]) {
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
case '&':
|
||||
stack.push_back((lhs && rhs) ? "1" : "0");
|
||||
break;
|
||||
case '|':
|
||||
stack.push_back((lhs || rhs) ? "1" : "0");
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case Token::Type::Operator: {
|
||||
// binary operators
|
||||
if (stack.empty() || !isnum(stack.back())) {
|
||||
return "";
|
||||
}
|
||||
const auto rhs = std::stod(stack.back());
|
||||
stack.pop_back();
|
||||
if (stack.empty() || !isnum(stack.back())) {
|
||||
return "";
|
||||
}
|
||||
const auto lhs = std::stod(stack.back());
|
||||
stack.pop_back();
|
||||
|
||||
switch (token.str[0]) {
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
case '^':
|
||||
stack.push_back(to_string(pow(lhs, rhs)));
|
||||
break;
|
||||
case '*':
|
||||
stack.push_back(to_string(lhs * rhs));
|
||||
break;
|
||||
case '/':
|
||||
stack.push_back(to_string(lhs / rhs));
|
||||
break;
|
||||
case '%':
|
||||
stack.push_back(std::to_string(static_cast<int>(lhs) % static_cast<int>(rhs)));
|
||||
break;
|
||||
case '+':
|
||||
stack.push_back(to_string(lhs + rhs));
|
||||
break;
|
||||
case '-':
|
||||
stack.push_back(to_string(lhs - rhs));
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
case Token::Type::LeftParen:
|
||||
case Token::Type::RightParen:
|
||||
case Token::Type::Unknown:
|
||||
default:
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// concatenate all elements in stack to a single string, separated by spaces and return
|
||||
std::string result = "";
|
||||
for (const auto & s : stack) {
|
||||
result += s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// check for multiple instances of <cond> ? <expr1> : <expr2>
|
||||
std::string compute(const std::string & expr) {
|
||||
auto expr_new = emsesp::Helpers::toLower(expr);
|
||||
|
||||
// search json with url:
|
||||
auto f = expr_new.find_first_of('{');
|
||||
while (f != std::string::npos) {
|
||||
auto e = f + 1;
|
||||
for (uint8_t i = 1; i > 0; e++) {
|
||||
if (e >= expr_new.length()) {
|
||||
return "";
|
||||
} else if (expr_new[e] == '}') {
|
||||
i--;
|
||||
} else if (expr_new[e] == '{') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
std::string cmd = expr_new.substr(f, e - f).c_str();
|
||||
JsonDocument doc;
|
||||
if (DeserializationError::Ok == deserializeJson(doc, cmd)) {
|
||||
HTTPClient http;
|
||||
std::string url = doc["url"] | "";
|
||||
if (http.begin(url.c_str())) {
|
||||
int httpResult = 0;
|
||||
for (JsonPair p : doc["header"].as<JsonObject>()) {
|
||||
http.addHeader(p.key().c_str(), p.value().as<std::string>().c_str());
|
||||
}
|
||||
std::string value = doc["value"] | "";
|
||||
std::string method = doc["method"] | "GET"; // default GET
|
||||
|
||||
// if there is data, force a POST
|
||||
if (value.length() || method == "post") {
|
||||
if (value.find_first_of('{') != std::string::npos) {
|
||||
http.addHeader("Content-Type", "application/json"); // auto-set to JSON
|
||||
}
|
||||
httpResult = http.POST(value.c_str());
|
||||
} else {
|
||||
httpResult = http.GET(); // normal GET
|
||||
}
|
||||
|
||||
if (httpResult > 0) {
|
||||
std::string result = emsesp::Helpers::toLower(http.getString().c_str());
|
||||
std::string key = doc["key"] | "";
|
||||
doc.clear();
|
||||
if (key.length() && DeserializationError::Ok == deserializeJson(doc, result)) {
|
||||
result = doc[key.c_str()].as<std::string>();
|
||||
}
|
||||
expr_new.replace(f, e - f, result.c_str());
|
||||
}
|
||||
http.end();
|
||||
}
|
||||
}
|
||||
f = expr_new.find_first_of('{', e);
|
||||
}
|
||||
|
||||
// positions: q-questionmark, c-colon
|
||||
auto q = expr_new.find_first_of('?');
|
||||
while (q != std::string::npos) {
|
||||
// find corresponding colon
|
||||
auto c1 = expr_new.find_first_of(':', q + 1);
|
||||
auto q1 = expr_new.find_first_of('?', q + 1);
|
||||
while (q1 < c1 && q1 != std::string::npos && c1 != std::string::npos) {
|
||||
q1 = expr_new.find_first_of('?', q1 + 1);
|
||||
c1 = expr_new.find_first_of(':', c1 + 1);
|
||||
}
|
||||
if (c1 == std::string::npos) {
|
||||
return ""; // error: missing colon
|
||||
}
|
||||
std::string cond = calculate(expr_new.substr(0, q));
|
||||
if (cond.length() == 0) {
|
||||
return "";
|
||||
} else if (cond[0] == '1') {
|
||||
expr_new.erase(c1); // remove second expression after colon
|
||||
expr_new.erase(0, q + 1); // remove condition before questionmark
|
||||
} else if (cond[0] == '0') {
|
||||
expr_new.erase(0, c1 + 1); // remove condition and first expression
|
||||
} else {
|
||||
return ""; // error
|
||||
}
|
||||
q = expr_new.find_first_of('?'); // search next instance
|
||||
}
|
||||
|
||||
return calculate(expr_new);
|
||||
}
|
||||
2147
src/core/system.cpp
Normal file
2147
src/core/system.cpp
Normal file
File diff suppressed because it is too large
Load Diff
444
src/core/system.h
Normal file
444
src/core/system.h
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_SYSTEM_H_
|
||||
#define EMSESP_SYSTEM_H_
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "console.h"
|
||||
#include "mqtt.h"
|
||||
#include "telegram.h"
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <esp_wifi.h>
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
// #include <esp_bt.h>
|
||||
#endif
|
||||
#include <ETH.h>
|
||||
#include <uuid/syslog.h>
|
||||
#endif
|
||||
|
||||
#include <uuid/log.h>
|
||||
#include <PButton.h>
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#if ESP_IDF_VERSION_MAJOR < 5
|
||||
#include "driver/temp_sensor.h"
|
||||
#else
|
||||
#include "driver/temperature_sensor.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using uuid::console::Shell;
|
||||
|
||||
#define EMSESP_FS_CONFIG_DIRECTORY "/config"
|
||||
|
||||
#define EMSESP_CUSTOMSUPPORT_FILE "/config/customSupport.json"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 };
|
||||
|
||||
class System {
|
||||
public:
|
||||
void start();
|
||||
void loop();
|
||||
|
||||
// commands
|
||||
static bool command_read(const char * value, const int8_t id);
|
||||
static bool command_send(const char * value, const int8_t id);
|
||||
static bool command_publish(const char * value, const int8_t id);
|
||||
static bool command_fetch(const char * value, const int8_t id);
|
||||
static bool command_restart(const char * value, const int8_t id);
|
||||
static bool command_format(const char * value, const int8_t id);
|
||||
static bool command_watch(const char * value, const int8_t id);
|
||||
static bool command_message(const char * value, const int8_t id);
|
||||
static bool command_info(const char * value, const int8_t id, JsonObject output);
|
||||
static bool command_response(const char * value, const int8_t id, JsonObject output);
|
||||
static bool command_service(const char * cmd, const char * value);
|
||||
|
||||
static bool get_value_info(JsonObject root, const char * cmd);
|
||||
static void get_value_json(JsonObject output, const std::string & circuit, const std::string & name, JsonVariant val);
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
static bool command_test(const char * value, const int8_t id);
|
||||
#endif
|
||||
|
||||
std::string reset_reason(uint8_t cpu) const;
|
||||
|
||||
void store_nvs_values();
|
||||
void system_restart(const char * partition = nullptr);
|
||||
void upload_isrunning(bool in_progress);
|
||||
bool upload_isrunning();
|
||||
void show_mem(const char * note);
|
||||
void reload_settings();
|
||||
void syslog_init();
|
||||
bool check_upgrade(bool factory_settings);
|
||||
bool check_restore();
|
||||
void heartbeat_json(JsonObject output);
|
||||
void send_heartbeat();
|
||||
void send_info_mqtt();
|
||||
|
||||
bool syslog_enabled() {
|
||||
return syslog_enabled_;
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
unsigned long syslog_count() {
|
||||
return syslog_.message_count();
|
||||
}
|
||||
|
||||
unsigned long syslog_fails() {
|
||||
return syslog_.message_fails();
|
||||
}
|
||||
#endif
|
||||
|
||||
String getBBQKeesGatewayDetails();
|
||||
|
||||
static bool uploadFirmwareURL(const char * url = nullptr);
|
||||
|
||||
void led_init(bool refresh);
|
||||
void network_init(bool refresh);
|
||||
void button_init(bool refresh);
|
||||
void commands_init();
|
||||
|
||||
static void extractSettings(const char * filename, const char * section, JsonObject output);
|
||||
static bool saveSettings(const char * filename, const char * section, JsonObject input);
|
||||
|
||||
static bool is_valid_gpio(uint8_t pin, bool has_psram = false);
|
||||
static bool load_board_profile(std::vector<int8_t> & data, const std::string & board_profile);
|
||||
|
||||
static bool readCommand(const char * data);
|
||||
|
||||
static void restart_requested(bool restart_requested) {
|
||||
restart_requested_ = restart_requested;
|
||||
}
|
||||
static bool restart_requested() {
|
||||
return restart_requested_;
|
||||
}
|
||||
|
||||
static void restart_pending(bool restart_pending) {
|
||||
restart_pending_ = restart_pending;
|
||||
}
|
||||
static bool restart_pending() {
|
||||
return restart_pending_;
|
||||
}
|
||||
|
||||
bool telnet_enabled() {
|
||||
return telnet_enabled_;
|
||||
}
|
||||
|
||||
bool modbus_enabled() {
|
||||
return modbus_enabled_;
|
||||
}
|
||||
|
||||
uint16_t modbus_port() {
|
||||
return modbus_port_;
|
||||
}
|
||||
|
||||
uint8_t modbus_max_clients() {
|
||||
return modbus_max_clients_;
|
||||
}
|
||||
|
||||
uint32_t modbus_timeout() {
|
||||
return modbus_timeout_;
|
||||
}
|
||||
|
||||
bool analog_enabled() {
|
||||
return analog_enabled_;
|
||||
}
|
||||
|
||||
void analog_enabled(bool b) {
|
||||
analog_enabled_ = b;
|
||||
}
|
||||
|
||||
void hide_led(bool b) {
|
||||
hide_led_ = b;
|
||||
}
|
||||
|
||||
bool readonly_mode() {
|
||||
return readonly_mode_;
|
||||
}
|
||||
|
||||
void readonly_mode(bool readonly_mode) {
|
||||
readonly_mode_ = readonly_mode;
|
||||
}
|
||||
|
||||
bool developer_mode() {
|
||||
return developer_mode_;
|
||||
}
|
||||
|
||||
void developer_mode(bool developer_mode) {
|
||||
developer_mode_ = developer_mode;
|
||||
}
|
||||
|
||||
// Boolean Format API/MQTT
|
||||
uint8_t bool_format() {
|
||||
return bool_format_;
|
||||
}
|
||||
|
||||
// Boolean Format Web
|
||||
uint8_t bool_dashboard() {
|
||||
return bool_dashboard_;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void bool_dashboard(uint8_t format) {
|
||||
bool_dashboard_ = 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() {
|
||||
return hostname_;
|
||||
}
|
||||
|
||||
void hostname(const std::string hostname) {
|
||||
hostname_ = hostname;
|
||||
}
|
||||
|
||||
bool ethernet_connected() {
|
||||
return ethernet_connected_;
|
||||
}
|
||||
|
||||
void ethernet_connected(bool b) {
|
||||
ethernet_connected_ = b;
|
||||
}
|
||||
|
||||
void has_ipv6(bool b) {
|
||||
has_ipv6_ = b;
|
||||
}
|
||||
|
||||
bool has_ipv6() {
|
||||
return has_ipv6_;
|
||||
}
|
||||
|
||||
void ntp_connected(bool b);
|
||||
bool ntp_connected();
|
||||
|
||||
bool network_connected() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
return (ethernet_connected() || WiFi.isConnected());
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void fahrenheit(bool b) {
|
||||
fahrenheit_ = b;
|
||||
}
|
||||
|
||||
bool fahrenheit() {
|
||||
return fahrenheit_;
|
||||
}
|
||||
|
||||
uint8_t language_index();
|
||||
|
||||
void locale(String locale) {
|
||||
locale_ = locale;
|
||||
}
|
||||
|
||||
std::string locale() {
|
||||
return std::string(locale_.c_str());
|
||||
}
|
||||
|
||||
void healthcheck(uint8_t healthcheck) {
|
||||
healthcheck_ = healthcheck;
|
||||
}
|
||||
|
||||
void show_system(uuid::console::Shell & shell);
|
||||
void wifi_reconnect();
|
||||
void show_users(uuid::console::Shell & shell);
|
||||
|
||||
uint32_t FStotal() {
|
||||
return fstotal_;
|
||||
}
|
||||
|
||||
void PSram(uint32_t psram) {
|
||||
psram_ = psram / 1024;
|
||||
}
|
||||
uint32_t PSram() {
|
||||
return psram_;
|
||||
}
|
||||
uint32_t appFree() {
|
||||
return appfree_;
|
||||
}
|
||||
uint32_t appUsed() {
|
||||
return appused_;
|
||||
}
|
||||
|
||||
// memory in kb
|
||||
static uint32_t getMaxAllocMem() {
|
||||
return max_alloc_mem_;
|
||||
}
|
||||
static uint32_t getHeapMem() {
|
||||
return heap_mem_;
|
||||
}
|
||||
static void refreshHeapMem() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
max_alloc_mem_ = ESP.getMaxAllocHeap() / 1024;
|
||||
heap_mem_ = ESP.getFreeHeap() / 1024;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool test_set_all_active() {
|
||||
return test_set_all_active_;
|
||||
}
|
||||
static void test_set_all_active(bool n) {
|
||||
#if defined(EMSESP_TEST)
|
||||
if (n) {
|
||||
logger_.debug("Using dummy entity values");
|
||||
}
|
||||
#endif
|
||||
test_set_all_active_ = n;
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||
float temperature() {
|
||||
return temperature_;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
static uuid::log::Logger logger_;
|
||||
static bool restart_requested_;
|
||||
static bool restart_pending_; // used in 2-stage process to call restart from Web API
|
||||
static bool test_set_all_active_; // force all entities in a device to have a value
|
||||
static uint32_t max_alloc_mem_;
|
||||
static uint32_t heap_mem_;
|
||||
|
||||
// button
|
||||
static PButton myPButton_; // PButton instance
|
||||
static void button_OnClick(PButton & b);
|
||||
static void button_OnDblClick(PButton & b);
|
||||
static void button_OnLongPress(PButton & b);
|
||||
static void button_OnVLongPress(PButton & b);
|
||||
static constexpr uint32_t BUTTON_Debounce = 40; // Debounce period to prevent flickering when pressing or releasing the button (in ms)
|
||||
static constexpr uint32_t BUTTON_DblClickDelay = 250; // Max period between clicks for a double click event (in ms)
|
||||
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)
|
||||
|
||||
// healthcheck
|
||||
#ifdef EMSESP_PINGTEST
|
||||
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 500; // do a system check every 1/2 second
|
||||
#else
|
||||
static constexpr uint32_t SYSTEM_CHECK_FREQUENCY = 5000; // do a system check every 5 seconds
|
||||
#endif
|
||||
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 uint8_t LED_ON = HIGH; // LED on
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
static uuid::syslog::SyslogService syslog_;
|
||||
#endif
|
||||
|
||||
void led_monitor();
|
||||
void system_check();
|
||||
|
||||
int8_t wifi_quality(int8_t dBm);
|
||||
|
||||
uint8_t healthcheck_ = HEALTHCHECK_NO_NETWORK | HEALTHCHECK_NO_BUS; // start with all flags set, no wifi and no ems bus connection
|
||||
uint32_t last_system_check_ = 0;
|
||||
|
||||
bool upload_isrunning_ = false; // true if we're in the middle of a OTA firmware upload
|
||||
bool ethernet_connected_ = false;
|
||||
bool has_ipv6_ = false;
|
||||
|
||||
bool ntp_connected_ = false;
|
||||
uint32_t ntp_last_check_ = 0;
|
||||
|
||||
bool eth_present_ = false;
|
||||
|
||||
// EMS-ESP settings
|
||||
// copies from WebSettings class in WebSettingsService.h and loaded with reload_settings()
|
||||
std::string hostname_;
|
||||
String locale_;
|
||||
bool hide_led_;
|
||||
uint8_t led_gpio_;
|
||||
bool analog_enabled_;
|
||||
bool low_clock_;
|
||||
String board_profile_;
|
||||
uint8_t pbutton_gpio_;
|
||||
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_dashboard_;
|
||||
uint8_t bool_format_;
|
||||
uint8_t enum_format_;
|
||||
bool readonly_mode_;
|
||||
String version_;
|
||||
bool modbus_enabled_;
|
||||
uint16_t modbus_port_;
|
||||
uint8_t modbus_max_clients_;
|
||||
uint32_t modbus_timeout_;
|
||||
bool developer_mode_;
|
||||
|
||||
// ethernet
|
||||
uint8_t phy_type_;
|
||||
int8_t eth_power_;
|
||||
uint8_t eth_phy_addr_;
|
||||
uint8_t eth_clock_mode_;
|
||||
|
||||
uint32_t fstotal_;
|
||||
uint32_t psram_;
|
||||
uint32_t appused_;
|
||||
uint32_t appfree_;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
temperature_sensor_handle_t temperature_handle_ = NULL;
|
||||
#endif
|
||||
float temperature_ = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
686
src/core/telegram.cpp
Normal file
686
src/core/telegram.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "telegram.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
// CRC lookup table with poly 12 for faster checking
|
||||
const uint8_t ems_crc_table[] = {0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24, 0x26,
|
||||
0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E,
|
||||
0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76,
|
||||
0x78, 0x7A, 0x7C, 0x7E, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E,
|
||||
0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0xBE, 0xC0, 0xC2, 0xC4, 0xC6,
|
||||
0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,
|
||||
0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE, 0x19, 0x1B, 0x1D, 0x1F, 0x11, 0x13, 0x15, 0x17, 0x09, 0x0B, 0x0D, 0x0F,
|
||||
0x01, 0x03, 0x05, 0x07, 0x39, 0x3B, 0x3D, 0x3F, 0x31, 0x33, 0x35, 0x37, 0x29, 0x2B, 0x2D, 0x2F, 0x21, 0x23, 0x25, 0x27,
|
||||
0x59, 0x5B, 0x5D, 0x5F, 0x51, 0x53, 0x55, 0x57, 0x49, 0x4B, 0x4D, 0x4F, 0x41, 0x43, 0x45, 0x47, 0x79, 0x7B, 0x7D, 0x7F,
|
||||
0x71, 0x73, 0x75, 0x77, 0x69, 0x6B, 0x6D, 0x6F, 0x61, 0x63, 0x65, 0x67, 0x99, 0x9B, 0x9D, 0x9F, 0x91, 0x93, 0x95, 0x97,
|
||||
0x89, 0x8B, 0x8D, 0x8F, 0x81, 0x83, 0x85, 0x87, 0xB9, 0xBB, 0xBD, 0xBF, 0xB1, 0xB3, 0xB5, 0xB7, 0xA9, 0xAB, 0xAD, 0xAF,
|
||||
0xA1, 0xA3, 0xA5, 0xA7, 0xD9, 0xDB, 0xDD, 0xDF, 0xD1, 0xD3, 0xD5, 0xD7, 0xC9, 0xCB, 0xCD, 0xCF, 0xC1, 0xC3, 0xC5, 0xC7,
|
||||
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;
|
||||
uint8_t EMSbus::tx_mode_ = EMSESP_DEFAULT_TX_MODE;
|
||||
uint8_t EMSbus::tx_state_ = Telegram::Operation::NONE;
|
||||
|
||||
uuid::log::Logger EMSbus::logger_{F_(telegram), uuid::log::Facility::CONSOLE};
|
||||
|
||||
// Calculates CRC checksum using lookup table for speed
|
||||
// length excludes the last byte (which mainly is the CRC)
|
||||
uint8_t EMSbus::calculate_crc(const uint8_t * data, const uint8_t length) {
|
||||
uint8_t i = 0;
|
||||
uint8_t crc = 0;
|
||||
while (i < length) {
|
||||
crc = ems_crc_table[crc];
|
||||
crc ^= data[i++];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
// creates a telegram object
|
||||
// stores header in separate member objects and the rest in the message_data block
|
||||
Telegram::Telegram(const uint8_t operation,
|
||||
const uint8_t src,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
const uint8_t * data,
|
||||
const uint8_t message_length)
|
||||
: operation(operation)
|
||||
, src(src)
|
||||
, dest(dest)
|
||||
, type_id(type_id)
|
||||
, offset(offset)
|
||||
, message_length(message_length) {
|
||||
// copy complete telegram data over, preventing buffer overflow
|
||||
// faster than using std::move()
|
||||
for (uint8_t i = 0; ((i < message_length) && (i < EMS_MAX_TELEGRAM_MESSAGE_LENGTH)); i++) {
|
||||
message_data[i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
// returns telegram as data bytes in hex (excluding CRC)
|
||||
std::string Telegram::to_string() const {
|
||||
uint8_t data[EMS_MAX_TELEGRAM_LENGTH];
|
||||
uint8_t length = 0;
|
||||
data[0] = this->src ^ RxService::ems_mask();
|
||||
data[3] = this->offset;
|
||||
if (this->operation == Telegram::Operation::TX_READ) {
|
||||
data[1] = this->dest | 0x80;
|
||||
data[4] = this->message_data[0];
|
||||
if (this->type_id > 0xFF) {
|
||||
data[2] = 0xFF;
|
||||
data[5] = (this->type_id >> 8) - 1;
|
||||
data[6] = this->type_id & 0xFF;
|
||||
length = 7;
|
||||
} else {
|
||||
data[2] = this->type_id;
|
||||
length = 5;
|
||||
}
|
||||
} else {
|
||||
data[1] = this->dest;
|
||||
if (this->type_id > 0xFF) {
|
||||
data[2] = 0xFF;
|
||||
data[4] = (this->type_id >> 8) - 1;
|
||||
data[5] = this->type_id & 0xFF;
|
||||
length = 6;
|
||||
} else {
|
||||
data[2] = this->type_id;
|
||||
length = 4;
|
||||
}
|
||||
for (uint8_t i = 0; i < this->message_length; i++) {
|
||||
data[length++] = this->message_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return Helpers::data_to_hex(data, length);
|
||||
}
|
||||
|
||||
// returns telegram's message body only, in hex
|
||||
std::string Telegram::to_string_message() const {
|
||||
if (this->message_length == 0) {
|
||||
return "<empty>";
|
||||
}
|
||||
|
||||
return Helpers::data_to_hex(this->message_data, this->message_length);
|
||||
}
|
||||
|
||||
// checks if we have an Rx telegram that needs processing
|
||||
void RxService::loop() {
|
||||
while (!rx_telegrams_.empty()) {
|
||||
auto telegram = rx_telegrams_.front().telegram_;
|
||||
(void)EMSESP::process_telegram(telegram); // further process the telegram
|
||||
increment_telegram_count(); // increase rx count
|
||||
rx_telegrams_.pop_front(); // remove it from the queue
|
||||
}
|
||||
}
|
||||
|
||||
// add a new rx telegram object
|
||||
// data is the whole telegram, assuming last byte holds the CRC
|
||||
// length includes the CRC
|
||||
// for EMS+ the type_id has the value + 256. We look for these type of telegrams with F7, F9 and FF in 3rd byte
|
||||
void RxService::add(uint8_t * data, uint8_t length) {
|
||||
if (length < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
// validate the CRC. if it fails then increment the number of corrupt/incomplete telegrams and only report to console/syslog
|
||||
uint8_t crc = calculate_crc(data, length - 1);
|
||||
if (data[length - 1] != crc) {
|
||||
if (data[0] != EMSuart::last_tx_src()) { // do not count echos as errors
|
||||
telegram_error_count_++;
|
||||
LOG_WARNING("Incomplete Rx: %s", Helpers::data_to_hex(data, length).c_str()); // include CRC
|
||||
} else {
|
||||
LOG_TRACE("Incomplete Rx: %s", Helpers::data_to_hex(data, length).c_str()); // include CRC
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// since it's a valid telegram, work out the ems mask
|
||||
// we check the 1st byte, which assumed is the src ID and see if the MSB (8th bit) is set
|
||||
// this is used to identify if the protocol should be Junkers/HT3 or Buderus
|
||||
// this only happens once with the first valid rx telegram is processed
|
||||
if (ems_mask() == EMS_MASK_UNSET) {
|
||||
ems_mask(data[0]);
|
||||
}
|
||||
|
||||
// src, dest and offset are always in fixed positions
|
||||
uint8_t src = data[0] & 0x7F; // strip MSB (HT3 adds it)
|
||||
uint8_t dest = data[1] & 0x7F; // strip MSB, don't care if its read or write for processing
|
||||
uint8_t offset = data[3]; // offset is always 4th byte
|
||||
uint8_t operation = (data[1] & 0x80) ? Telegram::Operation::RX_READ : Telegram::Operation::RX;
|
||||
|
||||
uint16_t type_id;
|
||||
uint8_t * message_data; // where the message block starts
|
||||
uint8_t message_length; // length of the message block, excluding CRC
|
||||
|
||||
// work out depending on the type, where the data message block starts and the message length
|
||||
// EMS 1 has type_id always in data[2], if it gets a ems+ inquiry it will reply with FF but short length
|
||||
// i.e. sending 0B A1 FF 00 01 D8 20 CRC to a MM10 Mixer (ems1.0), the reply is 21 0B FF 00 CRC
|
||||
// see: https://github.com/emsesp/EMS-ESP/issues/380#issuecomment-633663007
|
||||
if (data[2] != 0xFF || length < 6) {
|
||||
// EMS 1.0
|
||||
// also handle F7, F9 as EMS 1.0, see https://github.com/emsesp/EMS-ESP/issues/109#issuecomment-492781044
|
||||
type_id = data[2];
|
||||
message_data = data + 4;
|
||||
message_length = length - 5;
|
||||
} else if (data[1] & 0x80) {
|
||||
// EMS 2.0 read request
|
||||
type_id = (data[5] << 8) + data[6] + 256;
|
||||
message_data = data + 4; // only data is the requested length
|
||||
message_length = 1;
|
||||
} else {
|
||||
// EMS 2.0 / EMS+
|
||||
type_id = (data[4] << 8) + data[5] + 256;
|
||||
message_data = data + 6;
|
||||
message_length = length - 7;
|
||||
}
|
||||
|
||||
// if we're watching and "raw" print out actual telegram as bytes to the console
|
||||
// including the CRC at the end
|
||||
if (EMSESP::watch() == EMSESP::Watch::WATCH_RAW) {
|
||||
uint16_t trace_watch_id = EMSESP::watch_id();
|
||||
if ((trace_watch_id == WATCH_ID_NONE) || (type_id == trace_watch_id)
|
||||
|| ((trace_watch_id < 0x80) && ((src == trace_watch_id) || (dest == trace_watch_id)))) {
|
||||
LOG_NOTICE("Rx: %s", Helpers::data_to_hex(data, length).c_str());
|
||||
} else if (EMSESP::trace_raw()) {
|
||||
LOG_TRACE("Rx: %s", Helpers::data_to_hex(data, length).c_str());
|
||||
}
|
||||
} else if (EMSESP::trace_raw()) {
|
||||
LOG_TRACE("Rx: %s", Helpers::data_to_hex(data, length).c_str());
|
||||
}
|
||||
|
||||
LOG_DEBUG("New Rx telegram, message length %d", message_length);
|
||||
|
||||
// if we don't have a type_id exit,
|
||||
// do not exit on empty message, it is checked for toggle fetch
|
||||
if (type_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create the telegram
|
||||
auto telegram = std::make_shared<Telegram>(operation, src, dest, type_id, offset, message_data, message_length);
|
||||
|
||||
// check if queue is full, if so remove top item to make space
|
||||
if (rx_telegrams_.size() >= MAX_RX_TELEGRAMS) {
|
||||
rx_telegrams_.pop_front();
|
||||
}
|
||||
|
||||
rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram)); // add to queue
|
||||
}
|
||||
|
||||
// add empty telegram to rx-queue
|
||||
void RxService::add_empty(const uint8_t src, const uint8_t dest, const uint16_t type_id, uint8_t offset) {
|
||||
auto telegram = std::make_shared<Telegram>(Telegram::Operation::RX, src, dest, type_id, offset, nullptr, 0);
|
||||
// only if queue is not full
|
||||
if (rx_telegrams_.size() < MAX_RX_TELEGRAMS) {
|
||||
rx_telegrams_.emplace_back(rx_telegram_id_++, std::move(telegram)); // add to queue
|
||||
}
|
||||
}
|
||||
|
||||
// start and initialize Tx
|
||||
// send out request to EMS bus for all devices
|
||||
void TxService::start() {
|
||||
// grab the bus ID and tx_mode
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||
ems_bus_id(settings.ems_bus_id);
|
||||
tx_mode(settings.tx_mode);
|
||||
});
|
||||
|
||||
// reset counters
|
||||
telegram_read_count(0);
|
||||
telegram_write_count(0);
|
||||
telegram_fail_count(0);
|
||||
|
||||
// send first Tx request to bus master (boiler) for its registered devices
|
||||
// this will be added to the queue and sent during the first tx loop()
|
||||
read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER);
|
||||
}
|
||||
|
||||
// sends a 1 byte poll which is our own deviceID
|
||||
void TxService::send_poll() const {
|
||||
//LOG_DEBUG("Ack %02X",ems_bus_id() ^ ems_mask());
|
||||
if (tx_mode()) {
|
||||
EMSuart::send_poll(ems_bus_id() ^ ems_mask());
|
||||
}
|
||||
}
|
||||
|
||||
// get src id from next telegram to check poll in emsesp::incoming_telegram
|
||||
uint8_t TxService::get_send_id() {
|
||||
static uint32_t count = 0;
|
||||
if (!tx_telegrams_.empty() && tx_telegrams_.front().telegram_->src != ems_bus_id()) {
|
||||
if (++count > 500) { // after 500 polls (~3-10 sec) there will be no master poll for this id
|
||||
tx_telegrams_.pop_front();
|
||||
count = 0;
|
||||
return tx_telegrams_.empty() ? ems_bus_id() : tx_telegrams_.front().telegram_->src;
|
||||
}
|
||||
return tx_telegrams_.front().telegram_->src;
|
||||
}
|
||||
count = 0;
|
||||
return ems_bus_id();
|
||||
}
|
||||
|
||||
// Process the next telegram on the Tx queue
|
||||
// This is sent when we receive a poll request
|
||||
void TxService::send() {
|
||||
// don't process if we don't have a connection to the EMS bus
|
||||
if (!bus_connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if there's nothing in the queue to transmit or sending should be delayed, send back a poll and quit
|
||||
if (tx_telegrams_.empty() || (delayed_send_ && uuid::get_uptime() < delayed_send_)) {
|
||||
send_poll();
|
||||
return;
|
||||
}
|
||||
delayed_send_ = 0;
|
||||
|
||||
// if we're in read-only mode (tx_mode 0) forget the Tx call
|
||||
if (tx_mode() != 0) {
|
||||
send_telegram(tx_telegrams_.front());
|
||||
}
|
||||
|
||||
tx_telegrams_.pop_front(); // remove the telegram from the queue
|
||||
}
|
||||
|
||||
// process a Tx telegram
|
||||
void TxService::send_telegram(const QueuedTxTelegram & tx_telegram) {
|
||||
static uint8_t telegram_raw[EMS_MAX_TELEGRAM_LENGTH];
|
||||
|
||||
// build the header
|
||||
auto telegram = tx_telegram.telegram_;
|
||||
|
||||
// src - set MSB if it's Junkers/HT3
|
||||
uint8_t src = telegram->src;
|
||||
if (ems_mask() != EMS_MASK_UNSET) {
|
||||
src ^= ems_mask();
|
||||
}
|
||||
telegram_raw[0] = src;
|
||||
|
||||
// dest - for READ the MSB must be set
|
||||
// fix the READ or WRITE depending on the operation
|
||||
uint8_t dest = telegram->dest;
|
||||
|
||||
if (telegram->operation == Telegram::Operation::TX_READ) {
|
||||
dest |= 0x80; // read has 8th bit set for the destination
|
||||
}
|
||||
telegram_raw[1] = dest;
|
||||
|
||||
uint8_t message_p = 0; // this is the position in the telegram where we want to put our message data
|
||||
bool copy_data = true; // true if we want to copy over the data message block to the end of the telegram header
|
||||
|
||||
if (telegram->type_id > 0xFF) {
|
||||
// it's EMS 2.0/+
|
||||
telegram_raw[2] = 0xFF; // fixed value indicating an extended message
|
||||
telegram_raw[3] = telegram->offset;
|
||||
|
||||
// EMS+ has different format for read and write
|
||||
if (telegram->operation != Telegram::Operation::TX_READ && telegram->operation != Telegram::Operation::TX_RAW) {
|
||||
// WRITE/NONE
|
||||
telegram_raw[4] = (telegram->type_id >> 8) - 1; // type, 1st byte, high-byte, subtract 0x100
|
||||
telegram_raw[5] = telegram->type_id & 0xFF; // type, 2nd byte, low-byte
|
||||
message_p = 6;
|
||||
} else {
|
||||
// READ
|
||||
telegram_raw[4] =
|
||||
telegram->message_data[0] > 25 ? 25 : telegram->message_data[0]; // #bytes to return, which we assume is the only byte in the message block
|
||||
telegram_raw[5] = (telegram->type_id >> 8) - 1; // type, 1st byte, high-byte, subtract 0x100
|
||||
telegram_raw[6] = telegram->type_id & 0xFF; // type, 2nd byte, low-byte
|
||||
message_p = 7;
|
||||
copy_data = false; // there are no more data values after the type_id when reading on EMS+
|
||||
}
|
||||
} else {
|
||||
// EMS 1.0
|
||||
telegram_raw[2] = telegram->type_id;
|
||||
telegram_raw[3] = telegram->offset;
|
||||
message_p = 4;
|
||||
if (telegram->operation == Telegram::Operation::TX_READ) {
|
||||
telegram_raw[4] = telegram->message_data[0] > 27 ? 27 : telegram->message_data[0];
|
||||
message_p = 5;
|
||||
copy_data = false; // there are no more data value
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_data) {
|
||||
if (telegram->message_length > EMS_MAX_TELEGRAM_MESSAGE_LENGTH) {
|
||||
return; // too big
|
||||
}
|
||||
|
||||
// add the data to send to to the end of the header
|
||||
for (uint8_t i = 0; i < telegram->message_length; i++) {
|
||||
telegram_raw[message_p++] = telegram->message_data[i];
|
||||
}
|
||||
}
|
||||
// make a copy of the telegram with new dest (without read-flag)
|
||||
telegram_last_ = std::make_shared<Telegram>(
|
||||
telegram->operation, telegram->src, dest & 0x7F, telegram->type_id, telegram->offset, telegram->message_data, telegram->message_length);
|
||||
|
||||
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
|
||||
|
||||
// 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("[readonly] Sending write Tx telegram: %s", Helpers::data_to_hex(telegram_raw, length - 1).c_str());
|
||||
tx_state(Telegram::Operation::NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Sending %s Tx [#%d], telegram: %s",
|
||||
(telegram->operation != Telegram::Operation::TX_READ) ? ("write") : ("read"),
|
||||
tx_telegram.id_,
|
||||
Helpers::data_to_hex(telegram_raw, length - 1).c_str()); // exclude the last CRC byte
|
||||
|
||||
set_post_send_query(tx_telegram.validateid_);
|
||||
|
||||
//
|
||||
// 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("Failed to transmit Tx via UART.");
|
||||
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;
|
||||
}
|
||||
|
||||
if (telegram->operation == Telegram::Operation::TX_RAW) {
|
||||
tx_state(Telegram::Operation::TX_READ);
|
||||
if (EMSESP::response_id() == 0) {
|
||||
Mqtt::clear_response();
|
||||
EMSESP::set_response_id(telegram->type_id);
|
||||
if (telegram->message_data[0] >= (telegram->type_id > 0xFF ? 25 : 27)) {
|
||||
EMSESP::set_read_id(telegram->type_id);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
tx_state(telegram->operation); // tx now in a wait state
|
||||
}
|
||||
|
||||
void TxService::add(const uint8_t operation,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validateid,
|
||||
const bool front) {
|
||||
auto telegram = std::make_shared<Telegram>(operation, ems_bus_id(), dest, type_id, offset, message_data, message_length);
|
||||
|
||||
LOG_DEBUG("New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
|
||||
// if the queue is full, make room by removing the last one
|
||||
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
|
||||
LOG_WARNING("Tx queue overflow, skip one message");
|
||||
if (tx_telegrams_.front().telegram_->operation == Telegram::Operation::TX_WRITE) {
|
||||
telegram_write_fail_count_++;
|
||||
} else {
|
||||
telegram_read_fail_count_++;
|
||||
}
|
||||
tx_telegrams_.pop_front();
|
||||
}
|
||||
|
||||
if (front) {
|
||||
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram), false, validateid); // add to front of queue
|
||||
} else {
|
||||
tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validateid); // add to back of queue
|
||||
}
|
||||
if (validateid != 0) {
|
||||
EMSESP::wait_validate(validateid);
|
||||
}
|
||||
}
|
||||
|
||||
// builds a Tx telegram and adds to queue
|
||||
// this is used by the retry() function to put the last failed Tx back into the queue
|
||||
// format is EMS 1.0 (src, dest, type_id, offset, data)
|
||||
// length is the length of the whole telegram data, excluding the CRC
|
||||
void TxService::add(uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front) {
|
||||
// check length
|
||||
if (length < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
// build header. src, dest and offset have fixed positions
|
||||
uint8_t src = operation == Telegram::Operation::TX_RAW && data[0] ? data[0] : ems_bus_id();
|
||||
uint8_t dest = data[1];
|
||||
uint8_t offset = data[3];
|
||||
|
||||
uint16_t validate_id = validateid;
|
||||
uint16_t type_id;
|
||||
const uint8_t * message_data; // where the message block starts
|
||||
uint8_t message_length; // length of the message block, excluding CRC
|
||||
|
||||
// work out depending on the type, where the data message block starts and the message length
|
||||
// same logic as in RxService::add(), but adjusted for no appended CRC
|
||||
if (data[2] != 0xFF) {
|
||||
// EMS 1.0
|
||||
type_id = data[2];
|
||||
message_data = data + 4;
|
||||
message_length = length - 4;
|
||||
} else if (data[1] & 0x80) {
|
||||
type_id = (data[5] << 8) + data[6] + 256;
|
||||
message_data = data + 4;
|
||||
message_length = 1;
|
||||
} else {
|
||||
// EMS 2.0 / EMS+
|
||||
type_id = (data[4] << 8) + data[5] + 256;
|
||||
message_data = data + 6;
|
||||
message_length = length - 6;
|
||||
}
|
||||
|
||||
// if we don't have a type_id or empty data block, exit
|
||||
if ((type_id == 0) || (message_length == 0)) {
|
||||
LOG_DEBUG("Tx telegram type %d failed, length %d", type_id, message_length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (operation == Telegram::Operation::TX_RAW) {
|
||||
if (src != ems_bus_id() || dest == 0) {
|
||||
operation = Telegram::Operation::NONE; // do not check reply/ack for other ids and broadcasts
|
||||
} else if (dest & 0x80) {
|
||||
// keep operation RAW to set the response when sending
|
||||
// Mqtt::clear_response(); // set here when receiving command or when sending?
|
||||
} else {
|
||||
operation = Telegram::Operation::TX_WRITE;
|
||||
validate_id = type_id;
|
||||
}
|
||||
}
|
||||
|
||||
auto telegram = std::make_shared<Telegram>(operation, src, dest, type_id, offset, message_data, message_length); // operation is TX_WRITE or TX_READ
|
||||
|
||||
// if the queue is full, make room by removing the last one
|
||||
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
|
||||
LOG_WARNING("Tx queue overflow, skip one message");
|
||||
if (tx_telegrams_.front().telegram_->operation == Telegram::Operation::TX_WRITE) {
|
||||
telegram_write_fail_count_++;
|
||||
} else {
|
||||
telegram_read_fail_count_++;
|
||||
}
|
||||
tx_telegrams_.pop_front();
|
||||
}
|
||||
|
||||
LOG_DEBUG("New Tx [#%d] telegram, length %d", tx_telegram_id_, message_length);
|
||||
|
||||
if (front && (operation != Telegram::Operation::TX_RAW || EMSESP::response_id() == 0)) {
|
||||
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram), false, validate_id); // add to front of queue
|
||||
} else {
|
||||
tx_telegrams_.emplace_back(tx_telegram_id_++, std::move(telegram), false, validate_id); // add to back of queue
|
||||
}
|
||||
if (validate_id != 0) {
|
||||
EMSESP::wait_validate(validate_id);
|
||||
}
|
||||
}
|
||||
|
||||
// send a Tx telegram to request data from an EMS device
|
||||
void TxService::read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset, const uint8_t length, const bool front) {
|
||||
LOG_DEBUG("Tx read request to deviceID 0x%02X for typeID 0x%02X", dest, type_id);
|
||||
|
||||
uint8_t message_data = 0xFF;
|
||||
if (length > 0 && length < message_data) {
|
||||
message_data = length;
|
||||
}
|
||||
add(Telegram::Operation::TX_READ, dest, type_id, offset, &message_data, 1, 0, front);
|
||||
}
|
||||
|
||||
// Send a raw telegram to the bus, telegram is a text string of hex values
|
||||
bool TxService::send_raw(const char * telegram_data) {
|
||||
if (telegram_data == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// since the telegram data is a const, make a copy. add 1 to grab the \0 EOS
|
||||
char telegram[strlen(telegram_data) + 1];
|
||||
strlcpy(telegram, telegram_data, sizeof(telegram));
|
||||
|
||||
uint8_t count = 0;
|
||||
uint8_t data[2 + strlen(telegram) / 3];
|
||||
|
||||
// get values
|
||||
char * p = strtok(telegram, " ,"); // delimiter
|
||||
while (p != nullptr) {
|
||||
data[count++] = (uint8_t)strtol(p, 0, 16);
|
||||
p = strtok(nullptr, " ,");
|
||||
}
|
||||
|
||||
// check valid length
|
||||
if (count < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
add(Telegram::Operation::TX_RAW, data, count, 0, true); // add to top/front of Tx queue
|
||||
return true;
|
||||
}
|
||||
|
||||
// add last Tx to tx queue and increment count
|
||||
// returns retry count, or 0 if all done
|
||||
void TxService::retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length) {
|
||||
// have we reached the limit? if so, reset count and give up
|
||||
if (++retry_count_ > MAXIMUM_TX_RETRIES) {
|
||||
reset_retry_count(); // give up
|
||||
EMSESP::wait_validate(0); // do not wait for validation
|
||||
if (operation == Telegram::Operation::TX_READ) {
|
||||
if (telegram_last_->offset > 0) { // ignore errors for higher offsets
|
||||
LOG_DEBUG("Last Tx Read operation failed after %d retries. Ignoring request: %s", MAXIMUM_TX_RETRIES, telegram_last_->to_string().c_str());
|
||||
if (EMSESP::response_id()) {
|
||||
EMSESP::set_response_id(0);
|
||||
EMSESP::set_read_id(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
increment_telegram_read_fail_count(); // another Tx fail
|
||||
} else {
|
||||
increment_telegram_write_fail_count(); // another Tx fail
|
||||
}
|
||||
|
||||
LOG_ERROR("Last Tx %s operation failed after %d retries. Ignoring request: %s",
|
||||
(operation == Telegram::Operation::TX_WRITE) ? "Write" : "Read",
|
||||
MAXIMUM_TX_RETRIES,
|
||||
telegram_last_->to_string().c_str());
|
||||
|
||||
if (operation == Telegram::Operation::TX_READ) {
|
||||
EMSESP::rxservice_.add_empty(telegram_last_->dest, telegram_last_->src, telegram_last_->type_id, telegram_last_->offset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Last Tx %s operation failed. Retry #%d. sent message: %s, received: %s",
|
||||
(operation == Telegram::Operation::TX_WRITE) ? ("Write") : ("Read"),
|
||||
retry_count_,
|
||||
telegram_last_->to_string().c_str(),
|
||||
Helpers::data_to_hex(data, length - 1).c_str());
|
||||
|
||||
// add to the top of the queue
|
||||
if (tx_telegrams_.size() >= MAX_TX_TELEGRAMS) {
|
||||
LOG_WARNING("Tx queue overflow, skip retry");
|
||||
reset_retry_count(); // give up
|
||||
EMSESP::wait_validate(0); // do not wait for validation
|
||||
return;
|
||||
}
|
||||
|
||||
tx_telegrams_.emplace_front(tx_telegram_id_++, std::move(telegram_last_), true, get_post_send_query());
|
||||
}
|
||||
|
||||
// send a request to read the next block of data from longer telegrams
|
||||
uint16_t TxService::read_next_tx(const uint8_t offset, const uint8_t length) {
|
||||
uint8_t old_length = telegram_last_->type_id > 0xFF ? length - 7 : length - 5;
|
||||
uint8_t max_length = telegram_last_->type_id > 0xFF ? EMS_MAX_TELEGRAM_MESSAGE_LENGTH - 2 : EMS_MAX_TELEGRAM_MESSAGE_LENGTH;
|
||||
uint8_t next_length = telegram_last_->message_data[0] > old_length ? telegram_last_->message_data[0] - old_length - offset + telegram_last_->offset : 0;
|
||||
uint8_t next_offset = offset + old_length;
|
||||
|
||||
// check telegram, length, offset and overflow
|
||||
// some telegrams only reply with one byte less, but have higher offsets (boiler 0x10)
|
||||
// some reply with higher offset than requests and have not_set values intermediate (boiler 0xEA)
|
||||
|
||||
// We have the last byte received
|
||||
if (offset + old_length >= telegram_last_->offset + telegram_last_->message_data[0]) {
|
||||
return 0;
|
||||
}
|
||||
// we request all and get a short telegram with requested offset
|
||||
if ((next_length + next_offset) == 0xFF && old_length < max_length - 1 && offset <= telegram_last_->offset) {
|
||||
return 0;
|
||||
}
|
||||
if (offset >= telegram_last_->offset && old_length > 0 && next_length > 0) {
|
||||
add(Telegram::Operation::TX_READ, telegram_last_->dest, telegram_last_->type_id, next_offset, &next_length, 1, 0, true);
|
||||
return telegram_last_->type_id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// checks if a telegram is sent to us matches the last Tx request
|
||||
// incoming Rx src must match the last Tx dest
|
||||
// and incoming Rx dest must be us (our ems_bus_id)
|
||||
// for both src and dest we strip the MSB 8th bit
|
||||
// returns true if the src/dest match the last Tx sent
|
||||
bool TxService::is_last_tx(const uint8_t src, const uint8_t dest) const {
|
||||
return (((telegram_last_->dest & 0x7F) == (src & 0x7F)) && ((dest & 0x7F) == ems_bus_id()));
|
||||
}
|
||||
|
||||
// sends a type_id read request to fetch values after a successful Tx write operation
|
||||
// unless the post_send_query has a type_id of 0
|
||||
uint16_t TxService::post_send_query() {
|
||||
uint16_t post_typeid = this->get_post_send_query();
|
||||
|
||||
if (post_typeid) {
|
||||
uint8_t dest = (this->telegram_last_->dest & 0x7F);
|
||||
// when set a value with large offset before and validate on same type and offset, or complete telegram
|
||||
uint8_t length = (this->telegram_last_->type_id == post_typeid) ? this->telegram_last_->message_length : 0xFF;
|
||||
uint8_t offset = (this->telegram_last_->type_id == post_typeid) ? this->telegram_last_->offset : 0;
|
||||
this->add(Telegram::Operation::TX_READ, dest, post_typeid, offset, &length, 1, 0, true); // add to top/front of queue
|
||||
// read_request(telegram_last_post_send_query_, dest, 0); // no offset
|
||||
LOG_DEBUG("Sending post validate read, type ID 0x%02X to dest 0x%02X", post_typeid, dest);
|
||||
set_post_send_query(0); // reset
|
||||
// delay the request if we have a different type_id for post_send_query
|
||||
delayed_send_ = (this->telegram_last_->type_id == post_typeid) ? 0 : (uuid::get_uptime() + POST_SEND_DELAY);
|
||||
}
|
||||
|
||||
return post_typeid;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
458
src/core/telegram.h
Normal file
458
src/core/telegram.h
Normal file
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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_TELEGRAM_H
|
||||
#define EMSESP_TELEGRAM_H
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <uuid/log.h>
|
||||
|
||||
// UART drivers
|
||||
#if defined(ESP32)
|
||||
#include "uart/emsuart_esp32.h"
|
||||
#elif defined(EMSESP_STANDALONE)
|
||||
#include "emsuart_standalone.h"
|
||||
#endif
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
#if defined(EMSESP_STANDALONE)
|
||||
#define MAX_RX_TELEGRAMS 100 // size of Rx queue
|
||||
#define MAX_TX_TELEGRAMS 200 // size of Tx queue
|
||||
#else
|
||||
#define MAX_RX_TELEGRAMS 10 // size of Rx queue
|
||||
#define MAX_TX_TELEGRAMS 100 // size of Tx queue
|
||||
#endif
|
||||
|
||||
// default values for null values
|
||||
static constexpr uint8_t EMS_VALUE_BOOL = 0xFF; // used to mark that something is a boolean
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_OFF = 0x00; // boolean false
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_ON = 0x01; // boolean true. True can be 0x01 or 0xFF sometimes
|
||||
static constexpr uint8_t EMS_VALUE_BOOL_NOTSET = 0xFE; // random number for booleans, that's not 0, 1 or FF
|
||||
static constexpr uint8_t EMS_VALUE_UINT8_NOTSET = 0xFF; // for 8-bit unsigned ints/bytes
|
||||
static constexpr int8_t EMS_VALUE_INT8_NOTSET = 0x7F; // for signed 8-bit ints/bytes
|
||||
static constexpr uint16_t EMS_VALUE_UINT16_NOTSET = 0x7D00; // 32000: for 2-byte unsigned shorts
|
||||
static constexpr int16_t EMS_VALUE_INT16_NOTSET = 0x7D00; // 32000: for 2-byte signed shorts
|
||||
static constexpr uint32_t EMS_VALUE_UINT24_NOTSET = 0x00FFFFFF; // for 3-byte longs
|
||||
static constexpr uint32_t EMS_VALUE_UINT32_NOTSET = 0xFFFFFF00; // for 4-byte longs
|
||||
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_LENGTH = 32; // max length of a complete EMS telegram
|
||||
static constexpr uint8_t EMS_MAX_TELEGRAM_MESSAGE_LENGTH = 27; // max length of message block, assuming EMS1.0
|
||||
|
||||
#define EMS_VALUE_DEFAULT_INT8 EMS_VALUE_INT8_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_UINT8 EMS_VALUE_UINT8_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_INT16 EMS_VALUE_INT16_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_UINT16 EMS_VALUE_UINT16_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_UINT24 EMS_VALUE_UINT24_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_UIN32 EMS_VALUE_UINT32_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_BOOL EMS_VALUE_BOOL_NOTSET
|
||||
#define EMS_VALUE_DEFAULT_ENUM EMS_VALUE_UINT8_NOTSET
|
||||
|
||||
// used when System::test_set_all_active() is set
|
||||
#define EMS_VALUE_DEFAULT_INT8_DUMMY 11
|
||||
#define EMS_VALUE_DEFAULT_UINT8_DUMMY -12
|
||||
#define EMS_VALUE_DEFAULT_INT16_DUMMY -1234
|
||||
#define EMS_VALUE_DEFAULT_UINT16_DUMMY 1235
|
||||
#define EMS_VALUE_DEFAULT_UINT24_DUMMY 12456
|
||||
#define EMS_VALUE_DEFAULT_UINT32_DUMMY 124567
|
||||
#define EMS_VALUE_DEFAULT_BOOL_DUMMY 1
|
||||
#define EMS_VALUE_DEFAULT_ENUM_DUMMY 1
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class Telegram {
|
||||
public:
|
||||
Telegram(const uint8_t operation,
|
||||
const uint8_t src,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
const uint8_t * message_data,
|
||||
const uint8_t message_length);
|
||||
~Telegram() = default;
|
||||
|
||||
const uint8_t operation; // is Operation mode
|
||||
const uint8_t src; // device_id
|
||||
const uint8_t dest; // device_id
|
||||
const uint16_t type_id;
|
||||
const uint8_t offset;
|
||||
const uint8_t message_length;
|
||||
uint8_t message_data[EMS_MAX_TELEGRAM_MESSAGE_LENGTH];
|
||||
|
||||
enum Operation : uint8_t {
|
||||
NONE = 0,
|
||||
RX,
|
||||
RX_READ,
|
||||
TX_RAW,
|
||||
TX_READ,
|
||||
TX_WRITE,
|
||||
};
|
||||
|
||||
std::string to_string_message() const;
|
||||
std::string to_string() const;
|
||||
|
||||
// reads a bit value from a given telegram position
|
||||
bool read_bitvalue(uint8_t & value, const uint8_t index, const uint8_t bit) const {
|
||||
uint8_t abs_index = (index - this->offset);
|
||||
if ((abs_index >= this->message_length) || (abs_index > EMS_MAX_TELEGRAM_MESSAGE_LENGTH)) {
|
||||
return false; // out of bounds
|
||||
}
|
||||
|
||||
uint8_t val = value;
|
||||
value = (uint8_t)(((this->message_data[abs_index]) >> bit) & 0x01);
|
||||
return (val != value);
|
||||
}
|
||||
|
||||
// read a value from a telegram if its not out of bounds.
|
||||
// Then we update the value, regardless if its garbage
|
||||
template <typename Value>
|
||||
// assuming negative numbers are stored as 2's-complement
|
||||
// https://medium.com/@LeeJulija/how-integers-are-stored-in-memory-using-twos-complement-5ba04d61a56c
|
||||
// 2-compliment : https://www.rapidtables.com/convert/number/decimal-to-hex.html
|
||||
// https://en.wikipedia.org/wiki/Two%27s_complement
|
||||
// s is to override number of bytes read (e.g. use 3 to simulate a uint24_t)
|
||||
bool read_value(Value & value, const uint8_t index, uint8_t s = 0) const {
|
||||
uint8_t num_bytes = (!s) ? sizeof(Value) : s;
|
||||
// check for out of bounds, if so don't modify the value
|
||||
auto msg_size = (index - this->offset + num_bytes - 1);
|
||||
|
||||
if ((index < this->offset) || (msg_size >= this->message_length) || (msg_size > EMS_MAX_TELEGRAM_MESSAGE_LENGTH)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Value val = value;
|
||||
value = 0;
|
||||
for (uint8_t i = 0; i < num_bytes; i++) {
|
||||
value = (value << 8) + this->message_data[index - this->offset + i]; // shift by byte
|
||||
}
|
||||
|
||||
return (val != value);
|
||||
}
|
||||
|
||||
bool read_enumvalue(uint8_t & value, const uint8_t index, int8_t start = 0) const {
|
||||
if ((index < this->offset) || ((index - this->offset) >= this->message_length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t val = value;
|
||||
value = this->message_data[index - this->offset] - start;
|
||||
return (val != value);
|
||||
}
|
||||
|
||||
private:
|
||||
int8_t _getDataPosition(const uint8_t index, const uint8_t size) const;
|
||||
};
|
||||
|
||||
class EMSbus {
|
||||
public:
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
static constexpr uint8_t EMS_MASK_UNSET = 0xFF; // EMS bus type (budrus/junkers) hasn't been detected yet
|
||||
static constexpr uint8_t EMS_MASK_HT3 = 0x80; // EMS bus type Junkers/HT3
|
||||
static constexpr uint8_t EMS_MASK_BUDERUS = 0xFF; // EMS bus type Buderus
|
||||
static constexpr uint8_t EMS_TX_ERROR_LIMIT = 10; // % limit of failed Tx read/write attempts before showing a warning
|
||||
|
||||
static bool is_ht3() {
|
||||
return (ems_mask_ == EMS_MASK_HT3);
|
||||
}
|
||||
|
||||
static uint8_t ems_mask() {
|
||||
return ems_mask_;
|
||||
}
|
||||
|
||||
static void ems_mask(uint8_t ems_mask) {
|
||||
ems_mask_ = ems_mask & 0x80; // only keep the MSB (8th bit)
|
||||
}
|
||||
|
||||
static uint8_t tx_mode() {
|
||||
return tx_mode_;
|
||||
}
|
||||
|
||||
static void tx_mode(uint8_t tx_mode) {
|
||||
tx_mode_ = tx_mode;
|
||||
}
|
||||
|
||||
static uint8_t ems_bus_id() {
|
||||
return ems_bus_id_;
|
||||
}
|
||||
|
||||
static void ems_bus_id(uint8_t ems_bus_id) {
|
||||
ems_bus_id_ = ems_bus_id;
|
||||
}
|
||||
|
||||
// checks every 30 seconds if the EMS bus is still alive
|
||||
static bool bus_connected() {
|
||||
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
|
||||
return true;
|
||||
#else
|
||||
if ((uuid::get_uptime() - last_bus_activity_) > EMS_BUS_TIMEOUT) {
|
||||
bus_connected_ = false;
|
||||
}
|
||||
return bus_connected_;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
return tx_state_;
|
||||
}
|
||||
static void tx_state(uint8_t tx_state) {
|
||||
tx_state_ = tx_state;
|
||||
}
|
||||
|
||||
static uint8_t calculate_crc(const uint8_t * data, const uint8_t length);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t EMS_BUS_TIMEOUT = 30000; // timeout in ms before recognizing the ems bus is offline (30 seconds)
|
||||
|
||||
static uint32_t last_bus_activity_; // timestamp of last time a valid Rx came in
|
||||
static uint32_t bus_uptime_start_; // timestamp of first time we connected to the bus
|
||||
static bool bus_connected_; // start assuming the bus hasn't been connected
|
||||
static uint8_t ems_mask_; // unset=0xFF, buderus=0x00, junkers/ht3=0x80
|
||||
static uint8_t ems_bus_id_; // the bus id, which configurable and stored in settings
|
||||
static uint8_t tx_mode_; // local copy of the tx mode
|
||||
static uint8_t tx_state_; // state of the Tx line (NONE or waiting on a TX_READ or TX_WRITE)
|
||||
};
|
||||
|
||||
class RxService : public EMSbus {
|
||||
public:
|
||||
RxService() = default;
|
||||
~RxService() = default;
|
||||
|
||||
void loop();
|
||||
void add(uint8_t * data, uint8_t length);
|
||||
void add_empty(const uint8_t src, const uint8_t dst, const uint16_t type_id, uint8_t offset);
|
||||
|
||||
uint32_t telegram_count() const {
|
||||
return telegram_count_;
|
||||
}
|
||||
|
||||
void increment_telegram_count() {
|
||||
telegram_count_++;
|
||||
}
|
||||
|
||||
uint32_t telegram_error_count() const {
|
||||
return telegram_error_count_;
|
||||
}
|
||||
|
||||
// returns a %
|
||||
uint8_t quality() const {
|
||||
if (telegram_error_count_ == 0) {
|
||||
return 100; // all good, 100%
|
||||
}
|
||||
uint8_t q = (telegram_error_count_ * 100 / (telegram_count_ + telegram_error_count_));
|
||||
return (q <= EMS_BUS_QUALITY_RX_THRESHOLD ? 100 : 100 - q);
|
||||
}
|
||||
|
||||
struct QueuedRxTelegram {
|
||||
public:
|
||||
const uint16_t id_;
|
||||
const std::shared_ptr<const Telegram> telegram_;
|
||||
|
||||
~QueuedRxTelegram() = default;
|
||||
// removed && from telegram in 3.7.0-dev.43
|
||||
QueuedRxTelegram(uint16_t id, std::shared_ptr<Telegram> telegram)
|
||||
: id_(id)
|
||||
, telegram_(std::move(telegram)) {
|
||||
}
|
||||
};
|
||||
|
||||
std::deque<QueuedRxTelegram> queue() const {
|
||||
return rx_telegrams_;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr uint8_t EMS_BUS_QUALITY_RX_THRESHOLD = 5; // % threshold before reporting quality issues
|
||||
|
||||
uint8_t rx_telegram_id_ = 0; // queue counter
|
||||
uint32_t telegram_count_ = 0; // # Rx received
|
||||
uint32_t telegram_error_count_ = 0; // # Rx CRC errors
|
||||
std::shared_ptr<const Telegram> rx_telegram; // the incoming Rx telegram
|
||||
std::deque<QueuedRxTelegram> rx_telegrams_; // the Rx Queue
|
||||
};
|
||||
|
||||
class TxService : public EMSbus {
|
||||
public:
|
||||
static constexpr uint8_t TX_WRITE_FAIL = 4; // EMS return code for fail
|
||||
static constexpr uint8_t TX_WRITE_SUCCESS = 1; // EMS return code for success
|
||||
|
||||
TxService() = default;
|
||||
~TxService() = default;
|
||||
|
||||
void start();
|
||||
void send();
|
||||
uint8_t get_send_id();
|
||||
void add(const uint8_t operation,
|
||||
const uint8_t dest,
|
||||
const uint16_t type_id,
|
||||
const uint8_t offset,
|
||||
uint8_t * message_data,
|
||||
const uint8_t message_length,
|
||||
const uint16_t validateid,
|
||||
const bool front = false);
|
||||
void add(const uint8_t operation, const uint8_t * data, const uint8_t length, const uint16_t validateid, const bool front = false);
|
||||
void read_request(const uint16_t type_id, const uint8_t dest, const uint8_t offset = 0, const uint8_t length = 0, const bool readId = false);
|
||||
bool send_raw(const char * telegram_data);
|
||||
void send_poll() const;
|
||||
void retry_tx(const uint8_t operation, const uint8_t * data, const uint8_t length);
|
||||
bool is_last_tx(const uint8_t src, const uint8_t dest) const;
|
||||
uint16_t post_send_query();
|
||||
uint16_t read_next_tx(const uint8_t offset, const uint8_t length);
|
||||
|
||||
uint8_t retry_count() const {
|
||||
return retry_count_;
|
||||
}
|
||||
|
||||
void reset_retry_count() {
|
||||
retry_count_ = 0;
|
||||
}
|
||||
|
||||
void set_post_send_query(uint16_t type_id) {
|
||||
telegram_last_post_send_query_ = type_id;
|
||||
}
|
||||
|
||||
uint16_t get_post_send_query() const {
|
||||
return telegram_last_post_send_query_;
|
||||
}
|
||||
|
||||
uint32_t telegram_read_count() const {
|
||||
return telegram_read_count_;
|
||||
}
|
||||
uint32_t telegram_write_count() const {
|
||||
return telegram_write_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_++;
|
||||
}
|
||||
|
||||
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_;
|
||||
const bool retry_; // true if its a retry
|
||||
const uint16_t validateid_;
|
||||
|
||||
~QueuedTxTelegram() = default;
|
||||
// replaced && im std::shared_ptr<Telegram> telegram in 3.7.0-dev.43
|
||||
QueuedTxTelegram(uint16_t id, std::shared_ptr<Telegram> telegram, bool retry, uint16_t validateid)
|
||||
: id_(id)
|
||||
, telegram_(std::move(telegram))
|
||||
, retry_(retry)
|
||||
, validateid_(validateid) {
|
||||
}
|
||||
};
|
||||
|
||||
std::deque<QueuedTxTelegram> queue() const {
|
||||
return tx_telegrams_;
|
||||
}
|
||||
|
||||
bool tx_queue_empty() const {
|
||||
return tx_telegrams_.empty();
|
||||
}
|
||||
|
||||
static constexpr uint8_t MAXIMUM_TX_RETRIES = 3;
|
||||
static constexpr uint32_t POST_SEND_DELAY = 2000;
|
||||
|
||||
private:
|
||||
std::deque<QueuedTxTelegram> tx_telegrams_; // the Tx queue
|
||||
|
||||
uint32_t telegram_read_count_ = 0; // # Tx successful reads
|
||||
uint32_t telegram_write_count_ = 0; // # Tx successful writes
|
||||
uint32_t telegram_read_fail_count_ = 0; // # Tx unsuccessful transmits
|
||||
uint32_t telegram_write_fail_count_ = 0; // # Tx unsuccessful transmits
|
||||
|
||||
std::shared_ptr<Telegram> telegram_last_;
|
||||
uint16_t telegram_last_post_send_query_; // which type ID to query after a successful send, to read back the values just written
|
||||
uint8_t retry_count_ = 0; // count for # Tx retries
|
||||
uint32_t delayed_send_ = 0; // manage delay for post send query
|
||||
|
||||
uint8_t tx_telegram_id_ = 0; // queue counter
|
||||
|
||||
void send_telegram(const QueuedTxTelegram & tx_telegram);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
613
src/core/temperaturesensor.cpp
Normal file
613
src/core/temperaturesensor.cpp
Normal file
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// code originally written by nomis - https://github.com/nomis
|
||||
|
||||
#include "temperaturesensor.h"
|
||||
#include "emsesp.h"
|
||||
|
||||
#ifdef ESP32
|
||||
#define YIELD
|
||||
#else
|
||||
#define YIELD yield()
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger TemperatureSensor::logger_{F_(temperaturesensor), uuid::log::Facility::DAEMON};
|
||||
|
||||
// start the 1-wire
|
||||
void TemperatureSensor::start() {
|
||||
reload();
|
||||
|
||||
if (!dallas_gpio_) {
|
||||
sensors_.clear();
|
||||
return; // disabled if dallas gpio is 0
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
bus_.begin(dallas_gpio_);
|
||||
LOG_INFO("Starting Temperature Sensor service");
|
||||
#endif
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "%s/#", F_(temperaturesensor));
|
||||
Mqtt::subscribe(EMSdevice::DeviceType::TEMPERATURESENSOR, topic, nullptr); // use empty function callback
|
||||
}
|
||||
|
||||
// load settings
|
||||
void TemperatureSensor::reload() {
|
||||
// load the service settings
|
||||
EMSESP::webSettingsService.read([&](WebSettings const & settings) {
|
||||
dallas_gpio_ = settings.dallas_gpio;
|
||||
parasite_ = settings.dallas_parasite;
|
||||
});
|
||||
|
||||
for (auto & sensor : sensors_) {
|
||||
remove_ha_topic(sensor.id());
|
||||
sensor.ha_registered = false; // force HA configs to be re-created
|
||||
}
|
||||
}
|
||||
|
||||
void TemperatureSensor::loop() {
|
||||
if (!dallas_gpio_) {
|
||||
return; // dallas gpio is 0 (disabled)
|
||||
}
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
uint32_t time_now = uuid::get_uptime();
|
||||
|
||||
if (state_ == State::IDLE) {
|
||||
if (time_now - last_activity_ >= READ_INTERVAL_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_DEBUG("Read sensor temperature");
|
||||
#endif
|
||||
if (bus_.reset() || parasite_) {
|
||||
YIELD;
|
||||
bus_.skip();
|
||||
bus_.write(CMD_CONVERT_TEMP, parasite_ ? 1 : 0);
|
||||
state_ = State::READING;
|
||||
scanretry_ = 0;
|
||||
} else {
|
||||
// no sensors found
|
||||
if (sensors_.size()) {
|
||||
sensorfails_++;
|
||||
if (++scanretry_ > SCAN_MAX) { // every 30 sec
|
||||
scanretry_ = 0;
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_DEBUG("Error: Bus reset failed");
|
||||
#endif
|
||||
#ifndef EMSESP_TEST
|
||||
// don't reset if running in test mode where we simulate sensors
|
||||
for (auto & sensor : sensors_) {
|
||||
sensor.temperature_c = EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
last_activity_ = time_now;
|
||||
}
|
||||
} else if (state_ == State::READING) {
|
||||
if (temperature_convert_complete() && (time_now - last_activity_ > CONVERSION_MS)) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_DEBUG("Scanning for temperature sensors");
|
||||
#endif
|
||||
bus_.reset_search();
|
||||
state_ = State::SCANNING;
|
||||
} else if (time_now - last_activity_ > READ_TIMEOUT_MS) {
|
||||
#ifdef EMSESP_DEBUG_SENSOR
|
||||
LOG_WARNING("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("Sensor scan timeout");
|
||||
#endif
|
||||
state_ = State::IDLE;
|
||||
sensorfails_++;
|
||||
} else {
|
||||
uint8_t addr[ADDR_LEN] = {0};
|
||||
|
||||
if (bus_.search(addr)) {
|
||||
if (!parasite_) {
|
||||
bus_.depower();
|
||||
}
|
||||
if (bus_.crc8(addr, ADDR_LEN - 1) == addr[ADDR_LEN - 1]) {
|
||||
switch (addr[0]) {
|
||||
case TYPE_DS18B20:
|
||||
case TYPE_DS18S20:
|
||||
case TYPE_DS1822:
|
||||
case TYPE_DS1825:
|
||||
int16_t t;
|
||||
t = get_temperature_c(addr);
|
||||
if ((t >= -550) && (t <= 1250)) {
|
||||
sensorreads_++;
|
||||
// check if we already have this sensor
|
||||
bool found = false;
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.internal_id() == get_id(addr)) {
|
||||
t += sensor.offset();
|
||||
if (t != sensor.temperature_c) {
|
||||
sensor.temperature_c = t;
|
||||
publish_sensor(sensor);
|
||||
changed_ |= true;
|
||||
}
|
||||
sensor.read = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 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().read = true;
|
||||
changed_ = true;
|
||||
// look in the customization service for an optional alias or offset for that particular sensor
|
||||
sensors_.back().apply_customization();
|
||||
sensors_.back().temperature_c = t + sensors_.back().offset();
|
||||
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_++;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
sensorfails_++;
|
||||
LOG_ERROR("Unknown sensor %s", Sensor(addr).id().c_str());
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sensorfails_++;
|
||||
LOG_ERROR("Invalid sensor %s", Sensor(addr).id().c_str());
|
||||
}
|
||||
} else {
|
||||
if (!parasite_) {
|
||||
bus_.depower();
|
||||
}
|
||||
// check for missing sensors after some samples
|
||||
// but don't do this if running in test mode where we simulate sensors
|
||||
if (++scancnt_ > SCAN_MAX) {
|
||||
for (auto & sensor : sensors_) {
|
||||
if (!sensor.read) {
|
||||
sensor.temperature_c = EMS_VALUE_INT16_NOTSET;
|
||||
changed_ = true;
|
||||
}
|
||||
sensor.read = false;
|
||||
}
|
||||
scancnt_ = 0;
|
||||
} else if (scancnt_ == SCAN_START + 1) { // startup
|
||||
firstscan_ = sensors_.size();
|
||||
// LOG_DEBUG("Adding %d sensor(s) from first scan", firstscan_);
|
||||
} else if ((scancnt_ <= 0) && (firstscan_ != sensors_.size())) { // check 2 times for no change of sensor #
|
||||
scancnt_ = SCAN_START;
|
||||
sensors_.clear(); // restart scanning and clear to get correct numbering
|
||||
}
|
||||
state_ = State::IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TemperatureSensor::temperature_convert_complete() {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (parasite_) {
|
||||
return true; // don't care, use the minimum time in loop
|
||||
}
|
||||
return bus_.read_bit() == 1;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
int16_t TemperatureSensor::get_temperature_c(const uint8_t addr[]) {
|
||||
#ifndef EMSESP_STANDALONE
|
||||
if (!bus_.reset()) {
|
||||
LOG_ERROR("Bus reset failed before reading scratchpad from %s", Sensor(addr).id().c_str());
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
YIELD;
|
||||
|
||||
uint8_t scratchpad[SCRATCHPAD_LEN] = {0};
|
||||
bus_.select(addr);
|
||||
bus_.write(CMD_READ_SCRATCHPAD);
|
||||
bus_.read_bytes(scratchpad, SCRATCHPAD_LEN);
|
||||
YIELD;
|
||||
|
||||
if (!bus_.reset()) {
|
||||
LOG_ERROR("Bus reset failed after reading scratchpad from %s", Sensor(addr).id().c_str());
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
YIELD;
|
||||
|
||||
if (bus_.crc8(scratchpad, SCRATCHPAD_LEN - 1) != scratchpad[SCRATCHPAD_LEN - 1]) {
|
||||
LOG_WARNING("Invalid scratchpad CRC: %02X%02X%02X%02X%02X%02X%02X%02X%02X from sensor %s",
|
||||
scratchpad[0],
|
||||
scratchpad[1],
|
||||
scratchpad[2],
|
||||
scratchpad[3],
|
||||
scratchpad[4],
|
||||
scratchpad[5],
|
||||
scratchpad[6],
|
||||
scratchpad[7],
|
||||
scratchpad[8],
|
||||
Sensor(addr).id().c_str());
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
}
|
||||
|
||||
int16_t raw_value = ((int16_t)scratchpad[SCRATCHPAD_TEMP_MSB] << 8) | scratchpad[SCRATCHPAD_TEMP_LSB];
|
||||
|
||||
if (addr[0] == TYPE_DS18S20) {
|
||||
raw_value = (raw_value << 3) + 12 - scratchpad[SCRATCHPAD_CNT_REM];
|
||||
} else {
|
||||
// Adjust based on sensor resolution
|
||||
int resolution = 9 + ((scratchpad[SCRATCHPAD_CONFIG] >> 5) & 0x3);
|
||||
switch (resolution) {
|
||||
case 9:
|
||||
raw_value &= ~0x7;
|
||||
break;
|
||||
case 10:
|
||||
raw_value &= ~0x3;
|
||||
break;
|
||||
case 11:
|
||||
raw_value &= ~0x1;
|
||||
break;
|
||||
default: // 12
|
||||
break;
|
||||
}
|
||||
}
|
||||
raw_value = ((int32_t)raw_value * 625 + 500) / 1000; // round to 0.1
|
||||
return raw_value;
|
||||
#else
|
||||
return EMS_VALUE_INT16_NOTSET;
|
||||
#endif
|
||||
}
|
||||
|
||||
// update temperature sensor information name and offset
|
||||
bool TemperatureSensor::update(const std::string & id, const std::string & name, int16_t offset) {
|
||||
// find the sensor
|
||||
for (auto & sensor : sensors_) {
|
||||
if (sensor.id() == id) {
|
||||
// found a match, update the sensor object
|
||||
|
||||
// if HA is enabled then delete the old record
|
||||
if (Mqtt::ha_enabled()) {
|
||||
remove_ha_topic(id);
|
||||
sensor.ha_registered = false; // force HA configs to be re-created
|
||||
}
|
||||
|
||||
sensor.set_name(name);
|
||||
sensor.set_offset(offset);
|
||||
|
||||
// store the new name and offset in our configuration
|
||||
EMSESP::webCustomizationService.update([&id, &name, &offset, &sensor](WebCustomization & settings) {
|
||||
// look it up to see if it exists
|
||||
bool found = false;
|
||||
for (auto & SensorCustomization : settings.sensorCustomizations) {
|
||||
if (SensorCustomization.id == id) {
|
||||
SensorCustomization.name = name;
|
||||
SensorCustomization.offset = offset;
|
||||
found = true;
|
||||
LOG_DEBUG("Customizing existing sensor ID %s", id.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
auto newSensor = SensorCustomization();
|
||||
newSensor.id = id;
|
||||
newSensor.name = name;
|
||||
newSensor.offset = offset;
|
||||
settings.sensorCustomizations.push_back(newSensor);
|
||||
LOG_DEBUG("Adding new customization for sensor ID %s", id.c_str());
|
||||
}
|
||||
sensor.ha_registered = false; // it's changed so we may need to recreate the HA config
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true; // not found, nothing updated
|
||||
}
|
||||
|
||||
// check to see if values have been updated
|
||||
bool TemperatureSensor::updated_values() {
|
||||
if (changed_) {
|
||||
changed_ = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// called from emsesp.cpp for commands
|
||||
bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, const int8_t id) {
|
||||
// return empty json if there are no sensors
|
||||
if (sensors_.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(info)) || !strcmp(cmd, F_(values))) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
char val[10];
|
||||
output[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(entities))) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
get_value_json(output[sensor.name()].to<JsonObject>(), sensor);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is for a specific sensor
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
|
||||
for (const auto & sensor : sensors_) {
|
||||
// match custom name or sensor ID
|
||||
if (cmd == Helpers::toLower(sensor.name()) || cmd == Helpers::toLower(sensor.id())) {
|
||||
get_value_json(output, sensor);
|
||||
return Command::set_attribute(output, cmd, attribute_s);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
void TemperatureSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||
output["id"] = sensor.id();
|
||||
output["name"] = sensor.name();
|
||||
output["fullname"] = sensor.name();
|
||||
if (Helpers::hasValue(sensor.temperature_c)) {
|
||||
char val[10];
|
||||
output["value"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
|
||||
output["type"] = F_(number);
|
||||
output["uom"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
output["readable"] = true;
|
||||
output["writeable"] = false;
|
||||
output["visible"] = true;
|
||||
}
|
||||
|
||||
|
||||
// publish a single sensor to MQTT
|
||||
void TemperatureSensor::publish_sensor(const Sensor & sensor) {
|
||||
if (Mqtt::enabled() && Mqtt::publish_single()) {
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
if (Mqtt::publish_single2cmd()) {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", F_(temperaturesensor), sensor.name().c_str());
|
||||
} else {
|
||||
snprintf(topic, sizeof(topic), "%s%s/%s", F_(temperaturesensor), "_data", sensor.name().c_str());
|
||||
}
|
||||
char payload[10];
|
||||
Mqtt::queue_publish(topic, Helpers::render_value(payload, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
char cmd[COMMAND_MAX_LENGTH];
|
||||
snprintf(cmd, sizeof(cmd), "%s/%s", F_(temperaturesensor), sensor.name().c_str());
|
||||
EMSESP::webSchedulerService.onChange(cmd);
|
||||
}
|
||||
|
||||
// send empty config topic to remove the entry from HA
|
||||
void TemperatureSensor::remove_ha_topic(const std::string & id) {
|
||||
if (!Mqtt::ha_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Removing HA config for temperature sensor ID %s", id.c_str());
|
||||
// use '_' as HA doesn't like '-' in the topic name
|
||||
std::string sensorid = id;
|
||||
std::replace(sensorid.begin(), sensorid.end(), '-', '_');
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/temperaturesensor_%s/config", Mqtt::basename().c_str(), sensorid.c_str());
|
||||
Mqtt::queue_remove_topic(topic);
|
||||
}
|
||||
|
||||
// send all temperature sensor values as a JSON package to MQTT
|
||||
void TemperatureSensor::publish_values(const bool force) {
|
||||
if (!Mqtt::enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t num_sensors = sensors_.size();
|
||||
if (num_sensors == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force && Mqtt::publish_single()) {
|
||||
for (const auto & sensor : sensors_) {
|
||||
publish_sensor(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (auto & sensor : sensors_) {
|
||||
bool has_value = Helpers::hasValue(sensor.temperature_c);
|
||||
if (has_value) {
|
||||
char val[10];
|
||||
if (Mqtt::is_nested()) {
|
||||
JsonObject dataSensor = doc[sensor.id()].to<JsonObject>();
|
||||
dataSensor["name"] = sensor.name();
|
||||
dataSensor["temp"] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
} else {
|
||||
doc[sensor.name()] = serialized(Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
// create the HA MQTT config
|
||||
// to e.g. homeassistant/sensor/ems-esp/temperaturesensor_28-233D-9497-0C03/config
|
||||
if (Mqtt::ha_enabled()) {
|
||||
if (!has_value && sensor.ha_registered) {
|
||||
remove_ha_topic(sensor.id());
|
||||
sensor.ha_registered = false;
|
||||
} else if (!sensor.ha_registered || force) {
|
||||
LOG_DEBUG("Recreating HA config for sensor ID %s", sensor.id().c_str());
|
||||
|
||||
JsonDocument config;
|
||||
config["dev_cla"] = "temperature";
|
||||
config["stat_cla"] = "measurement";
|
||||
|
||||
char stat_t[50];
|
||||
snprintf(stat_t, sizeof(stat_t), "%s/%s_data", Mqtt::base().c_str(), F_(temperaturesensor)); // use base path
|
||||
config["stat_t"] = stat_t;
|
||||
|
||||
config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
|
||||
|
||||
char val_obj[70];
|
||||
char val_cond[170];
|
||||
if (Mqtt::is_nested()) {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']['temp']", sensor.id().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "value_json['%s'] is defined and %s is defined", sensor.id().c_str(), val_obj);
|
||||
} else {
|
||||
snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
|
||||
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
|
||||
}
|
||||
|
||||
// for the value template, there's a problem still with Domoticz probably due to the special characters.
|
||||
// See https://github.com/emsesp/EMS-ESP32/issues/1360
|
||||
if (Mqtt::discovery_type() == Mqtt::discoveryType::HOMEASSISTANT) {
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else -55}}";
|
||||
} else {
|
||||
config["val_tpl"] = (std::string) "{{" + val_obj + "}}"; // omit value conditional Jinja2 template code
|
||||
}
|
||||
|
||||
char uniq_s[70];
|
||||
if (Mqtt::entity_format() == Mqtt::entityFormat::MULTI_SHORT) {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "%s_%s_%s", Mqtt::basename().c_str(), F_(temperaturesensor), sensor.id().c_str());
|
||||
} else {
|
||||
snprintf(uniq_s, sizeof(uniq_s), "%s_%s", F_(temperaturesensor), sensor.id().c_str());
|
||||
}
|
||||
|
||||
config["obj_id"] = uniq_s;
|
||||
config["uniq_id"] = uniq_s; // same as object_id/obj_id
|
||||
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s", sensor.name().c_str());
|
||||
config["name"] = name;
|
||||
|
||||
// see if we need to create the [devs] discovery section, as this needs only to be done once for all sensors
|
||||
bool is_ha_device_created = false;
|
||||
for (const auto & sensor : sensors_) {
|
||||
if (sensor.ha_registered) {
|
||||
is_ha_device_created = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Mqtt::add_ha_sections_to_doc("temperature", stat_t, config, !is_ha_device_created, val_cond);
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(temperaturesensor), sensor.id().c_str());
|
||||
|
||||
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||
snprintf(topic, sizeof(topic), "%s_data", F_(temperaturesensor));
|
||||
Mqtt::queue_publish(topic, doc.as<JsonObject>());
|
||||
}
|
||||
|
||||
// skip crc from id
|
||||
TemperatureSensor::Sensor::Sensor(const uint8_t addr[])
|
||||
: internal_id_(((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16)
|
||||
| ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6])) {
|
||||
// create ID string
|
||||
char id_s[20];
|
||||
snprintf(id_s,
|
||||
sizeof(id_s),
|
||||
"%02X_%04X_%04X_%04X",
|
||||
(unsigned int)(internal_id_ >> 48) & 0xFF,
|
||||
(unsigned int)(internal_id_ >> 32) & 0xFFFF,
|
||||
(unsigned int)(internal_id_ >> 16) & 0xFFFF,
|
||||
(unsigned int)(internal_id_) & 0xFFFF);
|
||||
id_ = std::string(id_s);
|
||||
name_ = std::string{}; // name (alias) is empty
|
||||
offset_ = 0; // 0 degrees offset
|
||||
}
|
||||
|
||||
uint64_t TemperatureSensor::get_id(const uint8_t addr[]) {
|
||||
return (((uint64_t)addr[0] << 48) | ((uint64_t)addr[1] << 40) | ((uint64_t)addr[2] << 32) | ((uint64_t)addr[3] << 24) | ((uint64_t)addr[4] << 16)
|
||||
| ((uint64_t)addr[5] << 8) | ((uint64_t)addr[6]));
|
||||
}
|
||||
|
||||
// find the name from the customization service
|
||||
// if empty, return the ID as a string
|
||||
std::string TemperatureSensor::Sensor::name() const {
|
||||
if (name_.empty()) {
|
||||
return id_;
|
||||
}
|
||||
return name_;
|
||||
}
|
||||
|
||||
// look up in customization service for a specific sensor
|
||||
// and set the name and offset from that entry if it exists
|
||||
bool TemperatureSensor::Sensor::apply_customization() {
|
||||
EMSESP::webCustomizationService.read([&](const WebCustomization & settings) {
|
||||
auto const & sensors = settings.sensorCustomizations;
|
||||
if (!sensors.empty()) {
|
||||
for (const auto & sensor : sensors) {
|
||||
if (id_ == sensor.id) {
|
||||
LOG_DEBUG("Loading customization for temperature sensor %s", sensor.id.c_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
|
||||
#if defined(EMSESP_TEST)
|
||||
void TemperatureSensor::test() {
|
||||
// add 2 temperature sensors
|
||||
// Sensor ID: 01_0203_0405_0607
|
||||
uint8_t addr[ADDR_LEN] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
sensors_.emplace_back(addr);
|
||||
sensors_.back().apply_customization();
|
||||
sensors_.back().temperature_c = 123; // 12.3
|
||||
sensors_.back().read = true;
|
||||
publish_sensor(sensors_.back()); // call publish single
|
||||
|
||||
// Sensor ID: 0B_0C0D_0E0F_1011
|
||||
uint8_t addr2[ADDR_LEN] = {11, 12, 13, 14, 15, 16, 17, 18};
|
||||
sensors_.emplace_back(addr2);
|
||||
sensors_.back().apply_customization();
|
||||
sensors_.back().temperature_c = 456; // 45.6
|
||||
sensors_.back().read = true;
|
||||
publish_sensor(sensors_.back()); // call publish single
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace emsesp
|
||||
177
src/core/temperaturesensor.h
Normal file
177
src/core/temperaturesensor.h
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
||||
* Copyright 2020-2024 emsesp.org - proddy, MichaelDvP
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// code originally written by nomis - https://github.com/nomis
|
||||
|
||||
#ifndef EMSESP_TEMPERATURESENSOR_H
|
||||
#define EMSESP_TEMPERATURESENSOR_H
|
||||
|
||||
#include "helpers.h"
|
||||
#include "mqtt.h"
|
||||
#include "console.h"
|
||||
|
||||
#include <uuid/log.h>
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
#include <OneWire.h>
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
class TemperatureSensor {
|
||||
public:
|
||||
class Sensor {
|
||||
public:
|
||||
Sensor(const uint8_t addr[]);
|
||||
~Sensor() = default;
|
||||
|
||||
uint64_t internal_id() const {
|
||||
return internal_id_;
|
||||
}
|
||||
|
||||
std::string id() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// initial values
|
||||
int16_t temperature_c = EMS_VALUE_INT16_NOTSET; // value is *10
|
||||
bool read = false;
|
||||
bool ha_registered = false;
|
||||
|
||||
private:
|
||||
uint64_t internal_id_;
|
||||
std::string id_;
|
||||
std::string name_;
|
||||
int16_t offset_;
|
||||
};
|
||||
|
||||
TemperatureSensor() = default;
|
||||
~TemperatureSensor() = default;
|
||||
|
||||
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 = -1);
|
||||
|
||||
// return back reference to the sensor list, used by other classes
|
||||
std::vector<Sensor> sensors() const {
|
||||
return sensors_;
|
||||
}
|
||||
|
||||
uint32_t reads() const {
|
||||
return sensorreads_;
|
||||
}
|
||||
|
||||
uint32_t fails() const {
|
||||
return sensorfails_;
|
||||
}
|
||||
|
||||
bool sensor_enabled() const {
|
||||
return (dallas_gpio_ != 0);
|
||||
}
|
||||
|
||||
bool have_sensors() const {
|
||||
return (!sensors_.empty());
|
||||
}
|
||||
|
||||
size_t count_entities() const {
|
||||
return sensors_.size();
|
||||
}
|
||||
|
||||
bool update(const std::string & id, const std::string & name, int16_t offset);
|
||||
|
||||
#if defined(EMSESP_TEST)
|
||||
void test();
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint8_t MAX_SENSORS = 20;
|
||||
|
||||
enum class State { IDLE, READING, SCANNING };
|
||||
|
||||
static constexpr size_t ADDR_LEN = 8;
|
||||
|
||||
static constexpr size_t SCRATCHPAD_LEN = 9;
|
||||
static constexpr size_t SCRATCHPAD_TEMP_MSB = 1;
|
||||
static constexpr size_t SCRATCHPAD_TEMP_LSB = 0;
|
||||
static constexpr size_t SCRATCHPAD_CONFIG = 4;
|
||||
static constexpr size_t SCRATCHPAD_CNT_REM = 6;
|
||||
|
||||
// dallas chip types
|
||||
static constexpr uint8_t TYPE_DS18B20 = 0x28;
|
||||
static constexpr uint8_t TYPE_DS18S20 = 0x10;
|
||||
static constexpr uint8_t TYPE_DS1822 = 0x22;
|
||||
static constexpr uint8_t TYPE_DS1825 = 0x3B; // also DS1826
|
||||
|
||||
static constexpr uint32_t READ_INTERVAL_MS = 5000; // 5 seconds
|
||||
static constexpr uint32_t CONVERSION_MS = 1000; // 1 seconds
|
||||
static constexpr uint32_t READ_TIMEOUT_MS = 2000; // 2 seconds
|
||||
static constexpr uint32_t SCAN_TIMEOUT_MS = 3000; // 3 seconds
|
||||
|
||||
static constexpr uint8_t CMD_CONVERT_TEMP = 0x44;
|
||||
static constexpr uint8_t CMD_READ_SCRATCHPAD = 0xBE;
|
||||
|
||||
static constexpr int8_t SCAN_START = -3;
|
||||
static constexpr int8_t SCAN_MAX = 5;
|
||||
|
||||
static uuid::log::Logger logger_;
|
||||
|
||||
bool temperature_convert_complete();
|
||||
int16_t get_temperature_c(const uint8_t addr[]);
|
||||
uint64_t get_id(const uint8_t addr[]);
|
||||
void get_value_json(JsonObject output, const Sensor & sensor);
|
||||
void remove_ha_topic(const std::string & id);
|
||||
|
||||
std::vector<Sensor> sensors_; // our list of active sensors
|
||||
|
||||
#ifndef EMSESP_STANDALONE
|
||||
OneWire bus_;
|
||||
uint32_t last_activity_ = uuid::get_uptime();
|
||||
State state_ = State::IDLE;
|
||||
int8_t scancnt_ = SCAN_START;
|
||||
uint8_t firstscan_ = 0;
|
||||
int8_t scanretry_ = 0;
|
||||
#endif
|
||||
|
||||
uint8_t dallas_gpio_ = 0;
|
||||
bool parasite_ = false;
|
||||
bool changed_ = false;
|
||||
uint32_t sensorfails_ = 0;
|
||||
uint32_t sensorreads_ = 0;
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user