diff --git a/components/truma_inetbox/LinBusProtocol.cpp b/components/truma_inetbox/LinBusProtocol.cpp index a3df1bf..00919e3 100644 --- a/components/truma_inetbox/LinBusProtocol.cpp +++ b/components/truma_inetbox/LinBusProtocol.cpp @@ -150,7 +150,7 @@ void LinBusProtocol::lin_msg_diag_single_(const u_int8_t *message, u_int8_t leng //} } else if (broadcast_address && service_identifier == LIN_SID_ASSIGN_NAD && message_length == 6) { if (this->is_matching_identifier_(&message[3])) { - ESP_LOGI(TAG, "Assigned new SID %02X and reset device", message[7]); + ESP_LOGI(TAG, "Assigned new SID %02X", message[7]); // send response with old node address. std::array response = this->lin_empty_response_; @@ -158,9 +158,6 @@ void LinBusProtocol::lin_msg_diag_single_(const u_int8_t *message, u_int8_t leng response[1] = 1; /* bytes length*/ response[2] = LIN_SID_ASSIGN_NAD_RESPONSE; - // assumption an assign new SID occurs as part of init process. - this->lin_reset_device(); - this->prepare_update_msg_(response); this->lin_node_address_ = message[7]; } diff --git a/components/truma_inetbox/TrumaiNetBoxApp.cpp b/components/truma_inetbox/TrumaiNetBoxApp.cpp index 7857175..67cc173 100644 --- a/components/truma_inetbox/TrumaiNetBoxApp.cpp +++ b/components/truma_inetbox/TrumaiNetBoxApp.cpp @@ -200,6 +200,10 @@ StatusFrameAirconResponse *TrumaiNetBoxApp::update_aircon_prepare() { // prepare status response this->update_status_aircon_ = {}; + this->update_status_aircon_.mode = this->status_aircon_.mode; + this->update_status_aircon_.operation = this->status_aircon_.operation; + this->update_status_aircon_.energy_mix = this->status_aircon_.energy_mix; + this->update_status_aircon_.target_temp_aircon = this->status_aircon_.target_temp_aircon; this->update_status_aircon_prepared_ = true; return &this->update_status_aircon_; @@ -301,21 +305,11 @@ const u_int8_t *TrumaiNetBoxApp::lin_multiframe_recieved(const u_int8_t *message status_frame_create_empty(response_frame, STATUS_FRAME_AIRCON_RESPONSE, sizeof(StatusFrameAirconResponse), this->message_counter++); - response_frame->inner.airconResponse.unknown_01 = this->update_status_aircon_.unknown_01; + response_frame->inner.airconResponse.mode = this->update_status_aircon_.mode; response_frame->inner.airconResponse.unknown_02 = this->update_status_aircon_.unknown_02; - response_frame->inner.airconResponse.unknown_03 = this->update_status_aircon_.unknown_03; - response_frame->inner.airconResponse.unknown_04 = this->update_status_aircon_.unknown_04; - response_frame->inner.airconResponse.target_temp_room = this->update_status_aircon_.target_temp_room; - response_frame->inner.airconResponse.unknown_07 = this->update_status_aircon_.unknown_07; - response_frame->inner.airconResponse.unknown_08 = this->update_status_aircon_.unknown_08; - response_frame->inner.airconResponse.current_temp_aircon = this->update_status_aircon_.current_temp_aircon; - response_frame->inner.airconResponse.unknown_11 = this->update_status_aircon_.unknown_11; - response_frame->inner.airconResponse.unknown_12 = this->update_status_aircon_.unknown_12; - response_frame->inner.airconResponse.unknown_13 = this->update_status_aircon_.unknown_13; - response_frame->inner.airconResponse.unknown_14 = this->update_status_aircon_.unknown_14; - response_frame->inner.airconResponse.unknown_15 = this->update_status_aircon_.unknown_15; - response_frame->inner.airconResponse.unknown_16 = this->update_status_aircon_.unknown_16; - response_frame->inner.airconResponse.current_temp_room = this->update_status_aircon_.current_temp_room; + response_frame->inner.airconResponse.operation = this->update_status_aircon_.operation; + response_frame->inner.airconResponse.energy_mix = this->update_status_aircon_.energy_mix; + response_frame->inner.airconResponse.target_temp_aircon = this->update_status_aircon_.target_temp_aircon; status_frame_calculate_checksum(response_frame); (*return_len) = sizeof(StatusFrameHeader) + sizeof(StatusFrameAirconResponse); @@ -485,7 +479,7 @@ const u_int8_t *TrumaiNetBoxApp::lin_multiframe_recieved(const u_int8_t *message this->status_config_valid_ = true; this->status_config_updated_ = true; - ESP_LOGD(TAG, "StatusFrameConfig Offset: %.1f", offset_code_to_decimal(this->status_config_.temp_offset)); + ESP_LOGD(TAG, "StatusFrameConfig Offset: %.1f", temp_code_to_decimal(this->status_config_.temp_offset)); return response; } else if (header->message_type == STATUS_FRAME_DEVICES && header->message_length == sizeof(StatusFrameDevice)) { diff --git a/components/truma_inetbox/TrumaiNetBoxApp.h b/components/truma_inetbox/TrumaiNetBoxApp.h index 2d352a9..67efb5a 100644 --- a/components/truma_inetbox/TrumaiNetBoxApp.h +++ b/components/truma_inetbox/TrumaiNetBoxApp.h @@ -82,34 +82,42 @@ enum class TargetTemp : u_int16_t { // 200C TARGET_TEMP_WATER_BOOST = (200 + 273) * 10, + TARGET_TEMP_05C = (5 + 273) * 10, + TARGET_TEMP_06C = (6 + 273) * 10, + TARGET_TEMP_07C = (7 + 273) * 10, + TARGET_TEMP_08C = (8 + 273) * 10, + TARGET_TEMP_09C = (9 + 273) * 10, + TARGET_TEMP_10C = (10 + 273) * 10, + TARGET_TEMP_11C = (11 + 273) * 10, + TARGET_TEMP_12C = (12 + 273) * 10, + TARGET_TEMP_13C = (13 + 273) * 10, + TARGET_TEMP_14C = (14 + 273) * 10, + TARGET_TEMP_15C = (15 + 273) * 10, + TARGET_TEMP_16C = (16 + 273) * 10, + TARGET_TEMP_17C = (17 + 273) * 10, + TARGET_TEMP_18C = (18 + 273) * 10, + TARGET_TEMP_19C = (19 + 273) * 10, + TARGET_TEMP_20C = (20 + 273) * 10, + TARGET_TEMP_21C = (21 + 273) * 10, + TARGET_TEMP_22C = (22 + 273) * 10, + TARGET_TEMP_23C = (23 + 273) * 10, + TARGET_TEMP_24C = (24 + 273) * 10, + TARGET_TEMP_25C = (25 + 273) * 10, + TARGET_TEMP_26C = (26 + 273) * 10, + TARGET_TEMP_27C = (27 + 273) * 10, + TARGET_TEMP_28C = (28 + 273) * 10, + TARGET_TEMP_29C = (29 + 273) * 10, + TARGET_TEMP_30C = (30 + 273) * 10, + TARGET_TEMP_31C = (31 + 273) * 10, + TARGET_TEMP_ROOM_MIN = (5 + 273) * 10, - TARGET_TEMP_ROOM_05C = (5 + 273) * 10, - TARGET_TEMP_ROOM_06C = (6 + 273) * 10, - TARGET_TEMP_ROOM_07C = (7 + 273) * 10, - TARGET_TEMP_ROOM_08C = (8 + 273) * 10, - TARGET_TEMP_ROOM_09C = (9 + 273) * 10, - TARGET_TEMP_ROOM_10C = (10 + 273) * 10, - TARGET_TEMP_ROOM_11C = (11 + 273) * 10, - TARGET_TEMP_ROOM_12C = (12 + 273) * 10, - TARGET_TEMP_ROOM_13C = (13 + 273) * 10, - TARGET_TEMP_ROOM_14C = (14 + 273) * 10, - TARGET_TEMP_ROOM_15C = (15 + 273) * 10, - TARGET_TEMP_ROOM_16C = (16 + 273) * 10, - TARGET_TEMP_ROOM_17C = (17 + 273) * 10, - TARGET_TEMP_ROOM_18C = (18 + 273) * 10, - TARGET_TEMP_ROOM_19C = (19 + 273) * 10, - TARGET_TEMP_ROOM_20C = (20 + 273) * 10, - TARGET_TEMP_ROOM_21C = (21 + 273) * 10, - TARGET_TEMP_ROOM_22C = (22 + 273) * 10, - TARGET_TEMP_ROOM_23C = (23 + 273) * 10, - TARGET_TEMP_ROOM_24C = (24 + 273) * 10, - TARGET_TEMP_ROOM_25C = (25 + 273) * 10, - TARGET_TEMP_ROOM_26C = (26 + 273) * 10, - TARGET_TEMP_ROOM_27C = (27 + 273) * 10, - TARGET_TEMP_ROOM_28C = (28 + 273) * 10, - TARGET_TEMP_ROOM_29C = (29 + 273) * 10, - TARGET_TEMP_ROOM_30C = (30 + 273) * 10, TARGET_TEMP_ROOM_MAX = (30 + 273) * 10, + + TARGET_TEMP_AIRCON_MIN = (16 + 273) * 10, + TARGET_TEMP_AIRCON_MAX = (31 + 273) * 10, + + TARGET_TEMP_AIRCON_AUTO_MIN = (18 + 273) * 10, + TARGET_TEMP_AIRCON_AUTO_MAX = (25 + 273) * 10, }; enum class EnergyMix : u_int8_t { @@ -151,20 +159,6 @@ enum class ResponseAckResult : u_int8_t { RESPONSE_ACK_RESULT_ERROR_INVALID_ID = 0x3, }; -enum class TempOffset : u_int8_t { - TEMP_OFFSET_0_0C = (u_int8_t) ((-0.0f + 17) * 10), - TEMP_OFFSET_0_5C = (u_int8_t) ((-0.5f + 17) * 10), - TEMP_OFFSET_1_0C = (u_int8_t) ((-1.0f + 17) * 10), - TEMP_OFFSET_1_5C = (u_int8_t) ((-1.5f + 17) * 10), - TEMP_OFFSET_2_0C = (u_int8_t) ((-2.0f + 17) * 10), - TEMP_OFFSET_2_5C = (u_int8_t) ((-2.5f + 17) * 10), - TEMP_OFFSET_3_0C = (u_int8_t) ((-3.0f + 17) * 10), - TEMP_OFFSET_3_5C = (u_int8_t) ((-3.5f + 17) * 10), - TEMP_OFFSET_4_0C = (u_int8_t) ((-4.0f + 17) * 10), - TEMP_OFFSET_4_5C = (u_int8_t) ((-4.5f + 17) * 10), - TEMP_OFFSET_5_0C = (u_int8_t) ((-5.0f + 17) * 10), -}; - enum class ClockMode : u_int8_t { CLOCK_MODE_24H = 0x0, CLOCK_MODE_12H = 0x1, @@ -294,10 +288,10 @@ struct StatusFrameConfig { // NOLINT(altera-struct-pack-align) // 0x01 .. 0x0A u_int8_t display_brightness; Language language; - u_int8_t unknown_2; // 0xB4 - u_int8_t unknown_3; // 0x0A - TempOffset temp_offset; - u_int8_t unknown_5; // 0x0A + // Mit „AC SET“ wird ein Offset zwischen Kühlen und Heizen eingestellt. + // Die Einstellung ist in Schritten von 0,5 °C im Bereich von 0 °C bis +5 °C möglich. + TargetTemp ac_offset; + TargetTemp temp_offset; OperatingUnits temp_units; u_int8_t unknown_6; u_int8_t unknown_7; @@ -341,18 +335,28 @@ struct StatusFrameDevice { // NOLINT(altera-struct-pack-align) u_int8_t unknown_3; } __attribute__((packed)); +enum class AirconMode : u_int8_t { + // Auto - 18 to 25 + OFF = 0x00, + AC_VENTILATION = 0x04, + AC_COOLING = 0x05, +}; + +enum class AirconOperation : u_int8_t { + AC_ONLY = 0x71, + // Heater and Aircon + AUTO = 0x72, +}; + // Length 18 (0x12) // TODO struct StatusFrameAircon { // NOLINT(altera-struct-pack-align) - // Mode? 00 - OFF, 04 - AC Ventilation, 05 - AC Cooling - u_int8_t unknown_01; + AirconMode mode; // 0x00 u_int8_t unknown_02; - // 0x71 - u_int8_t unknown_03; - // 0x01 - u_int8_t unknown_04; - TargetTemp target_temp_room; + AirconOperation operation; + EnergyMix energy_mix; + TargetTemp target_temp_aircon; // 0x00 u_int8_t unknown_07; // 0x00 @@ -363,10 +367,7 @@ struct StatusFrameAircon { // NOLINT(altera-struct-pack-align) u_int8_t unknown_11; // 0x00 u_int8_t unknown_12; - // 0x00 - u_int8_t unknown_13; - // 0x00 - u_int8_t unknown_14; + ElectricPowerLevel el_power_level; // 0x00 u_int8_t unknown_15; // 0x00 @@ -374,55 +375,29 @@ struct StatusFrameAircon { // NOLINT(altera-struct-pack-align) TargetTemp current_temp_room; } __attribute__((packed)); -// TODO struct StatusFrameAirconResponse { // NOLINT(altera-struct-pack-align) - // Mode? 00 - OFF, 04 - AC Ventilation, 05 - AC Cooling - u_int8_t unknown_01; + AirconMode mode; // 0x00 u_int8_t unknown_02; - // 0x71 - u_int8_t unknown_03; - // 0x01 - u_int8_t unknown_04; - TargetTemp target_temp_room; - // 0x00 - u_int8_t unknown_07; - // 0x00 - u_int8_t unknown_08; - // No idea why two current_temp - TargetTemp current_temp_aircon; - // 0x00 - u_int8_t unknown_11; - // 0x00 - u_int8_t unknown_12; - // 0x00 - u_int8_t unknown_13; - // 0x00 - u_int8_t unknown_14; - // 0x00 - u_int8_t unknown_15; - // 0x00 - u_int8_t unknown_16; - TargetTemp current_temp_room; + AirconOperation operation; + EnergyMix energy_mix; + TargetTemp target_temp_aircon; } __attribute__((packed)); // Length 18 (0x12) // TODO struct StatusFrameAircon2 { // NOLINT(altera-struct-pack-align) - u_int8_t unknown_01; // 0x01 - u_int8_t unknown_02; // 0x00 - u_int8_t unknown_03; // 0x01 - u_int8_t unknown_04; // 0x00 - u_int8_t unknown_05; // 0x00 - u_int8_t unknown_06; // 0x00 - u_int8_t unknown_07; // 0x00 - u_int8_t unknown_08; // 0x00 - u_int8_t unknown_09; // 0x00 - u_int8_t unknown_10; // 0x00 - u_int8_t unknown_11; // 0x00 - u_int8_t unknown_12; // 0x00 - u_int8_t unknown_13; // 0x00 - u_int8_t unknown_14; // 0x00 + EnergyMix energy_mix_a; + u_int8_t unknown_02; // 0x00 + EnergyMix energy_mix_b; + u_int8_t unknown_04; // 0x00 + u_int8_t unknown_05; // 0x00 + u_int8_t unknown_06; // 0x00 + TargetTemp target_temp_aircon_auto; + ElectricPowerLevel el_power_level_a; + u_int8_t unknown_11; // 0x00 + u_int8_t unknown_12; // 0x00 + ElectricPowerLevel el_power_level_b; TargetTemp current_temp; TargetTemp target_temp; } __attribute__((packed)); @@ -432,51 +407,51 @@ struct StatusFrameAircon2 { // NOLINT(altera-struct-pack-align) struct StatusFrameAirconInit { // NOLINT(altera-struct-pack-align) u_int8_t unknown_01; // 0x00 u_int8_t unknown_02; // 0x00 - u_int8_t unknown_03; // 0x71 - u_int8_t unknown_04; // 0x01 - u_int8_t unknown_05; // 0x00 - u_int8_t unknown_06; // 0x00 - u_int8_t unknown_07; // 0x00 - u_int8_t unknown_08; // 0x00 - u_int8_t unknown_09; // 0x00 - u_int8_t unknown_10; // 0x00 - u_int8_t unknown_11; // 0x00 - u_int8_t unknown_12; // 0x00 - u_int8_t unknown_13; // 0x00 - u_int8_t unknown_14; // 0x00 - u_int8_t unknown_15; // 0x00 - u_int8_t unknown_16; // 0x00 - u_int8_t unknown_17; // 0x00 - u_int8_t unknown_18; // 0x00 - u_int8_t unknown_19; // 0x00 - u_int8_t unknown_20; // 0x00 - u_int8_t unknown_21; // 0x00 - u_int8_t unknown_22; // 0x00 + AirconOperation operation; + EnergyMix energy_mix; + u_int8_t unknown_05; // 0x00 + u_int8_t unknown_06; // 0x00 + u_int8_t unknown_07; // 0x00 + u_int8_t unknown_08; // 0x00 + u_int8_t unknown_09; // 0x00 + u_int8_t unknown_10; // 0x00 + u_int8_t unknown_11; // 0x00 + u_int8_t unknown_12; // 0x00 + u_int8_t unknown_13; // 0x00 + u_int8_t unknown_14; // 0x00 + u_int8_t unknown_15; // 0x00 + u_int8_t unknown_16; // 0x00 + u_int8_t unknown_17; // 0x00 + u_int8_t unknown_18; // 0x00 + u_int8_t unknown_19; // 0x00 + u_int8_t unknown_20; // 0x00 + u_int8_t unknown_21; // 0x00 + u_int8_t unknown_22; // 0x00 } __attribute__((packed)); // Length 20 (0x14) // TODO struct StatusFrameAirconInit2 { // NOLINT(altera-struct-pack-align) - u_int8_t unknown_01; // 0x01 - u_int8_t unknown_02; // 0x00 - u_int8_t unknown_03; // 0x01 - u_int8_t unknown_04; // 0x00 - u_int8_t unknown_05; // 0x00 - u_int8_t unknown_06; // 0x00 - u_int8_t unknown_07; // 0x00 - u_int8_t unknown_08; // 0x00 - u_int8_t unknown_09; // 0x00 - u_int8_t unknown_10; // 0x00 - u_int8_t unknown_11; // 0x00 - u_int8_t unknown_12; // 0x00 - u_int8_t unknown_13; // 0x00 - u_int8_t unknown_14; // 0x00 - u_int8_t unknown_15; // 0x00 - u_int8_t unknown_16; // 0x00 - u_int8_t unknown_17; // 0x00 - u_int8_t unknown_18; // 0x00 - u_int8_t unknown_19; // 0x00 - u_int8_t unknown_20; // 0x00 + EnergyMix energy_mix_a; + u_int8_t unknown_02; // 0x00 + EnergyMix energy_mix_b; + u_int8_t unknown_04; // 0x00 + u_int8_t unknown_05; // 0x00 + u_int8_t unknown_06; // 0x00 + u_int8_t unknown_07; // 0x00 + u_int8_t unknown_08; // 0x00 + u_int8_t unknown_09; // 0x00 + u_int8_t unknown_10; // 0x00 + u_int8_t unknown_11; // 0x00 + u_int8_t unknown_12; // 0x00 + u_int8_t unknown_13; // 0x00 + u_int8_t unknown_14; // 0x00 + u_int8_t unknown_15; // 0x00 + u_int8_t unknown_16; // 0x00 + u_int8_t unknown_17; // 0x00 + u_int8_t unknown_18; // 0x00 + u_int8_t unknown_19; // 0x00 + u_int8_t unknown_20; // 0x00 } __attribute__((packed)); union StatusFrame { // NOLINT(altera-struct-pack-align) diff --git a/components/truma_inetbox/TrumaiNetBoxApp_automation.cpp b/components/truma_inetbox/TrumaiNetBoxApp_automation.cpp index 8ddade1..42790af 100644 --- a/components/truma_inetbox/TrumaiNetBoxApp_automation.cpp +++ b/components/truma_inetbox/TrumaiNetBoxApp_automation.cpp @@ -56,7 +56,7 @@ bool TrumaiNetBoxApp::action_heater_water(u_int8_t temperature) { } auto heater = this->update_heater_prepare(); - heater->target_temp_water = deciaml_to_water_temp(temperature); + heater->target_temp_water = decimal_to_water_temp(temperature); // Ensure `energy_mix_a` is set. if (heater->target_temp_water != TargetTemp::TARGET_TEMP_OFF && heater->energy_mix_a == EnergyMix::ENERGY_MIX_NONE) { @@ -209,7 +209,7 @@ bool TrumaiNetBoxApp::action_timer_activate(u_int16_t start, u_int16_t stop, u_i } } - timer->timer_target_temp_water = deciaml_to_water_temp(water_temperature); + timer->timer_target_temp_water = decimal_to_water_temp(water_temperature); // If parameter `el_power_level` contains a valid mode use it. if (el_power_level == ElectricPowerLevel::ELECTRIC_POWER_LEVEL_0 || diff --git a/components/truma_inetbox/helpers.cpp b/components/truma_inetbox/helpers.cpp index 9273bf0..9a73bbf 100644 --- a/components/truma_inetbox/helpers.cpp +++ b/components/truma_inetbox/helpers.cpp @@ -31,6 +31,10 @@ float temp_code_to_decimal(u_int16_t val, float zero) { float temp_code_to_decimal(TargetTemp val, float zero) { return temp_code_to_decimal((u_int16_t) val, zero); } +TargetTemp decimal_to_temp(u_int8_t val) { return (TargetTemp) ((((u_int16_t) val) + 273) * 10); } + +TargetTemp decimal_to_temp(float val) { return (TargetTemp) ((val + 273) * 10); } + TargetTemp decimal_to_room_temp(u_int8_t val) { if (val == 0) { return TargetTemp::TARGET_TEMP_OFF; @@ -41,7 +45,7 @@ TargetTemp decimal_to_room_temp(u_int8_t val) { if (val >= 30) { return TargetTemp::TARGET_TEMP_ROOM_MAX; } - return (TargetTemp) ((((u_int16_t) val) + 273) * 10); + return decimal_to_temp(val); } TargetTemp decimal_to_room_temp(float val) { @@ -54,10 +58,36 @@ TargetTemp decimal_to_room_temp(float val) { if (val >= 30) { return TargetTemp::TARGET_TEMP_ROOM_MAX; } - return (TargetTemp) ((val + 273) * 10); + return decimal_to_temp(val); } -TargetTemp deciaml_to_water_temp(u_int8_t val) { +TargetTemp decimal_to_aircon_temp(u_int8_t val) { + if (val == 0) { + return TargetTemp::TARGET_TEMP_OFF; + } + if (val <= 16) { + return TargetTemp::TARGET_TEMP_AIRCON_MIN; + } + if (val >= 31) { + return TargetTemp::TARGET_TEMP_AIRCON_MAX; + } + return decimal_to_temp(val); +} + +TargetTemp decimal_to_aircon_temp(float val) { + if (val == NAN) { + return TargetTemp::TARGET_TEMP_OFF; + } + if (val <= 16) { + return TargetTemp::TARGET_TEMP_AIRCON_MIN; + } + if (val >= 31) { + return TargetTemp::TARGET_TEMP_AIRCON_MAX; + } + return decimal_to_temp(val); +} + +TargetTemp decimal_to_water_temp(u_int8_t val) { if (val < 40) { return TargetTemp::TARGET_TEMP_OFF; } else if (val >= 40 && val < 60) { @@ -69,8 +99,6 @@ TargetTemp deciaml_to_water_temp(u_int8_t val) { } } -float offset_code_to_decimal(TempOffset val) { return ((float) val) / 10.0f - 17.0f; } - const std::string operating_status_to_str(OperatingStatus val) { if (val == OperatingStatus::OPERATING_STATUS_OFF) { return "OFF"; diff --git a/components/truma_inetbox/helpers.h b/components/truma_inetbox/helpers.h index 79f3fb8..c968dae 100644 --- a/components/truma_inetbox/helpers.h +++ b/components/truma_inetbox/helpers.h @@ -12,10 +12,13 @@ u_int8_t addr_parity(const u_int8_t pid); u_int8_t data_checksum(const u_int8_t *message, u_int8_t length, uint16_t sum); float temp_code_to_decimal(u_int16_t val, float zero = NAN); float temp_code_to_decimal(TargetTemp val, float zero = NAN); +TargetTemp decimal_to_temp(u_int8_t val); +TargetTemp decimal_to_temp(float val); TargetTemp decimal_to_room_temp(u_int8_t val); TargetTemp decimal_to_room_temp(float val); -TargetTemp deciaml_to_water_temp(u_int8_t val); -float offset_code_to_decimal(TempOffset val); +TargetTemp decimal_to_aircon_temp(u_int8_t val); +TargetTemp decimal_to_aircon_temp(float val); +TargetTemp decimal_to_water_temp(u_int8_t val); const std::string operating_status_to_str(OperatingStatus val); ElectricPowerLevel decimal_to_el_power_level(u_int16_t val);