esphome 2025.4.2__py3-none-any.whl → 2025.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) 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 +416 -662
  13. esphome/components/api/api_connection.h +256 -57
  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/at581x/at581x.h +4 -4
  27. esphome/components/atm90e32/__init__.py +1 -0
  28. esphome/components/atm90e32/atm90e32.cpp +576 -199
  29. esphome/components/atm90e32/atm90e32.h +128 -31
  30. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  31. esphome/components/atm90e32/button/__init__.py +62 -10
  32. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  33. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  34. esphome/components/atm90e32/number/__init__.py +130 -0
  35. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  36. esphome/components/atm90e32/sensor.py +21 -4
  37. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  38. esphome/components/audio/__init__.py +96 -49
  39. esphome/components/audio/audio.h +48 -0
  40. esphome/components/audio/audio_decoder.cpp +1 -1
  41. esphome/components/audio/audio_resampler.cpp +2 -0
  42. esphome/components/audio/audio_resampler.h +1 -0
  43. esphome/components/ballu/climate.py +2 -9
  44. esphome/components/bang_bang/climate.py +5 -6
  45. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  46. esphome/components/bedjet/climate/__init__.py +3 -8
  47. esphome/components/bedjet/fan/__init__.py +2 -11
  48. esphome/components/binary/fan/__init__.py +13 -16
  49. esphome/components/binary_sensor/__init__.py +13 -10
  50. esphome/components/bl0906/constants.h +16 -16
  51. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  52. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  53. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  55. esphome/components/button/__init__.py +11 -8
  56. esphome/components/canbus/canbus.cpp +3 -0
  57. esphome/components/canbus/canbus.h +16 -0
  58. esphome/components/ccs811/sensor.py +9 -6
  59. esphome/components/climate/__init__.py +35 -2
  60. esphome/components/climate/climate_mode.h +1 -1
  61. esphome/components/climate/climate_traits.h +63 -57
  62. esphome/components/climate_ir/__init__.py +57 -17
  63. esphome/components/climate_ir_lg/climate.py +2 -5
  64. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  65. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  66. esphome/components/color/__init__.py +2 -0
  67. esphome/components/const/__init__.py +5 -0
  68. esphome/components/coolix/climate.py +2 -9
  69. esphome/components/copy/cover/__init__.py +10 -9
  70. esphome/components/copy/fan/__init__.py +11 -9
  71. esphome/components/copy/lock/__init__.py +11 -9
  72. esphome/components/copy/text/__init__.py +9 -6
  73. esphome/components/cover/__init__.py +37 -2
  74. esphome/components/cse7766/cse7766.cpp +2 -1
  75. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  76. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  77. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  78. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  80. esphome/components/current_based/cover.py +37 -36
  81. esphome/components/current_based/current_based_cover.cpp +2 -1
  82. esphome/components/daikin/climate.py +2 -9
  83. esphome/components/daikin/daikin.cpp +15 -9
  84. esphome/components/daikin/daikin.h +5 -5
  85. esphome/components/daikin_arc/climate.py +2 -7
  86. esphome/components/daikin_brc/climate.py +3 -5
  87. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  88. esphome/components/dallas_temp/dallas_temp.h +0 -1
  89. esphome/components/daly_bms/daly_bms.cpp +2 -1
  90. esphome/components/debug/debug_component.cpp +6 -1
  91. esphome/components/debug/debug_component.h +8 -0
  92. esphome/components/debug/debug_esp32.cpp +109 -254
  93. esphome/components/debug/sensor.py +14 -0
  94. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  95. esphome/components/delonghi/climate.py +2 -9
  96. esphome/components/demo/__init__.py +18 -20
  97. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  98. esphome/components/dps310/sensor.py +6 -6
  99. esphome/components/ee895/sensor.py +9 -9
  100. esphome/components/emmeti/climate.py +2 -9
  101. esphome/components/endstop/cover.py +17 -16
  102. esphome/components/endstop/endstop_cover.cpp +2 -1
  103. esphome/components/ens160_base/__init__.py +12 -9
  104. esphome/components/esp32/__init__.py +60 -3
  105. esphome/components/esp32/core.cpp +11 -5
  106. esphome/components/esp32/gpio.cpp +86 -24
  107. esphome/components/esp32/gpio.py +15 -16
  108. esphome/components/esp32/gpio_esp32.py +1 -2
  109. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  110. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  111. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  112. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  113. esphome/components/esp32_ble/ble.cpp +1 -0
  114. esphome/components/esp32_ble/ble.h +5 -3
  115. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  116. esphome/components/esp32_ble/ble_advertising.h +1 -0
  117. esphome/components/esp32_ble_server/__init__.py +3 -0
  118. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  119. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  120. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  121. esphome/components/esp32_camera/__init__.py +1 -1
  122. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  123. esphome/components/esp32_camera/esp32_camera.h +1 -1
  124. esphome/components/esp32_can/esp32_can.cpp +1 -1
  125. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  126. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  127. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  128. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  129. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  130. esphome/components/esp8266/gpio.cpp +69 -8
  131. esphome/components/ethernet/ethernet_component.cpp +1 -1
  132. esphome/components/event/__init__.py +13 -10
  133. esphome/components/factory_reset/switch/__init__.py +7 -21
  134. esphome/components/fan/__init__.py +52 -5
  135. esphome/components/fastled_base/__init__.py +1 -4
  136. esphome/components/fastled_base/fastled_light.cpp +1 -1
  137. esphome/components/feedback/cover.py +38 -33
  138. esphome/components/feedback/feedback_cover.cpp +2 -1
  139. esphome/components/fujitsu_general/climate.py +2 -9
  140. esphome/components/gcja5/gcja5.cpp +2 -1
  141. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  142. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  143. esphome/components/gpio_expander/cached_gpio.h +22 -7
  144. esphome/components/gps/__init__.py +47 -17
  145. esphome/components/gps/gps.cpp +42 -23
  146. esphome/components/gps/gps.h +17 -13
  147. esphome/components/graph/__init__.py +1 -2
  148. esphome/components/gree/climate.py +4 -6
  149. esphome/components/gree/gree.cpp +16 -2
  150. esphome/components/gree/gree.h +2 -2
  151. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  152. esphome/components/haier/climate.py +37 -34
  153. esphome/components/hbridge/fan/__init__.py +19 -17
  154. esphome/components/he60r/cover.py +4 -5
  155. esphome/components/heatpumpir/climate.py +3 -6
  156. esphome/components/hitachi_ac344/climate.py +2 -9
  157. esphome/components/hitachi_ac424/climate.py +2 -9
  158. esphome/components/hm3301/hm3301.h +1 -1
  159. esphome/components/hte501/sensor.py +6 -6
  160. esphome/components/http_request/__init__.py +39 -6
  161. esphome/components/http_request/http_request.cpp +20 -0
  162. esphome/components/http_request/http_request.h +57 -15
  163. esphome/components/http_request/http_request_arduino.cpp +22 -6
  164. esphome/components/http_request/http_request_arduino.h +4 -3
  165. esphome/components/http_request/http_request_host.cpp +141 -0
  166. esphome/components/http_request/http_request_host.h +37 -0
  167. esphome/components/http_request/http_request_idf.cpp +35 -3
  168. esphome/components/http_request/http_request_idf.h +10 -3
  169. esphome/components/http_request/httplib.h +9691 -0
  170. esphome/components/http_request/update/__init__.py +11 -8
  171. esphome/components/hyt271/sensor.py +6 -6
  172. esphome/components/i2c/i2c.h +4 -0
  173. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  174. esphome/components/i2s_audio/__init__.py +131 -22
  175. esphome/components/i2s_audio/i2s_audio.h +44 -4
  176. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  177. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  178. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  179. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  180. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  181. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  182. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  183. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  184. esphome/components/image/__init__.py +37 -17
  185. esphome/components/image/image.cpp +25 -8
  186. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  187. esphome/components/key_collector/__init__.py +35 -0
  188. esphome/components/key_collector/key_collector.cpp +8 -0
  189. esphome/components/key_collector/key_collector.h +10 -0
  190. esphome/components/kuntze/kuntze.cpp +2 -1
  191. esphome/components/ld2410/ld2410.h +1 -1
  192. esphome/components/ld2450/ld2450.h +1 -1
  193. esphome/components/light/__init__.py +57 -0
  194. esphome/components/lock/__init__.py +51 -4
  195. esphome/components/lock/automation.h +2 -13
  196. esphome/components/logger/__init__.py +22 -0
  197. esphome/components/logger/logger.cpp +154 -103
  198. esphome/components/logger/logger.h +211 -36
  199. esphome/components/logger/task_log_buffer.cpp +138 -0
  200. esphome/components/logger/task_log_buffer.h +69 -0
  201. esphome/components/lvgl/__init__.py +13 -5
  202. esphome/components/lvgl/automation.py +50 -1
  203. esphome/components/lvgl/defines.py +0 -1
  204. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  205. esphome/components/lvgl/text/__init__.py +1 -2
  206. esphome/components/mapping/__init__.py +134 -0
  207. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  208. esphome/components/max7219digit/max7219digit.cpp +28 -27
  209. esphome/components/mdns/__init__.py +11 -5
  210. esphome/components/mdns/mdns_component.cpp +11 -5
  211. esphome/components/mdns/mdns_component.h +3 -2
  212. esphome/components/mdns/mdns_esp32.cpp +4 -3
  213. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  214. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  215. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  216. esphome/components/media_player/__init__.py +33 -1
  217. esphome/components/mhz19/sensor.py +11 -7
  218. esphome/components/micro_wake_word/__init__.py +99 -31
  219. esphome/components/micro_wake_word/automation.h +54 -0
  220. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  221. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  222. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  223. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  224. esphome/components/micro_wake_word/streaming_model.h +85 -13
  225. esphome/components/microphone/__init__.py +139 -9
  226. esphome/components/microphone/automation.h +14 -2
  227. esphome/components/microphone/microphone.cpp +21 -0
  228. esphome/components/microphone/microphone.h +14 -5
  229. esphome/components/microphone/microphone_source.cpp +95 -0
  230. esphome/components/microphone/microphone_source.h +80 -0
  231. esphome/components/mics_4514/sensor.py +25 -14
  232. esphome/components/midea/climate.py +3 -4
  233. esphome/components/midea_ir/climate.py +3 -5
  234. esphome/components/mipi_spi/__init__.py +15 -0
  235. esphome/components/mipi_spi/display.py +474 -0
  236. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  237. esphome/components/mipi_spi/mipi_spi.h +171 -0
  238. esphome/components/mipi_spi/models/__init__.py +65 -0
  239. esphome/components/mipi_spi/models/amoled.py +72 -0
  240. esphome/components/mipi_spi/models/commands.py +82 -0
  241. esphome/components/mipi_spi/models/cyd.py +10 -0
  242. esphome/components/mipi_spi/models/ili.py +749 -0
  243. esphome/components/mipi_spi/models/jc.py +260 -0
  244. esphome/components/mipi_spi/models/lanbon.py +15 -0
  245. esphome/components/mipi_spi/models/lilygo.py +60 -0
  246. esphome/components/mipi_spi/models/waveshare.py +139 -0
  247. esphome/components/mitsubishi/climate.py +2 -5
  248. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  249. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  250. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  251. esphome/components/mlx90393/sensor.py +5 -0
  252. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  253. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  254. esphome/components/modbus/modbus.cpp +2 -1
  255. esphome/components/mqtt/__init__.py +1 -1
  256. esphome/components/mqtt/mqtt_client.cpp +6 -2
  257. esphome/components/mqtt/mqtt_const.h +4 -0
  258. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  259. esphome/components/mqtt/mqtt_fan.h +2 -0
  260. esphome/components/ms5611/sensor.py +6 -6
  261. esphome/components/ms8607/sensor.py +3 -3
  262. esphome/components/network/__init__.py +1 -1
  263. esphome/components/nextion/base_component.py +17 -16
  264. esphome/components/nextion/display.py +11 -2
  265. esphome/components/nextion/nextion.cpp +39 -1
  266. esphome/components/nextion/nextion.h +50 -0
  267. esphome/components/noblex/climate.py +2 -9
  268. esphome/components/number/__init__.py +12 -9
  269. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  270. esphome/components/one_wire/one_wire_bus.h +14 -8
  271. esphome/components/online_image/bmp_image.cpp +48 -11
  272. esphome/components/online_image/bmp_image.h +2 -0
  273. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  274. esphome/components/opentherm/number/__init__.py +11 -20
  275. esphome/components/opentherm/sensor/__init__.py +3 -3
  276. esphome/components/opentherm/switch/__init__.py +3 -5
  277. esphome/components/output/lock/__init__.py +11 -9
  278. esphome/components/packages/__init__.py +33 -31
  279. esphome/components/packet_transport/__init__.py +201 -0
  280. esphome/components/packet_transport/binary_sensor.py +19 -0
  281. esphome/components/packet_transport/packet_transport.cpp +534 -0
  282. esphome/components/packet_transport/packet_transport.h +154 -0
  283. esphome/components/packet_transport/sensor.py +19 -0
  284. esphome/components/pca9685/pca9685_output.cpp +2 -1
  285. esphome/components/pid/climate.py +2 -4
  286. esphome/components/pm2005/__init__.py +1 -0
  287. esphome/components/pm2005/pm2005.cpp +123 -0
  288. esphome/components/pm2005/pm2005.h +46 -0
  289. esphome/components/pm2005/sensor.py +86 -0
  290. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  291. esphome/components/pmsa003i/pmsa003i.h +25 -25
  292. esphome/components/pmsx003/pmsx003.cpp +195 -230
  293. esphome/components/pmsx003/pmsx003.h +51 -33
  294. esphome/components/pmsx003/sensor.py +21 -11
  295. esphome/components/pn7150/pn7150.h +2 -2
  296. esphome/components/pn7160/pn7160.h +2 -2
  297. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  298. esphome/components/prometheus/prometheus_handler.h +17 -0
  299. esphome/components/psram/__init__.py +7 -5
  300. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  301. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  302. esphome/components/pzem004t/pzem004t.cpp +2 -1
  303. esphome/components/qspi_dbi/__init__.py +0 -1
  304. esphome/components/qspi_dbi/display.py +2 -1
  305. esphome/components/qspi_dbi/models.py +1 -2
  306. esphome/components/remote_base/__init__.py +91 -0
  307. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  308. esphome/components/remote_base/beo4_protocol.h +43 -0
  309. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  310. esphome/components/remote_base/gobox_protocol.h +54 -0
  311. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  312. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  313. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  314. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  315. esphome/components/scd30/sensor.py +2 -3
  316. esphome/components/scd4x/sensor.py +4 -5
  317. esphome/components/sdp3x/sensor.py +2 -1
  318. esphome/components/sds011/sds011.cpp +2 -1
  319. esphome/components/select/__init__.py +19 -20
  320. esphome/components/sen5x/sen5x.cpp +55 -36
  321. esphome/components/sen5x/sensor.py +1 -1
  322. esphome/components/senseair/sensor.py +3 -3
  323. esphome/components/sensor/__init__.py +158 -14
  324. esphome/components/sensor/filter.cpp +23 -0
  325. esphome/components/sensor/filter.h +22 -0
  326. esphome/components/sgp30/sensor.py +14 -16
  327. esphome/components/sgp4x/sensor.py +1 -1
  328. esphome/components/sht4x/sht4x.cpp +43 -22
  329. esphome/components/sht4x/sht4x.h +1 -1
  330. esphome/components/shtcx/sensor.py +6 -6
  331. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  332. esphome/components/sml/text_sensor/__init__.py +4 -6
  333. esphome/components/sound_level/__init__.py +0 -0
  334. esphome/components/sound_level/sensor.py +97 -0
  335. esphome/components/sound_level/sound_level.cpp +194 -0
  336. esphome/components/sound_level/sound_level.h +73 -0
  337. esphome/components/speaker/media_player/__init__.py +4 -8
  338. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  339. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  340. esphome/components/speaker/speaker.h +4 -7
  341. esphome/components/speed/fan/__init__.py +17 -16
  342. esphome/components/spi/spi.h +11 -1
  343. esphome/components/sprinkler/__init__.py +18 -19
  344. esphome/components/sprinkler/sprinkler.cpp +6 -5
  345. esphome/components/switch/__init__.py +32 -42
  346. esphome/components/syslog/__init__.py +41 -0
  347. esphome/components/syslog/esphome_syslog.cpp +49 -0
  348. esphome/components/syslog/esphome_syslog.h +27 -0
  349. esphome/components/t6615/sensor.py +3 -3
  350. esphome/components/t6615/t6615.cpp +2 -1
  351. esphome/components/tca9555/tca9555.cpp +11 -6
  352. esphome/components/tcl112/climate.py +2 -9
  353. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  354. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  355. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  356. esphome/components/template/cover/__init__.py +27 -21
  357. esphome/components/template/fan/__init__.py +14 -12
  358. esphome/components/template/lock/__init__.py +20 -25
  359. esphome/components/template/lock/automation.h +18 -0
  360. esphome/components/template/text/__init__.py +4 -3
  361. esphome/components/template/valve/__init__.py +32 -21
  362. esphome/components/template/valve/automation.h +24 -0
  363. esphome/components/text/__init__.py +32 -1
  364. esphome/components/text_sensor/__init__.py +24 -29
  365. esphome/components/thermostat/climate.py +5 -5
  366. esphome/components/time_based/cover.py +17 -16
  367. esphome/components/time_based/time_based_cover.cpp +2 -1
  368. esphome/components/tm1638/switch/__init__.py +10 -7
  369. esphome/components/tormatic/cover.py +4 -5
  370. esphome/components/toshiba/climate.py +3 -5
  371. esphome/components/touchscreen/touchscreen.cpp +3 -1
  372. esphome/components/tuya/climate/__init__.py +5 -6
  373. esphome/components/tuya/cover/__init__.py +6 -11
  374. esphome/components/tuya/select/__init__.py +15 -5
  375. esphome/components/tuya/select/tuya_select.cpp +6 -1
  376. esphome/components/tuya/select/tuya_select.h +5 -1
  377. esphome/components/uart/packet_transport/__init__.py +20 -0
  378. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  379. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  380. esphome/components/uart/switch/uart_switch.cpp +2 -1
  381. esphome/components/udp/__init__.py +126 -128
  382. esphome/components/udp/automation.h +40 -0
  383. esphome/components/udp/binary_sensor.py +3 -25
  384. esphome/components/udp/packet_transport/__init__.py +29 -0
  385. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  386. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  387. esphome/components/udp/sensor.py +3 -25
  388. esphome/components/udp/udp_component.cpp +26 -470
  389. esphome/components/udp/udp_component.h +21 -128
  390. esphome/components/update/__init__.py +31 -1
  391. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  392. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  393. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  394. esphome/components/uptime/text_sensor/__init__.py +47 -7
  395. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  396. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  397. esphome/components/valve/__init__.py +34 -3
  398. esphome/components/valve/automation.h +1 -19
  399. esphome/components/vl53l0x/sensor.py +11 -0
  400. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  401. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  402. esphome/components/voice_assistant/__init__.py +36 -10
  403. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  404. esphome/components/voice_assistant/voice_assistant.h +26 -25
  405. esphome/components/waveshare_epaper/display.py +6 -0
  406. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  407. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  408. esphome/components/weikai/weikai.cpp +0 -52
  409. esphome/components/whirlpool/climate.py +3 -5
  410. esphome/components/whynter/climate.py +3 -5
  411. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  412. esphome/components/yashima/climate.py +6 -6
  413. esphome/components/zhlt01/climate.py +2 -7
  414. esphome/config.py +13 -13
  415. esphome/config_validation.py +38 -58
  416. esphome/const.py +15 -1
  417. esphome/core/__init__.py +2 -0
  418. esphome/core/application.cpp +27 -10
  419. esphome/core/application.h +9 -1
  420. esphome/core/automation.h +4 -3
  421. esphome/core/component.cpp +28 -7
  422. esphome/core/component.h +10 -1
  423. esphome/core/defines.h +23 -17
  424. esphome/core/doxygen.h +13 -0
  425. esphome/core/macros.h +4 -0
  426. esphome/core/scheduler.cpp +7 -1
  427. esphome/cpp_generator.py +6 -2
  428. esphome/dashboard/web_server.py +3 -3
  429. esphome/helpers.py +39 -0
  430. esphome/loader.py +4 -0
  431. esphome/log.py +15 -19
  432. esphome/mqtt.py +23 -10
  433. esphome/platformio_api.py +1 -1
  434. esphome/schema_extractors.py +0 -1
  435. esphome/voluptuous_schema.py +3 -1
  436. esphome/vscode.py +15 -0
  437. esphome/wizard.py +47 -37
  438. esphome/zeroconf.py +7 -3
  439. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  440. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/RECORD +444 -383
  441. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  442. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  443. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  444. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -33,6 +33,7 @@ from esphome.const import (
33
33
  UNIT_PERCENT,
34
34
  )
