esphome 2025.4.2__py3-none-any.whl → 2025.5.0b3__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 (440) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  3. esphome/components/adc/__init__.py +51 -34
  4. esphome/components/airthings_wave_base/__init__.py +1 -1
  5. esphome/components/alarm_control_panel/__init__.py +37 -2
  6. esphome/components/am43/cover/__init__.py +4 -5
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  8. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  9. esphome/components/analog_threshold/binary_sensor.py +10 -8
  10. esphome/components/anova/climate.py +4 -5
  11. esphome/components/api/__init__.py +25 -8
  12. esphome/components/api/api_connection.cpp +81 -13
  13. esphome/components/api/api_connection.h +13 -1
  14. esphome/components/api/api_frame_helper.cpp +232 -177
  15. esphome/components/api/api_frame_helper.h +61 -8
  16. esphome/components/api/api_noise_context.h +13 -4
  17. esphome/components/api/api_pb2.cpp +1422 -1
  18. esphome/components/api/api_pb2.h +255 -1
  19. esphome/components/api/api_pb2_service.cpp +162 -49
  20. esphome/components/api/api_pb2_service.h +90 -51
  21. esphome/components/api/api_pb2_size.h +361 -0
  22. esphome/components/api/api_server.cpp +110 -34
  23. esphome/components/api/api_server.h +8 -0
  24. esphome/components/api/proto.h +86 -17
  25. esphome/components/as7341/as7341.h +1 -1
  26. esphome/components/atm90e32/__init__.py +1 -0
  27. esphome/components/atm90e32/atm90e32.cpp +576 -199
  28. esphome/components/atm90e32/atm90e32.h +128 -31
  29. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  30. esphome/components/atm90e32/button/__init__.py +62 -10
  31. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  32. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  33. esphome/components/atm90e32/number/__init__.py +130 -0
  34. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  35. esphome/components/atm90e32/sensor.py +21 -4
  36. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  37. esphome/components/audio/__init__.py +96 -49
  38. esphome/components/audio/audio.h +48 -0
  39. esphome/components/audio/audio_decoder.cpp +1 -1
  40. esphome/components/audio/audio_resampler.cpp +2 -0
  41. esphome/components/audio/audio_resampler.h +1 -0
  42. esphome/components/ballu/climate.py +2 -9
  43. esphome/components/bang_bang/climate.py +5 -6
  44. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  45. esphome/components/bedjet/climate/__init__.py +3 -8
  46. esphome/components/bedjet/fan/__init__.py +2 -11
  47. esphome/components/binary/fan/__init__.py +13 -16
  48. esphome/components/binary_sensor/__init__.py +13 -10
  49. esphome/components/bl0906/constants.h +16 -16
  50. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  51. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  52. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  53. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  54. esphome/components/button/__init__.py +11 -8
  55. esphome/components/canbus/canbus.cpp +3 -0
  56. esphome/components/canbus/canbus.h +16 -0
  57. esphome/components/ccs811/sensor.py +9 -6
  58. esphome/components/climate/__init__.py +35 -2
  59. esphome/components/climate/climate_mode.h +1 -1
  60. esphome/components/climate/climate_traits.h +63 -57
  61. esphome/components/climate_ir/__init__.py +57 -17
  62. esphome/components/climate_ir_lg/climate.py +2 -5
  63. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  64. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  65. esphome/components/color/__init__.py +2 -0
  66. esphome/components/const/__init__.py +5 -0
  67. esphome/components/coolix/climate.py +2 -9
  68. esphome/components/copy/cover/__init__.py +10 -9
  69. esphome/components/copy/fan/__init__.py +11 -9
  70. esphome/components/copy/lock/__init__.py +11 -9
  71. esphome/components/copy/text/__init__.py +9 -6
  72. esphome/components/cover/__init__.py +37 -2
  73. esphome/components/cse7766/cse7766.cpp +2 -1
  74. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  75. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  76. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  77. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  78. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  79. esphome/components/current_based/cover.py +37 -36
  80. esphome/components/current_based/current_based_cover.cpp +2 -1
  81. esphome/components/daikin/climate.py +2 -9
  82. esphome/components/daikin/daikin.cpp +15 -9
  83. esphome/components/daikin/daikin.h +5 -5
  84. esphome/components/daikin_arc/climate.py +2 -7
  85. esphome/components/daikin_brc/climate.py +3 -5
  86. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  87. esphome/components/dallas_temp/dallas_temp.h +0 -1
  88. esphome/components/daly_bms/daly_bms.cpp +2 -1
  89. esphome/components/debug/debug_component.cpp +6 -1
  90. esphome/components/debug/debug_component.h +6 -0
  91. esphome/components/debug/debug_esp32.cpp +109 -254
  92. esphome/components/debug/sensor.py +14 -0
  93. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  94. esphome/components/delonghi/climate.py +2 -9
  95. esphome/components/demo/__init__.py +18 -20
  96. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  97. esphome/components/dps310/sensor.py +6 -6
  98. esphome/components/ee895/sensor.py +9 -9
  99. esphome/components/emmeti/climate.py +2 -9
  100. esphome/components/endstop/cover.py +17 -16
  101. esphome/components/endstop/endstop_cover.cpp +2 -1
  102. esphome/components/ens160_base/__init__.py +12 -9
  103. esphome/components/esp32/__init__.py +60 -3
  104. esphome/components/esp32/core.cpp +11 -5
  105. esphome/components/esp32/gpio.cpp +86 -24
  106. esphome/components/esp32/gpio.py +15 -16
  107. esphome/components/esp32/gpio_esp32.py +1 -2
  108. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  109. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  110. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  111. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  112. esphome/components/esp32_ble/ble.cpp +1 -0
  113. esphome/components/esp32_ble/ble.h +5 -3
  114. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  115. esphome/components/esp32_ble/ble_advertising.h +1 -0
  116. esphome/components/esp32_ble_server/__init__.py +3 -0
  117. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  118. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  119. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  120. esphome/components/esp32_camera/esp32_camera.cpp +2 -1
  121. esphome/components/esp32_camera/esp32_camera.h +1 -1
  122. esphome/components/esp32_can/esp32_can.cpp +1 -1
  123. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  124. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  125. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  126. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  127. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  128. esphome/components/esp8266/gpio.cpp +69 -8
  129. esphome/components/ethernet/ethernet_component.cpp +1 -1
  130. esphome/components/event/__init__.py +13 -10
  131. esphome/components/factory_reset/switch/__init__.py +7 -21
  132. esphome/components/fan/__init__.py +52 -5
  133. esphome/components/fastled_base/__init__.py +1 -4
  134. esphome/components/fastled_base/fastled_light.cpp +1 -1
  135. esphome/components/feedback/cover.py +38 -33
  136. esphome/components/feedback/feedback_cover.cpp +2 -1
  137. esphome/components/fujitsu_general/climate.py +2 -9
  138. esphome/components/gcja5/gcja5.cpp +2 -1
  139. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  140. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  141. esphome/components/gpio_expander/cached_gpio.h +22 -7
  142. esphome/components/gps/__init__.py +47 -17
  143. esphome/components/gps/gps.cpp +42 -23
  144. esphome/components/gps/gps.h +17 -13
  145. esphome/components/graph/__init__.py +1 -2
  146. esphome/components/gree/climate.py +4 -6
  147. esphome/components/gree/gree.cpp +16 -2
  148. esphome/components/gree/gree.h +2 -2
  149. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  150. esphome/components/haier/climate.py +37 -34
  151. esphome/components/hbridge/fan/__init__.py +19 -17
  152. esphome/components/he60r/cover.py +4 -5
  153. esphome/components/heatpumpir/climate.py +3 -6
  154. esphome/components/hitachi_ac344/climate.py +2 -9
  155. esphome/components/hitachi_ac424/climate.py +2 -9
  156. esphome/components/hm3301/hm3301.h +1 -1
  157. esphome/components/hte501/sensor.py +6 -6
  158. esphome/components/http_request/__init__.py +39 -6
  159. esphome/components/http_request/http_request.cpp +20 -0
  160. esphome/components/http_request/http_request.h +57 -15
  161. esphome/components/http_request/http_request_arduino.cpp +22 -6
  162. esphome/components/http_request/http_request_arduino.h +4 -3
  163. esphome/components/http_request/http_request_host.cpp +141 -0
  164. esphome/components/http_request/http_request_host.h +37 -0
  165. esphome/components/http_request/http_request_idf.cpp +35 -3
  166. esphome/components/http_request/http_request_idf.h +10 -3
  167. esphome/components/http_request/httplib.h +9691 -0
  168. esphome/components/http_request/update/__init__.py +11 -8
  169. esphome/components/hyt271/sensor.py +6 -6
  170. esphome/components/i2c/i2c.h +4 -0
  171. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  172. esphome/components/i2s_audio/__init__.py +131 -22
  173. esphome/components/i2s_audio/i2s_audio.h +44 -4
  174. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  175. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  176. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  177. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  178. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  179. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  180. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  181. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  182. esphome/components/image/__init__.py +37 -17
  183. esphome/components/image/image.cpp +25 -8
  184. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  185. esphome/components/key_collector/__init__.py +35 -0
  186. esphome/components/key_collector/key_collector.cpp +8 -0
  187. esphome/components/key_collector/key_collector.h +10 -0
  188. esphome/components/kuntze/kuntze.cpp +2 -1
  189. esphome/components/ld2410/ld2410.h +1 -1
  190. esphome/components/ld2450/ld2450.h +1 -1
  191. esphome/components/light/__init__.py +57 -0
  192. esphome/components/lock/__init__.py +51 -4
  193. esphome/components/lock/automation.h +2 -13
  194. esphome/components/logger/__init__.py +22 -0
  195. esphome/components/logger/logger.cpp +154 -103
  196. esphome/components/logger/logger.h +211 -36
  197. esphome/components/logger/task_log_buffer.cpp +138 -0
  198. esphome/components/logger/task_log_buffer.h +69 -0
  199. esphome/components/lvgl/__init__.py +13 -5
  200. esphome/components/lvgl/automation.py +50 -1
  201. esphome/components/lvgl/defines.py +0 -1
  202. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  203. esphome/components/lvgl/text/__init__.py +1 -2
  204. esphome/components/mapping/__init__.py +134 -0
  205. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  206. esphome/components/max7219digit/max7219digit.cpp +28 -27
  207. esphome/components/mdns/__init__.py +11 -5
  208. esphome/components/mdns/mdns_component.cpp +11 -5
  209. esphome/components/mdns/mdns_component.h +3 -2
  210. esphome/components/mdns/mdns_esp32.cpp +4 -3
  211. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  212. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  213. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  214. esphome/components/media_player/__init__.py +33 -1
  215. esphome/components/mhz19/sensor.py +11 -7
  216. esphome/components/micro_wake_word/__init__.py +99 -31
  217. esphome/components/micro_wake_word/automation.h +54 -0
  218. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  219. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  220. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  221. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  222. esphome/components/micro_wake_word/streaming_model.h +85 -13
  223. esphome/components/microphone/__init__.py +139 -9
  224. esphome/components/microphone/automation.h +14 -2
  225. esphome/components/microphone/microphone.cpp +21 -0
  226. esphome/components/microphone/microphone.h +14 -5
  227. esphome/components/microphone/microphone_source.cpp +95 -0
  228. esphome/components/microphone/microphone_source.h +80 -0
  229. esphome/components/mics_4514/sensor.py +25 -14
  230. esphome/components/midea/climate.py +3 -4
  231. esphome/components/midea_ir/climate.py +3 -5
  232. esphome/components/mipi_spi/__init__.py +15 -0
  233. esphome/components/mipi_spi/display.py +474 -0
  234. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  235. esphome/components/mipi_spi/mipi_spi.h +171 -0
  236. esphome/components/mipi_spi/models/__init__.py +65 -0
  237. esphome/components/mipi_spi/models/amoled.py +72 -0
  238. esphome/components/mipi_spi/models/commands.py +82 -0
  239. esphome/components/mipi_spi/models/cyd.py +10 -0
  240. esphome/components/mipi_spi/models/ili.py +749 -0
  241. esphome/components/mipi_spi/models/jc.py +260 -0
  242. esphome/components/mipi_spi/models/lanbon.py +15 -0
  243. esphome/components/mipi_spi/models/lilygo.py +60 -0
  244. esphome/components/mipi_spi/models/waveshare.py +139 -0
  245. esphome/components/mitsubishi/climate.py +2 -5
  246. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  247. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  248. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  249. esphome/components/mlx90393/sensor.py +5 -0
  250. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  251. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  252. esphome/components/modbus/modbus.cpp +2 -1
  253. esphome/components/mqtt/__init__.py +1 -1
  254. esphome/components/mqtt/mqtt_client.cpp +6 -2
  255. esphome/components/mqtt/mqtt_const.h +4 -0
  256. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  257. esphome/components/mqtt/mqtt_fan.h +2 -0
  258. esphome/components/ms5611/sensor.py +6 -6
  259. esphome/components/ms8607/sensor.py +3 -3
  260. esphome/components/network/__init__.py +1 -1
  261. esphome/components/nextion/base_component.py +17 -16
  262. esphome/components/nextion/display.py +11 -2
  263. esphome/components/nextion/nextion.cpp +39 -1
  264. esphome/components/nextion/nextion.h +50 -0
  265. esphome/components/noblex/climate.py +2 -9
  266. esphome/components/number/__init__.py +12 -9
  267. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  268. esphome/components/one_wire/one_wire_bus.h +14 -8
  269. esphome/components/online_image/bmp_image.cpp +48 -11
  270. esphome/components/online_image/bmp_image.h +2 -0
  271. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  272. esphome/components/opentherm/number/__init__.py +11 -20
  273. esphome/components/opentherm/sensor/__init__.py +3 -3
  274. esphome/components/opentherm/switch/__init__.py +3 -5
  275. esphome/components/output/lock/__init__.py +11 -9
  276. esphome/components/packages/__init__.py +33 -31
  277. esphome/components/packet_transport/__init__.py +201 -0
  278. esphome/components/packet_transport/binary_sensor.py +19 -0
  279. esphome/components/packet_transport/packet_transport.cpp +534 -0
  280. esphome/components/packet_transport/packet_transport.h +154 -0
  281. esphome/components/packet_transport/sensor.py +19 -0
  282. esphome/components/pca9685/pca9685_output.cpp +2 -1
  283. esphome/components/pid/climate.py +2 -4
  284. esphome/components/pm2005/__init__.py +1 -0
  285. esphome/components/pm2005/pm2005.cpp +123 -0
  286. esphome/components/pm2005/pm2005.h +46 -0
  287. esphome/components/pm2005/sensor.py +86 -0
  288. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  289. esphome/components/pmsa003i/pmsa003i.h +25 -25
  290. esphome/components/pmsx003/pmsx003.cpp +195 -230
  291. esphome/components/pmsx003/pmsx003.h +51 -33
  292. esphome/components/pmsx003/sensor.py +21 -11
  293. esphome/components/pn7150/pn7150.h +2 -2
  294. esphome/components/pn7160/pn7160.h +2 -2
  295. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  296. esphome/components/prometheus/prometheus_handler.h +17 -0
  297. esphome/components/psram/__init__.py +7 -5
  298. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  299. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  300. esphome/components/pzem004t/pzem004t.cpp +2 -1
  301. esphome/components/qspi_dbi/__init__.py +0 -1
  302. esphome/components/qspi_dbi/display.py +2 -1
  303. esphome/components/qspi_dbi/models.py +1 -2
  304. esphome/components/remote_base/__init__.py +91 -0
  305. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  306. esphome/components/remote_base/beo4_protocol.h +43 -0
  307. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  308. esphome/components/remote_base/gobox_protocol.h +54 -0
  309. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  310. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  311. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  312. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  313. esphome/components/scd30/sensor.py +2 -3
  314. esphome/components/scd4x/sensor.py +4 -5
  315. esphome/components/sdp3x/sensor.py +2 -1
  316. esphome/components/sds011/sds011.cpp +2 -1
  317. esphome/components/select/__init__.py +19 -20
  318. esphome/components/sen5x/sen5x.cpp +55 -36
  319. esphome/components/sen5x/sensor.py +1 -1
  320. esphome/components/senseair/sensor.py +3 -3
  321. esphome/components/sensor/__init__.py +158 -14
  322. esphome/components/sensor/filter.cpp +23 -0
  323. esphome/components/sensor/filter.h +22 -0
  324. esphome/components/sgp30/sensor.py +14 -16
  325. esphome/components/sgp4x/sensor.py +1 -1
  326. esphome/components/sht4x/sht4x.cpp +43 -22
  327. esphome/components/sht4x/sht4x.h +1 -1
  328. esphome/components/shtcx/sensor.py +6 -6
  329. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  330. esphome/components/sml/text_sensor/__init__.py +4 -6
  331. esphome/components/sound_level/__init__.py +0 -0
  332. esphome/components/sound_level/sensor.py +97 -0
  333. esphome/components/sound_level/sound_level.cpp +194 -0
  334. esphome/components/sound_level/sound_level.h +73 -0
  335. esphome/components/speaker/media_player/__init__.py +4 -8
  336. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  337. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  338. esphome/components/speaker/speaker.h +4 -7
  339. esphome/components/speed/fan/__init__.py +17 -16
  340. esphome/components/spi/spi.h +11 -1
  341. esphome/components/sprinkler/__init__.py +18 -19
  342. esphome/components/sprinkler/sprinkler.cpp +6 -5
  343. esphome/components/switch/__init__.py +32 -42
  344. esphome/components/syslog/__init__.py +41 -0
  345. esphome/components/syslog/esphome_syslog.cpp +49 -0
  346. esphome/components/syslog/esphome_syslog.h +27 -0
  347. esphome/components/t6615/sensor.py +3 -3
  348. esphome/components/t6615/t6615.cpp +2 -1
  349. esphome/components/tca9555/tca9555.cpp +11 -6
  350. esphome/components/tcl112/climate.py +2 -9
  351. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  352. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  353. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  354. esphome/components/template/cover/__init__.py +27 -21
  355. esphome/components/template/fan/__init__.py +14 -12
  356. esphome/components/template/lock/__init__.py +20 -25
  357. esphome/components/template/lock/automation.h +18 -0
  358. esphome/components/template/text/__init__.py +4 -3
  359. esphome/components/template/valve/__init__.py +32 -21
  360. esphome/components/template/valve/automation.h +24 -0
  361. esphome/components/text/__init__.py +32 -1
  362. esphome/components/text_sensor/__init__.py +24 -29
  363. esphome/components/thermostat/climate.py +5 -5
  364. esphome/components/time_based/cover.py +17 -16
  365. esphome/components/time_based/time_based_cover.cpp +2 -1
  366. esphome/components/tm1638/switch/__init__.py +10 -7
  367. esphome/components/tormatic/cover.py +4 -5
  368. esphome/components/toshiba/climate.py +3 -5
  369. esphome/components/touchscreen/touchscreen.cpp +3 -1
  370. esphome/components/tuya/climate/__init__.py +5 -6
  371. esphome/components/tuya/cover/__init__.py +6 -11
  372. esphome/components/tuya/select/__init__.py +15 -5
  373. esphome/components/tuya/select/tuya_select.cpp +6 -1
  374. esphome/components/tuya/select/tuya_select.h +5 -1
  375. esphome/components/uart/packet_transport/__init__.py +20 -0
  376. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  377. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  378. esphome/components/uart/switch/uart_switch.cpp +2 -1
  379. esphome/components/udp/__init__.py +126 -128
  380. esphome/components/udp/automation.h +40 -0
  381. esphome/components/udp/binary_sensor.py +3 -25
  382. esphome/components/udp/packet_transport/__init__.py +29 -0
  383. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  384. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  385. esphome/components/udp/sensor.py +3 -25
  386. esphome/components/udp/udp_component.cpp +26 -470
  387. esphome/components/udp/udp_component.h +21 -128
  388. esphome/components/update/__init__.py +31 -1
  389. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  390. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  391. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  392. esphome/components/uptime/text_sensor/__init__.py +47 -7
  393. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  394. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  395. esphome/components/valve/__init__.py +34 -3
  396. esphome/components/valve/automation.h +1 -19
  397. esphome/components/vl53l0x/sensor.py +11 -0
  398. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  399. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  400. esphome/components/voice_assistant/__init__.py +36 -10
  401. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  402. esphome/components/voice_assistant/voice_assistant.h +26 -25
  403. esphome/components/waveshare_epaper/display.py +6 -0
  404. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  405. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  406. esphome/components/whirlpool/climate.py +3 -5
  407. esphome/components/whynter/climate.py +3 -5
  408. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  409. esphome/components/yashima/climate.py +6 -6
  410. esphome/components/zhlt01/climate.py +2 -7
  411. esphome/config.py +13 -13
  412. esphome/config_validation.py +38 -58
  413. esphome/const.py +15 -1
  414. esphome/core/__init__.py +2 -0
  415. esphome/core/application.cpp +23 -10
  416. esphome/core/application.h +9 -1
  417. esphome/core/automation.h +4 -3
  418. esphome/core/component.cpp +28 -7
  419. esphome/core/component.h +10 -1
  420. esphome/core/defines.h +23 -17
  421. esphome/core/macros.h +4 -0
  422. esphome/core/scheduler.cpp +7 -1
  423. esphome/cpp_generator.py +6 -2
  424. esphome/dashboard/web_server.py +3 -3
  425. esphome/helpers.py +39 -0
  426. esphome/loader.py +4 -0
  427. esphome/log.py +15 -19
  428. esphome/mqtt.py +23 -10
  429. esphome/platformio_api.py +1 -1
  430. esphome/schema_extractors.py +0 -1
  431. esphome/voluptuous_schema.py +3 -1
  432. esphome/vscode.py +15 -0
  433. esphome/wizard.py +47 -37
  434. esphome/zeroconf.py +7 -3
  435. {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/METADATA +10 -11
  436. {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/RECORD +440 -380
  437. {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/WHEEL +1 -1
  438. {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/entry_points.txt +0 -0
  439. {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/licenses/LICENSE +0 -0
  440. {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,11 @@
2
2
 
3
3
  #ifdef USE_ESP32
4
4
 
5
+ #ifdef USE_I2S_LEGACY
5
6
  #include <driver/i2s.h>
7
+ #else
8
+ #include <driver/i2s_std.h>
9
+ #endif
6
10
 
7
11
  #include "esphome/components/audio/audio.h"
8
12
 
@@ -10,6 +14,8 @@
10
14
  #include "esphome/core/hal.h"
11
15
  #include "esphome/core/log.h"
12
16
 
17
+ #include "esp_timer.h"
18
+
13
19
  namespace esphome {
14
20
  namespace i2s_audio {
15
21
 
@@ -294,13 +300,21 @@ void I2SAudioSpeaker::speaker_task(void *params) {
294
300
  // Audio stream info changed, stop the speaker task so it will restart with the proper settings.
295
301
  break;
296
302
  }
297
-
303
+ #ifdef USE_I2S_LEGACY
298
304
  i2s_event_t i2s_event;
299
305
  while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) {
300
306
  if (i2s_event.type == I2S_EVENT_TX_Q_OVF) {
301
307
  tx_dma_underflow = true;
302
308
  }
303
309
  }
310
+ #else
311
+ bool overflow;
312
+ while (xQueueReceive(this_speaker->i2s_event_queue_, &overflow, 0)) {
313
+ if (overflow) {
314
+ tx_dma_underflow = true;
315
+ }
316
+ }
317
+ #endif
304
318
 
305
319
  if (this_speaker->pause_state_) {
306
320
  // Pause state is accessed atomically, so thread safe
@@ -319,6 +333,18 @@ void I2SAudioSpeaker::speaker_task(void *params) {
319
333
  bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_);
320
334
  }
321
335
 
336
+ #ifdef USE_ESP32_VARIANT_ESP32
337
+ // For ESP32 8/16 bit mono mode samples need to be switched.
338
+ if (audio_stream_info.get_channels() == 1 && audio_stream_info.get_bits_per_sample() <= 16) {
339
+ size_t len = bytes_read / sizeof(int16_t);
340
+ int16_t *tmp_buf = (int16_t *) this_speaker->data_buffer_;
341
+ for (int i = 0; i < len; i += 2) {
342
+ int16_t tmp = tmp_buf[i];
343
+ tmp_buf[i] = tmp_buf[i + 1];
344
+ tmp_buf[i + 1] = tmp;
345
+ }
346
+ }
347
+ #endif
322
348
  // Write the audio data to a single DMA buffer at a time to reduce latency for the audio duration played
323
349
  // callback.
324
350
  const uint32_t batches = (bytes_read + single_dma_buffer_input_size - 1) / single_dma_buffer_input_size;
@@ -327,6 +353,7 @@ void I2SAudioSpeaker::speaker_task(void *params) {
327
353
  size_t bytes_written = 0;
328
354
  size_t bytes_to_write = std::min(single_dma_buffer_input_size, bytes_read);
329
355
 
356
+ #ifdef USE_I2S_LEGACY
330
357
  if (audio_stream_info.get_bits_per_sample() == (uint8_t) this_speaker->bits_per_sample_) {
331
358
  i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_ + i * single_dma_buffer_input_size,
332
359
  bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
@@ -336,26 +363,20 @@ void I2SAudioSpeaker::speaker_task(void *params) {
336
363
  audio_stream_info.get_bits_per_sample(), this_speaker->bits_per_sample_, &bytes_written,
337
364
  pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
338
365
  }
366
+ #else
367
+ i2s_channel_write(this_speaker->tx_handle_, this_speaker->data_buffer_ + i * single_dma_buffer_input_size,
368
+ bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
369
+ #endif
339
370
 
340
- uint32_t write_timestamp = micros();
371
+ int64_t now = esp_timer_get_time();
341
372
 
342
373
  if (bytes_written != bytes_to_write) {
343
374
  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
344
375
  }
345
-
346
376
  bytes_read -= bytes_written;
347
377
 
348
- this_speaker->accumulated_frames_written_ += audio_stream_info.bytes_to_frames(bytes_written);
349
- const uint32_t new_playback_ms =
350
- audio_stream_info.frames_to_milliseconds_with_remainder(&this_speaker->accumulated_frames_written_);
351
- const uint32_t remainder_us =
352
- audio_stream_info.frames_to_microseconds(this_speaker->accumulated_frames_written_);
353
-
354
- uint32_t pending_frames =
355
- audio_stream_info.bytes_to_frames(bytes_read + this_speaker->audio_ring_buffer_->available());
356
- const uint32_t pending_ms = audio_stream_info.frames_to_milliseconds_with_remainder(&pending_frames);
357
-
358
- this_speaker->audio_output_callback_(new_playback_ms, remainder_us, pending_ms, write_timestamp);
378
+ this_speaker->audio_output_callback_(audio_stream_info.bytes_to_frames(bytes_written),
379
+ now + dma_buffers_duration_ms * 1000);
359
380
 
360
381
  tx_dma_underflow = false;
361
382
  last_data_received_time = millis();
@@ -369,8 +390,12 @@ void I2SAudioSpeaker::speaker_task(void *params) {
369
390
  }
370
391
 
371
392
  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
372
-
393
+ #ifdef USE_I2S_LEGACY
373
394
  i2s_driver_uninstall(this_speaker->parent_->get_port());
395
+ #else
396
+ i2s_channel_disable(this_speaker->tx_handle_);
397
+ i2s_del_channel(this_speaker->tx_handle_);
398
+ #endif
374
399
 
375
400
  this_speaker->parent_->unlock();
376
401
  }
@@ -462,12 +487,21 @@ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t rin
462
487
  }
463
488
 
464
489
  esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) {
490
+ #ifdef USE_I2S_LEGACY
465
491
  if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT
492
+ #else
493
+ if ((this->i2s_role_ & I2S_ROLE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT
494
+ #endif
466
495
  // Can't reconfigure I2S bus, so the sample rate must match the configured value
467
496
  return ESP_ERR_NOT_SUPPORTED;
468
497
  }
469
498
 
499
+ #ifdef USE_I2S_LEGACY
470
500
  if ((i2s_bits_per_sample_t) audio_stream_info.get_bits_per_sample() > this->bits_per_sample_) {
501
+ #else
502
+ if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO &&
503
+ (i2s_slot_bit_width_t) audio_stream_info.get_bits_per_sample() > this->slot_bit_width_) {
504
+ #endif
471
505
  // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
472
506
  return ESP_ERR_NOT_SUPPORTED;
473
507
  }
@@ -476,6 +510,9 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
476
510
  return ESP_ERR_INVALID_STATE;
477
511
  }
478
512
 
513
+ uint32_t dma_buffer_length = audio_stream_info.ms_to_frames(DMA_BUFFER_DURATION_MS);
514
+
515
+ #ifdef USE_I2S_LEGACY
479
516
  i2s_channel_fmt_t channel = this->channel_;
480
517
 
481
518
  if (audio_stream_info.get_channels() == 1) {
@@ -488,8 +525,6 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
488
525
  channel = I2S_CHANNEL_FMT_RIGHT_LEFT;
489
526
  }
490
527
 
491
- int dma_buffer_length = audio_stream_info.ms_to_frames(DMA_BUFFER_DURATION_MS);
492
-
493
528
  i2s_driver_config_t config = {
494
529
  .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX),
495
530
  .sample_rate = audio_stream_info.get_sample_rate(),
@@ -498,11 +533,11 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
498
533
  .communication_format = this->i2s_comm_fmt_,
499
534
  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
500
535
  .dma_buf_count = DMA_BUFFERS_COUNT,
501
- .dma_buf_len = dma_buffer_length,
536
+ .dma_buf_len = (int) dma_buffer_length,
502
537
  .use_apll = this->use_apll_,
503
538
  .tx_desc_auto_clear = true,
504
539
  .fixed_mclk = I2S_PIN_NO_CHANGE,
505
- .mclk_multiple = I2S_MCLK_MULTIPLE_256,
540
+ .mclk_multiple = this->mclk_multiple_,
506
541
  .bits_per_chan = this->bits_per_channel_,
507
542
  #if SOC_I2S_SUPPORTS_TDM
508
543
  .chan_mask = (i2s_channel_t) (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1),
@@ -545,6 +580,98 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
545
580
  i2s_driver_uninstall(this->parent_->get_port());
546
581
  this->parent_->unlock();
547
582
  }
583
+ #else
584
+ i2s_chan_config_t chan_cfg = {
585
+ .id = this->parent_->get_port(),
586
+ .role = this->i2s_role_,
587
+ .dma_desc_num = DMA_BUFFERS_COUNT,
588
+ .dma_frame_num = dma_buffer_length,
589
+ .auto_clear = true,
590
+ };
591
+ /* Allocate a new TX channel and get the handle of this channel */
592
+ esp_err_t err = i2s_new_channel(&chan_cfg, &this->tx_handle_, NULL);
593
+ if (err != ESP_OK) {
594
+ this->parent_->unlock();
595
+ return err;
596
+ }
597
+
598
+ i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
599
+ #ifdef I2S_CLK_SRC_APLL
600
+ if (this->use_apll_) {
601
+ clk_src = I2S_CLK_SRC_APLL;
602
+ }
603
+ #endif
604
+ i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config();
605
+
606
+ i2s_std_clk_config_t clk_cfg = {
607
+ .sample_rate_hz = audio_stream_info.get_sample_rate(),
608
+ .clk_src = clk_src,
609
+ .mclk_multiple = this->mclk_multiple_,
610
+ };
611
+
612
+ i2s_slot_mode_t slot_mode = this->slot_mode_;
613
+ i2s_std_slot_mask_t slot_mask = this->std_slot_mask_;
614
+ if (audio_stream_info.get_channels() == 1) {
615
+ slot_mode = I2S_SLOT_MODE_MONO;
616
+ } else if (audio_stream_info.get_channels() == 2) {
617
+ slot_mode = I2S_SLOT_MODE_STEREO;
618
+ slot_mask = I2S_STD_SLOT_BOTH;
619
+ }
620
+
621
+ i2s_std_slot_config_t std_slot_cfg;
622
+ if (this->i2s_comm_fmt_ == "std") {
623
+ std_slot_cfg =
624
+ I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) audio_stream_info.get_bits_per_sample(), slot_mode);
625
+ } else if (this->i2s_comm_fmt_ == "pcm") {
626
+ std_slot_cfg =
627
+ I2S_STD_PCM_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) audio_stream_info.get_bits_per_sample(), slot_mode);
628
+ } else {
629
+ std_slot_cfg =
630
+ I2S_STD_MSB_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) audio_stream_info.get_bits_per_sample(), slot_mode);
631
+ }
632
+ #ifdef USE_ESP32_VARIANT_ESP32
633
+ // There seems to be a bug on the ESP32 (non-variant) platform where setting the slot bit width higher then the bits
634
+ // per sample causes the audio to play too fast. Setting the ws_width to the configured slot bit width seems to
635
+ // make it play at the correct speed while sending more bits per slot.
636
+ if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO) {
637
+ std_slot_cfg.ws_width = static_cast<uint32_t>(this->slot_bit_width_);
638
+ }
639
+ #else
640
+ std_slot_cfg.slot_bit_width = this->slot_bit_width_;
641
+ #endif
642
+ std_slot_cfg.slot_mask = slot_mask;
643
+
644
+ pin_config.dout = this->dout_pin_;
645
+
646
+ i2s_std_config_t std_cfg = {
647
+ .clk_cfg = clk_cfg,
648
+ .slot_cfg = std_slot_cfg,
649
+ .gpio_cfg = pin_config,
650
+ };
651
+ /* Initialize the channel */
652
+ err = i2s_channel_init_std_mode(this->tx_handle_, &std_cfg);
653
+
654
+ if (err != ESP_OK) {
655
+ i2s_del_channel(this->tx_handle_);
656
+ this->parent_->unlock();
657
+ return err;
658
+ }
659
+ if (this->i2s_event_queue_ == nullptr) {
660
+ this->i2s_event_queue_ = xQueueCreate(1, sizeof(bool));
661
+ }
662
+ const i2s_event_callbacks_t callbacks = {
663
+ .on_send_q_ovf = i2s_overflow_cb,
664
+ };
665
+
666
+ i2s_channel_register_event_callback(this->tx_handle_, &callbacks, this);
667
+
668
+ /* Before reading data, start the TX channel first */
669
+ i2s_channel_enable(this->tx_handle_);
670
+ if (err != ESP_OK) {
671
+ i2s_del_channel(this->tx_handle_);
672
+ this->parent_->unlock();
673
+ }
674
+ #endif
548
675
 
