esphome 2024.11.3__py3-none-any.whl → 2024.12.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 (178) hide show
  1. esphome/components/adc/adc_sensor.h +7 -8
  2. esphome/components/adc/adc_sensor_common.cpp +24 -0
  3. esphome/components/adc/{adc_sensor.cpp → adc_sensor_esp32.cpp} +10 -179
  4. esphome/components/adc/adc_sensor_esp8266.cpp +58 -0
  5. esphome/components/adc/adc_sensor_rp2040.cpp +93 -0
  6. esphome/components/alarm_control_panel/alarm_control_panel_call.cpp +3 -4
  7. esphome/components/animation/__init__.py +1 -2
  8. esphome/components/apds9306/apds9306.cpp +2 -1
  9. esphome/components/audio/audio.h +1 -1
  10. esphome/components/bk72xx/__init__.py +1 -1
  11. esphome/components/cse7766/cse7766.cpp +1 -1
  12. esphome/components/deep_sleep/deep_sleep_esp32.cpp +2 -2
  13. esphome/components/dht/dht.cpp +2 -1
  14. esphome/components/display/display.cpp +10 -6
  15. esphome/components/display/display.h +14 -0
  16. esphome/components/display_menu_base/__init__.py +0 -2
  17. esphome/components/display_menu_base/display_menu_base.cpp +1 -1
  18. esphome/components/dsmr/dsmr.cpp +1 -1
  19. esphome/components/esp32/__init__.py +94 -12
  20. esphome/components/esp32/boards.py +222 -14
  21. esphome/components/esp32_ble/__init__.py +22 -2
  22. esphome/components/esp32_ble/ble.cpp +13 -5
  23. esphome/components/esp32_ble/ble.h +2 -0
  24. esphome/components/esp32_ble/ble_advertising.cpp +1 -1
  25. esphome/components/esp32_ble/ble_uuid.cpp +9 -10
  26. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +4 -1
  27. esphome/components/esp32_camera_web_server/camera_web_server.h +1 -1
  28. esphome/components/esp32_rmt_led_strip/light.py +3 -3
  29. esphome/components/esp8266/__init__.py +5 -7
  30. esphome/components/ezo/ezo.cpp +14 -26
  31. esphome/components/font/__init__.py +5 -23
  32. esphome/components/font/font.cpp +5 -3
  33. esphome/components/graphical_display_menu/__init__.py +2 -0
  34. esphome/components/haier/hon_climate.cpp +79 -80
  35. esphome/components/hbridge/switch/__init__.py +44 -0
  36. esphome/components/hbridge/switch/hbridge_switch.cpp +95 -0
  37. esphome/components/hbridge/switch/hbridge_switch.h +50 -0
  38. esphome/components/hitachi_ac344/hitachi_ac344.cpp +4 -2
  39. esphome/components/hitachi_ac424/hitachi_ac424.cpp +4 -2
  40. esphome/components/homeassistant/number/homeassistant_number.cpp +3 -0
  41. esphome/components/hx711/hx711.cpp +1 -1
  42. esphome/components/hx711/hx711.h +1 -1
  43. esphome/components/i2c/i2c_bus_esp_idf.cpp +2 -2
  44. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +61 -59
  45. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -17
  46. esphome/components/ili9xxx/display.py +1 -2
  47. esphome/components/ili9xxx/ili9xxx_display.cpp +3 -2
  48. esphome/components/image/__init__.py +1 -2
  49. esphome/components/logger/logger.cpp +1 -1
  50. esphome/components/ltr501/ltr501.cpp +1 -1
  51. esphome/components/lvgl/defines.py +8 -1
  52. esphome/components/lvgl/lv_validation.py +8 -3
  53. esphome/components/lvgl/lvgl_esphome.cpp +1 -1
  54. esphome/components/lvgl/lvgl_esphome.h +16 -0
  55. esphome/components/lvgl/widgets/animimg.py +12 -17
  56. esphome/components/lvgl/widgets/img.py +1 -3
  57. esphome/components/matrix_keypad/__init__.py +15 -3
  58. esphome/components/matrix_keypad/matrix_keypad.cpp +4 -0
  59. esphome/components/matrix_keypad/matrix_keypad.h +5 -0
  60. esphome/components/max31865/max31865.cpp +4 -2
  61. esphome/components/modbus_controller/modbus_controller.cpp +24 -24
  62. esphome/components/modbus_controller/modbus_controller.h +22 -22
  63. esphome/components/modbus_controller/number/modbus_number.cpp +8 -8
  64. esphome/components/modbus_controller/number/modbus_number.h +4 -4
  65. esphome/components/modbus_controller/output/modbus_output.cpp +7 -6
  66. esphome/components/modbus_controller/output/modbus_output.h +5 -5
  67. esphome/components/modbus_controller/select/modbus_select.cpp +4 -3
  68. esphome/components/modbus_controller/select/modbus_select.h +4 -4
  69. esphome/components/modbus_controller/switch/modbus_switch.cpp +5 -5
  70. esphome/components/modbus_controller/switch/modbus_switch.h +2 -2
  71. esphome/components/mqtt/__init__.py +4 -0
  72. esphome/components/mqtt/mqtt_alarm_control_panel.cpp +2 -5
  73. esphome/components/mqtt/mqtt_backend_esp32.cpp +3 -3
  74. esphome/components/mqtt/mqtt_client.cpp +4 -0
  75. esphome/components/mqtt/mqtt_client.h +6 -0
  76. esphome/components/mqtt/mqtt_climate.cpp +13 -3
  77. esphome/components/mqtt/mqtt_sensor.cpp +2 -0
  78. esphome/components/network/ip_address.h +1 -1
  79. esphome/components/nextion/__init__.py +2 -0
  80. esphome/components/nextion/automation.h +76 -0
  81. esphome/components/nextion/base_component.py +1 -0
  82. esphome/components/nextion/binary_sensor/__init__.py +43 -2
  83. esphome/components/nextion/display.py +15 -0
  84. esphome/components/nextion/nextion.cpp +8 -5
  85. esphome/components/nextion/nextion.h +7 -0
  86. esphome/components/nextion/nextion_upload_idf.cpp +2 -2
  87. esphome/components/nextion/sensor/__init__.py +38 -5
  88. esphome/components/nextion/switch/__init__.py +38 -2
  89. esphome/components/nextion/text_sensor/__init__.py +37 -2
  90. esphome/components/nfc/ndef_record.cpp +3 -3
  91. esphome/components/online_image/__init__.py +1 -0
  92. esphome/components/opentherm/opentherm.cpp +3 -3
  93. esphome/components/opentherm/opentherm.h +1 -1
  94. esphome/components/ota/automation.h +1 -1
  95. esphome/components/output/float_output.cpp +1 -1
  96. esphome/components/pca6416a/pca6416a.cpp +5 -3
  97. esphome/components/pca9554/pca9554.cpp +4 -4
  98. esphome/components/pipsolar/pipsolar.cpp +2 -2
  99. esphome/components/pipsolar/switch/pipsolar_switch.cpp +2 -2
  100. esphome/components/pn532/pn532_mifare_ultralight.cpp +2 -2
  101. esphome/components/pn7150/pn7150_mifare_ultralight.cpp +2 -2
  102. esphome/components/pn7160/pn7160_mifare_ultralight.cpp +2 -2
  103. esphome/components/qmc5883l/qmc5883l.cpp +45 -19
  104. esphome/components/qmc5883l/qmc5883l.h +1 -1
  105. esphome/components/qspi_dbi/qspi_dbi.cpp +2 -1
  106. esphome/components/remote_base/raw_protocol.cpp +1 -1
  107. esphome/components/rotary_encoder/rotary_encoder.cpp +3 -1
  108. esphome/components/rp2040/__init__.py +1 -1
  109. esphome/components/rtl87xx/__init__.py +1 -1
  110. esphome/components/safe_mode/automation.h +1 -1
  111. esphome/components/seeed_mr60bha2/__init__.py +41 -0
  112. esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +173 -0
  113. esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +61 -0
  114. esphome/components/seeed_mr60bha2/sensor.py +57 -0
  115. esphome/components/seeed_mr60fda2/__init__.py +41 -0
  116. esphome/components/seeed_mr60fda2/binary_sensor.py +33 -0
  117. esphome/components/seeed_mr60fda2/button/__init__.py +45 -0
  118. esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.cpp +9 -0
  119. esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.h +18 -0
  120. esphome/components/seeed_mr60fda2/button/reset_radar_button.cpp +9 -0
  121. esphome/components/seeed_mr60fda2/button/reset_radar_button.h +18 -0
  122. esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +368 -0
  123. esphome/components/seeed_mr60fda2/seeed_mr60fda2.h +101 -0
  124. esphome/components/seeed_mr60fda2/select/__init__.py +59 -0
  125. esphome/components/seeed_mr60fda2/select/height_threshold_select.cpp +15 -0
  126. esphome/components/seeed_mr60fda2/select/height_threshold_select.h +18 -0
  127. esphome/components/seeed_mr60fda2/select/install_height_select.cpp +15 -0
  128. esphome/components/seeed_mr60fda2/select/install_height_select.h +18 -0
  129. esphome/components/seeed_mr60fda2/select/sensitivity_select.cpp +15 -0
  130. esphome/components/seeed_mr60fda2/select/sensitivity_select.h +18 -0
  131. esphome/components/sen5x/sensor.py +5 -6
  132. esphome/components/shelly_dimmer/shelly_dimmer.cpp +1 -1
  133. esphome/components/sim800l/sim800l.cpp +1 -1
  134. esphome/components/sntp/sntp_component.cpp +14 -20
  135. esphome/components/sntp/sntp_component.h +6 -9
  136. esphome/components/sntp/time.py +4 -7
  137. esphome/components/sprinkler/sprinkler.cpp +2 -2
  138. esphome/components/st7735/st7735.cpp +1 -1
  139. esphome/components/st7789v/st7789v.cpp +1 -1
  140. esphome/components/stepper/stepper.h +0 -1
  141. esphome/components/sun_gtil2/sun_gtil2.cpp +1 -1
  142. esphome/components/switch/binary_sensor/__init__.py +31 -0
  143. esphome/components/switch/binary_sensor/switch_binary_sensor.cpp +17 -0
  144. esphome/components/switch/binary_sensor/switch_binary_sensor.h +22 -0
  145. esphome/components/sx1509/sx1509_gpio_pin.cpp +2 -1
  146. esphome/components/sx1509/sx1509_gpio_pin.h +5 -5
  147. esphome/components/uart/uart.h +1 -1
  148. esphome/components/udp/udp_component.cpp +32 -16
  149. esphome/components/ufire_ec/sensor.py +4 -4
  150. esphome/components/uln2003/uln2003.cpp +4 -1
  151. esphome/components/waveshare_epaper/display.py +8 -0
  152. esphome/components/waveshare_epaper/waveshare_epaper.cpp +191 -0
  153. esphome/components/waveshare_epaper/waveshare_epaper.h +56 -0
  154. esphome/components/wiegand/__init__.py +3 -4
  155. esphome/components/wifi/__init__.py +42 -0
  156. esphome/components/wifi/wifi_component.cpp +2 -2
  157. esphome/components/wifi/wifi_component.h +82 -1
  158. esphome/components/wifi/wifi_component_esp32_arduino.cpp +1 -1
  159. esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
  160. esphome/components/wifi/wifi_component_esp_idf.cpp +1 -1
  161. esphome/components/wifi/wifi_component_libretiny.cpp +1 -1
  162. esphome/components/wifi/wifi_component_pico_w.cpp +1 -1
  163. esphome/components/wireguard/wireguard.cpp +2 -2
  164. esphome/components/xiaomi_ble/xiaomi_ble.cpp +1 -1
  165. esphome/config_validation.py +15 -11
  166. esphome/const.py +7 -1
  167. esphome/core/component.cpp +1 -1
  168. esphome/core/config.py +1 -2
  169. esphome/core/defines.h +3 -1
  170. esphome/core/helpers.cpp +6 -1
  171. esphome/core/helpers.h +2 -1
  172. esphome/core/optional.h +2 -2
  173. {esphome-2024.11.3.dist-info → esphome-2024.12.0b1.dist-info}/METADATA +3 -3
  174. {esphome-2024.11.3.dist-info → esphome-2024.12.0b1.dist-info}/RECORD +178 -149
  175. {esphome-2024.11.3.dist-info → esphome-2024.12.0b1.dist-info}/LICENSE +0 -0
  176. {esphome-2024.11.3.dist-info → esphome-2024.12.0b1.dist-info}/WHEEL +0 -0
  177. {esphome-2024.11.3.dist-info → esphome-2024.12.0b1.dist-info}/entry_points.txt +0 -0
  178. {esphome-2024.11.3.dist-info → esphome-2024.12.0b1.dist-info}/top_level.txt +0 -0