35
35
 
36
+ CODEOWNERS = ["@ximex"]
36
37
  DEPENDENCIES = ["uart"]
37
38
 
38
39
  pmsx003_ns = cg.esphome_ns.namespace("pmsx003")
@@ -57,9 +58,18 @@ SENSORS_TO_TYPE = {
57
58
  CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
58
59
  CONF_PM_2_5: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
59
60
  CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
61
+ CONF_PM_1_0_STD: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
62
+ CONF_PM_2_5_STD: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
63
+ CONF_PM_10_0_STD: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
64
+ CONF_PM_0_3UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
65
+ CONF_PM_0_5UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
66
+ CONF_PM_1_0UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
67
+ CONF_PM_2_5UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
68
+ CONF_PM_5_0UM: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S],
69
+ CONF_PM_10_0UM: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S],
70
+ CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S],
60
71
  CONF_TEMPERATURE: [TYPE_PMS5003T, TYPE_PMS5003ST],
61
72
  CONF_HUMIDITY: [TYPE_PMS5003T, TYPE_PMS5003ST],
62
- CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S],
63
73
  }
64
74
 
65
75
 
@@ -164,6 +174,12 @@ CONFIG_SCHEMA = (
164
174
  accuracy_decimals=0,
165
175
  state_class=STATE_CLASS_MEASUREMENT,
166
176
  ),
