mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-03-17 07:06:31 +03:00
Compare commits
19 Commits
b320d8ded2
...
latest
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34a2b20be8 | ||
|
|
f1fc8d9aae | ||
|
|
b04355e3e1 | ||
|
|
cd3ae5cdf2 | ||
|
|
a261ca23af | ||
|
|
cb96904a5c | ||
|
|
4a2d78f8e1 | ||
|
|
f5af4fb52f | ||
|
|
2037bc3a10 | ||
|
|
64d17d7c65 | ||
|
|
92e2633342 | ||
|
|
96a7ea8a02 | ||
|
|
5c4aaa4510 | ||
|
|
c05e1cb77b | ||
|
|
5879ce4090 | ||
|
|
ac3e5c793c | ||
|
|
4326fb931b | ||
|
|
ced7051ce7 | ||
|
|
e9f77c1bde |
@@ -9,6 +9,9 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
||||
- comfortpoint for BC400 [#2935](https://github.com/emsesp/EMS-ESP32/issues/2935)
|
||||
- customize device brand [#2784](https://github.com/emsesp/EMS-ESP32/issues/2784)
|
||||
- set model for ems-esp devices temperature, analog, etc. [#2958](https://github.com/emsesp/EMS-ESP32/discussions/2958)
|
||||
- prometheus metrics for temperature/analog/scheduler/custom [#2962](https://github.com/emsesp/EMS-ESP32/issues/2962)
|
||||
- boiler pumpkick [#2965](https://github.com/emsesp/EMS-ESP32/discussions/2965)
|
||||
- heatpump reset [#2933](https://github.com/emsesp/EMS-ESP32/issues/2933)
|
||||
|
||||
## Fixed
|
||||
|
||||
@@ -21,3 +24,4 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
||||
- remove wrong burnMinPower [#2918](https://github.com/emsesp/EMS-ESP32/issues/2918)
|
||||
- store scheduler active state to nvs [#2946](https://github.com/emsesp/EMS-ESP32/discussions/2946)
|
||||
- translated modes `heat` and `eco` for HA-climate mode-str-tpl
|
||||
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
|
||||
|
||||
@@ -26,22 +26,22 @@
|
||||
"@alova/adapter-xhr": "2.3.1",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.3.8",
|
||||
"@mui/material": "^7.3.8",
|
||||
"@preact/compat": "^18.3.1",
|
||||
"@mui/icons-material": "^7.3.9",
|
||||
"@mui/material": "^7.3.9",
|
||||
"@preact/compat": "^18.3.2",
|
||||
"@table-library/react-table-library": "4.1.15",
|
||||
"alova": "3.5.0",
|
||||
"alova": "3.5.1",
|
||||
"async-validator": "^4.2.5",
|
||||
"etag": "^1.8.1",
|
||||
"formidable": "^3.5.4",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"magic-string": "^0.30.21",
|
||||
"mime-types": "^3.0.2",
|
||||
"preact": "^10.28.3",
|
||||
"preact": "^10.29.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router": "^7.13.0",
|
||||
"react-icons": "^5.6.0",
|
||||
"react-router": "^7.13.1",
|
||||
"react-toastify": "^11.0.5",
|
||||
"typesafe-i18n": "^5.27.1",
|
||||
"typescript": "^5.9.3"
|
||||
@@ -49,23 +49,23 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@preact/compat": "^18.3.1",
|
||||
"@preact/compat": "^18.3.2",
|
||||
"@preact/preset-vite": "^2.10.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"@types/node": "^25.2.3",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"axe-core": "^4.11.1",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^10.0.0",
|
||||
"eslint": "^10.0.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"prettier": "^3.8.1",
|
||||
"rollup-plugin-visualizer": "^6.0.5",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"terser": "^5.46.0",
|
||||
"typescript-eslint": "^8.55.0",
|
||||
"typescript-eslint": "^8.57.0",
|
||||
"vite": "^7.3.1",
|
||||
"vite-plugin-imagemin": "^0.6.1",
|
||||
"vite-tsconfig-paths": "^6.1.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.29.3"
|
||||
"packageManager": "pnpm@10.32.1"
|
||||
}
|
||||
|
||||
1654
interface/pnpm-lock.yaml
generated
1654
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -106,7 +106,7 @@ board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson @ 7.4.2
|
||||
ESP32Async/AsyncTCP @ 3.4.10
|
||||
ESP32Async/ESPAsyncWebServer @ 3.10.0
|
||||
ESP32Async/ESPAsyncWebServer @ 3.10.1
|
||||
https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
||||
|
||||
|
||||
|
||||
@@ -852,6 +852,15 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(metrics))) {
|
||||
std::string metrics = get_metrics_prometheus();
|
||||
if (!metrics.empty()) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is for a specific sensor, return the value
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
|
||||
@@ -866,6 +875,35 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
// generate Prometheus metrics format from analog values
|
||||
std::string AnalogSensor::get_metrics_prometheus() {
|
||||
std::string result;
|
||||
result.reserve(sensors_.size() * 140);
|
||||
char val[10];
|
||||
for (auto & sensor : sensors_) {
|
||||
result += (std::string) "# HELP emsesp_" + sensor.name() + " " + sensor.name();
|
||||
if (sensor.type() != AnalogType::DIGITAL_OUT && sensor.type() != AnalogType::DIGITAL_IN) {
|
||||
result += (std::string) ", " + EMSdevice::uom_to_string(sensor.uom());
|
||||
} else {
|
||||
result += (std::string) ", boolean";
|
||||
}
|
||||
result += (std::string) ", readable, visible";
|
||||
if (sensor.type() == AnalogType::COUNTER || sensor.type() == AnalogType::RGB || sensor.type() == AnalogType::PULSE
|
||||
|| (sensor.type() >= AnalogType::DIGITAL_OUT && sensor.type() <= AnalogType::PWM_2)
|
||||
|| (sensor.type() >= AnalogType::CNT_0 && sensor.type() <= AnalogType::CNT_2)) {
|
||||
result += (std::string) ", writable";
|
||||
}
|
||||
result += (std::string) "\n# TYPE emsesp_" + sensor.name() + " gauge\n";
|
||||
result += (std::string) "emsesp_" + sensor.name() + " ";
|
||||
if (sensor.type() != AnalogType::DIGITAL_OUT && sensor.type() != AnalogType::DIGITAL_IN) {
|
||||
result += (std::string) Helpers::render_value(val, sensor.value(), 2) + "\n";
|
||||
} else {
|
||||
result += (std::string) (sensor.value() == 0 ? "0\n" : "1\n");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// note we don't add the device and state classes here, as we do in the custom entity service
|
||||
void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||
output["name"] = (const char *)sensor.name();
|
||||
|
||||
@@ -177,6 +177,7 @@ class AnalogSensor {
|
||||
bool update(uint8_t gpio, const char * name, double offset, double factor, uint8_t uom, int8_t type, bool deleted, bool is_system);
|
||||
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
||||
void store_counters();
|
||||
std::string get_metrics_prometheus();
|
||||
static std::vector<uint8_t> exclude_types() {
|
||||
return exclude_types_;
|
||||
}
|
||||
|
||||
@@ -104,9 +104,9 @@ const char * EMSdevice::uom_to_string(uint8_t uom) {
|
||||
}
|
||||
}
|
||||
|
||||
const char * EMSdevice::brand_to_char() {
|
||||
std::string EMSdevice::brand_to_char() {
|
||||
if (!custom_brand().empty()) {
|
||||
return custom_brand().c_str();
|
||||
return custom_brand();
|
||||
}
|
||||
switch (brand_) {
|
||||
case EMSdevice::Brand::BOSCH:
|
||||
@@ -331,15 +331,15 @@ uint8_t EMSdevice::decode_brand(uint8_t value) {
|
||||
std::string EMSdevice::to_string() {
|
||||
// for devices that haven't been lookup yet, don't show all details
|
||||
if (product_id_ == 0) {
|
||||
return std::string(name()) + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
|
||||
return name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ")";
|
||||
}
|
||||
|
||||
if (brand_ == Brand::NO_BRAND && custom_brand().empty()) {
|
||||
return std::string(name()) + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
|
||||
return name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_ + ")";
|
||||
}
|
||||
|
||||
return std::string(brand_to_char()) + " " + name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_)
|
||||
+ ", Version:" + version_ + ")";
|
||||
return brand_to_char() + " " + name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_
|
||||
+ ")";
|
||||
}
|
||||
|
||||
// returns string of EMS device version and productID
|
||||
@@ -912,7 +912,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
// looks up the UOM for a given key from the device value table
|
||||
std::string EMSdevice::get_value_uom(const std::string & shortname) const {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE)) && (dv.short_name == shortname)) {
|
||||
if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE)) && !strcmp(dv.short_name, shortname.c_str())) {
|
||||
// ignore TIME since "minutes" is already added to the string value
|
||||
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
|
||||
break;
|
||||
@@ -1282,7 +1282,7 @@ void EMSdevice::setCustomizationEntity(const std::string & entity_id) {
|
||||
// set the min / max
|
||||
dv.set_custom_minmax();
|
||||
|
||||
if (Mqtt::ha_enabled() && dv.short_name == FL_(seltemp)[0] && (min != dv.min || max != dv.max)) {
|
||||
if (Mqtt::ha_enabled() && dv.tag <= DeviceValueTAG::TAG_HC8 && !strcmp(dv.short_name, FL_(selRoomTemp)[0]) && (min != dv.min || max != dv.max)) {
|
||||
set_climate_minmax(dv.tag, dv.min, dv.max);
|
||||
}
|
||||
|
||||
@@ -2160,14 +2160,14 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
||||
&& !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
||||
// create_device_config is only done once for the EMS device. It can added to any entity, so we take the first
|
||||
if (Mqtt::publish_ha_sensor_config_dv(dv, name().c_str(), std::string(brand_to_char()).c_str(), to_string_version().c_str(), false, create_device_config)) {
|
||||
if (Mqtt::publish_ha_sensor_config_dv(dv, name().c_str(), brand_to_char().c_str(), to_string_version().c_str(), false, create_device_config)) {
|
||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||
create_device_config = false; // only create the main config once
|
||||
count++;
|
||||
}
|
||||
|
||||
// SRC thermostats mapped to connect/src1/... always contains mode, seltemp, currtemp
|
||||
if (dv.tag >= DeviceValueTAG::TAG_SRC1 && dv.tag <= DeviceValueTAG::TAG_SRC16 && !strcmp(dv.short_name, FL_(seltemp)[0])) {
|
||||
// SRC thermostats mapped to connect/src1/... always contains mode, selRoomTemp, currtemp
|
||||
if (dv.tag >= DeviceValueTAG::TAG_SRC1 && dv.tag <= DeviceValueTAG::TAG_SRC16 && !strcmp(dv.short_name, FL_(selRoomTemp)[0])) {
|
||||
// add modes and icon if we have one
|
||||
const char * icon = nullptr;
|
||||
const char * const ** mode_options = nullptr;
|
||||
|
||||
@@ -63,7 +63,7 @@ class EMSdevice {
|
||||
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 brand_to_char();
|
||||
std::string to_string();
|
||||
std::string to_string_short();
|
||||
std::string to_string_version();
|
||||
|
||||
@@ -303,7 +303,7 @@ 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_reset, FL_(dash), FL_(maintenance), FL_(error), FL_(history), FL_(message), FL_(hp_error), FL_(burn_starts), FL_(factory))
|
||||
MAKE_ENUM(enum_maxHeat, FL_(0kW), FL_(2kW), FL_(3kW), FL_(4kW), FL_(6kW), FL_(9kW))
|
||||
MAKE_ENUM(enum_maxHeat1, FL_(0kW), FL_(3kW), FL_(6kW), FL_(9kW))
|
||||
MAKE_ENUM(enum_maxHeat2, FL_(3kW), FL_(6kW), FL_(9kW))
|
||||
|
||||
@@ -197,6 +197,9 @@ MAKE_WORD_TRANSLATION(lower, "lower", "niedriger", "lager", "lägre", "mniejszy"
|
||||
MAKE_WORD_TRANSLATION(error, "error", "Fehler", "error", "Fel", "błąd", "feil", "erreur", "Hata", "errore", "error", "chyba")
|
||||
MAKE_WORD_TRANSLATION(history, "history", "Fehlerspeicher", "geschiedenis", "historik", "historia", "historikk", "historique", "geçmiş", "storico", "história", "historie")
|
||||
MAKE_WORD_TRANSLATION(message, "message", "Meldung", "melding", "meddelande", "komunikat", "melding", "message", "mesajı", "messaggio", "správa", "zpráva")
|
||||
MAKE_WORD_TRANSLATION(hp_error, "hp error", "WP Fehler", "hp error", "hp Fel", "hp błąd", "hp feil", "hp erreur", "hp Hata", "hp errore", "hp error", "hp chyba")
|
||||
MAKE_WORD_TRANSLATION(factory, "factory", "werkseinst.", "", "", "", "", "", "", "", "", "")
|
||||
MAKE_WORD_TRANSLATION(burn_starts, "burner starts", "Brennerstarts", "", "", "", "", "", "", "", "", "")
|
||||
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", "omgekeerd", "inverterad", "odwrócony", "invertert", "inversé", "ters", "invertito", "invertovaný", "invertovaný")
|
||||
|
||||
@@ -424,6 +427,10 @@ MAKE_TRANSLATION(pumpOnTemp, "pumpontemp", "pump logic temperature", "Pumpenlogi
|
||||
MAKE_TRANSLATION(headertemp, "headertemp", "low loss header", "Hydr. Weiche", "open verdeler", "Fördelare", "sprzęgło hydrauliczne", "lav tap header", "bouteille de déc. hydr.", "isı bloğu gidiş suyu sıc.", "comp. idr.", "nízkostratová hlavica", "hydraulický oddělovač")
|
||||
MAKE_TRANSLATION(heatblock, "heatblock", "heating block", "Wärmezelle", "Aanvoertemp. warmtecel", "Värmeblock", "blok grzewczy", "varmeblokk", "départ corps de chauffe", "Hid.denge kabı sıcaklığı", "mandata scamb. pr.", "vykurovací blok", "blok topení")
|
||||
|
||||
MAKE_TRANSLATION(pumpKickHour, "pumpkickhour", "pump kick hour", "Stunde Pumpkick")
|
||||
MAKE_TRANSLATION(pumpKickDay, "pumpkickday", "pump kick day", "Tag Pumpkick")
|
||||
MAKE_TRANSLATION(pumpKickDelay, "pumpkickdelay", "pump kick delay", "Pause vor Pumpkick")
|
||||
|
||||
MAKE_TRANSLATION(curveOn, "curveon", "heatingcurve on", "Heizkurve an", "stookkromme aan", "Värmekurva På", "krzywa grzewcza włączona", "varmekurve på", "courbe de chauffage activée", "ısıtma eğrisi açık", "curva di riscaldamento attiva", "vykurovacia krivka zapnutá", "topná křivka zapnutá")
|
||||
MAKE_TRANSLATION(curveBase, "curvebase", "heatingcurve base", "Heizkurve Basis", "stookkromme basis", "Värmekurva Bas", "podstawa krzywej grzewczej", "varmekurve basis", "base de courbe de chauffage", "ısıtma eğrisi tabanı", "base curva di riscaldamento", "základňa vykurovacej krivky", "základ topné křivky")
|
||||
MAKE_TRANSLATION(curveEnd, "curveend", "heatingcurve end", "Heizkurve Ende", "stookkromme einde", "Värmekurva Slut", "koniec krzywej grzewczej", "varmekurve slutt", "fin de courbe de chauffage", "ısıtma eğrisi sonu", "fine curva di riscaldamento", "koniec vykurovacej krivky", "konec topné křivky")
|
||||
@@ -773,6 +780,7 @@ MAKE_TRANSLATION(comforttemp, "comforttemp", "comfort temperature", "Komforttemp
|
||||
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(baseflowtemp, "baseflowtemp", "base flow temperature", "Basis Vorlauftemperatur") // ToDo translate
|
||||
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")
|
||||
|
||||
@@ -2952,7 +2952,6 @@ void System::set_valid_system_gpios() {
|
||||
// excluded:
|
||||
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
||||
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
||||
// GPIO33 - GPIO37 = Octal flash/PSRAM
|
||||
// GPIO19 - GPIO20 = USB-JTAG
|
||||
// GPIO22 - GPIO25 = don't exist
|
||||
//
|
||||
@@ -2960,7 +2959,12 @@ void System::set_valid_system_gpios() {
|
||||
// GPIO11 - GPIO19 = ADC analog input only pins
|
||||
// GPIO47 - GPIO48 = valid on a Wemos S3
|
||||
// GPIO8 = used by Liligo S3 board profile for Rx
|
||||
if (ESP.getPsramSize() > 0) {
|
||||
// GPIO33 - GPIO37 = Octal flash/PSRAM
|
||||
valid_system_gpios_ = string_range_to_vector("0-48", "3, 45-46, 26-32, 33-37, 19-20, 22-25");
|
||||
} else {
|
||||
valid_system_gpios_ = string_range_to_vector("0-48", "3, 45-46, 26-32, 19-20, 22-25");
|
||||
}
|
||||
|
||||
#elif CONFIG_IDF_TARGET_ESP32
|
||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
||||
|
||||
@@ -400,6 +400,15 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(metrics))) {
|
||||
std::string metrics = get_metrics_prometheus();
|
||||
if (!metrics.empty()) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is for a specific sensor
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
|
||||
@@ -414,6 +423,21 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
// generate Prometheus metrics format from temperature values
|
||||
std::string TemperatureSensor::get_metrics_prometheus() {
|
||||
std::string result;
|
||||
result.reserve(sensors_.size() * 120);
|
||||
char val[10];
|
||||
for (auto & sensor : sensors_) {
|
||||
result += (std::string) "# HELP emsesp_" + sensor.name() + " " + sensor.name() + ", "
|
||||
+ EMSdevice::uom_to_string(EMSESP::system_.fahrenheit() ? DeviceValueUOM::FAHRENHEIT : DeviceValueUOM::DEGREES) + ", readable, visible\n";
|
||||
result += (std::string) "# TYPE emsesp_" + sensor.name() + " gauge\n";
|
||||
result +=
|
||||
(std::string) "emsesp_" + sensor.name() + " " + Helpers::render_value(val, sensor.temperature_c, 10, EMSESP::system_.fahrenheit() ? 2 : 0) + "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// note we don't add the device and state classes here, as we do in the custom entity service
|
||||
void TemperatureSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||
output["id"] = sensor.id();
|
||||
|
||||
@@ -96,6 +96,8 @@ class TemperatureSensor {
|
||||
bool updated_values();
|
||||
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
||||
|
||||
std::string get_metrics_prometheus();
|
||||
|
||||
// return back reference to the sensor list, used by other classes
|
||||
std::vector<Sensor, AllocatorPSRAM<Sensor>> sensors() const {
|
||||
return sensors_;
|
||||
|
||||
@@ -65,6 +65,7 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_telegram_type(0xE6, "UBAParametersPlus", true, MAKE_PF_CB(process_UBAParametersPlus));
|
||||
register_telegram_type(0xE9, "UBAMonitorWWPlus", false, MAKE_PF_CB(process_UBAMonitorWWPlus));
|
||||
register_telegram_type(0xEA, "UBAParameterWWPlus", true, MAKE_PF_CB(process_UBAParameterWWPlus));
|
||||
register_telegram_type(0xEB, "PumpKick", true, MAKE_PF_CB(process_PumpKick));
|
||||
register_telegram_type(0x28, "WeatherComp", true, MAKE_PF_CB(process_WeatherComp));
|
||||
register_telegram_type(0x2E0, "UBASetPoints", false, MAKE_PF_CB(process_UBASetPoints2));
|
||||
register_telegram_type(0x2CC, "HPPressure", true, MAKE_PF_CB(process_HpPressure));
|
||||
@@ -355,6 +356,24 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &pc1On_, DeviceValueType::BOOL, FL_(pc1On), DeviceValueUOM::NONE);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &pc1Rate_, DeviceValueType::UINT8, FL_(pc1Rate), DeviceValueUOM::PERCENT);
|
||||
|
||||
register_device_value(
|
||||
DeviceValueTAG::TAG_DEVICE_DATA, &pumpKickHour_, DeviceValueType::UINT8, FL_(pumpKickHour), DeviceValueUOM::HOURS, MAKE_CF_CB(set_pumpKickHour), 0, 23);
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&pumpKickDay_,
|
||||
DeviceValueType::ENUM,
|
||||
FL_(enum_dayOfWeek),
|
||||
FL_(pumpKickDay),
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_pumpKickDay));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&pumpKickDelay_,
|
||||
DeviceValueType::UINT16,
|
||||
FL_(pumpKickDelay),
|
||||
DeviceValueUOM::MINUTES,
|
||||
MAKE_CF_CB(set_pumpKickDelay),
|
||||
0,
|
||||
32767);
|
||||
|
||||
/*
|
||||
* Hybrid heatpump with telegram 0xBB is readable and writeable in boiler and thermostat
|
||||
* thermostat always overwrites settings in boiler
|
||||
@@ -2246,6 +2265,13 @@ void Boiler::process_HpPowerLimit(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, hpPowerLimit_, 0);
|
||||
}
|
||||
|
||||
// 0x0EB
|
||||
void Boiler::process_PumpKick(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, pumpKickHour_, 0);
|
||||
has_enumupdate(telegram, pumpKickDay_, 1, 1); // 1-mo, ...
|
||||
has_update(telegram, pumpKickDelay_, 2);
|
||||
}
|
||||
|
||||
// Boiler(0x08) -B-> All(0x00), ?(0x2E), data: 00 00 1C CE 00 00 05 E8 00 00 00 18 00 00 00 02
|
||||
void Boiler::process_Meters(std::shared_ptr<const Telegram> telegram) {
|
||||
has_update(telegram, gasMeterHeat_, 0);
|
||||
@@ -2980,24 +3006,25 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
|
||||
|
||||
if (num == 0) {
|
||||
return true; // dash
|
||||
} else if (num == 1) {
|
||||
// LOG_INFO("Reset boiler maintenance message");
|
||||
} else if (num == 1) { // Reset boiler maintenance message;
|
||||
write_command(0x05, 8, 0xFF, 0x1C);
|
||||
return true;
|
||||
} else if (num == 2) {
|
||||
// LOG_INFO("Reset boiler error message");
|
||||
write_command(0x05, 0, 0x5A); // error reset
|
||||
} else if (num == 2) { // Reset boiler error message;
|
||||
write_command(0x05, 0, 0x5A);
|
||||
return true;
|
||||
} else if (num == 3) {
|
||||
// LOG_INFO("Reset boiler history");
|
||||
write_command(0x05, 42, 0x01); // clear history
|
||||
} else if (num == 3) { // Reset boiler history
|
||||
write_command(0x05, 42, 0x01);
|
||||
return true;
|
||||
} else if (num == 4) {
|
||||
// LOG_INFO("Reset boiler message");
|
||||
write_command(0x05, 8, 0xFF); // same as maintenance
|
||||
write_command(0x05, 8, 0xFF); // reset messages, same as maintenance reset (1)
|
||||
return true;
|
||||
} else if (num == 5) {
|
||||
// LOG_INFO("Factory Reset");
|
||||
} else if (num == 5) { // reset Heatpump errors
|
||||
write_command(0x05, 50, 0xFF);
|
||||
return true;
|
||||
} else if (num == 6) { // reset burner starts
|
||||
write_command(0x05, 2, 165);
|
||||
return true;
|
||||
} else if (num == 7) { // factory reset
|
||||
write_command(0x05, 6, 154);
|
||||
return true;
|
||||
}
|
||||
@@ -3628,4 +3655,32 @@ bool Boiler::set_shutdown(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Boiler::set_pumpKickHour(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (Helpers::value2number(value, v, 0, 23)) {
|
||||
write_command(0xEB, 0, v, 0xEB);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Boiler::set_pumpKickDay(const char * value, const int8_t id) {
|
||||
uint8_t v;
|
||||
if (Helpers::value2enum(value, v, FL_(enum_dayOfWeek))) {
|
||||
write_command(0xEB, 1, v + 1, 0xEB);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Boiler::set_pumpKickDelay(const char * value, const int8_t id) {
|
||||
int v;
|
||||
if (Helpers::value2number(value, v, 0, 32767)) {
|
||||
uint8_t data[2] = {(uint8_t)(v >> 8), (uint8_t)v};
|
||||
write_command(0xEB, 2, data, 2, 0xEB);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -319,6 +319,9 @@ class Boiler : public EMSdevice {
|
||||
int16_t pc1Flow_;
|
||||
uint8_t pc1Rate_;
|
||||
uint8_t pc1On_;
|
||||
uint8_t pumpKickHour_; // hour
|
||||
uint8_t pumpKickDay_; // day 1=mo
|
||||
uint16_t pumpKickDelay_; // delay minutes after pump running
|
||||
|
||||
// HIU
|
||||
// uint16_t cwFlowRate_; // cold water flow rate *10
|
||||
@@ -397,6 +400,7 @@ class Boiler : public EMSdevice {
|
||||
void process_HpFan(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpPower2(std::shared_ptr<const Telegram> telegram);
|
||||
void process_HpPowerLimit(std::shared_ptr<const Telegram> telegram);
|
||||
void process_PumpKick(std::shared_ptr<const Telegram> telegram);
|
||||
|
||||
void process_Meters(std::shared_ptr<const Telegram> telegram);
|
||||
void process_Energy(std::shared_ptr<const Telegram> telegram);
|
||||
@@ -602,6 +606,10 @@ class Boiler : public EMSdevice {
|
||||
bool set_nrgHeat(const char * value, const int8_t id);
|
||||
bool set_nrgWw(const char * value, const int8_t id);
|
||||
bool set_nomPower(const char * value, const int8_t id);
|
||||
|
||||
bool set_pumpKickHour(const char * value, const int8_t id);
|
||||
bool set_pumpKickDay(const char * value, const int8_t id);
|
||||
bool set_pumpKickDelay(const char * value, const int8_t id);
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -211,13 +211,14 @@ bool Connect::set_mode(const char * value, const int8_t id) {
|
||||
return false;
|
||||
}
|
||||
uint8_t v;
|
||||
if (Helpers::value2enum(value, v, FL_(enum_mode2), {3, 1, 0})) {
|
||||
// if (Helpers::value2enum(value, v, FL_(enum_mode8))) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_mode2), {3, 1, 0})) {
|
||||
if (!Helpers::value2enum(value, v, FL_(enum_mode_ha), {3, 1, 0})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
write_command(0xBB5 + rc->room(), 0, v); // no validate, mode change is broadcasted
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Connect::set_seltemp(const char * value, const int8_t id) {
|
||||
auto rc = room_circuit(id - DeviceValueTAG::TAG_SRC1);
|
||||
|
||||
@@ -1101,6 +1101,8 @@ void Thermostat::process_JunkersWW(std::shared_ptr<const Telegram> telegram) {
|
||||
void Thermostat::process_JunkersDisp(std::shared_ptr<const Telegram> telegram) {
|
||||
has_enumupdate(telegram, ibaMainDisplay_, 1, 1);
|
||||
has_update(telegram, ibaLanguage_, 3);
|
||||
has_update(telegram, ibaMinExtTemperature_, 16);
|
||||
has_update(telegram, ibaBuildingType_, 17); // percent /10
|
||||
}
|
||||
|
||||
// type 0x02A5 - data from Worchester CRF200
|
||||
@@ -1250,12 +1252,12 @@ void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
if (hc->heatingtype != 3) {
|
||||
has_update(telegram, hc->designtemp, 4);
|
||||
has_update(telegram, hc->minflowtemp, model() == EMSdevice::EMS_DEVICE_FLAG_BC400 ? 13 : 8);
|
||||
} else {
|
||||
has_update(telegram, hc->designtemp, 5);
|
||||
has_update(telegram, hc->minflowtemp, 8);
|
||||
}
|
||||
|
||||
// minflowtemp could be in 8 or 13 #2969
|
||||
has_update(telegram, hc->minflowtemp, 13);
|
||||
has_update(telegram, hc->baseflowtemp, 8);
|
||||
has_update(telegram, hc->fastHeatup, 10);
|
||||
has_update(telegram, hc->comfortPointOffset, 11);
|
||||
has_update(telegram, hc->comfortPointTemp, 12);
|
||||
@@ -2025,6 +2027,8 @@ bool Thermostat::set_minexttemp(const char * value, const int8_t id) {
|
||||
write_command(0x241, 10, mt, 0x241);
|
||||
} else if (isRC300()) {
|
||||
write_command(0x240, 10, mt, 0x240);
|
||||
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
||||
write_command(0x110, 16, mt, 0x110);
|
||||
} else {
|
||||
write_command(EMS_TYPE_IBASettings, 5, mt, EMS_TYPE_IBASettings);
|
||||
}
|
||||
@@ -2181,6 +2185,14 @@ bool Thermostat::set_remotehum(const char * value, const int8_t id) {
|
||||
|
||||
// 0xA5/0xA7 - Set the building settings
|
||||
bool Thermostat::set_building(const char * value, const int8_t id) {
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
||||
int i;
|
||||
if (Helpers::value2number(value, i, 0, 100)) {
|
||||
write_command(0x110, 17, i / 10, 0x110);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
uint8_t bd;
|
||||
if (!Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) {
|
||||
return false;
|
||||
@@ -2326,6 +2338,7 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
|
||||
// 1-FB10, 2-FB100
|
||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS && !has_flags(EMSdevice::EMS_DEVICE_FLAG_JUNKERS_OLD)) {
|
||||
if (Helpers::value2enum(value, ctrl, FL_(enum_j_control))) {
|
||||
hc->control = ctrl; // set in advance, dont wait for verify
|
||||
write_command(set_typeids[hc->hc()], 1, ctrl);
|
||||
return true;
|
||||
}
|
||||
@@ -2374,6 +2387,7 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
|
||||
}
|
||||
} else if (Helpers::value2enum(value, ctrl, FL_(enum_control))) {
|
||||
write_command(set_typeids[hc->hc()], EMS_OFFSET_RC35Set_control, ctrl);
|
||||
hc->control = ctrl; // set in advance, dont wait for verify
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4074,10 +4088,16 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
||||
}
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::BASEFLOW:
|
||||
set_typeid = summer_typeids[hc->hc()];
|
||||
validate_typeid = set_typeid;
|
||||
offset = 8;
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::MINFLOW:
|
||||
set_typeid = summer_typeids[hc->hc()];
|
||||
validate_typeid = set_typeid;
|
||||
offset = hc->heatingtype != 3 && model == EMS_DEVICE_FLAG_BC400 ? 13 : 8;
|
||||
offset = 13;
|
||||
factor = 1;
|
||||
break;
|
||||
case HeatingCircuit::Mode::MAXFLOW:
|
||||
@@ -4730,6 +4750,19 @@ void Thermostat::register_device_values() {
|
||||
DeviceValueUOM::NONE,
|
||||
MAKE_CF_CB(set_language));
|
||||
}
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&ibaBuildingType_,
|
||||
DeviceValueType::UINT8,
|
||||
DeviceValueNumOp::DV_NUMOP_MUL10,
|
||||
FL_(ibaBuildingType),
|
||||
DeviceValueUOM::PERCENT,
|
||||
MAKE_CF_CB(set_building));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&ibaMinExtTemperature_,
|
||||
DeviceValueType::INT8,
|
||||
FL_(ibaMinExtTemperature),
|
||||
DeviceValueUOM::DEGREES,
|
||||
MAKE_CF_CB(set_minexttemp));
|
||||
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||
&hybridStrategy_,
|
||||
DeviceValueType::ENUM,
|
||||
@@ -4880,6 +4913,7 @@ void Thermostat::register_device_values_hc(std::shared_ptr<Thermostat::HeatingCi
|
||||
register_device_value(tag, &hc->summertemp, DeviceValueType::UINT8, FL_(summertemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_summertemp), 10, 30);
|
||||
register_device_value(tag, &hc->designtemp, DeviceValueType::UINT8, FL_(designtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_designtemp));
|
||||
register_device_value(tag, &hc->offsettemp, DeviceValueType::INT8, FL_(offsettemp), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_offsettemp));
|
||||
register_device_value(tag, &hc->baseflowtemp, DeviceValueType::UINT8, FL_(baseflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_baseflowtemp));
|
||||
register_device_value(tag, &hc->minflowtemp, DeviceValueType::UINT8, FL_(minflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_minflowtemp));
|
||||
register_device_value(tag, &hc->maxflowtemp, DeviceValueType::UINT8, FL_(maxflowtemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_maxflowtemp));
|
||||
register_device_value(tag, &hc->roominfluence, DeviceValueType::UINT8, FL_(roominfluence), DeviceValueUOM::DEGREES_R, MAKE_CF_CB(set_roominfluence));
|
||||
|
||||
@@ -50,7 +50,7 @@ class Thermostat : public EMSdevice {
|
||||
uint8_t daymidtemp;
|
||||
uint8_t nighttemp;
|
||||
uint8_t holidaytemp;
|
||||
uint8_t heatingtype; // type of heating: 1 radiator, 2 convectors, 3 floors, 4 room supply
|
||||
uint8_t heatingtype; // type of heating: 1 radiator, 2 convectors, 3 floor, 4 room supply
|
||||
uint8_t targetflowtemp;
|
||||
uint8_t summertemp;
|
||||
int8_t nofrosttemp; // signed -20°C to +10°C
|
||||
@@ -65,6 +65,7 @@ class Thermostat : public EMSdevice {
|
||||
int16_t curroominfl;
|
||||
uint8_t flowtempoffset;
|
||||
uint8_t minflowtemp;
|
||||
uint8_t baseflowtemp;
|
||||
uint8_t maxflowtemp;
|
||||
uint8_t reducemode;
|
||||
uint8_t nofrostmode;
|
||||
@@ -169,6 +170,7 @@ class Thermostat : public EMSdevice {
|
||||
DAYMID,
|
||||
COOLTEMP,
|
||||
COOLSTART,
|
||||
BASEFLOW,
|
||||
UNKNOWN
|
||||
|
||||
};
|
||||
@@ -583,6 +585,9 @@ class Thermostat : public EMSdevice {
|
||||
inline bool set_minflowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MINFLOW);
|
||||
}
|
||||
inline bool set_baseflowtemp(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::BASEFLOW);
|
||||
}
|
||||
inline bool set_roominfluence(const char * value, const int8_t id) {
|
||||
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE, true);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.8.2-dev.6"
|
||||
#define EMSESP_APP_VERSION "3.8.2-dev.9"
|
||||
|
||||
@@ -343,6 +343,15 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(metrics))) {
|
||||
std::string metrics = get_metrics_prometheus();
|
||||
if (!metrics.empty()) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// specific value info
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
for (auto const & entity : *customEntityItems_) {
|
||||
@@ -354,6 +363,54 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
// generate Prometheus metrics format from custom entities
|
||||
std::string WebCustomEntityService::get_metrics_prometheus() {
|
||||
std::string result;
|
||||
result.reserve(customEntityItems_->size() * 140);
|
||||
char val[10];
|
||||
for (CustomEntityItem & entity : *customEntityItems_) {
|
||||
if (entity.hide || entity.name[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
result += (std::string) "# HELP emsesp_" + entity.name + " " + entity.name;
|
||||
if (entity.uom != 0) {
|
||||
result += (std::string) ", " + EMSdevice::uom_to_string(entity.uom);
|
||||
}
|
||||
result += (std::string) ", readable, visible" + (entity.writeable ? ", writable\n" : "\n");
|
||||
result += (std::string) "# TYPE emsesp_" + entity.name + " gauge\n";
|
||||
result += (std::string) "emsesp_" + entity.name + " ";
|
||||
switch (entity.value_type) {
|
||||
case DeviceValueType::BOOL:
|
||||
result += (std::string)(entity.value == 0 ? "0" : "1");
|
||||
break;
|
||||
case DeviceValueType::INT8:
|
||||
result += (std::string)Helpers::render_value(val, entity.factor * (int8_t)entity.value, 2);
|
||||
break;
|
||||
case DeviceValueType::UINT8:
|
||||
result += (std::string)Helpers::render_value(val, entity.factor * (uint8_t)entity.value, 2);
|
||||
break;
|
||||
case DeviceValueType::INT16:
|
||||
result += (std::string)Helpers::render_value(val, entity.factor * (int16_t)entity.value, 2);
|
||||
break;
|
||||
case DeviceValueType::UINT16:
|
||||
result += (std::string)Helpers::render_value(val, entity.factor * (uint16_t)entity.value, 2);
|
||||
break;
|
||||
case DeviceValueType::UINT24:
|
||||
case DeviceValueType::TIME:
|
||||
case DeviceValueType::UINT32:
|
||||
result += (std::string)Helpers::render_value(val, entity.factor * entity.value, 2);
|
||||
break;
|
||||
default:
|
||||
if (entity.data.length() > 0) {
|
||||
result += entity.data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
result += (std::string) "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// build the json for specific entity
|
||||
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem const & entity) {
|
||||
output["name"] = (const char *)entity.name;
|
||||
|
||||
@@ -68,6 +68,8 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
|
||||
void show_values(JsonObject output);
|
||||
void generate_value_web(JsonObject output, const bool is_dashboard = false);
|
||||
|
||||
std::string get_metrics_prometheus();
|
||||
|
||||
uint8_t count_entities();
|
||||
void ha_reset() {
|
||||
ha_configdone_ = false;
|
||||
|
||||
@@ -102,7 +102,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
|
||||
if (webScheduler.scheduleItems.back().name[0] != '\0') {
|
||||
char key[sizeof(webScheduler.scheduleItems.back().name) + 2];
|
||||
snprintf(key, sizeof(key), "s:%s", webScheduler.scheduleItems.back().name);
|
||||
if (EMSESP::nvs_.isKey(key)) {
|
||||
if (EMSESP::nvs_.isKey(key) && webScheduler.scheduleItems.back().flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
||||
webScheduler.scheduleItems.back().active = EMSESP::nvs_.getBool(key);
|
||||
}
|
||||
Command::add(
|
||||
@@ -138,20 +138,11 @@ bool WebSchedulerService::command_setvalue(const char * value, const int8_t id,
|
||||
publish();
|
||||
}
|
||||
// save new state to nvs #2946
|
||||
if (scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
||||
char key[sizeof(scheduleItem.name) + 2];
|
||||
snprintf(key, sizeof(key), "s:%s", scheduleItem.name);
|
||||
EMSESP::nvs_.putBool(key, scheduleItem.active);
|
||||
/* save to filesystem
|
||||
EMSESP::webSchedulerService.update([&](WebScheduler & webSchedule) {
|
||||
for (auto si : webSchedule.scheduleItems) {
|
||||
if (!strcmp(si.name, scheduleItem.name)) {
|
||||
si.active = scheduleItem.active;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return StateUpdateResult::CHANGED;
|
||||
});
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -184,6 +175,15 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, F_(metrics))) {
|
||||
std::string metrics = get_metrics_prometheus();
|
||||
if (!metrics.empty()) {
|
||||
output["api_data"] = metrics;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char * attribute_s = Command::get_attribute(cmd);
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
||||
if (Helpers::toLower(scheduleItem.name) == cmd) {
|
||||
@@ -195,6 +195,21 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
||||
return false; // not found
|
||||
}
|
||||
|
||||
// generate Prometheus metrics format from scheduler values
|
||||
std::string WebSchedulerService::get_metrics_prometheus() {
|
||||
std::string result;
|
||||
result.reserve(scheduleItems_->size() * 140);
|
||||
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
||||
if (scheduleItem.name[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
result += (std::string) "# HELP emsesp_" + scheduleItem.name + " " + scheduleItem.name + ", boolean, readable, visible, writable\n";
|
||||
result += (std::string) "# TYPE emsesp_" + scheduleItem.name + " gauge\n";
|
||||
result += (std::string) "emsesp_" + scheduleItem.name + " " + (scheduleItem.active ? "1\n" : "0\n");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// build the json for specific entity
|
||||
void WebSchedulerService::get_value_json(JsonObject output, const ScheduleItem & scheduleItem) {
|
||||
output["name"] = (const char *)scheduleItem.name;
|
||||
@@ -483,6 +498,10 @@ void WebSchedulerService::loop() {
|
||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
||||
command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str()));
|
||||
scheduleItem.active = false;
|
||||
publish_single(scheduleItem.name, false);
|
||||
if (EMSESP::mqtt_.get_publish_onchange(0)) {
|
||||
publish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
|
||||
uint8_t count_entities(bool cmd_only = false);
|
||||
bool onChange(const char * cmd);
|
||||
|
||||
std::string get_metrics_prometheus();
|
||||
|
||||
std::string raw_value;
|
||||
std::string computed_value;
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ void test_2() {
|
||||
"off\",\"heatingtemp\":\"heating temperature\",\"maintenance\":\"maintenance scheduled\",\"maintenancedate\":\"next maintenance "
|
||||
"date\",\"maintenancetime\":\"time to next maintenance\",\"nofrostmode\":\"nofrost mode\",\"nofrosttemp\":\"nofrost "
|
||||
"temperature\",\"nompower\":\"nominal Power\",\"nrgheat\":\"energy heating\",\"pumpcharacter\":\"boiler pump characteristic\",\"pumpdelay\":\"pump "
|
||||
"delay\",\"pumpmode\":\"boiler pump mode\",\"pumpmodmax\":\"boiler pump max power\",\"pumpmodmin\":\"boiler pump min power\",\"pumpontemp\":\"pump "
|
||||
"delay\",\"pumpkickday\":\"pump kick day\",\"pumpkickdelay\":\"pump kick delay\",\"pumpkickhour\":\"pump kick hour\",\"pumpmode\":\"boiler pump "
|
||||
"mode\",\"pumpmodmax\":\"boiler pump max power\",\"pumpmodmin\":\"boiler pump min power\",\"pumpontemp\":\"pump "
|
||||
"logic temperature\",\"reset\":\"reset\",\"selburnpow\":\"burner selected max power\",\"selflowtemp\":\"selected flow "
|
||||
"temperature\",\"summertemp\":\"summer temperature\"}]";
|
||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/commands"));
|
||||
|
||||
Reference in New Issue
Block a user