549
676
  return err;
550
677
  }
@@ -564,6 +691,15 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
564
691
  vTaskDelete(nullptr);
565
692
  }
566
693
 
694
+ #ifndef USE_I2S_LEGACY
695
+ bool IRAM_ATTR I2SAudioSpeaker::i2s_overflow_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) {
696
+ I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) user_ctx;
697
+ bool overflow = true;
698
+ xQueueOverwrite(this_speaker->i2s_event_queue_, &overflow);
699
+ return false;
700
+ }
701
+ #endif
702
+
567
703
  } // namespace i2s_audio
568
704
  } // namespace esphome
569
705
 
@@ -4,8 +4,6 @@
4
4
 
5
5
  #include "../i2s_audio.h"
6
6
 
7
- #include <driver/i2s.h>
8
-
9
7
  #include <freertos/event_groups.h>
10
8
  #include <freertos/queue.h>
11
9
  #include <freertos/FreeRTOS.h>
@@ -30,11 +28,16 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
30
28
 
31
29
  void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
32
30
  void set_timeout(uint32_t ms) { this->timeout_ = ms; }
33
- void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; }
31
+ #ifdef USE_I2S_LEGACY
34
32
  #if SOC_I2S_SUPPORTS_DAC
35
33
  void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; }
36
34
  #endif