177
+ cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema(
178
+ unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
179
+ icon=ICON_CHEMICAL_WEAPON,
180
+ accuracy_decimals=0,
181
+ state_class=STATE_CLASS_MEASUREMENT,
182
+ ),
167
183
  cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
168
184
  unit_of_measurement=UNIT_CELSIUS,
169
185
  accuracy_decimals=1,
@@ -176,12 +192,6 @@ CONFIG_SCHEMA = (
176
192
  device_class=DEVICE_CLASS_HUMIDITY,
177
193
  state_class=STATE_CLASS_MEASUREMENT,
178
194
  ),
179
- cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema(
180
- unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER,
181
- icon=ICON_CHEMICAL_WEAPON,
182
- accuracy_decimals=0,
183
- state_class=STATE_CLASS_MEASUREMENT,
184
- ),
185
195
  cv.Optional(CONF_UPDATE_INTERVAL, default="0s"): validate_update_interval,
186
196
  }
187
197
  )
@@ -256,6 +266,10 @@ async def to_code(config):
256
266
  sens = await sensor.new_sensor(config[CONF_PM_10_0UM])
257
267
  cg.add(var.set_pm_particles_100um_sensor(sens))
258
268
 
269
+ if CONF_FORMALDEHYDE in config:
270
+ sens = await sensor.new_sensor(config[CONF_FORMALDEHYDE])
271
+ cg.add(var.set_formaldehyde_sensor(sens))
272
+
259
273
  if CONF_TEMPERATURE in config:
260
274
  sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
261
275
  cg.add(var.set_temperature_sensor(sens))
@@ -264,8 +278,4 @@ async def to_code(config):
264
278
  sens = await sensor.new_sensor(config[CONF_HUMIDITY])
265
279
  cg.add(var.set_humidity_sensor(sens))
266
280
 
267
- if CONF_FORMALDEHYDE in config:
268
- sens = await sensor.new_sensor(config[CONF_FORMALDEHYDE])
269
- cg.add(var.set_formaldehyde_sensor(sens))
270
-
271
281
  cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
@@ -123,8 +123,8 @@ enum class NCIState : uint8_t {
123
123
  RFST_POLL_ACTIVE,
124
124
  EP_DEACTIVATING,
125
125
  EP_SELECTING,
126
- TEST = 0XFE,
127
- FAILED = 0XFF,
126
+ TEST = 0xFE,
127
+ FAILED = 0xFF,
128
128
  };
129
129
 
130
130
  enum class TestMode : uint8_t {
@@ -138,8 +138,8 @@ enum class NCIState : uint8_t {
138
138
  RFST_POLL_ACTIVE,
139
139
  EP_DEACTIVATING,
140
140
  EP_SELECTING,
141
- TEST = 0XFE,
142
- FAILED = 0XFF,
141
+ TEST = 0xFE,
142
+ FAILED = 0xFF,
143
143
  };
144
144
 
145
145
  enum class TestMode : uint8_t {
@@ -89,6 +89,12 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) {
89
89
  this->valve_row_(stream, obj, area, node, friendly_name);
90
90
  #endif
91
91
 
92
+ #ifdef USE_CLIMATE
93
+ this->climate_type_(stream);
94
+ for (auto *obj : App.get_climates())
95
+ this->climate_row_(stream, obj, area, node, friendly_name);
96
+ #endif
97
+
92
98
  req->send(stream);
93
99
  }
94
100
 
@@ -824,6 +830,174 @@ void PrometheusHandler::valve_row_(AsyncResponseStream *stream, valve::Valve *ob
824
830
  }
825
831
  #endif
826
832
 
833
+ #ifdef USE_CLIMATE
834
+ void PrometheusHandler::climate_type_(AsyncResponseStream *stream) {
835
+ stream->print(F("#TYPE esphome_climate_setting gauge\n"));
836
+ stream->print(F("#TYPE esphome_climate_value gauge\n"));
837
+ stream->print(F("#TYPE esphome_climate_failed gauge\n"));
838
+ }
839
+
840
+ void PrometheusHandler::climate_setting_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area,
841
+ std::string &node, std::string &friendly_name, std::string &setting,
842
+ const LogString *setting_value) {
843
+ stream->print(F("esphome_climate_setting{id=\""));
844
+ stream->print(relabel_id_(obj).c_str());
845
+ add_area_label_(stream, area);
846
+ add_node_label_(stream, node);
847
+ add_friendly_name_label_(stream, friendly_name);
848
+ stream->print(F("\",name=\""));
849
+ stream->print(relabel_name_(obj).c_str());
850
+ stream->print(F("\",category=\""));
851
+ stream->print(setting.c_str());
852
+ stream->print(F("\",setting_value=\""));
853
+ stream->print(LOG_STR_ARG(setting_value));
854
+ stream->print(F("\"} "));
855
+ stream->print(F("1.0"));
856
+ stream->print(F("\n"));
857
+ }
858
+
859
+ void PrometheusHandler::climate_value_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area,
860
+ std::string &node, std::string &friendly_name, std::string &category,
861
+ std::string &climate_value) {
862
+ stream->print(F("esphome_climate_value{id=\""));
863
+ stream->print(relabel_id_(obj).c_str());
864
+ add_area_label_(stream, area);
865
+ add_node_label_(stream, node);
866
+ add_friendly_name_label_(stream, friendly_name);
867
+ stream->print(F("\",name=\""));
868
+ stream->print(relabel_name_(obj).c_str());
869
+ stream->print(F("\",category=\""));
870
+ stream->print(category.c_str());
871
+ stream->print(F("\"} "));
872
+ stream->print(climate_value.c_str());
873
+ stream->print(F("\n"));
874
+ }
875
+
876
+ void PrometheusHandler::climate_failed_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area,
877
+ std::string &node, std::string &friendly_name, std::string &category,
878
+ bool is_failed_value) {
879
+ stream->print(F("esphome_climate_failed{id=\""));
880
+ stream->print(relabel_id_(obj).c_str());
881
+ add_area_label_(stream, area);
882
+ add_node_label_(stream, node);
883
+ add_friendly_name_label_(stream, friendly_name);
884
+ stream->print(F("\",name=\""));
885
+ stream->print(relabel_name_(obj).c_str());
886
+ stream->print(F("\",category=\""));
887
+ stream->print(category.c_str());
888
+ stream->print(F("\"} "));
889
+ if (is_failed_value) {
890
+ stream->print(F("1.0"));
891
+ } else {
892
+ stream->print(F("0.0"));
893
+ }
894
+ stream->print(F("\n"));
895
+ }
896
+
897
+ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area,
898
+ std::string &node, std::string &friendly_name) {
899
+ if (obj->is_internal() && !this->include_internal_)
900
+ return;
901
+ // Data itself
902
+ bool any_failures = false;
903
+ std::string climate_mode_category = "mode";
904
+ const auto *climate_mode_value = climate::climate_mode_to_string(obj->mode);
905
+ climate_setting_row_(stream, obj, area, node, friendly_name, climate_mode_category, climate_mode_value);
906
+ const auto traits = obj->get_traits();
907
+ // Now see if traits is supported
908
+ int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
909
+ int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
910
+ // max temp
911
+ std::string max_temp = "maximum_temperature";
912
+ auto max_temp_value = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
913
+ climate_value_row_(stream, obj, area, node, friendly_name, max_temp, max_temp_value);
914
+ // max temp
915
+ std::string min_temp = "mininum_temperature";
916
+ auto min_temp_value = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
917
+ climate_value_row_(stream, obj, area, node, friendly_name, min_temp, min_temp_value);
918
+ // now check optional traits
919
+ if (traits.get_supports_current_temperature()) {
920
+ std::string current_temp = "current_temperature";
921
+ if (std::isnan(obj->current_temperature)) {
922
+ climate_failed_row_(stream, obj, area, node, friendly_name, current_temp, true);
923
+ any_failures = true;
924
+ } else {
925
+ auto current_temp_value = value_accuracy_to_string(obj->current_temperature, current_accuracy);
926
+ climate_value_row_(stream, obj, area, node, friendly_name, current_temp, current_temp_value);
927
+ climate_failed_row_(stream, obj, area, node, friendly_name, current_temp, false);
928
+ }
929
+ }
930
+ if (traits.get_supports_current_humidity()) {
931
+ std::string current_humidity = "current_humidity";
932
+ if (std::isnan(obj->current_humidity)) {
933
+ climate_failed_row_(stream, obj, area, node, friendly_name, current_humidity, true);
934
+ any_failures = true;
935
+ } else {
936
+ auto current_humidity_value = value_accuracy_to_string(obj->current_humidity, 0);
937
+ climate_value_row_(stream, obj, area, node, friendly_name, current_humidity, current_humidity_value);
938
+ climate_failed_row_(stream, obj, area, node, friendly_name, current_humidity, false);
939
+ }
940
+ }
941
+ if (traits.get_supports_target_humidity()) {
942
+ std::string target_humidity = "target_humidity";
943
+ if (std::isnan(obj->target_humidity)) {
944
+ climate_failed_row_(stream, obj, area, node, friendly_name, target_humidity, true);
945
+ any_failures = true;
946
+ } else {
947
+ auto target_humidity_value = value_accuracy_to_string(obj->target_humidity, 0);
948
+ climate_value_row_(stream, obj, area, node, friendly_name, target_humidity, target_humidity_value);
949
+ climate_failed_row_(stream, obj, area, node, friendly_name, target_humidity, false);
950
+ }
951
+ }
952
+ if (traits.get_supports_two_point_target_temperature()) {
953
+ std::string target_temp_low = "target_temperature_low";
954
+ auto target_temp_low_value = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
955
+ climate_value_row_(stream, obj, area, node, friendly_name, target_temp_low, target_temp_low_value);
956
+ std::string target_temp_high = "target_temperature_high";
957
+ auto target_temp_high_value = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
958
+ climate_value_row_(stream, obj, area, node, friendly_name, target_temp_high, target_temp_high_value);
959
+ } else {
960
+ std::string target_temp = "target_temperature";
961
+ auto target_temp_value = value_accuracy_to_string(obj->target_temperature, target_accuracy);
962
+ climate_value_row_(stream, obj, area, node, friendly_name, target_temp, target_temp_value);
963
+ }
964
+ if (traits.get_supports_action()) {
965
+ std::string climate_trait_category = "action";
966
+ const auto *climate_trait_value = climate::climate_action_to_string(obj->action);
967
+ climate_setting_row_(stream, obj, area, node, friendly_name, climate_trait_category, climate_trait_value);
968
+ }
969
+ if (traits.get_supports_fan_modes()) {
970
+ std::string climate_trait_category = "fan_mode";
971
+ if (obj->fan_mode.has_value()) {
972
+ const auto *climate_trait_value = climate::climate_fan_mode_to_string(obj->fan_mode.value());
973
+ climate_setting_row_(stream, obj, area, node, friendly_name, climate_trait_category, climate_trait_value);
974
+ climate_failed_row_(stream, obj, area, node, friendly_name, climate_trait_category, false);
975
+ } else {
976
+ climate_failed_row_(stream, obj, area, node, friendly_name, climate_trait_category, true);
977
+ any_failures = true;
978
+ }
979
+ }
980
+ if (traits.get_supports_presets()) {
981
+ std::string climate_trait_category = "preset";
982
+ if (obj->preset.has_value()) {
983
+ const auto *climate_trait_value = climate::climate_preset_to_string(obj->preset.value());
984
+ climate_setting_row_(stream, obj, area, node, friendly_name, climate_trait_category, climate_trait_value);
985
+ climate_failed_row_(stream, obj, area, node, friendly_name, climate_trait_category, false);
986
+ } else {
987
+ climate_failed_row_(stream, obj, area, node, friendly_name, climate_trait_category, true);
988
+ any_failures = true;
989
+ }
990
+ }
991
+ if (traits.get_supports_swing_modes()) {
992
+ std::string climate_trait_category = "swing_mode";
993
+ const auto *climate_trait_value = climate::climate_swing_mode_to_string(obj->swing_mode);
994
+ climate_setting_row_(stream, obj, area, node, friendly_name, climate_trait_category, climate_trait_value);
995
+ }
996
+ std::string all_climate_category = "all";
997
+ climate_failed_row_(stream, obj, area, node, friendly_name, all_climate_category, any_failures);
998
+ }
999
+ #endif
1000
+
827
1001
  } // namespace prometheus
828
1002
  } // namespace esphome
829
1003
  #endif
@@ -8,6 +8,9 @@
8
8
  #include "esphome/core/component.h"
9
9
  #include "esphome/core/controller.h"
10
10
  #include "esphome/core/entity_base.h"
11
+ #ifdef USE_CLIMATE
12
+ #include "esphome/core/log.h"
13
+ #endif
11
14
 
12
15
  namespace esphome {
13
16
  namespace prometheus {
@@ -169,6 +172,20 @@ class PrometheusHandler : public AsyncWebHandler, public Component {
169
172
  std::string &friendly_name);
170
173
  #endif
171
174
 
175
+ #ifdef USE_CLIMATE
176
+ /// Return the type for prometheus
177
+ void climate_type_(AsyncResponseStream *stream);
178
+ /// Return the climate state as prometheus data point
179
+ void climate_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area, std::string &node,
180
+ std::string &friendly_name);
181
+ void climate_failed_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area, std::string &node,
182
+ std::string &friendly_name, std::string &category, bool is_failed_value);
183
+ void climate_setting_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area, std::string &node,
184
+ std::string &friendly_name, std::string &setting, const LogString *setting_value);
185
+ void climate_value_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area, std::string &node,
186
+ std::string &friendly_name, std::string &category, std::string &climate_value);
187
+ #endif
188
+
172
189
  web_server_base::WebServerBase *base_;