@@ -33,14 +33,15 @@ enum SpeakerEventGroupBits : uint32_t {
33
33
  STATE_RUNNING = (1 << 11),
34
34
  STATE_STOPPING = (1 << 12),
35
35
  STATE_STOPPED = (1 << 13),
36
- ERR_INVALID_FORMAT = (1 << 14),
37
- ERR_TASK_FAILED_TO_START = (1 << 15),
38
- ERR_ESP_INVALID_STATE = (1 << 16),
36
+ ERR_TASK_FAILED_TO_START = (1 << 14),
37
+ ERR_ESP_INVALID_STATE = (1 << 15),
38
+ ERR_ESP_NOT_SUPPORTED = (1 << 16),
39
39
  ERR_ESP_INVALID_ARG = (1 << 17),
40
40
  ERR_ESP_INVALID_SIZE = (1 << 18),
41
41
  ERR_ESP_NO_MEM = (1 << 19),
42
42
  ERR_ESP_FAIL = (1 << 20),
43
- ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE | ERR_ESP_NO_MEM | ERR_ESP_FAIL,
43
+ ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_NOT_SUPPORTED | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE |
44
+ ERR_ESP_NO_MEM | ERR_ESP_FAIL,
44
45
  ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
45
46
  };
46
47
 
@@ -55,6 +56,8 @@ static esp_err_t err_bit_to_esp_err(uint32_t bit) {
55
56
  return ESP_ERR_INVALID_SIZE;
56
57
  case SpeakerEventGroupBits::ERR_ESP_NO_MEM:
57
58
  return ESP_ERR_NO_MEM;
59
+ case SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED:
60
+ return ESP_ERR_NOT_SUPPORTED;
58
61
  default:
59
62
  return ESP_FAIL;
60
63
  }
@@ -135,19 +138,19 @@ void I2SAudioSpeaker::loop() {
135
138
  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
136
139
  }
137
140
 
138
- if (event_group_bits & SpeakerEventGroupBits::ERR_INVALID_FORMAT) {
141
+ if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
142
+ uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
143
+ ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
144
+ this->status_set_warning();
145
+ }
146
+
147
+ if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
139
148
  this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
140
149
  ESP_LOGE(TAG,
141
150
  "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
142
151
  this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels,
143
152
  this->audio_stream_info_.bits_per_sample);
144
153
  }
145
-
146
- if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
147
- uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
148
- ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
149
- this->status_set_warning();
150
- }
151
154
  }
152
155
 
153
156
  void I2SAudioSpeaker::set_volume(float volume) {
@@ -236,13 +239,15 @@ void I2SAudioSpeaker::speaker_task(void *params) {
236
239
  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING);
237
240
 
238
241
  audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_;
239
- const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample();
240
- const uint8_t number_of_channels = audio_stream_info.channels;
241
242
 
242
- const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 *
243
- bytes_per_sample * number_of_channels;
243
+ const uint32_t bytes_per_ms =
244
+ audio_stream_info.channels * audio_stream_info.get_bytes_per_sample() * audio_stream_info.sample_rate / 1000;
245
+
246
+ const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * bytes_per_ms;
247
+
248
+ // Ensure ring buffer is at least as large as the total size of the DMA buffers
244
249
  const size_t ring_buffer_size =
245
- this_speaker->buffer_duration_ms_ * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels;
250
+ std::min((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms);
246
251
 
247
252
  if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) {
248
253
  // Failed to allocate buffers
@@ -250,14 +255,7 @@ void I2SAudioSpeaker::speaker_task(void *params) {
250
255
  this_speaker->delete_task_(dma_buffers_size);
251
256
  }
252
257
 
253
- if (this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_())) {
254
- // Failed to start I2S driver
255
- this_speaker->delete_task_(dma_buffers_size);
256
- }
257
-
258
- if (!this_speaker->send_esp_err_to_event_group_(this_speaker->reconfigure_i2s_stream_info_(audio_stream_info))) {
259
- // Successfully set the I2S stream info, ready to write audio data to the I2S port
260
-
258
+ if (!this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_(audio_stream_info))) {
261
259
  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
262
260
 
263
261
  bool stop_gracefully = false;
@@ -275,6 +273,12 @@ void I2SAudioSpeaker::speaker_task(void *params) {
275
273
  stop_gracefully = true;
276
274
  }
277
275
 
276
+ if (this_speaker->audio_stream_info_ != audio_stream_info) {
277
+ // Audio stream info has changed, stop the speaker task so it will restart with the proper settings.
278
+
279
+ break;
280
+ }
281
+
278
282
  i2s_event_t i2s_event;
279
283
  while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) {
280
284
  if (i2s_event.type == I2S_EVENT_TX_Q_OVF) {
@@ -316,17 +320,14 @@ void I2SAudioSpeaker::speaker_task(void *params) {
316
320
  }
317
321
  }
318
322
  }
319
- } else {
320
- // Couldn't configure the I2S port to be compatible with the incoming audio
321
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_INVALID_FORMAT);
322
- }
323
- i2s_zero_dma_buffer(this_speaker->parent_->get_port());
324
323
 
325
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
324
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
325
+
326
+ i2s_driver_uninstall(this_speaker->parent_->get_port());
326
327
 
327
- i2s_driver_uninstall(this_speaker->parent_->get_port());
328
+ this_speaker->parent_->unlock();
329
+ }
328
330
 
329
- this_speaker->parent_->unlock();
330
331
  this_speaker->delete_task_(dma_buffers_size);
331
332
  }
332
333
 
@@ -382,6 +383,9 @@ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) {
382
383
  case ESP_ERR_NO_MEM:
383
384
  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
384
385
  return true;
386
+ case ESP_ERR_NOT_SUPPORTED:
387
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED);
388
+ return true;
385
389
  default:
386
390
  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL);
387
391
  return true;
@@ -411,18 +415,40 @@ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t rin
411
415
  return ESP_OK;
412
416
  }
413
417
 
414
- esp_err_t I2SAudioSpeaker::start_i2s_driver_() {
418
+ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) {
419
+ if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.sample_rate)) { // NOLINT
420
+ // Can't reconfigure I2S bus, so the sample rate must match the configured value
421
+ return ESP_ERR_NOT_SUPPORTED;
422
+ }
423
+
424
+ if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) {
425
+ // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
426
+ return ESP_ERR_NOT_SUPPORTED;
427
+ }
428
+
415
429
  if (!this->parent_->try_lock()) {
416
430
  return ESP_ERR_INVALID_STATE;
417
431
  }
418
432
 
433
+ i2s_channel_fmt_t channel = this->channel_;
434
+
435
+ if (audio_stream_info.channels == 1) {
436
+ if (this->channel_ == I2S_CHANNEL_FMT_ONLY_LEFT) {
437
+ channel = I2S_CHANNEL_FMT_ONLY_LEFT;
438
+ } else {
439
+ channel = I2S_CHANNEL_FMT_ONLY_RIGHT;
440
+ }
441
+ } else if (audio_stream_info.channels == 2) {
442
+ channel = I2S_CHANNEL_FMT_RIGHT_LEFT;
443
+ }
444
+
419
445
  int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000;
420
446
 
421
447
  i2s_driver_config_t config = {
422
448
  .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX),
423
- .sample_rate = this->sample_rate_,
449
+ .sample_rate = audio_stream_info.sample_rate,
424
450
  .bits_per_sample = this->bits_per_sample_,
425
- .channel_format = this->channel_,
451
+ .channel_format = channel,
426
452
  .communication_format = this->i2s_comm_fmt_,
427
453
  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
428
454
  .dma_buf_count = DMA_BUFFERS_COUNT,
@@ -477,30 +503,6 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_() {
477
503
  return err;
478
504
  }
479
505
 
480
- esp_err_t I2SAudioSpeaker::reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info) {
481
- if (this->i2s_mode_ & I2S_MODE_MASTER) {
482
- // ESP controls for the the I2S bus, so adjust the sample rate and bits per sample to match the incoming audio
483
- this->sample_rate_ = audio_stream_info.sample_rate;
484
- this->bits_per_sample_ = (i2s_bits_per_sample_t) audio_stream_info.bits_per_sample;
485
- } else if (this->sample_rate_ != audio_stream_info.sample_rate) {
486
- // Can't reconfigure I2S bus, so the sample rate must match the configured value
487
- return ESP_ERR_INVALID_ARG;
488
- }
489
-
490
- if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) {
491
- // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
492
- return ESP_ERR_INVALID_ARG;
493
- }
494
-
495
- if (audio_stream_info.channels == 1) {
496
- return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_MONO);
497
- } else if (audio_stream_info.channels == 2) {
498
- return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_STEREO);
499
- }
500
-
501
- return ESP_ERR_INVALID_ARG;
502
- }
503
-
504
506
  void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
505
507
  this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr
506
508
 
@@ -91,24 +91,15 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
91
91
  esp_err_t allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size);
92
92
 
93
93
  /// @brief Starts the ESP32 I2S driver.
94
- /// Attempts to lock the I2S port, starts the I2S driver, and sets the data out pin. If it fails, it will unlock
95
- /// the I2S port and uninstall the driver, if necessary.
96
- /// @return ESP_ERR_INVALID_STATE if the I2S port is already locked.
97
- /// ESP_ERR_INVALID_ARG if installing the driver or setting the data out pin fails due to a parameter error.
94
+ /// Attempts to lock the I2S port, starts the I2S driver using the passed in stream information, and sets the data out
95
+ /// pin. If it fails, it will unlock the I2S port and uninstall the driver, if necessary.
96
+ /// @param audio_stream_info Stream information for the I2S driver.
97
+ /// @return ESP_ERR_NOT_ALLOWED if the I2S port can't play the incoming audio stream.
98
+ /// ESP_ERR_INVALID_STATE if the I2S port is already locked.
99
+ /// ESP_ERR_INVALID_ARG if nstalling the driver or setting the data outpin fails due to a parameter error.
98
100
  /// ESP_ERR_NO_MEM if the driver fails to install due to a memory allocation error.
99
- /// ESP_FAIL if setting the data out pin fails due to an IO error
100
- /// ESP_OK if successful
101
- esp_err_t start_i2s_driver_();
102
-
103
- /// @brief Adjusts the I2S driver configuration to match the incoming audio stream.
104
- /// Modifies I2S driver's sample rate, bits per sample, and number of channel settings. If the I2S is in secondary
105
- /// mode, it only modifies the number of channels.
106
- /// @param audio_stream_info Describes the incoming audio stream
107
- /// @return ESP_ERR_INVALID_ARG if there is a parameter error, if there is more than 2 channels in the stream, or if
108
- /// the audio settings are incompatible with the configuration.
109
- /// ESP_ERR_NO_MEM if the driver fails to reconfigure due to a memory allocation error.
110
- /// ESP_OK if successful.
111
- esp_err_t reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info);
101
+ /// ESP_FAIL if setting the data out pin fails due to an IO error ESP_OK if successful
102
+ esp_err_t start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info);
112
103
 
113
104
  /// @brief Deletes the speaker's task.
114
105
  /// Deallocates the data_buffer_ and audio_ring_buffer_, if necessary, and deletes the task. Should only be called by
@@ -1,6 +1,6 @@
1
1
  from esphome import core, pins
2
2
  import esphome.codegen as cg
3
- from esphome.components import display, font, spi
3
+ from esphome.components import display, spi
4
4
  from esphome.components.display import validate_rotation
5
5
  import esphome.config_validation as cv
6
6
  from esphome.const import (
@@ -147,7 +147,6 @@ def _validate(config):
147
147
 
148
148
 
149
149
  CONFIG_SCHEMA = cv.All(
150
- font.validate_pillow_installed,
151
150
  display.FULL_DISPLAY_SCHEMA.extend(
152
151
  {
153
152
  cv.GenerateID(): cv.declare_id(ILI9XXXDisplay),
@@ -313,8 +313,9 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons
313
313
  // do color conversion pixel-by-pixel into the buffer and draw it later. If this is happening the user has not
314
314
  // configured the renderer well.
315
315
  if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || !big_endian) {
316
- return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
317
- x_pad);
316
+ display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset,
317
+ x_pad);
318
+ return;
318
319
  }
319
320
  this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1);
320
321
  // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display.
@@ -10,7 +10,6 @@ import puremagic
10
10
 
11
11
  from esphome import core, external_files
12
12
  import esphome.codegen as cg
13
- from esphome.components import font
14
13
  import esphome.config_validation as cv