35
+ void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; }
37
36
  void set_i2s_comm_fmt(i2s_comm_format_t mode) { this->i2s_comm_fmt_ = mode; }
37
+ #else
38
+ void set_dout_pin(uint8_t pin) { this->dout_pin_ = (gpio_num_t) pin; }
39
+ void set_i2s_comm_fmt(std::string mode) { this->i2s_comm_fmt_ = std::move(mode); }
40
+ #endif
38
41
 
39
42
  void start() override;
40
43
  void stop() override;
@@ -86,6 +89,10 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
86
89
  /// @return True if an ERR_ESP bit is set and false if err == ESP_OK
87
90
  bool send_esp_err_to_event_group_(esp_err_t err);
88
91
 
92
+ #ifndef USE_I2S_LEGACY
93
+ static bool i2s_overflow_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
94
+ #endif
95
+
89
96
  /// @brief Allocates the data buffer and ring buffer
90
97
  /// @param data_buffer_size Number of bytes to allocate for the data buffer.
91
98
  /// @param ring_buffer_size Number of bytes to allocate for the ring buffer.
@@ -121,7 +128,6 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
121
128
  uint32_t buffer_duration_ms_;
122
129
 
123
130
  optional<uint32_t> timeout_;
124
- uint8_t dout_pin_;
125
131
 
