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; }