43 Commits
test ... patch

Author SHA1 Message Date
Proddy
0f6ad533f6 Merge pull request #1157 from MichaelDvP/patch
Some fixes to patch
2023-04-12 19:50:11 +02:00
MichaelDvP
4ae68fc3fd fix esp32s3 upload 2023-04-11 11:21:49 +02:00
MichaelDvP
b97a9e3e5b mqtt resend timeout from 10 min to 10 sec 2023-04-11 11:20:34 +02:00
MichaelDvP
e79e104837 fix nofrost mode enum order 2023-04-11 11:19:19 +02:00
MichaelDvP
7851e83162 fix heartbeat interval, #1131 2023-04-11 11:18:24 +02:00
MichaelDvP
389f0bd2e8 rebuild HA-mqtt on language change 2023-04-11 11:17:20 +02:00
MichaelDvP
a4bbbb460a add Greenstar HIU 2023-04-11 11:16:02 +02:00
Proddy
d01697a065 Merge pull request #1115 from proddy/patch
Patch
2023-03-11 17:06:19 +01:00
Proddy
a027d17a7d ready for 3.5.2 2023-03-11 16:07:18 +01:00
Proddy
a27787d0d5 update doc 2023-03-11 15:47:07 +01:00
Proddy
24baf844fd Merge pull request #1111 from MichaelDvP/patch
Last minute fix
2023-03-10 16:52:59 +05:30
MichaelDvP
6f125b7fbb Merge branch 'emsesp:patch' into patch 2023-03-10 12:09:54 +01:00
Proddy
51b00cb280 Merge pull request #1110 from proddy/patch
fix binary_sensor mqtt topic for sensors
2023-03-10 15:33:47 +05:30
Proddy
31ffa8483e fix binary_sensor mqtt topic for sensors 2023-03-10 11:03:36 +01:00
MichaelDvP
e7cbd97662 Merge branch 'patch' of https://github.com/MichaelDvP/EMS-ESP32 into patch 2023-03-07 07:16:36 +01:00
MichaelDvP
0b0ca1efd1 fix avty 2023-03-07 07:15:49 +01:00
Proddy
4518396833 Merge pull request #1100 from MichaelDvP/patch
fix rc35 hc active detection
2023-03-04 16:02:15 +01:00
MichaelDvP
8de6448b74 fix rc35 hc active detection 2023-03-04 11:13:05 +01:00
Proddy
6f1b65e70b Merge pull request #1089 from proddy/patch
show device type name in dialog
2023-02-28 17:56:52 +01:00
Proddy
b93890440a show device type name in dialog 2023-02-28 17:56:24 +01:00
Proddy
7cee4916a2 Merge pull request #1086 from MichaelDvP/patch
patches
2023-02-28 08:43:35 +01:00
MichaelDvP
3e7b9a7dd9 Merge patch 2023-02-28 07:32:19 +01:00
Proddy
069c63b55a Update version.h 2023-02-27 15:17:58 +01:00
MichaelDvP
e9bd1d4b81 set heatingTemp, burnMaxPower,... 2023-02-24 16:40:24 +01:00
MichaelDvP
1fa1ee5b24 add option for start boiler with forced heating off 2023-02-24 15:52:16 +01:00
MichaelDvP
8800b88f62 new entity forceHeatingOff sends every minute 0x1A 2023-02-24 11:04:43 +01:00
MichaelDvP
fd6df7279b no ha_entitiy_config remove 2023-02-23 10:33:19 +01:00
MichaelDvP
0dceb25569 Merge branch 'dev' of https://github.com/pswid/EMS-ESP32 into dev_ 2023-02-23 09:17:29 +01:00
MichaelDvP
96d5324945 solar valve VS1 2023-02-23 09:04:21 +01:00
pswid
971df73f13 added "availability" section in HA Discovery
This will allow to not remove discovery topics during each ems-esp restart (MQTT init), so it can solve issue #910.

After applying this fix (and removing from the code commands that delete discovery topics) HA no longer reports errors/warnings in the log. I'm testing if for over a week.
Now it is enough to delete discovery topics only when it is really needed (e.g. the entity has been removed by customization, discovery prefix has been changed or the HA option has been disabled in the configuration).
2023-02-22 13:16:05 +01:00
MichaelDvP
24a4cb85ff analogsensor HA discovery: types on remove 2023-02-21 15:20:30 +01:00
MichaelDvP
7f9582d01a no restart for change entity-language 2023-02-21 15:17:41 +01:00
MichaelDvP
dfa5e23e90 HA config recreate #1067 2023-02-21 13:56:31 +01:00
MichaelDvP
016e18002c recreate HA discovery by device 2023-02-21 07:44:43 +01:00
MichaelDvP
e0b89ae3ed Updatecheck to flash, not to filesize 2023-02-21 07:41:01 +01:00
MichaelDvP
daad2ffe6c fix flowTemp, etc for HM200, #500 2023-02-18 11:45:29 +01:00
MichaelDvP
a9caadaf5e add hm200 entities #500 2023-02-18 10:42:18 +01:00
MichaelDvP
0d8d750e46 fix mqtt-discovery for analog sensor commands #1035 2023-02-17 08:40:46 +01:00
MichaelDvP
7a394c8e89 add Tado thermostat, old version device 0x19. no entities, no broadcast 2023-02-15 10:04:44 +01:00
MichaelDvP
bcf83616f8 changelog, version 3.5.1-dev.0 2023-02-12 17:30:34 +01:00
MichaelDvP
32bf13ca9a fix #954, write selflowtemp with same value 2023-02-12 17:30:00 +01:00
MichaelDvP
608500e417 fix RC30_N hc detection #786 2023-02-12 17:28:44 +01:00
MichaelDvP
bfe498758c weblog buffer without large json 2023-02-12 17:27:23 +01:00
40 changed files with 14020 additions and 564 deletions

View File