173
190
  bool include_internal_{false};
174
191
  std::map<EntityBase *, std::string> relabel_map_id_;
@@ -16,6 +16,8 @@ from esphome.const import (
16
16
  CONF_ID,
17
17
  CONF_MODE,
18
18
  CONF_SPEED,
19
+ KEY_CORE,
20
+ KEY_FRAMEWORK_VERSION,
19
21
  PLATFORM_ESP32,
20
22
  )
21
23
  from esphome.core import CORE
@@ -110,11 +112,11 @@ async def to_code(config):
110
112
  add_idf_sdkconfig_option(f"{SPIRAM_MODES[config[CONF_MODE]]}", True)
111
113
  add_idf_sdkconfig_option(f"{SPIRAM_SPEEDS[config[CONF_SPEED]]}", True)
112
114
  if config[CONF_MODE] == TYPE_OCTAL and config[CONF_SPEED] == 120e6:
113
- add_idf_sdkconfig_option("CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240", True)
114
- # This works only on IDF 5.4.x but does no harm on earlier versions
115
- add_idf_sdkconfig_option(
116
- "CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR", True
117
- )
115
+ add_idf_sdkconfig_option("CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240", True)
116
+ if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(5, 4, 0):
117
+ add_idf_sdkconfig_option(
118
+ "CONFIG_SPIRAM_TIMING_TUNING_POINT_VIA_TEMPERATURE_SENSOR", True
119
+ )
118
120
  if config[CONF_ENABLE_ECC]:
119
121
  add_idf_sdkconfig_option("CONFIG_SPIRAM_ECC_ENABLE", True)
120
122
 
@@ -18,6 +18,9 @@ void PulseMeterSensor::setup() {
18
18
  this->pin_->setup();
19
19
  this->isr_pin_ = pin_->to_isr();
20
20
 
21
+ // Set the pin value to the current value to avoid a false edge
22
+ this->last_pin_val_ = this->pin_->digital_read();
23
+
21
24
  // Set the last processed edge to now for the first timeout
22
25
  this->last_processed_edge_us_ = micros();
23
26
 
@@ -25,23 +28,37 @@ void PulseMeterSensor::setup() {
25
28
  this->pin_->attach_interrupt(PulseMeterSensor::edge_intr, this, gpio::INTERRUPT_RISING_EDGE);
26
29
  } else if (this->filter_mode_ == FILTER_PULSE) {
27
30
  // Set the pin value to the current value to avoid a false edge
28
- this->pulse_state_.last_pin_val_ = this->isr_pin_.digital_read();
29
- this->pulse_state_.latched_ = this->pulse_state_.last_pin_val_;
31
+ this->pulse_state_.latched_ = this->last_pin_val_;
30
32
  this->pin_->attach_interrupt(PulseMeterSensor::pulse_intr, this, gpio::INTERRUPT_ANY_EDGE);
31
33
  }
32
34
  }
33
35
 
