esphome 2024.6.6__py3-none-any.whl → 2024.7.0b1__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 (78) hide show
  1. esphome/components/cover/cover.h +2 -2
  2. esphome/components/esp32_camera/__init__.py +6 -3
  3. esphome/components/ethernet/ethernet_component.cpp +3 -2
  4. esphome/components/font/__init__.py +2 -28
  5. esphome/components/haier/binary_sensor/__init__.py +4 -4
  6. esphome/components/haier/button/__init__.py +1 -1
  7. esphome/components/haier/climate.py +43 -9
  8. esphome/components/haier/haier_base.cpp +4 -0
  9. esphome/components/haier/haier_base.h +11 -1
  10. esphome/components/haier/hon_climate.cpp +109 -55
  11. esphome/components/haier/hon_climate.h +7 -1
  12. esphome/components/haier/hon_packet.h +5 -0
  13. esphome/components/haier/sensor/__init__.py +5 -5
  14. esphome/components/haier/smartair2_climate.cpp +1 -0
  15. esphome/components/haier/text_sensor/__init__.py +4 -4
  16. esphome/components/heatpumpir/climate.py +8 -2
  17. esphome/components/heatpumpir/heatpumpir.cpp +6 -0
  18. esphome/components/heatpumpir/heatpumpir.h +6 -0
  19. esphome/components/http_request/update/http_request_update.cpp +6 -7
  20. esphome/components/http_request/update/http_request_update.h +0 -3
  21. esphome/components/image/__init__.py +2 -29
  22. esphome/components/improv_serial/improv_serial_component.cpp +8 -8
  23. esphome/components/mdns/__init__.py +3 -3
  24. esphome/components/mdns/mdns_component.cpp +3 -1
  25. esphome/components/mdns/mdns_component.h +3 -1
  26. esphome/components/mdns/mdns_esp32.cpp +2 -1
  27. esphome/components/mdns/mdns_esp8266.cpp +2 -1
  28. esphome/components/mdns/mdns_host.cpp +2 -1
  29. esphome/components/mdns/mdns_libretiny.cpp +2 -1
  30. esphome/components/mdns/mdns_rp2040.cpp +2 -1
  31. esphome/components/micro_wake_word/__init__.py +203 -56
  32. esphome/components/micro_wake_word/micro_wake_word.cpp +225 -275
  33. esphome/components/micro_wake_word/micro_wake_word.h +77 -107
  34. esphome/components/micro_wake_word/preprocessor_settings.h +20 -0
  35. esphome/components/micro_wake_word/streaming_model.cpp +189 -0
  36. esphome/components/micro_wake_word/streaming_model.h +84 -0
  37. esphome/components/modbus_controller/text_sensor/__init__.py +2 -1
  38. esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +4 -1
  39. esphome/components/modbus_controller/text_sensor/modbus_textsensor.h +1 -1
  40. esphome/components/number/__init__.py +2 -0
  41. esphome/components/qspi_amoled/display.py +16 -4
  42. esphome/components/qspi_amoled/qspi_amoled.cpp +16 -0
  43. esphome/components/qspi_amoled/qspi_amoled.h +0 -3
  44. esphome/components/remote_base/dooya_protocol.cpp +4 -4
  45. esphome/components/remote_base/rc_switch_protocol.cpp +1 -1
  46. esphome/components/script/__init__.py +1 -1
  47. esphome/components/sensor/__init__.py +2 -0
  48. esphome/components/tuya/tuya.cpp +8 -2
  49. esphome/components/tuya/tuya.h +3 -1
  50. esphome/components/uart/__init__.py +72 -9
  51. esphome/components/uart/uart_component_esp32_arduino.cpp +18 -4
  52. esphome/components/uart/uart_component_esp_idf.cpp +22 -2
  53. esphome/components/uart/uart_component_host.cpp +295 -0
  54. esphome/components/uart/uart_component_host.h +38 -0
  55. esphome/components/uptime/sensor.py +44 -11
  56. esphome/components/uptime/{uptime_sensor.cpp → uptime_seconds_sensor.cpp} +11 -7
  57. esphome/components/uptime/{uptime_sensor.h → uptime_seconds_sensor.h} +2 -2
  58. esphome/components/uptime/uptime_timestamp_sensor.cpp +39 -0
  59. esphome/components/uptime/uptime_timestamp_sensor.h +30 -0
  60. esphome/components/veml7700/veml7700.cpp +1 -1
  61. esphome/components/veml7700/veml7700.h +5 -5
  62. esphome/components/voice_assistant/voice_assistant.cpp +1 -1
  63. esphome/components/wifi/wifi_component_esp_idf.cpp +1 -1
  64. esphome/components/wifi/wifi_component_pico_w.cpp +18 -2
  65. esphome/components/wireguard/__init__.py +1 -1
  66. esphome/components/x9c/output.py +7 -1
  67. esphome/const.py +2 -1
  68. esphome/core/defines.h +1 -0
  69. esphome/core/helpers.cpp +1 -1
  70. esphome/core/helpers.h +1 -1
  71. esphome/external_files.py +26 -0
  72. {esphome-2024.6.6.dist-info → esphome-2024.7.0b1.dist-info}/METADATA +1 -1
  73. {esphome-2024.6.6.dist-info → esphome-2024.7.0b1.dist-info}/RECORD +77 -71
  74. esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h +0 -493
  75. {esphome-2024.6.6.dist-info → esphome-2024.7.0b1.dist-info}/LICENSE +0 -0
  76. {esphome-2024.6.6.dist-info → esphome-2024.7.0b1.dist-info}/WHEEL +0 -0
  77. {esphome-2024.6.6.dist-info → esphome-2024.7.0b1.dist-info}/entry_points.txt +0 -0
  78. {esphome-2024.6.6.dist-info → esphome-2024.7.0b1.dist-info}/top_level.txt +0 -0