@@ -1,19 +1,20 @@
# Changelog # Changelog
# [3.6.0] # [3.5.1]
## **IMPORTANT! BREAKING CHANGES**
-
## Added ## Added
- - Detect old Tado thermostat, device-id 0x19, no entities
- Some more HM200 entities [#500](https://github.com/emsesp/EMS-ESP32/issues/500)
- Add entity to force heating off (for systems without thermostat) [#951](https://github.com/emsesp/EMS-ESP32/issues/951)
## Fixed ## Fixed
- - HA-discovery for analog sensor commands [#1035](https://github.com/emsesp/EMS-ESP32/issues/1035)
## Changed ## Changed
- - Use byte 0 for detection RC30 active heatingcircuit [#786](https://github.com/emsesp/EMS-ESP32/issues/786)
- Write repeated selflowtemp if tx-queue is empty without verify [#954](https://github.com/emsesp/EMS-ESP32/issues/954)
- HA discovery recreate after disconnect by device [#1067](https://github.com/emsesp/EMS-ESP32/issues/1067)
- File upload: check flash size (overflow) instead of filesize

14038
interface/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,15 +8,15 @@
"@emotion/styled": "^11.10.6", "@emotion/styled": "^11.10.6",
"@msgpack/msgpack": "^2.8.0", "@msgpack/msgpack": "^2.8.0",
"@mui/icons-material": "^5.11.9", "@mui/icons-material": "^5.11.9",
"@mui/material": "^5.11.9", "@mui/material": "^5.11.10",
"@table-library/react-table-library": "4.0.25", "@table-library/react-table-library": "4.0.26",
"@types/lodash": "^4.14.191", "@types/lodash": "^4.14.191",
"@types/node": "^18.14.0", "@types/node": "^18.14.0",
"@types/react": "^18.0.28", "@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"axios": "^1.3.3", "axios": "^1.3.4",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"notistack": "^2.0.8", "notistack": "^2.0.8",

View File

@@ -73,7 +73,6 @@ const SystemLog: FC = () => {
}); });
const [errorMessage, setErrorMessage] = useState<string>(); const [errorMessage, setErrorMessage] = useState<string>();
const [reconnectTimeout, setReconnectTimeout] = useState<NodeJS.Timeout>();
const [logEntries, setLogEntries] = useState<LogEntries>({ events: [] }); const [logEntries, setLogEntries] = useState<LogEntries>({ events: [] });
const [lastIndex, setLastIndex] = useState<number>(0); const [lastIndex, setLastIndex] = useState<number>(0);
@@ -162,7 +161,7 @@ const SystemLog: FC = () => {
const fetchLog = useCallback(async () => { const fetchLog = useCallback(async () => {
try { try {
setLogEntries((await SystemApi.readLogEntries()).data); await SystemApi.readLogEntries();
} catch (error) { } catch (error) {
setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING())); setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING()));
} }
@@ -176,20 +175,14 @@ const SystemLog: FC = () => {
const es = new EventSource(addAccessTokenParameter(LOG_EVENTSOURCE_URL)); const es = new EventSource(addAccessTokenParameter(LOG_EVENTSOURCE_URL));
es.onmessage = onMessage; es.onmessage = onMessage;
es.onerror = () => { es.onerror = () => {
if (reconnectTimeout) {
es.close(); es.close();
setReconnectTimeout(setTimeout(reloadPage, 1000)); reloadPage();
}
}; };
return () => { return () => {
es.close(); es.close();
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
}; };
// eslint-disable-next-line // eslint-disable-next-line
}, [reconnectTimeout]); }, []);
const content = () => { const content = () => {
if (!data) { if (!data) {

View File

@@ -127,6 +127,7 @@ const de: Translation = {
BYPASS_TOKEN: 'Zugriffstoken-Autorisierung bei API-Aufrufen umgehen', BYPASS_TOKEN: 'Zugriffstoken-Autorisierung bei API-Aufrufen umgehen',
READONLY: 'Nur-Lese-Modus aktivieren (blockiert alle ausgehenden EMS Tx Write-Befehle)', READONLY: 'Nur-Lese-Modus aktivieren (blockiert alle ausgehenden EMS Tx Write-Befehle)',
UNDERCLOCK_CPU: 'CPU-Geschwindigkeit untertakten', UNDERCLOCK_CPU: 'CPU-Geschwindigkeit untertakten',
HEATINGOFF: 'Boiler Start mit Heizung ausgeschaltet',
ENABLE_SHOWER_TIMER: 'Duschtimer aktivieren', ENABLE_SHOWER_TIMER: 'Duschtimer aktivieren',
ENABLE_SHOWER_ALERT: 'Duschalarm aktivieren', ENABLE_SHOWER_ALERT: 'Duschalarm aktivieren',
TRIGGER_TIME: 'Auslösezeit', TRIGGER_TIME: 'Auslösezeit',

View File

@@ -127,6 +127,7 @@ const en: Translation = {
BYPASS_TOKEN: 'Bypass Access Token authorization on API calls', BYPASS_TOKEN: 'Bypass Access Token authorization on API calls',
READONLY: 'Enable read-only mode (blocks all outgoing EMS Tx Write commands)', READONLY: 'Enable read-only mode (blocks all outgoing EMS Tx Write commands)',
UNDERCLOCK_CPU: 'Underclock CPU speed', UNDERCLOCK_CPU: 'Underclock CPU speed',
HEATINGOFF: 'Start boiler with forced heating off',
ENABLE_SHOWER_TIMER: 'Enable Shower Timer', ENABLE_SHOWER_TIMER: 'Enable Shower Timer',
ENABLE_SHOWER_ALERT: 'Enable Shower Alert', ENABLE_SHOWER_ALERT: 'Enable Shower Alert',
TRIGGER_TIME: 'Trigger Time', TRIGGER_TIME: 'Trigger Time',

View File

@@ -127,6 +127,7 @@ const fr: Translation = {
BYPASS_TOKEN: 'Contourner l\'autorisation du jeton d\'accès sur les appels API', BYPASS_TOKEN: 'Contourner l\'autorisation du jeton d\'accès sur les appels API',
READONLY: 'Activer le mode lecture uniquement (bloque toutes les commandes EMS sortantes en écriture Tx)', READONLY: 'Activer le mode lecture uniquement (bloque toutes les commandes EMS sortantes en écriture Tx)',
UNDERCLOCK_CPU: 'Underclock du CPU', UNDERCLOCK_CPU: 'Underclock du CPU',
HEATINGOFF: 'Start boiler with forced heating off',
ENABLE_SHOWER_TIMER: 'Activer la minuterie de la douche', ENABLE_SHOWER_TIMER: 'Activer la minuterie de la douche',
ENABLE_SHOWER_ALERT: 'Activer les alertes de durée de douche', ENABLE_SHOWER_ALERT: 'Activer les alertes de durée de douche',
TRIGGER_TIME: 'Durée avant déclenchement', TRIGGER_TIME: 'Durée avant déclenchement',

View File

@@ -127,6 +127,7 @@ const nl: Translation = {
BYPASS_TOKEN: 'API Access Token authenticatie uitschakelen', BYPASS_TOKEN: 'API Access Token authenticatie uitschakelen',
READONLY: 'Activeer read-only modus (blokkeert alle outgaande EMS Tx schrijf commandos)', READONLY: 'Activeer read-only modus (blokkeert alle outgaande EMS Tx schrijf commandos)',
UNDERCLOCK_CPU: 'Underclock CPU snelheid', UNDERCLOCK_CPU: 'Underclock CPU snelheid',
HEATINGOFF: 'Start boiler with forced heating off',
ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)', ENABLE_SHOWER_TIMER: 'Activeer Douche Timer (tijdmeting)',
ENABLE_SHOWER_ALERT: 'Activeer Douchemelding', ENABLE_SHOWER_ALERT: 'Activeer Douchemelding',
TRIGGER_TIME: 'Trigger tijd', TRIGGER_TIME: 'Trigger tijd',

View File

@@ -127,6 +127,7 @@ const no: Translation = {
BYPASS_TOKEN: 'Utelat Aksess Token authorisering av API kall', BYPASS_TOKEN: 'Utelat Aksess Token authorisering av API kall',
READONLY: 'Aktiver read-only modus (blokker all EMS Tx Skriving)', READONLY: 'Aktiver read-only modus (blokker all EMS Tx Skriving)',
UNDERCLOCK_CPU: 'Underklokking av prosessorhastighet', UNDERCLOCK_CPU: 'Underklokking av prosessorhastighet',
HEATINGOFF: 'Start boiler with forced heating off',
ENABLE_SHOWER_TIMER: 'Aktiver Dusjtimer', ENABLE_SHOWER_TIMER: 'Aktiver Dusjtimer',
ENABLE_SHOWER_ALERT: 'Aktiver Dusj-varsling', ENABLE_SHOWER_ALERT: 'Aktiver Dusj-varsling',
TRIGGER_TIME: 'Aktiveringstid', TRIGGER_TIME: 'Aktiveringstid',

View File

@@ -127,6 +127,7 @@ const pl: BaseTranslation = {
BYPASS_TOKEN: 'Pomiń autoryzację tokenem w wywołaniach API', BYPASS_TOKEN: 'Pomiń autoryzację tokenem w wywołaniach API',
READONLY: 'Tryb pracy "tylko do odczytu" (blokuje wszystkie komendy zapisu na magistralę EMS)', READONLY: 'Tryb pracy "tylko do odczytu" (blokuje wszystkie komendy zapisu na magistralę EMS)',
UNDERCLOCK_CPU: 'Obniż taktowanie CPU', UNDERCLOCK_CPU: 'Obniż taktowanie CPU',
HEATINGOFF: 'Start boiler with forced heating off',
ENABLE_SHOWER_TIMER: 'Aktywuj minutnik prysznica', ENABLE_SHOWER_TIMER: 'Aktywuj minutnik prysznica',
ENABLE_SHOWER_ALERT: 'Aktywuj alarm prysznica', ENABLE_SHOWER_ALERT: 'Aktywuj alarm prysznica',
TRIGGER_TIME: 'Wyzwalaj po czasie', TRIGGER_TIME: 'Wyzwalaj po czasie',
@@ -157,7 +158,7 @@ const pl: BaseTranslation = {
CUSTOMIZATIONS_HELP_3: 'zablokuj akcje zapisu', CUSTOMIZATIONS_HELP_3: 'zablokuj akcje zapisu',
CUSTOMIZATIONS_HELP_4: 'wyklucz z MQTT i API', CUSTOMIZATIONS_HELP_4: 'wyklucz z MQTT i API',
CUSTOMIZATIONS_HELP_5: 'ukryj na pulpicie', CUSTOMIZATIONS_HELP_5: 'ukryj na pulpicie',
CUSTOMIZATIONS_HELP_6: 'remove from memory', CUSTOMIZATIONS_HELP_6: 'usuń z pamięci',
SELECT_DEVICE: 'wybierz urządzenie', SELECT_DEVICE: 'wybierz urządzenie',
SET_ALL: 'Ustaw wszystko jako', SET_ALL: 'Ustaw wszystko jako',
OPTIONS: 'Opcje', OPTIONS: 'Opcje',
@@ -202,7 +203,7 @@ const pl: BaseTranslation = {
CPU_FREQ: 'Taktowanie CPU', CPU_FREQ: 'Taktowanie CPU',
HEAP: 'HEAP (wolne / maksymalny przydział)', HEAP: 'HEAP (wolne / maksymalny przydział)',
PSRAM: 'PSRAM (rozmiar / wolne)', PSRAM: 'PSRAM (rozmiar / wolne)',
FLASH: 'Flash (rozmiar / taktowanie)', FLASH: 'FLASH (rozmiar / taktowanie)',
APPSIZE: 'Aplikacja (wykorzystane / wolne)', APPSIZE: 'Aplikacja (wykorzystane / wolne)',
FILESYSTEM: 'System plików (wykorzystane / wolne)', FILESYSTEM: 'System plików (wykorzystane / wolne)',
BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)', BUFFER_SIZE: 'Maksymalna pojemność bufora (ilość wpisów)',

View File

@@ -127,6 +127,7 @@ const sv: Translation = {
BYPASS_TOKEN: 'Inaktivera Token-autensiering för API-anrop', BYPASS_TOKEN: 'Inaktivera Token-autensiering för API-anrop',
READONLY: 'Aktivera read-only (blockerar alla utgående skrivkommandon mot EMS-bussen)', READONLY: 'Aktivera read-only (blockerar alla utgående skrivkommandon mot EMS-bussen)',
UNDERCLOCK_CPU: 'Nedklocka Processorhastighet', UNDERCLOCK_CPU: 'Nedklocka Processorhastighet',
HEATINGOFF: 'Start boiler with forced heating off',
ENABLE_SHOWER_TIMER: 'Aktivera Dusch-timer', ENABLE_SHOWER_TIMER: 'Aktivera Dusch-timer',
ENABLE_SHOWER_ALERT: 'Aktivera Dusch-varning', ENABLE_SHOWER_ALERT: 'Aktivera Dusch-varning',
TRIGGER_TIME: 'Aktiveringstid', TRIGGER_TIME: 'Aktiveringstid',

View File

@@ -650,7 +650,7 @@ const DashboardData: FC = () => {
<DialogContent dividers> <DialogContent dividers>
<List dense={true}> <List dense={true}>
<ListItem> <ListItem>
<ListItemText primary={LL.TYPE()} secondary={coreData.devices[deviceDialog].t} /> <ListItemText primary={LL.TYPE()} secondary={coreData.devices[deviceDialog].tn} />
</ListItem> </ListItem>
<ListItem> <ListItem>
<ListItemText primary={LL.NAME(0)} secondary={coreData.devices[deviceDialog].n} /> <ListItemText primary={LL.NAME(0)} secondary={coreData.devices[deviceDialog].n} />

View File

@@ -408,6 +408,11 @@ const SettingsApplication: FC = () => {
label={LL.UNDERCLOCK_CPU()} label={LL.UNDERCLOCK_CPU()}
disabled={saving} disabled={saving}
/> />
<BlockFormControlLabel
control={<Checkbox checked={data.boiler_heatingoff} onChange={updateFormValue} name="boiler_heatingoff" />}
label={LL.HEATINGOFF()}
disabled={saving}
/>
<Grid container spacing={0} direction="row" justifyContent="flex-start" alignItems="flex-start"> <Grid container spacing={0} direction="row" justifyContent="flex-start" alignItems="flex-start">
<BlockFormControlLabel <BlockFormControlLabel
control={<Checkbox checked={data.shower_timer} onChange={updateFormValue} name="shower_timer" />} control={<Checkbox checked={data.shower_timer} onChange={updateFormValue} name="shower_timer" />}

View File

@@ -7,6 +7,7 @@ export interface Settings {
syslog_mark_interval: number; syslog_mark_interval: number;
syslog_host: string; syslog_host: string;
syslog_port: number; syslog_port: number;
boiler_heatingoff: boolean;
shower_timer: boolean; shower_timer: boolean;
shower_alert: boolean; shower_alert: boolean;
shower_alert_coldshot: number; shower_alert_coldshot: number;

View File

@@ -71,13 +71,13 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
return; return;
} }
#elif CONFIG_IDF_TARGET_ESP32S3 #elif CONFIG_IDF_TARGET_ESP32S3
if (len > 12 && (data[0] != 0xE9 || data[12] != 3)) { if (len > 12 && (data[0] != 0xE9 || data[12] != 9)) {
handleError(request, 503); // service unavailable handleError(request, 503); // service unavailable
return; return;
} }
#endif #endif
// it's firmware - initialize the ArduinoOTA updater // it's firmware - initialize the ArduinoOTA updater
if (Update.begin(fsize)) { if (Update.begin()) {
if (strlen(md5) == 32) { if (strlen(md5) == 32) {
Update.setMD5(md5); Update.setMD5(md5);
md5[0] = '\0'; md5[0] = '\0';

View File

@@ -68,7 +68,7 @@ void AnalogSensor::reload() {
analog_enabled_ = true; // for local offline testing analog_enabled_ = true; // for local offline testing
#endif #endif
for (auto sensor : sensors_) { for (auto sensor : sensors_) {
remove_ha_topic(sensor.gpio()); remove_ha_topic(sensor.type(), sensor.gpio());
sensor.ha_registered = false; sensor.ha_registered = false;
} }
if (!analog_enabled_) { if (!analog_enabled_) {
@@ -333,7 +333,7 @@ bool AnalogSensor::update(uint8_t gpio, const std::string & name, double offset,
// if the sensor exists and we're using HA, delete the old HA record // if the sensor exists and we're using HA, delete the old HA record
if (found_sensor && Mqtt::ha_enabled()) { if (found_sensor && Mqtt::ha_enabled()) {
remove_ha_topic(gpio); // the GPIO remove_ha_topic(type, gpio); // the GPIO
} }
// we didn't find it, it's new, so create and store it // we didn't find it, it's new, so create and store it
@@ -384,7 +384,7 @@ void AnalogSensor::publish_sensor(const Sensor & sensor) const {
} }
// send empty config topic to remove the entry from HA // send empty config topic to remove the entry from HA
void AnalogSensor::remove_ha_topic(const uint8_t gpio) const { void AnalogSensor::remove_ha_topic(const int8_t type, const uint8_t gpio) const {
if (!Mqtt::ha_enabled()) { if (!Mqtt::ha_enabled()) {
return; return;
} }
@@ -392,7 +392,21 @@ void AnalogSensor::remove_ha_topic(const uint8_t gpio) const {
LOG_DEBUG("Removing HA config for analog sensor GPIO %02d", gpio); LOG_DEBUG("Removing HA config for analog sensor GPIO %02d", gpio);
#endif #endif
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
#if CONFIG_IDF_TARGET_ESP32
if (type == AnalogType::DIGITAL_OUT && gpio != 25 && gpio != 26) {
#else
if (type == AnalogType::DIGITAL_OUT)
#endif
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
} else if (type == AnalogType::DIGITAL_OUT) { // DAC
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
} else if (type >= AnalogType::PWM_0) {
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
} else if (type == AnalogType::DIGITAL_IN) {
snprintf(topic, sizeof(topic), "binary_sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
} else {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio); snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), gpio);
}
Mqtt::publish_ha(topic); Mqtt::publish_ha(topic);
} }
@@ -447,13 +461,16 @@ void AnalogSensor::publish_values(const bool force) {
snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); // use base path snprintf(stat_t, sizeof(stat_t), "%s/analogsensor_data", Mqtt::base().c_str()); // use base path
config["stat_t"] = stat_t; config["stat_t"] = stat_t;
char str[50]; char val_obj[50];
char val_cond[65];
if (Mqtt::is_nested()) { if (Mqtt::is_nested()) {
snprintf(str, sizeof(str), "{{value_json['%02d'].value}}", sensor.gpio()); snprintf(val_obj, sizeof(val_obj), "value_json['%02d'].value", sensor.gpio());
snprintf(val_cond, sizeof(val_cond), "value_json['%02d'] is defined", sensor.gpio());
} else { } else {
snprintf(str, sizeof(str), "{{value_json['%s']}", sensor.name().c_str()); snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
} }
config["val_tpl"] = str; config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
char uniq_s[70]; char uniq_s[70];
if (Mqtt::entity_format() == 2) { if (Mqtt::entity_format() == 2) {
@@ -462,22 +479,76 @@ void AnalogSensor::publish_values(const bool force) {
snprintf(uniq_s, sizeof(uniq_s), "analogsensor_%02d", sensor.gpio()); snprintf(uniq_s, sizeof(uniq_s), "analogsensor_%02d", sensor.gpio());
} }
config["object_id"] = uniq_s; config["obj_id"] = uniq_s;
config["uniq_id"] = uniq_s; // same as object_id config["uniq_id"] = uniq_s; // same as object_id
snprintf(str, sizeof(str), "%s", sensor.name().c_str()); char name[50];
config["name"] = str; snprintf(name, sizeof(name), "%s", sensor.name().c_str());
config["name"] = name;
if (sensor.uom() != DeviceValueUOM::NONE) { if (sensor.uom() != DeviceValueUOM::NONE) {
config["unit_of_meas"] = EMSdevice::uom_to_string(sensor.uom()); config["unit_of_meas"] = EMSdevice::uom_to_string(sensor.uom());
} }
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
// Set commands for some analog types
char command_topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
#if CONFIG_IDF_TARGET_ESP32
if (sensor.type() == AnalogType::DIGITAL_OUT && sensor.gpio() != 25 && sensor.gpio() != 26) {
#else
if (sensor.type() == AnalogType::DIGITAL_OUT)
#endif
snprintf(topic, sizeof(topic), "switch/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
config["cmd_t"] = command_topic;
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
config["pl_on"] = true;
config["pl_off"] = false;
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
config["pl_on"] = 1;
config["pl_off"] = 0;
} else {
char result[12];
config["pl_on"] = Helpers::render_boolean(result, true);
config["pl_off"] = Helpers::render_boolean(result, false);
}
} else if (sensor.type() == AnalogType::DIGITAL_OUT) { // DAC
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
config["cmd_t"] = command_topic;
config["min"] = 0;
config["max"] = 255;
config["mode"] = "box"; // auto, slider or box
config["step"] = 1;
} else if (sensor.type() >= AnalogType::PWM_0) {
snprintf(topic, sizeof(topic), "number/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
config["cmd_t"] = command_topic;
config["min"] = 0;
config["max"] = 100;
config["mode"] = "box"; // auto, slider or box
config["step"] = 0.1;
} else if (sensor.type() == AnalogType::COUNTER) {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
snprintf(command_topic, sizeof(command_topic), "%s/analogsensor/%s", Mqtt::basename().c_str(), sensor.name().c_str());
config["cmd_t"] = command_topic;
config["stat_cla"] = "total_increasing";
// config["mode"] = "box"; // auto, slider or box
// config["step"] = sensor.factor();
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
snprintf(topic, sizeof(topic), "binary_sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
} else {
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio());
config["stat_cla"] = "measurement";
}
JsonObject dev = config.createNestedObject("dev"); JsonObject dev = config.createNestedObject("dev");
JsonArray ids = dev.createNestedArray("ids"); JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp"); ids.add("ems-esp");
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; // add "availability" section
snprintf(topic, sizeof(topic), "sensor/%s/analogsensor_%02d/config", Mqtt::basename().c_str(), sensor.gpio()); Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
Mqtt::publish_ha(topic, config.as<JsonObject>()); Mqtt::publish_ha(topic, config.as<JsonObject>());

View File

@@ -170,7 +170,7 @@ class AnalogSensor {
static uuid::log::Logger logger_; static uuid::log::Logger logger_;
void remove_ha_topic(const uint8_t id) const; void remove_ha_topic(const int8_t type, const uint8_t id) const;
bool command_setvalue(const char * value, const int8_t gpio); bool command_setvalue(const char * value, const int8_t gpio);
void measure(); void measure();
bool command_info(const char * value, const int8_t id, JsonObject & output) const; bool command_info(const char * value, const int8_t id, JsonObject & output) const;

View File

@@ -514,13 +514,16 @@ void DallasSensor::publish_values(const bool force) {
config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES); config["unit_of_meas"] = EMSdevice::uom_to_string(DeviceValueUOM::DEGREES);
char str[50]; char val_obj[50];
char val_cond[65];
if (Mqtt::is_nested()) { if (Mqtt::is_nested()) {
snprintf(str, sizeof(str), "{{value_json['%s'].temp}}", sensor.id().c_str()); snprintf(val_obj, sizeof(val_obj), "value_json['%s'].temp", sensor.id().c_str());
snprintf(val_cond, sizeof(val_cond), "value_json['%s'] is defined", sensor.id().c_str());
} else { } else {
snprintf(str, sizeof(str), "{{value_json['%s']}}", sensor.name().c_str()); snprintf(val_obj, sizeof(val_obj), "value_json['%s']", sensor.name().c_str());
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
} }
config["val_tpl"] = str; config["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + "}}";
char uniq_s[70]; char uniq_s[70];
if (Mqtt::entity_format() == 2) { if (Mqtt::entity_format() == 2) {
@@ -529,16 +532,20 @@ void DallasSensor::publish_values(const bool force) {
snprintf(uniq_s, sizeof(uniq_s), "dallassensor_%s", sensor.id().c_str()); snprintf(uniq_s, sizeof(uniq_s), "dallassensor_%s", sensor.id().c_str());
} }
config["object_id"] = uniq_s; config["obj_id"] = uniq_s;
config["uniq_id"] = uniq_s; // same as object_id config["uniq_id"] = uniq_s; // same as object_id
snprintf(str, sizeof(str), "%s", sensor.name().c_str()); char name[50];
config["name"] = str; snprintf(name, sizeof(name), "%s", sensor.name().c_str());
config["name"] = name;
JsonObject dev = config.createNestedObject("dev"); JsonObject dev = config.createNestedObject("dev");
JsonArray ids = dev.createNestedArray("ids"); JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp"); ids.add("ems-esp");
// add "availability" section
Mqtt::add_avty_to_doc(stat_t, config.as<JsonObject>(), val_cond);
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
// use '_' as HA doesn't like '-' in the topic name // use '_' as HA doesn't like '-' in the topic name
std::string sensorid = sensor.id(); std::string sensorid = sensor.id();

View File

@@ -61,6 +61,10 @@
#define EMSESP_DEFAULT_TRACELOG_RAW false #define EMSESP_DEFAULT_TRACELOG_RAW false
#endif #endif
#ifndef EMSESP_DEFAULT_BOILER_HEATINGOFF
#define EMSESP_DEFAULT_BOILER_HEATINGOFF false
#endif
#ifndef EMSESP_DEFAULT_SHOWER_TIMER #ifndef EMSESP_DEFAULT_SHOWER_TIMER
#define EMSESP_DEFAULT_SHOWER_TIMER false #define EMSESP_DEFAULT_SHOWER_TIMER false
#endif #endif

View File

@@ -48,6 +48,7 @@
{208, DeviceType::BOILER, "Logamax Plus/GB192/Condens GC9000/Greenstar ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE}, {208, DeviceType::BOILER, "Logamax Plus/GB192/Condens GC9000/Greenstar ErP", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{210, DeviceType::BOILER, "Cascade MC400", DeviceFlags::EMS_DEVICE_FLAG_NONE}, {210, DeviceType::BOILER, "Cascade MC400", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{211, DeviceType::BOILER, "EasyControl Adapter", DeviceFlags::EMS_DEVICE_FLAG_NONE}, {211, DeviceType::BOILER, "EasyControl Adapter", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{219, DeviceType::BOILER, "Greenstar HIU", DeviceFlags::EMS_DEVICE_FLAG_NONE},
{234, DeviceType::BOILER, "Logamax Plus GB122/Condense 2300", DeviceFlags::EMS_DEVICE_FLAG_NONE}, {234, DeviceType::BOILER, "Logamax Plus GB122/Condense 2300", DeviceFlags::EMS_DEVICE_FLAG_NONE},
// Controllers - 0x09 / 0x10 / 0x50 // Controllers - 0x09 / 0x10 / 0x50

View File

@@ -100,7 +100,12 @@ Boiler::Boiler(uint8_t device_type, int8_t device_id, uint8_t product_id, const
// reset is a command uses a dummy variable which is always zero, shown as blank, but provides command enum options // reset is a command uses a dummy variable which is always zero, shown as blank, but provides command enum options
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &reset_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset)); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &reset_, DeviceValueType::CMD, FL_(enum_reset), FL_(reset), DeviceValueUOM::NONE, MAKE_CF_CB(set_reset));
has_update(reset_, 0); has_update(reset_, 0);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&forceHeatingOff_,
DeviceValueType::BOOL,
FL_(forceHeatingOff),
DeviceValueUOM::NONE,
MAKE_CF_CB(set_forceHeatingOff));
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingActive_, DeviceValueType::BOOL, FL_(heatingActive), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingActive_, DeviceValueType::BOOL, FL_(heatingActive), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &tapwaterActive_, DeviceValueType::BOOL, FL_(tapwaterActive), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &tapwaterActive_, DeviceValueType::BOOL, FL_(tapwaterActive), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &selFlowTemp_, DeviceValueType::UINT, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp)); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &selFlowTemp_, DeviceValueType::UINT, FL_(selFlowTemp), DeviceValueUOM::DEGREES, MAKE_CF_CB(set_flow_temp));
@@ -875,6 +880,11 @@ void Boiler::check_active(const bool force) {
Mqtt::publish(F_(tapwater_active), Helpers::render_boolean(s, b)); Mqtt::publish(F_(tapwater_active), Helpers::render_boolean(s, b));
EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class EMSESP::tap_water_active(b); // let EMS-ESP know, used in the Shower class
} }
if (!Helpers::hasValue(forceHeatingOff_, EMS_VALUE_BOOL)) {
EMSESP::webSettingsService.read([&](WebSettings & settings) { forceHeatingOff_ = (settings.boiler_heatingoff || selFlowTemp_ == 0) ? 1 : 0; });
has_update(&forceHeatingOff_);
}
} }
// 0x18 // 0x18
@@ -1075,6 +1085,11 @@ void Boiler::process_UBAMonitorSlow(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes has_update(telegram, burn2WorkMin_, 16, 3); // force to 3 bytes
has_update(telegram, heatWorkMin_, 19, 3); // force to 3 bytes has_update(telegram, heatWorkMin_, 19, 3); // force to 3 bytes
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
uint8_t data[] = {0, 0, 0, 0};
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
}
} }
/* /*
@@ -1105,6 +1120,11 @@ void Boiler::process_UBAMonitorSlowPlus(std::shared_ptr<const Telegram> telegram
has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes has_update(telegram, heatStarts_, 22, 3); // force to 3 bytes
has_update(telegram, heatingPumpMod_, 25); has_update(telegram, heatingPumpMod_, 25);
// temperature measurements at 4, see #620 // temperature measurements at 4, see #620
if (forceHeatingOff_ == EMS_VALUE_BOOL_ON && telegram->dest == 0) {
uint8_t data[] = {0, 0, 0, 0};
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
}
} }
/* /*
@@ -1711,7 +1731,9 @@ bool Boiler::set_flow_temp(const char * value, const int8_t id) {
} }
// no write/verify if there is no change, see https://github.com/emsesp/EMS-ESP32/issues/654 // no write/verify if there is no change, see https://github.com/emsesp/EMS-ESP32/issues/654
// put it to end of tx-queue
if (v == selFlowTemp_) { if (v == selFlowTemp_) {
EMSESP::txservice_.add(Telegram::Operation::TX_WRITE, device_id(), EMS_TYPE_UBASetPoints, 0, (uint8_t *) &v, 1, 0, false);
return true; return true;
} }
@@ -2574,4 +2596,17 @@ bool Boiler::set_wwAltOpPrio(const char * value, const int8_t id) {
return false; return false;
} }
bool Boiler::set_forceHeatingOff(const char * value, const int8_t id) {
bool v;
if (Helpers::value2bool(value, v)) {
has_update(forceHeatingOff_, v);
if (!v && Helpers::hasValue(heatingTemp_)) {
uint8_t data[] = {heatingTemp_, (Helpers::hasValue(burnMaxPower_) ? burnMaxPower_ : (uint8_t)100), (Helpers::hasValue(pumpModMax_) ? pumpModMax_ : (uint8_t)0), 0};
write_command(EMS_TYPE_UBASetPoints, 0, data, sizeof(data), 0);
}
return true;
}
return false;
}
} // namespace emsesp } // namespace emsesp

View File

@@ -87,7 +87,6 @@ class Boiler : public EMSdevice {
uint32_t wwWorkM_; // DHW minutes uint32_t wwWorkM_; // DHW minutes
int8_t wwHystOn_; int8_t wwHystOn_;
int8_t wwHystOff_; int8_t wwHystOff_;
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
uint16_t wwMixerTemp_; // mixing temperature uint16_t wwMixerTemp_; // mixing temperature
uint16_t wwCylMiddleTemp_; // Cyl middle temperature (TS3) uint16_t wwCylMiddleTemp_; // Cyl middle temperature (TS3)
uint16_t wwSolarTemp_; uint16_t wwSolarTemp_;
@@ -95,6 +94,11 @@ class Boiler : public EMSdevice {
uint8_t wwAltOpPrioHeat_; // alternating operation, prioritise heat time uint8_t wwAltOpPrioHeat_; // alternating operation, prioritise heat time
uint8_t wwAltOpPrioWw_; // alternating operation, prioritise dhw time uint8_t wwAltOpPrioWw_; // alternating operation, prioritise dhw time
// special function
uint8_t forceHeatingOff_;
uint8_t wwTapActivated_; // maintenance-mode to switch DHW off
// main // main
uint8_t reset_; // for reset command uint8_t reset_; // for reset command
uint8_t heatingActive_; // Central heating is on/off uint8_t heatingActive_; // Central heating is on/off
@@ -447,6 +451,8 @@ class Boiler : public EMSdevice {
inline bool set_wwAltOpPrioWw(const char * value, const int8_t id) { inline bool set_wwAltOpPrioWw(const char * value, const int8_t id) {
return set_wwAltOpPrio(value, 3); return set_wwAltOpPrio(value, 3);
} }
bool set_forceHeatingOff(const char * value, const int8_t id);
/* /*
bool set_hybridStrategy(const char * value, const int8_t id); bool set_hybridStrategy(const char * value, const int8_t id);
bool set_switchOverTemp(const char * value, const int8_t id); bool set_switchOverTemp(const char * value, const int8_t id);

View File

@@ -32,14 +32,25 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
register_telegram_type(0x999, "HPFunctionTest", true, MAKE_PF_CB(process_HPFunctionTest)); register_telegram_type(0x999, "HPFunctionTest", true, MAKE_PF_CB(process_HPFunctionTest));
register_telegram_type(0x9A0, "HPTemperature", false, MAKE_PF_CB(process_HPTemperature)); register_telegram_type(0x9A0, "HPTemperature", false, MAKE_PF_CB(process_HPTemperature));
register_telegram_type(0x99B, "HPFlowTemp", false, MAKE_PF_CB(process_HPFlowTemp)); register_telegram_type(0x99B, "HPFlowTemp", false, MAKE_PF_CB(process_HPFlowTemp));
register_telegram_type(0x99C, "HPComp", false, MAKE_PF_CB(process_HPComp));
// device values // device values
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &airHumidity_, DeviceValueType::UINT, FL_(airHumidity), DeviceValueUOM::PERCENT); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &airHumidity_, DeviceValueType::UINT, FL_(airHumidity), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dewTemperature_, DeviceValueType::UINT, FL_(dewTemperature), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &dewTemperature_, DeviceValueType::UINT, FL_(dewTemperature), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &flowTemp_, DeviceValueType::UINT, FL_(curFlowTemp), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &retTemp_, DeviceValueType::UINT, FL_(retTemp), DeviceValueUOM::DEGREES); &flowTemp_,
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &sysRetTemp_, DeviceValueType::UINT, FL_(sysRetTemp), DeviceValueUOM::DEGREES); DeviceValueType::SHORT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(curFlowTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &retTemp_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(retTemp), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&sysRetTemp_,
DeviceValueType::SHORT,
DeviceValueNumOp::DV_NUMOP_DIV10,
FL_(sysRetTemp),
DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTa4_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTa4), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTa4_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTa4), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTr1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTr1), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpTr1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpTr1), DeviceValueUOM::DEGREES);
@@ -51,6 +62,9 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpJr0_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPl1), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpJr0_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPl1), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpJr1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPh1), DeviceValueUOM::DEGREES); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpJr1_, DeviceValueType::SHORT, DeviceValueNumOp::DV_NUMOP_DIV10, FL_(hpPh1), DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &heatingPumpMod_, DeviceValueType::UINT, FL_(heatingPumpMod), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &hpCompSpd_, DeviceValueType::UINT, FL_(hpCompSpd), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
&controlStrategy_, &controlStrategy_,
DeviceValueType::ENUM, DeviceValueType::ENUM,
@@ -177,6 +191,7 @@ void Heatpump::process_HPFlowTemp(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, flowTemp_, 4); has_update(telegram, flowTemp_, 4);
has_update(telegram, retTemp_, 6); has_update(telegram, retTemp_, 6);
has_update(telegram, sysRetTemp_, 14); has_update(telegram, sysRetTemp_, 14);
has_update(telegram, heatingPumpMod_, 19);
} }
// 0x0998 HPSettings // 0x0998 HPSettings
@@ -193,6 +208,14 @@ void Heatpump::process_HPSettings(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, switchOverTemp_, 14); has_update(telegram, switchOverTemp_, 14);
} }
// 0x099C HPComp
// Broadcast (0x099C), data: 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 76 00 00
// data: 00 2B 00 03 04 13 00 00 00 00 00 02 02 02 (offset 24)
void Heatpump::process_HPComp(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, hpCompSpd_, 51);
}
// 0x999 HPFunctionTest
void Heatpump::process_HPFunctionTest(std::shared_ptr<const Telegram> telegram) { void Heatpump::process_HPFunctionTest(std::shared_ptr<const Telegram> telegram) {
has_update(telegram, airPurgeMode_, 0); has_update(telegram, airPurgeMode_, 0);
has_update(telegram, heatPumpOutput_, 2); has_update(telegram, heatPumpOutput_, 2);

View File

@@ -41,6 +41,8 @@ class Heatpump : public EMSdevice {
uint8_t energyPriceEl_; uint8_t energyPriceEl_;
uint8_t energyPricePV_; uint8_t energyPricePV_;
int8_t switchOverTemp_; int8_t switchOverTemp_;
uint8_t heatingPumpMod_;
uint8_t hpCompSpd_;
// Function test // Function test
uint8_t airPurgeMode_; uint8_t airPurgeMode_;
@@ -51,9 +53,9 @@ class Heatpump : public EMSdevice {
uint8_t heatCable_; uint8_t heatCable_;
// HM200 temperature // HM200 temperature
int16_t flowTemp_; int16_t flowTemp_; // TH1
int16_t retTemp_; int16_t retTemp_; // TH2
int16_t sysRetTemp_; int16_t sysRetTemp_; // TH3
int16_t hpTc3_; // condenser temp. int16_t hpTc3_; // condenser temp.
int16_t hpTr1_; // compressor temp. int16_t hpTr1_; // compressor temp.
int16_t hpTr3_; // cond. temp. heating int16_t hpTr3_; // cond. temp. heating
@@ -71,6 +73,8 @@ class Heatpump : public EMSdevice {
void process_HPFunctionTest(std::shared_ptr<const Telegram> telegram); void process_HPFunctionTest(std::shared_ptr<const Telegram> telegram);
void process_HPTemperature(std::shared_ptr<const Telegram> telegram); void process_HPTemperature(std::shared_ptr<const Telegram> telegram);
void process_HPFlowTemp(std::shared_ptr<const Telegram> telegram); void process_HPFlowTemp(std::shared_ptr<const Telegram> telegram);
void process_HPComp(std::shared_ptr<const Telegram> telegram);
bool set_controlStrategy(const char * value, const int8_t id); bool set_controlStrategy(const char * value, const int8_t id);
bool set_lowNoiseMode(const char * value, const int8_t id); bool set_lowNoiseMode(const char * value, const int8_t id);
bool set_lowNoiseStart(const char * value, const int8_t id); bool set_lowNoiseStart(const char * value, const int8_t id);

View File

@@ -317,6 +317,7 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const c
DeviceValueUOM::DEGREES); DeviceValueUOM::DEGREES);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cylPumpMod_, DeviceValueType::UINT, FL_(cylPumpMod), DeviceValueUOM::PERCENT); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cylPumpMod_, DeviceValueType::UINT, FL_(cylPumpMod), DeviceValueUOM::PERCENT);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &valveStatus_, DeviceValueType::BOOL, FL_(valveStatus), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &valveStatus_, DeviceValueType::BOOL, FL_(valveStatus), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &vs1Status_, DeviceValueType::BOOL, FL_(vs1Status), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cylHeated_, DeviceValueType::BOOL, FL_(cylHeated), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &cylHeated_, DeviceValueType::BOOL, FL_(cylHeated), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &collectorShutdown_, DeviceValueType::BOOL, FL_(collectorShutdown), DeviceValueUOM::NONE); register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &collectorShutdown_, DeviceValueType::BOOL, FL_(collectorShutdown), DeviceValueUOM::NONE);
register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, register_device_value(DeviceValueTAG::TAG_DEVICE_DATA,
@@ -820,6 +821,7 @@ void Solar::process_SM100Status(std::shared_ptr<const Telegram> telegram) {
* byte 10 = PS1 Solar circuit pump for collector array 1: test=b0001(1), on=b0100(4) and off=b0011(3) * byte 10 = PS1 Solar circuit pump for collector array 1: test=b0001(1), on=b0100(4) and off=b0011(3)
*/ */
void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) { void Solar::process_SM100Status2(std::shared_ptr<const Telegram> telegram) {
has_bitupdate(telegram, vs1Status_, 0, 2); // on if bit 2 set
has_bitupdate(telegram, valveStatus_, 4, 2); // on if bit 2 set has_bitupdate(telegram, valveStatus_, 4, 2); // on if bit 2 set
has_bitupdate(telegram, solarPump_, 10, 2); // on if bit 2 set has_bitupdate(telegram, solarPump_, 10, 2); // on if bit 2 set
has_bitupdate(telegram, solarPump2_, 1, 2); // on if bit 2 set has_bitupdate(telegram, solarPump2_, 1, 2); // on if bit 2 set

View File

@@ -45,6 +45,7 @@ class Solar : public EMSdevice {
uint8_t solarPump2Mod_; // PS4: modulation solar pump uint8_t solarPump2Mod_; // PS4: modulation solar pump
uint8_t m1Valve_; // M1: heat assistance valve uint8_t m1Valve_; // M1: heat assistance valve
uint8_t m1Power_; // M1: heat assistance valve uint8_t m1Power_; // M1: heat assistance valve
uint8_t vs1Status_; // VS1: status
// 0x363 heat counter // 0x363 heat counter
uint16_t heatCntFlowTemp_; uint16_t heatCntFlowTemp_;

View File

@@ -1192,14 +1192,9 @@ void Thermostat::process_RC30Temp(std::shared_ptr<const Telegram> telegram) {
// type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - data from the RC35 thermostat (0x10) - 16 bytes // type 0x3E (HC1), 0x48 (HC2), 0x52 (HC3), 0x5C (HC4) - data from the RC35 thermostat (0x10) - 16 bytes
void Thermostat::process_RC35Monitor(std::shared_ptr<const Telegram> telegram) { void Thermostat::process_RC35Monitor(std::shared_ptr<const Telegram> telegram) {
// exit if the 15th byte (second from last) is 0x00, which I think is calculated flow setpoint temperature // Check if heatingciruit is active, see https://github.com/emsesp/EMS-ESP32/issues/786
// with weather controlled RC35s this value is >=5, otherwise can be zero and our setpoint temps will be incorrect uint16_t active = 0;
// see https://github.com/emsesp/EMS-ESP/issues/373#issuecomment-627907301 if (!telegram->read_value(active, 3)) {
if (telegram->offset > 0 || telegram->message_length < 15) {
return;
}
if (telegram->message_data[14] == 0x00) {
return; return;
} }

View File

@@ -1642,22 +1642,6 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
return has_values; return has_values;
} }
// remove the Home Assistant configs for each device value/entity if its not visible or active or marked as read-only
// this is called when an MQTT publish is done via an EMS Device in emsesp.cpp::publish_device_values()
void EMSdevice::mqtt_ha_entity_config_remove() {
for (auto & dv : devicevalues_) {
if (dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED)
&& ((dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) || (!dv.has_state(DeviceValueState::DV_ACTIVE)))) {
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
if (dv.short_name == FL_(climate)[0]) {
Mqtt::publish_ha_climate_config(dv.tag, false, true); // delete topic (remove = true)
} else {
Mqtt::publish_ha_sensor_config(dv, "", "", true); // delete topic (remove = true)
}
}
}
}
// create the Home Assistant configs for each device value / entity // create the Home Assistant configs for each device value / entity
// this is called when an MQTT publish is done via an EMS Device in emsesp.cpp::publish_device_values() // this is called when an MQTT publish is done via an EMS Device in emsesp.cpp::publish_device_values()
void EMSdevice::mqtt_ha_entity_config_create() { void EMSdevice::mqtt_ha_entity_config_create() {
@@ -1686,6 +1670,9 @@ void EMSdevice::mqtt_ha_entity_config_create() {
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
} }
if (ESP.getFreeHeap() < (65 * 1024)) {
break;
}
} }
ha_config_done(!create_device_config); ha_config_done(!create_device_config);
@@ -1694,7 +1681,6 @@ void EMSdevice::mqtt_ha_entity_config_create() {
// remove all config topics in HA // remove all config topics in HA
void EMSdevice::ha_config_clear() { void EMSdevice::ha_config_clear() {
for (auto & dv : devicevalues_) { for (auto & dv : devicevalues_) {
Mqtt::publish_ha_sensor_config(dv, "", "", true); // delete topic (remove = true)
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED); dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
} }

View File

@@ -289,7 +289,6 @@ class EMSdevice {
void publish_all_values(); void publish_all_values();
void mqtt_ha_entity_config_create(); void mqtt_ha_entity_config_create();
void mqtt_ha_entity_config_remove();
std::string telegram_type_name(std::shared_ptr<const Telegram> telegram) const; std::string telegram_type_name(std::shared_ptr<const Telegram> telegram) const;
@@ -306,13 +305,6 @@ class EMSdevice {
ha_config_done_ = v; ha_config_done_ = v;
} }
bool ha_config_firstrun() const {
return ha_config_firstrun_;
}
void ha_config_firstrun(const bool v) {
ha_config_firstrun_ = v;
}
enum Brand : uint8_t { enum Brand : uint8_t {
NO_BRAND = 0, // 0 NO_BRAND = 0, // 0
BOSCH, // 1 BOSCH, // 1
@@ -366,6 +358,7 @@ class EMSdevice {
static constexpr uint8_t EMS_DEVICE_ID_RFSENSOR = 0x40; // RF sensor only sending, no reply static constexpr uint8_t EMS_DEVICE_ID_RFSENSOR = 0x40; // RF sensor only sending, no reply
static constexpr uint8_t EMS_DEVICE_ID_RFBASE = 0x50; static constexpr uint8_t EMS_DEVICE_ID_RFBASE = 0x50;
static constexpr uint8_t EMS_DEVICE_ID_ROOMTHERMOSTAT = 0x17; // TADO using this with no version reply static constexpr uint8_t EMS_DEVICE_ID_ROOMTHERMOSTAT = 0x17; // TADO using this with no version reply
static constexpr uint8_t EMS_DEVICE_ID_TADO_OLD = 0x19; // TADO using this with no broadcast and version
// generic type IDs // generic type IDs
static constexpr uint16_t EMS_TYPE_VERSION = 0x02; // type ID for Version information. Generic across all EMS devices. static constexpr uint16_t EMS_TYPE_VERSION = 0x02; // type ID for Version information. Generic across all EMS devices.
@@ -431,7 +424,6 @@ class EMSdevice {
bool ha_config_done_ = false; bool ha_config_done_ = false;
bool has_update_ = false; bool has_update_ = false;
bool ha_config_firstrun_ = true; // this means a first setup of HA is needed after a restart
struct TelegramFunction { struct TelegramFunction {
uint16_t telegram_type_id_; // it's type_id uint16_t telegram_type_id_; // it's type_id

View File

@@ -535,24 +535,6 @@ void EMSESP::publish_device_values(uint8_t device_type) {
bool nested = (Mqtt::is_nested()); bool nested = (Mqtt::is_nested());
// group by device type // group by device type
if (Mqtt::ha_enabled()) {
for (const auto & emsdevice : emsdevices) {
if (emsdevice && (emsdevice->device_type() == device_type)) {
// specially for MQTT Discovery
// we may have some RETAINED /config topics that reference fields in the data payloads that no longer exist
// remove them immediately to prevent HA from complaining
// we need to do this first before the data payload is published, and only done once!
if (emsdevice->ha_config_firstrun()) {
emsdevice->ha_config_clear();
emsdevice->ha_config_firstrun(false);
return;
} else {
// see if we need to delete and /config topics before adding the payloads
emsdevice->mqtt_ha_entity_config_remove();
}
}
}
}
for (uint8_t tag = DeviceValueTAG::TAG_BOILER_DATA_WW; tag <= DeviceValueTAG::TAG_HS16; tag++) { for (uint8_t tag = DeviceValueTAG::TAG_BOILER_DATA_WW; tag <= DeviceValueTAG::TAG_HS16; tag++) {
JsonObject json_hc = json; JsonObject json_hc = json;
bool nest_created = false; bool nest_created = false;
@@ -1057,7 +1039,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, const
// see: https://github.com/emsesp/EMS-ESP32/issues/103#issuecomment-911717342 and https://github.com/emsesp/EMS-ESP32/issues/624 // see: https://github.com/emsesp/EMS-ESP32/issues/103#issuecomment-911717342 and https://github.com/emsesp/EMS-ESP32/issues/624
name = "RF room temperature sensor"; name = "RF room temperature sensor";
device_type = DeviceType::THERMOSTAT; device_type = DeviceType::THERMOSTAT;
} else if (device_id == EMSdevice::EMS_DEVICE_ID_ROOMTHERMOSTAT) { } else if (device_id == EMSdevice::EMS_DEVICE_ID_ROOMTHERMOSTAT || device_id == EMSdevice::EMS_DEVICE_ID_TADO_OLD) {
name = "Generic thermostat"; name = "Generic thermostat";
device_type = DeviceType::THERMOSTAT; device_type = DeviceType::THERMOSTAT;
flags = DeviceFlags::EMS_DEVICE_FLAG_RC10 | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE; flags = DeviceFlags::EMS_DEVICE_FLAG_RC10 | DeviceFlags::EMS_DEVICE_FLAG_NO_WRITE;

View File

@@ -291,7 +291,7 @@ MAKE_PSTR_ENUM(enum_modetype5, FL_(off), FL_(on))
MAKE_PSTR_ENUM(enum_reducemode, FL_(nofrost), FL_(reduce), FL_(room), FL_(outdoor)) MAKE_PSTR_ENUM(enum_reducemode, FL_(nofrost), FL_(reduce), FL_(room), FL_(outdoor))
MAKE_PSTR_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 values: 1-3 MAKE_PSTR_ENUM(enum_reducemode1, FL_(outdoor), FL_(room), FL_(reduce)) // RC310 values: 1-3
MAKE_PSTR_ENUM(enum_nofrostmode, FL_(off), FL_(room), FL_(outdoor)) MAKE_PSTR_ENUM(enum_nofrostmode, FL_(off), FL_(outdoor), FL_(room))
MAKE_PSTR_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor)) MAKE_PSTR_ENUM(enum_nofrostmode1, FL_(room), FL_(outdoor), FL_(room_outdoor))
MAKE_PSTR_ENUM(enum_controlmode, FL_(off), FL_(optimized), FL_(simple), FL_(mpc), FL_(room), FL_(power), FL_(constant)) MAKE_PSTR_ENUM(enum_controlmode, FL_(off), FL_(optimized), FL_(simple), FL_(mpc), FL_(room), FL_(power), FL_(constant))

View File

@@ -269,6 +269,7 @@ MAKE_PSTR_LIST(cyl2, "cyl 2", "Zyl_2", "Cil 2", "Cyl 2", "cyl 2", "cyl 2", "cyl
// Entity translations // Entity translations
// Boiler // Boiler
MAKE_PSTR_LIST(forceHeatingOff, "heatingoff", "force heating off", "Heizen abschalten", "", "", "", "", "")
MAKE_PSTR_LIST(wwtapactivated, "wwtapactivated", "turn on/off", "Durchlauferhitzer aktiv", "zet aan/uit", "på/av", "system przygotowywania", "slå på/av", "ecs activée") MAKE_PSTR_LIST(wwtapactivated, "wwtapactivated", "turn on/off", "Durchlauferhitzer aktiv", "zet aan/uit", "på/av", "system przygotowywania", "slå på/av", "ecs activée")
MAKE_PSTR_LIST(reset, "reset", "Reset", "Reset", "Reset", "Nollställ", "kasowanie komunikatu", "nullstill", "reset") MAKE_PSTR_LIST(reset, "reset", "Reset", "Reset", "Reset", "Nollställ", "kasowanie komunikatu", "nullstill", "reset")
MAKE_PSTR_LIST(oilPreHeat, "oilpreheat", "oil preheating", "Ölvorwärmung", "Olie voorverwarming", "Förvärmning olja", "podgrzewanie oleju", "oljeforvarming", "préchauffage de l'huile") MAKE_PSTR_LIST(oilPreHeat, "oilpreheat", "oil preheating", "Ölvorwärmung", "Olie voorverwarming", "Förvärmning olja", "podgrzewanie oleju", "oljeforvarming", "préchauffage de l'huile")
@@ -683,6 +684,7 @@ MAKE_PSTR_LIST(solarPump, "solarpump", "pump (PS1)", "Pumpe (PS1)", "Pomp (PS1)"
MAKE_PSTR_LIST(solarPump2, "solarpump2", "pump 2 (PS4)", "Pumpe 2 (PS4)", "Pomp 2 (PS4)", "Pump 2 (PS4)", "pompa solarna 2 (PS4)", "solpumpe 2 (PS4)", "pompe 2 (PS4)") MAKE_PSTR_LIST(solarPump2, "solarpump2", "pump 2 (PS4)", "Pumpe 2 (PS4)", "Pomp 2 (PS4)", "Pump 2 (PS4)", "pompa solarna 2 (PS4)", "solpumpe 2 (PS4)", "pompe 2 (PS4)")
MAKE_PSTR_LIST(solarPump2Mod, "solarpump2mod", "pump 2 modulation (PS4)", "Pumpe 2 Modulation (PS4)", "Modulatie pomp 2 (PS4)", "Pump 2 Modulering (PS4)", "modulacja pompy solarnej 2 (PS4)", "solpumpe2modulering (PS4)", "modulation pompe solaire 2 (PS4)") MAKE_PSTR_LIST(solarPump2Mod, "solarpump2mod", "pump 2 modulation (PS4)", "Pumpe 2 Modulation (PS4)", "Modulatie pomp 2 (PS4)", "Pump 2 Modulering (PS4)", "modulacja pompy solarnej 2 (PS4)", "solpumpe2modulering (PS4)", "modulation pompe solaire 2 (PS4)")
MAKE_PSTR_LIST(valveStatus, "valvestatus", "valve status", "Ventilstatus", "Klepstatus", "Ventilstatus", "stan zaworu", "ventilstatus", "statut valve") MAKE_PSTR_LIST(valveStatus, "valvestatus", "valve status", "Ventilstatus", "Klepstatus", "Ventilstatus", "stan zaworu", "ventilstatus", "statut valve")
MAKE_PSTR_LIST(vs1Status, "vs1status", "valve status VS1", "Ventilstatus VS1", "Klepstatus VS1", "Ventilstatus VS1", "stan zaworu VS1", "ventilstatus VS1", "statut valve VS1")
MAKE_PSTR_LIST(cylHeated, "cylheated", "cyl heated", "Speichertemperatur erreicht", "Boilertemperatuur behaald", "Värmepanna Uppvärmd", "zasobnik został nagrzany", "bereder oppvarmt", "cylindre chauffé") MAKE_PSTR_LIST(cylHeated, "cylheated", "cyl heated", "Speichertemperatur erreicht", "Boilertemperatuur behaald", "Värmepanna Uppvärmd", "zasobnik został nagrzany", "bereder oppvarmt", "cylindre chauffé")
MAKE_PSTR_LIST(collectorShutdown, "collectorshutdown", "collector shutdown", "Kollektorabschaltung", "Collector afschakeling", "Kollektor Avstängning", "wyłączenie kolektora", "kollektor stengt", "arrêt collecteur") MAKE_PSTR_LIST(collectorShutdown, "collectorshutdown", "collector shutdown", "Kollektorabschaltung", "Collector afschakeling", "Kollektor Avstängning", "wyłączenie kolektora", "kollektor stengt", "arrêt collecteur")
MAKE_PSTR_LIST(pumpWorkTime, "pumpworktime", "pump working time", "Pumpenlaufzeit", "Pomplooptijd", "Pump Drifttid", "czas pracy pompy", "driftstid pumpe", "durée fonctionnement pompe") MAKE_PSTR_LIST(pumpWorkTime, "pumpworktime", "pump working time", "Pumpenlaufzeit", "Pomplooptijd", "Pump Drifttid", "czas pracy pompy", "driftstid pumpe", "durée fonctionnement pompe")

View File

@@ -685,7 +685,7 @@ std::shared_ptr<const MqttMessage> Mqtt::queue_message(const uint8_t operation,
#endif #endif
// if the queue is full, make room but removing the last one // if the queue is full, make room but removing the last one
if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES) { if (mqtt_messages_.size() >= MAX_MQTT_MESSAGES || ESP.getFreeHeap() < (60 * 1024)) {
mqtt_messages_.pop_front(); mqtt_messages_.pop_front();
LOG_WARNING("Queue overflow, removing one message"); LOG_WARNING("Queue overflow, removing one message");
mqtt_publish_fails_++; mqtt_publish_fails_++;
@@ -815,8 +815,8 @@ void Mqtt::process_queue() {
#if defined(EMSESP_DEBUG) #if defined(EMSESP_DEBUG)
LOG_DEBUG("[DEBUG] Waiting for QOS-ACK"); LOG_DEBUG("[DEBUG] Waiting for QOS-ACK");
#endif #endif
// if we don't get the ack within 10 minutes, republish with new packet_id // if we don't get the ack within 10 seconds, republish with new packet_id
if (uuid::get_uptime_sec() - last_publish_queue_ < 600) { if (uuid::get_uptime_sec() - last_publish_queue_ < 10) {
return; return;
} }
} }
@@ -1074,6 +1074,8 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
const char * sc_ha = "stat_cla"; // state class const char * sc_ha = "stat_cla"; // state class
const char * uom_ha = "unit_of_meas"; // unit of measure const char * uom_ha = "unit_of_meas"; // unit of measure
char sample_val[30] = "0"; // sample, correct(!) entity value, used only to prevent warning/error in HA if real value is not published yet
// handle commands, which are device entities that are writable // handle commands, which are device entities that are writable
// we add the command topic parameter // we add the command topic parameter
// note: there is no way to handle strings in HA so datetimes (e.g. set_datetime, set_holiday, set_wwswitchtime etc) are excluded // note: there is no way to handle strings in HA so datetimes (e.g. set_datetime, set_holiday, set_wwswitchtime etc) are excluded
@@ -1093,6 +1095,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
for (uint8_t i = 0; i < options_size; i++) { for (uint8_t i = 0; i < options_size; i++) {
option_list.add(Helpers::translated_word(options[i])); option_list.add(Helpers::translated_word(options[i]));
} }
snprintf(sample_val, sizeof(sample_val), "'%s'", Helpers::translated_word(options[0]));
} else if (type != DeviceValueType::STRING && type != DeviceValueType::BOOL) { } else if (type != DeviceValueType::STRING && type != DeviceValueType::BOOL) {
// Must be Numeric.... // Must be Numeric....
doc["mode"] = "box"; // auto, slider or box doc["mode"] = "box"; // auto, slider or box
@@ -1109,6 +1112,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
if (dv_set_min != 0 || dv_set_max != 0) { if (dv_set_min != 0 || dv_set_max != 0) {
doc["min"] = dv_set_min; doc["min"] = dv_set_min;
doc["max"] = dv_set_max; doc["max"] = dv_set_max;
snprintf(sample_val, sizeof(sample_val), "%i", dv_set_min);
} }
// set icons // set icons
@@ -1146,17 +1150,15 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
// value template // value template
// if its nested mqtt format then use the appended entity name, otherwise take the original name // if its nested mqtt format then use the appended entity name, otherwise take the original name
char val_tpl[75]; char val_obj[100];
if (is_nested()) { char val_cond[200];
if (tag >= DeviceValueTAG::TAG_HC1) { if (is_nested() && tag >= DeviceValueTAG::TAG_HC1) {
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s.%s}}", EMSdevice::tag_to_mqtt(tag).c_str(), entity); snprintf(val_obj, sizeof(val_obj), "value_json.%s.%s", EMSdevice::tag_to_mqtt(tag).c_str(), entity);
snprintf(val_cond, sizeof(val_cond), "value_json.%s is defined and %s is defined", EMSdevice::tag_to_mqtt(tag).c_str(), val_obj);
} else { } else {
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", entity); snprintf(val_obj, sizeof(val_obj), "value_json.%s", entity);
snprintf(val_cond, sizeof(val_cond), "%s is defined", val_obj);
} }
} else {
snprintf(val_tpl, sizeof(val_tpl), "{{value_json.%s}}", entity);
}
doc["val_tpl"] = val_tpl;
// special case to handle booleans // special case to handle booleans
// applies to both Binary Sensor (read only) and a Switch (for a command) // applies to both Binary Sensor (read only) and a Switch (for a command)
@@ -1165,6 +1167,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
doc["pl_on"] = true; doc["pl_on"] = true;
doc["pl_off"] = false; doc["pl_off"] = false;
snprintf(sample_val, sizeof(sample_val), "false");
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) { } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
doc["pl_on"] = 1; doc["pl_on"] = 1;
doc["pl_off"] = 0; doc["pl_off"] = 0;
@@ -1172,6 +1175,7 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
char result[12]; char result[12];
doc["pl_on"] = Helpers::render_boolean(result, true); doc["pl_on"] = Helpers::render_boolean(result, true);
doc["pl_off"] = Helpers::render_boolean(result, false); doc["pl_off"] = Helpers::render_boolean(result, false);
snprintf(sample_val, sizeof(sample_val), "'%s'", Helpers::render_boolean(result, false));
} }
doc[sc_ha] = F_(measurement); //do we want this??? doc[sc_ha] = F_(measurement); //do we want this???
} else { } else {
@@ -1189,6 +1193,8 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
} }
} }
doc["val_tpl"] = (std::string) "{{" + val_obj + " if " + val_cond + " else " + sample_val + "}}";
// this next section is adding the state class, device class and sometimes the icon // this next section is adding the state class, device class and sometimes the icon
// used for Sensor and Binary Sensor Entities in HA // used for Sensor and Binary Sensor Entities in HA
if (set_ha_classes) { if (set_ha_classes) {
@@ -1284,6 +1290,9 @@ void Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
// add the dev json object to the end // add the dev json object to the end
doc["dev"] = dev_json; doc["dev"] = dev_json;
// add "availability" section
add_avty_to_doc(stat_t, doc.as<JsonObject>(), val_cond);
publish_ha(topic, doc.as<JsonObject>()); publish_ha(topic, doc.as<JsonObject>());
} }
@@ -1295,6 +1304,9 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
char hc_mode_s[30]; char hc_mode_s[30];
char seltemp_s[30]; char seltemp_s[30];
char currtemp_s[30]; char currtemp_s[30];
char hc_mode_cond[80];
char seltemp_cond[80];
char currtemp_cond[170];
char mode_str_tpl[400]; char mode_str_tpl[400];
char name_s[10]; char name_s[10];
char uniq_id_s[60]; char uniq_id_s[60];
@@ -1312,24 +1324,31 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
if (Mqtt::is_nested()) { if (Mqtt::is_nested()) {
// nested format // nested format
snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.hc%d.mode", hc_num); snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.hc%d.mode", hc_num);
snprintf(seltemp_s, sizeof(seltemp_s), "{{value_json.hc%d.seltemp}}", hc_num); snprintf(hc_mode_cond, sizeof(hc_mode_cond), "value_json.hc%d is undefined or %s is undefined", hc_num, hc_mode_s);
snprintf(seltemp_s, sizeof(seltemp_s), "value_json.hc%d.seltemp", hc_num);
snprintf(seltemp_cond, sizeof(seltemp_cond), "value_json.hc%d is defined and %s is defined", hc_num, seltemp_s);
if (has_roomtemp) { if (has_roomtemp) {
snprintf(currtemp_s, sizeof(currtemp_s), "{{value_json.hc%d.currtemp}}", hc_num); snprintf(currtemp_s, sizeof(currtemp_s), "value_json.hc%d.currtemp", hc_num);
snprintf(currtemp_cond, sizeof(currtemp_cond), "value_json.hc%d is defined and %s is defined", hc_num, currtemp_s);
} }
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str()); snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_NONE).c_str());
} else { } else {
// single format // single format
snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.mode"); snprintf(hc_mode_s, sizeof(hc_mode_s), "value_json.mode");
snprintf(seltemp_s, sizeof(seltemp_s), "{{value_json.seltemp}}"); snprintf(hc_mode_cond, sizeof(hc_mode_cond), "%s is undefined", hc_mode_s);
snprintf(seltemp_s, sizeof(seltemp_s), "value_json.seltemp");
snprintf(seltemp_cond, sizeof(seltemp_cond), "%s is defined", seltemp_s);
if (has_roomtemp) { if (has_roomtemp) {
snprintf(currtemp_s, sizeof(currtemp_s), "{{value_json.currtemp}}"); snprintf(currtemp_s, sizeof(currtemp_s), "value_json.currtemp");
snprintf(currtemp_cond, sizeof(currtemp_cond), "%s is defined", currtemp_s);
} }
snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_HC1 + hc_num - 1).c_str()); snprintf(topic_t, sizeof(topic_t), "~/%s", Mqtt::tag_to_topic(EMSdevice::DeviceType::THERMOSTAT, DeviceValueTAG::TAG_HC1 + hc_num - 1).c_str());
} }
snprintf(mode_str_tpl, snprintf(mode_str_tpl,
sizeof(mode_str_tpl), sizeof(mode_str_tpl),
"{%%if %s=='manual'%%}heat{%%elif %s=='day'%%}heat{%%elif %s=='night'%%}off{%%elif %s=='off'%%}off{%%else%%}auto{%%endif%%}", "{%%if %s%%}off{%%elif %s=='manual'%%}heat{%%elif %s=='day'%%}heat{%%elif %s=='night'%%}off{%%elif %s=='off'%%}off{%%else%%}auto{%%endif%%}",
hc_mode_cond,
hc_mode_s, hc_mode_s,
hc_mode_s, hc_mode_s,
hc_mode_s, hc_mode_s,
@@ -1346,7 +1365,7 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
snprintf(temp_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/seltemp", hc_num); snprintf(temp_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/seltemp", hc_num);
snprintf(mode_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/mode", hc_num); snprintf(mode_cmd_s, sizeof(temp_cmd_s), "~/thermostat/hc%d/mode", hc_num);
StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG> doc; StaticJsonDocument<EMSESP_JSON_SIZE_HA_CONFIG + 512> doc;
doc["~"] = mqtt_base_; doc["~"] = mqtt_base_;
doc["uniq_id"] = uniq_id_s; doc["uniq_id"] = uniq_id_s;
@@ -1356,12 +1375,10 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
doc["mode_stat_tpl"] = mode_str_tpl; doc["mode_stat_tpl"] = mode_str_tpl;
doc["temp_cmd_t"] = temp_cmd_s; doc["temp_cmd_t"] = temp_cmd_s;
doc["temp_stat_t"] = topic_t; doc["temp_stat_t"] = topic_t;
doc["temp_stat_tpl"] = seltemp_s; doc["temp_stat_tpl"] = (std::string) "{{" + seltemp_s + " if " + seltemp_cond + " else 0}}";
doc["mode_cmd_t"] = mode_cmd_s;
if (has_roomtemp) { if (has_roomtemp) {
doc["curr_temp_t"] = topic_t; doc["curr_temp_t"] = topic_t;
doc["curr_temp_tpl"] = currtemp_s; doc["curr_temp_tpl"] = (std::string) "{{" + currtemp_s + " if " + currtemp_cond + " else 0}}";
} }
doc["min_temp"] = Helpers::render_value(min_s, min, 0, EMSESP::system_.fahrenheit() ? 2 : 0); doc["min_temp"] = Helpers::render_value(min_s, min, 0, EMSESP::system_.fahrenheit() ? 2 : 0);
@@ -1369,8 +1386,8 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
doc["temp_step"] = "0.5"; doc["temp_step"] = "0.5";
// the HA climate component only responds to auto, heat and off // the HA climate component only responds to auto, heat and off
doc["mode_cmd_t"] = mode_cmd_s;
JsonArray modes = doc.createNestedArray("modes"); JsonArray modes = doc.createNestedArray("modes");
modes.add("auto"); modes.add("auto");
modes.add("heat"); modes.add("heat");
modes.add("off"); modes.add("off");
@@ -1379,6 +1396,9 @@ void Mqtt::publish_ha_climate_config(const uint8_t tag, const bool has_roomtemp,
JsonArray ids = dev.createNestedArray("ids"); JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp-thermostat"); ids.add("ems-esp-thermostat");
// add "availability" section
add_avty_to_doc(topic_t, doc.as<JsonObject>(), seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond);
publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
} }
@@ -1401,4 +1421,41 @@ std::string Mqtt::tag_to_topic(uint8_t device_type, uint8_t tag) {
} }
} }
// adds "availability" section to HA Discovery config
void Mqtt::add_avty_to_doc(const char * state_t, const JsonObject & doc, const char * cond1, const char * cond2, const char * negcond) {
const char * tpl_draft = "{{'online' if %s else 'offline'}}";
char tpl[150];
JsonArray avty = doc.createNestedArray("avty");
StaticJsonDocument<512> avty_json;
snprintf(tpl, sizeof(tpl), "%s/status", mqtt_base_.c_str());
avty_json["t"] = tpl;
snprintf(tpl, sizeof(tpl), tpl_draft, "value == 'online'");
avty_json["val_tpl"] = tpl;
avty.add(avty_json);
avty_json.clear();
avty_json["t"] = state_t;
snprintf(tpl, sizeof(tpl), tpl_draft, cond1 == nullptr ? "value is defined" : cond1);
avty_json["val_tpl"] = tpl;
avty.add(avty_json);
if (cond2 != nullptr) {
avty_json.clear();
avty_json["t"] = state_t;
snprintf(tpl, sizeof(tpl), tpl_draft, cond2);
avty_json["val_tpl"] = tpl;
avty.add(avty_json);
}
if (negcond != nullptr) {
avty_json.clear();
avty_json["t"] = state_t;
snprintf(tpl, sizeof(tpl), "{{'offline' if %s else 'online'}}", negcond);
avty_json["val_tpl"] = tpl;
avty.add(avty_json);
}
doc["avty_mode"] = "all";
}
} // namespace emsesp } // namespace emsesp

View File

@@ -240,6 +240,9 @@ class Mqtt {
static std::string tag_to_topic(uint8_t device_type, uint8_t tag); static std::string tag_to_topic(uint8_t device_type, uint8_t tag);
static void
add_avty_to_doc(const char * state_t, const JsonObject & doc, const char * cond1 = nullptr, const char * cond2 = nullptr, const char * negcond = nullptr);
struct QueuedMqttMessage { struct QueuedMqttMessage {
const uint32_t id_; const uint32_t id_;
const std::shared_ptr<const MqttMessage> content_; const std::shared_ptr<const MqttMessage> content_;

View File

@@ -168,8 +168,8 @@ void Shower::set_shower_state(bool state, bool force) {
doc["stat_t"] = stat_t; doc["stat_t"] = stat_t;
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
doc["pl_on"] = true; doc["pl_on"] = "true";
doc["pl_off"] = false; doc["pl_off"] = "false";
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) { } else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
doc["pl_on"] = 1; doc["pl_on"] = 1;
doc["pl_off"] = 0; doc["pl_off"] = 0;
@@ -183,6 +183,9 @@ void Shower::set_shower_state(bool state, bool force) {
JsonArray ids = dev.createNestedArray("ids"); JsonArray ids = dev.createNestedArray("ids");
ids.add("ems-esp"); ids.add("ems-esp");
// add "availability" section
Mqtt::add_avty_to_doc(stat_t, doc.as<JsonObject>());
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE]; char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str()); snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag Mqtt::publish_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag

View File

@@ -659,7 +659,6 @@ void System::network_init(bool refresh) {
} }
last_system_check_ = 0; // force the LED to go from fast flash to pulse last_system_check_ = 0; // force the LED to go from fast flash to pulse
send_heartbeat();
if (phy_type_ == PHY_type::PHY_TYPE_NONE) { if (phy_type_ == PHY_type::PHY_TYPE_NONE) {
return; return;
@@ -715,7 +714,6 @@ void System::system_check() {
digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON); digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
#endif #endif
} }
send_heartbeat();
} else { } else {
// turn off LED so we're ready to the flashes // turn off LED so we're ready to the flashes
if (led_gpio_) { if (led_gpio_) {

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.5.1-dev.0" #define EMSESP_APP_VERSION "3.5.2-patch.0"

View File

@@ -136,7 +136,7 @@ void WebLogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
log_messages_.pop_front(); log_messages_.pop_front();
} }
log_messages_.emplace_back(log_message_id_++, std::move(message)); log_messages_.emplace_back(++log_message_id_, std::move(message));
EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) { EMSESP::esp8266React.getNTPSettingsService()->read([&](NTPSettings & settings) {
if (!settings.enabled || (time(nullptr) < 1500000000L)) { if (!settings.enabled || (time(nullptr) < 1500000000L)) {
@@ -191,7 +191,7 @@ char * WebLogService::messagetime(char * out, const uint64_t t, const size_t buf
// send to web eventsource // send to web eventsource
void WebLogService::transmit(const QueuedLogMessage & message) { void WebLogService::transmit(const QueuedLogMessage & message) {
auto jsonDocument = DynamicJsonDocument(EMSESP_JSON_SIZE_MEDIUM); StaticJsonDocument<EMSESP_JSON_SIZE_MEDIUM> jsonDocument;
JsonObject logEvent = jsonDocument.to<JsonObject>(); JsonObject logEvent = jsonDocument.to<JsonObject>();
char time_string[25]; char time_string[25];
@@ -212,33 +212,10 @@ void WebLogService::transmit(const QueuedLogMessage & message) {
// send the complete log buffer to the API, not filtering on log level // send the complete log buffer to the API, not filtering on log level
void WebLogService::fetchLog(AsyncWebServerRequest * request) { void WebLogService::fetchLog(AsyncWebServerRequest * request) {
// auto * response = new MsgpackAsyncJsonResponse(false, EMSESP_JSON_SIZE_LARGE_DYN + 192 * log_messages_.size()); log_message_id_tail_ = 0;
size_t buffer = EMSESP_JSON_SIZE_XLARGE_DYN + 192 * log_messages_.size(); request->send(200);
auto * response = new MsgpackAsyncJsonResponse(false, buffer);
while (!response->getSize()) {
delete response;
buffer -= 1024;
response = new MsgpackAsyncJsonResponse(false, buffer);
} }
JsonObject root = response->getRoot();
JsonArray log = root.createNestedArray("events");
log_message_id_tail_ = log_messages_.back().id_;
last_transmit_ = uuid::get_uptime_ms();
for (const auto & message : log_messages_) {
JsonObject logEvent = log.createNestedObject();
char time_string[25];
logEvent["t"] = messagetime(time_string, message.content_->uptime_ms, sizeof(time_string));
logEvent["l"] = message.content_->level;
logEvent["i"] = message.id_;
logEvent["n"] = message.content_->name;
logEvent["m"] = message.content_->text;
}
log_message_id_tail_ = log_messages_.back().id_;
response->setLength();
request->send(response);
}
// sets the values like level after a POST // sets the values like level after a POST
void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) { void WebLogService::setValues(AsyncWebServerRequest * request, JsonVariant & json) {

View File

@@ -47,6 +47,7 @@ void WebSettings::read(WebSettings & settings, JsonObject & root) {
root["syslog_mark_interval"] = settings.syslog_mark_interval; root["syslog_mark_interval"] = settings.syslog_mark_interval;
root["syslog_host"] = settings.syslog_host; root["syslog_host"] = settings.syslog_host;
root["syslog_port"] = settings.syslog_port; root["syslog_port"] = settings.syslog_port;
root["boiler_heatingoff"] = settings.boiler_heatingoff;
root["shower_timer"] = settings.shower_timer; root["shower_timer"] = settings.shower_timer;
root["shower_alert"] = settings.shower_alert; root["shower_alert"] = settings.shower_alert;
root["shower_alert_coldshot"] = settings.shower_alert_coldshot; root["shower_alert_coldshot"] = settings.shower_alert_coldshot;
@@ -240,38 +241,40 @@ StateUpdateResult WebSettings::update(JsonObject & root, WebSettings & settings)
settings.low_clock = root["low_clock"] | false; settings.low_clock = root["low_clock"] | false;
check_flag(prev, settings.low_clock, ChangeFlags::RESTART); check_flag(prev, settings.low_clock, ChangeFlags::RESTART);
String old_local = settings.locale;
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
EMSESP::system_.locale(settings.locale);
#ifndef EMSESP_STANDALONE
if (!old_local.equals(settings.locale)) {
add_flags(ChangeFlags::RESTART); // force restart
}
#endif
// //
// these may need mqtt restart to rebuild HA discovery topics // these may need mqtt restart to rebuild HA discovery topics
// //
prev = settings.bool_format; prev = settings.bool_format;
settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT; settings.bool_format = root["bool_format"] | EMSESP_DEFAULT_BOOL_FORMAT;
EMSESP::system_.bool_format(settings.bool_format); EMSESP::system_.bool_format(settings.bool_format);
if (Mqtt::ha_enabled()) if (Mqtt::ha_enabled()) {
check_flag(prev, settings.bool_format, ChangeFlags::MQTT); check_flag(prev, settings.bool_format, ChangeFlags::MQTT);
}
prev = settings.enum_format; prev = settings.enum_format;
settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT; settings.enum_format = root["enum_format"] | EMSESP_DEFAULT_ENUM_FORMAT;
EMSESP::system_.enum_format(settings.enum_format); EMSESP::system_.enum_format(settings.enum_format);
if (Mqtt::ha_enabled()) if (Mqtt::ha_enabled()) {
check_flag(prev, settings.enum_format, ChangeFlags::MQTT); check_flag(prev, settings.enum_format, ChangeFlags::MQTT);
}
String old_locale = settings.locale;
settings.locale = root["locale"] | EMSESP_DEFAULT_LOCALE;
EMSESP::system_.locale(settings.locale);
if (Mqtt::ha_enabled() && !old_locale.equals(settings.locale)) {
add_flags(ChangeFlags::MQTT);
}
// //
// without checks or necessary restarts... // without checks or necessary restarts...
// //
settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW; settings.trace_raw = root["trace_raw"] | EMSESP_DEFAULT_TRACELOG_RAW;
EMSESP::trace_raw(settings.trace_raw); EMSESP::trace_raw(settings.trace_raw);
settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API; settings.notoken_api = root["notoken_api"] | EMSESP_DEFAULT_NOTOKEN_API;
settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW; settings.solar_maxflow = root["solar_maxflow"] | EMSESP_DEFAULT_SOLAR_MAXFLOW;
settings.boiler_heatingoff = root["boiler_heatingoff"] | EMSESP_DEFAULT_BOILER_HEATINGOFF;
settings.fahrenheit = root["fahrenheit"] | false; settings.fahrenheit = root["fahrenheit"] | false;
EMSESP::system_.fahrenheit(settings.fahrenheit); EMSESP::system_.fahrenheit(settings.fahrenheit);

View File

@@ -33,6 +33,7 @@ class WebSettings {
String locale; String locale;
uint8_t tx_mode; uint8_t tx_mode;
uint8_t ems_bus_id; uint8_t ems_bus_id;
bool boiler_heatingoff;
bool shower_timer; bool shower_timer;
bool shower_alert; bool shower_alert;
uint8_t shower_alert_trigger; uint8_t shower_alert_trigger;