esphome 2024.9.2__py3-none-any.whl → 2024.10.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. esphome/components/aic3204/__init__.py +0 -0
  2. esphome/components/aic3204/aic3204.cpp +173 -0
  3. esphome/components/aic3204/aic3204.h +88 -0
  4. esphome/components/aic3204/audio_dac.py +52 -0
  5. esphome/components/aic3204/automation.h +23 -0
  6. esphome/components/alarm_control_panel/__init__.py +3 -4
  7. esphome/components/animation/__init__.py +16 -12
  8. esphome/components/api/api_connection.cpp +2 -0
  9. esphome/components/api/api_connection.h +3 -1
  10. esphome/components/api/api_frame_helper.cpp +2 -1
  11. esphome/components/api/api_frame_helper.h +2 -1
  12. esphome/components/api/api_server.cpp +2 -0
  13. esphome/components/api/api_server.h +3 -1
  14. esphome/components/api/custom_api_device.h +3 -2
  15. esphome/components/api/homeassistant_service.h +4 -3
  16. esphome/components/api/list_entities.cpp +2 -0
  17. esphome/components/api/list_entities.h +3 -2
  18. esphome/components/api/subscribe_state.cpp +2 -0
  19. esphome/components/api/subscribe_state.h +3 -2
  20. esphome/components/audio_dac/__init__.py +57 -0
  21. esphome/components/audio_dac/audio_dac.h +23 -0
  22. esphome/components/audio_dac/automation.h +43 -0
  23. esphome/components/bang_bang/bang_bang_climate.cpp +5 -2
  24. esphome/components/bedjet/bedjet_codec.cpp +4 -2
  25. esphome/components/binary_sensor/__init__.py +3 -4
  26. esphome/components/button/__init__.py +3 -4
  27. esphome/components/ch422g/__init__.py +26 -17
  28. esphome/components/ch422g/ch422g.cpp +66 -49
  29. esphome/components/ch422g/ch422g.h +17 -19
  30. esphome/components/climate/__init__.py +3 -4
  31. esphome/components/cover/__init__.py +4 -5
  32. esphome/components/cse7766/cse7766.cpp +12 -1
  33. esphome/components/cse7766/cse7766.h +4 -0
  34. esphome/components/cse7766/sensor.py +13 -1
  35. esphome/components/cst816/touchscreen/__init__.py +7 -4
  36. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +20 -19
  37. esphome/components/cst816/touchscreen/cst816_touchscreen.h +2 -0
  38. esphome/components/datetime/__init__.py +21 -14
  39. esphome/components/datetime/datetime_base.h +8 -1
  40. esphome/components/datetime/datetime_entity.cpp +2 -0
  41. esphome/components/datetime/datetime_entity.h +2 -0
  42. esphome/components/datetime/time_entity.cpp +2 -0
  43. esphome/components/datetime/time_entity.h +2 -0
  44. esphome/components/esp32/__init__.py +20 -4
  45. esphome/components/esp32_improv/__init__.py +82 -1
  46. esphome/components/esp32_improv/automation.h +72 -0
  47. esphome/components/esp32_improv/esp32_improv_component.cpp +13 -5
  48. esphome/components/esp32_improv/esp32_improv_component.h +15 -0
  49. esphome/components/ethernet/__init__.py +5 -0
  50. esphome/components/ethernet/ethernet_component.cpp +13 -0
  51. esphome/components/ethernet/ethernet_component.h +1 -0
  52. esphome/components/event/__init__.py +20 -12
  53. esphome/components/fan/__init__.py +3 -4
  54. esphome/components/gp2y1010au0f/__init__.py +0 -0
  55. esphome/components/gp2y1010au0f/gp2y1010au0f.cpp +67 -0
  56. esphome/components/gp2y1010au0f/gp2y1010au0f.h +52 -0
  57. esphome/components/gp2y1010au0f/sensor.py +61 -0
  58. esphome/components/gpio_expander/__init__.py +0 -0
  59. esphome/components/gpio_expander/cached_gpio.h +38 -0
  60. esphome/components/grove_gas_mc_v2/__init__.py +0 -0
  61. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +88 -0
  62. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +39 -0
  63. esphome/components/grove_gas_mc_v2/sensor.py +77 -0
  64. esphome/components/haier/climate.py +4 -3
  65. esphome/components/haier/haier_base.cpp +63 -8
  66. esphome/components/haier/haier_base.h +29 -3
  67. esphome/components/haier/hon_climate.cpp +122 -65
  68. esphome/components/haier/hon_climate.h +18 -2
  69. esphome/components/haier/smartair2_climate.cpp +21 -21
  70. esphome/components/haier/switch/__init__.py +91 -0
  71. esphome/components/haier/switch/beeper.cpp +14 -0
  72. esphome/components/haier/switch/beeper.h +18 -0
  73. esphome/components/haier/switch/display.cpp +14 -0
  74. esphome/components/haier/switch/display.h +18 -0
  75. esphome/components/haier/switch/health_mode.cpp +14 -0
  76. esphome/components/haier/switch/health_mode.h +18 -0
  77. esphome/components/haier/switch/quiet_mode.cpp +14 -0
  78. esphome/components/haier/switch/quiet_mode.h +18 -0
  79. esphome/components/hmac_md5/hmac_md5.cpp +2 -0
  80. esphome/components/hmac_md5/hmac_md5.h +2 -1
  81. esphome/components/i2s_audio/speaker/__init__.py +19 -0
  82. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  83. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +2 -0
  84. esphome/components/ili9xxx/ili9xxx_display.h +1 -0
  85. esphome/components/image/__init__.py +12 -12
  86. esphome/components/image/image.cpp +44 -0
  87. esphome/components/image/image.h +17 -2
  88. esphome/components/inkplate6/display.py +2 -0
  89. esphome/components/inkplate6/inkplate.h +30 -2
  90. esphome/components/light/__init__.py +3 -4
  91. esphome/components/lock/__init__.py +3 -4
  92. esphome/components/lvgl/__init__.py +16 -5
  93. esphome/components/lvgl/defines.py +1 -0
  94. esphome/components/lvgl/hello_world.py +64 -0
  95. esphome/components/lvgl/lv_validation.py +159 -3
  96. esphome/components/lvgl/lvgl_esphome.cpp +0 -43
  97. esphome/components/lvgl/lvgl_esphome.h +0 -4
  98. esphome/components/lvgl/styles.py +3 -2
  99. esphome/components/lvgl/text/__init__.py +3 -3
  100. esphome/components/lvgl/widgets/__init__.py +2 -0
  101. esphome/components/lvgl/widgets/animimg.py +3 -4
  102. esphome/components/lvgl/widgets/dropdown.py +5 -1
  103. esphome/components/lvgl/widgets/meter.py +16 -11
  104. esphome/components/md5/__init__.py +6 -0
  105. esphome/components/md5/md5.cpp +2 -0
  106. esphome/components/md5/md5.h +2 -0
  107. esphome/components/micro_wake_word/__init__.py +7 -0
  108. esphome/components/mics_4514/sensor.py +11 -26
  109. esphome/components/modbus_controller/__init__.py +7 -5
  110. esphome/components/modbus_controller/binary_sensor/__init__.py +6 -6
  111. esphome/components/modbus_controller/number/__init__.py +5 -6
  112. esphome/components/modbus_controller/output/__init__.py +10 -14
  113. esphome/components/modbus_controller/select/__init__.py +1 -1
  114. esphome/components/modbus_controller/sensor/__init__.py +7 -7
  115. esphome/components/modbus_controller/switch/__init__.py +6 -7
  116. esphome/components/modbus_controller/text_sensor/__init__.py +8 -9
  117. esphome/components/mqtt/__init__.py +3 -0
  118. esphome/components/mqtt/mqtt_client.cpp +2 -0
  119. esphome/components/mqtt/mqtt_client.h +2 -0
  120. esphome/components/nau7802/__init__.py +0 -0
  121. esphome/components/nau7802/nau7802.cpp +323 -0
  122. esphome/components/nau7802/nau7802.h +121 -0
  123. esphome/components/nau7802/sensor.py +134 -0
  124. esphome/components/nextion/base_component.py +1 -0
  125. esphome/components/nextion/display.py +4 -0
  126. esphome/components/nextion/nextion.cpp +19 -4
  127. esphome/components/nextion/nextion.h +16 -0
  128. esphome/components/npi19/__init__.py +0 -0
  129. esphome/components/npi19/npi19.cpp +111 -0
  130. esphome/components/npi19/npi19.h +30 -0
  131. esphome/components/npi19/sensor.py +52 -0
  132. esphome/components/number/__init__.py +3 -5
  133. esphome/components/online_image/__init__.py +1 -1
  134. esphome/components/online_image/online_image.h +1 -2
  135. esphome/components/opentherm/__init__.py +57 -0
  136. esphome/components/opentherm/hub.cpp +277 -0
  137. esphome/components/opentherm/hub.h +110 -0
  138. esphome/components/opentherm/opentherm.cpp +568 -0
  139. esphome/components/opentherm/opentherm.h +347 -0
  140. esphome/components/pulse_counter/pulse_counter_sensor.cpp +8 -1
  141. esphome/components/pulse_counter/pulse_counter_sensor.h +1 -0
  142. esphome/components/radon_eye_ble/radon_eye_listener.cpp +10 -3
  143. esphome/components/remote_transmitter/__init__.py +18 -2
  144. esphome/components/remote_transmitter/remote_transmitter.h +6 -0
  145. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +2 -0
  146. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  147. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  148. esphome/components/select/__init__.py +3 -4
  149. esphome/components/sensor/__init__.py +3 -4
  150. esphome/components/shelly_dimmer/shelly_dimmer.cpp +32 -32
  151. esphome/components/shelly_dimmer/shelly_dimmer.h +2 -0
  152. esphome/components/st7701s/st7701s.cpp +21 -8
  153. esphome/components/st7701s/st7701s.h +2 -0
  154. esphome/components/switch/__init__.py +3 -4
  155. esphome/components/tca9555/__init__.py +72 -0
  156. esphome/components/tca9555/tca9555.cpp +140 -0
  157. esphome/components/tca9555/tca9555.h +64 -0
  158. esphome/components/tcs34725/tcs34725.cpp +62 -64
  159. esphome/components/tem3200/__init__.py +0 -0
  160. esphome/components/tem3200/sensor.py +55 -0
  161. esphome/components/tem3200/tem3200.cpp +151 -0
  162. esphome/components/tem3200/tem3200.h +30 -0
  163. esphome/components/template/binary_sensor/__init__.py +19 -6
  164. esphome/components/text/__init__.py +3 -4
  165. esphome/components/text_sensor/__init__.py +3 -4
  166. esphome/components/thermostat/climate.py +11 -9
  167. esphome/components/thermostat/thermostat_climate.cpp +21 -15
  168. esphome/components/tm1638/binary_sensor/__init__.py +3 -2
  169. esphome/components/tm1638/display.py +5 -5
  170. esphome/components/tm1638/output/__init__.py +3 -2
  171. esphome/components/tm1638/switch/__init__.py +3 -2
  172. esphome/components/touchscreen/touchscreen.cpp +2 -2
  173. esphome/components/update/__init__.py +3 -4
  174. esphome/components/valve/__init__.py +3 -4
  175. esphome/components/web_server/__init__.py +78 -22
  176. esphome/components/web_server/server_index_v3.h +3989 -3979
  177. esphome/components/web_server/web_server.cpp +219 -34
  178. esphome/components/web_server/web_server.h +10 -1
  179. esphome/components/wifi/wifi_component_esp_idf.cpp +4 -5
  180. esphome/config_validation.py +1 -0
  181. esphome/const.py +12 -2
  182. esphome/core/defines.h +4 -2
  183. esphome/core/helpers.cpp +46 -10
  184. esphome/core/helpers.h +8 -0
  185. esphome/core/ring_buffer.cpp +12 -2
  186. esphome/core/ring_buffer.h +3 -0
  187. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/METADATA +5 -3
  188. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/RECORD +192 -143
  189. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/LICENSE +0 -0
  190. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/WHEEL +0 -0
  191. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/entry_points.txt +0 -0
  192. {esphome-2024.9.2.dist-info → esphome-2024.10.0.dist-info}/top_level.txt +0 -0
