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
@@ -25,6 +25,10 @@ static const uint16_t SEN5X_CMD_TEMPERATURE_COMPENSATION = 0x60B2;
25
25
  static const uint16_t SEN5X_CMD_VOC_ALGORITHM_STATE = 0x6181;
26
26
  static const uint16_t SEN5X_CMD_VOC_ALGORITHM_TUNING = 0x60D0;
27
27
 
28
+ static const int8_t SEN5X_INDEX_SCALE_FACTOR = 10; // used for VOC and NOx index values
29
+ static const int8_t SEN5X_MIN_INDEX_VALUE = 1 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
30
+ static const int16_t SEN5X_MAX_INDEX_VALUE = 500 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
31
+
28
32
  void SEN5XComponent::setup() {
29
33
  ESP_LOGCONFIG(TAG, "Setting up sen5x...");
30
34
 
@@ -88,8 +92,9 @@ void SEN5XComponent::setup() {
88
92
  product_name_.push_back(current_char);
89
93
  // second char
90
94
  current_char = *current_int & 0xFF;
91
- if (current_char)
95
+ if (current_char) {
92
96
  product_name_.push_back(current_char);
97
+ }
93
98
  }
94
99
  current_int++;
95
100
  } while (current_char && --max);
@@ -271,10 +276,10 @@ void SEN5XComponent::dump_config() {
271
276
  ESP_LOGCONFIG(TAG, " Low RH/T acceleration mode");
272
277
  break;
273
278
  case MEDIUM_ACCELERATION:
274
- ESP_LOGCONFIG(TAG, " Medium RH/T accelertion mode");
279
+ ESP_LOGCONFIG(TAG, " Medium RH/T acceleration mode");
275
280
  break;
276
281
  case HIGH_ACCELERATION:
277
- ESP_LOGCONFIG(TAG, " High RH/T accelertion mode");
282
+ ESP_LOGCONFIG(TAG, " High RH/T acceleration mode");
278
283
  break;
279
284
  }
280
285
  }
@@ -337,47 +342,61 @@ void SEN5XComponent::update() {
337
342
  ESP_LOGD(TAG, "read data error (%d)", this->last_error_);
338
343
  return;
339
344
  }
