esphome 2024.6.6__py3-none-any.whl → 2024.7.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 (102) hide show
  1. esphome/components/aht10/aht10.cpp +4 -2
  2. esphome/components/climate/climate.cpp +10 -6
  3. esphome/components/climate/climate_traits.h +3 -3
  4. esphome/components/cover/cover.h +2 -2
  5. esphome/components/esp32_camera/__init__.py +6 -3
  6. esphome/components/esp32_can/canbus.py +3 -0
  7. esphome/components/ethernet/ethernet_component.cpp +8 -3
  8. esphome/components/font/__init__.py +2 -28
  9. esphome/components/gree/climate.py +1 -0
  10. esphome/components/gree/gree.cpp +11 -3
  11. esphome/components/gree/gree.h +5 -1
  12. esphome/components/haier/binary_sensor/__init__.py +4 -4
  13. esphome/components/haier/button/__init__.py +1 -1
  14. esphome/components/haier/climate.py +43 -9
  15. esphome/components/haier/haier_base.cpp +4 -0
  16. esphome/components/haier/haier_base.h +11 -1
  17. esphome/components/haier/hon_climate.cpp +109 -55
  18. esphome/components/haier/hon_climate.h +7 -1
  19. esphome/components/haier/hon_packet.h +5 -0
  20. esphome/components/haier/sensor/__init__.py +5 -5
  21. esphome/components/haier/smartair2_climate.cpp +1 -0
  22. esphome/components/haier/text_sensor/__init__.py +4 -4
  23. esphome/components/heatpumpir/climate.py +12 -5
  24. esphome/components/heatpumpir/heatpumpir.cpp +11 -0
  25. esphome/components/heatpumpir/heatpumpir.h +11 -0
  26. esphome/components/http_request/http_request_arduino.cpp +7 -2
  27. esphome/components/http_request/update/http_request_update.cpp +6 -7
  28. esphome/components/http_request/update/http_request_update.h +0 -3
  29. esphome/components/i2s_audio/__init__.py +10 -0
  30. esphome/components/i2s_audio/microphone/__init__.py +7 -0
  31. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +2 -3
  32. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +3 -0
  33. esphome/components/image/__init__.py +2 -29
  34. esphome/components/improv_serial/improv_serial_component.cpp +9 -8
  35. esphome/components/ltr390/ltr390.cpp +44 -29
  36. esphome/components/ltr390/ltr390.h +9 -5
  37. esphome/components/ltr390/sensor.py +35 -5
  38. esphome/components/mdns/__init__.py +3 -3
  39. esphome/components/mdns/mdns_component.cpp +3 -1
  40. esphome/components/mdns/mdns_component.h +3 -1
  41. esphome/components/mdns/mdns_esp32.cpp +2 -1
  42. esphome/components/mdns/mdns_esp8266.cpp +2 -1
  43. esphome/components/mdns/mdns_host.cpp +2 -1
  44. esphome/components/mdns/mdns_libretiny.cpp +2 -1
  45. esphome/components/mdns/mdns_rp2040.cpp +2 -1
  46. esphome/components/micro_wake_word/__init__.py +205 -56
  47. esphome/components/micro_wake_word/micro_wake_word.cpp +225 -275
  48. esphome/components/micro_wake_word/micro_wake_word.h +77 -107
  49. esphome/components/micro_wake_word/preprocessor_settings.h +20 -0
  50. esphome/components/micro_wake_word/streaming_model.cpp +189 -0
  51. esphome/components/micro_wake_word/streaming_model.h +84 -0
  52. esphome/components/mitsubishi/mitsubishi.cpp +1 -0
  53. esphome/components/modbus_controller/text_sensor/__init__.py +2 -1
  54. esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +4 -1
  55. esphome/components/modbus_controller/text_sensor/modbus_textsensor.h +1 -1
  56. esphome/components/number/__init__.py +2 -0
  57. esphome/components/ota/ota_backend_arduino_esp32.cpp +22 -7
  58. esphome/components/ota/ota_backend_arduino_esp8266.cpp +23 -8
  59. esphome/components/ota/ota_backend_arduino_libretiny.cpp +22 -7
  60. esphome/components/ota/ota_backend_arduino_rp2040.cpp +22 -7
  61. esphome/components/pmsa003i/pmsa003i.cpp +9 -0
  62. esphome/components/qspi_amoled/display.py +16 -4
  63. esphome/components/qspi_amoled/qspi_amoled.cpp +16 -0
  64. esphome/components/qspi_amoled/qspi_amoled.h +0 -3
  65. esphome/components/remote_base/dooya_protocol.cpp +4 -4
  66. esphome/components/remote_base/rc_switch_protocol.cpp +1 -1
  67. esphome/components/restart/button/__init__.py +2 -0
  68. esphome/components/script/__init__.py +1 -1
  69. esphome/components/sensor/__init__.py +2 -0
  70. esphome/components/tuya/tuya.cpp +8 -2
  71. esphome/components/tuya/tuya.h +3 -1
  72. esphome/components/uart/__init__.py +72 -9
  73. esphome/components/uart/uart_component_esp32_arduino.cpp +18 -4
  74. esphome/components/uart/uart_component_esp_idf.cpp +22 -2
  75. esphome/components/uart/uart_component_host.cpp +295 -0
  76. esphome/components/uart/uart_component_host.h +38 -0
  77. esphome/components/uptime/sensor.py +44 -11
  78. esphome/components/uptime/{uptime_sensor.cpp → uptime_seconds_sensor.cpp} +11 -7
  79. esphome/components/uptime/{uptime_sensor.h → uptime_seconds_sensor.h} +2 -2
  80. esphome/components/uptime/uptime_timestamp_sensor.cpp +39 -0
  81. esphome/components/uptime/uptime_timestamp_sensor.h +30 -0
  82. esphome/components/veml7700/veml7700.cpp +1 -1
  83. esphome/components/veml7700/veml7700.h +5 -5
  84. esphome/components/voice_assistant/voice_assistant.cpp +4 -2
  85. esphome/components/web_server/server_index_v2.h +42 -41
  86. esphome/components/web_server/server_index_v3.h +368 -367
  87. esphome/components/wifi/wifi_component_esp_idf.cpp +1 -1
  88. esphome/components/wifi/wifi_component_pico_w.cpp +18 -2
  89. esphome/components/wireguard/__init__.py +1 -1
  90. esphome/components/x9c/output.py +7 -1
  91. esphome/const.py +2 -1
  92. esphome/core/defines.h +1 -0
  93. esphome/core/helpers.cpp +2 -2
  94. esphome/core/helpers.h +1 -1
  95. esphome/external_files.py +26 -0
  96. {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/METADATA +1 -1
  97. {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/RECORD +101 -95
  98. esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h +0 -493
  99. {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/LICENSE +0 -0
  100. {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/WHEEL +0 -0
  101. {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/entry_points.txt +0 -0
  102. {esphome-2024.6.6.dist-info → esphome-2024.7.0.dist-info}/top_level.txt +0 -0
@@ -93,8 +93,9 @@ void AHT10Component::restart_read_() {
93
93
 
94
94
  void AHT10Component::read_data_() {
95
95
  uint8_t data[6];
96
- if (this->read_count_ > 1)
96
+ if (this->read_count_ > 1) {
97
97
  ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
98
+ }
98
99
  if (this->read(data, 6) != i2c::ERROR_OK) {
99
100
  this->status_set_warning("AHT10 read failed, retrying soon");
100
101
  this->restart_read_();
@@ -119,8 +120,9 @@ void AHT10Component::read_data_() {
119
120
  return;
120
121
  }
121
122
  }
122
- if (this->read_count_ > 1)
123
+ if (this->read_count_ > 1) {
123
124
  ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
125
+ }
124
126
  uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
125
127
  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
126
128
 
@@ -574,21 +574,25 @@ void Climate::dump_traits_(const char *tag) {
574
574
  ESP_LOGCONFIG(tag, " - Max temperature: %.1f", traits.get_visual_max_temperature());
575
575
  ESP_LOGCONFIG(tag, " - Temperature step:");
576
576
  ESP_LOGCONFIG(tag, " Target: %.1f", traits.get_visual_target_temperature_step());
577
- ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step());
578
- ESP_LOGCONFIG(tag, " - Min humidity: %.0f", traits.get_visual_min_humidity());
579
- ESP_LOGCONFIG(tag, " - Max humidity: %.0f", traits.get_visual_max_humidity());
580
577
  if (traits.get_supports_current_temperature()) {
581
- ESP_LOGCONFIG(tag, " [x] Supports current temperature");
578
+ ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step());
582
579
  }
583
- if (traits.get_supports_current_humidity()) {
584
- ESP_LOGCONFIG(tag, " [x] Supports current humidity");
580
+ if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) {
581
+ ESP_LOGCONFIG(tag, " - Min humidity: %.0f", traits.get_visual_min_humidity());
582
+ ESP_LOGCONFIG(tag, " - Max humidity: %.0f", traits.get_visual_max_humidity());
585
583
  }
586
584
  if (traits.get_supports_two_point_target_temperature()) {
587
585
  ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature");
588
586
  }
587
+ if (traits.get_supports_current_temperature()) {
588
+ ESP_LOGCONFIG(tag, " [x] Supports current temperature");
589
+ }
589
590
  if (traits.get_supports_target_humidity()) {
590
591
  ESP_LOGCONFIG(tag, " [x] Supports target humidity");
591
592
  }
593
+ if (traits.get_supports_current_humidity()) {
594
+ ESP_LOGCONFIG(tag, " [x] Supports current humidity");
595
+ }
592
596
  if (traits.get_supports_action()) {
593
597
  ESP_LOGCONFIG(tag, " [x] Supports action");
594
598
  }
@@ -73,7 +73,7 @@ class ClimateTraits {
73
73
  ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
74
74
  void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); }
75
75
  bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); }
76
- std::set<ClimateMode> get_supported_modes() const { return supported_modes_; }
76
+ const std::set<ClimateMode> &get_supported_modes() const { return supported_modes_; }
77
77
 
78
78
  void set_supports_action(bool supports_action) { supports_action_ = supports_action; }
79
79
  bool get_supports_action() const { return supports_action_; }
@@ -101,7 +101,7 @@ class ClimateTraits {
101
101
  void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); }
102
102
  bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); }
103
103
  bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); }
104
- std::set<ClimateFanMode> get_supported_fan_modes() const { return supported_fan_modes_; }
104
+ const std::set<ClimateFanMode> &get_supported_fan_modes() const { return supported_fan_modes_; }
105
105
 
106
106
  void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
107
107
  supported_custom_fan_modes_ = std::move(supported_custom_fan_modes);
@@ -140,7 +140,7 @@ class ClimateTraits {
140
140
  }
141
141
  bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); }
142
142
  bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); }
143
- std::set<ClimateSwingMode> get_supported_swing_modes() const { return supported_swing_modes_; }
143
+ const std::set<ClimateSwingMode> &get_supported_swing_modes() const { return supported_swing_modes_; }
144
144
 
145
145
  float get_visual_min_temperature() const { return visual_min_temperature_; }
146
146
  void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; }
@@ -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)
@@ -11,6 +11,7 @@ from esphome.components.esp32.const import (
11
11
  VARIANT_ESP32S2,
12
12
  VARIANT_ESP32S3,
13
13
  VARIANT_ESP32C3,
14
+ VARIANT_ESP32C6,
14
15
  VARIANT_ESP32H2,
15
16
  )
16
17
 
@@ -47,6 +48,7 @@ CAN_SPEEDS_ESP32_S2 = {
47
48
 
48
49
  CAN_SPEEDS_ESP32_S3 = {**CAN_SPEEDS_ESP32_S2}
49
50
  CAN_SPEEDS_ESP32_C3 = {**CAN_SPEEDS_ESP32_S2}
51
+ CAN_SPEEDS_ESP32_C6 = {**CAN_SPEEDS_ESP32_S2}
50
52
  CAN_SPEEDS_ESP32_H2 = {**CAN_SPEEDS_ESP32_S2}
51
53
 
52
54
  CAN_SPEEDS = {
@@ -54,6 +56,7 @@ CAN_SPEEDS = {
54
56
  VARIANT_ESP32S2: CAN_SPEEDS_ESP32_S2,
55
57
  VARIANT_ESP32S3: CAN_SPEEDS_ESP32_S3,
56
58
  VARIANT_ESP32C3: CAN_SPEEDS_ESP32_C3,
59
+ VARIANT_ESP32C6: CAN_SPEEDS_ESP32_C6,
57
60
  VARIANT_ESP32H2: CAN_SPEEDS_ESP32_H2,
58
61
  }
59
62
 
@@ -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;
@@ -393,7 +394,7 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b
393
394
  const esp_netif_ip_info_t *ip_info = &event->ip_info;
394
395
  ESP_LOGV(TAG, "[Ethernet event] ETH Got IP " IPSTR, IP2STR(&ip_info->ip));
395
396
  global_eth_component->got_ipv4_address_ = true;
396
- #if USE_NETWORK_IPV6
397
+ #if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
397
398
  global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT;
398
399
  #else
399
400
  global_eth_component->connected_ = true;
@@ -406,8 +407,12 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_
406
407
  ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data;
407
408
  ESP_LOGV(TAG, "[Ethernet event] ETH Got IPv6: " IPV6STR, IPV62STR(event->ip6_info.ip));
408
409
  global_eth_component->ipv6_count_ += 1;
410
+ #if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
409
411
  global_eth_component->connected_ =
410
412
  global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT);
413
+ #else
414
+ global_eth_component->connected_ = global_eth_component->got_ipv4_address_;
415
+ #endif
411
416
  }
412
417
  #endif /* USE_NETWORK_IPV6 */
413
418
 
@@ -631,7 +636,7 @@ void EthernetComponent::write_phy_register_(esp_eth_mac_t *mac, PHYRegister regi
631
636
  ESPHL_ERROR_CHECK(err, "Writing PHY Register failed");
632
637
 
633
638
  if (this->type_ == ETHERNET_TYPE_RTL8201 && register_data.page) {
634
- ESP_LOGD(TAG, "Select PHY Register Page 0x%02" PRIX32, 0x0);
639
+ ESP_LOGD(TAG, "Select PHY Register Page 0x00");
635
640
  err = mac->write_phy_reg(mac, this->phy_addr_, eth_phy_psr_reg_addr, 0x0);
636
641
  ESPHL_ERROR_CHECK(err, "Select PHY Register Page 0 failed");
637
642
  }
@@ -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
 
@@ -16,6 +16,7 @@ MODELS = {
16
16
  "yan": Model.GREE_YAN,
17
17
  "yaa": Model.GREE_YAA,
18
18
  "yac": Model.GREE_YAC,
19
+ "yac1fb9": Model.GREE_YAC1FB9,
19
20
  }
20
21
 
21
22
  CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
@@ -24,7 +24,7 @@ void GreeClimate::transmit_state() {
24
24
  remote_state[4] |= (this->horizontal_swing_() << 4);
25
25
  }
26
26
 
27
- if (this->model_ == GREE_YAA || this->model_ == GREE_YAC) {
27
+ if (this->model_ == GREE_YAA || this->model_ == GREE_YAC || this->model_ == GREE_YAC1FB9) {
28
28
  remote_state[2] = 0x20; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN
29
29
  remote_state[3] = 0x50; // bits 4..7 always 0101
30
30
  remote_state[6] = 0x20; // YAA1FB, FAA1FB1, YB1F2 bits 4..7 always 0010
@@ -53,7 +53,11 @@ void GreeClimate::transmit_state() {
53
53
  data->set_carrier_frequency(GREE_IR_FREQUENCY);
54
54
 
55
55
  data->mark(GREE_HEADER_MARK);
56
- data->space(GREE_HEADER_SPACE);
56
+ if (this->model_ == GREE_YAC1FB9) {
57
+ data->space(GREE_YAC1FB9_HEADER_SPACE);
58
+ } else {
59
+ data->space(GREE_HEADER_SPACE);
60
+ }
57
61
 
58
62
  for (int i = 0; i < 4; i++) {
59
63
  for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
@@ -71,7 +75,11 @@ void GreeClimate::transmit_state() {
71
75
  data->space(GREE_ZERO_SPACE);
72
76
 
73
77
  data->mark(GREE_BIT_MARK);
74
- data->space(GREE_MESSAGE_SPACE);
78
+ if (this->model_ == GREE_YAC1FB9) {
79
+ data->space(GREE_YAC1FB9_MESSAGE_SPACE);
80
+ } else {
81
+ data->space(GREE_MESSAGE_SPACE);
82
+ }
75
83
 
76
84
  for (int i = 4; i < 8; i++) {
77
85
  for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
@@ -41,6 +41,10 @@ const uint32_t GREE_YAC_HEADER_MARK = 6000;
41
41
  const uint32_t GREE_YAC_HEADER_SPACE = 3000;
42
42
  const uint32_t GREE_YAC_BIT_MARK = 650;
43
43
 
44
+ // Timing specific to YAC1FB9
45
+ const uint32_t GREE_YAC1FB9_HEADER_SPACE = 4500;
46
+ const uint32_t GREE_YAC1FB9_MESSAGE_SPACE = 19980;
47
+
44
48
  // State Frame size
45
49
  const uint8_t GREE_STATE_FRAME_SIZE = 8;
46
50
 
@@ -67,7 +71,7 @@ const uint8_t GREE_HDIR_MRIGHT = 0x05;
67
71
  const uint8_t GREE_HDIR_RIGHT = 0x06;
68
72
 
69
73
  // Model codes
70
- enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC };
74
+ enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC, GREE_YAC1FB9 };
71
75
 
72
76
  class GreeClimate : public climate_ir::ClimateIR {
73
77
  public:
@@ -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