@@ -52,8 +52,6 @@ bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::stead
52
52
  HaierClimateBase::HaierClimateBase()
53
53
  : haier_protocol_(*this),
54
54
  protocol_phase_(ProtocolPhases::SENDING_INIT_1),
55
- display_status_(true),
56
- health_mode_(false),
57
55
  force_send_control_(false),
58
56
  forced_request_status_(false),
59
57
  reset_protocol_request_(false),
@@ -127,21 +125,34 @@ haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_() {
127
125
  }
128
126
  #endif
129
127
 
130
- bool HaierClimateBase::get_display_state() const { return this->display_status_; }
128
+ void HaierClimateBase::save_settings() {
129
+ HaierBaseSettings settings{this->get_health_mode(), this->get_display_state()};
130
+ if (!this->base_rtc_.save(&settings)) {
131
+ ESP_LOGW(TAG, "Failed to save settings");
132
+ }
133
+ }
134
+
135
+ bool HaierClimateBase::get_display_state() const {
136
+ return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON);
137
+ }
131
138
 
132
139
  void HaierClimateBase::set_display_state(bool state) {
133
- if (this->display_status_ != state) {
134
- this->display_status_ = state;
140
+ if (state != this->get_display_state()) {
141
+ this->display_status_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
135
142
  this->force_send_control_ = true;
143
+ this->save_settings();
136
144
  }
137
145
  }
138
146
 
139
- bool HaierClimateBase::get_health_mode() const { return this->health_mode_; }
147
+ bool HaierClimateBase::get_health_mode() const {
148
+ return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON);
149
+ }
140
150
 
141
151
  void HaierClimateBase::set_health_mode(bool state) {
142
- if (this->health_mode_ != state) {
143
- this->health_mode_ = state;
152
+ if (state != this->get_health_mode()) {
153
+ this->health_mode_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
144
154
  this->force_send_control_ = true;
155
+ this->save_settings();
145
156
  }
146
157
  }
147
158
 
@@ -287,6 +298,14 @@ void HaierClimateBase::loop() {
287
298
  }
288
299
  this->process_phase(now);
289
300
  this->haier_protocol_.loop();
301
+ #ifdef USE_SWITCH
302
+ if ((this->display_switch_ != nullptr) && (this->display_switch_->state != this->get_display_state())) {
303
+ this->display_switch_->publish_state(this->get_display_state());
304
+ }
305
+ if ((this->health_mode_switch_ != nullptr) && (this->health_mode_switch_->state != this->get_health_mode())) {
306
+ this->health_mode_switch_->publish_state(this->get_health_mode());
307
+ }
308
+ #endif // USE_SWITCH
290
309
  }
291
310
 
292
311
  void HaierClimateBase::process_protocol_reset() {
@@ -329,6 +348,26 @@ bool HaierClimateBase::prepare_pending_action() {
329
348
 
330
349
  ClimateTraits HaierClimateBase::traits() { return traits_; }
331
350
 
351
+ void HaierClimateBase::initialization() {
352
+ constexpr uint32_t restore_settings_version = 0xA77D21EF;
353
+ this->base_rtc_ =
354
+ global_preferences->make_preference<HaierBaseSettings>(this->get_object_id_hash() ^ restore_settings_version);
355
+ HaierBaseSettings recovered;
356
+ if (!this->base_rtc_.load(&recovered)) {
357
+ recovered = {false, true};
358
+ }
359
+ this->display_status_ = recovered.display_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
360
+ this->health_mode_ = recovered.health_mode ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
361
+ #ifdef USE_SWITCH
362
+ if (this->display_switch_ != nullptr) {
363
+ this->display_switch_->publish_state(this->get_display_state());
364
+ }
365
+ if (this->health_mode_switch_ != nullptr) {
366
+ this->health_mode_switch_->publish_state(this->get_health_mode());
367
+ }
368
+ #endif
369
+ }
370
+
332
371
  void HaierClimateBase::control(const ClimateCall &call) {
333
372
  ESP_LOGD("Control", "Control call");
334
373
  if (!this->valid_connection()) {
@@ -353,6 +392,22 @@ void HaierClimateBase::control(const ClimateCall &call) {
353
392
  }
354
393
  }
355
394
 
395
+ #ifdef USE_SWITCH
396
+ void HaierClimateBase::set_display_switch(switch_::Switch *sw) {
397
+ this->display_switch_ = sw;
398
+ if ((this->display_switch_ != nullptr) && (this->valid_connection())) {
399
+ this->display_switch_->publish_state(this->get_display_state());
400
+ }
401
+ }
402
+
403
+ void HaierClimateBase::set_health_mode_switch(switch_::Switch *sw) {
404
+ this->health_mode_switch_ = sw;
405
+ if ((this->health_mode_switch_ != nullptr) && (this->valid_connection())) {
406
+ this->health_mode_switch_->publish_state(this->get_health_mode());
407
+ }
408
+ }
409
+ #endif
410
+
356
411
  void HaierClimateBase::HvacSettings::reset() {
357
412
  this->valid = false;
358
413
  this->mode.reset();
@@ -8,6 +8,10 @@
8
8
  // HaierProtocol
9
9
  #include <protocol/haier_protocol.h>
10
10
 
11
+ #ifdef USE_SWITCH
12
+ #include "esphome/components/switch/switch.h"
13
+ #endif
14
+
11
15
  namespace esphome {
12
16
  namespace haier {
13
17
 
@@ -20,10 +24,24 @@ enum class ActionRequest : uint8_t {
20
24
  START_STERI_CLEAN = 5, // only hOn
21
25
  };
22
26
 
27
+ struct HaierBaseSettings {
28
+ bool health_mode;
29
+ bool display_state;
30
+ };
31
+
23
32
  class HaierClimateBase : public esphome::Component,
24
33
  public esphome::climate::Climate,
25
34
  public esphome::uart::UARTDevice,
26
35
  public haier_protocol::ProtocolStream {
36
+ #ifdef USE_SWITCH
37
+ public:
38
+ void set_display_switch(switch_::Switch *sw);
39
+ void set_health_mode_switch(switch_::Switch *sw);
40
+
41
+ protected:
42
+ switch_::Switch *display_switch_{nullptr};
43
+ switch_::Switch *health_mode_switch_{nullptr};
44
+ #endif
27
45
  public:
28
46
  HaierClimateBase();
29
47
  HaierClimateBase(const HaierClimateBase &) = delete;
@@ -82,7 +100,8 @@ class HaierClimateBase : public esphome::Component,
82
100
  virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
83
101
  virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming)
84
102
  virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; // NOLINT(readability-identifier-naming)
85
- virtual void initialization(){};
103
+ virtual void save_settings();
104
+ virtual void initialization();
86
105
  virtual bool prepare_pending_action();
87
106
  virtual void process_protocol_reset();
88
107
  esphome::climate::ClimateTraits traits() override;
@@ -127,13 +146,19 @@ class HaierClimateBase : public esphome::Component,
127
146
  ActionRequest action;
128
147
  esphome::optional<haier_protocol::HaierMessage> message;
129
148
  };
149
+ enum class SwitchState {
150
+ OFF = 0b00,
151
+ ON = 0b01,
152
+ PENDING_OFF = 0b10,
153
+ PENDING_ON = 0b11,
154
+ };
130
155
  haier_protocol::ProtocolHandler haier_protocol_;
131
156
  ProtocolPhases protocol_phase_;
132
157
  esphome::optional<PendingAction> action_request_;
133
158
  uint8_t fan_mode_speed_;
134
159
  uint8_t other_modes_fan_speed_;
135
- bool display_status_;
136
- bool health_mode_;
160
+ SwitchState display_status_{SwitchState::ON};
161
+ SwitchState health_mode_{SwitchState::OFF};
137
162
  bool force_send_control_;
138
163
  bool forced_request_status_;
139
164
  bool reset_protocol_request_;
@@ -148,6 +173,7 @@ class HaierClimateBase : public esphome::Component,
148
173
  std::chrono::steady_clock::time_point last_status_request_; // To request AC status
149
174
  std::chrono::steady_clock::time_point last_signal_request_; // To send WiFI signal level
150
175
  CallbackManager<void(const char *, size_t)> status_message_callback_{};
176
+ ESPPreferenceObject base_rtc_;
151
177
  };
152
178
 
153
179
  class StatusMessageTrigger : public Trigger<const char *, size_t> {
@@ -31,9 +31,32 @@ HonClimate::HonClimate()
31
31
 
32
32
  HonClimate::~HonClimate() {}
33
33
 
34
- void HonClimate::set_beeper_state(bool state) { this->beeper_status_ = state; }
34
+ void HonClimate::set_beeper_state(bool state) {
35
+ if (state != this->settings_.beeper_state) {
36
+ this->settings_.beeper_state = state;
37
+ #ifdef USE_SWITCH
38
+ this->beeper_switch_->publish_state(state);
39
+ #endif
40
+ this->hon_rtc_.save(&this->settings_);
41
+ }
42
+ }
43
+
44
+ bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state; }
45
+
46
+ void HonClimate::set_quiet_mode_state(bool state) {
47
+ if (state != this->get_quiet_mode_state()) {
48
+ this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
49
+ this->settings_.quiet_mode_state = state;
50
+ #ifdef USE_SWITCH
51
+ this->quiet_mode_switch_->publish_state(state);
52
+ #endif
53
+ this->hon_rtc_.save(&this->settings_);
54
+ }
55
+ }
35
56
 
36
- bool HonClimate::get_beeper_state() const { return this->beeper_status_; }
57
+ bool HonClimate::get_quiet_mode_state() const {
58
+ return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON);
59
+ }
37
60
 
38
61
  esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
39
62
  return this->current_vertical_swing_;
@@ -474,16 +497,19 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
474
497
  }
475
498
 
476
499
  void HonClimate::initialization() {
477
- constexpr uint32_t restore_settings_version = 0xE834D8DCUL;
478
- this->rtc_ = global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
500
+ HaierClimateBase::initialization();
501
+ constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
502
+ this->hon_rtc_ =
503
+ global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
479
504
  HonSettings recovered;
480
- if (this->rtc_.load(&recovered)) {
505
+ if (this->hon_rtc_.load(&recovered)) {
481
506
  this->settings_ = recovered;
482
507
  } else {
483
- this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER};
508
+ this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER, true, false};
484
509
  }
485
510
  this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
486
511
  this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
512
+ this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
487
513
  }
488
514
 
489
515
  haier_protocol::HaierMessage HonClimate::get_control_message() {
@@ -519,8 +545,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
519
545
  out_data->ac_power = 1;
520
546
  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN;
521
547
  out_data->fan_mode = this->fan_mode_speed_; // Auto doesn't work in fan only mode
522
- // Disabling boost and eco mode for Fan only
523
- out_data->quiet_mode = 0;
548
+ // Disabling boost for Fan only
524
549
  out_data->fast_mode = 0;
525
550
  break;
526
551
  case CLIMATE_MODE_COOL:
@@ -582,47 +607,34 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
582
607
  }
583
608
  if (out_data->ac_power == 0) {
584
609
  // If AC is off - no presets allowed
585
- out_data->quiet_mode = 0;
586
610
  out_data->fast_mode = 0;
587
611
  out_data->sleep_mode = 0;
588
612
  } else if (climate_control.preset.has_value()) {
589
613
  switch (climate_control.preset.value()) {
590
614
  case CLIMATE_PRESET_NONE:
591
- out_data->quiet_mode = 0;
592
- out_data->fast_mode = 0;
593
- out_data->sleep_mode = 0;
594
- out_data->ten_degree = 0;
595
- break;
596
- case CLIMATE_PRESET_ECO:
597
- // Eco is not supported in Fan only mode
598
- out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
599
615
  out_data->fast_mode = 0;
600
616
  out_data->sleep_mode = 0;
601
617
  out_data->ten_degree = 0;
602
618
  break;
603
619
  case CLIMATE_PRESET_BOOST:
604
- out_data->quiet_mode = 0;
605
620
  // Boost is not supported in Fan only mode
606
621
  out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
607
622
  out_data->sleep_mode = 0;
608
623
  out_data->ten_degree = 0;
609
624
  break;
610
625
  case CLIMATE_PRESET_AWAY:
611
- out_data->quiet_mode = 0;
612
626
  out_data->fast_mode = 0;
613
627
  out_data->sleep_mode = 0;
614
628
  // 10 degrees allowed only in heat mode
615
629
  out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
616
630
  break;
617
631
  case CLIMATE_PRESET_SLEEP:
618
- out_data->quiet_mode = 0;
619
632
  out_data->fast_mode = 0;
620
633
  out_data->sleep_mode = 1;
621
634
  out_data->ten_degree = 0;
622
635
  break;
623
636
  default:
624
637
  ESP_LOGE("Control", "Unsupported preset");
625
- out_data->quiet_mode = 0;
626
638
  out_data->fast_mode = 0;
627
639
  out_data->sleep_mode = 0;
628
640
  out_data->ten_degree = 0;
@@ -638,10 +650,23 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
638
650
  out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value();
639
651
  this->pending_horizontal_direction_.reset();
640
652
  }
641
- out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0;
653
+ {
654
+ // Quiet mode
655
+ if ((out_data->ac_power == 0) || (out_data->ac_mode == (uint8_t) hon_protocol::ConditioningMode::FAN)) {
656
+ // If AC is off or in fan only mode - no quiet mode allowed
657
+ out_data->quiet_mode = 0;
658
+ } else {
659
+ out_data->quiet_mode = this->get_quiet_mode_state() ? 1 : 0;
660
+ }
661
+ // Clean quiet mode state pending flag
662
+ this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
663
+ }
664
+ out_data->beeper_status = ((!this->get_beeper_state()) || (!has_hvac_settings)) ? 1 : 0;
642
665
  control_out_buffer[4] = 0; // This byte should be cleared before setting values
643
- out_data->display_status = this->display_status_ ? 1 : 0;
644
- out_data->health_mode = this->health_mode_ ? 1 : 0;
666
+ out_data->display_status = this->get_display_state() ? 1 : 0;
667
+ this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
668
+ out_data->health_mode = this->get_health_mode() ? 1 : 0;
669
+ this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
645
670
  return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
646
671
  (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
647
672
  control_out_buffer, this->real_control_packet_size_);
@@ -765,6 +790,22 @@ void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::stri
765
790
  }
766
791
  #endif // USE_TEXT_SENSOR
767
792
 
793
+ #ifdef USE_SWITCH
794
+ void HonClimate::set_beeper_switch(switch_::Switch *sw) {
795
+ this->beeper_switch_ = sw;
796
+ if (this->beeper_switch_ != nullptr) {
797
+ this->beeper_switch_->publish_state(this->get_beeper_state());
798
+ }
799
+ }
800
+
801
+ void HonClimate::set_quiet_mode_switch(switch_::Switch *sw) {
802
+ this->quiet_mode_switch_ = sw;
803
+ if (this->quiet_mode_switch_ != nullptr) {
804
+ this->quiet_mode_switch_->publish_state(this->settings_.quiet_mode_state);
805
+ }
806
+ }
807
+ #endif // USE_SWITCH
808
+
768
809
  haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
769
810
  size_t expected_size =
770
811
  2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_;
@@ -827,9 +868,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
827
868
  {
828
869
  // Extra modes/presets
829
870
  optional<ClimatePreset> old_preset = this->preset;
830
- if (packet.control.quiet_mode != 0) {
831
- this->preset = CLIMATE_PRESET_ECO;
832
- } else if (packet.control.fast_mode != 0) {
871
+ if (packet.control.fast_mode != 0) {
833
872
  this->preset = CLIMATE_PRESET_BOOST;
834
873
  } else if (packet.control.sleep_mode != 0) {
835
874
  this->preset = CLIMATE_PRESET_SLEEP;
@@ -883,28 +922,26 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
883
922
  }
884
923
  should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
885
924
  }
886
- {
887
- // Display status
888
- // should be before "Climate mode" because it is changing this->mode
889
- if (packet.control.ac_power != 0) {
890
- // if AC is off display status always ON so process it only when AC is on
891
- bool disp_status = packet.control.display_status != 0;
892
- if (disp_status != this->display_status_) {
893
- // Do something only if display status changed
894
- if (this->mode == CLIMATE_MODE_OFF) {
895
- // AC just turned on from remote need to turn off display
896
- this->force_send_control_ = true;
897
- } else {
898
- this->display_status_ = disp_status;
899
- }
925
+ // Display status
926
+ // should be before "Climate mode" because it is changing this->mode
927
+ if (packet.control.ac_power != 0) {
928
+ // if AC is off display status always ON so process it only when AC is on
929
+ bool disp_status = packet.control.display_status != 0;
930
+ if (disp_status != this->get_display_state()) {
931
+ // Do something only if display status changed
932
+ if (this->mode == CLIMATE_MODE_OFF) {
933
+ // AC just turned on from remote need to turn off display
934
+ this->force_send_control_ = true;
935
+ } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
936
+ this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
900
937
  }
901
938
  }
902
939
  }
903
- {
904
- // Health mode
905
- bool old_health_mode = this->health_mode_;
906
- this->health_mode_ = packet.control.health_mode == 1;
907
- should_publish = should_publish || (old_health_mode != this->health_mode_);
940
+ // Health mode
941
+ if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
942
+ bool old_health_mode = this->get_health_mode();
943
+ this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
944
+ should_publish = should_publish || (old_health_mode != this->get_health_mode());
908
945
  }
909
946
  {
910
947
  CleaningState new_cleaning;
@@ -958,17 +995,36 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
958
995
  }
959
996
  should_publish = should_publish || (old_mode != this->mode);
960
997
  }
998
+ {
999
+ // Quiet mode, should be after climate mode
1000
+ if ((this->mode != CLIMATE_MODE_FAN_ONLY) && (this->mode != CLIMATE_MODE_OFF) &&
1001
+ ((((uint8_t) this->quiet_mode_state_) & 0b10) == 0)) {
1002
+ // In proper mode and not in pending state
1003
+ bool new_quiet_mode = packet.control.quiet_mode != 0;
1004
+ if (new_quiet_mode != this->get_quiet_mode_state()) {
1005
+ this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF;
1006
+ this->settings_.quiet_mode_state = new_quiet_mode;
1007
+ this->hon_rtc_.save(&this->settings_);
1008
+ }
1009
+ }
1010
+ }
961
1011
  {
962
1012
  // Swing mode
963
1013
  ClimateSwingMode old_swing_mode = this->swing_mode;
964
- if (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO) {
965
- if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
1014
+ const std::set<ClimateSwingMode> &swing_modes = traits_.get_supported_swing_modes();
1015
+ bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end();
1016
+ bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end();
1017
+ if (horizontal_swing_supported &&
1018
+ (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) {
1019
+ if (vertical_swing_supported &&
1020
+ (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
966
1021
  this->swing_mode = CLIMATE_SWING_BOTH;
967
1022
  } else {
968
1023
  this->swing_mode = CLIMATE_SWING_HORIZONTAL;
969
1024
  }
970
1025
  } else {
971
- if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
1026
+ if (vertical_swing_supported &&
1027
+ (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
972
1028
  this->swing_mode = CLIMATE_SWING_VERTICAL;
973
1029
  } else {
974
1030
  this->swing_mode = CLIMATE_SWING_OFF;
@@ -985,7 +1041,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
985
1041
  if (save_settings) {
986
1042
  this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value();
987
1043
  this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value();
988
- this->rtc_.save(&this->settings_);
1044
+ this->hon_rtc_.save(&this->settings_);
989
1045
  }
990
1046
  should_publish = should_publish || (old_swing_mode != this->swing_mode);
991
1047
  }
@@ -1017,7 +1073,7 @@ void HonClimate::fill_control_messages_queue_() {
1017
1073
  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1018
1074
  (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1019
1075
  (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
1020
- this->beeper_status_ ? ZERO_BUF : ONE_BUF, 2));
1076
+ this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2));
1021
1077
  }
1022
1078
  // Health mode
1023
1079
  {
@@ -1025,13 +1081,16 @@ void HonClimate::fill_control_messages_queue_() {
1025
1081
  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1026
1082
  (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1027
1083
  (uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
1028
- this->health_mode_ ? ONE_BUF : ZERO_BUF, 2));
1084
+ this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2));
1085
+ this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
1029
1086
  }
1030
1087
  // Climate mode
1088
+ ClimateMode climate_mode = this->mode;
1031
1089
  bool new_power = this->mode != CLIMATE_MODE_OFF;
1032
1090
  uint8_t fan_mode_buf[] = {0x00, 0xFF};
1033
1091
  uint8_t quiet_mode_buf[] = {0x00, 0xFF};
1034
1092
  if (climate_control.mode.has_value()) {
1093
+ climate_mode = climate_control.mode.value();
1035
1094
  uint8_t buffer[2] = {0x00, 0x00};
1036
1095
  switch (climate_control.mode.value()) {
1037
1096
  case CLIMATE_MODE_OFF:
@@ -1076,8 +1135,6 @@ void HonClimate::fill_control_messages_queue_() {
1076
1135
  (uint8_t) hon_protocol::DataParameters::AC_MODE,
1077
1136
  buffer, 2));
1078
1137
  fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode
1079
- // Disabling eco mode for Fan only
1080
- quiet_mode_buf[1] = 0;
1081
1138
  break;
1082
1139
  case CLIMATE_MODE_COOL:
1083
1140
  new_power = true;
@@ -1108,30 +1165,20 @@ void HonClimate::fill_control_messages_queue_() {
1108
1165
  uint8_t away_mode_buf[] = {0x00, 0xFF};
1109
1166
  if (!new_power) {
1110
1167
  // If AC is off - no presets allowed
1111
- quiet_mode_buf[1] = 0x00;
1112
1168
  fast_mode_buf[1] = 0x00;
1113
1169
  away_mode_buf[1] = 0x00;
1114
1170
  } else if (climate_control.preset.has_value()) {
1115
1171
  switch (climate_control.preset.value()) {
1116
1172
  case CLIMATE_PRESET_NONE:
1117
- quiet_mode_buf[1] = 0x00;
1118
- fast_mode_buf[1] = 0x00;
1119
- away_mode_buf[1] = 0x00;
1120
- break;
1121
- case CLIMATE_PRESET_ECO:
1122
- // Eco is not supported in Fan only mode
1123
- quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
1124
1173
  fast_mode_buf[1] = 0x00;
1125
1174
  away_mode_buf[1] = 0x00;
1126
1175
  break;
1127
1176
  case CLIMATE_PRESET_BOOST:
1128
- quiet_mode_buf[1] = 0x00;
1129
1177
  // Boost is not supported in Fan only mode
1130
1178
  fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
1131
1179
  away_mode_buf[1] = 0x00;
1132
1180
  break;
1133
1181
  case CLIMATE_PRESET_AWAY:
1134
- quiet_mode_buf[1] = 0x00;
1135
1182
  fast_mode_buf[1] = 0x00;
1136
1183
  away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
1137
1184
  break;
@@ -1140,8 +1187,18 @@ void HonClimate::fill_control_messages_queue_() {
1140
1187
  break;
1141
1188
  }
1142
1189
  }
1190
+ {
1191
+ // Quiet mode
1192
+ if (new_power && (climate_mode != CLIMATE_MODE_FAN_ONLY) && this->get_quiet_mode_state()) {
1193
+ quiet_mode_buf[1] = 0x01;
1194
+ } else {
1195
+ quiet_mode_buf[1] = 0x00;
1196
+ }
1197
+ // Clean quiet mode state pending flag
1198
+ this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
1199
+ }
1143
1200
  auto presets = this->traits_.get_supported_presets();
1144
- if ((quiet_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_ECO) != presets.end()))) {
1201
+ if (quiet_mode_buf[1] != 0xFF) {
1145
1202
  this->control_messages_queue_.push(
1146
1203
  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1147
1204
  (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
@@ -10,6 +10,9 @@
10
10
  #ifdef USE_TEXT_SENSOR
11
11
  #include "esphome/components/text_sensor/text_sensor.h"
12
12
  #endif
13
+ #ifdef USE_SWITCH
14
+ #include "esphome/components/switch/switch.h"
15
+ #endif
13
16
  #include "esphome/core/automation.h"
14
17
  #include "haier_base.h"
15
18
  #include "hon_packet.h"
@@ -28,6 +31,8 @@ enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE
28
31
  struct HonSettings {
29
32
  hon_protocol::VerticalSwingMode last_vertiacal_swing;
30
33
  hon_protocol::HorizontalSwingMode last_horizontal_swing;
34
+ bool beeper_state;
35
+ bool quiet_mode_state;
31
36
  };
32
37
 
33
38
  class HonClimate : public HaierClimateBase {
@@ -86,6 +91,15 @@ class HonClimate : public HaierClimateBase {
86
91
  protected:
87
92
  void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
88
93
  text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
94
+ #endif
95
+ #ifdef USE_SWITCH
96
+ public:
97
+ void set_beeper_switch(switch_::Switch *sw);
98
+ void set_quiet_mode_switch(switch_::Switch *sw);
99
+
100
+ protected:
101
+ switch_::Switch *beeper_switch_{nullptr};
102
+ switch_::Switch *quiet_mode_switch_{nullptr};
89
103
  #endif
90
104
  public:
91
105
  HonClimate();
@@ -95,6 +109,8 @@ class HonClimate : public HaierClimateBase {
95
109
  void dump_config() override;
96
110
  void set_beeper_state(bool state);
97
111
  bool get_beeper_state() const;
112
+ void set_quiet_mode_state(bool state);
113
+ bool get_quiet_mode_state() const;
98
114
  esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const;
99
115
  void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
100
116
  esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
@@ -153,7 +169,6 @@ class HonClimate : public HaierClimateBase {
153
169
  bool functions_[5];
154
170
  };
155
171
 
156
- bool beeper_status_;
157
172
  CleaningState cleaning_status_;
158
173
  bool got_valid_outdoor_temp_;
159
174
  esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{};
@@ -175,7 +190,8 @@ class HonClimate : public HaierClimateBase {
175
190
  esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{};
176
191
  esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
177
192
  HonSettings settings_;
178
- ESPPreferenceObject rtc_;
193
+ ESPPreferenceObject hon_rtc_;
194
+ SwitchState quiet_mode_state_{SwitchState::OFF};
179
195
  };
180
196
 
181
197
  class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
@@ -376,8 +376,10 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() {
376
376
  }
377
377
  }
378
378
  }
379
- out_data->display_status = this->display_status_ ? 0 : 1;
380
- out_data->health_mode = this->health_mode_ ? 1 : 0;
379
+ out_data->display_status = this->get_display_state() ? 0 : 1;
380
+ this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
381
+ out_data->health_mode = this->get_health_mode() ? 1 : 0;
382
+ this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
381
383
  return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer,
382
384
  sizeof(smartair2_protocol::HaierPacketControl));
383
385
  }
@@ -446,28 +448,26 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin
446
448
  }
447
449
  should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
448
450
  }
449
- {
450
- // Display status
451
- // should be before "Climate mode" because it is changing this->mode
452
- if (packet.control.ac_power != 0) {
453
- // if AC is off display status always ON so process it only when AC is on
454
- bool disp_status = packet.control.display_status == 0;
455
- if (disp_status != this->display_status_) {
456
- // Do something only if display status changed
457
- if (this->mode == CLIMATE_MODE_OFF) {
458
- // AC just turned on from remote need to turn off display
459
- this->force_send_control_ = true;
460
- } else {
461
- this->display_status_ = disp_status;
462
- }
451
+ // Display status
452
+ // should be before "Climate mode" because it is changing this->mode
453
+ if (packet.control.ac_power != 0) {
454
+ // if AC is off display status always ON so process it only when AC is on
455
+ bool disp_status = packet.control.display_status == 0;
456
+ if (disp_status != this->get_display_state()) {
457
+ // Do something only if display status changed
458
+ if (this->mode == CLIMATE_MODE_OFF) {
459
+ // AC just turned on from remote need to turn off display
460
+ this->force_send_control_ = true;
461
+ } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
462
+ this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
463
463
  }
464
464
  }
465
465
  }
466
- {
467
- // Health mode
468
- bool old_health_mode = this->health_mode_;
469
- this->health_mode_ = packet.control.health_mode == 1;
470
- should_publish = should_publish || (old_health_mode != this->health_mode_);
466
+ // Health mode
467
+ if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
468
+ bool old_health_mode = this->get_health_mode();
469
+ this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
470
+ should_publish = should_publish || (old_health_mode != this->get_health_mode());
471
471
  }
472
472
  {
473
473
  // Climate mode