15
14
  from esphome.const import (
16
15
  CONF_DITHER,
@@ -233,7 +232,7 @@ IMAGE_SCHEMA = cv.Schema(
233
232
  )
234
233
  )
235
234
 
236
- CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA)
235
+ CONFIG_SCHEMA = IMAGE_SCHEMA
237
236
 
238
237
 
239
238
  def load_svg_image(file: bytes, resize: tuple[int, int]):
@@ -47,7 +47,7 @@ void Logger::write_header_(int level, const char *tag, int line) {
47
47
  if (current_task == main_task_) {
48
48
  this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line);
49
49
  } else {
50
- const char *thread_name = "";
50
+ const char *thread_name = ""; // NOLINT(clang-analyzer-deadcode.DeadStores)
51
51
  #if defined(USE_ESP32)
52
52
  thread_name = pcTaskGetName(current_task);
53
53
  #elif defined(USE_LIBRETINY)
@@ -23,7 +23,7 @@ bool operator==(const GainTimePair &lhs, const GainTimePair &rhs) {
23
23
  }
24
24
 
25
25
  bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs) {
26
- return !(lhs.gain == rhs.gain && lhs.time == rhs.time);
26
+ return lhs.gain != rhs.gain || lhs.time != rhs.time;
27
27
  }
28
28
 
29
29
  template<typename T, size_t size> T get_next(const T (&array)[size], const T val) {
@@ -8,7 +8,7 @@ import logging
8
8
 
9
9
  from esphome import codegen as cg, config_validation as cv
10
10
  from esphome.const import CONF_ITEMS
11
- from esphome.core import Lambda
11
+ from esphome.core import ID, Lambda
12
12
  from esphome.cpp_generator import LambdaExpression, MockObj
13
13
  from esphome.cpp_types import uint32
14
14
  from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
@@ -72,6 +72,12 @@ class LValidator:
72
72
  )
73
73
  if self.retmapper is not None:
74
74
  return self.retmapper(value)
75
+ if isinstance(value, ID):
76
+ return await cg.get_variable(value)
77
+ if isinstance(value, list):
78
+ value = [
79
+ await cg.get_variable(x) if isinstance(x, ID) else x for x in value
80
+ ]
75
81
  return cg.safe_exp(value)
76
82
 
77
83
 
@@ -162,6 +168,7 @@ LV_EVENT_MAP = {
162
168
  "READY": "READY",
163
169
  "CANCEL": "CANCEL",
164
170
  "ALL_EVENTS": "ALL",
171
+ "CHANGE": "VALUE_CHANGED",
165
172
  }
166
173
 
167
174
  LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP)
@@ -1,6 +1,7 @@
1
1
  from typing import Union
2
2
 
3
3
  import esphome.codegen as cg
4
+ from esphome.components import image
4
5
  from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw
5
6
  from esphome.components.font import Font
6
7
  from esphome.components.image import Image_
@@ -31,7 +32,7 @@ from .defines import (
31
32
  literal,
32
33
  )
33
34
  from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component
34
- from .types import lv_font_t, lv_gradient_t, lv_img_t
35
+ from .types import lv_font_t, lv_gradient_t
35
36
 
36
37
  opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER")
37
38
 
@@ -332,8 +333,12 @@ def image_validator(value):
332
333
 
333
334
  lv_image = LValidator(
334
335
  image_validator,
335
- lv_img_t,
336
- retmapper=lambda x: MockObj(x, "->").get_lv_img_dsc(),
336
+ image.Image_.operator("ptr"),
337
+ requires="image",
338
+ )
339
+ lv_image_list = LValidator(
340
+ cv.ensure_list(image_validator),
341
+ cg.std_vector.template(image.Image_.operator("ptr")),
337
342
  requires="image",
338
343
  )
339
344
  lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal)
@@ -279,7 +279,7 @@ std::string LvSelectable::get_selected_text() {
279
279
  static std::string join_string(std::vector<std::string> options) {
280
280
  return std::accumulate(
281
281
  options.begin(), options.end(), std::string(),
282
- [](const std::string &a, const std::string &b) -> std::string { return a + (a.length() > 0 ? "\n" : "") + b; });
282
+ [](const std::string &a, const std::string &b) -> std::string { return a + (!a.empty() ? "\n" : "") + b; });
283
283
  }
284
284
 
285
285
  void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t anim) {
@@ -60,6 +60,22 @@ inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image)
60
60
  lv_disp_set_bg_image(disp, image->get_lv_img_dsc());
61
61
  }
62
62
  #endif // USE_LVGL_IMAGE
63
+ #ifdef USE_LVGL_ANIMIMG
64
+ inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
65
+ auto *dsc = static_cast<std::vector<lv_img_dsc_t *> *>(lv_obj_get_user_data(img));
66
+ if (dsc == nullptr) {
67
+ // object will be lazily allocated but never freed.
68
+ dsc = new std::vector<lv_img_dsc_t *>(images.size()); // NOLINT
69
+ lv_obj_set_user_data(img, dsc);
70
+ }
71
+ dsc->clear();
72
+ for (auto &image : images) {
73
+ dsc->push_back(image->get_lv_img_dsc());
74
+ }
75
+ lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size());
76
+ }
77
+
78
+ #endif // USE_LVGL_ANIMIMG
63
79
 