126
132
  bool task_created_{false};
127
133
  bool pause_state_{false};
@@ -130,10 +136,17 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
130
136
 
131
137
  size_t bytes_written_{0};
132
138
 
139
+ #ifdef USE_I2S_LEGACY
133
140
  #if SOC_I2S_SUPPORTS_DAC
134
141
  i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE};
135
142
  #endif
143
+ uint8_t dout_pin_;
136
144
  i2s_comm_format_t i2s_comm_fmt_;
145
+ #else
146
+ gpio_num_t dout_pin_;
147
+ std::string i2s_comm_fmt_;
148
+ i2s_chan_handle_t tx_handle_;
149
+ #endif
137
150
 
138
151
  uint32_t accumulated_frames_written_{0};
139
152
  };
@@ -388,7 +388,7 @@ static const uint8_t PROGMEM INITCMD_GC9D01N[] = {
388
388
  0x8D, 1, 0xFF,
389
389
  0x8E, 1, 0xFF,
390
390
  0x8F, 1, 0xFF,
391
- 0X3A, 1, 0x05, // COLMOD: Pixel Format Set (3Ah) MCU interface, 16 bits / pixel
391
+ 0x3A, 1, 0x05, // COLMOD: Pixel Format Set (3Ah) MCU interface, 16 bits / pixel
392
392
  0xEC, 1, 0x01, // Inversion (ECh) DINV=1+2H1V column for Dual Gate (BFh=0)
393
393
  // According to datasheet Inversion (ECh) value 0x01 isn't valid, but Lilygo uses it everywhere
394
394
  0x74, 7, 0x02, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -286,11 +286,22 @@ CONF_TRANSPARENCY = "transparency"
286
286
  IMAGE_DOWNLOAD_TIMEOUT = 30 # seconds
287
287
 
288
288
  SOURCE_LOCAL = "local"
289
- SOURCE_MDI = "mdi"
290
289
  SOURCE_WEB = "web"
291
290
 
291
+ SOURCE_MDI = "mdi"
292
+ SOURCE_MDIL = "mdil"
293
+ SOURCE_MEMORY = "memory"
294
+
295
+ MDI_SOURCES = {
296
+ SOURCE_MDI: "https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg/",
297
+ SOURCE_MDIL: "https://raw.githubusercontent.com/Pictogrammers/MaterialDesignLight/refs/heads/master/svg/",
298
+ SOURCE_MEMORY: "https://raw.githubusercontent.com/Pictogrammers/Memory/refs/heads/main/src/svg/",
299
+ }
300
+
292
301
  Image_ = image_ns.class_("Image")
293
302
 
303
+ INSTANCE_TYPE = Image_
304
+
294
305
 
295
306
  def compute_local_image_path(value) -> Path:
296
307
  url = value[CONF_URL] if isinstance(value, dict) else value
@@ -311,12 +322,12 @@ def download_file(url, path):
311
322
  return str(path)
312
323
 
313
324
 
314
- def download_mdi(value):
325
+ def download_gh_svg(value, source):
315
326
  mdi_id = value[CONF_ICON] if isinstance(value, dict) else value
316
- base_dir = external_files.compute_local_file_dir(DOMAIN) / "mdi"
327
+ base_dir = external_files.compute_local_file_dir(DOMAIN) / source
317
328
  path = base_dir / f"{mdi_id}.svg"
318
329
 
319
- url = f"https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg/{mdi_id}.svg"
330
+ url = MDI_SOURCES[source] + mdi_id + ".svg"
320
331
  return download_file(url, path)
321
332
 
322
333
 
@@ -351,12 +362,12 @@ def validate_cairosvg_installed():
351
362
 
352
363
  def validate_file_shorthand(value):
353
364
  value = cv.string_strict(value)
354
- if value.startswith("mdi:"):
355
- match = re.search(r"mdi:([a-zA-Z0-9\-]+)", value)
365
+ parts = value.strip().split(":")
366
+ if len(parts) == 2 and parts[0] in MDI_SOURCES:
367
+ match = re.match(r"[a-zA-Z0-9\-]+", parts[1])
356
368
  if match is None:
357
- raise cv.Invalid("Could not parse mdi icon name.")
358
- icon = match.group(1)
359
- return download_mdi(icon)
369
+ raise cv.Invalid(f"Could not parse mdi icon name from '{value}'.")
370
+ return download_gh_svg(parts[1], parts[0])
360
371
 
361
372
  if value.startswith("http://") or value.startswith("https://"):
362
373
  return download_image(value)
@@ -372,12 +383,20 @@ LOCAL_SCHEMA = cv.All(
372
383
  local_path,
373
384
  )
374
385
 
375
- MDI_SCHEMA = cv.All(
376
- {
377
- cv.Required(CONF_ICON): cv.string,
378
- },
379
- download_mdi,
380
- )
386
+
387
+ def mdi_schema(source):
388
+ def validate_mdi(value):
389
+ return download_gh_svg(value, source)
390
+
391
+ return cv.All(
392
+ cv.Schema(
393
+ {
394
+ cv.Required(CONF_ICON): cv.string,
395
+ }
396
+ ),
397
+ validate_mdi,
398
+ )
399
+
381
400
 
382
401
  WEB_SCHEMA = cv.All(
383
402
  {
@@ -386,12 +405,13 @@ WEB_SCHEMA = cv.All(
386
405
  download_image,
387
406
  )
388
407
 
408
+
389
409
  TYPED_FILE_SCHEMA = cv.typed_schema(
390
410
  {
391
411
  SOURCE_LOCAL: LOCAL_SCHEMA,
392
- SOURCE_MDI: MDI_SCHEMA,
393
412
  SOURCE_WEB: WEB_SCHEMA,
394
- },
413
+ }
414
+ | {source: mdi_schema(source) for source in MDI_SOURCES},
395
415
  key=CONF_SOURCE,
396
416
  )
397
417
 
@@ -6,10 +6,27 @@ namespace esphome {
6
6
  namespace image {
7
7
 
8
8
  void Image::draw(int x, int y, display::Display *display, Color color_on, Color color_off) {
9
+ int img_x0 = 0;
10
+ int img_y0 = 0;
11
+ int w = width_;
12
+ int h = height_;
13
+
14
+ auto clipping = display->get_clipping();
15
+ if (clipping.is_set()) {
16
+ if (clipping.x > x)
17
+ img_x0 += clipping.x - x;
18
+ if (clipping.y > y)
19
+ img_y0 += clipping.y - y;
20
+ if (w > clipping.x2() - x)
21
+ w = clipping.x2() - x;
22
+ if (h > clipping.y2() - y)
23
+ h = clipping.y2() - y;
24
+ }
25
+
9
26
  switch (type_) {
10
27
  case IMAGE_TYPE_BINARY: {
11
- for (int img_x = 0; img_x < width_; img_x++) {
12
- for (int img_y = 0; img_y < height_; img_y++) {
28
+ for (int img_x = img_x0; img_x < w; img_x++) {
29
+ for (int img_y = img_y0; img_y < h; img_y++) {
13
30
  if (this->get_binary_pixel_(img_x, img_y)) {
14
31
  display->draw_pixel_at(x + img_x, y + img_y, color_on);
15
32
  } else if (!this->transparency_) {
@@ -20,8 +37,8 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color
20
37
  break;
21
38
  }
22
39
  case IMAGE_TYPE_GRAYSCALE:
23
- for (int img_x = 0; img_x < width_; img_x++) {
24
- for (int img_y = 0; img_y < height_; img_y++) {
40
+ for (int img_x = img_x0; img_x < w; img_x++) {
41
+ for (int img_y = img_y0; img_y < h; img_y++) {
25
42
  const uint32_t pos = (img_x + img_y * this->width_);
26
43
  const uint8_t gray = progmem_read_byte(this->data_start_ + pos);
27
44
  Color color = Color(gray, gray, gray, 0xFF);
@@ -47,8 +64,8 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color
47
64
  }
48
65
  break;
49
66
  case IMAGE_TYPE_RGB565:
50
- for (int img_x = 0; img_x < width_; img_x++) {
51
- for (int img_y = 0; img_y < height_; img_y++) {
67
+ for (int img_x = img_x0; img_x < w; img_x++) {
68
+ for (int img_y = img_y0; img_y < h; img_y++) {
52
69
  auto color = this->get_rgb565_pixel_(img_x, img_y);
53
70
  if (color.w >= 0x80) {
54
71
  display->draw_pixel_at(x + img_x, y + img_y, color);
@@ -57,8 +74,8 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color
57
74
  }
58
75
  break;
59
76
  case IMAGE_TYPE_RGB:
60
- for (int img_x = 0; img_x < width_; img_x++) {
61
- for (int img_y = 0; img_y < height_; img_y++) {
77
+ for (int img_x = img_x0; img_x < w; img_x++) {
78
+ for (int img_y = img_y0; img_y < h; img_y++) {
62
79
  auto color = this->get_rgb_pixel_(img_x, img_y);
63
80
  if (color.w >= 0x80) {
64
81
  display->draw_pixel_at(x + img_x, y + img_y, color);
@@ -9,7 +9,7 @@ uint8_t temprature_sens_read();
9
9
  }
10
10
  #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \
11
11
  defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \
12
- defined(USE_ESP32_VARIANT_ESP32C2)
12
+ defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32P4)
13
13
  #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
14
14
  #include "driver/temp_sensor.h"
15
15
  #else
@@ -33,7 +33,8 @@ static const char *const TAG = "internal_temperature";
33
33
  #ifdef USE_ESP32
34
34
  #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \
35
35
  (defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \
36
- defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2))
36
+ defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \
37
+ defined(USE_ESP32_VARIANT_ESP32P4))
37
38
  static temperature_sensor_handle_t tsensNew = NULL;
38
39
  #endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && USE_ESP32_VARIANT
39
40
  #endif // USE_ESP32
@@ -49,7 +50,7 @@ void InternalTemperatureSensor::update() {
49
50
  success = (raw != 128);
50
51
  #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \
51
52
  defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \
52
- defined(USE_ESP32_VARIANT_ESP32C2)
53
+ defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32P4)
53
54
  #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
54
55
  temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT();
55
56
  temp_sensor_set_config(tsens);
@@ -100,7 +101,8 @@ void InternalTemperatureSensor::setup() {
100
101
  #ifdef USE_ESP32
101
102
  #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \
102
103
  (defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \
103
- defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2))
104
+ defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \
105
+ defined(USE_ESP32_VARIANT_ESP32P4))
104
106
  ESP_LOGCONFIG(TAG, "Setting up temperature sensor...");
105
107
 
106
108
  temperature_sensor_config_t tsens_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
@@ -3,6 +3,7 @@ import esphome.codegen as cg
3
3
  from esphome.components import key_provider
4
4
  import esphome.config_validation as cv
5
5
  from esphome.const import (
6
+ CONF_ENABLE_ON_BOOT,
6
7
  CONF_ID,
7
8
  CONF_MAX_LENGTH,
8
9
  CONF_MIN_LENGTH,
@@ -28,6 +29,8 @@ CONF_ON_RESULT = "on_result"
28
29
 
29
30
  key_collector_ns = cg.esphome_ns.namespace("key_collector")
30
31
  KeyCollector = key_collector_ns.class_("KeyCollector", cg.Component)
32
+ EnableAction = key_collector_ns.class_("EnableAction", automation.Action)
33
+ DisableAction = key_collector_ns.class_("DisableAction", automation.Action)
31
34
 
32
35
  CONFIG_SCHEMA = cv.All(
33
36
  cv.COMPONENT_SCHEMA.extend(
@@ -46,6 +49,7 @@ CONFIG_SCHEMA = cv.All(
46
49
  cv.Optional(CONF_ON_RESULT): automation.validate_automation(single=True),
47
50
  cv.Optional(CONF_ON_TIMEOUT): automation.validate_automation(single=True),
48
51
  cv.Optional(CONF_TIMEOUT): cv.positive_time_period_milliseconds,
52
+ cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
49
53
  }
50
54
  ),
51
55
  cv.has_at_least_one_key(CONF_END_KEYS, CONF_MAX_LENGTH),
@@ -94,3 +98,34 @@ async def to_code(config):
94
98
  )
95
99
  if CONF_TIMEOUT in config:
96
100
  cg.add(var.set_timeout(config[CONF_TIMEOUT]))
101
+ cg.add(var.set_enabled(config[CONF_ENABLE_ON_BOOT]))
102
+
103
+
104
+ @automation.register_action(
105
+ "key_collector.enable",
106
+ EnableAction,
107
+ automation.maybe_simple_id(
108
+ {
109
+ cv.GenerateID(): cv.use_id(KeyCollector),
110
+ }
111
+ ),
112
+ )
113
+ async def enable_to_code(config, action_id, template_arg, args):
114
+ var = cg.new_Pvariable(action_id, template_arg)
115
+ await cg.register_parented(var, config[CONF_ID])
116
+ return var
117
+
118
+
119
+ @automation.register_action(
120
+ "key_collector.disable",
121
+ DisableAction,
122
+ automation.maybe_simple_id(
123
+ {
124
+ cv.GenerateID(): cv.use_id(KeyCollector),
125
+ }
126
+ ),
127
+ )
128
+ async def disable_to_code(config, action_id, template_arg, args):
129
+ var = cg.new_Pvariable(action_id, template_arg)
130
+ await cg.register_parented(var, config[CONF_ID])
131
+ return var
@@ -45,6 +45,12 @@ void KeyCollector::set_provider(key_provider::KeyProvider *provider) {
45
45
  provider->add_on_key_callback([this](uint8_t key) { this->key_pressed_(key); });
46
46
  }
47
47
 
48
+ void KeyCollector::set_enabled(bool enabled) {
49
+ this->enabled_ = enabled;
50
+ if (!enabled)
51
+ this->clear(false);
52
+ }
53
+
48
54
  void KeyCollector::clear(bool progress_update) {
49
55
  this->result_.clear();
50
56
  this->start_key_ = 0;
@@ -55,6 +61,8 @@ void KeyCollector::clear(bool progress_update) {
55
61
  void KeyCollector::send_key(uint8_t key) { this->key_pressed_(key); }
56
62
 
57
63
  void KeyCollector::key_pressed_(uint8_t key) {
64
+ if (!this->enabled_)
65
+ return;
58
66
  this->last_key_time_ = millis();
59
67
  if (!this->start_keys_.empty() && !this->start_key_) {
60
68
  if (this->start_keys_.find(key) != std::string::npos) {
@@ -25,6 +25,7 @@ class KeyCollector : public Component {
25
25
  Trigger<std::string, uint8_t, uint8_t> *get_result_trigger() const { return this->result_trigger_; };
26
26
  Trigger<std::string, uint8_t> *get_timeout_trigger() const { return this->timeout_trigger_; };
27
27
  void set_timeout(int timeout) { this->timeout_ = timeout; };
28
+ void set_enabled(bool enabled);
28
29
 
29
30
  void clear(bool progress_update = true);
30
31
  void send_key(uint8_t key);
@@ -47,6 +48,15 @@ class KeyCollector : public Component {
47
48
  Trigger<std::string, uint8_t> *timeout_trigger_;
48
49
  uint32_t last_key_time_;
49
50
  uint32_t timeout_{0};
51
+ bool enabled_;
52
+ };
53
+
54
+ template<typename... Ts> class EnableAction : public Action<Ts...>, public Parented<KeyCollector> {
55
+ void play(Ts... x) override { this->parent_->set_enabled(true); }
56
+ };
57
+
58
+ template<typename... Ts> class DisableAction : public Action<Ts...>, public Parented<KeyCollector> {
59
+ void play(Ts... x) override { this->parent_->set_enabled(false); }
50
60
  };
51
61
 
52
62
  } // namespace key_collector
@@ -1,5 +1,6 @@
1
1
  #include "kuntze.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
 
4
5
  namespace esphome {
5
6
  namespace kuntze {
@@ -60,7 +61,7 @@ void Kuntze::on_modbus_data(const std::vector<uint8_t> &data) {
60
61
  }
61
62
 
62
63
  void Kuntze::loop() {
63
- uint32_t now = millis();
64
+ uint32_t now = App.get_loop_component_start_time();
64
65
  // timeout after 15 seconds
65
66
  if (this->waiting_ && (now - this->last_send_ > 15000)) {
66
67
  ESP_LOGW(TAG, "timed out waiting for response");