diff --git a/platformio.ini b/platformio.ini
index 02ab00239..515665283 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -83,7 +83,7 @@ board_build.partitions = esp32_partition_4M.csv
build_flags = ${common.build_flags} -Os
build_unflags = ${common.unbuild_flags}
-[env:esp32_4M+]
+[env:esp32_4Mplus]
extends = espressi32_base
extra_scripts =
pre:scripts/build_interface.py
diff --git a/src/device_library.h b/src/device_library.h
index 1a9237568..58ee81ee8 100644
--- a/src/device_library.h
+++ b/src/device_library.h
@@ -150,6 +150,9 @@
// Heat Pumps - 0x53
{248, DeviceType::HEATPUMP, "Hybrid Manager HM200", DeviceFlags::EMS_DEVICE_FLAG_NONE},
+// Ventilation - 0x51
+{231, DeviceType::VENTILATION, "Logavent HRV176", DeviceFlags::EMS_DEVICE_FLAG_NONE},
+
// Heatsource - 0x60
{228, DeviceType::HEATSOURCE, "AM200", DeviceFlags::EMS_DEVICE_FLAG_NONE}, // alternative heatsource
diff --git a/src/devices/ventilation.cpp b/src/devices/ventilation.cpp
new file mode 100644
index 000000000..65d01d9fa
--- /dev/null
+++ b/src/devices/ventilation.cpp
@@ -0,0 +1,96 @@
+/*
+ * EMS-ESP - https://github.com/emsesp/EMS-ESP
+ * Copyright 2020-2023 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ventilation.h"
+
+namespace emsesp {
+
+REGISTER_FACTORY(Ventilation, EMSdevice::DeviceType::VENTILATION);
+
+Ventilation::Ventilation(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand)
+ : EMSdevice(device_type, device_id, product_id, version, name, flags, brand) {
+ // HRV176 module, device_id 0x51
+ register_telegram_type(0x56B, "VentilationMode", true, MAKE_PF_CB(process_ModeMessage));
+ register_telegram_type(0x585, "Blowerspeed", false, MAKE_PF_CB(process_BlowerMessage));
+ register_telegram_type(0x583, "VentilationMonitor", false, MAKE_PF_CB(process_MonitorMessage));
+ register_telegram_type(0x5D9, "Airquality", false, MAKE_PF_CB(process_VOCMessage));
+ // register_telegram_type(0x5, "VentilationSet", true, MAKE_PF_CB(process_SetMessage));
+
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &outFresh_, DeviceValueType::SHORT, FL_(outFresh), DeviceValueUOM::DEGREES);
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &inFresh_, DeviceValueType::SHORT, FL_(inFresh), DeviceValueUOM::DEGREES);
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &outEx_, DeviceValueType::SHORT, FL_(outEx), DeviceValueUOM::DEGREES);
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &inEx_, DeviceValueType::SHORT, FL_(inEx), DeviceValueUOM::DEGREES);
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &ventInSpeed_, DeviceValueType::UINT, FL_(ventInSpeed), DeviceValueUOM::PERCENT);
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &ventOutSpeed_, DeviceValueType::UINT, FL_(ventOutSpeed), DeviceValueUOM::PERCENT);
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &mode_, DeviceValueType::ENUM, FL_(enum_ventMode), FL_(ventInSpeed), DeviceValueUOM::NONE, MAKE_CF_CB(set_ventMode));
+ register_device_value(DeviceValueTAG::TAG_DEVICE_DATA, &voc_, DeviceValueType::USHORT, FL_(airquality), DeviceValueUOM::NONE);
+}
+
+// message
+void Ventilation::process_SetMessage(std::shared_ptr telegram) {
+}
+
+// message 583
+void Ventilation::process_MonitorMessage(std::shared_ptr telegram) {
+ // has_update(telegram, outFresh_, ?);
+ // has_update(telegram, inFresh_, ?);
+ // has_update(telegram, outEx_, ?);
+ // has_update(telegram, inEx_, ?);
+}
+
+// message 575 10 bytes
+// data: 02 02 46 46 00 00 FF 80 00 01
+// 0-level out, 1-level in, 2-mod out, 3-mod in, 9-mode:1-manual/2-auto/3-prog
+
+// message 585 26 bytes long
+// Data: 46 46 00 00 00 77 00 03 F4 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+void Ventilation::process_BlowerMessage(std::shared_ptr telegram) {
+ has_update(telegram, ventOutSpeed_, 0);
+ has_update(telegram, ventInSpeed_, 1);
+}
+
+// message 0x05D9, data: 03 9C FF
+void Ventilation::process_VOCMessage(std::shared_ptr telegram) {
+ has_update(telegram, voc_, 0);
+}
+
+// message 0x56B
+// level 0=0, 1=1, 2=2, 3=3, 4= 4, Auto 0xFF, demand 5, sleep 6, intense 7, bypass-8, party 9, fireplace 0A
+void Ventilation::process_ModeMessage(std::shared_ptr telegram) {
+ has_enumupdate(telegram, mode_, 0, -1);
+}
+
+bool Ventilation::set_ventMode(const char * value, const int8_t id) {
+ uint8_t v;
+ if (!Helpers::value2enum(value, v, FL_(enum_ventMode))) {
+ return false;
+ }
+ write_command(0x56B, 0, v - 1, 0x56B);
+ return true;
+}
+
+bool Ventilation::set_filter(const char * value, const int8_t id) {
+ int v;
+ if (!Helpers::value2number(value, v)) {
+ return false;
+ }
+ // write_command(0x5xx, 0, v, 0x5xx);
+ return true;
+}
+
+} // namespace emsesp
diff --git a/src/devices/ventilation.h b/src/devices/ventilation.h
new file mode 100644
index 000000000..0d0b2a304
--- /dev/null
+++ b/src/devices/ventilation.h
@@ -0,0 +1,117 @@
+/*
+ * EMS-ESP - https://github.com/emsesp/EMS-ESP
+ * Copyright 2020-2023 Paul Derbyshire
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef EMSESP_VENTILATION_H
+#define EMSESP_VENTILATION_H
+
+#include "emsesp.h"
+
+namespace emsesp {
+
+class Ventilation : public EMSdevice {
+ public:
+ Ventilation(uint8_t device_type, uint8_t device_id, uint8_t product_id, const char * version, const char * name, uint8_t flags, uint8_t brand);
+
+ private:
+ uint8_t mode_;
+ int16_t outFresh_;
+ int16_t inFresh_;
+ int16_t inEx_;
+ int16_t outEx_;
+ uint16_t voc_;
+ uint8_t bypass_;
+ uint16_t filterRemain_;
+ uint8_t ventInSpeed_;
+ uint8_t ventOutSpeed_;
+
+ // handlers: 0x056B 0x0575 0x0583 0x0585 0x0586 0x0587 0x0588 0x058D 0x058E 0x058F 0x0590 0x05CF 0x05D9 0x05E3
+ void process_SetMessage(std::shared_ptr telegram);
+ void process_MonitorMessage(std::shared_ptr telegram);
+ void process_ModeMessage(std::shared_ptr telegram); // 0x56B
+ void process_BlowerMessage(std::shared_ptr telegram); // 0x56B
+ void process_VOCMessage(std::shared_ptr telegram); // 0x56B
+
+ bool set_ventMode(const char * value, const int8_t id);
+ bool set_filter(const char * value, const int8_t id);
+
+
+ /* Sensors:
+ outdoor air temp (außenluft)
+ supply air temp (zuluft)
+ extract air temp (abluft)
+ away air temp (fortluft)
+ supply blower (zuluftgebläse)
+ supply blower mod (zuluftebläse drehzahl)
+ away blower (abluftgebläse)
+ away blower mod (abluftgebläse drehzahl)
+ Anschlussvariante
+ el. vorheizer
+ ext. el. vorheizreg.
+ nachheiz zulufttemp
+ mischer öffnen
+ mischer schließen
+ mischerposition
+ zuluft temp soll
+ zuluft temp ist
+ leistung nachheizreg.
+ erdwärmetauscher klappe
+ solekreispumpe
+ abluftfeuchte
+ abluftqualität
+ raumluftfechte
+ raumluftqualität
+ luftfeuchte fernbed. 1..4
+ */
+ /* Parameters:
+ Gerätetyp,
+ Nennvolumentstrom,
+ Filterlaufzeit 1-6-12 m
+ Filterwechsel confirm CMD
+ Lüftungsfrostschutz: _el._preheat_, Disballance | Interval
+ Ext. Frostschutz: on/_off_
+ Bypass _on_, off
+ min. outdoortemp 12 15 19 °C
+ max. outdoortemp 21-24-30 C
+ Enthalpie Wärmetauscher instaliert nein-ja
+ Feuchteschutz AUs/ 1-24 h
+ Lüfterstufe 1-4, Drehzahlanpassung
+ ext. Luftfeuchtefühler inst.? _nein_, ja
+ Abluftfeuchtefühler inst.? _nein_, ja
+ Luftfeuchte Fernbed. _nein_, ja
+ Luftfeuchte: trocken, _normal_, feucht
+ Abluftqualitätsfühler inst. _ja_, nein
+ ext. Luftqualfühl? _nein_, ja
+ Lufqualität: ausreichend, _normal_, hoch
+ el. Nachheizregister inst. _nein_, ja
+ Nachheiz-Zuluft temp: 10-22-30 °C
+ Erdwärmetauscher inst? _nein_, Luft, Sole
+ Taster Funktion: nein, einschlafen, intensiv, bypass, party, kamin
+ ext. Störung aktivieren: _nein_, ja, invertiert
+ Dauer einschlafen: 15-60-120 min
+ Dauer Intensiv: 5-15-60 min
+ Dauer Bypass Abluft: 1-8-12 h
+ Dauer Bypass: 1-8-12 h
+ Dauer PArty 1-8-12 h
+ Dauer Kamin: 5-10-15 min
+ Volumenstromabgleich 90-100-110 %
+ */
+};
+
+} // namespace emsesp
+
+#endif
diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp
index f7bd9d2e8..1bf1decdf 100644
--- a/src/emsdevice.cpp
+++ b/src/emsdevice.cpp
@@ -137,6 +137,8 @@ const char * EMSdevice::device_type_2_device_name(const uint8_t device_type) {
return F_(heatsource);
case DeviceType::CUSTOM:
return F_(custom);
+ case DeviceType::VENTILATION:
+ return F_(ventilation);
default:
return Helpers::translated_word(FL_(unknown), true);
}
@@ -170,6 +172,8 @@ const char * EMSdevice::device_type_2_device_name_translated() {
return Helpers::translated_word(FL_(pump_device));
case DeviceType::HEATSOURCE:
return Helpers::translated_word(FL_(heatsource_device));
+ case DeviceType::VENTILATION:
+ return Helpers::translated_word(FL_(ventilation_device));
default:
break;
}
@@ -234,6 +238,9 @@ uint8_t EMSdevice::device_name_2_device_type(const char * topic) {
if (!strcmp(lowtopic, F_(custom))) {
return DeviceType::CUSTOM;
}
+ if (!strcmp(lowtopic, F_(ventilation))) {
+ return DeviceType::VENTILATION;
+ }
return DeviceType::UNKNOWN;
}
diff --git a/src/emsdevice.h b/src/emsdevice.h
index 5e2729e88..70826cae2 100644
--- a/src/emsdevice.h
+++ b/src/emsdevice.h
@@ -155,7 +155,7 @@ class EMSdevice {
}
}
- inline void has_enumupdate(std::shared_ptr telegram, uint8_t & value, const uint8_t index, uint8_t s = 0) {
+ inline void has_enumupdate(std::shared_ptr telegram, uint8_t & value, const uint8_t index, int8_t s = 0) {
if (telegram->read_enumvalue(value, index, s)) {
has_update_ = true;
publish_value((void *)&value);
@@ -337,6 +337,7 @@ class EMSdevice {
GENERIC,
HEATSOURCE,
CUSTOM,
+ VENTILATION,
UNKNOWN
};
diff --git a/src/locale_common.h b/src/locale_common.h
index c6640f295..39e930b73 100644
--- a/src/locale_common.h
+++ b/src/locale_common.h
@@ -99,6 +99,7 @@ MAKE_WORD(pump)
MAKE_WORD(heatsource)
MAKE_WORD(scheduler)
MAKE_WORD(custom)
+MAKE_WORD(ventilation)
// brands
MAKE_WORD_CUSTOM(bosch, "Bosch")
@@ -192,6 +193,10 @@ MAKE_NOTRANSLATION(3kW, "3 kW")
MAKE_NOTRANSLATION(4kW, "4 kW")
MAKE_NOTRANSLATION(6kW, "6 kW")
MAKE_NOTRANSLATION(9kW, "9 kW")
+MAKE_NOTRANSLATION(L1, "L1")
+MAKE_NOTRANSLATION(L2, "L2")
+MAKE_NOTRANSLATION(L3, "L3")
+MAKE_NOTRANSLATION(L4, "L4")
// templates - this are not translated and will be saved under options_single
MAKE_NOTRANSLATION(tpl_datetime, "Format: < NTP | dd.mm.yyyy-hh:mm:ss-day(0-6)-dst(0/1) >")
@@ -343,6 +348,9 @@ MAKE_ENUM(enum_blockMode, FL_(off), FL_(auto), FL_(blocking))
MAKE_ENUM(enum_bufConfig, FL_(off), FL_(monovalent), FL_(bivalent))
MAKE_ENUM(enum_blockTerm, FL_(n_o), FL_(n_c))
+// Ventilation
+MAKE_ENUM(enum_ventMode, FL_(auto), FL_(off), FL_(L1), FL_(L2), FL_(L3), FL_(L4), FL_(demand), FL_(sleep), FL_(intense), FL_(bypass), FL_(partymode), FL_(fireplace))
+
#pragma GCC diagnostic pop
// clang-format on
diff --git a/src/locale_translations.h b/src/locale_translations.h
index 00f918cc3..030a2a71b 100644
--- a/src/locale_translations.h
+++ b/src/locale_translations.h
@@ -50,6 +50,7 @@ MAKE_WORD_TRANSLATION(heatsource_device, "Heatsource", "Heizquelle", "Heatsource
MAKE_WORD_TRANSLATION(sensors_device, "Sensors", "Sensoren", "Sensoren", "Sensorer", "Czujniki", "Sensorer", "Capteurs", "Sensör Cihazı")
MAKE_WORD_TRANSLATION(unknown_device, "Unknown", "Unbekannt", "Onbekend", "Okänt", "Nieznane urządzenie", "Ukjent", "Inconnu", "") // TODO translate
MAKE_WORD_TRANSLATION(custom_device, "User defined entities", "Nutzer deklarierte Entitäten", "", "", "", "", "", "") // TODO translate
+MAKE_WORD_TRANSLATION(ventilation_device, "Ventilation", "Lüftung", "", "", "", "", "", "") // TODO translate
// commands
// TODO translate
@@ -265,10 +266,17 @@ MAKE_WORD_TRANSLATION(close, "close", "geschlossen", "Gesloten", "Stängd", "zam
MAKE_WORD_TRANSLATION(cyl1, "cyl 1", "Zyl_1", "Cil 1", "Cyl 1", "cyl 1", "cyl 1", "cyl 1", "cly 1")
MAKE_WORD_TRANSLATION(cyl2, "cyl 2", "Zyl_2", "Cil 2", "Cyl 2", "cyl 2", "cyl 2", "cyl 2", "cly 1")
-// Entity translations
+// ventilation
+MAKE_WORD_TRANSLATION(demand, "demand", "Bedarf")
+MAKE_WORD_TRANSLATION(intense, "intense", "Intensiv")
+MAKE_WORD_TRANSLATION(sleep, "sleep", "Einschlafen")
+MAKE_WORD_TRANSLATION(partymode, "party", "Party")
+MAKE_WORD_TRANSLATION(fireplace, "fireplace", "Kamin")
+
// MQTT Discovery - this is special device entity for 'climate'
MAKE_TRANSLATION(haclimate, "haclimate", "Discovery current room temperature", "Discovery Temperatur", "", "", "termostat w HA", "HA Avlest temp", "", "") // TODO translate
+// Entity translations
// Boiler
MAKE_TRANSLATION(wwtapactivated, "wwtapactivated", "turn on/off", "Durchlauferhitzer aktiv", "zet aan/uit", "på/av", "system przygotowywania", "Varmtvann active", "ecs activée", "")
MAKE_TRANSLATION(reset, "reset", "Reset", "Reset", "Reset", "Nollställ", "kasowanie komunikatu", "nullstill", "reset", "")
@@ -486,6 +494,11 @@ MAKE_TRANSLATION(blockTerm, "blockterm", "config of block terminal", "Konfig. Sp
MAKE_TRANSLATION(blockHyst, "blockhyst", "hyst. for boiler block", "Hysterese Sperrmodus", "Hysterese blokeerterminal", "Hysteres Blockeringsmodul", "tryb blokowania histerezy", "hystrese blokkeringsmodus", "hyst. Blocage chaudière", "")
MAKE_TRANSLATION(releaseWait, "releasewait", "boiler release wait time", "Wartezeit Kessel-Freigabe", "Wachttijd ketel vrijgave", "Väntetid Frisläppning", "czas oczekiwania na zwolnienie kotła", "kjele frigjøringsventetid", "temps attente libération chaudière", "")
+// HIU
+MAKE_TRANSLATION(netFlowTemp, "netflowtemp", "heat network flow temp")
+MAKE_TRANSLATION(cwFlowRate, "cwflowrate", "cold water flow rate")
+MAKE_TRANSLATION(keepWarmTemp, "keepwarmtemp", "keep warm temperature")
+
// the following are dhw for the boiler and automatically tagged with 'dhw'
MAKE_TRANSLATION(wwSelTemp, "wwseltemp", "selected temperature", "gewählte Temperatur", "Geselecteerd temperatuur", "Vald Temperatur", "temperatura wyższa/komfort", "valgt temperatur", "température sélectionnée", "")
MAKE_TRANSLATION(wwSelTempLow, "wwseltemplow", "selected lower temperature", "untere Solltemperatur", "Onderste streeftemperatuur", "Vald lägstatemperatur", "temperatura niższa/eko", "valgt nedre temperatur", "température basse sélectionnée", "")
@@ -752,6 +765,16 @@ MAKE_TRANSLATION(status, "status", "status", "Status", "Status", "Status", "stat
// RF sensor, id 0x40, telegram 0x435
MAKE_TRANSLATION(RFTemp, "rftemp", "RF room temperature sensor", "RF Raumtemperatur Sensor", "RF ruimtetemperatuur sensor", "RF Rumsgivare Temp", "bezprzewodowy czujnik temperatury pomieszczenia", "RF romsgiver temp", "capteur de température de pièce RF", "")
+// ventilation
+MAKE_TRANSLATION(outFresh, "outfresh", "outdoor fresh air", "Außenlufttemp.")
+MAKE_TRANSLATION(inFresh, "infresh", "indoor fresh air", "Zulufttemp.")
+MAKE_TRANSLATION(outEx, "outexhaust", "outdoor exhaust air", "Ablufttemp.")
+MAKE_TRANSLATION(inEx, "inexhaust", "indoor exhaust air", "Fortlufttemp.")
+MAKE_TRANSLATION(ventMode, "ventmode", "ventilation mode", "Belüftungsmodus")
+MAKE_TRANSLATION(ventInSpeed, "ventinspeed", "in blower speed", "Zuluft-Drehzahl")
+MAKE_TRANSLATION(ventOutSpeed, "ventoutspeed", "out blower speed", "Abluft-Drehzahl")
+MAKE_TRANSLATION(airquality, "airquality", "air quality (voc)", "Luftqualität (VOC)")
+
/*
// unknown fields to track (SM10), only for testing
// **** NO TRANSLATION NEEDED ****
diff --git a/src/telegram.h b/src/telegram.h
index 00ec4d305..5f873e159 100644
--- a/src/telegram.h
+++ b/src/telegram.h
@@ -137,7 +137,7 @@ class Telegram {
return (val != value);
}
- bool read_enumvalue(uint8_t & value, const uint8_t index, uint8_t start = 0) const {
+ bool read_enumvalue(uint8_t & value, const uint8_t index, int8_t start = 0) const {
if ((index < this->offset) || ((index - this->offset) >= this->message_length)) {
return false;
}