340
- float pm_1_0 = measurements[0] / 10.0;
341
- if (measurements[0] == 0xFFFF)
342
- pm_1_0 = NAN;
343
- float pm_2_5 = measurements[1] / 10.0;
344
- if (measurements[1] == 0xFFFF)
345
- pm_2_5 = NAN;
346
- float pm_4_0 = measurements[2] / 10.0;
347
- if (measurements[2] == 0xFFFF)
348
- pm_4_0 = NAN;
349
- float pm_10_0 = measurements[3] / 10.0;
350
- if (measurements[3] == 0xFFFF)
351
- pm_10_0 = NAN;
352
- float humidity = measurements[4] / 100.0;
353
- if (measurements[4] == 0xFFFF)
354
- humidity = NAN;
355
- float temperature = (int16_t) measurements[5] / 200.0;
356
- if (measurements[5] == 0xFFFF)
357
- temperature = NAN;
358
- float voc = measurements[6] / 10.0;
359
- if (measurements[6] == 0xFFFF)
360
- voc = NAN;
361
- float nox = measurements[7] / 10.0;
362
- if (measurements[7] == 0xFFFF)
363
- nox = NAN;
364
-
365
- if (this->pm_1_0_sensor_ != nullptr)
345
+
346
+ ESP_LOGVV(TAG, "pm_1_0 = 0x%.4x", measurements[0]);
347
+ float pm_1_0 = measurements[0] == UINT16_MAX ? NAN : measurements[0] / 10.0f;
348
+
349
+ ESP_LOGVV(TAG, "pm_2_5 = 0x%.4x", measurements[1]);
350
+ float pm_2_5 = measurements[1] == UINT16_MAX ? NAN : measurements[1] / 10.0f;
351
+
352
+ ESP_LOGVV(TAG, "pm_4_0 = 0x%.4x", measurements[2]);
353
+ float pm_4_0 = measurements[2] == UINT16_MAX ? NAN : measurements[2] / 10.0f;
354
+
355
+ ESP_LOGVV(TAG, "pm_10_0 = 0x%.4x", measurements[3]);
356
+ float pm_10_0 = measurements[3] == UINT16_MAX ? NAN : measurements[3] / 10.0f;
357
+
358
+ ESP_LOGVV(TAG, "humidity = 0x%.4x", measurements[4]);
359
+ float humidity = measurements[4] == INT16_MAX ? NAN : static_cast<int16_t>(measurements[4]) / 100.0f;
360
+
361
+ ESP_LOGVV(TAG, "temperature = 0x%.4x", measurements[5]);
362
+ float temperature = measurements[5] == INT16_MAX ? NAN : static_cast<int16_t>(measurements[5]) / 200.0f;
363
+
364
+ ESP_LOGVV(TAG, "voc = 0x%.4x", measurements[6]);
365
+ int16_t voc_idx = static_cast<int16_t>(measurements[6]);
366
+ float voc = (voc_idx < SEN5X_MIN_INDEX_VALUE || voc_idx > SEN5X_MAX_INDEX_VALUE)
367
+ ? NAN
368
+ : static_cast<float>(voc_idx) / 10.0f;
369
+
370
+ ESP_LOGVV(TAG, "nox = 0x%.4x", measurements[7]);
371
+ int16_t nox_idx = static_cast<int16_t>(measurements[7]);
372
+ float nox = (nox_idx < SEN5X_MIN_INDEX_VALUE || nox_idx > SEN5X_MAX_INDEX_VALUE)
373
+ ? NAN
374
+ : static_cast<float>(nox_idx) / 10.0f;
375
+
376
+ if (this->pm_1_0_sensor_ != nullptr) {
366
377
  this->pm_1_0_sensor_->publish_state(pm_1_0);
367
- if (this->pm_2_5_sensor_ != nullptr)
378
+ }
379
+ if (this->pm_2_5_sensor_ != nullptr) {
368
380
  this->pm_2_5_sensor_->publish_state(pm_2_5);
369
- if (this->pm_4_0_sensor_ != nullptr)
381
+ }
382
+ if (this->pm_4_0_sensor_ != nullptr) {
370
383
  this->pm_4_0_sensor_->publish_state(pm_4_0);
371
- if (this->pm_10_0_sensor_ != nullptr)
384
+ }
385
+ if (this->pm_10_0_sensor_ != nullptr) {
372
386
  this->pm_10_0_sensor_->publish_state(pm_10_0);
373
- if (this->temperature_sensor_ != nullptr)
387
+ }
388
+ if (this->temperature_sensor_ != nullptr) {
374
389
  this->temperature_sensor_->publish_state(temperature);
375
- if (this->humidity_sensor_ != nullptr)
390
+ }
391
+ if (this->humidity_sensor_ != nullptr) {
376
392
  this->humidity_sensor_->publish_state(humidity);
377
- if (this->voc_sensor_ != nullptr)
393
+ }
394
+ if (this->voc_sensor_ != nullptr) {
378
395
  this->voc_sensor_->publish_state(voc);
379
- if (this->nox_sensor_ != nullptr)
396
+ }
397
+ if (this->nox_sensor_ != nullptr) {
380
398
  this->nox_sensor_->publish_state(nox);
399
+ }
381
400
  this->status_clear_warning();
382
401
  });
383
402
  }
@@ -4,6 +4,7 @@ import esphome.codegen as cg
4
4
  from esphome.components import i2c, sensirion_common, sensor
5
5
  import esphome.config_validation as cv