34
36
  void PulseMeterSensor::loop() {
35
- const uint32_t now = micros();
36
-
37
37
  // Reset the count in get before we pass it back to the ISR as set
38
38
  this->get_->count_ = 0;
39
39
 
40
- // Swap out set and get to get the latest state from the ISR
41
- // The ISR could interrupt on any of these lines and the results would be consistent
42
- auto *temp = this->set_;
43
- this->set_ = this->get_;
44
- this->get_ = temp;
40
+ {
41
+ // Lock the interrupt so the interrupt code doesn't interfere with itself
42
+ InterruptLock lock;
43
+
44
+ // Sometimes ESP devices miss interrupts if the edge rises or falls too slowly.
45
+ // See https://github.com/espressif/arduino-esp32/issues/4172
46
+ // If the edges are rising too slowly it also implies that the pulse rate is slow.
47
+ // Therefore the update rate of the loop is likely fast enough to detect the edges.
48
+ // When the main loop detects an edge that the ISR didn't it will run the ISR functions directly.
49
+ bool current = this->pin_->digital_read();
50
+ if (this->filter_mode_ == FILTER_EDGE && current && !this->last_pin_val_) {
51
+ PulseMeterSensor::edge_intr(this);
52
+ } else if (this->filter_mode_ == FILTER_PULSE && current != this->last_pin_val_) {
53
+ PulseMeterSensor::pulse_intr(this);
54
+ }
55
+ this->last_pin_val_ = current;
56
+
57
+ // Swap out set and get to get the latest state from the ISR
58
+ std::swap(this->set_, this->get_);
59
+ }
60
+
61
+ const uint32_t now = micros();
45
62
 
46
63
  // If an edge was peeked, repay the debt
47
64
  if (this->peeked_edge_ && this->get_->count_ > 0) {
@@ -131,6 +148,9 @@ void IRAM_ATTR PulseMeterSensor::edge_intr(PulseMeterSensor *sensor) {
131
148
  set.last_rising_edge_us_ = now;
132
149
  set.count_++;
133
150
  }
151
+
152
+ // This ISR is bound to rising edges, so the pin is high
153
+ sensor->last_pin_val_ = true;
134
154
  }
135
155
 
136
156
  void IRAM_ATTR PulseMeterSensor::pulse_intr(PulseMeterSensor *sensor) {
@@ -144,9 +164,9 @@ void IRAM_ATTR PulseMeterSensor::pulse_intr(PulseMeterSensor *sensor) {
144
164
  // Filter length has passed since the last interrupt
145
165
  const bool length = now - state.last_intr_ >= sensor->filter_us_;
146
166
 
147
- if (length && state.latched_ && !state.last_pin_val_) { // Long enough low edge
167
+ if (length && state.latched_ && !sensor->last_pin_val_) { // Long enough low edge
148
168
  state.latched_ = false;
149
- } else if (length && !state.latched_ && state.last_pin_val_) { // Long enough high edge
169
+ } else if (length && !state.latched_ && sensor->last_pin_val_) { // Long enough high edge
150
170
  state.latched_ = true;
151
171
  set.last_detected_edge_us_ = state.last_intr_;
152
172
  set.count_++;
@@ -158,7 +178,7 @@ void IRAM_ATTR PulseMeterSensor::pulse_intr(PulseMeterSensor *sensor) {
158
178
  set.last_rising_edge_us_ = !state.latched_ && pin_val ? now : set.last_detected_edge_us_;
159
179
 
160
180
  state.last_intr_ = now;
161
- state.last_pin_val_ = pin_val;
181
+ sensor->last_pin_val_ = pin_val;
162
182
  }
163
183
 
164
184
  } // namespace pulse_meter
@@ -49,9 +49,7 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
49
49
 
50
50
  // This struct (and the two pointers) are used to pass data between the ISR and loop.
51
51
  // These two pointers are exchanged each loop.
52
- // Therefore you can't use data in the pointer to loop receives to set values in the pointer to loop sends.
53
- // As a result it's easiest if you only use these pointers to send data from the ISR to the loop.
54
- // (except for resetting the values)
52
+ // Use these to send data from the ISR to the loop not the other way around (except for resetting the values).
55
53
  struct State {
56
54
  uint32_t last_detected_edge_us_ = 0;
57
55
  uint32_t last_rising_edge_us_ = 0;
@@ -61,9 +59,12 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
61
59
  volatile State *set_ = state_;
62
60
  volatile State *get_ = state_ + 1;
63
61
 
64
- // Only use these variables in the ISR
62
+ // Only use the following variables in the ISR or while guarded by an InterruptLock
65
63
  ISRInternalGPIOPin isr_pin_;
66
64
 
65
+ /// The last pin value seen
66
+ bool last_pin_val_ = false;
67
+
67
68
  /// Filter state for edge mode
68
69
  struct EdgeState {
69
70
  uint32_t last_sent_edge_us_ = 0;
@@ -74,7 +75,6 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
74
75
  struct PulseState {
75
76
  uint32_t last_intr_ = 0;
76
77
  bool latched_ = false;
77
- bool last_pin_val_ = false;
78
78
  };
79
79
  PulseState pulse_state_{};
80
80
  };
@@ -1,5 +1,6 @@
1
1
  #include "pzem004t.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
  #include <cinttypes>
4
5
 
5
6
  namespace esphome {
@@ -16,7 +17,7 @@ void PZEM004T::setup() {
16
17
  }
17
18
 
18
19
  void PZEM004T::loop() {
19
- const uint32_t now = millis();
20
+ const uint32_t now = App.get_loop_component_start_time();
20
21
  if (now - this->last_read_ > 500 && this->available() < 7) {
21
22
  while (this->available())
22
23
  this->read();
@@ -1,4 +1,3 @@
1
1
  CODEOWNERS = ["@clydebarrow"]
2
2
 
3
3
  CONF_DRAW_FROM_ORIGIN = "draw_from_origin"
4
- CONF_DRAW_ROUNDING = "draw_rounding"
@@ -1,6 +1,7 @@
1
1
  from esphome import pins
2
2
  import esphome.codegen as cg
3
3
  from esphome.components import display, spi
4
+ from esphome.components.const import CONF_DRAW_ROUNDING
4
5
  import esphome.config_validation as cv
5
6
  from esphome.const import (
6
7
  CONF_BRIGHTNESS,
@@ -24,7 +25,7 @@ from esphome.const import (
24
25
  )
25
26
  from esphome.core import TimePeriod
26
27
 
27
- from . import CONF_DRAW_FROM_ORIGIN, CONF_DRAW_ROUNDING
28
+ from . import CONF_DRAW_FROM_ORIGIN
28
29
  from .models import DriverChip
29
30
 
30
31
  DEPENDENCIES = ["spi"]
@@ -1,8 +1,7 @@
1
1
  # Commands
2
+ from esphome.components.const import CONF_DRAW_ROUNDING
2
3
  from esphome.const import CONF_INVERT_COLORS, CONF_SWAP_XY
3
4
 
4
- from . import CONF_DRAW_ROUNDING
5
-
6
5
  SW_RESET_CMD = 0x01
7
6
  SLEEP_IN = 0x10
8
7
  SLEEP_OUT = 0x11
@@ -28,6 +28,7 @@ from esphome.const import (
28
28
  CONF_RC_CODE_2,
29
29
  CONF_REPEAT,
30
30
  CONF_SECOND,
31
+ CONF_SOURCE,
31
32
  CONF_STATE,
32
33
  CONF_SYNC,
33
34
  CONF_TIMES,
@@ -265,6 +266,53 @@ async def build_dumpers(config):
265
266
  return dumpers
266
267
 
267
268
 
269
+ # Beo4
270
+ Beo4Data, Beo4BinarySensor, Beo4Trigger, Beo4Action, Beo4Dumper = declare_protocol(
271
+ "Beo4"
272
+ )
273
+ BEO4_SCHEMA = cv.Schema(
274
+ {
275
+ cv.Required(CONF_SOURCE): cv.hex_uint8_t,
276
+ cv.Required(CONF_COMMAND): cv.hex_uint8_t,
277
+ cv.Optional(CONF_COMMAND_REPEATS, default=1): cv.uint8_t,
278
+ }
279
+ )
280
+
281
+
282
+ @register_binary_sensor("beo4", Beo4BinarySensor, BEO4_SCHEMA)
283
+ def beo4_binary_sensor(var, config):
284
+ cg.add(
285
+ var.set_data(
286
+ cg.StructInitializer(
287
+ Beo4Data,
288
+ ("source", config[CONF_SOURCE]),
289
+ ("command", config[CONF_COMMAND]),
290
+ ("repeats", config[CONF_COMMAND_REPEATS]),
291
+ )
292
+ )
293
+ )
294
+
295
+
296
+ @register_trigger("beo4", Beo4Trigger, Beo4Data)
297
+ def beo4_trigger(var, config):
298
+ pass
299
+
300
+
301
+ @register_dumper("beo4", Beo4Dumper)
302
+ def beo4_dumper(var, config):
303
+ pass
304
+
305
+
306
+ @register_action("beo4", Beo4Action, BEO4_SCHEMA)
307
+ async def beo4_action(var, config, args):
308
+ template_ = await cg.templatable(config[CONF_SOURCE], args, cg.uint8)
309
+ cg.add(var.set_source(template_))
310
+ template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8)
311
+ cg.add(var.set_command(template_))
312
+ template_ = await cg.templatable(config[CONF_COMMAND_REPEATS], args, cg.uint8)
313
+ cg.add(var.set_repeats(template_))
314
+
315
+
268
316
  # ByronSX
269
317
  (
270
318
  ByronSXData,
@@ -881,6 +929,49 @@ async def pronto_action(var, config, args):
881
929
  cg.add(var.set_data(template_))
882
930
 
883
931
 
932
+ # Gobox
933
+ (
934
+ GoboxData,
935
+ GoboxBinarySensor,
936
+ GoboxTrigger,
937
+ GoboxAction,
938
+ GoboxDumper,
939
+ ) = declare_protocol("Gobox")
940
+ GOBOX_SCHEMA = cv.Schema(
941
+ {
942
+ cv.Required(CONF_CODE): cv.int_,
943
+ }
944
+ )
945
+
946
+
947
+ @register_binary_sensor("gobox", GoboxBinarySensor, GOBOX_SCHEMA)
948
+ def gobox_binary_sensor(var, config):
949
+ cg.add(
950
+ var.set_data(
951
+ cg.StructInitializer(
952
+ GoboxData,
953
+ ("code", config[CONF_CODE]),
954
+ )
955
+ )
956
+ )
957
+
958
+
959
+ @register_trigger("gobox", GoboxTrigger, GoboxData)
960
+ def gobox_trigger(var, config):
961
+ pass
962
+
963
+
964
+ @register_dumper("gobox", GoboxDumper)
965
+ def gobox_dumper(var, config):
966
+ pass
967
+
968
+
969
+ @register_action("gobox", GoboxAction, GOBOX_SCHEMA)
970
+ async def gobox_action(var, config, args):
971
+ template_ = await cg.templatable(config[CONF_CODE], args, cg.int_)
972
+ cg.add(var.set_code(template_))
973
+
974
+
884
975
  # Roomba
885
976
  (
886
977
  RoombaData,