64
80
  // Parent class for things that wrap an LVGL object
65
81
  class LvCompound {
@@ -1,20 +1,18 @@
1
1
  from esphome import automation
2
- import esphome.codegen as cg
3
2
  import esphome.config_validation as cv
4
3
  from esphome.const import CONF_DURATION, CONF_ID
5
4
 
6
5
  from ..automation import action_to_code
7
6
  from ..defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC
8
7
  from ..helpers import lvgl_components_required
9
- from ..lv_validation import lv_image, lv_milliseconds
8
+ from ..lv_validation import lv_image_list, lv_milliseconds
10
9
  from ..lvcode import lv
11
- from ..types import LvType, ObjUpdateAction, void_ptr
10
+ from ..types import LvType, ObjUpdateAction
12
11
  from . import Widget, WidgetType, get_widgets
13
12
  from .img import CONF_IMAGE
14
13
  from .label import CONF_LABEL
15
14
 
16
15
  CONF_ANIMIMG = "animimg"
17
- CONF_SRC_LIST_ID = "src_list_id"
18
16
 
19
17
 
20
18
  def lv_repeat_count(value):
@@ -32,14 +30,14 @@ ANIMIMG_BASE_SCHEMA = cv.Schema(
32
30
  ANIMIMG_SCHEMA = ANIMIMG_BASE_SCHEMA.extend(
33
31
  {
34
32
  cv.Required(CONF_DURATION): lv_milliseconds,
35
- cv.Required(CONF_SRC): cv.ensure_list(lv_image),
36
- cv.GenerateID(CONF_SRC_LIST_ID): cv.declare_id(void_ptr),
33
+ cv.Required(CONF_SRC): lv_image_list,
37
34
  }
38
35
  )
39
36
 
40
37
  ANIMIMG_MODIFY_SCHEMA = ANIMIMG_BASE_SCHEMA.extend(
41
38
  {
42
39
  cv.Optional(CONF_DURATION): lv_milliseconds,
40
+ cv.Optional(CONF_SRC): lv_image_list,
43
41
  }
44
42
  )
45
43
 
@@ -59,17 +57,14 @@ class AnimimgType(WidgetType):
59
57
  async def to_code(self, w: Widget, config):
60
58
  lvgl_components_required.add(CONF_IMAGE)
61
59
  lvgl_components_required.add(CONF_ANIMIMG)
62
- if CONF_SRC in config:
63
- srcs = [
64
- await lv_image.process(await cg.get_variable(x))
65
- for x in config[CONF_SRC]
66
- ]
67
- src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs)
68
- count = len(config[CONF_SRC])
69
- lv.animimg_set_src(w.obj, src_id, count)
70
- lv.animimg_set_repeat_count(w.obj, config[CONF_REPEAT_COUNT])
71
- lv.animimg_set_duration(w.obj, config[CONF_DURATION])
72
- if config.get(CONF_AUTO_START):
60
+ if srcs := config.get(CONF_SRC):
61
+ srcs = await lv_image_list.process(srcs)
62
+ lv.animimg_set_src(w.obj, srcs)
63
+ if repeat_count := config.get(CONF_REPEAT_COUNT):
64
+ lv.animimg_set_repeat_count(w.obj, repeat_count)
65
+ if duration := config.get(CONF_DURATION):
66
+ lv.animimg_set_duration(w.obj, duration)
67
+ if config[CONF_AUTO_START]:
73
68
  lv.animimg_start(w.obj)
74
69
 
75
70
  def get_uses(self):
@@ -1,4 +1,3 @@
1
- import esphome.codegen as cg
2
1
  import esphome.config_validation as cv
3
2
  from esphome.const import CONF_ANGLE, CONF_MODE
4
3
 
@@ -65,7 +64,6 @@ class ImgType(WidgetType):
65
64
 
66
65
  async def to_code(self, w: Widget, config):
67
66
  if src := config.get(CONF_SRC):
68
- src = await cg.get_variable(src)
69
67
  lv.img_set_src(w.obj, await lv_image.process(src))
70
68
  if (cf_angle := config.get(CONF_ANGLE)) is not None:
71
69
  pivot_x = config[CONF_PIVOT_X]
@@ -81,7 +79,7 @@ class ImgType(WidgetType):
81
79
  if CONF_ANTIALIAS in config:
82
80
  lv.img_set_antialias(w.obj, config[CONF_ANTIALIAS])
83
81
  if mode := config.get(CONF_MODE):
84
- lv.img_set_mode(w.obj, mode)
82
+ await w.set_property("size_mode", mode)
85
83
 
86
84
 
87
85
  img_spec = ImgType()
@@ -1,8 +1,8 @@
1
+ from esphome import automation, pins
1
2
  import esphome.codegen as cg
2
- import esphome.config_validation as cv
3
- from esphome import pins
4
3
  from esphome.components import key_provider
5
- from esphome.const import CONF_ID, CONF_PIN
4
+ import esphome.config_validation as cv
5
+ from esphome.const import CONF_ID, CONF_ON_KEY, CONF_PIN, CONF_TRIGGER_ID
6
6
 
7
7
  CODEOWNERS = ["@ssieb"]
8
8
 
@@ -14,6 +14,9 @@ matrix_keypad_ns = cg.esphome_ns.namespace("matrix_keypad")
14
14
  MatrixKeypad = matrix_keypad_ns.class_(
15
15
  "MatrixKeypad", key_provider.KeyProvider, cg.Component
16
16
  )
17
+ MatrixKeyTrigger = matrix_keypad_ns.class_(
18
+ "MatrixKeyTrigger", automation.Trigger.template(cg.uint8)
19
+ )
17
20
 
18
21
  CONF_KEYPAD_ID = "keypad_id"
19
22
  CONF_ROWS = "rows"
@@ -47,6 +50,11 @@ CONFIG_SCHEMA = cv.All(
47
50
  cv.Optional(CONF_DEBOUNCE_TIME, default=1): cv.int_range(min=1, max=100),
48
51
  cv.Optional(CONF_HAS_DIODES): cv.boolean,
49
52
  cv.Optional(CONF_HAS_PULLDOWNS): cv.boolean,
53
+ cv.Optional(CONF_ON_KEY): automation.validate_automation(
54
+ {
55
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MatrixKeyTrigger),
56
+ }
57
+ ),
50
58
  }
51
59
  ),
52
60
  check_keys,
@@ -73,3 +81,7 @@ async def to_code(config):
73
81
  cg.add(var.set_has_diodes(config[CONF_HAS_DIODES]))
74
82
  if CONF_HAS_PULLDOWNS in config:
75
83
  cg.add(var.set_has_pulldowns(config[CONF_HAS_PULLDOWNS]))
84
+ for conf in config.get(CONF_ON_KEY, []):
85
+ trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
86
+ cg.add(var.register_key_trigger(trigger))
87
+ await automation.build_automation(trigger, [(cg.uint8, "x")], conf)
@@ -86,6 +86,8 @@ void MatrixKeypad::loop() {
86
86
  if (!this->keys_.empty()) {
87
87
  uint8_t keycode = this->keys_[key];
88
88
  ESP_LOGD(TAG, "key '%c' pressed", keycode);
89
+ for (auto &trigger : this->key_triggers_)
90
+ trigger->trigger(keycode);
89
91
  for (auto &listener : this->listeners_)
90
92
  listener->key_pressed(keycode);
91
93
  this->send_key_(keycode);
@@ -107,5 +109,7 @@ void MatrixKeypad::dump_config() {
107
109
 
108
110
  void MatrixKeypad::register_listener(MatrixKeypadListener *listener) { this->listeners_.push_back(listener); }
109
111
 
112
+ void MatrixKeypad::register_key_trigger(MatrixKeyTrigger *trig) { this->key_triggers_.push_back(trig); }
113
+
110
114
  } // namespace matrix_keypad
111
115
  } // namespace esphome
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include "esphome/components/key_provider/key_provider.h"
4
+ #include "esphome/core/automation.h"
4
5
  #include "esphome/core/component.h"
5
6
  #include "esphome/core/hal.h"
6
7
  #include "esphome/core/helpers.h"
@@ -18,6 +19,8 @@ class MatrixKeypadListener {
18
19
  virtual void key_released(uint8_t key){};
19
20
  };
20
21
 
22
+ class MatrixKeyTrigger : public Trigger<uint8_t> {};
23
+
21
24
  class MatrixKeypad : public key_provider::KeyProvider, public Component {
22
25
  public:
23
26
  void setup() override;
@@ -31,6 +34,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
31
34
  void set_has_pulldowns(int has_pulldowns) { has_pulldowns_ = has_pulldowns; };
32
35
 
33
36
  void register_listener(MatrixKeypadListener *listener);
37
+ void register_key_trigger(MatrixKeyTrigger *trig);
34
38
 
35
39
  protected:
36
40
  std::vector<GPIOPin *> rows_;
@@ -42,6 +46,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
42
46
  int pressed_key_ = -1;
43
47
 
44
48
  std::vector<MatrixKeypadListener *> listeners_{};
49
+ std::vector<MatrixKeyTrigger *> key_triggers_;
45
50
  };
46
51
 
47
52
  } // namespace matrix_keypad
@@ -106,7 +106,8 @@ void MAX31865Sensor::read_data_() {
106
106
 
107
107
  // Check faults
108
108
  const uint8_t faults = this->read_register_(FAULT_STATUS_REG);
109
- if ((has_fault_ = faults & 0b00111100)) {
109
+ has_fault_ = faults & 0b00111100;
110
+ if (has_fault_) {
110
111
  if (faults & (1 << 2)) {
111
112
  ESP_LOGE(TAG, "Overvoltage/undervoltage fault");
112
113
  }
@@ -125,7 +126,8 @@ void MAX31865Sensor::read_data_() {
125
126
  } else {
126
127
  this->status_clear_error();
127
128
  }
128
- if ((has_warn_ = faults & 0b11000000)) {
129
+ has_warn_ = faults & 0b11000000;
130
+ if (has_warn_) {
129
131
  if (faults & (1 << 6)) {
130
132
  ESP_LOGW(TAG, "RTD Low Threshold");
131
133
  }