mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-03-14 05:36:34 +03:00
Compare commits
12 Commits
e9f77c1bde
...
c05e1cb77b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c05e1cb77b | ||
|
|
5879ce4090 | ||
|
|
ac3e5c793c | ||
|
|
4326fb931b | ||
|
|
ced7051ce7 | ||
|
|
421da246ed | ||
|
|
148a721e17 | ||
|
|
a811670c5a | ||
|
|
72f08a86cf | ||
|
|
27c471f45f | ||
|
|
e303972d26 | ||
|
|
97bb03d703 |
@@ -8,10 +8,12 @@ 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)
|
||||
|
||||
## Fixed
|
||||
|
||||
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936)
|
||||
- SRC climate creation [#2936](https://github.com/emsesp/EMS-ESP32/issues/2936) and [#2960](https://github.com/emsesp/EMS-ESP32/issues/2960)
|
||||
|
||||
## Changed
|
||||
|
||||
|
||||
62
interface/pnpm-lock.yaml
generated
62
interface/pnpm-lock.yaml
generated
@@ -508,13 +508,9 @@ packages:
|
||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@isaacs/balanced-match@4.0.1':
|
||||
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
'@isaacs/brace-expansion@5.0.1':
|
||||
resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
'@isaacs/cliui@9.0.0':
|
||||
resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
@@ -1062,6 +1058,10 @@ packages:
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
balanced-match@4.0.2:
|
||||
resolution: {integrity: sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
@@ -1101,6 +1101,10 @@ packages:
|
||||
brace-expansion@2.0.2:
|
||||
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
||||
|
||||
brace-expansion@5.0.2:
|
||||
resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
braces@3.0.3:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1155,8 +1159,8 @@ packages:
|
||||
resolution: {integrity: sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
caniuse-lite@1.0.30001769:
|
||||
resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==}
|
||||
caniuse-lite@1.0.30001770:
|
||||
resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==}
|
||||
|
||||
caw@2.0.1:
|
||||
resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==}
|
||||
@@ -2077,6 +2081,10 @@ packages:
|
||||
resolution: {integrity: sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
jackspeak@4.2.3:
|
||||
resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
javascript-natural-sort@0.7.1:
|
||||
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
|
||||
|
||||
@@ -2247,8 +2255,8 @@ packages:
|
||||
resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
minimatch@10.1.2:
|
||||
resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==}
|
||||
minimatch@10.2.0:
|
||||
resolution: {integrity: sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
minimatch@3.1.2:
|
||||
@@ -3432,7 +3440,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@eslint/object-schema': 3.0.1
|
||||
debug: 4.4.3
|
||||
minimatch: 10.1.2
|
||||
minimatch: 10.2.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -3466,11 +3474,7 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.4.3': {}
|
||||
|
||||
'@isaacs/balanced-match@4.0.1': {}
|
||||
|
||||
'@isaacs/brace-expansion@5.0.1':
|
||||
dependencies:
|
||||
'@isaacs/balanced-match': 4.0.1
|
||||
'@isaacs/cliui@9.0.0': {}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
@@ -3805,7 +3809,7 @@ snapshots:
|
||||
|
||||
'@types/minimatch@6.0.0':
|
||||
dependencies:
|
||||
minimatch: 10.1.2
|
||||
minimatch: 10.2.0
|
||||
|
||||
'@types/node@25.2.3':
|
||||
dependencies:
|
||||
@@ -3986,6 +3990,10 @@ snapshots:
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
balanced-match@4.0.2:
|
||||
dependencies:
|
||||
jackspeak: 4.2.3
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
baseline-browser-mapping@2.9.19: {}
|
||||
@@ -4039,6 +4047,10 @@ snapshots:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
brace-expansion@5.0.2:
|
||||
dependencies:
|
||||
balanced-match: 4.0.2
|
||||
|
||||
braces@3.0.3:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
@@ -4046,7 +4058,7 @@ snapshots:
|
||||
browserslist@4.28.1:
|
||||
dependencies:
|
||||
baseline-browser-mapping: 2.9.19
|
||||
caniuse-lite: 1.0.30001769
|
||||
caniuse-lite: 1.0.30001770
|
||||
electron-to-chromium: 1.5.286
|
||||
node-releases: 2.0.27
|
||||
update-browserslist-db: 1.2.3(browserslist@4.28.1)
|
||||
@@ -4105,7 +4117,7 @@ snapshots:
|
||||
|
||||
camelcase@2.1.1: {}
|
||||
|
||||
caniuse-lite@1.0.30001769: {}
|
||||
caniuse-lite@1.0.30001770: {}
|
||||
|
||||
caw@2.0.1:
|
||||
dependencies:
|
||||
@@ -4581,7 +4593,7 @@ snapshots:
|
||||
imurmurhash: 0.1.4
|
||||
is-glob: 4.0.3
|
||||
json-stable-stringify-without-jsonify: 1.0.1
|
||||
minimatch: 10.1.2
|
||||
minimatch: 10.2.0
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.9.4
|
||||
transitivePeerDependencies:
|
||||
@@ -5116,6 +5128,10 @@ snapshots:
|
||||
has-to-string-tag-x: 1.4.1
|
||||
is-object: 1.0.2
|
||||
|
||||
jackspeak@4.2.3:
|
||||
dependencies:
|
||||
'@isaacs/cliui': 9.0.0
|
||||
|
||||
javascript-natural-sort@0.7.1: {}
|
||||
|
||||
jpegtran-bin@5.0.2:
|
||||
@@ -5274,9 +5290,9 @@ snapshots:
|
||||
|
||||
mimic-response@1.0.1: {}
|
||||
|
||||
minimatch@10.1.2:
|
||||
minimatch@10.2.0:
|
||||
dependencies:
|
||||
'@isaacs/brace-expansion': 5.0.1
|
||||
brace-expansion: 5.0.2
|
||||
|
||||
minimatch@3.1.2:
|
||||
dependencies:
|
||||
|
||||
@@ -106,7 +106,7 @@ board_build.filesystem = littlefs
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson @ 7.4.2
|
||||
ESP32Async/AsyncTCP @ 3.4.10
|
||||
ESP32Async/ESPAsyncWebServer @ 3.9.6
|
||||
ESP32Async/ESPAsyncWebServer @ 3.10.0
|
||||
https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
||||
|
||||
|
||||
|
||||
@@ -316,6 +316,10 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.ha_number_mode != settings.ha_number_mode) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (newSettings.entity_format != settings.entity_format) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -2135,7 +2135,7 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
|
||||
if (needs_update) {
|
||||
const char * const ** mode_options = nullptr;
|
||||
for (auto & d : devicevalues_) {
|
||||
for (const auto & d : devicevalues_) {
|
||||
// make sure mode in same circuit is DeviceValueType::ENUM
|
||||
if ((d.tag == dv.tag) && (d.type == DeviceValueType::ENUM) && !strcmp(d.short_name, FL_(mode)[0]) && (d.options_size > 0)) {
|
||||
// get options
|
||||
@@ -2166,20 +2166,26 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
||||
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_(mode)[0])) {
|
||||
// add icon if we have one
|
||||
// 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;
|
||||
for (auto & d : devicevalues_) {
|
||||
if (d.tag == dv.tag && !strcmp(d.short_name, FL_(icon)[0]) && (dv.type == DeviceValueType::ENUM)) {
|
||||
const char * const ** mode_options = nullptr;
|
||||
for (const auto & d : devicevalues_) {
|
||||
if ((d.tag != dv.tag) || (d.type != DeviceValueType::ENUM)) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(d.short_name, FL_(mode)[0]) && (d.options_size > 0)) {
|
||||
mode_options = d.options;
|
||||
}
|
||||
if (!strcmp(d.short_name, FL_(icon)[0])) {
|
||||
uint8_t val = *(uint8_t *)(d.value_p);
|
||||
if (val != 0 && val < d.options_size) {
|
||||
icon = d.options[val][0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Mqtt::publish_ha_climate_config(dv, true, dv.options, false, icon);
|
||||
Mqtt::publish_ha_climate_config(dv, true, mode_options, false, icon);
|
||||
count++;
|
||||
}
|
||||
|
||||
|
||||
@@ -1545,9 +1545,7 @@ void Mqtt::add_ha_dev_section(JsonObject doc, const char * name, const bool crea
|
||||
|
||||
// add mf (manufacturer/brand), mdl (model), sw (software version) and via_device
|
||||
dev_json["mf"] = brand != nullptr ? brand : "EMS-ESP";
|
||||
if (model != nullptr) {
|
||||
dev_json["mdl"] = model;
|
||||
}
|
||||
dev_json["mdl"] = model != nullptr ? model : "EMS-ESP";
|
||||
dev_json["sw"] = version != nullptr ? version : "v" + std::string(EMSESP_APP_VERSION);
|
||||
dev_json["via_device"] = Mqtt::basename();
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -129,15 +129,20 @@ void Connect::process_roomThermostat(std::shared_ptr<const Telegram> telegram) {
|
||||
}
|
||||
has_update(telegram, rc->temp_, 0);
|
||||
has_update(telegram, rc->humidity_, 2); // could show -3 if not set
|
||||
// make sure we have read mode and icon, needed for ha climate
|
||||
if (!Mqtt::ha_enabled() || (Helpers::hasValue(rc->mode_) && Helpers::hasValue(rc->icon_))) {
|
||||
has_update(telegram, rc->seltemp_, 3);
|
||||
}
|
||||
|
||||
// calculate dew temperature
|
||||
if (rc->humidity_ >= 0 && rc->humidity_ <= 100) {
|
||||
const float k2 = 17.62;
|
||||
const float k3 = 243.12;
|
||||
const float t = (float)rc->temp_ / 10;
|
||||
const float h = (float)rc->humidity_ / 100;
|
||||
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
|
||||
has_update(rc->dewtemp_, dt);
|
||||
}
|
||||
}
|
||||
|
||||
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in offset 0
|
||||
@@ -206,12 +211,13 @@ 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) {
|
||||
@@ -221,8 +227,9 @@ bool Connect::set_seltemp(const char * value, const int8_t id) {
|
||||
}
|
||||
float v;
|
||||
if (Helpers::value2float(value, v)) {
|
||||
// write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
||||
write_command(0xBB5 + rc->room(), rc->mode_ == 0 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
||||
// depends on mode, auto (2 for enum_mode2, 0 for enum_mode8) set in offset 1
|
||||
write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
||||
// write_command(0xBB5 + rc->room(), rc->mode_ == 0 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define EMSESP_APP_VERSION "3.8.2-dev.5"
|
||||
#define EMSESP_APP_VERSION "3.8.2-dev.7"
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user