mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-03-16 14:46:32 +03:00
Compare commits
31 Commits
b153364b60
...
latest
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34a2b20be8 | ||
|
|
f1fc8d9aae | ||
|
|
b04355e3e1 | ||
|
|
cd3ae5cdf2 | ||
|
|
a261ca23af | ||
|
|
cb96904a5c | ||
|
|
4a2d78f8e1 | ||
|
|
f5af4fb52f | ||
|
|
2037bc3a10 | ||
|
|
64d17d7c65 | ||
|
|
92e2633342 | ||
|
|
96a7ea8a02 | ||
|
|
5c4aaa4510 | ||
|
|
c05e1cb77b | ||
|
|
5879ce4090 | ||
|
|
ac3e5c793c | ||
|
|
4326fb931b | ||
|
|
ced7051ce7 | ||
|
|
421da246ed | ||
|
|
148a721e17 | ||
|
|
a811670c5a | ||
|
|
72f08a86cf | ||
|
|
27c471f45f | ||
|
|
e303972d26 | ||
|
|
97bb03d703 | ||
|
|
e9f77c1bde | ||
|
|
81cba6c0a8 | ||
|
|
89029df25e | ||
|
|
3463b6818d | ||
|
|
349843e666 | ||
|
|
96ae3bbbba |
@@ -7,10 +7,15 @@ For more details go to [emsesp.org](https://emsesp.org/).
|
|||||||
## Added
|
## Added
|
||||||
|
|
||||||
- comfortpoint for BC400 [#2935](https://github.com/emsesp/EMS-ESP32/issues/2935)
|
- 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
|
## 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
|
## Changed
|
||||||
|
|
||||||
@@ -19,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)
|
- 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)
|
- 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
|
- translated modes `heat` and `eco` for HA-climate mode-str-tpl
|
||||||
|
- support `minflowtemp` and `baseflowtemp` [#2969](https://github.com/emsesp/EMS-ESP32/discussions/2969)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"adapter": "react",
|
"adapter": "react",
|
||||||
"baseLocale": "pl",
|
"baseLocale": "pl",
|
||||||
"$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json"
|
"$schema": "https://unpkg.com/typesafe-i18n@5.27.1/schema/typesafe-i18n.json"
|
||||||
}
|
}
|
||||||
@@ -26,46 +26,46 @@
|
|||||||
"@alova/adapter-xhr": "2.3.1",
|
"@alova/adapter-xhr": "2.3.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@mui/icons-material": "^7.3.7",
|
"@mui/icons-material": "^7.3.9",
|
||||||
"@mui/material": "^7.3.7",
|
"@mui/material": "^7.3.9",
|
||||||
"@preact/compat": "^18.3.1",
|
"@preact/compat": "^18.3.2",
|
||||||
"@table-library/react-table-library": "4.1.15",
|
"@table-library/react-table-library": "4.1.15",
|
||||||
"alova": "3.5.0",
|
"alova": "3.5.1",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"etag": "^1.8.1",
|
"etag": "^1.8.1",
|
||||||
"formidable": "^3.5.4",
|
"formidable": "^3.5.4",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"magic-string": "^0.30.21",
|
"magic-string": "^0.30.21",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"preact": "^10.28.3",
|
"preact": "^10.29.0",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.6.0",
|
||||||
"react-router": "^7.13.0",
|
"react-router": "^7.13.1",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"typesafe-i18n": "^5.26.2",
|
"typesafe-i18n": "^5.27.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.29.0",
|
"@babel/core": "^7.29.0",
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@preact/compat": "^18.3.1",
|
"@preact/compat": "^18.3.2",
|
||||||
"@preact/preset-vite": "^2.10.3",
|
"@preact/preset-vite": "^2.10.3",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
"@types/node": "^25.2.2",
|
"@types/node": "^25.5.0",
|
||||||
"@types/react": "^19.2.13",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"axe-core": "^4.11.1",
|
"axe-core": "^4.11.1",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"eslint": "^10.0.0",
|
"eslint": "^10.0.3",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
"rollup-plugin-visualizer": "^6.0.5",
|
"rollup-plugin-visualizer": "^7.0.1",
|
||||||
"terser": "^5.46.0",
|
"terser": "^5.46.0",
|
||||||
"typescript-eslint": "^8.54.0",
|
"typescript-eslint": "^8.57.0",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-plugin-imagemin": "^0.6.1",
|
"vite-plugin-imagemin": "^0.6.1",
|
||||||
"vite-tsconfig-paths": "^6.1.0"
|
"vite-tsconfig-paths": "^6.1.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.29.2"
|
"packageManager": "pnpm@10.32.1"
|
||||||
}
|
}
|
||||||
|
|||||||
1730
interface/pnpm-lock.yaml
generated
1730
interface/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -111,13 +111,14 @@ const Customizations = () => {
|
|||||||
const [selectedDeviceTypeNameURL, setSelectedDeviceTypeNameURL] =
|
const [selectedDeviceTypeNameURL, setSelectedDeviceTypeNameURL] =
|
||||||
useState<string>(''); // needed for API URL
|
useState<string>(''); // needed for API URL
|
||||||
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
|
const [selectedDeviceName, setSelectedDeviceName] = useState<string>('');
|
||||||
|
const [selectedDeviceBrand, setSelectedDeviceBrand] = useState<string>('');
|
||||||
|
|
||||||
const { send: sendResetCustomizations } = useRequest(resetCustomizations(), {
|
const { send: sendResetCustomizations } = useRequest(resetCustomizations(), {
|
||||||
immediate: false
|
immediate: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const { send: sendDeviceName } = useRequest(
|
const { send: sendDeviceName } = useRequest(
|
||||||
(data: { id: number; name: string }) => writeDeviceName(data),
|
(data: { id: number; name: string; brand: string }) => writeDeviceName(data),
|
||||||
{
|
{
|
||||||
immediate: false
|
immediate: false
|
||||||
}
|
}
|
||||||
@@ -267,6 +268,7 @@ const Customizations = () => {
|
|||||||
if (device) {
|
if (device) {
|
||||||
setSelectedDeviceTypeNameURL(device.url || '');
|
setSelectedDeviceTypeNameURL(device.url || '');
|
||||||
setSelectedDeviceName(device.n);
|
setSelectedDeviceName(device.n);
|
||||||
|
setSelectedDeviceBrand(device.b);
|
||||||
}
|
}
|
||||||
setNumChanges(0);
|
setNumChanges(0);
|
||||||
setRestartNeeded(false);
|
setRestartNeeded(false);
|
||||||
@@ -442,7 +444,11 @@ const Customizations = () => {
|
|||||||
}, [devices, deviceEntities, selectedDevice, sendCustomizationEntities, LL]);
|
}, [devices, deviceEntities, selectedDevice, sendCustomizationEntities, LL]);
|
||||||
|
|
||||||
const renameDevice = useCallback(async () => {
|
const renameDevice = useCallback(async () => {
|
||||||
await sendDeviceName({ id: selectedDevice, name: selectedDeviceName })
|
await sendDeviceName({
|
||||||
|
id: selectedDevice,
|
||||||
|
name: selectedDeviceName,
|
||||||
|
brand: selectedDeviceBrand
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success(LL.UPDATED_OF(LL.NAME(1)));
|
toast.success(LL.UPDATED_OF(LL.NAME(1)));
|
||||||
})
|
})
|
||||||
@@ -453,7 +459,14 @@ const Customizations = () => {
|
|||||||
setRename(false);
|
setRename(false);
|
||||||
await fetchCoreData();
|
await fetchCoreData();
|
||||||
});
|
});
|
||||||
}, [selectedDevice, selectedDeviceName, sendDeviceName, LL, fetchCoreData]);
|
}, [
|
||||||
|
selectedDevice,
|
||||||
|
selectedDeviceName,
|
||||||
|
selectedDeviceBrand,
|
||||||
|
sendDeviceName,
|
||||||
|
LL,
|
||||||
|
fetchCoreData
|
||||||
|
]);
|
||||||
|
|
||||||
const renderDeviceList = () => (
|
const renderDeviceList = () => (
|
||||||
<>
|
<>
|
||||||
@@ -462,15 +475,26 @@ const Customizations = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
|
<Box display="flex" flexWrap="wrap" alignItems="center" gap={2}>
|
||||||
{rename ? (
|
{rename ? (
|
||||||
|
<>
|
||||||
<TextField
|
<TextField
|
||||||
name="device"
|
name="device"
|
||||||
label={LL.EMS_DEVICE()}
|
label={LL.EMS_DEVICE()}
|
||||||
fullWidth
|
style={{ minWidth: '48%' }}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={selectedDeviceName}
|
value={selectedDeviceName}
|
||||||
onChange={(e) => setSelectedDeviceName(e.target.value)}
|
onChange={(e) => setSelectedDeviceName(e.target.value)}
|
||||||
margin="normal"
|
margin="normal"
|
||||||
/>
|
/>
|
||||||
|
<TextField
|
||||||
|
name="brand"
|
||||||
|
label={LL.BRAND()}
|
||||||
|
style={{ minWidth: '48%' }}
|
||||||
|
variant="outlined"
|
||||||
|
value={selectedDeviceBrand}
|
||||||
|
onChange={(e) => setSelectedDeviceBrand(e.target.value)}
|
||||||
|
margin="normal"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<TextField
|
<TextField
|
||||||
name="device"
|
name="device"
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ board_build.filesystem = littlefs
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson @ 7.4.2
|
bblanchon/ArduinoJson @ 7.4.2
|
||||||
ESP32Async/AsyncTCP @ 3.4.10
|
ESP32Async/AsyncTCP @ 3.4.10
|
||||||
ESP32Async/ESPAsyncWebServer @ 3.9.6
|
ESP32Async/ESPAsyncWebServer @ 3.10.1
|
||||||
https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -316,6 +316,10 @@ StateUpdateResult MqttSettings::update(JsonObject root, MqttSettings & settings)
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newSettings.ha_number_mode != settings.ha_number_mode) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (newSettings.entity_format != settings.entity_format) {
|
if (newSettings.entity_format != settings.entity_format) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -852,6 +852,15 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
|
|||||||
return true;
|
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
|
// this is for a specific sensor, return the value
|
||||||
const char * attribute_s = Command::get_attribute(cmd);
|
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
|
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
|
// 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) {
|
void AnalogSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||||
output["name"] = (const char *)sensor.name();
|
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 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);
|
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
||||||
void store_counters();
|
void store_counters();
|
||||||
|
std::string get_metrics_prometheus();
|
||||||
static std::vector<uint8_t> exclude_types() {
|
static std::vector<uint8_t> exclude_types() {
|
||||||
return exclude_types_;
|
return exclude_types_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,21 @@ const char * EMSdevice::tag_to_mqtt(int8_t tag) {
|
|||||||
return (DeviceValue::DeviceValueTAG_mqtt[tag > DeviceValue::NUM_TAGS ? 0 : tag]);
|
return (DeviceValue::DeviceValueTAG_mqtt[tag > DeviceValue::NUM_TAGS ? 0 : tag]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t EMSdevice::tag_to_flag(const uint8_t tag) {
|
||||||
|
if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) {
|
||||||
|
return CommandFlag::CMD_FLAG_HC;
|
||||||
|
} else if (tag >= DeviceValueTAG::TAG_DHW1 && tag <= DeviceValueTAG::TAG_DHW10) {
|
||||||
|
return CommandFlag::CMD_FLAG_DHW;
|
||||||
|
} else if (tag >= DeviceValueTAG::TAG_HS1 && tag <= DeviceValueTAG::TAG_HS16) {
|
||||||
|
return CommandFlag::CMD_FLAG_HS;
|
||||||
|
} else if (tag >= DeviceValueTAG::TAG_AHS1 && tag <= DeviceValueTAG::TAG_AHS1) {
|
||||||
|
return CommandFlag::CMD_FLAG_AHS;
|
||||||
|
} else if (tag >= DeviceValueTAG::TAG_SRC1 && tag <= DeviceValueTAG::TAG_SRC16) {
|
||||||
|
return CommandFlag::CMD_FLAG_SRC;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// convert UOM to a char string - translating only for hours/minutes/seconds
|
// convert UOM to a char string - translating only for hours/minutes/seconds
|
||||||
const char * EMSdevice::uom_to_string(uint8_t uom) {
|
const char * EMSdevice::uom_to_string(uint8_t uom) {
|
||||||
switch (uom) {
|
switch (uom) {
|
||||||
@@ -89,7 +104,10 @@ 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();
|
||||||
|
}
|
||||||
switch (brand_) {
|
switch (brand_) {
|
||||||
case EMSdevice::Brand::BOSCH:
|
case EMSdevice::Brand::BOSCH:
|
||||||
return F_(bosch);
|
return F_(bosch);
|
||||||
@@ -313,15 +331,15 @@ uint8_t EMSdevice::decode_brand(uint8_t value) {
|
|||||||
std::string EMSdevice::to_string() {
|
std::string EMSdevice::to_string() {
|
||||||
// for devices that haven't been lookup yet, don't show all details
|
// for devices that haven't been lookup yet, don't show all details
|
||||||
if (product_id_ == 0) {
|
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) {
|
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_)
|
return brand_to_char() + " " + name() + " (DeviceID:" + Helpers::hextoa(device_id_) + ", ProductID:" + Helpers::itoa(product_id_) + ", Version:" + version_
|
||||||
+ ", Version:" + version_ + ")";
|
+ ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns string of EMS device version and productID
|
// returns string of EMS device version and productID
|
||||||
@@ -332,7 +350,7 @@ std::string EMSdevice::to_string_version() {
|
|||||||
// returns out brand + device name
|
// returns out brand + device name
|
||||||
// translated
|
// translated
|
||||||
std::string EMSdevice::to_string_short() {
|
std::string EMSdevice::to_string_short() {
|
||||||
if (brand_ == Brand::NO_BRAND) {
|
if (brand_ == Brand::NO_BRAND && custom_brand().empty()) {
|
||||||
return std::string(device_type_2_device_name_translated()) + ": " + name();
|
return std::string(device_type_2_device_name_translated()) + ": " + name();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,25 +668,21 @@ void EMSdevice::add_device_value(int8_t tag, // to b
|
|||||||
|
|
||||||
// add a new command if it has a function attached
|
// add a new command if it has a function attached
|
||||||
if (has_cmd) {
|
if (has_cmd) {
|
||||||
uint8_t flags = CommandFlag::ADMIN_ONLY; // executing commands require admin privileges
|
uint8_t flags = CommandFlag::ADMIN_ONLY | tag_to_flag(tag); // executing commands require admin privileges
|
||||||
|
|
||||||
if (tag >= DeviceValueTAG::TAG_HC1 && tag <= DeviceValueTAG::TAG_HC8) {
|
|
||||||
flags |= CommandFlag::CMD_FLAG_HC;
|
|
||||||
} else if (tag >= DeviceValueTAG::TAG_DHW1 && tag <= DeviceValueTAG::TAG_DHW10) {
|
|
||||||
flags |= CommandFlag::CMD_FLAG_DHW;
|
|
||||||
} else if (tag >= DeviceValueTAG::TAG_HS1 && tag <= DeviceValueTAG::TAG_HS16) {
|
|
||||||
flags |= CommandFlag::CMD_FLAG_HS;
|
|
||||||
} else if (tag >= DeviceValueTAG::TAG_AHS1 && tag <= DeviceValueTAG::TAG_AHS1) {
|
|
||||||
flags |= CommandFlag::CMD_FLAG_AHS;
|
|
||||||
} else if (tag >= DeviceValueTAG::TAG_SRC1 && tag <= DeviceValueTAG::TAG_SRC16) {
|
|
||||||
flags |= CommandFlag::CMD_FLAG_SRC;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the command to our library
|
// add the command to our library
|
||||||
Command::add(device_type_, device_id_, short_name, f, fullname, flags);
|
Command::add(device_type_, device_id_, short_name, f, fullname, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EMSdevice::erase_device_values() {
|
||||||
|
for (auto & dv : devicevalues_) {
|
||||||
|
if (dv.has_cmd) {
|
||||||
|
Command::erase_command(device_type_, dv.short_name, tag_to_flag(dv.tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
devicevalues_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// single list of options
|
// single list of options
|
||||||
void EMSdevice::register_device_value(int8_t tag,
|
void EMSdevice::register_device_value(int8_t tag,
|
||||||
void * value_p,
|
void * value_p,
|
||||||
@@ -898,7 +912,7 @@ void EMSdevice::publish_value(void * value_p) const {
|
|||||||
// looks up the UOM for a given key from the device value table
|
// looks up the UOM for a given key from the device value table
|
||||||
std::string EMSdevice::get_value_uom(const std::string & shortname) const {
|
std::string EMSdevice::get_value_uom(const std::string & shortname) const {
|
||||||
for (const auto & dv : devicevalues_) {
|
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
|
// ignore TIME since "minutes" is already added to the string value
|
||||||
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
|
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
|
||||||
break;
|
break;
|
||||||
@@ -1268,7 +1282,7 @@ void EMSdevice::setCustomizationEntity(const std::string & entity_id) {
|
|||||||
// set the min / max
|
// set the min / max
|
||||||
dv.set_custom_minmax();
|
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);
|
set_climate_minmax(dv.tag, dv.min, dv.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2121,7 +2135,7 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
|||||||
|
|
||||||
if (needs_update) {
|
if (needs_update) {
|
||||||
const char * const ** mode_options = nullptr;
|
const char * const ** mode_options = nullptr;
|
||||||
for (auto & d : devicevalues_) {
|
for (const auto & d : devicevalues_) {
|
||||||
// make sure mode in same circuit is DeviceValueType::ENUM
|
// 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)) {
|
if ((d.tag == dv.tag) && (d.type == DeviceValueType::ENUM) && !strcmp(d.short_name, FL_(mode)[0]) && (d.options_size > 0)) {
|
||||||
// get options
|
// get options
|
||||||
@@ -2146,26 +2160,32 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
|||||||
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
||||||
&& !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
&& !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
|
// 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(), brand_to_char(), 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);
|
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||||
create_device_config = false; // only create the main config once
|
create_device_config = false; // only create the main config once
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SRC thermostats mapped to connect/src1/... always contains mode, seltemp, currtemp
|
// 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_(mode)[0])) {
|
if (dv.tag >= DeviceValueTAG::TAG_SRC1 && dv.tag <= DeviceValueTAG::TAG_SRC16 && !strcmp(dv.short_name, FL_(selRoomTemp)[0])) {
|
||||||
// add icon if we have one
|
// add modes and icon if we have one
|
||||||
const char * icon = nullptr;
|
const char * icon = nullptr;
|
||||||
for (auto & d : devicevalues_) {
|
const char * const ** mode_options = nullptr;
|
||||||
if (d.tag == dv.tag && !strcmp(d.short_name, FL_(icon)[0]) && (dv.type == DeviceValueType::ENUM)) {
|
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);
|
uint8_t val = *(uint8_t *)(d.value_p);
|
||||||
if (val != 0 && val < d.options_size) {
|
if (val != 0 && val < d.options_size) {
|
||||||
icon = d.options[val][0];
|
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++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class EMSdevice {
|
|||||||
static const char * tag_to_mqtt(int8_t tag);
|
static const char * tag_to_mqtt(int8_t tag);
|
||||||
static uint8_t decode_brand(uint8_t value);
|
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);
|
static bool export_values(uint8_t device_type, JsonObject output, const int8_t id, const uint8_t output_target);
|
||||||
|
static uint8_t tag_to_flag(const uint8_t tag);
|
||||||
|
|
||||||
// non static functions
|
// non static functions
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ class EMSdevice {
|
|||||||
const char * device_type_2_device_name_translated(); // returns 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_tags(const int8_t tag) const;
|
||||||
bool has_cmd(const char * cmd, const int8_t id) 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();
|
||||||
std::string to_string_short();
|
std::string to_string_short();
|
||||||
std::string to_string_version();
|
std::string to_string_version();
|
||||||
@@ -124,6 +125,14 @@ class EMSdevice {
|
|||||||
return custom_name_;
|
return custom_name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set custom brand
|
||||||
|
void custom_brand(std::string const & custom_brand) {
|
||||||
|
custom_brand_ = custom_brand;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string custom_brand() const {
|
||||||
|
return custom_brand_;
|
||||||
|
}
|
||||||
// set device model
|
// set device model
|
||||||
void model(std::string const & model) {
|
void model(std::string const & model) {
|
||||||
model_ = model;
|
model_ = model;
|
||||||
@@ -282,6 +291,8 @@ class EMSdevice {
|
|||||||
int16_t min,
|
int16_t min,
|
||||||
uint32_t max);
|
uint32_t max);
|
||||||
|
|
||||||
|
void erase_device_values();
|
||||||
|
|
||||||
void
|
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);
|
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);
|
||||||
|
|
||||||
@@ -526,6 +537,7 @@ class EMSdevice {
|
|||||||
char version_[6];
|
char version_[6];
|
||||||
const char * default_name_; // the fixed name the EMS model taken from the device library
|
const char * default_name_; // the fixed name the EMS model taken from the device library
|
||||||
std::string custom_name_ = ""; // custom name
|
std::string custom_name_ = ""; // custom name
|
||||||
|
std::string custom_brand_ = ""; // custom brand
|
||||||
std::string model_ = ""; // model, taken from the 0x01 telegram. see process_deviceName()
|
std::string model_ = ""; // model, taken from the 0x01 telegram. see process_deviceName()
|
||||||
uint8_t flags_ = 0;
|
uint8_t flags_ = 0;
|
||||||
uint8_t brand_ = Brand::NO_BRAND;
|
uint8_t brand_ = Brand::NO_BRAND;
|
||||||
|
|||||||
@@ -1310,6 +1310,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
|||||||
if (product_id == 0 || (*it)->product_id() != 0) { // update only with valid product_id
|
if (product_id == 0 || (*it)->product_id() != 0) { // update only with valid product_id
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
(*it)->erase_device_values();
|
||||||
emsdevices.erase(it); // erase the old device without product_id and re detect
|
emsdevices.erase(it); // erase the old device without product_id and re detect
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1450,6 +1451,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
|
|||||||
if ((e.device_id == device_id) && (e.product_id == product_id)) {
|
if ((e.device_id == device_id) && (e.product_id == product_id)) {
|
||||||
LOG_DEBUG("Have customizations for %s with deviceID 0x%02X productID %d", e.custom_name.c_str(), device_id, product_id);
|
LOG_DEBUG("Have customizations for %s with deviceID 0x%02X productID %d", e.custom_name.c_str(), device_id, product_id);
|
||||||
emsdevices.back()->custom_name(e.custom_name);
|
emsdevices.back()->custom_name(e.custom_name);
|
||||||
|
emsdevices.back()->custom_brand(e.custom_brand);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1760,7 +1762,7 @@ void EMSESP::start() {
|
|||||||
nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
|
nvs_.begin("ems-esp", false, "nvs"); // fallback to small nvs
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("NVS device information: %s", system_.getBBQKeesGatewayDetails().isEmpty() ? "not set" : system_.getBBQKeesGatewayDetails().c_str());
|
LOG_DEBUG("Fuse device information: %s", system_.getBBQKeesGatewayDetails().isEmpty() ? "not set" : system_.getBBQKeesGatewayDetails().c_str());
|
||||||
|
|
||||||
webSettingsService.begin(); // load EMS-ESP Application settings
|
webSettingsService.begin(); // load EMS-ESP Application settings
|
||||||
|
|
||||||
|
|||||||
@@ -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_comfort1, FL_(high_comfort), FL_(eco))
|
||||||
MAKE_ENUM(enum_comfort2, FL_(eco), FL_(high_comfort))
|
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_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_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_maxHeat1, FL_(0kW), FL_(3kW), FL_(6kW), FL_(9kW))
|
||||||
MAKE_ENUM(enum_maxHeat2, 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(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(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(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(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ý")
|
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(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(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(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(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")
|
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(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(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(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(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(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(roominfluence, "roominfluence", "room influence", "Raumeinfluss", "Ruimteinvloed", "Rumspåverkan", "wpływ pomieszczenia", "rominnflytelse", "influence pièce", "oda etkisi", "influenza della camera", "vplyv miestnosti", "vliv prostoru")
|
||||||
|
|||||||
@@ -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
|
// add mf (manufacturer/brand), mdl (model), sw (software version) and via_device
|
||||||
dev_json["mf"] = brand != nullptr ? brand : "EMS-ESP";
|
dev_json["mf"] = brand != nullptr ? brand : "EMS-ESP";
|
||||||
if (model != nullptr) {
|
dev_json["mdl"] = model != nullptr ? model : "EMS-ESP";
|
||||||
dev_json["mdl"] = model;
|
|
||||||
}
|
|
||||||
dev_json["sw"] = version != nullptr ? version : "v" + std::string(EMSESP_APP_VERSION);
|
dev_json["sw"] = version != nullptr ? version : "v" + std::string(EMSESP_APP_VERSION);
|
||||||
dev_json["via_device"] = Mqtt::basename();
|
dev_json["via_device"] = Mqtt::basename();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2952,7 +2952,6 @@ void System::set_valid_system_gpios() {
|
|||||||
// excluded:
|
// excluded:
|
||||||
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
// GPIO3, GPIO45 - GPIO46 = strapping pins
|
||||||
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
// GPIO26 - GPIO32 = SPI flash and PSRAM and not recommended
|
||||||
// GPIO33 - GPIO37 = Octal flash/PSRAM
|
|
||||||
// GPIO19 - GPIO20 = USB-JTAG
|
// GPIO19 - GPIO20 = USB-JTAG
|
||||||
// GPIO22 - GPIO25 = don't exist
|
// GPIO22 - GPIO25 = don't exist
|
||||||
//
|
//
|
||||||
@@ -2960,7 +2959,12 @@ void System::set_valid_system_gpios() {
|
|||||||
// GPIO11 - GPIO19 = ADC analog input only pins
|
// GPIO11 - GPIO19 = ADC analog input only pins
|
||||||
// GPIO47 - GPIO48 = valid on a Wemos S3
|
// GPIO47 - GPIO48 = valid on a Wemos S3
|
||||||
// GPIO8 = used by Liligo S3 board profile for Rx
|
// 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");
|
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
|
#elif CONFIG_IDF_TARGET_ESP32
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/gpio.html
|
// 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;
|
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
|
// this is for a specific sensor
|
||||||
const char * attribute_s = Command::get_attribute(cmd);
|
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
|
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
|
// 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) {
|
void TemperatureSensor::get_value_json(JsonObject output, const Sensor & sensor) {
|
||||||
output["id"] = sensor.id();
|
output["id"] = sensor.id();
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ class TemperatureSensor {
|
|||||||
bool updated_values();
|
bool updated_values();
|
||||||
bool get_value_info(JsonObject output, const char * cmd, const int8_t id = -1);
|
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
|
// return back reference to the sensor list, used by other classes
|
||||||
std::vector<Sensor, AllocatorPSRAM<Sensor>> sensors() const {
|
std::vector<Sensor, AllocatorPSRAM<Sensor>> sensors() const {
|
||||||
return sensors_;
|
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(0xE6, "UBAParametersPlus", true, MAKE_PF_CB(process_UBAParametersPlus));
|
||||||
register_telegram_type(0xE9, "UBAMonitorWWPlus", false, MAKE_PF_CB(process_UBAMonitorWWPlus));
|
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(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(0x28, "WeatherComp", true, MAKE_PF_CB(process_WeatherComp));
|
||||||
register_telegram_type(0x2E0, "UBASetPoints", false, MAKE_PF_CB(process_UBASetPoints2));
|
register_telegram_type(0x2E0, "UBASetPoints", false, MAKE_PF_CB(process_UBASetPoints2));
|
||||||
register_telegram_type(0x2CC, "HPPressure", true, MAKE_PF_CB(process_HpPressure));
|
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, &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, &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
|
* Hybrid heatpump with telegram 0xBB is readable and writeable in boiler and thermostat
|
||||||
* thermostat always overwrites settings in boiler
|
* 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);
|
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
|
// 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) {
|
void Boiler::process_Meters(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_update(telegram, gasMeterHeat_, 0);
|
has_update(telegram, gasMeterHeat_, 0);
|
||||||
@@ -2980,24 +3006,25 @@ bool Boiler::set_reset(const char * value, const int8_t id) {
|
|||||||
|
|
||||||
if (num == 0) {
|
if (num == 0) {
|
||||||
return true; // dash
|
return true; // dash
|
||||||
} else if (num == 1) {
|
} else if (num == 1) { // Reset boiler maintenance message;
|
||||||
// LOG_INFO("Reset boiler maintenance message");
|
|
||||||
write_command(0x05, 8, 0xFF, 0x1C);
|
write_command(0x05, 8, 0xFF, 0x1C);
|
||||||
return true;
|
return true;
|
||||||
} else if (num == 2) {
|
} else if (num == 2) { // Reset boiler error message;
|
||||||
// LOG_INFO("Reset boiler error message");
|
write_command(0x05, 0, 0x5A);
|
||||||
write_command(0x05, 0, 0x5A); // error reset
|
|
||||||
return true;
|
return true;
|
||||||
} else if (num == 3) {
|
} else if (num == 3) { // Reset boiler history
|
||||||
// LOG_INFO("Reset boiler history");
|
write_command(0x05, 42, 0x01);
|
||||||
write_command(0x05, 42, 0x01); // clear history
|
|
||||||
return true;
|
return true;
|
||||||
} else if (num == 4) {
|
} else if (num == 4) {
|
||||||
// LOG_INFO("Reset boiler message");
|
write_command(0x05, 8, 0xFF); // reset messages, same as maintenance reset (1)
|
||||||
write_command(0x05, 8, 0xFF); // same as maintenance
|
|
||||||
return true;
|
return true;
|
||||||
} else if (num == 5) {
|
} else if (num == 5) { // reset Heatpump errors
|
||||||
// LOG_INFO("Factory Reset");
|
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);
|
write_command(0x05, 6, 154);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -3628,4 +3655,32 @@ bool Boiler::set_shutdown(const char * value, const int8_t id) {
|
|||||||
return false;
|
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
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -319,6 +319,9 @@ class Boiler : public EMSdevice {
|
|||||||
int16_t pc1Flow_;
|
int16_t pc1Flow_;
|
||||||
uint8_t pc1Rate_;
|
uint8_t pc1Rate_;
|
||||||
uint8_t pc1On_;
|
uint8_t pc1On_;
|
||||||
|
uint8_t pumpKickHour_; // hour
|
||||||
|
uint8_t pumpKickDay_; // day 1=mo
|
||||||
|
uint16_t pumpKickDelay_; // delay minutes after pump running
|
||||||
|
|
||||||
// HIU
|
// HIU
|
||||||
// uint16_t cwFlowRate_; // cold water flow rate *10
|
// 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_HpFan(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_HpPower2(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_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_Meters(std::shared_ptr<const Telegram> telegram);
|
||||||
void process_Energy(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_nrgHeat(const char * value, const int8_t id);
|
||||||
bool set_nrgWw(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_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
|
} // namespace emsesp
|
||||||
|
|||||||
@@ -129,15 +129,20 @@ void Connect::process_roomThermostat(std::shared_ptr<const Telegram> telegram) {
|
|||||||
}
|
}
|
||||||
has_update(telegram, rc->temp_, 0);
|
has_update(telegram, rc->temp_, 0);
|
||||||
has_update(telegram, rc->humidity_, 2); // could show -3 if not set
|
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);
|
has_update(telegram, rc->seltemp_, 3);
|
||||||
|
}
|
||||||
|
|
||||||
// calculate dew temperature
|
// calculate dew temperature
|
||||||
|
if (rc->humidity_ >= 0 && rc->humidity_ <= 100) {
|
||||||
const float k2 = 17.62;
|
const float k2 = 17.62;
|
||||||
const float k3 = 243.12;
|
const float k3 = 243.12;
|
||||||
const float t = (float)rc->temp_ / 10;
|
const float t = (float)rc->temp_ / 10;
|
||||||
const float h = (float)rc->humidity_ / 100;
|
const float h = (float)rc->humidity_ / 100;
|
||||||
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
|
int16_t dt = (10 * k3 * (((k2 * t) / (k3 + t)) + log(h)) / (((k2 * k3) / (k3 + t)) - log(h)));
|
||||||
has_update(rc->dewtemp_, dt);
|
has_update(rc->dewtemp_, dt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// gateway(0x48) W gateway(0x50), ?(0x0B42), data: 01 // icon in offset 0
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
uint8_t v;
|
uint8_t v;
|
||||||
if (Helpers::value2enum(value, v, FL_(enum_mode2), {3, 1, 0})) {
|
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_mode_ha), {3, 1, 0})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
write_command(0xBB5 + rc->room(), 0, v); // no validate, mode change is broadcasted
|
write_command(0xBB5 + rc->room(), 0, v); // no validate, mode change is broadcasted
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connect::set_seltemp(const char * value, const int8_t id) {
|
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;
|
float v;
|
||||||
if (Helpers::value2float(value, v)) {
|
if (Helpers::value2float(value, v)) {
|
||||||
// write_command(0xBB5 + rc->room(), rc->mode_ == 2 ? 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_ == 0 ? 1 : 3, v == -1 ? 0xFF : uint8_t(v * 2));
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1101,6 +1101,8 @@ void Thermostat::process_JunkersWW(std::shared_ptr<const Telegram> telegram) {
|
|||||||
void Thermostat::process_JunkersDisp(std::shared_ptr<const Telegram> telegram) {
|
void Thermostat::process_JunkersDisp(std::shared_ptr<const Telegram> telegram) {
|
||||||
has_enumupdate(telegram, ibaMainDisplay_, 1, 1);
|
has_enumupdate(telegram, ibaMainDisplay_, 1, 1);
|
||||||
has_update(telegram, ibaLanguage_, 3);
|
has_update(telegram, ibaLanguage_, 3);
|
||||||
|
has_update(telegram, ibaMinExtTemperature_, 16);
|
||||||
|
has_update(telegram, ibaBuildingType_, 17); // percent /10
|
||||||
}
|
}
|
||||||
|
|
||||||
// type 0x02A5 - data from Worchester CRF200
|
// type 0x02A5 - data from Worchester CRF200
|
||||||
@@ -1250,12 +1252,12 @@ void Thermostat::process_RC300Summer(std::shared_ptr<const Telegram> telegram) {
|
|||||||
|
|
||||||
if (hc->heatingtype != 3) {
|
if (hc->heatingtype != 3) {
|
||||||
has_update(telegram, hc->designtemp, 4);
|
has_update(telegram, hc->designtemp, 4);
|
||||||
has_update(telegram, hc->minflowtemp, model() == EMSdevice::EMS_DEVICE_FLAG_BC400 ? 13 : 8);
|
|
||||||
} else {
|
} else {
|
||||||
has_update(telegram, hc->designtemp, 5);
|
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->fastHeatup, 10);
|
||||||
has_update(telegram, hc->comfortPointOffset, 11);
|
has_update(telegram, hc->comfortPointOffset, 11);
|
||||||
has_update(telegram, hc->comfortPointTemp, 12);
|
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);
|
write_command(0x241, 10, mt, 0x241);
|
||||||
} else if (isRC300()) {
|
} else if (isRC300()) {
|
||||||
write_command(0x240, 10, mt, 0x240);
|
write_command(0x240, 10, mt, 0x240);
|
||||||
|
} else if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS) {
|
||||||
|
write_command(0x110, 16, mt, 0x110);
|
||||||
} else {
|
} else {
|
||||||
write_command(EMS_TYPE_IBASettings, 5, mt, EMS_TYPE_IBASettings);
|
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
|
// 0xA5/0xA7 - Set the building settings
|
||||||
bool Thermostat::set_building(const char * value, const int8_t id) {
|
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;
|
uint8_t bd;
|
||||||
if (!Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) {
|
if (!Helpers::value2enum(value, bd, FL_(enum_ibaBuildingType))) {
|
||||||
return false;
|
return false;
|
||||||
@@ -2326,6 +2338,7 @@ bool Thermostat::set_control(const char * value, const int8_t id) {
|
|||||||
// 1-FB10, 2-FB100
|
// 1-FB10, 2-FB100
|
||||||
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS && !has_flags(EMSdevice::EMS_DEVICE_FLAG_JUNKERS_OLD)) {
|
if (model() == EMSdevice::EMS_DEVICE_FLAG_JUNKERS && !has_flags(EMSdevice::EMS_DEVICE_FLAG_JUNKERS_OLD)) {
|
||||||
if (Helpers::value2enum(value, ctrl, FL_(enum_j_control))) {
|
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);
|
write_command(set_typeids[hc->hc()], 1, ctrl);
|
||||||
return true;
|
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))) {
|
} else if (Helpers::value2enum(value, ctrl, FL_(enum_control))) {
|
||||||
write_command(set_typeids[hc->hc()], EMS_OFFSET_RC35Set_control, ctrl);
|
write_command(set_typeids[hc->hc()], EMS_OFFSET_RC35Set_control, ctrl);
|
||||||
|
hc->control = ctrl; // set in advance, dont wait for verify
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4074,10 +4088,16 @@ bool Thermostat::set_temperature(const float temperature, const uint8_t mode, co
|
|||||||
}
|
}
|
||||||
factor = 1;
|
factor = 1;
|
||||||
break;
|
break;
|
||||||
|
case HeatingCircuit::Mode::BASEFLOW:
|
||||||
|
set_typeid = summer_typeids[hc->hc()];
|
||||||
|
validate_typeid = set_typeid;
|
||||||
|
offset = 8;
|
||||||
|
factor = 1;
|
||||||
|
break;
|
||||||
case HeatingCircuit::Mode::MINFLOW:
|
case HeatingCircuit::Mode::MINFLOW:
|
||||||
set_typeid = summer_typeids[hc->hc()];
|
set_typeid = summer_typeids[hc->hc()];
|
||||||
validate_typeid = set_typeid;
|
validate_typeid = set_typeid;
|
||||||
offset = hc->heatingtype != 3 && model == EMS_DEVICE_FLAG_BC400 ? 13 : 8;
|
offset = 13;
|
||||||
factor = 1;
|
factor = 1;
|
||||||
break;
|
break;
|
||||||
case HeatingCircuit::Mode::MAXFLOW:
|
case HeatingCircuit::Mode::MAXFLOW:
|
||||||
@@ -4730,6 +4750,19 @@ void Thermostat::register_device_values() {
|
|||||||
DeviceValueUOM::NONE,
|
DeviceValueUOM::NONE,
|
||||||
MAKE_CF_CB(set_language));
|
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,
|
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
|
||||||
&hybridStrategy_,
|
&hybridStrategy_,
|
||||||
DeviceValueType::ENUM,
|
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->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->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->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->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->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));
|
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 daymidtemp;
|
||||||
uint8_t nighttemp;
|
uint8_t nighttemp;
|
||||||
uint8_t holidaytemp;
|
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 targetflowtemp;
|
||||||
uint8_t summertemp;
|
uint8_t summertemp;
|
||||||
int8_t nofrosttemp; // signed -20°C to +10°C
|
int8_t nofrosttemp; // signed -20°C to +10°C
|
||||||
@@ -65,6 +65,7 @@ class Thermostat : public EMSdevice {
|
|||||||
int16_t curroominfl;
|
int16_t curroominfl;
|
||||||
uint8_t flowtempoffset;
|
uint8_t flowtempoffset;
|
||||||
uint8_t minflowtemp;
|
uint8_t minflowtemp;
|
||||||
|
uint8_t baseflowtemp;
|
||||||
uint8_t maxflowtemp;
|
uint8_t maxflowtemp;
|
||||||
uint8_t reducemode;
|
uint8_t reducemode;
|
||||||
uint8_t nofrostmode;
|
uint8_t nofrostmode;
|
||||||
@@ -169,6 +170,7 @@ class Thermostat : public EMSdevice {
|
|||||||
DAYMID,
|
DAYMID,
|
||||||
COOLTEMP,
|
COOLTEMP,
|
||||||
COOLSTART,
|
COOLSTART,
|
||||||
|
BASEFLOW,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -583,6 +585,9 @@ class Thermostat : public EMSdevice {
|
|||||||
inline bool set_minflowtemp(const char * value, const int8_t id) {
|
inline bool set_minflowtemp(const char * value, const int8_t id) {
|
||||||
return set_temperature_value(value, id, HeatingCircuit::Mode::MINFLOW);
|
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) {
|
inline bool set_roominfluence(const char * value, const int8_t id) {
|
||||||
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE, true);
|
return set_temperature_value(value, id, HeatingCircuit::Mode::ROOMINFLUENCE, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#define EMSESP_APP_VERSION "3.8.2-dev.3"
|
#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;
|
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
|
// specific value info
|
||||||
const char * attribute_s = Command::get_attribute(cmd);
|
const char * attribute_s = Command::get_attribute(cmd);
|
||||||
for (auto const & entity : *customEntityItems_) {
|
for (auto const & entity : *customEntityItems_) {
|
||||||
@@ -354,6 +363,54 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
|||||||
return false; // not found
|
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
|
// build the json for specific entity
|
||||||
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem const & entity) {
|
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem const & entity) {
|
||||||
output["name"] = (const char *)entity.name;
|
output["name"] = (const char *)entity.name;
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
|
|||||||
void show_values(JsonObject output);
|
void show_values(JsonObject output);
|
||||||
void generate_value_web(JsonObject output, const bool is_dashboard = false);
|
void generate_value_web(JsonObject output, const bool is_dashboard = false);
|
||||||
|
|
||||||
|
std::string get_metrics_prometheus();
|
||||||
|
|
||||||
uint8_t count_entities();
|
uint8_t count_entities();
|
||||||
void ha_reset() {
|
void ha_reset() {
|
||||||
ha_configdone_ = false;
|
ha_configdone_ = false;
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ void WebCustomization::read(WebCustomization & customizations, JsonObject root)
|
|||||||
entityJson["product_id"] = entityCustomization.product_id;
|
entityJson["product_id"] = entityCustomization.product_id;
|
||||||
entityJson["device_id"] = entityCustomization.device_id;
|
entityJson["device_id"] = entityCustomization.device_id;
|
||||||
entityJson["custom_name"] = entityCustomization.custom_name;
|
entityJson["custom_name"] = entityCustomization.custom_name;
|
||||||
|
entityJson["custom_brand"] = entityCustomization.custom_brand;
|
||||||
|
|
||||||
// entries are in the form <XX><shortname>[optional customname] e.g "08heatingactive|heating is on"
|
// entries are in the form <XX><shortname>[optional customname] e.g "08heatingactive|heating is on"
|
||||||
JsonArray masked_entityJson = entityJson["entity_ids"].to<JsonArray>();
|
JsonArray masked_entityJson = entityJson["entity_ids"].to<JsonArray>();
|
||||||
@@ -134,7 +135,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
|
|||||||
analog.type = analogJson["type"];
|
analog.type = analogJson["type"];
|
||||||
analog.is_system = analogJson["is_system"] | false;
|
analog.is_system = analogJson["is_system"] | false;
|
||||||
if (_start && analog.type == EMSESP::analogsensor_.AnalogType::DIGITAL_OUT && analog.uom > DeviceValue::DeviceValueUOM::NONE) {
|
if (_start && analog.type == EMSESP::analogsensor_.AnalogType::DIGITAL_OUT && analog.uom > DeviceValue::DeviceValueUOM::NONE) {
|
||||||
analog.offset = analog.uom - 1;
|
analog.offset = analog.uom > 1 ? 1 : 0;
|
||||||
}
|
}
|
||||||
customizations.analogCustomizations.push_back(analog); // add to list
|
customizations.analogCustomizations.push_back(analog); // add to list
|
||||||
}
|
}
|
||||||
@@ -150,6 +151,7 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
|
|||||||
emsEntity.product_id = masked_entity["product_id"];
|
emsEntity.product_id = masked_entity["product_id"];
|
||||||
emsEntity.device_id = masked_entity["device_id"];
|
emsEntity.device_id = masked_entity["device_id"];
|
||||||
emsEntity.custom_name = masked_entity["custom_name"] | "";
|
emsEntity.custom_name = masked_entity["custom_name"] | "";
|
||||||
|
emsEntity.custom_brand = masked_entity["custom_brand"] | "";
|
||||||
|
|
||||||
auto masked_entity_ids = masked_entity["entity_ids"].as<JsonArray>();
|
auto masked_entity_ids = masked_entity["entity_ids"].as<JsonArray>();
|
||||||
for (const JsonVariant masked_entity_id : masked_entity_ids) {
|
for (const JsonVariant masked_entity_id : masked_entity_ids) {
|
||||||
@@ -244,7 +246,8 @@ void WebCustomizationService::writeDeviceName(AsyncWebServerRequest * request, J
|
|||||||
if (emsdevice->unique_id() == unique_device_id) {
|
if (emsdevice->unique_id() == unique_device_id) {
|
||||||
uint8_t product_id = emsdevice->product_id();
|
uint8_t product_id = emsdevice->product_id();
|
||||||
uint8_t device_id = emsdevice->device_id();
|
uint8_t device_id = emsdevice->device_id();
|
||||||
auto custom_name = json["name"].as<std::string>();
|
std::string custom_name = json["name"] | "";
|
||||||
|
std::string custom_brand = json["brand"] | "";
|
||||||
|
|
||||||
// updates current record or creates a new one
|
// updates current record or creates a new one
|
||||||
bool entry_exists = false;
|
bool entry_exists = false;
|
||||||
@@ -252,6 +255,7 @@ void WebCustomizationService::writeDeviceName(AsyncWebServerRequest * request, J
|
|||||||
for (auto it = settings.entityCustomizations.begin(); it != settings.entityCustomizations.end();) {
|
for (auto it = settings.entityCustomizations.begin(); it != settings.entityCustomizations.end();) {
|
||||||
if ((*it).product_id == product_id && (*it).device_id == device_id) {
|
if ((*it).product_id == product_id && (*it).device_id == device_id) {
|
||||||
(*it).custom_name = custom_name;
|
(*it).custom_name = custom_name;
|
||||||
|
(*it).custom_brand = custom_brand;
|
||||||
entry_exists = true;
|
entry_exists = true;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@@ -265,6 +269,7 @@ void WebCustomizationService::writeDeviceName(AsyncWebServerRequest * request, J
|
|||||||
new_entry.product_id = product_id;
|
new_entry.product_id = product_id;
|
||||||
new_entry.device_id = device_id;
|
new_entry.device_id = device_id;
|
||||||
new_entry.custom_name = custom_name;
|
new_entry.custom_name = custom_name;
|
||||||
|
new_entry.custom_brand = custom_brand;
|
||||||
settings.entityCustomizations.push_back(new_entry);
|
settings.entityCustomizations.push_back(new_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +278,7 @@ void WebCustomizationService::writeDeviceName(AsyncWebServerRequest * request, J
|
|||||||
|
|
||||||
// update the EMS Device record real-time
|
// update the EMS Device record real-time
|
||||||
emsdevice->custom_name(custom_name);
|
emsdevice->custom_name(custom_name);
|
||||||
|
emsdevice->custom_brand(custom_brand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -463,6 +469,7 @@ void WebCustomizationService::load_test_data() {
|
|||||||
emsEntity.product_id = 123;
|
emsEntity.product_id = 123;
|
||||||
emsEntity.device_id = 8;
|
emsEntity.device_id = 8;
|
||||||
emsEntity.custom_name = "My Custom Boiler";
|
emsEntity.custom_name = "My Custom Boiler";
|
||||||
|
emsEntity.custom_brand = "";
|
||||||
emsEntity.entity_ids.push_back("08heatingactive|is my heating on?");
|
emsEntity.entity_ids.push_back("08heatingactive|is my heating on?");
|
||||||
emsEntity.entity_ids.push_back("08tapwateractive");
|
emsEntity.entity_ids.push_back("08tapwateractive");
|
||||||
emsEntity.entity_ids.push_back("08selflowtemp|<90");
|
emsEntity.entity_ids.push_back("08selflowtemp|<90");
|
||||||
@@ -472,6 +479,7 @@ void WebCustomizationService::load_test_data() {
|
|||||||
for (const auto & emsdevice : EMSESP::emsdevices) {
|
for (const auto & emsdevice : EMSESP::emsdevices) {
|
||||||
if (emsdevice->is_device_id(emsEntity.device_id)) {
|
if (emsdevice->is_device_id(emsEntity.device_id)) {
|
||||||
emsdevice->custom_name(emsEntity.custom_name);
|
emsdevice->custom_name(emsEntity.custom_name);
|
||||||
|
emsdevice->custom_brand(emsEntity.custom_brand);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class EntityCustomization {
|
|||||||
uint8_t product_id; // device's product id
|
uint8_t product_id; // device's product id
|
||||||
uint8_t device_id; // device's device id
|
uint8_t device_id; // device's device id
|
||||||
std::string custom_name; // custom device name
|
std::string custom_name; // custom device name
|
||||||
|
std::string custom_brand; // custom brand name
|
||||||
std::vector<std::string> entity_ids; // array of entity ids with masks and optional custom fullname
|
std::vector<std::string> entity_ids; // array of entity ids with masks and optional custom fullname
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ StateUpdateResult WebScheduler::update(JsonObject root, WebScheduler & webSchedu
|
|||||||
if (webScheduler.scheduleItems.back().name[0] != '\0') {
|
if (webScheduler.scheduleItems.back().name[0] != '\0') {
|
||||||
char key[sizeof(webScheduler.scheduleItems.back().name) + 2];
|
char key[sizeof(webScheduler.scheduleItems.back().name) + 2];
|
||||||
snprintf(key, sizeof(key), "s:%s", webScheduler.scheduleItems.back().name);
|
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);
|
webScheduler.scheduleItems.back().active = EMSESP::nvs_.getBool(key);
|
||||||
}
|
}
|
||||||
Command::add(
|
Command::add(
|
||||||
@@ -138,20 +138,11 @@ bool WebSchedulerService::command_setvalue(const char * value, const int8_t id,
|
|||||||
publish();
|
publish();
|
||||||
}
|
}
|
||||||
// save new state to nvs #2946
|
// save new state to nvs #2946
|
||||||
|
if (scheduleItem.flags != SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
||||||
char key[sizeof(scheduleItem.name) + 2];
|
char key[sizeof(scheduleItem.name) + 2];
|
||||||
snprintf(key, sizeof(key), "s:%s", scheduleItem.name);
|
snprintf(key, sizeof(key), "s:%s", scheduleItem.name);
|
||||||
EMSESP::nvs_.putBool(key, scheduleItem.active);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,6 +175,15 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
|||||||
return true;
|
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);
|
const char * attribute_s = Command::get_attribute(cmd);
|
||||||
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
|
||||||
if (Helpers::toLower(scheduleItem.name) == cmd) {
|
if (Helpers::toLower(scheduleItem.name) == cmd) {
|
||||||
@@ -195,6 +195,21 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
|
|||||||
return false; // not found
|
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
|
// build the json for specific entity
|
||||||
void WebSchedulerService::get_value_json(JsonObject output, const ScheduleItem & scheduleItem) {
|
void WebSchedulerService::get_value_json(JsonObject output, const ScheduleItem & scheduleItem) {
|
||||||
output["name"] = (const char *)scheduleItem.name;
|
output["name"] = (const char *)scheduleItem.name;
|
||||||
@@ -483,6 +498,10 @@ void WebSchedulerService::loop() {
|
|||||||
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
if (scheduleItem.active && scheduleItem.flags == SCHEDULEFLAG_SCHEDULE_IMMEDIATE) {
|
||||||
command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str()));
|
command(scheduleItem.name, scheduleItem.cmd.c_str(), compute(scheduleItem.value.c_str()));
|
||||||
scheduleItem.active = false;
|
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);
|
uint8_t count_entities(bool cmd_only = false);
|
||||||
bool onChange(const char * cmd);
|
bool onChange(const char * cmd);
|
||||||
|
|
||||||
|
std::string get_metrics_prometheus();
|
||||||
|
|
||||||
std::string raw_value;
|
std::string raw_value;
|
||||||
std::string computed_value;
|
std::string computed_value;
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ void test_2() {
|
|||||||
"off\",\"heatingtemp\":\"heating temperature\",\"maintenance\":\"maintenance scheduled\",\"maintenancedate\":\"next maintenance "
|
"off\",\"heatingtemp\":\"heating temperature\",\"maintenance\":\"maintenance scheduled\",\"maintenancedate\":\"next maintenance "
|
||||||
"date\",\"maintenancetime\":\"time to next maintenance\",\"nofrostmode\":\"nofrost mode\",\"nofrosttemp\":\"nofrost "
|
"date\",\"maintenancetime\":\"time to next maintenance\",\"nofrostmode\":\"nofrost mode\",\"nofrosttemp\":\"nofrost "
|
||||||
"temperature\",\"nompower\":\"nominal Power\",\"nrgheat\":\"energy heating\",\"pumpcharacter\":\"boiler pump characteristic\",\"pumpdelay\":\"pump "
|
"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 "
|
"logic temperature\",\"reset\":\"reset\",\"selburnpow\":\"burner selected max power\",\"selflowtemp\":\"selected flow "
|
||||||
"temperature\",\"summertemp\":\"summer temperature\"}]";
|
"temperature\",\"summertemp\":\"summer temperature\"}]";
|
||||||
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/commands"));
|
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/commands"));
|
||||||
|
|||||||
Reference in New Issue
Block a user