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
@@ -7,7 +7,6 @@ from esphome.const import (
7
7
  CONF_CLOSE_ACTION,
8
8
  CONF_CLOSE_DURATION,
9
9
  CONF_CLOSE_ENDSTOP,
10
- CONF_ID,
11
10
  CONF_MAX_DURATION,
12
11
  CONF_OPEN_ACTION,
13
12
  CONF_OPEN_DURATION,
@@ -50,36 +49,43 @@ def validate_infer_endstop(config):
50
49
  return config
51
50
 
52
51
 
53
- CONFIG_FEEDBACK_COVER_BASE_SCHEMA = cover.COVER_SCHEMA.extend(
54
- {
55
- cv.GenerateID(): cv.declare_id(FeedbackCover),
56
- cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
57
- cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
58
- cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
59
- cv.Optional(CONF_OPEN_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
60
- cv.Optional(CONF_OPEN_SENSOR): cv.use_id(binary_sensor.BinarySensor),
61
- cv.Optional(CONF_OPEN_OBSTACLE_SENSOR): cv.use_id(binary_sensor.BinarySensor),
62
- cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
63
- cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
64
- cv.Optional(CONF_CLOSE_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
65
- cv.Optional(CONF_CLOSE_SENSOR): cv.use_id(binary_sensor.BinarySensor),
66
- cv.Optional(CONF_CLOSE_OBSTACLE_SENSOR): cv.use_id(binary_sensor.BinarySensor),
67
- cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds,
68
- cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean,
69
- cv.Optional(CONF_ASSUMED_STATE): cv.boolean,
70
- cv.Optional(
71
- CONF_UPDATE_INTERVAL, "1000ms"
72
- ): cv.positive_time_period_milliseconds,
73
- cv.Optional(CONF_INFER_ENDSTOP_FROM_MOVEMENT, False): cv.boolean,
74
- cv.Optional(
75
- CONF_DIRECTION_CHANGE_WAIT_TIME
76
- ): cv.positive_time_period_milliseconds,
77
- cv.Optional(
78
- CONF_ACCELERATION_WAIT_TIME, "0s"
79
- ): cv.positive_time_period_milliseconds,
80
- cv.Optional(CONF_OBSTACLE_ROLLBACK, default="10%"): cv.percentage,
81
- },
82
- ).extend(cv.COMPONENT_SCHEMA)
52
+ CONFIG_FEEDBACK_COVER_BASE_SCHEMA = (
53
+ cover.cover_schema(FeedbackCover)
54
+ .extend(
55
+ {
56
+ cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
57
+ cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
58
+ cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
59
+ cv.Optional(CONF_OPEN_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
60
+ cv.Optional(CONF_OPEN_SENSOR): cv.use_id(binary_sensor.BinarySensor),
61
+ cv.Optional(CONF_OPEN_OBSTACLE_SENSOR): cv.use_id(
62
+ binary_sensor.BinarySensor
63
+ ),
64
+ cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
65
+ cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
66
+ cv.Optional(CONF_CLOSE_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
67
+ cv.Optional(CONF_CLOSE_SENSOR): cv.use_id(binary_sensor.BinarySensor),
68
+ cv.Optional(CONF_CLOSE_OBSTACLE_SENSOR): cv.use_id(
69
+ binary_sensor.BinarySensor
70
+ ),
71
+ cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds,
72
+ cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean,
73
+ cv.Optional(CONF_ASSUMED_STATE): cv.boolean,
74
+ cv.Optional(
75
+ CONF_UPDATE_INTERVAL, "1000ms"
76
+ ): cv.positive_time_period_milliseconds,
77
+ cv.Optional(CONF_INFER_ENDSTOP_FROM_MOVEMENT, False): cv.boolean,
78
+ cv.Optional(
79
+ CONF_DIRECTION_CHANGE_WAIT_TIME
80
+ ): cv.positive_time_period_milliseconds,
81
+ cv.Optional(
82
+ CONF_ACCELERATION_WAIT_TIME, "0s"
83
+ ): cv.positive_time_period_milliseconds,
84
+ cv.Optional(CONF_OBSTACLE_ROLLBACK, default="10%"): cv.percentage,
85
+ },
86
+ )
87
+ .extend(cv.COMPONENT_SCHEMA)
88
+ )
83
89
 
84
90
 
85
91
  CONFIG_SCHEMA = cv.All(
@@ -90,9 +96,8 @@ CONFIG_SCHEMA = cv.All(
90
96
 
91
97
 
92
98
  async def to_code(config):
93
- var = cg.new_Pvariable(config[CONF_ID])
99
+ var = await cover.new_cover(config)
94
100
  await cg.register_component(var, config)
95
- await cover.register_cover(var, config)
96
101
 
97
102
  # STOP
98
103
  await automation.build_automation(
@@ -1,6 +1,7 @@
1
1
  #include "feedback_cover.h"
2
2
  #include "esphome/core/hal.h"
3
3
  #include "esphome/core/log.h"
4
+ #include "esphome/core/application.h"
4
5
 
5
6
  namespace esphome {
6
7
  namespace feedback {
@@ -220,7 +221,7 @@ void FeedbackCover::set_open_obstacle_sensor(binary_sensor::BinarySensor *open_o
220
221
  void FeedbackCover::loop() {
221
222
  if (this->current_operation == COVER_OPERATION_IDLE)
222
223
  return;
223
- const uint32_t now = millis();
224
+ const uint32_t now = App.get_loop_component_start_time();
224
225
 
225
226
  // Recompute position every loop cycle
226
227
  this->recompute_position_();
@@ -1,7 +1,5 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import climate_ir
3
- import esphome.config_validation as cv
4
- from esphome.const import CONF_ID
5
3
 
6
4
  AUTO_LOAD = ["climate_ir"]
7
5
 
@@ -10,13 +8,8 @@ FujitsuGeneralClimate = fujitsu_general_ns.class_(
10
8
  "FujitsuGeneralClimate", climate_ir.ClimateIR
11
9
  )
12
10
 
13
- CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
14
- {
15
- cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate),
16
- }
17
- )
11
+ CONFIG_SCHEMA = climate_ir.climate_ir_with_receiver_schema(FujitsuGeneralClimate)
18
12
 
19
13
 
20
14
  async def to_code(config):
21
- var = cg.new_Pvariable(config[CONF_ID])
22
- await climate_ir.register_climate_ir(var, config)
15
+ await climate_ir.new_climate_ir(config)
@@ -6,6 +6,7 @@
6
6
  */
7
7
  #include "gcja5.h"
8
8
  #include "esphome/core/log.h"
9
+ #include "esphome/core/application.h"
9
10
  #include <cstring>
10
11
 
11
12
  namespace esphome {
@@ -16,7 +17,7 @@ static const char *const TAG = "gcja5";
16
17
  void GCJA5Component::setup() { ESP_LOGCONFIG(TAG, "Setting up gcja5..."); }
17
18
 
18
19
  void GCJA5Component::loop() {
19
- const uint32_t now = millis();
20
+ const uint32_t now = App.get_loop_component_start_time();
20
21
  if (now - this->last_transmission_ >= 500) {
21
22
  // last transmission too long ago. Reset RX index.
22
23
  this->rx_message_.clear();
@@ -10,8 +10,10 @@ static const char *const TAG = "gpio.one_wire";
10
10
  void GPIOOneWireBus::setup() {
11
11
  ESP_LOGCONFIG(TAG, "Setting up 1-wire bus...");
12
12
  this->t_pin_->setup();
13
- // clear bus with 480µs high, otherwise initial reset in search might fail
14
13
  this->t_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
14
+ // clear bus with 480µs high, otherwise initial reset in search might fail
15
+ this->pin_.digital_write(true);
16
+ this->pin_.pin_mode(gpio::FLAG_OUTPUT);
15
17
  delayMicroseconds(480);
16
18
  this->search();
17
19
  }
@@ -22,40 +24,49 @@ void GPIOOneWireBus::dump_config() {
22
24
  this->dump_devices_(TAG);
23
25
  }
24
26
 
25
- bool HOT IRAM_ATTR GPIOOneWireBus::reset() {
27
+ int HOT IRAM_ATTR GPIOOneWireBus::reset_int() {
28
+ InterruptLock lock;
26
29
  // See reset here:
27
30
  // https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
28
31
  // Wait for communication to clear (delay G)
29
- pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
32
+ this->pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
30
33
  uint8_t retries = 125;
31
34
  do {
32
35
  if (--retries == 0)
33
- return false;
36
+ return -1;
34
37
  delayMicroseconds(2);
35
- } while (!pin_.digital_read());
38
+ } while (!this->pin_.digital_read());
36
39
 
37
- bool r;
40
+ bool r = false;
38
41
 
39
42
  // Send 480µs LOW TX reset pulse (drive bus low, delay H)
40
- pin_.pin_mode(gpio::FLAG_OUTPUT);
41
- pin_.digital_write(false);
43
+ this->pin_.digital_write(false);
44
+ this->pin_.pin_mode(gpio::FLAG_OUTPUT);
42
45
  delayMicroseconds(480);
43
46
 
44
47
  // Release the bus, delay I
45
- pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
46
- delayMicroseconds(70);
48
+ this->pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
49
+ uint32_t start = micros();
50
+ delayMicroseconds(30);
51
+
52
+ while (micros() - start < 300) {
53
+ // sample bus, 0=device(s) present, 1=no device present
54
+ r = !this->pin_.digital_read();
55
+ if (r)
56
+ break;
57
+ delayMicroseconds(1);
58
+ }
47
59
 
48
- // sample bus, 0=device(s) present, 1=no device present
49
- r = !pin_.digital_read();
50
60
  // delay J
51
- delayMicroseconds(410);
52
- return r;
61
+ delayMicroseconds(start + 480 - micros());
62
+ this->pin_.digital_write(true);
63
+ this->pin_.pin_mode(gpio::FLAG_OUTPUT);
64
+ return r ? 1 : 0;
53
65
  }
54
66
 
55
67
  void HOT IRAM_ATTR GPIOOneWireBus::write_bit_(bool bit) {
56
68
  // drive bus low
57
- pin_.pin_mode(gpio::FLAG_OUTPUT);
58
- pin_.digital_write(false);
69
+ this->pin_.digital_write(false);
59
70
 
60
71
  // from datasheet:
61
72
  // write 0 low time: t_low0: min=60µs, max=120µs
@@ -64,72 +75,62 @@ void HOT IRAM_ATTR GPIOOneWireBus::write_bit_(bool bit) {
64
75
  // recovery time: t_rec: min=1µs
65
76
  // ds18b20 appears to read the bus after roughly 14µs
66
77
  uint32_t delay0 = bit ? 6 : 60;
67
- uint32_t delay1 = bit ? 59 : 5;
78
+ uint32_t delay1 = bit ? 64 : 10;
68
79
 
69
80
  // delay A/C
70
81
  delayMicroseconds(delay0);
71
82
  // release bus
72
- pin_.digital_write(true);
83
+ this->pin_.digital_write(true);
73
84
  // delay B/D
74
85
  delayMicroseconds(delay1);
75
86
  }
76
87
 
77
88
  bool HOT IRAM_ATTR GPIOOneWireBus::read_bit_() {
78
89
  // drive bus low
79
- pin_.pin_mode(gpio::FLAG_OUTPUT);
80
- pin_.digital_write(false);
90
+ this->pin_.digital_write(false);
81
91
 
82
- // note: for reading we'll need very accurate timing, as the
83
- // timing for the digital_read() is tight; according to the datasheet,
84
- // we should read at the end of 16µs starting from the bus low
85
- // typically, the ds18b20 pulls the line high after 11µs for a logical 1
86
- // and 29µs for a logical 0
87
-
88
- uint32_t start = micros();
89
- // datasheet says >1µs
90
- delayMicroseconds(2);
92
+ // datasheet says >= 1µs
93
+ delayMicroseconds(5);
91
94
 
92
95
  // release bus, delay E
93
- pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
94
-
95
- // measure from start value directly, to get best accurate timing no matter
96
- // how long pin_mode/delayMicroseconds took
97
- uint32_t now = micros();
98
- if (now - start < 12)
99
- delayMicroseconds(12 - (now - start));
96
+ this->pin_.pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
100
97
 
98
+ delayMicroseconds(8);
101
99
  // sample bus to read bit from peer
102
- bool r = pin_.digital_read();
100
+ bool r = this->pin_.digital_read();
103
101
 
104
- // read slot is at least 60µs; get as close to 60µs to spend less time with interrupts locked
105
- now = micros();
106
- if (now - start < 60)
107
- delayMicroseconds(60 - (now - start));
102
+ // read slot is at least 60µs
103
+ delayMicroseconds(50);
108
104
 
105
+ this->pin_.digital_write(true);
106
+ this->pin_.pin_mode(gpio::FLAG_OUTPUT);
109
107
  return r;
110
108
  }
111
109
 
112
110
  void IRAM_ATTR GPIOOneWireBus::write8(uint8_t val) {
111
+ InterruptLock lock;
113
112
  for (uint8_t i = 0; i < 8; i++) {
114
113
  this->write_bit_(bool((1u << i) & val));
115
114
  }
116
115
  }
117
116
 
118
117
  void IRAM_ATTR GPIOOneWireBus::write64(uint64_t val) {
118
+ InterruptLock lock;
119
119
  for (uint8_t i = 0; i < 64; i++) {
120
120
  this->write_bit_(bool((1ULL << i) & val));
121
121
  }
122
122
  }
123
123
 
124
124
  uint8_t IRAM_ATTR GPIOOneWireBus::read8() {
125
+ InterruptLock lock;
125
126
  uint8_t ret = 0;
126
- for (uint8_t i = 0; i < 8; i++) {
127
+ for (uint8_t i = 0; i < 8; i++)
127
128
  ret |= (uint8_t(this->read_bit_()) << i);
128
- }
129
129
  return ret;
130
130
  }
131
131
 
132
132
  uint64_t IRAM_ATTR GPIOOneWireBus::read64() {
133
+ InterruptLock lock;
133
134
  uint64_t ret = 0;
134
135
  for (uint8_t i = 0; i < 8; i++) {
135
136
  ret |= (uint64_t(this->read_bit_()) << i);
@@ -144,6 +145,7 @@ void GPIOOneWireBus::reset_search() {
144
145
  }
145
146
 
146
147
  uint64_t IRAM_ATTR GPIOOneWireBus::search_int() {
148
+ InterruptLock lock;
147
149
  if (this->last_device_flag_)
148
150
  return 0u;
149
151
 
@@ -18,7 +18,6 @@ class GPIOOneWireBus : public one_wire::OneWireBus, public Component {
18
18
  this->pin_ = pin->to_isr();
19
19
  }
20
20
 
21
- bool reset() override;
22
21
  void write8(uint8_t val) override;
23
22
  void write64(uint64_t val) override;
24
23
  uint8_t read8() override;
@@ -31,10 +30,12 @@ class GPIOOneWireBus : public one_wire::OneWireBus, public Component {
31
30
  bool last_device_flag_{false};
32
31
  uint64_t address_;
33
32
 
33
+ int reset_int() override;
34
34
  void reset_search() override;
35
35
  uint64_t search_int() override;
36
36
  void write_bit_(bool bit);
37
37
  bool read_bit_();
38
+ bool read_bit_(uint32_t *t);
38
39
  };
39
40
 
40
41
  } // namespace gpio
@@ -8,30 +8,45 @@ namespace esphome {
8
8
  namespace gpio_expander {
9
9
 
10
10
  /// @brief A class to cache the read state of a GPIO expander.
11
+ /// This class caches reads between GPIO Pins which are on the same bank.
12
+ /// This means that for reading whole Port (ex. 8 pins) component needs only one
13
+ /// I2C/SPI read per main loop call. It assumes, that one bit in byte identifies one GPIO pin
14
+ /// Template parameters:
15
+ /// T - Type which represents internal register. Could be uint8_t or uint16_t. Adjust to
16
+ /// match size of your internal GPIO bank register.
17
+ /// N - Number of pins
11
18
  template<typename T, T N> class CachedGpioExpander {
12
19
  public:
13
20
  bool digital_read(T pin) {
14
- if (!this->read_cache_invalidated_[pin]) {
15
- this->read_cache_invalidated_[pin] = true;
16
- return this->digital_read_cache(pin);
21
+ uint8_t bank = pin / (sizeof(T) * BITS_PER_BYTE);
22
+ if (this->read_cache_invalidated_[bank]) {
23
+ this->read_cache_invalidated_[bank] = false;
24
+ if (!this->digital_read_hw(pin))
25
+ return false;
17
26
  }
18
- return this->digital_read_hw(pin);
27
+ return this->digital_read_cache(pin);
19
28
  }
20
29
 
21
30
  void digital_write(T pin, bool value) { this->digital_write_hw(pin, value); }
22
31
 
23
32
  protected:
33
+ /// @brief Call component low level function to read GPIO state from device
24
34
  virtual bool digital_read_hw(T pin) = 0;
35
+ /// @brief Call component read function from internal cache.
25
36
  virtual bool digital_read_cache(T pin) = 0;
37
+ /// @brief Call component low level function to write GPIO state to device
26
38
  virtual void digital_write_hw(T pin, bool value) = 0;
39
+ const uint8_t cache_byte_size_ = N / (sizeof(T) * BITS_PER_BYTE);
27
40
 
41
+ /// @brief Invalidate cache. This function should be called in component loop().
28
42
  void reset_pin_cache_() {
29
- for (T i = 0; i < N; i++) {
30
- this->read_cache_invalidated_[i] = false;
43
+ for (T i = 0; i < this->cache_byte_size_; i++) {
44
+ this->read_cache_invalidated_[i] = true;
31
45
  }
32
46
  }
33
47
 
34
- std::array<bool, N> read_cache_invalidated_{};
48
+ static const uint8_t BITS_PER_BYTE = 8;
49
+ std::array<bool, N / (sizeof(T) * BITS_PER_BYTE)> read_cache_invalidated_{};
35
50
  };
36
51
 
37
52
  } // namespace gpio_expander
@@ -9,22 +9,32 @@ from esphome.const import (
9
9
  CONF_LONGITUDE,
10
10
  CONF_SATELLITES,
11
11
  CONF_SPEED,
12
+ DEVICE_CLASS_SPEED,
12
13
  STATE_CLASS_MEASUREMENT,
13
14
  UNIT_DEGREES,
14
15
  UNIT_KILOMETER_PER_HOUR,
15
16
  UNIT_METER,
16
17
  )
17
18
 
19
+ CONF_GPS_ID = "gps_id"
20
+ CONF_HDOP = "hdop"
21
+
22
+ ICON_ALTIMETER = "mdi:altimeter"
23
+ ICON_COMPASS = "mdi:compass"
24
+ ICON_LATITUDE = "mdi:latitude"
25
+ ICON_LONGITUDE = "mdi:longitude"
26
+ ICON_SATELLITE = "mdi:satellite-variant"
27
+ ICON_SPEEDOMETER = "mdi:speedometer"
28
+
18
29
  DEPENDENCIES = ["uart"]
19
30
  AUTO_LOAD = ["sensor"]
20
31
 
21
- CODEOWNERS = ["@coogle"]
32
+ CODEOWNERS = ["@coogle", "@ximex"]
22
33
 
23
34
  gps_ns = cg.esphome_ns.namespace("gps")
24
35
  GPS = gps_ns.class_("GPS", cg.Component, uart.UARTDevice)
25
36
  GPSListener = gps_ns.class_("GPSListener")
26
37
 
27
- CONF_GPS_ID = "gps_id"
28
38
  MULTI_CONF = True
29
39
  CONFIG_SCHEMA = cv.All(
30
40
  cv.Schema(
@@ -32,28 +42,44 @@ CONFIG_SCHEMA = cv.All(
32
42
  cv.GenerateID(): cv.declare_id(GPS),
33
43
  cv.Optional(CONF_LATITUDE): sensor.sensor_schema(
34
44
  unit_of_measurement=UNIT_DEGREES,
45
+ icon=ICON_LATITUDE,
35
46
  accuracy_decimals=6,
47
+ state_class=STATE_CLASS_MEASUREMENT,
36
48
  ),
37
49
  cv.Optional(CONF_LONGITUDE): sensor.sensor_schema(
38
50
  unit_of_measurement=UNIT_DEGREES,
51
+ icon=ICON_LONGITUDE,
39
52
  accuracy_decimals=6,
53
+ state_class=STATE_CLASS_MEASUREMENT,
40
54
  ),
41
55
  cv.Optional(CONF_SPEED): sensor.sensor_schema(
42
56
  unit_of_measurement=UNIT_KILOMETER_PER_HOUR,
43
- accuracy_decimals=6,
57
+ icon=ICON_SPEEDOMETER,
58
+ accuracy_decimals=3,
59
+ device_class=DEVICE_CLASS_SPEED,
60
+ state_class=STATE_CLASS_MEASUREMENT,
44
61
  ),
45
62
  cv.Optional(CONF_COURSE): sensor.sensor_schema(
46
63
  unit_of_measurement=UNIT_DEGREES,
64
+ icon=ICON_COMPASS,
47
65
  accuracy_decimals=2,
66
+ state_class=STATE_CLASS_MEASUREMENT,
48
67
  ),
49
68
  cv.Optional(CONF_ALTITUDE): sensor.sensor_schema(
50
69
  unit_of_measurement=UNIT_METER,
51
- accuracy_decimals=1,
70
+ icon=ICON_ALTIMETER,
71
+ accuracy_decimals=2,
72
+ state_class=STATE_CLASS_MEASUREMENT,
52
73
  ),
53
74
  cv.Optional(CONF_SATELLITES): sensor.sensor_schema(
75
+ icon=ICON_SATELLITE,
54
76
  accuracy_decimals=0,
55
77
  state_class=STATE_CLASS_MEASUREMENT,
56
78
  ),
79
+ cv.Optional(CONF_HDOP): sensor.sensor_schema(
80
+ accuracy_decimals=3,
81
+ state_class=STATE_CLASS_MEASUREMENT,
82
+ ),
57
83
  }
58
84
  )
59
85
  .extend(cv.polling_component_schema("20s"))
@@ -68,29 +94,33 @@ async def to_code(config):
68
94
  await cg.register_component(var, config)
69
95
  await uart.register_uart_device(var, config)
70
96
 
71
- if CONF_LATITUDE in config:
72
- sens = await sensor.new_sensor(config[CONF_LATITUDE])
97
+ if latitude_config := config.get(CONF_LATITUDE):
98
+ sens = await sensor.new_sensor(latitude_config)
73
99
  cg.add(var.set_latitude_sensor(sens))
74
100
 
75
- if CONF_LONGITUDE in config:
76
- sens = await sensor.new_sensor(config[CONF_LONGITUDE])
101
+ if longitude_config := config.get(CONF_LONGITUDE):
102
+ sens = await sensor.new_sensor(longitude_config)
77
103
  cg.add(var.set_longitude_sensor(sens))
78
104
 
79
- if CONF_SPEED in config:
80
- sens = await sensor.new_sensor(config[CONF_SPEED])
105
+ if speed_config := config.get(CONF_SPEED):
106
+ sens = await sensor.new_sensor(speed_config)
81
107
  cg.add(var.set_speed_sensor(sens))
82
108
 
83
- if CONF_COURSE in config:
84
- sens = await sensor.new_sensor(config[CONF_COURSE])
109
+ if course_config := config.get(CONF_COURSE):
110
+ sens = await sensor.new_sensor(course_config)
85
111
  cg.add(var.set_course_sensor(sens))
86
112
 
87
- if CONF_ALTITUDE in config:
88
- sens = await sensor.new_sensor(config[CONF_ALTITUDE])
113
+ if altitude_config := config.get(CONF_ALTITUDE):
114
+ sens = await sensor.new_sensor(altitude_config)
89
115
  cg.add(var.set_altitude_sensor(sens))
90
116
 
91
- if CONF_SATELLITES in config:
92
- sens = await sensor.new_sensor(config[CONF_SATELLITES])
117
+ if satellites_config := config.get(CONF_SATELLITES):
118
+ sens = await sensor.new_sensor(satellites_config)
93
119
  cg.add(var.set_satellites_sensor(sens))
94
120
 
121
+ if hdop_config := config.get(CONF_HDOP):
122
+ sens = await sensor.new_sensor(hdop_config)
123
+ cg.add(var.set_hdop_sensor(sens))
124
+
95
125
  # https://platformio.org/lib/show/1655/TinyGPSPlus
96
- cg.add_library("mikalhart/TinyGPSPlus", "1.0.2")
126
+ cg.add_library("mikalhart/TinyGPSPlus", "1.1.0")
@@ -10,6 +10,17 @@ static const char *const TAG = "gps";
10
10
 
11
11
  TinyGPSPlus &GPSListener::get_tiny_gps() { return this->parent_->get_tiny_gps(); }
12
12
 
13
+ void GPS::dump_config() {
14
+ ESP_LOGCONFIG(TAG, "GPS:");
15
+ LOG_SENSOR(" ", "Latitude", this->latitude_sensor_);
16
+ LOG_SENSOR(" ", "Longitude", this->longitude_sensor_);
17
+ LOG_SENSOR(" ", "Speed", this->speed_sensor_);
18
+ LOG_SENSOR(" ", "Course", this->course_sensor_);
19
+ LOG_SENSOR(" ", "Altitude", this->altitude_sensor_);
20
+ LOG_SENSOR(" ", "Satellites", this->satellites_sensor_);
21
+ LOG_SENSOR(" ", "HDOP", this->hdop_sensor_);
22
+ }
23
+
13
24
  void GPS::update() {
14
25
  if (this->latitude_sensor_ != nullptr)
15
26
  this->latitude_sensor_->publish_state(this->latitude_);
@@ -28,43 +39,51 @@ void GPS::update() {
28
39
 
29
40
  if (this->satellites_sensor_ != nullptr)
30
41
  this->satellites_sensor_->publish_state(this->satellites_);
42
+
43
+ if (this->hdop_sensor_ != nullptr)
44
+ this->hdop_sensor_->publish_state(this->hdop_);
31
45
  }
32
46
 
33
47
  void GPS::loop() {
34
- while (this->available() && !this->has_time_) {
48
+ while (this->available() > 0 && !this->has_time_) {
35
49
  if (this->tiny_gps_.encode(this->read())) {
36
- if (tiny_gps_.location.isUpdated()) {
37
- this->latitude_ = tiny_gps_.location.lat();
38
- this->longitude_ = tiny_gps_.location.lng();
50
+ if (this->tiny_gps_.location.isUpdated()) {
51
+ this->latitude_ = this->tiny_gps_.location.lat();
52
+ this->longitude_ = this->tiny_gps_.location.lng();
39
53
 
40
54
  ESP_LOGD(TAG, "Location:");
41
- ESP_LOGD(TAG, " Lat: %f", this->latitude_);
42
- ESP_LOGD(TAG, " Lon: %f", this->longitude_);
55
+ ESP_LOGD(TAG, " Lat: %.6f °", this->latitude_);
56
+ ESP_LOGD(TAG, " Lon: %.6f °", this->longitude_);
43
57
  }
44
58
 
45
- if (tiny_gps_.speed.isUpdated()) {
46
- this->speed_ = tiny_gps_.speed.kmph();
47
- ESP_LOGD(TAG, "Speed:");
48
- ESP_LOGD(TAG, " %f km/h", this->speed_);
59
+ if (this->tiny_gps_.speed.isUpdated()) {
60
+ this->speed_ = this->tiny_gps_.speed.kmph();
61
+ ESP_LOGD(TAG, "Speed: %.3f km/h", this->speed_);
49
62
  }
50
- if (tiny_gps_.course.isUpdated()) {
51
- this->course_ = tiny_gps_.course.deg();
52
- ESP_LOGD(TAG, "Course:");
53
- ESP_LOGD(TAG, " %f °", this->course_);
63
+
64
+ if (this->tiny_gps_.course.isUpdated()) {
65
+ this->course_ = this->tiny_gps_.course.deg();
66
+ ESP_LOGD(TAG, "Course: %.2f °", this->course_);
54
67
  }
55
- if (tiny_gps_.altitude.isUpdated()) {
56
- this->altitude_ = tiny_gps_.altitude.meters();
57
- ESP_LOGD(TAG, "Altitude:");
58
- ESP_LOGD(TAG, " %f m", this->altitude_);
68
+
69
+ if (this->tiny_gps_.altitude.isUpdated()) {
70
+ this->altitude_ = this->tiny_gps_.altitude.meters();
71
+ ESP_LOGD(TAG, "Altitude: %.2f m", this->altitude_);
59
72
  }
60
- if (tiny_gps_.satellites.isUpdated()) {
61
- this->satellites_ = tiny_gps_.satellites.value();
62
- ESP_LOGD(TAG, "Satellites:");
63
- ESP_LOGD(TAG, " %d", this->satellites_);
73
+
74
+ if (this->tiny_gps_.satellites.isUpdated()) {
75
+ this->satellites_ = this->tiny_gps_.satellites.value();
76
+ ESP_LOGD(TAG, "Satellites: %d", this->satellites_);
77
+ }
78
+
79
+ if (this->tiny_gps_.hdop.isUpdated()) {
80
+ this->hdop_ = this->tiny_gps_.hdop.hdop();
81
+ ESP_LOGD(TAG, "HDOP: %.3f", this->hdop_);
64
82
  }
65
83
 
66
- for (auto *listener : this->listeners_)
84
+ for (auto *listener : this->listeners_) {
67
85
  listener->on_update(this->tiny_gps_);
86
+ }
68
87
  }
69
88
  }
70
89
  }