@@ -129,13 +129,13 @@ class Cover : public EntityBase, public EntityBase_DeviceClass {
129
129
  *
130
130
  * This is a legacy method and may be removed later, please use `.make_call()` instead.
131
131
  */
132
- ESPDEPRECATED("open() is deprecated, use make_call().set_command_open() instead.", "2021.9")
132
+ ESPDEPRECATED("open() is deprecated, use make_call().set_command_open().perform() instead.", "2021.9")
133
133
  void open();
134
134
  /** Close the cover.
135
135
  *
136
136
  * This is a legacy method and may be removed later, please use `.make_call()` instead.
137
137
  */
138
- ESPDEPRECATED("close() is deprecated, use make_call().set_command_close() instead.", "2021.9")
138
+ ESPDEPRECATED("close() is deprecated, use make_call().set_command_close().perform() instead.", "2021.9")
139
139
  void close();
140
140
  /** Stop the cover.
141
141
  *
@@ -17,7 +17,7 @@ from esphome.const import (
17
17
  CONF_VSYNC_PIN,
18
18
  )
19
19
  from esphome.core import CORE
20
- from esphome.components.esp32 import add_idf_sdkconfig_option
20
+ from esphome.components.esp32 import add_idf_component
21
21
  from esphome.cpp_helpers import setup_entity
22
22
 
23
23
  DEPENDENCIES = ["esp32"]
@@ -290,8 +290,11 @@ async def to_code(config):
290
290
  cg.add_define("USE_ESP32_CAMERA")
291
291
 
292
292
  if CORE.using_esp_idf:
293
- cg.add_library("espressif/esp32-camera", "1.0.0")
294
- add_idf_sdkconfig_option("CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC", True)
293
+ add_idf_component(
294
+ name="esp32-camera",
295
+ repo="https://github.com/espressif/esp32-camera.git",
296
+ ref="v2.0.9",
297
+ )
295
298
 
296
299
  for conf in config.get(CONF_ON_STREAM_START, []):
297
300
  trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
@@ -65,7 +65,8 @@ void EthernetComponent::setup() {
65
65
  .intr_flags = 0,
66
66
  };
67
67
 
68
- #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
68
+ #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \
69
+ defined(USE_ESP32_VARIANT_ESP32C6)
69
70
  auto host = SPI2_HOST;
70
71
  #else
71
72
  auto host = SPI3_HOST;
@@ -631,7 +632,7 @@ void EthernetComponent::write_phy_register_(esp_eth_mac_t *mac, PHYRegister regi
631
632
  ESPHL_ERROR_CHECK(err, "Writing PHY Register failed");
632
633
 
633
634
  if (this->type_ == ETHERNET_TYPE_RTL8201 && register_data.page) {
634
- ESP_LOGD(TAG, "Select PHY Register Page 0x%02" PRIX32, 0x0);
635
+ ESP_LOGD(TAG, "Select PHY Register Page 0x00");
635
636
  err = mac->write_phy_reg(mac, this->phy_addr_, eth_phy_psr_reg_addr, 0x0);
636
637
  ESPHL_ERROR_CHECK(err, "Select PHY Register Page 0 failed");
637
638
  }
@@ -17,7 +17,6 @@ from esphome.helpers import (
17
17
  cpp_string_escape,
18
18
  )
19
19
  from esphome.const import (
20
- __version__,
21
20
  CONF_FAMILY,
22
21
  CONF_FILE,
23
22
  CONF_GLYPHS,
@@ -185,31 +184,6 @@ def get_font_path(value, type) -> Path:
185
184
  return None
186
185
 
187
186
 
188
- def download_content(url: str, path: Path) -> None:
189
- if not external_files.has_remote_file_changed(url, path):
190
- _LOGGER.debug("Remote file has not changed %s", url)
191
- return
192
-
193
- _LOGGER.debug(
194
- "Remote file has changed, downloading from %s to %s",
195
- url,
196
- path,
197
- )
198
-
199
- try:
200
- req = requests.get(
201
- url,
202
- timeout=external_files.NETWORK_TIMEOUT,
203
- headers={"User-agent": f"ESPHome/{__version__} (https://esphome.io)"},
204
- )
205
- req.raise_for_status()
206
- except requests.exceptions.RequestException as e:
207
- raise cv.Invalid(f"Could not download from {url}: {e}")
208
-
209
- path.parent.mkdir(parents=True, exist_ok=True)
210
- path.write_bytes(req.content)
211
-
212
-
213
187
  def download_gfont(value):
214
188
  name = (
215
189
  f"{value[CONF_FAMILY]}:ital,wght@{int(value[CONF_ITALIC])},{value[CONF_WEIGHT]}"
@@ -236,7 +210,7 @@ def download_gfont(value):
236
210
  ttf_url = match.group(1)
237
211
  _LOGGER.debug("download_gfont: ttf_url=%s", ttf_url)
238
212
 
239
- download_content(ttf_url, path)
213
+ external_files.download_content(ttf_url, path)
240
214
  return value
241
215
 
242
216
 
@@ -244,7 +218,7 @@ def download_web_font(value):
244
218
  url = value[CONF_URL]
245
219
  path = get_font_path(value, TYPE_WEB)
246
220
 
247
- download_content(url, path)
221
+ external_files.download_content(url, path)
248
222
  _LOGGER.debug("download_web_font: path=%s", path)
249
223
  return value
250
224
 
@@ -56,7 +56,7 @@ SENSOR_TYPES = {
56
56
 
57
57
  CONFIG_SCHEMA = cv.Schema(
58
58
  {
59
- cv.Required(CONF_HAIER_ID): cv.use_id(HonClimate),
59
+ cv.GenerateID(CONF_HAIER_ID): cv.use_id(HonClimate),
60
60
  }
61
61
  ).extend({cv.Optional(type): schema for type, schema in SENSOR_TYPES.items()})
62
62
 
@@ -64,8 +64,8 @@ CONFIG_SCHEMA = cv.Schema(
64
64
  async def to_code(config):
65
65
  paren = await cg.get_variable(config[CONF_HAIER_ID])
66
66
 
67
- for type, _ in SENSOR_TYPES.items():
68
- if conf := config.get(type):
67
+ for type_ in SENSOR_TYPES:
68
+ if conf := config.get(type_):
69
69
  sens = await binary_sensor.new_binary_sensor(conf)
70
- binary_sensor_type = getattr(BinarySensorTypeEnum, type.upper())
70
+ binary_sensor_type = getattr(BinarySensorTypeEnum, type_.upper())
71
71
  cg.add(paren.set_sub_binary_sensor(binary_sensor_type, sens))
@@ -21,7 +21,7 @@ ICON_SPRAY_BOTTLE = "mdi:spray-bottle"
21
21
 
22
22
  CONFIG_SCHEMA = cv.Schema(
23
23
  {
24
- cv.Required(CONF_HAIER_ID): cv.use_id(HonClimate),
24
+ cv.GenerateID(CONF_HAIER_ID): cv.use_id(HonClimate),
25
25
  cv.Optional(CONF_SELF_CLEANING): button.button_schema(
26
26
  SelfCleaningButton,
27
27
  icon=ICON_SPRAY_BOTTLE,
@@ -38,6 +38,9 @@ PROTOCOL_MAX_TEMPERATURE = 30.0
38
38
  PROTOCOL_TARGET_TEMPERATURE_STEP = 1.0
39
39
  PROTOCOL_CURRENT_TEMPERATURE_STEP = 0.5
40
40
  PROTOCOL_CONTROL_PACKET_SIZE = 10
41
+ PROTOCOL_MIN_SENSORS_PACKET_SIZE = 18
42
+ PROTOCOL_DEFAULT_SENSORS_PACKET_SIZE = 22
43
+ PROTOCOL_STATUS_MESSAGE_HEADER_SIZE = 0
41
44
 
42
45
  CODEOWNERS = ["@paveldn"]
43
46
  DEPENDENCIES = ["climate", "uart"]
@@ -48,6 +51,9 @@ CONF_CONTROL_PACKET_SIZE = "control_packet_size"
48
51
  CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow"
49
52
  CONF_ON_ALARM_START = "on_alarm_start"
50
53
  CONF_ON_ALARM_END = "on_alarm_end"
54
+ CONF_ON_STATUS_MESSAGE = "on_status_message"
55
+ CONF_SENSORS_PACKET_SIZE = "sensors_packet_size"
56
+ CONF_STATUS_MESSAGE_HEADER_SIZE = "status_message_header_size"
51
57
  CONF_VERTICAL_AIRFLOW = "vertical_airflow"
52
58
  CONF_WIFI_SIGNAL = "wifi_signal"
53
59
 
@@ -129,6 +135,11 @@ HaierAlarmEndTrigger = haier_ns.class_(
129
135
  automation.Trigger.template(cg.uint8, cg.const_char_ptr),
130
136
  )
131
137
 
138
+ StatusMessageTrigger = haier_ns.class_(
139
+ "StatusMessageTrigger",
140
+ automation.Trigger.template(cg.const_char_ptr, cg.size_t),
141
+ )
142
+
132
143
 
133
144
  def validate_visual(config):
134
145
  if CONF_VISUAL in config:
@@ -183,7 +194,6 @@ BASE_CONFIG_SCHEMA = (
183
194
  cv.Optional(
184
195
  CONF_SUPPORTED_SWING_MODES,
185
196
  default=[
186
- "OFF",
187
197
  "VERTICAL",
188
198
  "HORIZONTAL",
189
199
  "BOTH",
@@ -194,6 +204,11 @@ BASE_CONFIG_SCHEMA = (
194
204
  cv.Optional(
195
205
  CONF_ANSWER_TIMEOUT,
196
206
  ): cv.positive_time_period_milliseconds,
207
+ cv.Optional(CONF_ON_STATUS_MESSAGE): automation.validate_automation(
208
+ {
209
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StatusMessageTrigger),
210
+ }
211
+ ),
197
212
  }
198
213
  )
199
214
  .extend(uart.UART_DEVICE_SCHEMA)
@@ -211,7 +226,7 @@ CONFIG_SCHEMA = cv.All(
211
226
  ): cv.boolean,
212
227
  cv.Optional(
213
228
  CONF_SUPPORTED_PRESETS,
214
- default=list(["BOOST", "COMFORT"]), # No AWAY by default
229
+ default=["BOOST", "COMFORT"], # No AWAY by default
215
230
  ): cv.ensure_list(
216
231
  cv.enum(SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS, upper=True)
217
232
  ),
@@ -229,9 +244,17 @@ CONFIG_SCHEMA = cv.All(
229
244
  cv.Optional(
230
245
  CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
231
246
  ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
247
+ cv.Optional(
248
+ CONF_SENSORS_PACKET_SIZE,
249
+ default=PROTOCOL_DEFAULT_SENSORS_PACKET_SIZE,
250
+ ): cv.int_range(min=PROTOCOL_MIN_SENSORS_PACKET_SIZE, max=50),
251
+ cv.Optional(
252
+ CONF_STATUS_MESSAGE_HEADER_SIZE,
253
+ default=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE,
254
+ ): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE),
232
255
  cv.Optional(
233
256
  CONF_SUPPORTED_PRESETS,
234
- default=list(["BOOST", "ECO", "SLEEP"]), # No AWAY by default
257
+ default=["BOOST", "ECO", "SLEEP"], # No AWAY by default
235
258
  ): cv.ensure_list(
236
259
  cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
237
260
  ),
@@ -427,11 +450,7 @@ def _final_validate(config):
427
450
  "No logger component found, logging for Haier protocol is disabled"
428
451
  )
429
452
  cg.add_build_flag("-DHAIER_LOG_LEVEL=0")
430
- if (
431
- (CONF_WIFI_SIGNAL in config)
432
- and (config[CONF_WIFI_SIGNAL])
433
- and CONF_WIFI not in full_config
434
- ):
453
+ if config.get(CONF_WIFI_SIGNAL) and CONF_WIFI not in full_config:
435
454
  raise cv.Invalid(
436
455
  f"No WiFi configured, if you want to use haier climate without WiFi add {CONF_WIFI_SIGNAL}: false to climate configuration"
437
456
  )
@@ -473,6 +492,16 @@ async def to_code(config):
473
492
  config[CONF_CONTROL_PACKET_SIZE] - PROTOCOL_CONTROL_PACKET_SIZE
474
493
  )
475
494
  )
495
+ if CONF_SENSORS_PACKET_SIZE in config:
496
+ cg.add(
497
+ var.set_extra_sensors_packet_bytes_size(
498
+ config[CONF_SENSORS_PACKET_SIZE] - PROTOCOL_MIN_SENSORS_PACKET_SIZE
499
+ )
500
+ )
501
+ if CONF_STATUS_MESSAGE_HEADER_SIZE in config:
502
+ cg.add(
503
+ var.set_status_message_header_size(config[CONF_STATUS_MESSAGE_HEADER_SIZE])
504
+ )
476
505
  for conf in config.get(CONF_ON_ALARM_START, []):
477
506
  trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
478
507
  await automation.build_automation(
@@ -483,5 +512,10 @@ async def to_code(config):
483
512
  await automation.build_automation(
484
513
  trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf
485
514
  )
515
+ for conf in config.get(CONF_ON_STATUS_MESSAGE, []):
516
+ trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
517
+ await automation.build_automation(
518
+ trigger, [(cg.const_char_ptr, "data"), (cg.size_t, "data_size")], conf
519
+ )
486
520
  # https://github.com/paveldn/HaierProtocol
487
- cg.add_library("pavlodn/HaierProtocol", "0.9.28")
521
+ cg.add_library("pavlodn/HaierProtocol", "0.9.31")
@@ -186,6 +186,10 @@ void HaierClimateBase::send_custom_command(const haier_protocol::HaierMessage &m
186
186
  this->action_request_ = PendingAction({ActionRequest::SEND_CUSTOM_COMMAND, message});
187
187
  }
188
188
 
189
+ void HaierClimateBase::add_status_message_callback(std::function<void(const char *, size_t)> &&callback) {
190
+ this->status_message_callback_.add(std::move(callback));
191
+ }
192
+
189
193
  haier_protocol::HandlerError HaierClimateBase::answer_preprocess_(
190
194
  haier_protocol::FrameType request_message_type, haier_protocol::FrameType expected_request_message_type,
191
195
  haier_protocol::FrameType answer_message_type, haier_protocol::FrameType expected_answer_message_type,
@@ -4,6 +4,7 @@
4
4
  #include <set>
5
5
  #include "esphome/components/climate/climate.h"
6
6
  #include "esphome/components/uart/uart.h"
7
+ #include "esphome/core/automation.h"
7
8
  // HaierProtocol
8
9
  #include <protocol/haier_protocol.h>
9
10
 
@@ -56,6 +57,7 @@ class HaierClimateBase : public esphome::Component,
56
57
  void set_answer_timeout(uint32_t timeout);
57
58
  void set_send_wifi(bool send_wifi);
58
59
  void send_custom_command(const haier_protocol::HaierMessage &message);
60
+ void add_status_message_callback(std::function<void(const char *, size_t)> &&callback);
59
61
 
60
62
  protected:
61
63
  enum class ProtocolPhases {
@@ -140,11 +142,19 @@ class HaierClimateBase : public esphome::Component,
140
142
  esphome::climate::ClimateTraits traits_;
141
143
  HvacSettings current_hvac_settings_;
142
144
  HvacSettings next_hvac_settings_;
143
- std::unique_ptr<uint8_t[]> last_status_message_;
145
+ std::unique_ptr<uint8_t[]> last_status_message_{nullptr};
144
146
  std::chrono::steady_clock::time_point last_request_timestamp_; // For interval between messages
145
147
  std::chrono::steady_clock::time_point last_valid_status_timestamp_; // For protocol timeout
146
148
  std::chrono::steady_clock::time_point last_status_request_; // To request AC status
147
149
  std::chrono::steady_clock::time_point last_signal_request_; // To send WiFI signal level
150
+ CallbackManager<void(const char *, size_t)> status_message_callback_{};
151
+ };
152
+
153
+ class StatusMessageTrigger : public Trigger<const char *, size_t> {
154
+ public:
155
+ explicit StatusMessageTrigger(HaierClimateBase *parent) {
156
+ parent->add_status_message_callback([this](const char *data, size_t data_size) { this->trigger(data, data_size); });
157
+ }
148
158
  };
149
159
 
150
160
  } // namespace haier
@@ -18,12 +18,13 @@ constexpr int PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET = -64;
18
18
  constexpr uint8_t CONTROL_MESSAGE_RETRIES = 5;
19
19
  constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL = std::chrono::milliseconds(500);
20
20
  constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS = 600000;
21
+ const uint8_t ONE_BUF[] = {0x00, 0x01};
22
+ const uint8_t ZERO_BUF[] = {0x00, 0x00};
21
23
 
22
24
  HonClimate::HonClimate()
23
25
  : cleaning_status_(CleaningState::NO_CLEANING), got_valid_outdoor_temp_(false), active_alarms_{0x00, 0x00, 0x00,
24
26
  0x00, 0x00, 0x00,
25
27
  0x00, 0x00} {
26
- last_status_message_ = std::unique_ptr<uint8_t[]>(new uint8_t[sizeof(hon_protocol::HaierPacketControl)]);
27
28
  this->fan_mode_speed_ = (uint8_t) hon_protocol::FanMode::FAN_MID;
28
29
  this->other_modes_fan_speed_ = (uint8_t) hon_protocol::FanMode::FAN_AUTO;
29
30
  }
@@ -169,11 +170,18 @@ haier_protocol::HandlerError HonClimate::status_handler_(haier_protocol::FrameTy
169
170
  this->action_request_.reset();
170
171
  this->force_send_control_ = false;
171
172
  } else {
172
- if (data_size >= sizeof(hon_protocol::HaierPacketControl) + 2) {
173
- memcpy(this->last_status_message_.get(), data + 2, sizeof(hon_protocol::HaierPacketControl));
173
+ if (!this->last_status_message_) {
174
+ this->real_control_packet_size_ = sizeof(hon_protocol::HaierPacketControl) + this->extra_control_packet_bytes_;
175
+ this->real_sensors_packet_size_ = sizeof(hon_protocol::HaierPacketSensors) + this->extra_sensors_packet_bytes_;
176
+ this->last_status_message_.reset();
177
+ this->last_status_message_ = std::unique_ptr<uint8_t[]>(new uint8_t[this->real_control_packet_size_]);
178
+ };
179
+ if (data_size >= this->real_control_packet_size_ + 2) {
180
+ memcpy(this->last_status_message_.get(), data + 2 + this->status_message_header_size_,
181
+ this->real_control_packet_size_);
182
+ this->status_message_callback_.call((const char *) data, data_size);
174
183
  } else {
175
- ESP_LOGW(TAG, "Status packet too small: %d (should be >= %d)", data_size,
176
- sizeof(hon_protocol::HaierPacketControl));
184
+ ESP_LOGW(TAG, "Status packet too small: %d (should be >= %d)", data_size, this->real_control_packet_size_);
177
185
  }
178
186
  switch (this->protocol_phase_) {
179
187
  case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST:
@@ -479,8 +487,8 @@ void HonClimate::initialization() {
479
487
  }
480
488
 
481
489
  haier_protocol::HaierMessage HonClimate::get_control_message() {
482
- uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
483
- memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
490
+ uint8_t control_out_buffer[haier_protocol::MAX_FRAME_SIZE];
491
+ memcpy(control_out_buffer, this->last_status_message_.get(), this->real_control_packet_size_);
484
492
  hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
485
493
  control_out_buffer[4] = 0; // This byte should be cleared before setting values
486
494
  bool has_hvac_settings = false;
@@ -636,7 +644,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
636
644
  out_data->health_mode = this->health_mode_ ? 1 : 0;
637
645
  return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
638
646
  (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
639
- control_out_buffer, sizeof(hon_protocol::HaierPacketControl));
647
+ control_out_buffer, this->real_control_packet_size_);
640
648
  }
641
649
 
642
650
  void HonClimate::process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new) {
@@ -758,15 +766,17 @@ void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::stri
758
766
  #endif // USE_TEXT_SENSOR
759
767
 
760
768
  haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
761
- size_t expected_size = 2 + sizeof(hon_protocol::HaierPacketControl) + sizeof(hon_protocol::HaierPacketSensors) +
762
- this->extra_control_packet_bytes_;
763
- if (size < expected_size)
769
+ size_t expected_size =
770
+ 2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_;
771
+ if (size < expected_size) {
772
+ ESP_LOGW(TAG, "Unexpected message size %d (expexted >= %d)", size, expected_size);
764
773
  return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
774
+ }
765
775
  uint16_t subtype = (((uint16_t) packet_buffer[0]) << 8) + packet_buffer[1];
766
- if ((subtype == 0x7D01) && (size >= expected_size + 4 + sizeof(hon_protocol::HaierPacketBigData))) {
776
+ if ((subtype == 0x7D01) && (size >= expected_size + sizeof(hon_protocol::HaierPacketBigData))) {
767
777
  // Got BigData packet
768
778
  const hon_protocol::HaierPacketBigData *bd_packet =
769
- (const hon_protocol::HaierPacketBigData *) (&packet_buffer[expected_size + 4]);
779
+ (const hon_protocol::HaierPacketBigData *) (&packet_buffer[expected_size]);
770
780
  #ifdef USE_SENSOR
771
781
  this->update_sub_sensor_(SubSensorType::INDOOR_COIL_TEMPERATURE, bd_packet->indoor_coil_temperature / 2.0 - 20);
772
782
  this->update_sub_sensor_(SubSensorType::OUTDOOR_COIL_TEMPERATURE, bd_packet->outdoor_coil_temperature - 64);
@@ -795,9 +805,9 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
795
805
  hon_protocol::HaierPacketControl control;
796
806
  hon_protocol::HaierPacketSensors sensors;
797
807
  } packet;
798
- memcpy(&packet.control, packet_buffer + 2, sizeof(hon_protocol::HaierPacketControl));
799
- memcpy(&packet.sensors,
800
- packet_buffer + 2 + sizeof(hon_protocol::HaierPacketControl) + this->extra_control_packet_bytes_,
808
+ memcpy(&packet.control, packet_buffer + 2 + this->status_message_header_size_,
809
+ sizeof(hon_protocol::HaierPacketControl));
810
+ memcpy(&packet.sensors, packet_buffer + 2 + this->status_message_header_size_ + this->real_control_packet_size_,
801
811
  sizeof(hon_protocol::HaierPacketSensors));
802
812
  if (packet.sensors.error_status != 0) {
803
813
  ESP_LOGW(TAG, "HVAC error, code=0x%02X", packet.sensors.error_status);
@@ -996,8 +1006,6 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
996
1006
  }
997
1007
 
998
1008
  void HonClimate::fill_control_messages_queue_() {
999
- static uint8_t one_buf[] = {0x00, 0x01};
1000
- static uint8_t zero_buf[] = {0x00, 0x00};
1001
1009
  if (!this->current_hvac_settings_.valid && !this->force_send_control_)
1002
1010
  return;
1003
1011
  this->clear_control_messages_queue_();
@@ -1009,7 +1017,7 @@ void HonClimate::fill_control_messages_queue_() {
1009
1017
  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1010
1018
  (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1011
1019
  (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
1012
- this->beeper_status_ ? zero_buf : one_buf, 2));
1020
+ this->beeper_status_ ? ZERO_BUF : ONE_BUF, 2));
1013
1021
  }
1014
1022
  // Health mode
1015
1023
  {
@@ -1017,7 +1025,7 @@ void HonClimate::fill_control_messages_queue_() {
1017
1025
  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1018
1026
  (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1019
1027
  (uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
1020
- this->health_mode_ ? one_buf : zero_buf, 2));
1028
+ this->health_mode_ ? ONE_BUF : ZERO_BUF, 2));
1021
1029
  }
1022
1030
  // Climate mode
1023
1031
  bool new_power = this->mode != CLIMATE_MODE_OFF;
@@ -1092,7 +1100,7 @@ void HonClimate::fill_control_messages_queue_() {
1092
1100
  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1093
1101
  (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1094
1102
  (uint8_t) hon_protocol::DataParameters::AC_POWER,
1095
- new_power ? one_buf : zero_buf, 2));
1103
+ new_power ? ONE_BUF : ZERO_BUF, 2));
1096
1104
  }
1097
1105
  // CLimate preset
1098
1106
  {
@@ -1165,6 +1173,35 @@ void HonClimate::fill_control_messages_queue_() {
1165
1173
  (uint8_t) hon_protocol::DataParameters::SET_POINT,
1166
1174
  buffer, 2));
1167
1175
  }
1176
+ // Vertical swing mode
1177
+ if (climate_control.swing_mode.has_value()) {
1178
+ uint8_t vertical_swing_buf[] = {0x00, (uint8_t) hon_protocol::VerticalSwingMode::AUTO};
1179
+ uint8_t horizontal_swing_buf[] = {0x00, (uint8_t) hon_protocol::HorizontalSwingMode::AUTO};
1180
+ switch (climate_control.swing_mode.value()) {
1181
+ case CLIMATE_SWING_OFF:
1182
+ horizontal_swing_buf[1] = (uint8_t) this->settings_.last_horizontal_swing;
1183
+ vertical_swing_buf[1] = (uint8_t) this->settings_.last_vertiacal_swing;
1184
+ break;
1185
+ case CLIMATE_SWING_VERTICAL:
1186
+ horizontal_swing_buf[1] = (uint8_t) this->settings_.last_horizontal_swing;
1187
+ break;
1188
+ case CLIMATE_SWING_HORIZONTAL:
1189
+ vertical_swing_buf[1] = (uint8_t) this->settings_.last_vertiacal_swing;
1190
+ break;
1191
+ case CLIMATE_SWING_BOTH:
1192
+ break;
1193
+ }
1194
+ this->control_messages_queue_.push(
1195
+ haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1196
+ (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1197
+ (uint8_t) hon_protocol::DataParameters::HORIZONTAL_SWING_MODE,
1198
+ horizontal_swing_buf, 2));
1199
+ this->control_messages_queue_.push(
1200
+ haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1201
+ (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1202
+ (uint8_t) hon_protocol::DataParameters::VERTICAL_SWING_MODE,
1203
+ vertical_swing_buf, 2));
1204
+ }
1168
1205
  // Fan mode
1169
1206
  if (climate_control.fan_mode.has_value()) {
1170
1207
  switch (climate_control.fan_mode.value()) {
@@ -1202,40 +1239,56 @@ void HonClimate::clear_control_messages_queue_() {
1202
1239
 
1203
1240
  bool HonClimate::prepare_pending_action() {
1204
1241
  switch (this->action_request_.value().action) {
1205
- case ActionRequest::START_SELF_CLEAN: {
1206
- uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
1207
- memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
1208
- hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
1209
- out_data->self_cleaning_status = 1;
1210
- out_data->steri_clean = 0;
1211
- out_data->set_point = 0x06;
1212
- out_data->vertical_swing_mode = (uint8_t) hon_protocol::VerticalSwingMode::CENTER;
1213
- out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::CENTER;
1214
- out_data->ac_power = 1;
1215
- out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::DRY;
1216
- out_data->light_status = 0;
1217
- this->action_request_.value().message = haier_protocol::HaierMessage(
1218
- haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
1219
- control_out_buffer, sizeof(hon_protocol::HaierPacketControl));
1220
- }
1221
- return true;
1222
- case ActionRequest::START_STERI_CLEAN: {
1223
- uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
1224
- memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
1225
- hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
1226
- out_data->self_cleaning_status = 0;
1227
- out_data->steri_clean = 1;
1228
- out_data->set_point = 0x06;
1229
- out_data->vertical_swing_mode = (uint8_t) hon_protocol::VerticalSwingMode::CENTER;
1230
- out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::CENTER;
1231
- out_data->ac_power = 1;
1232
- out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::DRY;
1233
- out_data->light_status = 0;
1234
- this->action_request_.value().message = haier_protocol::HaierMessage(
1235
- haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
1236
- control_out_buffer, sizeof(hon_protocol::HaierPacketControl));
1237
- }
1238
- return true;
1242
+ case ActionRequest::START_SELF_CLEAN:
1243
+ if (this->control_method_ == HonControlMethod::SET_GROUP_PARAMETERS) {
1244
+ uint8_t control_out_buffer[haier_protocol::MAX_FRAME_SIZE];
1245
+ memcpy(control_out_buffer, this->last_status_message_.get(), this->real_control_packet_size_);
1246
+ hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
1247
+ out_data->self_cleaning_status = 1;
1248
+ out_data->steri_clean = 0;
1249
+ out_data->set_point = 0x06;
1250
+ out_data->vertical_swing_mode = (uint8_t) hon_protocol::VerticalSwingMode::CENTER;
1251
+ out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::CENTER;
1252
+ out_data->ac_power = 1;
1253
+ out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::DRY;
1254
+ out_data->light_status = 0;
1255
+ this->action_request_.value().message = haier_protocol::HaierMessage(
1256
+ haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
1257
+ control_out_buffer, this->real_control_packet_size_);
1258
+ return true;
1259
+ } else if (this->control_method_ == HonControlMethod::SET_SINGLE_PARAMETER) {
1260
+ this->action_request_.value().message =
1261
+ haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1262
+ (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
1263
+ (uint8_t) hon_protocol::DataParameters::SELF_CLEANING,
1264
+ ONE_BUF, 2);
1265
+ return true;
1266
+ } else {
1267
+ this->action_request_.reset();
1268
+ return false;
1269
+ }
1270
+ case ActionRequest::START_STERI_CLEAN:
1271
+ if (this->control_method_ == HonControlMethod::SET_GROUP_PARAMETERS) {
1272
+ uint8_t control_out_buffer[haier_protocol::MAX_FRAME_SIZE];
1273
+ memcpy(control_out_buffer, this->last_status_message_.get(), this->real_control_packet_size_);
1274
+ hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
1275
+ out_data->self_cleaning_status = 0;
1276
+ out_data->steri_clean = 1;
1277
+ out_data->set_point = 0x06;
1278
+ out_data->vertical_swing_mode = (uint8_t) hon_protocol::VerticalSwingMode::CENTER;
1279
+ out_data->horizontal_swing_mode = (uint8_t) hon_protocol::HorizontalSwingMode::CENTER;
1280
+ out_data->ac_power = 1;
1281
+ out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::DRY;
1282
+ out_data->light_status = 0;
1283
+ this->action_request_.value().message = haier_protocol::HaierMessage(
1284
+ haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
1285
+ control_out_buffer, this->real_control_packet_size_);
1286
+ return true;
1287
+ } else {
1288
+ // No Steri clean support (yet?) in SET_SINGLE_PARAMETER
1289
+ this->action_request_.reset();
1290
+ return false;
1291
+ }
1239
1292
  default:
1240
1293
  return HaierClimateBase::prepare_pending_action();
1241
1294
  }
@@ -1251,6 +1304,7 @@ void HonClimate::process_protocol_reset() {
1251
1304
  #endif // USE_SENSOR
1252
1305
  this->got_valid_outdoor_temp_ = false;
1253
1306
  this->hvac_hardware_info_.reset();
1307
+ this->last_status_message_.reset(nullptr);
1254
1308
  }
1255
1309
 
1256
1310
  bool HonClimate::should_get_big_data_() {
@@ -104,6 +104,8 @@ class HonClimate : public HaierClimateBase {
104
104
  void start_self_cleaning();
105
105
  void start_steri_cleaning();
106
106
  void set_extra_control_packet_bytes_size(size_t size) { this->extra_control_packet_bytes_ = size; };
107
+ void set_extra_sensors_packet_bytes_size(size_t size) { this->extra_sensors_packet_bytes_ = size; };
108
+ void set_status_message_header_size(size_t size) { this->status_message_header_size_ = size; };
107
109
  void set_control_method(HonControlMethod method) { this->control_method_ = method; };
108
110
  void add_alarm_start_callback(std::function<void(uint8_t, const char *)> &&callback);
109
111
  void add_alarm_end_callback(std::function<void(uint8_t, const char *)> &&callback);
@@ -158,7 +160,11 @@ class HonClimate : public HaierClimateBase {
158
160
  esphome::optional<hon_protocol::HorizontalSwingMode> pending_horizontal_direction_{};
159
161
  esphome::optional<HardwareInfo> hvac_hardware_info_{};
160
162
  uint8_t active_alarms_[8];
161
- int extra_control_packet_bytes_;
163
+ int extra_control_packet_bytes_{0};
164
+ int extra_sensors_packet_bytes_{4};
165
+ int status_message_header_size_{0};
166
+ int real_control_packet_size_{sizeof(hon_protocol::HaierPacketControl)};
167
+ int real_sensors_packet_size_{sizeof(hon_protocol::HaierPacketSensors) + 4};
162
168
  HonControlMethod control_method_;
163
169
  std::queue<haier_protocol::HaierMessage> control_messages_queue_;
164
170
  CallbackManager<void(uint8_t, const char *)> alarm_start_callback_{};
@@ -41,15 +41,20 @@ enum class ConditioningMode : uint8_t {
41
41
  enum class DataParameters : uint8_t {
42
42
  AC_POWER = 0x01,
43
43
  SET_POINT = 0x02,
44
+ VERTICAL_SWING_MODE = 0x03,
44
45
  AC_MODE = 0x04,
45
46
  FAN_MODE = 0x05,
46
47
  USE_FAHRENHEIT = 0x07,
48
+ DISPLAY_STATUS = 0x09,
47
49
  TEN_DEGREE = 0x0A,
48
50
  HEALTH_MODE = 0x0B,
51
+ HORIZONTAL_SWING_MODE = 0x0C,
52
+ SELF_CLEANING = 0x0D,
49
53
  BEEPER_STATUS = 0x16,
50
54
  LOCK_REMOTE = 0x17,
51
55
  QUIET_MODE = 0x19,
52
56
  FAST_MODE = 0x1A,
57
+ SLEEP_MODE = 0x1B,
53
58
  };
54
59
 
55
60
  enum class SpecialMode : uint8_t { NONE = 0x00, ELDERLY = 0x01, CHILDREN = 0x02, PREGNANT = 0x03 };
@@ -137,16 +137,16 @@ SENSOR_TYPES = {
137
137
 
138
138
  CONFIG_SCHEMA = cv.Schema(
139
139
  {
140
- cv.Required(CONF_HAIER_ID): cv.use_id(HonClimate),
140
+ cv.GenerateID(CONF_HAIER_ID): cv.use_id(HonClimate),
141
141
  }
142
- ).extend({cv.Optional(type): schema for type, schema in SENSOR_TYPES.items()})
142
+ ).extend({cv.Optional(type_): schema for type_, schema in SENSOR_TYPES.items()})
143
143
 
144
144
 
145
145
  async def to_code(config):
146
146
  paren = await cg.get_variable(config[CONF_HAIER_ID])
147
147
 
148
- for type, _ in SENSOR_TYPES.items():
149
- if conf := config.get(type):
148
+ for type_ in SENSOR_TYPES:
149
+ if conf := config.get(type_):
150
150
  sens = await sensor.new_sensor(conf)
151
- sensor_type = getattr(SensorTypeEnum, type.upper())
151
+ sensor_type = getattr(SensorTypeEnum, type_.upper())
152
152
  cg.add(paren.set_sub_sensor(sensor_type, sens))