6
6
  from esphome.const import (
7
+ CONF_GAIN_FACTOR,
7
8
  CONF_HUMIDITY,
8
9
  CONF_ID,
9
10
  CONF_OFFSET,
@@ -43,7 +44,6 @@ RhtAccelerationMode = sen5x_ns.enum("RhtAccelerationMode")
43
44
  CONF_ACCELERATION_MODE = "acceleration_mode"
44
45
  CONF_ALGORITHM_TUNING = "algorithm_tuning"
45
46
  CONF_AUTO_CLEANING_INTERVAL = "auto_cleaning_interval"
46
- CONF_GAIN_FACTOR = "gain_factor"
47
47
  CONF_GATING_MAX_DURATION_MINUTES = "gating_max_duration_minutes"
48
48
  CONF_INDEX_OFFSET = "index_offset"
49
49
  CONF_LEARNING_TIME_GAIN_HOURS = "learning_time_gain_hours"
@@ -38,7 +38,7 @@ CONFIG_SCHEMA = (
38
38
  cv.Schema(
39
39
  {
40
40
  cv.GenerateID(): cv.declare_id(SenseAirComponent),
41
- cv.Required(CONF_CO2): sensor.sensor_schema(
41
+ cv.Optional(CONF_CO2): sensor.sensor_schema(
42
42
  unit_of_measurement=UNIT_PARTS_PER_MILLION,
43
43
  icon=ICON_MOLECULE_CO2,
44
44
  accuracy_decimals=0,
@@ -57,8 +57,8 @@ async def to_code(config):
57
57
  await cg.register_component(var, config)
58
58
  await uart.register_uart_device(var, config)
59
59
 
60
- if CONF_CO2 in config:
61
- sens = await sensor.new_sensor(config[CONF_CO2])
60
+ if co2 := config.get(CONF_CO2):
61
+ sens = await sensor.new_sensor(co2)
62
62
  cg.add(var.set_co2_sensor(sens))
63
63
 
64
64
 
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import math
2
3
 
3
4
  from esphome import automation
@@ -9,6 +10,7 @@ from esphome.const import (
9
10
  CONF_ACCURACY_DECIMALS,
10
11
  CONF_ALPHA,
11
12
  CONF_BELOW,
13
+ CONF_CALIBRATION,
12
14
  CONF_DEVICE_CLASS,
13
15
  CONF_ENTITY_CATEGORY,
14
16
  CONF_EXPIRE_AFTER,
@@ -30,6 +32,7 @@ from esphome.const import (
30
32
  CONF_SEND_EVERY,
31
33
  CONF_SEND_FIRST_AT,
32
34
  CONF_STATE_CLASS,
35
+ CONF_TEMPERATURE,
33
36
  CONF_TIMEOUT,
34
37
  CONF_TO,
35
38
  CONF_TRIGGER_ID,
@@ -153,6 +156,8 @@ DEVICE_CLASSES = [
153
156
  DEVICE_CLASS_WIND_SPEED,
154
157
  ]
155
158
 
159
+ _LOGGER = logging.getLogger(__name__)
160
+
156
161
  sensor_ns = cg.esphome_ns.namespace("sensor")
157
162
  StateClasses = sensor_ns.enum("StateClass")
158
163
  STATE_CLASSES = {
@@ -246,6 +251,8 @@ HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component)
246
251
  DeltaFilter = sensor_ns.class_("DeltaFilter", Filter)
247
252
  OrFilter = sensor_ns.class_("OrFilter", Filter)
248
253
  CalibrateLinearFilter = sensor_ns.class_("CalibrateLinearFilter", Filter)
254
+ ToNTCResistanceFilter = sensor_ns.class_("ToNTCResistanceFilter", Filter)
255
+ ToNTCTemperatureFilter = sensor_ns.class_("ToNTCTemperatureFilter", Filter)
249
256
  CalibratePolynomialFilter = sensor_ns.class_("CalibratePolynomialFilter", Filter)
250
257
  SensorInRangeCondition = sensor_ns.class_("SensorInRangeCondition", Filter)
251
258
  ClampFilter = sensor_ns.class_("ClampFilter", Filter)
@@ -257,7 +264,7 @@ validate_accuracy_decimals = cv.int_
257
264
  validate_icon = cv.icon
258
265
  validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
259
266
 
260
- SENSOR_SCHEMA = (
267
+ _SENSOR_SCHEMA = (
261
268
  cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
262
269
  .extend(cv.MQTT_COMPONENT_SCHEMA)
263
270
  .extend(
@@ -302,22 +309,20 @@ SENSOR_SCHEMA = (
302
309
  )
303
310
  )
304
311
 
305
- _UNDEF = object()
306
-
307
312
 
308
313
  def sensor_schema(
309
- class_: MockObjClass = _UNDEF,
314
+ class_: MockObjClass = cv.UNDEFINED,
310
315
  *,
311
- unit_of_measurement: str = _UNDEF,
312
- icon: str = _UNDEF,
313
- accuracy_decimals: int = _UNDEF,
314
- device_class: str = _UNDEF,
315
- state_class: str = _UNDEF,
316
- entity_category: str = _UNDEF,
316
+ unit_of_measurement: str = cv.UNDEFINED,
317
+ icon: str = cv.UNDEFINED,
318
+ accuracy_decimals: int = cv.UNDEFINED,
319
+ device_class: str = cv.UNDEFINED,
320
+ state_class: str = cv.UNDEFINED,
321
+ entity_category: str = cv.UNDEFINED,
317
322
  ) -> cv.Schema:
318
323
  schema = {}
319
324
 
320
- if class_ is not _UNDEF:
325
+ if class_ is not cv.UNDEFINED:
321
326
  # Not optional.
322
327
  schema[cv.GenerateID()] = cv.declare_id(class_)
323
328
 
@@ -329,10 +334,15 @@ def sensor_schema(
329
334
  (CONF_STATE_CLASS, state_class, validate_state_class),
330
335
  (CONF_ENTITY_CATEGORY, entity_category, sensor_entity_category),
331
336
  ]:
332
- if default is not _UNDEF:
337
+ if default is not cv.UNDEFINED:
333
338
  schema[cv.Optional(key, default=default)] = validator
334
339
 
335
- return SENSOR_SCHEMA.extend(schema)
340
+ return _SENSOR_SCHEMA.extend(schema)
341
+
342
+
343
+ # Remove before 2025.11.0
344
+ SENSOR_SCHEMA = sensor_schema()
345
+ SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("sensor"))
336
346
 
337
347
 
338
348
  @FILTER_REGISTRY.register("offset", OffsetFilter, cv.templatable(cv.float_))
@@ -804,7 +814,9 @@ async def setup_sensor_core_(var, config):
804
814
  mqtt_ = cg.new_Pvariable(mqtt_id, var)
805
815
  await mqtt.register_mqtt_component(mqtt_, config)
806
816
 
807
- if (expire_after := config.get(CONF_EXPIRE_AFTER, _UNDEF)) is not _UNDEF:
817
+ if (
818
+ expire_after := config.get(CONF_EXPIRE_AFTER, cv.UNDEFINED)
819
+ ) is not cv.UNDEFINED:
808
820
  if expire_after is None:
809
821
  cg.add(mqtt_.disable_expire_after())
810
822
  else:
@@ -852,6 +864,138 @@ async def sensor_in_range_to_code(config, condition_id, template_arg, args):
852
864
  return var
853
865
 
854
866
 
867
+ def validate_ntc_calibration_parameter(value):
868
+ if isinstance(value, dict):
869
+ return cv.Schema(
870
+ {
871
+ cv.Required(CONF_TEMPERATURE): cv.temperature,
872
+ cv.Required(CONF_VALUE): cv.resistance,
873
+ }
874
+ )(value)
875
+
876
+ value = cv.string(value)
877
+ parts = value.split("->")
878
+ if len(parts) != 2:
879
+ raise cv.Invalid("Calibration parameter must be of form 3000 -> 23°C")
880
+ resistance = cv.resistance(parts[0].strip())
881
+ temperature = cv.temperature(parts[1].strip())
882
+ return validate_ntc_calibration_parameter(
883
+ {
884
+ CONF_TEMPERATURE: temperature,
885
+ CONF_VALUE: resistance,
886
+ }
887
+ )
888
+
889
+
890
+ CONF_A = "a"
891
+ CONF_B = "b"
892
+ CONF_C = "c"
893
+ ZERO_POINT = 273.15
894
+
895
+
896
+ def ntc_calc_steinhart_hart(value):
897
+ r1 = value[0][CONF_VALUE]
898
+ r2 = value[1][CONF_VALUE]
899
+ r3 = value[2][CONF_VALUE]
900
+ t1 = value[0][CONF_TEMPERATURE] + ZERO_POINT
901
+ t2 = value[1][CONF_TEMPERATURE] + ZERO_POINT
902
+ t3 = value[2][CONF_TEMPERATURE] + ZERO_POINT
903
+
904
+ l1 = math.log(r1)
905
+ l2 = math.log(r2)
906
+ l3 = math.log(r3)
907
+
908
+ y1 = 1 / t1
909
+ y2 = 1 / t2
910
+ y3 = 1 / t3
911
+
912
+ g2 = (y2 - y1) / (l2 - l1)
913
+ g3 = (y3 - y1) / (l3 - l1)
914
+
915
+ c = (g3 - g2) / (l3 - l2) * 1 / (l1 + l2 + l3)
916
+ b = g2 - c * (l1 * l1 + l1 * l2 + l2 * l2)
917
+ a = y1 - (b + l1 * l1 * c) * l1
918
+ return a, b, c
919
+
920
+
921
+ def ntc_get_abc(value):
922
+ a = value[CONF_A]
923
+ b = value[CONF_B]
924
+ c = value[CONF_C]
925
+ return a, b, c
926
+
927
+
928
+ def ntc_process_calibration(value):
929
+ if isinstance(value, dict):
930
+ value = cv.Schema(
931
+ {
932
+ cv.Required(CONF_A): cv.float_,
933
+ cv.Required(CONF_B): cv.float_,
934
+ cv.Required(CONF_C): cv.float_,
935
+ }
936
+ )(value)
937
+ a, b, c = ntc_get_abc(value)
938
+ elif isinstance(value, list):
939
+ if len(value) != 3:
940
+ raise cv.Invalid(
941
+ "Steinhart–Hart Calibration must consist of exactly three values"
942
+ )
943
+ value = cv.Schema([validate_ntc_calibration_parameter])(value)
944
+ a, b, c = ntc_calc_steinhart_hart(value)
945
+ else:
946
+ raise cv.Invalid(
947
+ f"Calibration parameter accepts either a list for steinhart-hart calibration, or mapping for b-constant calibration, not {type(value)}"
948
+ )
949
+ _LOGGER.info("Coefficient: a:%s, b:%s, c:%s", a, b, c)
950
+ return {
951
+ CONF_A: a,
952
+ CONF_B: b,
953
+ CONF_C: c,
954
+ }
955
+
956
+
957
+ @FILTER_REGISTRY.register(
958
+ "to_ntc_resistance",
959
+ ToNTCResistanceFilter,
960
+ cv.All(
961
+ cv.Schema(
962
+ {
963
+ cv.Required(CONF_CALIBRATION): ntc_process_calibration,
964
+ }
965
+ ),
966
+ ),
967
+ )
968
+ async def calibrate_ntc_resistance_filter_to_code(config, filter_id):
969
+ calib = config[CONF_CALIBRATION]
970
+ return cg.new_Pvariable(
971
+ filter_id,
972
+ calib[CONF_A],
973
+ calib[CONF_B],
974
+ calib[CONF_C],
975
+ )
976
+
977
+
978
+ @FILTER_REGISTRY.register(
979
+ "to_ntc_temperature",
980
+ ToNTCTemperatureFilter,
981
+ cv.All(
982
+ cv.Schema(
983
+ {
984
+ cv.Required(CONF_CALIBRATION): ntc_process_calibration,
985
+ }
986
+ ),
987
+ ),
988
+ )
989
+ async def calibrate_ntc_temperature_filter_to_code(config, filter_id):
990
+ calib = config[CONF_CALIBRATION]
991
+ return cg.new_Pvariable(
992
+ filter_id,
993
+ calib[CONF_A],
994
+ calib[CONF_B],
995
+ calib[CONF_C],
996
+ )
997
+
998
+
855
999
  def _mean(xs):
856
1000
  return sum(xs) / len(xs)
857
1001
 
@@ -481,5 +481,28 @@ optional<float> RoundMultipleFilter::new_value(float value) {
481
481
  return value;
482
482
  }
483
483
 
484
+ optional<float> ToNTCResistanceFilter::new_value(float value) {
485
+ if (!std::isfinite(value)) {
486
+ return NAN;
487
+ }
488
+ double k = 273.15;
489
+ // https://de.wikipedia.org/wiki/Steinhart-Hart-Gleichung#cite_note-stein2_s4-3
490
+ double t = value + k;
491
+ double y = (this->a_ - 1 / (t)) / (2 * this->c_);
492
+ double x = sqrt(pow(this->b_ / (3 * this->c_), 3) + y * y);
493
+ double resistance = exp(pow(x - y, 1 / 3.0) - pow(x + y, 1 / 3.0));
494
+ return resistance;
495
+ }
496
+
497
+ optional<float> ToNTCTemperatureFilter::new_value(float value) {
498
+ if (!std::isfinite(value)) {
499
+ return NAN;
500
+ }
501
+ double lr = log(double(value));
502
+ double v = this->a_ + this->b_ * lr + this->c_ * lr * lr * lr;
503
+ double temp = float(1.0 / v - 273.15);
504
+ return temp;
505
+ }
506
+
484
507
  } // namespace sensor
485
508
  } // namespace esphome
@@ -439,5 +439,27 @@ class RoundMultipleFilter : public Filter {
439
439
  float multiple_;
440
440
  };
441
441
 
442
+ class ToNTCResistanceFilter : public Filter {
443
+ public:
444
+ ToNTCResistanceFilter(double a, double b, double c) : a_(a), b_(b), c_(c) {}
445
+ optional<float> new_value(float value) override;
446
+
447
+ protected:
448
+ double a_;
449
+ double b_;
450
+ double c_;
451
+ };
452
+
453
+ class ToNTCTemperatureFilter : public Filter {
454
+ public:
455
+ ToNTCTemperatureFilter(double a, double b, double c) : a_(a), b_(b), c_(c) {}
456
+ optional<float> new_value(float value) override;
457
+
458
+ protected:
459
+ double a_;
460
+ double b_;
461
+ double c_;
462
+ };
463
+
442
464
  } // namespace sensor
443
465
  } // namespace esphome
@@ -37,14 +37,14 @@ CONFIG_SCHEMA = (
37
37
  cv.Schema(
38
38
  {
39
39
  cv.GenerateID(): cv.declare_id(SGP30Component),
40
- cv.Required(CONF_ECO2): sensor.sensor_schema(
40
+ cv.Optional(CONF_ECO2): sensor.sensor_schema(
41
41
  unit_of_measurement=UNIT_PARTS_PER_MILLION,
42
42
  icon=ICON_MOLECULE_CO2,
43
43
  accuracy_decimals=0,
44
44
  device_class=DEVICE_CLASS_CARBON_DIOXIDE,
45
45
  state_class=STATE_CLASS_MEASUREMENT,
46
46
  ),
47
- cv.Required(CONF_TVOC): sensor.sensor_schema(
47
+ cv.Optional(CONF_TVOC): sensor.sensor_schema(
48
48
  unit_of_measurement=UNIT_PARTS_PER_BILLION,
49
49
  icon=ICON_RADIATOR,
50
50
  accuracy_decimals=0,
@@ -86,32 +86,30 @@ async def to_code(config):
86
86
  await cg.register_component(var, config)
87
87
  await i2c.register_i2c_device(var, config)
88
88
 
89
- if CONF_ECO2 in config:
90
- sens = await sensor.new_sensor(config[CONF_ECO2])
89
+ if eco2_config := config.get(CONF_ECO2):
90
+ sens = await sensor.new_sensor(eco2_config)
91
91
  cg.add(var.set_eco2_sensor(sens))
92
92
 
93
- if CONF_TVOC in config:
94
- sens = await sensor.new_sensor(config[CONF_TVOC])
93
+ if tvoc_config := config.get(CONF_TVOC):
94
+ sens = await sensor.new_sensor(tvoc_config)
95
95
  cg.add(var.set_tvoc_sensor(sens))
96
96
 
97
- if CONF_ECO2_BASELINE in config:
98
- sens = await sensor.new_sensor(config[CONF_ECO2_BASELINE])
97
+ if eco2_baseline_config := config.get(CONF_ECO2_BASELINE):
98
+ sens = await sensor.new_sensor(eco2_baseline_config)
99
99
  cg.add(var.set_eco2_baseline_sensor(sens))
100
100
 
101
- if CONF_TVOC_BASELINE in config:
102
- sens = await sensor.new_sensor(config[CONF_TVOC_BASELINE])
101
+ if tvoc_baseline_config := config.get(CONF_TVOC_BASELINE):
102
+ sens = await sensor.new_sensor(tvoc_baseline_config)
103
103
  cg.add(var.set_tvoc_baseline_sensor(sens))
104
104
 
105
- if CONF_STORE_BASELINE in config:
106
- cg.add(var.set_store_baseline(config[CONF_STORE_BASELINE]))
105
+ if (store_baseline := config.get(CONF_STORE_BASELINE)) is not None:
106
+ cg.add(var.set_store_baseline(store_baseline))
107
107
 
108
- if CONF_BASELINE in config:
109
- baseline_config = config[CONF_BASELINE]
108
+ if baseline_config := config.get(CONF_BASELINE):
110
109
  cg.add(var.set_eco2_baseline(baseline_config[CONF_ECO2_BASELINE]))
111
110
  cg.add(var.set_tvoc_baseline(baseline_config[CONF_TVOC_BASELINE]))
112
111
 
113
- if CONF_COMPENSATION in config:
114
- compensation_config = config[CONF_COMPENSATION]
112
+ if compensation_config := config.get(CONF_COMPENSATION):
115
113
  sens = await cg.get_variable(compensation_config[CONF_HUMIDITY_SOURCE])
116
114
  cg.add(var.set_humidity_sensor(sens))
117
115
  sens = await cg.get_variable(compensation_config[CONF_TEMPERATURE_SOURCE])
@@ -3,6 +3,7 @@ from esphome.components import i2c, sensirion_common, sensor
3
3
  import esphome.config_validation as cv
4
4
  from esphome.const import (
5
5
  CONF_COMPENSATION,
6
+ CONF_GAIN_FACTOR,
6
7
  CONF_ID,
7
8
  CONF_STORE_BASELINE,
8
9
  CONF_TEMPERATURE_SOURCE,
@@ -24,7 +25,6 @@ SGP4xComponent = sgp4x_ns.class_(
24
25
  )
25
26
 
26
27
  CONF_ALGORITHM_TUNING = "algorithm_tuning"
27
- CONF_GAIN_FACTOR = "gain_factor"
28
28
  CONF_GATING_MAX_DURATION_MINUTES = "gating_max_duration_minutes"
29
29
  CONF_HUMIDITY_SOURCE = "humidity_source"
30
30
  CONF_INDEX_OFFSET = "index_offset"
@@ -12,14 +12,22 @@ void SHT4XComponent::start_heater_() {
12
12
  uint8_t cmd[] = {MEASURECOMMANDS[this->heater_command_]};
13
13
 
14
14
  ESP_LOGD(TAG, "Heater turning on");
15
- this->write(cmd, 1);
15
+ if (this->write(cmd, 1) != i2c::ERROR_OK) {
16
+ this->status_set_error("Failed to turn on heater");
17
+ }
16
18
  }
17
19
 
18
20
  void SHT4XComponent::setup() {
19
21
  ESP_LOGCONFIG(TAG, "Setting up sht4x...");
20
22
 
21
- if (this->duty_cycle_ > 0.0) {
22
- uint32_t heater_interval = (uint32_t) (this->heater_time_ / this->duty_cycle_);
23
+ auto err = this->write(nullptr, 0);
24
+ if (err != i2c::ERROR_OK) {
25
+ this->mark_failed();
26
+ return;
27
+ }
28
+
29
+ if (std::isfinite(this->duty_cycle_) && this->duty_cycle_ > 0.0f) {
30
+ uint32_t heater_interval = static_cast<uint32_t>(static_cast<uint16_t>(this->heater_time_) / this->duty_cycle_);
23
31
  ESP_LOGD(TAG, "Heater interval: %" PRIu32, heater_interval);
24
32
 
25
33
  if (this->heater_power_ == SHT4X_HEATERPOWER_HIGH) {
@@ -47,37 +55,50 @@ void SHT4XComponent::setup() {
47
55
  }
48
56
  }
49
57
 
50
- void SHT4XComponent::dump_config() { LOG_I2C_DEVICE(this); }
58
+ void SHT4XComponent::dump_config() {
59
+ ESP_LOGCONFIG(TAG, "SHT4x:");
60
+ LOG_I2C_DEVICE(this);
61
+ if (this->is_failed()) {
62
+ ESP_LOGE(TAG, "Communication with SHT4x failed!");
63
+ }
64
+ }
51
65
 
52
66
  void SHT4XComponent::update() {
53
67
  // Send command
54
- this->write_command(MEASURECOMMANDS[this->precision_]);
68
+ if (!this->write_command(MEASURECOMMANDS[this->precision_])) {
69
+ // Warning will be printed only if warning status is not set yet
70
+ this->status_set_warning("Failed to send measurement command");
71
+ return;
72
+ }
55
73
 
56
74
  this->set_timeout(10, [this]() {
57
75
  uint16_t buffer[2];
58
76
 
59
77
  // Read measurement
60
- bool read_status = this->read_data(buffer, 2);
78
+ if (!this->read_data(buffer, 2)) {
79
+ // Using ESP_LOGW to force the warning to be printed
80
+ ESP_LOGW(TAG, "Sensor read failed");
81
+ this->status_set_warning();
82
+ return;
83
+ }
61
84
 
62
- if (read_status) {
63
- // Evaluate and publish measurements
64
- if (this->temp_sensor_ != nullptr) {
65
- // Temp is contained in the first result word
66
- float sensor_value_temp = buffer[0];
67
- float temp = -45 + 175 * sensor_value_temp / 65535;
85
+ this->status_clear_warning();
68
86
 
69
- this->temp_sensor_->publish_state(temp);
70
- }
87
+ // Evaluate and publish measurements
88
+ if (this->temp_sensor_ != nullptr) {
89
+ // Temp is contained in the first result word
90
+ float sensor_value_temp = buffer[0];
91
+ float temp = -45 + 175 * sensor_value_temp / 65535;
92
+
93
+ this->temp_sensor_->publish_state(temp);
94
+ }
71
95
 
72
- if (this->humidity_sensor_ != nullptr) {
73
- // Relative humidity is in the second result word
74
- float sensor_value_rh = buffer[1];
75
- float rh = -6 + 125 * sensor_value_rh / 65535;
96
+ if (this->humidity_sensor_ != nullptr) {
97
+ // Relative humidity is in the second result word
98
+ float sensor_value_rh = buffer[1];
99
+ float rh = -6 + 125 * sensor_value_rh / 65535;
76
100
 
77
- this->humidity_sensor_->publish_state(rh);
78
- }
79
- } else {
80
- ESP_LOGD(TAG, "Sensor read failed");
101
+ this->humidity_sensor_->publish_state(rh);
81
102
  }
82
103
  });
83
104
  }
@@ -13,7 +13,7 @@ enum SHT4XPRECISION { SHT4X_PRECISION_HIGH = 0, SHT4X_PRECISION_MED, SHT4X_PRECI
13
13
 
14
14
  enum SHT4XHEATERPOWER { SHT4X_HEATERPOWER_HIGH, SHT4X_HEATERPOWER_MED, SHT4X_HEATERPOWER_LOW };
15
15
 
16
- enum SHT4XHEATERTIME { SHT4X_HEATERTIME_LONG = 1100, SHT4X_HEATERTIME_SHORT = 110 };
16
+ enum SHT4XHEATERTIME : uint16_t { SHT4X_HEATERTIME_LONG = 1100, SHT4X_HEATERTIME_SHORT = 110 };
17
17
 
18
18
  class SHT4XComponent : public PollingComponent, public sensirion_common::SensirionI2CDevice {
19
19
  public: