esphome 2024.12.4__py3-none-any.whl → 2025.2.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 (355) hide show
  1. esphome/__main__.py +16 -3
  2. esphome/components/adc/__init__.py +17 -11
  3. esphome/components/adc/adc_sensor.h +17 -0
  4. esphome/components/adc/adc_sensor_common.cpp +55 -0
  5. esphome/components/adc/adc_sensor_esp32.cpp +8 -5
  6. esphome/components/adc/adc_sensor_esp8266.cpp +10 -6
  7. esphome/components/adc/adc_sensor_libretiny.cpp +11 -6
  8. esphome/components/adc/adc_sensor_rp2040.cpp +13 -10
  9. esphome/components/adc/sensor.py +9 -3
  10. esphome/components/ads1115/ads1115.cpp +56 -7
  11. esphome/components/ads1115/ads1115.h +13 -1
  12. esphome/components/ads1115/sensor/__init__.py +16 -0
  13. esphome/components/ads1115/sensor/ads1115_sensor.cpp +2 -1
  14. esphome/components/ads1115/sensor/ads1115_sensor.h +2 -0
  15. esphome/components/animation/__init__.py +23 -261
  16. esphome/components/animation/animation.cpp +2 -2
  17. esphome/components/animation/animation.h +2 -1
  18. esphome/components/api/api_pb2.cpp +14 -0
  19. esphome/components/api/api_pb2.h +1 -0
  20. esphome/components/audio/__init__.py +112 -0
  21. esphome/components/audio/audio.cpp +67 -0
  22. esphome/components/audio/audio.h +125 -7
  23. esphome/components/audio/audio_decoder.cpp +361 -0
  24. esphome/components/audio/audio_decoder.h +135 -0
  25. esphome/components/audio/audio_reader.cpp +308 -0
  26. esphome/components/audio/audio_reader.h +85 -0
  27. esphome/components/audio/audio_resampler.cpp +159 -0
  28. esphome/components/audio/audio_resampler.h +101 -0
  29. esphome/components/audio/audio_transfer_buffer.cpp +165 -0
  30. esphome/components/audio/audio_transfer_buffer.h +139 -0
  31. esphome/components/audio_adc/__init__.py +41 -0
  32. esphome/components/audio_adc/audio_adc.h +17 -0
  33. esphome/components/audio_adc/automation.h +23 -0
  34. esphome/components/bk72xx/__init__.py +1 -0
  35. esphome/components/ble_client/ble_client.cpp +1 -2
  36. esphome/components/ble_client/sensor/__init__.py +1 -1
  37. esphome/components/ble_client/text_sensor/__init__.py +1 -1
  38. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -0
  39. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -0
  40. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
  41. esphome/components/ch422g/ch422g.h +2 -0
  42. esphome/components/climate/__init__.py +1 -1
  43. esphome/components/climate_ir/climate_ir.cpp +2 -1
  44. esphome/components/coolix/coolix.cpp +2 -1
  45. esphome/components/custom/__init__.py +0 -3
  46. esphome/components/custom/binary_sensor/__init__.py +2 -28
  47. esphome/components/custom/climate/__init__.py +2 -27
  48. esphome/components/custom/cover/__init__.py +2 -27
  49. esphome/components/custom/light/__init__.py +2 -27
  50. esphome/components/custom/output/__init__.py +2 -58
  51. esphome/components/custom/sensor/__init__.py +2 -24
  52. esphome/components/custom/switch/__init__.py +2 -24
  53. esphome/components/custom/text_sensor/__init__.py +2 -29
  54. esphome/components/custom_component/__init__.py +3 -27
  55. esphome/components/daly_bms/daly_bms.cpp +6 -0
  56. esphome/components/daly_bms/daly_bms.h +2 -0
  57. esphome/components/daly_bms/sensor.py +6 -0
  58. esphome/components/debug/debug_component.cpp +4 -0
  59. esphome/components/debug/debug_component.h +14 -0
  60. esphome/components/debug/debug_esp32.cpp +154 -74
  61. esphome/components/dfplayer/dfplayer.cpp +15 -2
  62. esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +2 -1
  63. esphome/components/dht/dht.cpp +2 -1
  64. esphome/components/display/__init__.py +18 -5
  65. esphome/components/display/display.cpp +2 -1
  66. esphome/components/display/rect.cpp +2 -1
  67. esphome/components/es7210/__init__.py +0 -0
  68. esphome/components/es7210/audio_adc.py +51 -0
  69. esphome/components/es7210/es7210.cpp +228 -0
  70. esphome/components/es7210/es7210.h +62 -0
  71. esphome/components/es7210/es7210_const.h +129 -0
  72. esphome/components/es7243e/__init__.py +0 -0
  73. esphome/components/es7243e/audio_adc.py +34 -0
  74. esphome/components/es7243e/es7243e.cpp +125 -0
  75. esphome/components/es7243e/es7243e.h +37 -0
  76. esphome/components/es7243e/es7243e_const.h +54 -0
  77. esphome/components/es8156/__init__.py +0 -0
  78. esphome/components/es8156/audio_dac.py +27 -0
  79. esphome/components/es8156/es8156.cpp +87 -0
  80. esphome/components/es8156/es8156.h +51 -0
  81. esphome/components/es8156/es8156_const.h +68 -0
  82. esphome/components/es8311/audio_dac.py +1 -2
  83. esphome/components/esp32/__init__.py +1 -0
  84. esphome/components/esp32/core.cpp +5 -1
  85. esphome/components/esp32/gpio.h +2 -0
  86. esphome/components/esp32_ble/__init__.py +39 -0
  87. esphome/components/esp32_ble/queue.h +4 -4
  88. esphome/components/esp32_ble_client/ble_client_base.cpp +46 -0
  89. esphome/components/esp32_ble_client/ble_client_base.h +2 -0
  90. esphome/components/esp32_ble_server/__init__.py +582 -12
  91. esphome/components/esp32_ble_server/ble_characteristic.cpp +48 -60
  92. esphome/components/esp32_ble_server/ble_characteristic.h +24 -17
  93. esphome/components/esp32_ble_server/ble_descriptor.cpp +21 -9
  94. esphome/components/esp32_ble_server/ble_descriptor.h +17 -6
  95. esphome/components/esp32_ble_server/ble_server.cpp +62 -67
  96. esphome/components/esp32_ble_server/ble_server.h +28 -32
  97. esphome/components/esp32_ble_server/ble_server_automations.cpp +77 -0
  98. esphome/components/esp32_ble_server/ble_server_automations.h +115 -0
  99. esphome/components/esp32_ble_server/ble_service.cpp +17 -15
  100. esphome/components/esp32_ble_server/ble_service.h +10 -14
  101. esphome/components/esp32_ble_tracker/__init__.py +6 -39
  102. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +33 -10
  103. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +8 -4
  104. esphome/components/esp32_improv/__init__.py +2 -8
  105. esphome/components/esp32_improv/esp32_improv_component.cpp +21 -20
  106. esphome/components/esp32_improv/esp32_improv_component.h +3 -4
  107. esphome/components/esp32_rmt/__init__.py +28 -3
  108. esphome/components/esp32_rmt_led_strip/led_strip.cpp +73 -6
  109. esphome/components/esp32_rmt_led_strip/led_strip.h +21 -3
  110. esphome/components/esp32_rmt_led_strip/light.py +72 -7
  111. esphome/components/esp32_touch/esp32_touch.cpp +5 -0
  112. esphome/components/esp8266/__init__.py +1 -0
  113. esphome/components/esp8266/gpio.h +1 -0
  114. esphome/components/ethernet/__init__.py +10 -10
  115. esphome/components/event/event.cpp +4 -2
  116. esphome/components/event/event.h +2 -0
  117. esphome/components/event_emitter/__init__.py +5 -0
  118. esphome/components/event_emitter/event_emitter.cpp +14 -0
  119. esphome/components/event_emitter/event_emitter.h +63 -0
  120. esphome/components/gcja5/gcja5.cpp +2 -1
  121. esphome/components/haier/haier_base.cpp +2 -1
  122. esphome/components/haier/hon_climate.cpp +2 -1
  123. esphome/components/heatpumpir/heatpumpir.cpp +2 -1
  124. esphome/components/host/__init__.py +1 -0
  125. esphome/components/host/gpio.h +1 -0
  126. esphome/components/http_request/http_request.h +2 -2
  127. esphome/components/http_request/http_request_arduino.cpp +1 -1
  128. esphome/components/http_request/http_request_idf.cpp +1 -1
  129. esphome/components/i2c/i2c_bus_esp_idf.cpp +4 -0
  130. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +7 -5
  131. esphome/components/i2s_audio/speaker/__init__.py +53 -6
  132. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +92 -46
  133. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -0
  134. esphome/components/ili9xxx/display.py +29 -11
  135. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -5
  136. esphome/components/ili9xxx/ili9xxx_display.h +2 -1
  137. esphome/components/image/__init__.py +443 -255
  138. esphome/components/image/image.cpp +115 -61
  139. esphome/components/image/image.h +15 -24
  140. esphome/components/json/json_util.cpp +8 -34
  141. esphome/components/libretiny/__init__.py +1 -0
  142. esphome/components/libretiny/gpio_arduino.h +1 -0
  143. esphome/components/light/light_color_values.h +1 -1
  144. esphome/components/logger/__init__.py +43 -7
  145. esphome/components/logger/logger.cpp +16 -11
  146. esphome/components/logger/logger.h +11 -7
  147. esphome/components/logger/select/__init__.py +29 -0
  148. esphome/components/logger/select/logger_level_select.cpp +27 -0
  149. esphome/components/logger/select/logger_level_select.h +15 -0
  150. esphome/components/lvgl/__init__.py +96 -73
  151. esphome/components/lvgl/automation.py +39 -7
  152. esphome/components/lvgl/defines.py +8 -2
  153. esphome/components/lvgl/lvgl_esphome.cpp +8 -15
  154. esphome/components/lvgl/lvgl_esphome.h +20 -5
  155. esphome/components/lvgl/schemas.py +25 -14
  156. esphome/components/lvgl/trigger.py +27 -3
  157. esphome/components/lvgl/widgets/dropdown.py +1 -1
  158. esphome/components/lvgl/widgets/keyboard.py +8 -1
  159. esphome/components/lvgl/widgets/meter.py +2 -1
  160. esphome/components/lvgl/widgets/msgbox.py +1 -1
  161. esphome/components/lvgl/widgets/obj.py +1 -12
  162. esphome/components/lvgl/widgets/page.py +37 -2
  163. esphome/components/lvgl/widgets/tabview.py +1 -1
  164. esphome/components/max6956/max6956.h +2 -0
  165. esphome/components/mcp23016/mcp23016.h +2 -0
  166. esphome/components/mcp23xxx_base/mcp23xxx_base.h +2 -0
  167. esphome/components/mdns/__init__.py +1 -1
  168. esphome/components/media_player/__init__.py +37 -8
  169. esphome/components/media_player/automation.h +11 -2
  170. esphome/components/media_player/media_player.cpp +8 -0
  171. esphome/components/media_player/media_player.h +8 -4
  172. esphome/components/micronova/switch/micronova_switch.cpp +4 -2
  173. esphome/components/midea/ac_automations.h +3 -1
  174. esphome/components/midea/air_conditioner.cpp +7 -5
  175. esphome/components/midea/air_conditioner.h +1 -1
  176. esphome/components/midea/climate.py +4 -2
  177. esphome/components/midea/ir_transmitter.h +36 -5
  178. esphome/components/mixer/__init__.py +0 -0
  179. esphome/components/mixer/speaker/__init__.py +172 -0
  180. esphome/components/mixer/speaker/automation.h +19 -0
  181. esphome/components/mixer/speaker/mixer_speaker.cpp +624 -0
  182. esphome/components/mixer/speaker/mixer_speaker.h +207 -0
  183. esphome/components/mpr121/mpr121.h +2 -0
  184. esphome/components/mqtt/__init__.py +1 -1
  185. esphome/components/mqtt/mqtt_client.cpp +7 -1
  186. esphome/components/mqtt/mqtt_client.h +1 -1
  187. esphome/components/mqtt/mqtt_climate.cpp +2 -2
  188. esphome/components/network/ip_address.h +2 -0
  189. esphome/components/nextion/automation.h +17 -0
  190. esphome/components/nextion/display.py +42 -17
  191. esphome/components/nextion/nextion.cpp +4 -10
  192. esphome/components/nextion/nextion.h +89 -82
  193. esphome/components/nextion/nextion_commands.cpp +10 -10
  194. esphome/components/ntc/sensor.py +2 -4
  195. esphome/components/online_image/__init__.py +98 -46
  196. esphome/components/online_image/bmp_image.cpp +101 -0
  197. esphome/components/online_image/bmp_image.h +40 -0
  198. esphome/components/online_image/image_decoder.cpp +28 -2
  199. esphome/components/online_image/image_decoder.h +24 -15
  200. esphome/components/online_image/jpeg_image.cpp +90 -0
  201. esphome/components/online_image/jpeg_image.h +34 -0
  202. esphome/components/online_image/online_image.cpp +112 -53
  203. esphome/components/online_image/online_image.h +24 -7
  204. esphome/components/online_image/png_image.cpp +7 -3
  205. esphome/components/online_image/png_image.h +2 -1
  206. esphome/components/opentherm/__init__.py +73 -7
  207. esphome/components/opentherm/automation.h +25 -0
  208. esphome/components/opentherm/const.py +1 -0
  209. esphome/components/opentherm/generate.py +39 -6
  210. esphome/components/opentherm/hub.cpp +117 -79
  211. esphome/components/opentherm/hub.h +31 -15
  212. esphome/components/opentherm/opentherm.cpp +47 -23
  213. esphome/components/opentherm/opentherm.h +27 -6
  214. esphome/components/opentherm/opentherm_macros.h +11 -0
  215. esphome/components/opentherm/schema.py +78 -1
  216. esphome/components/opentherm/validate.py +7 -2
  217. esphome/components/pca6416a/pca6416a.h +2 -0
  218. esphome/components/pca9554/pca9554.h +2 -0
  219. esphome/components/pcf8574/pcf8574.h +2 -0
  220. esphome/components/preferences/__init__.py +2 -4
  221. esphome/components/preferences/syncer.h +10 -3
  222. esphome/components/prometheus/prometheus_handler.cpp +313 -0
  223. esphome/components/prometheus/prometheus_handler.h +48 -7
  224. esphome/components/psram/psram.cpp +8 -1
  225. esphome/components/pulse_counter/pulse_counter_sensor.cpp +14 -9
  226. esphome/components/pulse_counter/pulse_counter_sensor.h +4 -4
  227. esphome/components/pulse_meter/pulse_meter_sensor.cpp +2 -0
  228. esphome/components/qspi_dbi/__init__.py +3 -0
  229. esphome/components/qspi_dbi/display.py +74 -47
  230. esphome/components/qspi_dbi/models.py +245 -2
  231. esphome/components/qspi_dbi/qspi_dbi.cpp +9 -16
  232. esphome/components/qspi_dbi/qspi_dbi.h +2 -2
  233. esphome/components/remote_base/__init__.py +77 -25
  234. esphome/components/remote_base/remote_base.cpp +1 -1
  235. esphome/components/remote_base/remote_base.h +20 -2
  236. esphome/components/remote_base/toto_protocol.cpp +100 -0
  237. esphome/components/remote_base/toto_protocol.h +45 -0
  238. esphome/components/remote_receiver/__init__.py +55 -10
  239. esphome/components/remote_receiver/remote_receiver.h +36 -3
  240. esphome/components/remote_receiver/remote_receiver_esp32.cpp +145 -6
  241. esphome/components/remote_transmitter/__init__.py +62 -4
  242. esphome/components/remote_transmitter/remote_transmitter.h +21 -2
  243. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +140 -4
  244. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +3 -3
  245. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +3 -3
  246. esphome/components/resampler/__init__.py +0 -0
  247. esphome/components/resampler/speaker/__init__.py +103 -0
  248. esphome/components/resampler/speaker/resampler_speaker.cpp +318 -0
  249. esphome/components/resampler/speaker/resampler_speaker.h +107 -0
  250. esphome/components/resistance/resistance_sensor.h +2 -3
  251. esphome/components/resistance/sensor.py +2 -9
  252. esphome/components/rotary_encoder/rotary_encoder.cpp +8 -4
  253. esphome/components/rp2040/__init__.py +1 -0
  254. esphome/components/rp2040/gpio.h +1 -0
  255. esphome/components/rtl87xx/__init__.py +2 -0
  256. esphome/components/sdl/binary_sensor.py +270 -0
  257. esphome/components/sdl/sdl_esphome.cpp +16 -0
  258. esphome/components/sdl/sdl_esphome.h +9 -0
  259. esphome/components/seeed_mr60bha2/binary_sensor.py +25 -0
  260. esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +26 -2
  261. esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +9 -20
  262. esphome/components/seeed_mr60bha2/sensor.py +9 -1
  263. esphome/components/sn74hc165/sn74hc165.h +3 -0
  264. esphome/components/sn74hc595/sn74hc595.h +3 -0
  265. esphome/components/speaker/__init__.py +5 -4
  266. esphome/components/speaker/media_player/__init__.py +458 -0
  267. esphome/components/speaker/media_player/audio_pipeline.cpp +568 -0
  268. esphome/components/speaker/media_player/audio_pipeline.h +159 -0
  269. esphome/components/speaker/media_player/automation.h +26 -0
  270. esphome/components/speaker/media_player/speaker_media_player.cpp +577 -0
  271. esphome/components/speaker/media_player/speaker_media_player.h +160 -0
  272. esphome/components/speaker/speaker.h +20 -0
  273. esphome/components/spi/__init__.py +1 -5
  274. esphome/components/spi/spi.cpp +7 -1
  275. esphome/components/spi/spi.h +21 -2
  276. esphome/components/spi_led_strip/light.py +3 -5
  277. esphome/components/spi_led_strip/spi_led_strip.cpp +67 -0
  278. esphome/components/spi_led_strip/spi_led_strip.h +8 -60
  279. esphome/components/sprinkler/sprinkler.cpp +3 -1
  280. esphome/components/sx1509/sx1509_gpio_pin.h +2 -0
  281. esphome/components/tca9555/tca9555.h +2 -0
  282. esphome/components/toshiba/toshiba.cpp +2 -1
  283. esphome/components/tuya/light/tuya_light.cpp +4 -2
  284. esphome/components/uart/uart_component_esp32_arduino.cpp +2 -2
  285. esphome/components/uart/uart_component_esp_idf.cpp +2 -2
  286. esphome/components/udp/__init__.py +8 -2
  287. esphome/components/udp/udp_component.cpp +25 -56
  288. esphome/components/udp/udp_component.h +3 -0
  289. esphome/components/uponor_smatrix/sensor/__init__.py +14 -4
  290. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +5 -0
  291. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +1 -0
  292. esphome/components/uptime/text_sensor/__init__.py +19 -0
  293. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +63 -0
  294. esphome/components/uptime/text_sensor/uptime_text_sensor.h +25 -0
  295. esphome/components/voice_assistant/voice_assistant.cpp +24 -14
  296. esphome/components/voice_assistant/voice_assistant.h +8 -0
  297. esphome/components/waveshare_epaper/display.py +22 -1
  298. esphome/components/waveshare_epaper/waveshare_213v3.cpp +9 -3
  299. esphome/components/waveshare_epaper/waveshare_epaper.cpp +1155 -44
  300. esphome/components/waveshare_epaper/waveshare_epaper.h +208 -7
  301. esphome/components/web_server/web_server.cpp +28 -6
  302. esphome/components/weikai/weikai.h +2 -0
  303. esphome/components/wifi/__init__.py +6 -6
  304. esphome/components/wifi/wifi_component.cpp +1 -1
  305. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -1
  306. esphome/components/wireguard/__init__.py +2 -2
  307. esphome/components/xl9535/xl9535.h +2 -0
  308. esphome/components/xxtea/__init__.py +3 -0
  309. esphome/components/xxtea/xxtea.cpp +46 -0
  310. esphome/components/xxtea/xxtea.h +26 -0
  311. esphome/components/yashima/yashima.cpp +2 -1
  312. esphome/config.py +9 -5
  313. esphome/config_validation.py +55 -17
  314. esphome/const.py +7 -10
  315. esphome/core/__init__.py +6 -13
  316. esphome/core/base_automation.h +1 -0
  317. esphome/core/config.py +57 -72
  318. esphome/core/defines.h +9 -1
  319. esphome/core/gpio.h +7 -0
  320. esphome/core/helpers.cpp +19 -15
  321. esphome/core/helpers.h +57 -8
  322. esphome/core/log.h +9 -7
  323. esphome/cpp_generator.py +2 -2
  324. esphome/espota2.py +3 -2
  325. esphome/loader.py +12 -4
  326. esphome/log.py +5 -7
  327. esphome/yaml_util.py +2 -2
  328. {esphome-2024.12.4.dist-info → esphome-2025.2.0b1.dist-info}/METADATA +12 -7
  329. {esphome-2024.12.4.dist-info → esphome-2025.2.0b1.dist-info}/RECORD +338 -289
  330. esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +0 -16
  331. esphome/components/custom/binary_sensor/custom_binary_sensor.h +0 -26
  332. esphome/components/custom/climate/custom_climate.h +0 -22
  333. esphome/components/custom/cover/custom_cover.h +0 -21
  334. esphome/components/custom/light/custom_light_output.h +0 -24
  335. esphome/components/custom/output/custom_output.h +0 -37
  336. esphome/components/custom/sensor/custom_sensor.cpp +0 -16
  337. esphome/components/custom/sensor/custom_sensor.h +0 -24
  338. esphome/components/custom/switch/custom_switch.cpp +0 -16
  339. esphome/components/custom/switch/custom_switch.h +0 -24
  340. esphome/components/custom/text_sensor/custom_text_sensor.cpp +0 -16
  341. esphome/components/custom/text_sensor/custom_text_sensor.h +0 -26
  342. esphome/components/custom_component/custom_component.h +0 -28
  343. esphome/components/esp32_ble_server/ble_2901.cpp +0 -18
  344. esphome/components/esp32_ble_server/ble_2901.h +0 -19
  345. esphome/components/resistance_sampler/__init__.py +0 -6
  346. esphome/components/resistance_sampler/resistance_sampler.h +0 -10
  347. esphome/components/uptime/{sensor.py → sensor/__init__.py} +3 -3
  348. /esphome/components/uptime/{uptime_seconds_sensor.cpp → sensor/uptime_seconds_sensor.cpp} +0 -0
  349. /esphome/components/uptime/{uptime_seconds_sensor.h → sensor/uptime_seconds_sensor.h} +0 -0
  350. /esphome/components/uptime/{uptime_timestamp_sensor.cpp → sensor/uptime_timestamp_sensor.cpp} +0 -0
  351. /esphome/components/uptime/{uptime_timestamp_sensor.h → sensor/uptime_timestamp_sensor.h} +0 -0
  352. {esphome-2024.12.4.dist-info → esphome-2025.2.0b1.dist-info}/LICENSE +0 -0
  353. {esphome-2024.12.4.dist-info → esphome-2025.2.0b1.dist-info}/WHEEL +0 -0
  354. {esphome-2024.12.4.dist-info → esphome-2025.2.0b1.dist-info}/entry_points.txt +0 -0
  355. {esphome-2024.12.4.dist-info → esphome-2025.2.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+
3
+ #include "image_decoder.h"
4
+ #include "esphome/core/defines.h"
5
+ #ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT
6
+ #include <JPEGDEC.h>
7
+
8
+ namespace esphome {
9
+ namespace online_image {
10
+
11
+ /**
12
+ * @brief Image decoder specialization for JPEG images.
13
+ */
14
+ class JpegDecoder : public ImageDecoder {
15
+ public:
16
+ /**
17
+ * @brief Construct a new JPEG Decoder object.
18
+ *
19
+ * @param display The image to decode the stream into.
20
+ */
21
+ JpegDecoder(OnlineImage *image) : ImageDecoder(image) {}
22
+ ~JpegDecoder() override {}
23
+
24
+ int prepare(size_t download_size) override;
25
+ int HOT decode(uint8_t *buffer, size_t size) override;
26
+
27
+ protected:
28
+ JPEGDEC jpeg_{};
29
+ };
30
+
31
+ } // namespace online_image
32
+ } // namespace esphome
33
+
34
+ #endif // USE_ONLINE_IMAGE_JPEG_SUPPORT
@@ -6,6 +6,12 @@ static const char *const TAG = "online_image";
6
6
 
7
7
  #include "image_decoder.h"
8
8
 
9
+ #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT
10
+ #include "bmp_image.h"
11
+ #endif
12
+ #ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT
13
+ #include "jpeg_image.h"
14
+ #endif
9
15
  #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
10
16
  #include "png_image.h"
11
17
  #endif
@@ -25,10 +31,11 @@ inline bool is_color_on(const Color &color) {
25
31
  }
26
32
 
27
33
  OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type,
28
- uint32_t download_buffer_size)
29
- : Image(nullptr, 0, 0, type),
34
+ image::Transparency transparency, uint32_t download_buffer_size)
35
+ : Image(nullptr, 0, 0, type, transparency),
30
36
  buffer_(nullptr),
31
37
  download_buffer_(download_buffer_size),
38
+ download_buffer_initial_size_(download_buffer_size),
32
39
  format_(format),
33
40
  fixed_width_(width),
34
41
  fixed_height_(height) {
@@ -45,7 +52,7 @@ void OnlineImage::draw(int x, int y, display::Display *display, Color color_on,
45
52
 
46
53
  void OnlineImage::release() {
47
54
  if (this->buffer_) {
48
- ESP_LOGD(TAG, "Deallocating old buffer...");
55
+ ESP_LOGV(TAG, "Deallocating old buffer...");
49
56
  this->allocator_.deallocate(this->buffer_, this->get_buffer_size_());
50
57
  this->data_start_ = nullptr;
51
58
  this->buffer_ = nullptr;
@@ -70,28 +77,19 @@ bool OnlineImage::resize_(int width_in, int height_in) {
70
77
  if (this->buffer_) {
71
78
  return false;
72
79
  }
73
- auto new_size = this->get_buffer_size_(width, height);
74
- ESP_LOGD(TAG, "Allocating new buffer of %d Bytes...", new_size);
75
- delay_microseconds_safe(2000);
80
+ size_t new_size = this->get_buffer_size_(width, height);
81
+ ESP_LOGD(TAG, "Allocating new buffer of %zu bytes", new_size);
76
82
  this->buffer_ = this->allocator_.allocate(new_size);
77
- if (this->buffer_) {
78
- this->buffer_width_ = width;
79
- this->buffer_height_ = height;
80
- this->width_ = width;
81
- ESP_LOGD(TAG, "New size: (%d, %d)", width, height);
82
- } else {
83
- #if defined(USE_ESP8266)
84
- // NOLINTNEXTLINE(readability-static-accessed-through-instance)
85
- int max_block = ESP.getMaxFreeBlockSize();
86
- #elif defined(USE_ESP32)
87
- int max_block = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL);
88
- #else
89
- int max_block = -1;
90
- #endif
91
- ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %d Bytes", max_block);
83
+ if (this->buffer_ == nullptr) {
84
+ ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", new_size,
85
+ this->allocator_.get_max_free_block_size());
92
86
  this->end_connection_();
93
87
  return false;
94
88
  }
89
+ this->buffer_width_ = width;
90
+ this->buffer_height_ = height;
91
+ this->width_ = width;
92
+ ESP_LOGV(TAG, "New size: (%d, %d)", width, height);
95
93
  return true;
96
94
  }
97
95
 
@@ -99,11 +97,38 @@ void OnlineImage::update() {
99
97
  if (this->decoder_) {
100
98
  ESP_LOGW(TAG, "Image already being updated.");
101
99
  return;
102
- } else {
103
- ESP_LOGI(TAG, "Updating image");
104
100
  }
101
+ ESP_LOGI(TAG, "Updating image %s", this->url_.c_str());
105
102
 
106
- this->downloader_ = this->parent_->get(this->url_);
103
+ std::list<http_request::Header> headers = {};
104
+
105
+ http_request::Header accept_header;
106
+ accept_header.name = "Accept";
107
+ std::string accept_mime_type;
108
+ switch (this->format_) {
109
+ #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT
110
+ case ImageFormat::BMP:
111
+ accept_mime_type = "image/bmp";
112
+ break;
113
+ #endif // ONLINE_IMAGE_BMP_SUPPORT
114
+ #ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT
115
+ case ImageFormat::JPEG:
116
+ accept_mime_type = "image/jpeg";
117
+ break;
118
+ #endif // USE_ONLINE_IMAGE_JPEG_SUPPORT
119
+ #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
120
+ case ImageFormat::PNG:
121
+ accept_mime_type = "image/png";
122
+ break;
123
+ #endif // ONLINE_IMAGE_PNG_SUPPORT
124
+ default:
125
+ accept_mime_type = "image/*";
126
+ }
127
+ accept_header.value = accept_mime_type + ",*/*;q=0.8";
128
+
129
+ headers.push_back(accept_header);
130
+
131
+ this->downloader_ = this->parent_->get(this->url_, headers);
107
132
 
108
133
  if (this->downloader_ == nullptr) {
109
134
  ESP_LOGE(TAG, "Download failed.");
@@ -128,20 +153,39 @@ void OnlineImage::update() {
128
153
  ESP_LOGD(TAG, "Starting download");
129
154
  size_t total_size = this->downloader_->content_length;
130
155
 
156
+ #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT
157
+ if (this->format_ == ImageFormat::BMP) {
158
+ ESP_LOGD(TAG, "Allocating BMP decoder");
159
+ this->decoder_ = make_unique<BmpDecoder>(this);
160
+ }
161
+ #endif // ONLINE_IMAGE_BMP_SUPPORT
162
+ #ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT
163
+ if (this->format_ == ImageFormat::JPEG) {
164
+ ESP_LOGD(TAG, "Allocating JPEG decoder");
165
+ this->decoder_ = esphome::make_unique<JpegDecoder>(this);
166
+ }
167
+ #endif // USE_ONLINE_IMAGE_JPEG_SUPPORT
131
168
  #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
132
169
  if (this->format_ == ImageFormat::PNG) {
133
- this->decoder_ = esphome::make_unique<PngDecoder>(this);
170
+ ESP_LOGD(TAG, "Allocating PNG decoder");
171
+ this->decoder_ = make_unique<PngDecoder>(this);
134
172
  }
135
173
  #endif // ONLINE_IMAGE_PNG_SUPPORT
136
174
 
137
175
  if (!this->decoder_) {
138
- ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported.");
176
+ ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported: %d", this->format_);
139
177
  this->end_connection_();
140
178
  this->download_error_callback_.call();
141
179
  return;
142
180
  }
143
- this->decoder_->prepare(total_size);
144
- ESP_LOGI(TAG, "Downloading image");
181
+ auto prepare_result = this->decoder_->prepare(total_size);
182
+ if (prepare_result < 0) {
183
+ this->end_connection_();
184
+ this->download_error_callback_.call();
185
+ return;
186
+ }
187
+ ESP_LOGI(TAG, "Downloading image (Size: %d)", total_size);
188
+ this->start_time_ = ::time(nullptr);
145
189
  }
146
190
 
147
191
  void OnlineImage::loop() {
@@ -150,10 +194,12 @@ void OnlineImage::loop() {
150
194
  return;
151
195
  }
152
196
  if (!this->downloader_ || this->decoder_->is_finished()) {
153
- ESP_LOGD(TAG, "Image fully downloaded");
154
197
  this->data_start_ = buffer_;
155
198
  this->width_ = buffer_width_;
156
199
  this->height_ = buffer_height_;
200
+ ESP_LOGD(TAG, "Image fully downloaded, read %zu bytes, width/height = %d/%d", this->downloader_->get_bytes_read(),
201
+ this->width_, this->height_);
202
+ ESP_LOGD(TAG, "Total time: %lds", ::time(nullptr) - this->start_time_);
157
203
  this->end_connection_();
158
204
  this->download_finished_callback_.call();
159
205
  return;
@@ -164,6 +210,10 @@ void OnlineImage::loop() {
164
210
  }
165
211
  size_t available = this->download_buffer_.free_capacity();
166
212
  if (available) {
213
+ // Some decoders need to fully download the image before downloading.
214
+ // In case of huge images, don't wait blocking until the whole image has been downloaded,
215
+ // use smaller chunks
216
+ available = std::min(available, this->download_buffer_initial_size_);
167
217
  auto len = this->downloader_->read(this->download_buffer_.append(), available);
168
218
  if (len > 0) {
169
219
  this->download_buffer_.write(len);
@@ -179,6 +229,19 @@ void OnlineImage::loop() {
179
229
  }
180
230
  }
181
231
 
232
+ void OnlineImage::map_chroma_key(Color &color) {
233
+ if (this->transparency_ == image::TRANSPARENCY_CHROMA_KEY) {
234
+ if (color.g == 1 && color.r == 0 && color.b == 0) {
235
+ color.g = 0;
236
+ }
237
+ if (color.w < 0x80) {
238
+ color.r = 0;
239
+ color.g = this->type_ == ImageType::IMAGE_TYPE_RGB565 ? 4 : 1;
240
+ color.b = 0;
241
+ }
242
+ }
243
+ }
244
+
182
245
  void OnlineImage::draw_pixel_(int x, int y, Color color) {
183
246
  if (!this->buffer_) {
184
247
  ESP_LOGE(TAG, "Buffer not allocated!");
@@ -192,57 +255,53 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) {
192
255
  switch (this->type_) {
193
256
  case ImageType::IMAGE_TYPE_BINARY: {
194
257
  const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
195
- const uint32_t pos = x + y * width_8;
196
- if ((this->has_transparency() && color.w > 127) || is_color_on(color)) {
197
- this->buffer_[pos / 8u] |= (0x80 >> (pos % 8u));
258
+ pos = x + y * width_8;
259
+ auto bitno = 0x80 >> (pos % 8u);
260
+ pos /= 8u;
261
+ auto on = is_color_on(color);
262
+ if (this->has_transparency() && color.w < 0x80)
263
+ on = false;
264
+ if (on) {
265
+ this->buffer_[pos] |= bitno;
198
266
  } else {
199
- this->buffer_[pos / 8u] &= ~(0x80 >> (pos % 8u));
267
+ this->buffer_[pos] &= ~bitno;
200
268
  }
201
269
  break;
202
270
  }
203
271
  case ImageType::IMAGE_TYPE_GRAYSCALE: {
204
272
  uint8_t gray = static_cast<uint8_t>(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b);
205
- if (this->has_transparency()) {
273
+ if (this->transparency_ == image::TRANSPARENCY_CHROMA_KEY) {
206
274
  if (gray == 1) {
207
275
  gray = 0;
208
276
  }
209
277
  if (color.w < 0x80) {
210
278
  gray = 1;
211
279
  }
280
+ } else if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) {
281
+ if (color.w != 0xFF)
282
+ gray = color.w;
212
283
  }
213
284
  this->buffer_[pos] = gray;
214
285
  break;
215
286
  }
216
287
  case ImageType::IMAGE_TYPE_RGB565: {
288
+ this->map_chroma_key(color);
217
289
  uint16_t col565 = display::ColorUtil::color_to_565(color);
218
290
  this->buffer_[pos + 0] = static_cast<uint8_t>((col565 >> 8) & 0xFF);
219
291
  this->buffer_[pos + 1] = static_cast<uint8_t>(col565 & 0xFF);
220
- if (this->has_transparency())
292
+ if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) {
221
293
  this->buffer_[pos + 2] = color.w;
294
+ }
222
295
  break;
223
296
  }
224
- case ImageType::IMAGE_TYPE_RGBA: {
297
+ case ImageType::IMAGE_TYPE_RGB: {
298
+ this->map_chroma_key(color);
225
299
  this->buffer_[pos + 0] = color.r;
226
300
  this->buffer_[pos + 1] = color.g;
227
301
  this->buffer_[pos + 2] = color.b;
228
- this->buffer_[pos + 3] = color.w;
229
- break;
230
- }
231
- case ImageType::IMAGE_TYPE_RGB24:
232
- default: {
233
- if (this->has_transparency()) {
234
- if (color.b == 1 && color.r == 0 && color.g == 0) {
235
- color.b = 0;
236
- }
237
- if (color.w < 0x80) {
238
- color.r = 0;
239
- color.g = 0;
240
- color.b = 1;
241
- }
302
+ if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) {
303
+ this->buffer_[pos + 3] = color.w;
242
304
  }
243
- this->buffer_[pos + 0] = color.r;
244
- this->buffer_[pos + 1] = color.g;
245
- this->buffer_[pos + 2] = color.b;
246
305
  break;
247
306
  }
248
307
  }
@@ -1,10 +1,10 @@
1
1
  #pragma once
2
2
 
3
+ #include "esphome/components/http_request/http_request.h"
4
+ #include "esphome/components/image/image.h"
3
5
  #include "esphome/core/component.h"
4
6
  #include "esphome/core/defines.h"
5
7
  #include "esphome/core/helpers.h"
6
- #include "esphome/components/http_request/http_request.h"
7
- #include "esphome/components/image/image.h"
8
8
 
9
9
  #include "image_decoder.h"
10
10
 
@@ -23,10 +23,12 @@ using t_http_codes = enum {
23
23
  enum ImageFormat {
24
24
  /** Automatically detect from MIME type. Not supported yet. */
25
25
  AUTO,
26
- /** JPEG format. Not supported yet. */
26
+ /** JPEG format. */
27
27
  JPEG,
28
28
  /** PNG format. */
29
29
  PNG,
30
+ /** BMP format. */
31
+ BMP,
30
32
  };
31
33
 
32
34
  /**
@@ -48,12 +50,13 @@ class OnlineImage : public PollingComponent,
48
50
  * @param buffer_size Size of the buffer used to download the image.
49
51
  */
50
52
  OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type,
51
- uint32_t buffer_size);
53
+ image::Transparency transparency, uint32_t buffer_size);
52
54
 
53
55
  void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override;
54
56
 
55
57
  void update() override;
56
58
  void loop() override;
59
+ void map_chroma_key(Color &color);
57
60
 
58
61
  /** Set the URL to download the image from. */
59
62
  void set_url(const std::string &url) {
@@ -76,14 +79,20 @@ class OnlineImage : public PollingComponent,
76
79
  */
77
80
  void release();
78
81
 
82
+ /**
83
+ * Resize the download buffer
84
+ *
85
+ * @param size The new size for the download buffer.
86
+ */
87
+ size_t resize_download_buffer(size_t size) { return this->download_buffer_.resize(size); }
88
+
79
89
  void add_on_finished_callback(std::function<void()> &&callback);
80
90
  void add_on_error_callback(std::function<void()> &&callback);
81
91
 
82
92
  protected:
83
93
  bool validate_url_(const std::string &url);
84
94
 
85
- using Allocator = ExternalRAMAllocator<uint8_t>;
86
- Allocator allocator_{Allocator::Flags::ALLOW_FAILURE};
95
+ RAMAllocator<uint8_t> allocator_{};
87
96
 
88
97
  uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); }
89
98
  int get_buffer_size_(int width, int height) const { return (this->get_bpp() * width + 7u) / 8u * height; }
@@ -117,6 +126,12 @@ class OnlineImage : public PollingComponent,
117
126
 
118
127
  uint8_t *buffer_;
119
128
  DownloadBuffer download_buffer_;
129
+ /**
130
+ * This is the *initial* size of the download buffer, not the current size.
131
+ * The download buffer can be resized at runtime; the download_buffer_initial_size_
132
+ * will *not* change even if the download buffer has been resized.
133
+ */
134
+ size_t download_buffer_initial_size_;
120
135
 
121
136
  const ImageFormat format_;
122
137
  image::Image *placeholder_{nullptr};
@@ -146,7 +161,9 @@ class OnlineImage : public PollingComponent,
146
161
  */
147
162
  int buffer_height_;
148
163
 
149
- friend void ImageDecoder::set_size(int width, int height);
164
+ time_t start_time_;
165
+
166
+ friend bool ImageDecoder::set_size(int width, int height);
150
167
  friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color);
151
168
  };
152
169
 
@@ -2,7 +2,6 @@
2
2
  #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
3
3
 
4
4
  #include "esphome/components/display/display_buffer.h"
5
- #include "esphome/core/application.h"
6
5
  #include "esphome/core/helpers.h"
7
6
  #include "esphome/core/log.h"
8
7
 
@@ -41,17 +40,22 @@ static void draw_callback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, ui
41
40
  decoder->draw(x, y, w, h, color);
42
41
  }
43
42
 
44
- void PngDecoder::prepare(uint32_t download_size) {
43
+ int PngDecoder::prepare(size_t download_size) {
45
44
  ImageDecoder::prepare(download_size);
45
+ if (!this->pngle_) {
46
+ ESP_LOGE(TAG, "PNG decoder engine not initialized!");
47
+ return DECODE_ERROR_OUT_OF_MEMORY;
48
+ }
46
49
  pngle_set_user_data(this->pngle_, this);
47
50
  pngle_set_init_callback(this->pngle_, init_callback);
48
51
  pngle_set_draw_callback(this->pngle_, draw_callback);
52
+ return 0;
49
53
  }
50
54
 
51
55
  int HOT PngDecoder::decode(uint8_t *buffer, size_t size) {
52
56
  if (!this->pngle_) {
53
57
  ESP_LOGE(TAG, "PNG decoder engine not initialized!");
54
- return -1;
58
+ return DECODE_ERROR_OUT_OF_MEMORY;
55
59
  }
56
60
  if (size < 256 && size < this->download_size_ - this->decoded_bytes_) {
57
61
  ESP_LOGD(TAG, "Waiting for data");
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include "image_decoder.h"
4
+ #include "esphome/core/defines.h"
4
5
  #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT
5
6
  #include <pngle.h>
6
7
 
@@ -20,7 +21,7 @@ class PngDecoder : public ImageDecoder {
20
21
  PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {}
21
22
  ~PngDecoder() override { pngle_destroy(this->pngle_); }
22
23
 
23
- void prepare(uint32_t download_size) override;
24
+ int prepare(size_t download_size) override;
24
25
  int HOT decode(uint8_t *buffer, size_t size) override;
25
26
 
26
27
  protected:
@@ -1,10 +1,12 @@
1
1
  from typing import Any
2
2
 
3
+ import logging
4
+ from esphome import automation
3
5
  import esphome.codegen as cg
4
6
  import esphome.config_validation as cv
5
7
  from esphome import pins
6
8
  from esphome.components import sensor
7
- from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266
9
+ from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266, CONF_TRIGGER_ID
8
10
  from . import const, schema, validate, generate
9
11
 
10
12
  CODEOWNERS = ["@olegtarasov"]
@@ -20,7 +22,21 @@ CONF_CH2_ACTIVE = "ch2_active"
20
22
  CONF_SUMMER_MODE_ACTIVE = "summer_mode_active"
21
23
  CONF_DHW_BLOCK = "dhw_block"
22
24
  CONF_SYNC_MODE = "sync_mode"
23
- CONF_OPENTHERM_VERSION = "opentherm_version"
25
+ CONF_OPENTHERM_VERSION = "opentherm_version" # Deprecated, will be removed
26
+ CONF_BEFORE_SEND = "before_send"
27
+ CONF_BEFORE_PROCESS_RESPONSE = "before_process_response"
28
+
29
+ # Triggers
30
+ BeforeSendTrigger = generate.opentherm_ns.class_(
31
+ "BeforeSendTrigger",
32
+ automation.Trigger.template(generate.OpenthermData.operator("ref")),
33
+ )
34
+ BeforeProcessResponseTrigger = generate.opentherm_ns.class_(
35
+ "BeforeProcessResponseTrigger",
36
+ automation.Trigger.template(generate.OpenthermData.operator("ref")),
37
+ )
38
+
39
+ _LOGGER = logging.getLogger(__name__)
24
40
 
25
41
  CONFIG_SCHEMA = cv.All(
26
42
  cv.Schema(
@@ -36,7 +52,19 @@ CONFIG_SCHEMA = cv.All(
36
52
  cv.Optional(CONF_SUMMER_MODE_ACTIVE, False): cv.boolean,
37
53
  cv.Optional(CONF_DHW_BLOCK, False): cv.boolean,
38
54
  cv.Optional(CONF_SYNC_MODE, False): cv.boolean,
39
- cv.Optional(CONF_OPENTHERM_VERSION): cv.positive_float,
55
+ cv.Optional(CONF_OPENTHERM_VERSION): cv.positive_float, # Deprecated
56
+ cv.Optional(CONF_BEFORE_SEND): automation.validate_automation(
57
+ {
58
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BeforeSendTrigger),
59
+ }
60
+ ),
61
+ cv.Optional(CONF_BEFORE_PROCESS_RESPONSE): automation.validate_automation(
62
+ {
63
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
64
+ BeforeProcessResponseTrigger
65
+ ),
66
+ }
67
+ ),
40
68
  }
41
69
  )
42
70
  .extend(
@@ -44,6 +72,11 @@ CONFIG_SCHEMA = cv.All(
44
72
  schema.INPUTS, (lambda _: cv.use_id(sensor.Sensor))
45
73
  )
46
74
  )
75
+ .extend(
76
+ validate.create_entities_schema(
77
+ schema.SETTINGS, (lambda s: s.validation_schema)
78
+ )
79
+ )
47
80
  .extend(cv.COMPONENT_SCHEMA),
48
81
  cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]),
49
82
  )
@@ -60,18 +93,33 @@ async def to_code(config: dict[str, Any]) -> None:
60
93
  out_pin = await cg.gpio_pin_expression(config[CONF_OUT_PIN])
61
94
  cg.add(var.set_out_pin(out_pin))
62
95
 
63
- non_sensors = {CONF_ID, CONF_IN_PIN, CONF_OUT_PIN}
96
+ non_sensors = {
97
+ CONF_ID,
98
+ CONF_IN_PIN,
99
+ CONF_OUT_PIN,
100
+ CONF_BEFORE_SEND,
101
+ CONF_BEFORE_PROCESS_RESPONSE,
102
+ }
64
103
  input_sensors = []
104
+ settings = []
65
105
  for key, value in config.items():
66
106
  if key in non_sensors:
67
107
  continue
68
108
  if key in schema.INPUTS:
69
109
  input_sensor = await cg.get_variable(value)
70
- cg.add(
71
- getattr(var, f"set_{key}_{const.INPUT_SENSOR.lower()}")(input_sensor)
72
- )
110
+ cg.add(getattr(var, f"set_{key}_{const.INPUT_SENSOR}")(input_sensor))
73
111
  input_sensors.append(key)
112
+ elif key in schema.SETTINGS:
113
+ if value == schema.SETTINGS[key].default_value:
114
+ continue
115
+ cg.add(getattr(var, f"set_{key}_{const.SETTING}")(value))
116
+ settings.append(key)
74
117
  else:
118
+ if key == CONF_OPENTHERM_VERSION:
119
+ _LOGGER.warning(
120
+ "opentherm_version is deprecated and will be removed in esphome 2025.2.0\n"
121
+ "Please change to 'opentherm_version_controller'."
122
+ )
75
123
  cg.add(getattr(var, f"set_{key}")(value))
76
124
 
77
125
  if len(input_sensors) > 0:
@@ -81,3 +129,21 @@ async def to_code(config: dict[str, Any]) -> None:
81
129
  )
82
130
  generate.define_readers(const.INPUT_SENSOR, input_sensors)
83
131
  generate.add_messages(var, input_sensors, schema.INPUTS)
132
+
133
+ if len(settings) > 0:
134
+ generate.define_has_settings(settings, schema.SETTINGS)
135
+ generate.define_message_handler(const.SETTING, settings, schema.SETTINGS)
136
+ generate.define_setting_readers(const.SETTING, settings)
137
+ generate.add_messages(var, settings, schema.SETTINGS)
138
+
139
+ for conf in config.get(CONF_BEFORE_SEND, []):
140
+ trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
141
+ await automation.build_automation(
142
+ trigger, [(generate.OpenthermData.operator("ref"), "x")], conf
143
+ )
144
+
145
+ for conf in config.get(CONF_BEFORE_PROCESS_RESPONSE, []):
146
+ trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
147
+ await automation.build_automation(
148
+ trigger, [(generate.OpenthermData.operator("ref"), "x")], conf
149
+ )
@@ -0,0 +1,25 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/automation.h"
4
+ #include "hub.h"
5
+ #include "opentherm.h"
6
+
7
+ namespace esphome {
8
+ namespace opentherm {
9
+
10
+ class BeforeSendTrigger : public Trigger<OpenthermData &> {
11
+ public:
12
+ BeforeSendTrigger(OpenthermHub *hub) {
13
+ hub->add_on_before_send_callback([this](OpenthermData &x) { this->trigger(x); });
14
+ }
15
+ };
16
+
17
+ class BeforeProcessResponseTrigger : public Trigger<OpenthermData &> {
18
+ public:
19
+ BeforeProcessResponseTrigger(OpenthermHub *hub) {
20
+ hub->add_on_before_process_response_callback([this](OpenthermData &x) { this->trigger(x); });
21
+ }
22
+ };
23
+
24
+ } // namespace opentherm
25
+ } // namespace esphome
@@ -9,3 +9,4 @@ SWITCH = "switch"
9
9
  NUMBER = "number"
10
10
  OUTPUT = "output"
11
11
  INPUT_SENSOR = "input_sensor"
12
+ SETTING = "setting"
@@ -1,13 +1,14 @@
1
1
  from collections.abc import Awaitable
2
- from typing import Any, Callable
2
+ from typing import Any, Callable, Optional
3
3
 
4
4
  import esphome.codegen as cg
5
5
  from esphome.const import CONF_ID
6
6
  from . import const
7
- from .schema import TSchema
7
+ from .schema import TSchema, SettingSchema
8
8
 
9
9
  opentherm_ns = cg.esphome_ns.namespace("opentherm")
10
10
  OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component)
11
+ OpenthermData = opentherm_ns.class_("OpenthermData")
11
12
 
12
13
 
13
14
  def define_has_component(component_type: str, keys: list[str]) -> None:
@@ -21,6 +22,24 @@ def define_has_component(component_type: str, keys: list[str]) -> None:
21
22
  cg.add_define(f"OPENTHERM_HAS_{component_type.upper()}_{key}")
22
23
 
23
24
 
25
+ # We need a separate set of macros for settings because there are different backing field types we need to take
26
+ # into account
27
+ def define_has_settings(keys: list[str], schemas: dict[str, SettingSchema]) -> None:
28
+ cg.add_define(
29
+ "OPENTHERM_SETTING_LIST(F, sep)",
30
+ cg.RawExpression(
31
+ " sep ".join(
32
+ map(
33
+ lambda key: f"F({schemas[key].backing_type}, {key}_setting, {schemas[key].default_value})",
34
+ keys,
35
+ )
36
+ )
37
+ ),
38
+ )
39
+ for key in keys:
40
+ cg.add_define(f"OPENTHERM_HAS_SETTING_{key}")
41
+
42
+
24
43
  def define_message_handler(
25
44
  component_type: str, keys: list[str], schemas: dict[str, TSchema]
26
45
  ) -> None:
@@ -74,16 +93,30 @@ def define_readers(component_type: str, keys: list[str]) -> None:
74
93
  )
75
94
 
76
95
 
96
+ def define_setting_readers(component_type: str, keys: list[str]) -> None:
97
+ for key in keys:
98
+ cg.add_define(
99
+ f"OPENTHERM_READ_{key}",
100
+ cg.RawExpression(f"this->{key}_{component_type.lower()}"),
101
+ )
102
+
103
+
77
104
  def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]):
78
- messages: set[tuple[str, bool]] = set()
105
+ messages: dict[str, tuple[bool, Optional[int]]] = {}
79
106
  for key in keys:
80
- messages.add((schemas[key].message, schemas[key].keep_updated))
81
- for msg, keep_updated in messages:
107
+ messages[schemas[key].message] = (
108
+ schemas[key].keep_updated,
109
+ schemas[key].order if hasattr(schemas[key], "order") else None,
110
+ )
111
+ for msg, (keep_updated, order) in messages.items():
82
112
  msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}")
83
113
  if keep_updated:
84
114
  cg.add(hub.add_repeating_message(msg_expr))
85
115
  else:
86
- cg.add(hub.add_initial_message(msg_expr))
116
+ if order is not None:
117
+ cg.add(hub.add_initial_message(msg_expr, order))
118
+ else:
119
+ cg.add(hub.add_initial_message(msg_expr))
87
120
 
88
121
 
89
122
  def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None: