esphome 2025.7.4__py3-none-any.whl → 2025.8.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 (758) hide show
  1. esphome/__main__.py +190 -83
  2. esphome/automation.py +2 -4
  3. esphome/build_gen/__init__.py +0 -0
  4. esphome/build_gen/platformio.py +102 -0
  5. esphome/components/a4988/a4988.cpp +0 -1
  6. esphome/components/absolute_humidity/absolute_humidity.cpp +0 -2
  7. esphome/components/absolute_humidity/sensor.py +2 -2
  8. esphome/components/adc/__init__.py +123 -85
  9. esphome/components/adc/adc_sensor.h +98 -35
  10. esphome/components/adc/adc_sensor_common.cpp +10 -4
  11. esphome/components/adc/adc_sensor_esp32.cpp +291 -123
  12. esphome/components/adc/adc_sensor_esp8266.cpp +1 -4
  13. esphome/components/adc/adc_sensor_libretiny.cpp +1 -2
  14. esphome/components/adc/adc_sensor_rp2040.cpp +1 -2
  15. esphome/components/adc/adc_sensor_zephyr.cpp +207 -0
  16. esphome/components/adc/sensor.py +61 -27
  17. esphome/components/adc128s102/adc128s102.cpp +1 -4
  18. esphome/components/ade7880/sensor.py +75 -49
  19. esphome/components/ads1115/ads1115.cpp +0 -1
  20. esphome/components/ads1118/ads1118.cpp +0 -1
  21. esphome/components/ags10/ags10.cpp +0 -4
  22. esphome/components/aht10/aht10.cpp +0 -4
  23. esphome/components/aic3204/aic3204.cpp +0 -2
  24. esphome/components/airthings_wave_plus/__init__.py +1 -1
  25. esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +22 -4
  26. esphome/components/airthings_wave_plus/airthings_wave_plus.h +10 -1
  27. esphome/components/airthings_wave_plus/sensor.py +55 -28
  28. esphome/components/alarm_control_panel/__init__.py +4 -9
  29. esphome/components/am2315c/am2315c.cpp +0 -2
  30. esphome/components/am2320/am2320.cpp +0 -1
  31. esphome/components/animation/__init__.py +14 -11
  32. esphome/components/apds9306/apds9306.cpp +0 -4
  33. esphome/components/apds9960/apds9960.cpp +0 -1
  34. esphome/components/api/__init__.py +29 -4
  35. esphome/components/api/api_connection.cpp +378 -401
  36. esphome/components/api/api_connection.h +112 -56
  37. esphome/components/api/api_frame_helper.cpp +69 -896
  38. esphome/components/api/api_frame_helper.h +31 -126
  39. esphome/components/api/api_frame_helper_noise.cpp +583 -0
  40. esphome/components/api/api_frame_helper_noise.h +68 -0
  41. esphome/components/api/api_frame_helper_plaintext.cpp +290 -0
  42. esphome/components/api/api_frame_helper_plaintext.h +53 -0
  43. esphome/components/api/api_noise_context.h +2 -4
  44. esphome/components/api/api_pb2.cpp +1601 -1808
  45. esphome/components/api/api_pb2.h +367 -323
  46. esphome/components/api/api_pb2_dump.cpp +1137 -3466
  47. esphome/components/api/api_pb2_includes.h +34 -0
  48. esphome/components/api/api_pb2_service.cpp +94 -105
  49. esphome/components/api/api_pb2_service.h +27 -16
  50. esphome/components/api/api_server.cpp +18 -17
  51. esphome/components/api/api_server.h +8 -5
  52. esphome/components/api/client.py +16 -8
  53. esphome/components/api/custom_api_device.h +68 -14
  54. esphome/components/api/homeassistant_service.h +24 -19
  55. esphome/components/api/list_entities.cpp +3 -5
  56. esphome/components/api/list_entities.h +2 -4
  57. esphome/components/api/proto.cpp +3 -5
  58. esphome/components/api/proto.h +239 -274
  59. esphome/components/api/subscribe_state.cpp +2 -4
  60. esphome/components/api/subscribe_state.h +2 -4
  61. esphome/components/api/user_services.cpp +2 -4
  62. esphome/components/api/user_services.h +8 -8
  63. esphome/components/as3935/as3935.cpp +0 -2
  64. esphome/components/as3935_spi/as3935_spi.cpp +0 -2
  65. esphome/components/as5600/__init__.py +1 -1
  66. esphome/components/as5600/as5600.cpp +0 -2
  67. esphome/components/as5600/sensor/__init__.py +0 -1
  68. esphome/components/as7341/as7341.cpp +0 -1
  69. esphome/components/async_tcp/__init__.py +1 -1
  70. esphome/components/at581x/at581x.cpp +1 -1
  71. esphome/components/atm90e26/atm90e26.cpp +0 -1
  72. esphome/components/atm90e32/atm90e32.cpp +475 -116
  73. esphome/components/atm90e32/atm90e32.h +43 -5
  74. esphome/components/audio/audio.h +2 -2
  75. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +0 -2
  76. esphome/components/beken_spi_led_strip/led_strip.cpp +0 -2
  77. esphome/components/bh1750/bh1750.cpp +0 -1
  78. esphome/components/binary_sensor/__init__.py +14 -12
  79. esphome/components/ble_client/__init__.py +4 -7
  80. esphome/components/bluetooth_proxy/__init__.py +40 -3
  81. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +392 -81
  82. esphome/components/bluetooth_proxy/bluetooth_connection.h +16 -5
  83. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +102 -311
  84. esphome/components/bluetooth_proxy/bluetooth_proxy.h +30 -14
  85. esphome/components/bme280_base/bme280_base.cpp +15 -16
  86. esphome/components/bme680/bme680.cpp +2 -3
  87. esphome/components/bme680_bsec/bme680_bsec.cpp +0 -2
  88. esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +0 -2
  89. esphome/components/bmi160/bmi160.cpp +0 -1
  90. esphome/components/bmp085/bmp085.cpp +0 -1
  91. esphome/components/bmp280_base/bmp280_base.cpp +13 -14
  92. esphome/components/bmp3xx_base/bmp3xx_base.cpp +0 -1
  93. esphome/components/bmp581/bmp581.cpp +0 -2
  94. esphome/components/bp1658cj/bp1658cj.cpp +0 -1
  95. esphome/components/bp5758d/bp5758d.cpp +0 -1
  96. esphome/components/button/__init__.py +0 -1
  97. esphome/components/canbus/__init__.py +2 -3
  98. esphome/components/canbus/canbus.cpp +0 -1
  99. esphome/components/cap1188/cap1188.cpp +0 -2
  100. esphome/components/captive_portal/__init__.py +1 -1
  101. esphome/components/cd74hc4067/cd74hc4067.cpp +0 -2
  102. esphome/components/ch422g/ch422g.cpp +0 -1
  103. esphome/components/chsc6x/chsc6x_touchscreen.cpp +0 -3
  104. esphome/components/climate/__init__.py +0 -1
  105. esphome/components/climate/climate_traits.h +24 -0
  106. esphome/components/cm1106/cm1106.cpp +0 -1
  107. esphome/components/const/__init__.py +6 -0
  108. esphome/components/cover/__init__.py +0 -1
  109. esphome/components/cover/cover.cpp +9 -13
  110. esphome/components/cs5460a/cs5460a.cpp +0 -2
  111. esphome/components/cse7761/cse7761.cpp +0 -1
  112. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +0 -2
  113. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +0 -2
  114. esphome/components/dac7678/dac7678_output.cpp +0 -2
  115. esphome/components/dallas_temp/dallas_temp.cpp +0 -1
  116. esphome/components/datetime/__init__.py +0 -2
  117. esphome/components/debug/__init__.py +15 -1
  118. esphome/components/debug/debug_zephyr.cpp +281 -0
  119. esphome/components/debug/sensor.py +2 -1
  120. esphome/components/deep_sleep/deep_sleep_component.cpp +0 -1
  121. esphome/components/deep_sleep/deep_sleep_esp32.cpp +20 -1
  122. esphome/components/dfrobot_sen0395/__init__.py +1 -2
  123. esphome/components/dht/dht.cpp +0 -1
  124. esphome/components/dht12/dht12.cpp +0 -1
  125. esphome/components/display/__init__.py +16 -3
  126. esphome/components/display_menu_base/__init__.py +1 -1
  127. esphome/components/dps310/dps310.cpp +0 -2
  128. esphome/components/ds1307/ds1307.cpp +0 -1
  129. esphome/components/ds2484/ds2484.cpp +0 -1
  130. esphome/components/duty_cycle/duty_cycle_sensor.cpp +0 -1
  131. esphome/components/ee895/ee895.cpp +0 -1
  132. esphome/components/ektf2232/touchscreen/ektf2232.cpp +0 -1
  133. esphome/components/emc2101/emc2101.cpp +0 -2
  134. esphome/components/ens160_base/ens160_base.cpp +0 -2
  135. esphome/components/ens210/ens210.cpp +0 -1
  136. esphome/components/es7210/es7210.cpp +0 -2
  137. esphome/components/es7243e/es7243e.cpp +0 -2
  138. esphome/components/es8156/es8156.cpp +0 -2
  139. esphome/components/es8311/es8311.cpp +0 -2
  140. esphome/components/es8388/es8388.cpp +0 -2
  141. esphome/components/esp32/__init__.py +203 -60
  142. esphome/components/esp32/boards.py +17 -0
  143. esphome/components/esp32/gpio.cpp +0 -1
  144. esphome/components/esp32/gpio.py +1 -2
  145. esphome/components/esp32/gpio_esp32_h2.py +2 -7
  146. esphome/components/esp32/gpio_esp32_p4.py +2 -7
  147. esphome/components/esp32/post_build.py.script +112 -61
  148. esphome/components/esp32_ble/__init__.py +41 -2
  149. esphome/components/esp32_ble/ble.cpp +14 -10
  150. esphome/components/esp32_ble/ble.h +18 -18
  151. esphome/components/esp32_ble/ble_advertising.cpp +5 -5
  152. esphome/components/esp32_ble/ble_advertising.h +7 -5
  153. esphome/components/esp32_ble/ble_event.h +139 -73
  154. esphome/components/esp32_ble/ble_scan_result.h +2 -4
  155. esphome/components/esp32_ble/ble_uuid.cpp +5 -5
  156. esphome/components/esp32_ble/ble_uuid.h +6 -5
  157. esphome/components/esp32_ble_beacon/__init__.py +4 -0
  158. esphome/components/esp32_ble_client/__init__.py +1 -1
  159. esphome/components/esp32_ble_client/ble_characteristic.cpp +4 -4
  160. esphome/components/esp32_ble_client/ble_characteristic.h +6 -4
  161. esphome/components/esp32_ble_client/ble_client_base.cpp +155 -104
  162. esphome/components/esp32_ble_client/ble_client_base.h +17 -6
  163. esphome/components/esp32_ble_client/ble_descriptor.h +6 -4
  164. esphome/components/esp32_ble_client/ble_service.cpp +4 -4
  165. esphome/components/esp32_ble_client/ble_service.h +6 -4
  166. esphome/components/esp32_ble_server/__init__.py +15 -12
  167. esphome/components/esp32_ble_tracker/__init__.py +79 -11
  168. esphome/components/esp32_ble_tracker/automation.h +4 -4
  169. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +264 -261
  170. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +103 -37
  171. esphome/components/esp32_camera/__init__.py +13 -1
  172. esphome/components/esp32_camera/esp32_camera.cpp +7 -2
  173. esphome/components/esp32_camera/esp32_camera.h +1 -0
  174. esphome/components/esp32_dac/esp32_dac.cpp +3 -19
  175. esphome/components/esp32_dac/esp32_dac.h +4 -8
  176. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -6
  177. esphome/components/esp32_rmt_led_strip/light.py +1 -1
  178. esphome/components/esp32_touch/__init__.py +2 -3
  179. esphome/components/esp32_touch/esp32_touch.h +9 -6
  180. esphome/components/esp32_touch/esp32_touch_common.cpp +2 -0
  181. esphome/components/esp32_touch/esp32_touch_v1.cpp +7 -9
  182. esphome/components/esp32_touch/esp32_touch_v2.cpp +10 -6
  183. esphome/components/esp8266/__init__.py +3 -1
  184. esphome/components/esp8266_pwm/esp8266_pwm.cpp +0 -1
  185. esphome/components/esphome/ota/__init__.py +1 -2
  186. esphome/components/esphome/ota/ota_esphome.cpp +150 -77
  187. esphome/components/esphome/ota/ota_esphome.h +8 -1
  188. esphome/components/espnow/__init__.py +309 -0
  189. esphome/components/espnow/automation.h +175 -0
  190. esphome/components/espnow/espnow_component.cpp +468 -0
  191. esphome/components/espnow/espnow_component.h +183 -0
  192. esphome/components/espnow/espnow_err.h +19 -0
  193. esphome/components/espnow/espnow_packet.h +166 -0
  194. esphome/components/ethernet/__init__.py +7 -1
  195. esphome/components/ethernet/esp_eth_phy_jl1101.c +5 -0
  196. esphome/components/ethernet/ethernet_component.cpp +0 -1
  197. esphome/components/ethernet/ethernet_component.h +4 -0
  198. esphome/components/ethernet_info/ethernet_info_text_sensor.h +0 -3
  199. esphome/components/event/__init__.py +0 -1
  200. esphome/components/factory_reset/__init__.py +92 -0
  201. esphome/components/factory_reset/factory_reset.cpp +76 -0
  202. esphome/components/factory_reset/factory_reset.h +43 -0
  203. esphome/components/fan/__init__.py +0 -1
  204. esphome/components/fan/fan_traits.h +16 -0
  205. esphome/components/fastled_base/fastled_light.cpp +0 -1
  206. esphome/components/fastled_spi/light.py +1 -3
  207. esphome/components/fingerprint_grow/fingerprint_grow.cpp +0 -2
  208. esphome/components/font/__init__.py +9 -1
  209. esphome/components/fs3000/fs3000.cpp +0 -2
  210. esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +0 -2
  211. esphome/components/ft63x6/ft63x6.cpp +0 -1
  212. esphome/components/gdk101/gdk101.cpp +0 -1
  213. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +0 -1
  214. esphome/components/gl_r01_i2c/sensor.py +1 -1
  215. esphome/components/gpio/one_wire/gpio_one_wire.cpp +0 -1
  216. esphome/components/gpio/switch/gpio_switch.cpp +0 -2
  217. esphome/components/gpio_expander/cached_gpio.h +24 -15
  218. esphome/components/gps/__init__.py +6 -2
  219. esphome/components/gps/gps.cpp +50 -49
  220. esphome/components/gps/gps.h +4 -8
  221. esphome/components/gps/time/gps_time.cpp +3 -9
  222. esphome/components/gps/time/gps_time.h +4 -7
  223. esphome/components/graph/__init__.py +1 -1
  224. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +0 -1
  225. esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +0 -1
  226. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +21 -12
  227. esphome/components/gt911/touchscreen/gt911_touchscreen.h +26 -2
  228. esphome/components/haier/climate.py +5 -10
  229. esphome/components/haier/haier_base.cpp +0 -1
  230. esphome/components/hbridge/switch/hbridge_switch.cpp +0 -2
  231. esphome/components/hdc1080/hdc1080.cpp +0 -2
  232. esphome/components/heatpumpir/climate.py +2 -2
  233. esphome/components/hlw8012/hlw8012.cpp +0 -1
  234. esphome/components/hm3301/hm3301.cpp +0 -1
  235. esphome/components/hmc5883l/hmc5883l.cpp +0 -1
  236. esphome/components/homeassistant/__init__.py +1 -0
  237. esphome/components/homeassistant/number/__init__.py +1 -0
  238. esphome/components/homeassistant/number/homeassistant_number.cpp +11 -7
  239. esphome/components/homeassistant/switch/__init__.py +1 -0
  240. esphome/components/homeassistant/switch/homeassistant_switch.cpp +9 -5
  241. esphome/components/honeywellabp/honeywellabp.cpp +1 -4
  242. esphome/components/host/__init__.py +2 -0
  243. esphome/components/hte501/hte501.cpp +0 -1
  244. esphome/components/http_request/__init__.py +2 -3
  245. esphome/components/http_request/http_request_idf.cpp +2 -2
  246. esphome/components/htu21d/htu21d.cpp +0 -2
  247. esphome/components/htu31d/htu31d.cpp +0 -2
  248. esphome/components/hx711/hx711.cpp +0 -1
  249. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +0 -1
  250. esphome/components/hydreon_rgxx/sensor.py +4 -5
  251. esphome/components/i2c/i2c_bus.h +1 -1
  252. esphome/components/i2c/i2c_bus_arduino.cpp +1 -2
  253. esphome/components/i2c/i2c_bus_esp_idf.cpp +192 -17
  254. esphome/components/i2c/i2c_bus_esp_idf.h +11 -1
  255. esphome/components/i2s_audio/__init__.py +6 -5
  256. esphome/components/i2s_audio/i2s_audio.cpp +0 -2
  257. esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +1 -4
  258. esphome/components/i2s_audio/microphone/__init__.py +4 -6
  259. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +46 -19
  260. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +4 -3
  261. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +273 -269
  262. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +19 -34
  263. esphome/components/ili9xxx/display.py +4 -3
  264. esphome/components/ili9xxx/ili9xxx_display.cpp +0 -2
  265. esphome/components/image/__init__.py +123 -92
  266. esphome/components/improv_serial/__init__.py +7 -8
  267. esphome/components/ina219/ina219.cpp +0 -1
  268. esphome/components/ina226/ina226.cpp +0 -2
  269. esphome/components/ina260/ina260.cpp +0 -2
  270. esphome/components/ina2xx_base/__init__.py +2 -5
  271. esphome/components/ina2xx_base/ina2xx_base.cpp +0 -2
  272. esphome/components/ina3221/ina3221.cpp +0 -1
  273. esphome/components/internal_temperature/internal_temperature.cpp +0 -2
  274. esphome/components/interval/interval.h +5 -9
  275. esphome/components/json/__init__.py +1 -1
  276. esphome/components/kmeteriso/kmeteriso.cpp +0 -2
  277. esphome/components/lc709203f/lc709203f.cpp +0 -2
  278. esphome/components/lcd_gpio/display.py +1 -3
  279. esphome/components/lcd_gpio/gpio_lcd_display.cpp +0 -1
  280. esphome/components/lcd_pcf8574/pcf8574_display.cpp +0 -1
  281. esphome/components/ld2410/__init__.py +4 -6
  282. esphome/components/ld2410/binary_sensor.py +4 -0
  283. esphome/components/ld2410/ld2410.cpp +56 -100
  284. esphome/components/ld2410/ld2410.h +17 -15
  285. esphome/components/ld2410/sensor.py +24 -10
  286. esphome/components/ld2412/__init__.py +46 -0
  287. esphome/components/ld2412/binary_sensor.py +70 -0
  288. esphome/components/ld2412/button/__init__.py +74 -0
  289. esphome/components/ld2412/button/factory_reset_button.cpp +9 -0
  290. esphome/components/ld2412/button/factory_reset_button.h +18 -0
  291. esphome/components/ld2412/button/query_button.cpp +9 -0
  292. esphome/components/ld2412/button/query_button.h +18 -0
  293. esphome/components/ld2412/button/restart_button.cpp +9 -0
  294. esphome/components/ld2412/button/restart_button.h +18 -0
  295. esphome/components/ld2412/button/start_dynamic_background_correction_button.cpp +11 -0
  296. esphome/components/ld2412/button/start_dynamic_background_correction_button.h +18 -0
  297. esphome/components/ld2412/ld2412.cpp +861 -0
  298. esphome/components/ld2412/ld2412.h +141 -0
  299. esphome/components/ld2412/number/__init__.py +126 -0
  300. esphome/components/ld2412/number/gate_threshold_number.cpp +14 -0
  301. esphome/components/ld2412/number/gate_threshold_number.h +19 -0
  302. esphome/components/ld2412/number/light_threshold_number.cpp +12 -0
  303. esphome/components/ld2412/number/light_threshold_number.h +18 -0
  304. esphome/components/ld2412/number/max_distance_timeout_number.cpp +12 -0
  305. esphome/components/ld2412/number/max_distance_timeout_number.h +18 -0
  306. esphome/components/ld2412/select/__init__.py +82 -0
  307. esphome/components/ld2412/select/baud_rate_select.cpp +12 -0
  308. esphome/components/ld2412/select/baud_rate_select.h +18 -0
  309. esphome/components/ld2412/select/distance_resolution_select.cpp +12 -0
  310. esphome/components/ld2412/select/distance_resolution_select.h +18 -0
  311. esphome/components/ld2412/select/light_out_control_select.cpp +12 -0
  312. esphome/components/ld2412/select/light_out_control_select.h +18 -0
  313. esphome/components/ld2412/sensor.py +124 -0
  314. esphome/components/ld2412/switch/__init__.py +45 -0
  315. esphome/components/ld2412/switch/bluetooth_switch.cpp +12 -0
  316. esphome/components/ld2412/switch/bluetooth_switch.h +18 -0
  317. esphome/components/ld2412/switch/engineering_mode_switch.cpp +12 -0
  318. esphome/components/ld2412/switch/engineering_mode_switch.h +18 -0
  319. esphome/components/ld2412/text_sensor.py +34 -0
  320. esphome/components/ld2420/ld2420.cpp +0 -1
  321. esphome/components/ld2450/__init__.py +3 -4
  322. esphome/components/ld2450/binary_sensor.py +3 -0
  323. esphome/components/ld2450/ld2450.cpp +77 -165
  324. esphome/components/ld2450/ld2450.h +26 -54
  325. esphome/components/ld2450/sensor.py +120 -6
  326. esphome/components/ld2450/text_sensor.py +5 -4
  327. esphome/components/ld24xx/__init__.py +1 -0
  328. esphome/components/ld24xx/ld24xx.h +65 -0
  329. esphome/components/ledc/ledc_output.cpp +0 -1
  330. esphome/components/libretiny/__init__.py +2 -0
  331. esphome/components/light/__init__.py +0 -1
  332. esphome/components/light/effects.py +70 -45
  333. esphome/components/light/light_call.cpp +101 -66
  334. esphome/components/light/light_color_values.h +16 -11
  335. esphome/components/light/light_json_schema.cpp +46 -44
  336. esphome/components/light/light_state.cpp +8 -11
  337. esphome/components/light/light_traits.h +17 -0
  338. esphome/components/lightwaverf/lightwaverf.cpp +0 -2
  339. esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +0 -1
  340. esphome/components/lock/__init__.py +0 -1
  341. esphome/components/logger/__init__.py +31 -9
  342. esphome/components/logger/logger.cpp +12 -7
  343. esphome/components/logger/logger.h +25 -14
  344. esphome/components/logger/logger_esp32.cpp +2 -7
  345. esphome/components/logger/logger_esp8266.cpp +2 -4
  346. esphome/components/logger/logger_host.cpp +2 -4
  347. esphome/components/logger/logger_libretiny.cpp +2 -4
  348. esphome/components/logger/logger_rp2040.cpp +2 -4
  349. esphome/components/logger/logger_zephyr.cpp +86 -0
  350. esphome/components/logger/select/logger_level_select.cpp +2 -4
  351. esphome/components/logger/select/logger_level_select.h +2 -4
  352. esphome/components/logger/task_log_buffer.cpp +2 -4
  353. esphome/components/logger/task_log_buffer.h +2 -4
  354. esphome/components/lps22/sensor.py +5 -5
  355. esphome/components/ltr390/ltr390.cpp +0 -2
  356. esphome/components/ltr501/ltr501.cpp +0 -1
  357. esphome/components/ltr_als_ps/ltr_als_ps.cpp +0 -1
  358. esphome/components/lvgl/__init__.py +14 -13
  359. esphome/components/lvgl/automation.py +2 -4
  360. esphome/components/lvgl/defines.py +0 -2
  361. esphome/components/lvgl/helpers.py +1 -1
  362. esphome/components/lvgl/lv_validation.py +7 -4
  363. esphome/components/lvgl/lvgl_esphome.cpp +2 -3
  364. esphome/components/lvgl/styles.py +2 -2
  365. esphome/components/lvgl/types.py +1 -1
  366. esphome/components/lvgl/widgets/__init__.py +2 -2
  367. esphome/components/lvgl/widgets/arc.py +14 -11
  368. esphome/components/lvgl/widgets/buttonmatrix.py +1 -1
  369. esphome/components/lvgl/widgets/qrcode.py +7 -7
  370. esphome/components/lvgl/widgets/spinner.py +6 -6
  371. esphome/components/lvgl/widgets/switch.py +2 -2
  372. esphome/components/lvgl/widgets/tabview.py +3 -3
  373. esphome/components/lvgl/widgets/tileview.py +15 -7
  374. esphome/components/m5stack_8angle/m5stack_8angle.cpp +0 -1
  375. esphome/components/matrix_keypad/__init__.py +4 -3
  376. esphome/components/max17043/max17043.cpp +0 -2
  377. esphome/components/max31855/max31855.cpp +1 -4
  378. esphome/components/max31856/max31856.cpp +0 -4
  379. esphome/components/max31865/max31865.cpp +0 -1
  380. esphome/components/max44009/max44009.cpp +0 -1
  381. esphome/components/max6675/max6675.cpp +1 -4
  382. esphome/components/max6956/max6956.cpp +0 -1
  383. esphome/components/max7219/max7219.cpp +0 -1
  384. esphome/components/max7219digit/display.py +1 -1
  385. esphome/components/max7219digit/max7219digit.cpp +0 -1
  386. esphome/components/max9611/max9611.cpp +0 -1
  387. esphome/components/mcp23008/__init__.py +1 -1
  388. esphome/components/mcp23008/mcp23008.cpp +0 -1
  389. esphome/components/mcp23016/mcp23016.cpp +0 -1
  390. esphome/components/mcp23017/__init__.py +1 -1
  391. esphome/components/mcp23017/mcp23017.cpp +0 -1
  392. esphome/components/mcp23s08/__init__.py +1 -1
  393. esphome/components/mcp23s08/mcp23s08.cpp +0 -1
  394. esphome/components/mcp23s17/__init__.py +1 -1
  395. esphome/components/mcp23s17/mcp23s17.cpp +0 -1
  396. esphome/components/mcp23x08_base/__init__.py +2 -0
  397. esphome/components/mcp23x08_base/mcp23x08_base.cpp +9 -7
  398. esphome/components/mcp23x08_base/mcp23x08_base.h +9 -4
  399. esphome/components/mcp23x17_base/__init__.py +2 -0
  400. esphome/components/mcp23x17_base/mcp23x17_base.cpp +20 -7
  401. esphome/components/mcp23x17_base/mcp23x17_base.h +9 -4
  402. esphome/components/mcp23xxx_base/__init__.py +11 -5
  403. esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +15 -12
  404. esphome/components/mcp23xxx_base/mcp23xxx_base.h +8 -7
  405. esphome/components/mcp3008/mcp3008.cpp +1 -4
  406. esphome/components/mcp3204/mcp3204.cpp +1 -4
  407. esphome/components/mcp4461/mcp4461.cpp +0 -1
  408. esphome/components/mcp4725/mcp4725.cpp +0 -1
  409. esphome/components/mcp4728/mcp4728.cpp +0 -1
  410. esphome/components/mcp9600/mcp9600.cpp +0 -2
  411. esphome/components/mcp9808/mcp9808.cpp +0 -2
  412. esphome/components/mdns/__init__.py +3 -0
  413. esphome/components/mdns/mdns_component.cpp +2 -0
  414. esphome/components/mdns/mdns_component.h +4 -0
  415. esphome/components/media_player/__init__.py +40 -0
  416. esphome/components/media_player/automation.h +16 -0
  417. esphome/components/media_player/media_player.cpp +13 -0
  418. esphome/components/media_player/media_player.h +50 -3
  419. esphome/components/micro_wake_word/micro_wake_word.cpp +0 -3
  420. esphome/components/mics_4514/mics_4514.cpp +1 -6
  421. esphome/components/midea/ir_transmitter.h +4 -4
  422. esphome/components/mipi/__init__.py +416 -0
  423. esphome/components/mipi_dsi/__init__.py +5 -0
  424. esphome/components/mipi_dsi/display.py +233 -0
  425. esphome/components/mipi_dsi/mipi_dsi.cpp +379 -0
  426. esphome/components/mipi_dsi/mipi_dsi.h +123 -0
  427. esphome/components/mipi_dsi/models/__init__.py +0 -0
  428. esphome/components/mipi_dsi/models/guition.py +38 -0
  429. esphome/components/mipi_dsi/models/m5stack.py +57 -0
  430. esphome/components/mipi_dsi/models/waveshare.py +105 -0
  431. esphome/components/mipi_rgb/models/lilygo.py +0 -0
  432. esphome/components/mipi_spi/__init__.py +0 -9
  433. esphome/components/mipi_spi/display.py +220 -256
  434. esphome/components/mipi_spi/mipi_spi.cpp +1 -485
  435. esphome/components/mipi_spi/mipi_spi.h +556 -108
  436. esphome/components/mipi_spi/models/__init__.py +0 -65
  437. esphome/components/mipi_spi/models/adafruit.py +30 -0
  438. esphome/components/mipi_spi/models/amoled.py +41 -5
  439. esphome/components/mipi_spi/models/ili.py +5 -5
  440. esphome/components/mipi_spi/models/jc.py +1 -3
  441. esphome/components/mipi_spi/models/lilygo.py +1 -1
  442. esphome/components/mipi_spi/models/waveshare.py +16 -1
  443. esphome/components/mixer/speaker/__init__.py +4 -5
  444. esphome/components/mlx90393/sensor.py +7 -5
  445. esphome/components/mlx90393/sensor_mlx90393.cpp +0 -1
  446. esphome/components/mlx90614/mlx90614.cpp +0 -1
  447. esphome/components/mmc5603/mmc5603.cpp +0 -1
  448. esphome/components/mmc5983/mmc5983.cpp +0 -2
  449. esphome/components/mpl3115a2/mpl3115a2.cpp +0 -2
  450. esphome/components/mpr121/__init__.py +7 -6
  451. esphome/components/mpr121/mpr121.cpp +0 -1
  452. esphome/components/mpu6050/mpu6050.cpp +0 -1
  453. esphome/components/mpu6886/mpu6886.cpp +0 -1
  454. esphome/components/mqtt/__init__.py +1 -2
  455. esphome/components/mqtt/mqtt_button.cpp +1 -1
  456. esphome/components/mqtt/mqtt_client.cpp +0 -1
  457. esphome/components/mqtt/mqtt_component.cpp +8 -14
  458. esphome/components/mqtt/mqtt_component.h +0 -7
  459. esphome/components/mqtt/mqtt_sensor.cpp +0 -1
  460. esphome/components/mqtt/mqtt_sensor.h +0 -1
  461. esphome/components/mqtt/mqtt_text_sensor.cpp +0 -1
  462. esphome/components/mqtt/mqtt_text_sensor.h +0 -1
  463. esphome/components/ms5611/ms5611.cpp +0 -1
  464. esphome/components/ms8607/ms8607.cpp +0 -1
  465. esphome/components/msa3xx/msa3xx.cpp +0 -2
  466. esphome/components/my9231/my9231.cpp +0 -2
  467. esphome/components/nau7802/nau7802.cpp +0 -1
  468. esphome/components/neopixelbus/light.py +3 -0
  469. esphome/components/network/util.cpp +29 -0
  470. esphome/components/nextion/nextion.cpp +2 -2
  471. esphome/components/nfc/binary_sensor/{binary_sensor.cpp → nfc_binary_sensor.cpp} +1 -1
  472. esphome/components/npi19/npi19.cpp +0 -2
  473. esphome/components/nrf52/__init__.py +223 -0
  474. esphome/components/nrf52/boards.py +34 -0
  475. esphome/components/nrf52/const.py +18 -0
  476. esphome/components/nrf52/gpio.py +79 -0
  477. esphome/components/number/__init__.py +2 -1
  478. esphome/components/one_wire/__init__.py +1 -2
  479. esphome/components/one_wire/one_wire.cpp +0 -2
  480. esphome/components/one_wire/one_wire.h +0 -2
  481. esphome/components/opentherm/hub.cpp +0 -1
  482. esphome/components/opentherm/number/__init__.py +2 -2
  483. esphome/components/openthread/__init__.py +2 -3
  484. esphome/components/openthread/openthread.cpp +30 -13
  485. esphome/components/openthread/openthread.h +3 -0
  486. esphome/components/openthread/openthread_esp.cpp +3 -1
  487. esphome/components/opt3001/sensor.py +2 -6
  488. esphome/components/output/__init__.py +38 -0
  489. esphome/components/output/automation.h +24 -0
  490. esphome/components/output/switch/output_switch.cpp +0 -2
  491. esphome/components/packages/__init__.py +1 -2
  492. esphome/components/packet_transport/__init__.py +4 -3
  493. esphome/components/pca6416a/pca6416a.cpp +0 -1
  494. esphome/components/pca9554/pca9554.cpp +0 -1
  495. esphome/components/pca9685/pca9685_output.cpp +0 -2
  496. esphome/components/pcf85063/pcf85063.cpp +0 -1
  497. esphome/components/pcf8563/pcf8563.cpp +0 -1
  498. esphome/components/pcf8574/pcf8574.cpp +0 -1
  499. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +0 -1
  500. esphome/components/pipsolar/pipsolar.cpp +54 -42
  501. esphome/components/pipsolar/pipsolar.h +5 -4
  502. esphome/components/pipsolar/sensor/__init__.py +1 -1
  503. esphome/components/pm2005/pm2005.cpp +0 -1
  504. esphome/components/pmsa003i/pmsa003i.cpp +0 -2
  505. esphome/components/pmwcs3/sensor.py +1 -2
  506. esphome/components/pn532/pn532.cpp +0 -2
  507. esphome/components/pn532_spi/pn532_spi.cpp +0 -2
  508. esphome/components/power_supply/power_supply.cpp +7 -10
  509. esphome/components/power_supply/power_supply.h +1 -1
  510. esphome/components/psram/__init__.py +6 -1
  511. esphome/components/pulse_counter/pulse_counter_sensor.cpp +0 -1
  512. esphome/components/pulse_counter/sensor.py +9 -6
  513. esphome/components/pylontech/pylontech.cpp +0 -1
  514. esphome/components/qmc5883l/qmc5883l.cpp +0 -1
  515. esphome/components/qmp6988/qmp6988.cpp +0 -2
  516. esphome/components/qspi_dbi/display.py +2 -3
  517. esphome/components/qspi_dbi/qspi_dbi.cpp +0 -2
  518. esphome/components/qwiic_pir/binary_sensor.py +2 -3
  519. esphome/components/qwiic_pir/qwiic_pir.cpp +0 -2
  520. esphome/components/rc522/rc522.cpp +9 -31
  521. esphome/components/rc522_spi/rc522_spi.cpp +0 -1
  522. esphome/components/remote_base/__init__.py +5 -6
  523. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -1
  524. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +0 -1
  525. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -1
  526. esphome/components/remote_transmitter/__init__.py +26 -0
  527. esphome/components/remote_transmitter/automation.h +18 -0
  528. esphome/components/remote_transmitter/remote_transmitter.h +2 -1
  529. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -1
  530. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  531. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  532. esphome/components/resampler/speaker/__init__.py +4 -5
  533. esphome/components/rf_bridge/__init__.py +4 -8
  534. esphome/components/rotary_encoder/rotary_encoder.cpp +0 -2
  535. esphome/components/rp2040/__init__.py +3 -1
  536. esphome/components/rp2040_pio_led_strip/led_strip.cpp +0 -2
  537. esphome/components/rp2040_pio_led_strip/light.py +1 -2
  538. esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -5
  539. esphome/components/rpi_dpi_rgb/display.py +13 -15
  540. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -3
  541. esphome/components/runtime_stats/__init__.py +34 -0
  542. esphome/components/runtime_stats/runtime_stats.cpp +102 -0
  543. esphome/components/runtime_stats/runtime_stats.h +132 -0
  544. esphome/components/scd30/scd30.cpp +0 -2
  545. esphome/components/scd30/sensor.py +1 -2
  546. esphome/components/scd4x/scd4x.cpp +0 -1
  547. esphome/components/scd4x/sensor.py +1 -3
  548. esphome/components/sdl/display.py +3 -1
  549. esphome/components/sdl/sdl_esphome.cpp +0 -2
  550. esphome/components/sdp3x/sdp3x.cpp +0 -2
  551. esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +0 -2
  552. esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +0 -3
  553. esphome/components/select/__init__.py +2 -3
  554. esphome/components/select/select_traits.cpp +1 -1
  555. esphome/components/select/select_traits.h +1 -1
  556. esphome/components/sen0321/sen0321.cpp +0 -1
  557. esphome/components/sen5x/sen5x.cpp +0 -2
  558. esphome/components/senseair/senseair.cpp +7 -3
  559. esphome/components/senseair/senseair.h +11 -0
  560. esphome/components/sensor/__init__.py +36 -4
  561. esphome/components/sensor/filter.cpp +49 -10
  562. esphome/components/sensor/filter.h +22 -7
  563. esphome/components/sensor/sensor.cpp +0 -1
  564. esphome/components/sensor/sensor.h +0 -9
  565. esphome/components/sfa30/sfa30.cpp +0 -4
  566. esphome/components/sgp30/sgp30.cpp +0 -2
  567. esphome/components/sgp4x/sensor.py +1 -1
  568. esphome/components/sgp4x/sgp4x.cpp +0 -2
  569. esphome/components/shelly_dimmer/shelly_dimmer.cpp +0 -2
  570. esphome/components/sht3xd/sht3xd.cpp +0 -2
  571. esphome/components/sht4x/sht4x.cpp +0 -2
  572. esphome/components/shtcx/shtcx.cpp +0 -1
  573. esphome/components/sim800l/__init__.py +2 -4
  574. esphome/components/sm16716/sm16716.cpp +0 -1
  575. esphome/components/sm2135/sm2135.cpp +0 -1
  576. esphome/components/sm2235/sm2235.cpp +0 -1
  577. esphome/components/sm2335/sm2335.cpp +0 -1
  578. esphome/components/sn74hc165/sn74hc165.cpp +0 -1
  579. esphome/components/sn74hc595/sn74hc595.cpp +0 -1
  580. esphome/components/sntp/sntp_component.cpp +0 -1
  581. esphome/components/sound_level/sensor.py +1 -1
  582. esphome/components/speaker/media_player/__init__.py +21 -33
  583. esphome/components/speaker/media_player/audio_pipeline.cpp +4 -7
  584. esphome/components/spi/__init__.py +29 -13
  585. esphome/components/spi/spi.cpp +0 -2
  586. esphome/components/spi_device/spi_device.cpp +1 -4
  587. esphome/components/sprinkler/__init__.py +4 -4
  588. esphome/components/sps30/sps30.cpp +0 -1
  589. esphome/components/ssd1306_base/__init__.py +11 -11
  590. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  591. esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +0 -1
  592. esphome/components/ssd1306_spi/ssd1306_spi.cpp +0 -1
  593. esphome/components/ssd1322_spi/ssd1322_spi.cpp +0 -1
  594. esphome/components/ssd1325_spi/ssd1325_spi.cpp +0 -1
  595. esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +0 -1
  596. esphome/components/ssd1327_spi/ssd1327_spi.cpp +0 -1
  597. esphome/components/ssd1331_spi/ssd1331_spi.cpp +0 -1
  598. esphome/components/ssd1351_spi/ssd1351_spi.cpp +0 -1
  599. esphome/components/st7567_i2c/st7567_i2c.cpp +0 -1
  600. esphome/components/st7567_spi/st7567_spi.cpp +0 -1
  601. esphome/components/st7701s/display.py +10 -14
  602. esphome/components/st7701s/st7701s.cpp +0 -3
  603. esphome/components/st7735/st7735.cpp +0 -1
  604. esphome/components/st7789v/st7789v.cpp +0 -1
  605. esphome/components/st7920/st7920.cpp +0 -1
  606. esphome/components/status_led/light/status_led_light.cpp +0 -2
  607. esphome/components/status_led/status_led.cpp +0 -1
  608. esphome/components/stepper/__init__.py +2 -4
  609. esphome/components/sts3x/sts3x.cpp +0 -1
  610. esphome/components/substitutions/__init__.py +10 -16
  611. esphome/components/substitutions/jinja.py +24 -1
  612. esphome/components/sun/__init__.py +2 -3
  613. esphome/components/switch/__init__.py +31 -1
  614. esphome/components/switch/automation.h +24 -0
  615. esphome/components/switch/switch.cpp +8 -0
  616. esphome/components/switch/switch.h +8 -0
  617. esphome/components/sx126x/sx126x.cpp +0 -2
  618. esphome/components/sx127x/sx127x.cpp +0 -2
  619. esphome/components/sx1509/__init__.py +7 -5
  620. esphome/components/sx1509/output/sx1509_float_output.cpp +1 -1
  621. esphome/components/sx1509/sx1509.cpp +0 -2
  622. esphome/components/syslog/esphome_syslog.cpp +1 -1
  623. esphome/components/tc74/tc74.cpp +0 -1
  624. esphome/components/tca9548a/tca9548a.cpp +0 -1
  625. esphome/components/tca9555/tca9555.cpp +0 -1
  626. esphome/components/tcs34725/tcs34725.cpp +0 -1
  627. esphome/components/tee501/tee501.cpp +0 -1
  628. esphome/components/tem3200/tem3200.cpp +0 -2
  629. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +0 -1
  630. esphome/components/template/cover/template_cover.cpp +0 -1
  631. esphome/components/template/select/template_select.cpp +0 -1
  632. esphome/components/template/text/template_text.cpp +0 -2
  633. esphome/components/template/valve/template_valve.cpp +0 -1
  634. esphome/components/text/__init__.py +0 -1
  635. esphome/components/text/text_traits.h +2 -0
  636. esphome/components/text_sensor/__init__.py +2 -1
  637. esphome/components/text_sensor/text_sensor.cpp +0 -2
  638. esphome/components/text_sensor/text_sensor.h +0 -8
  639. esphome/components/thermostat/climate.py +4 -4
  640. esphome/components/time/__init__.py +7 -4
  641. esphome/components/time/real_time_clock.cpp +16 -3
  642. esphome/components/tlc59208f/tlc59208f_output.cpp +0 -2
  643. esphome/components/tlc5947/tlc5947.cpp +0 -2
  644. esphome/components/tlc5971/tlc5971.cpp +0 -2
  645. esphome/components/tm1621/tm1621.cpp +0 -2
  646. esphome/components/tm1637/tm1637.cpp +0 -2
  647. esphome/components/tm1638/tm1638.cpp +0 -2
  648. esphome/components/tm1651/__init__.py +45 -48
  649. esphome/components/tm1651/tm1651.cpp +213 -47
  650. esphome/components/tm1651/tm1651.h +37 -32
  651. esphome/components/tmp117/tmp117.cpp +0 -2
  652. esphome/components/tsl2561/tsl2561.cpp +0 -1
  653. esphome/components/tsl2591/tsl2591.cpp +0 -1
  654. esphome/components/tt21100/touchscreen/tt21100.cpp +0 -2
  655. esphome/components/ttp229_bsf/ttp229_bsf.cpp +0 -1
  656. esphome/components/ttp229_lsf/ttp229_lsf.cpp +0 -1
  657. esphome/components/tuya/climate/__init__.py +9 -10
  658. esphome/components/tuya/number/__init__.py +8 -6
  659. esphome/components/tx20/tx20.cpp +0 -1
  660. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -1
  661. esphome/components/uart/uart_component_esp8266.cpp +0 -1
  662. esphome/components/uart/uart_component_esp_idf.cpp +0 -2
  663. esphome/components/uart/uart_component_libretiny.cpp +0 -2
  664. esphome/components/uart/uart_component_rp2040.cpp +0 -2
  665. esphome/components/udp/__init__.py +1 -1
  666. esphome/components/ufire_ec/sensor.py +1 -2
  667. esphome/components/ufire_ec/ufire_ec.cpp +0 -2
  668. esphome/components/ufire_ise/sensor.py +1 -2
  669. esphome/components/ufire_ise/ufire_ise.cpp +0 -2
  670. esphome/components/ultrasonic/ultrasonic_sensor.cpp +0 -1
  671. esphome/components/update/__init__.py +0 -1
  672. esphome/components/uptime/sensor/uptime_seconds_sensor.cpp +0 -1
  673. esphome/components/uptime/sensor/uptime_seconds_sensor.h +0 -2
  674. esphome/components/usb_host/usb_host_client.cpp +0 -1
  675. esphome/components/usb_host/usb_host_component.cpp +0 -1
  676. esphome/components/valve/__init__.py +0 -1
  677. esphome/components/veml3235/veml3235.cpp +0 -3
  678. esphome/components/veml7700/veml7700.cpp +0 -2
  679. esphome/components/version/version_text_sensor.cpp +0 -1
  680. esphome/components/version/version_text_sensor.h +0 -1
  681. esphome/components/vl53l0x/vl53l0x_sensor.cpp +0 -4
  682. esphome/components/voice_assistant/voice_assistant.cpp +9 -8
  683. esphome/components/web_server/__init__.py +13 -0
  684. esphome/components/web_server/web_server.cpp +188 -353
  685. esphome/components/web_server/web_server.h +61 -1
  686. esphome/components/web_server_base/__init__.py +1 -1
  687. esphome/components/web_server_base/web_server_base.cpp +2 -0
  688. esphome/components/web_server_base/web_server_base.h +6 -0
  689. esphome/components/web_server_idf/web_server_idf.cpp +10 -8
  690. esphome/components/web_server_idf/web_server_idf.h +2 -0
  691. esphome/components/weikai_i2c/weikai_i2c.cpp +1 -2
  692. esphome/components/weikai_spi/weikai_spi.cpp +1 -1
  693. esphome/components/wifi/__init__.py +17 -43
  694. esphome/components/wifi/wifi_component.cpp +100 -36
  695. esphome/components/wifi/wifi_component.h +5 -1
  696. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -0
  697. esphome/components/wifi/wifi_component_esp_idf.cpp +30 -0
  698. esphome/components/wifi_info/wifi_info_text_sensor.h +0 -6
  699. esphome/components/wifi_signal/wifi_signal_sensor.h +0 -1
  700. esphome/components/wireguard/wireguard.cpp +0 -2
  701. esphome/components/x9c/x9c.cpp +0 -2
  702. esphome/components/xgzp68xx/xgzp68xx.cpp +0 -1
  703. esphome/components/xl9535/xl9535.cpp +0 -2
  704. esphome/components/zephyr/__init__.py +252 -0
  705. esphome/components/zephyr/const.py +16 -0
  706. esphome/components/zephyr/core.cpp +90 -0
  707. esphome/components/zephyr/gpio.cpp +120 -0
  708. esphome/components/zephyr/gpio.h +38 -0
  709. esphome/components/zephyr/pre_build.py.script +4 -0
  710. esphome/components/zephyr/preferences.cpp +156 -0
  711. esphome/components/zephyr/preferences.h +13 -0
  712. esphome/config.py +38 -16
  713. esphome/config_helpers.py +1 -2
  714. esphome/config_validation.py +12 -16
  715. esphome/const.py +26 -1
  716. esphome/core/__init__.py +92 -51
  717. esphome/core/application.cpp +75 -21
  718. esphome/core/application.h +106 -171
  719. esphome/core/color.h +10 -0
  720. esphome/core/component.cpp +41 -25
  721. esphome/core/component.h +9 -6
  722. esphome/core/component_iterator.cpp +61 -261
  723. esphome/core/component_iterator.h +15 -0
  724. esphome/core/config.py +26 -11
  725. esphome/core/defines.h +40 -2
  726. esphome/core/entity_base.h +18 -0
  727. esphome/core/entity_helpers.py +45 -10
  728. esphome/core/helpers.cpp +8 -15
  729. esphome/core/helpers.h +60 -6
  730. esphome/core/lock_free_queue.h +1 -1
  731. esphome/core/scheduler.cpp +311 -77
  732. esphome/core/scheduler.h +141 -28
  733. esphome/cpp_generator.py +2 -6
  734. esphome/cpp_helpers.py +1 -1
  735. esphome/dashboard/dashboard.py +2 -3
  736. esphome/dashboard/dns.py +2 -8
  737. esphome/dashboard/web_server.py +34 -19
  738. esphome/espota2.py +1 -4
  739. esphome/git.py +3 -1
  740. esphome/helpers.py +23 -4
  741. esphome/log.py +3 -1
  742. esphome/mqtt.py +3 -5
  743. esphome/platformio_api.py +7 -4
  744. esphome/types.py +12 -0
  745. esphome/util.py +20 -8
  746. esphome/voluptuous_schema.py +4 -3
  747. esphome/vscode.py +1 -2
  748. esphome/wizard.py +1 -4
  749. esphome/writer.py +16 -108
  750. esphome/yaml_util.py +7 -5
  751. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/METADATA +13 -14
  752. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/RECORD +757 -677
  753. esphome/components/mipi_spi/models/commands.py +0 -82
  754. /esphome/components/nfc/binary_sensor/{binary_sensor.h → nfc_binary_sensor.h} +0 -0
  755. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/WHEEL +0 -0
  756. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/entry_points.txt +0 -0
  757. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/licenses/LICENSE +0 -0
  758. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/top_level.txt +0 -0
@@ -35,13 +35,37 @@
35
35
  // bt_trace.h
36
36
  #undef TAG
37
37
 
38
- namespace esphome {
39
- namespace esp32_ble_tracker {
38
+ namespace esphome::esp32_ble_tracker {
40
39
 
41
40
  static const char *const TAG = "esp32_ble_tracker";
42
41
 
43
42
  ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
44
43
 
44
+ const char *client_state_to_string(ClientState state) {
45
+ switch (state) {
46
+ case ClientState::INIT:
47
+ return "INIT";
48
+ case ClientState::DISCONNECTING:
49
+ return "DISCONNECTING";
50
+ case ClientState::IDLE:
51
+ return "IDLE";
52
+ case ClientState::SEARCHING:
53
+ return "SEARCHING";
54
+ case ClientState::DISCOVERED:
55
+ return "DISCOVERED";
56
+ case ClientState::READY_TO_CONNECT:
57
+ return "READY_TO_CONNECT";
58
+ case ClientState::CONNECTING:
59
+ return "CONNECTING";
60
+ case ClientState::CONNECTED:
61
+ return "CONNECTED";
62
+ case ClientState::ESTABLISHED:
63
+ return "ESTABLISHED";
64
+ default:
65
+ return "UNKNOWN";
66
+ }
67
+ }
68
+
45
69
  float ESP32BLETracker::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
46
70
 
47
71
  void ESP32BLETracker::setup() {
@@ -50,13 +74,6 @@ void ESP32BLETracker::setup() {
50
74
  ESP_LOGE(TAG, "BLE Tracker was marked failed by ESP32BLE");
51
75
  return;
52
76
  }
53
- RAMAllocator<BLEScanResult> allocator;
54
- this->scan_ring_buffer_ = allocator.allocate(SCAN_RESULT_BUFFER_SIZE);
55
-
56
- if (this->scan_ring_buffer_ == nullptr) {
57
- ESP_LOGE(TAG, "Could not allocate ring buffer for BLE Tracker!");
58
- this->mark_failed();
59
- }
60
77
 
61
78
  global_esp32_ble_tracker = this;
62
79
 
@@ -84,118 +101,49 @@ void ESP32BLETracker::loop() {
84
101
  this->start_scan();
85
102
  }
86
103
  }
87
- int connecting = 0;
88
- int discovered = 0;
89
- int searching = 0;
90
- int disconnecting = 0;
91
- for (auto *client : this->clients_) {
92
- switch (client->state()) {
93
- case ClientState::DISCONNECTING:
94
- disconnecting++;
95
- break;
96
- case ClientState::DISCOVERED:
97
- discovered++;
98
- break;
99
- case ClientState::SEARCHING:
100
- searching++;
101
- break;
102
- case ClientState::CONNECTING:
103
- case ClientState::READY_TO_CONNECT:
104
- connecting++;
105
- break;
106
- default:
107
- break;
108
- }
109
- }
110
- if (connecting != connecting_ || discovered != discovered_ || searching != searching_ ||
111
- disconnecting != disconnecting_) {
112
- connecting_ = connecting;
113
- discovered_ = discovered;
114
- searching_ = searching;
115
- disconnecting_ = disconnecting;
116
- ESP_LOGD(TAG, "connecting: %d, discovered: %d, searching: %d, disconnecting: %d", connecting_, discovered_,
117
- searching_, disconnecting_);
118
- }
119
- bool promote_to_connecting = discovered && !searching && !connecting;
120
104
 
121
- // Process scan results from lock-free SPSC ring buffer
122
- // Consumer side: This runs in the main loop thread
105
+ // Check for scan timeout - moved here from scheduler to avoid false reboots
106
+ // when the loop is blocked
123
107
  if (this->scanner_state_ == ScannerState::RUNNING) {
124
- // Load our own index with relaxed ordering (we're the only writer)
125
- uint8_t read_idx = this->ring_read_index_.load(std::memory_order_relaxed);
126
-
127
- // Load producer's index with acquire to see their latest writes
128
- uint8_t write_idx = this->ring_write_index_.load(std::memory_order_acquire);
129
-
130
- while (read_idx != write_idx) {
131
- // Process one result at a time directly from ring buffer
132
- BLEScanResult &scan_result = this->scan_ring_buffer_[read_idx];
133
-
134
- if (this->raw_advertisements_) {
135
- for (auto *listener : this->listeners_) {
136
- listener->parse_devices(&scan_result, 1);
137
- }
138
- for (auto *client : this->clients_) {
139
- client->parse_devices(&scan_result, 1);
140
- }
141
- }
142
-
143
- if (this->parse_advertisements_) {
144
- ESPBTDevice device;
145
- device.parse_scan_rst(scan_result);
146
-
147
- bool found = false;
148
- for (auto *listener : this->listeners_) {
149
- if (listener->parse_device(device))
150
- found = true;
151
- }
152
-
153
- for (auto *client : this->clients_) {
154
- if (client->parse_device(device)) {
155
- found = true;
156
- if (!connecting && client->state() == ClientState::DISCOVERED) {
157
- promote_to_connecting = true;
158
- }
159
- }
160
- }
161
-
162
- if (!found && !this->scan_continuous_) {
163
- this->print_bt_device_info(device);
108
+ switch (this->scan_timeout_state_) {
109
+ case ScanTimeoutState::MONITORING: {
110
+ uint32_t now = App.get_loop_component_start_time();
111
+ uint32_t timeout_ms = this->scan_duration_ * 2000;
112
+ // Robust time comparison that handles rollover correctly
113
+ // This works because unsigned arithmetic wraps around predictably
114
+ if ((now - this->scan_start_time_) > timeout_ms) {
115
+ // First time we've seen the timeout exceeded - wait one more loop iteration
116
+ // This ensures all components have had a chance to process pending events
117
+ // This is because esp32_ble may not have run yet and called
118
+ // gap_scan_event_handler yet when the loop unblocks
119
+ ESP_LOGW(TAG, "Scan timeout exceeded");
120
+ this->scan_timeout_state_ = ScanTimeoutState::EXCEEDED_WAIT;
164
121
  }
122
+ break;
165
123
  }
124
+ case ScanTimeoutState::EXCEEDED_WAIT:
125
+ // We've waited at least one full loop iteration, and scan is still running
126
+ ESP_LOGE(TAG, "Scan never terminated, rebooting");
127
+ App.reboot();
128
+ break;
166
129
 
167
- // Move to next entry in ring buffer
168
- read_idx = (read_idx + 1) % SCAN_RESULT_BUFFER_SIZE;
169
-
170
- // Store with release to ensure reads complete before index update
171
- this->ring_read_index_.store(read_idx, std::memory_order_release);
172
- }
173
-
174
- // Log dropped results periodically
175
- size_t dropped = this->scan_results_dropped_.exchange(0, std::memory_order_relaxed);
176
- if (dropped > 0) {
177
- ESP_LOGW(TAG, "Dropped %zu BLE scan results due to buffer overflow", dropped);
130
+ case ScanTimeoutState::INACTIVE:
131
+ // This case should be unreachable - scanner and timeout states are always synchronized
132
+ break;
178
133
  }
179
134
  }
180
- if (this->scanner_state_ == ScannerState::STOPPED) {
181
- this->end_of_scan_(); // Change state to IDLE
135
+
136
+ ClientStateCounts counts = this->count_client_states_();
137
+ if (counts != this->client_state_counts_) {
138
+ this->client_state_counts_ = counts;
139
+ ESP_LOGD(TAG, "connecting: %d, discovered: %d, searching: %d, disconnecting: %d",
140
+ this->client_state_counts_.connecting, this->client_state_counts_.discovered,
141
+ this->client_state_counts_.searching, this->client_state_counts_.disconnecting);
182
142
  }
143
+
183
144
  if (this->scanner_state_ == ScannerState::FAILED ||
184
145
  (this->scan_set_param_failed_ && this->scanner_state_ == ScannerState::RUNNING)) {
185
- this->stop_scan_();
186
- if (this->scan_start_fail_count_ == std::numeric_limits<uint8_t>::max()) {
187
- ESP_LOGE(TAG, "Scan could not restart after %d attempts, rebooting to restore stack (IDF)",
188
- std::numeric_limits<uint8_t>::max());
189
- App.reboot();
190
- }
191
- if (this->scan_start_failed_) {
192
- ESP_LOGE(TAG, "Scan start failed: %d", this->scan_start_failed_);
193
- this->scan_start_failed_ = ESP_BT_STATUS_SUCCESS;
194
- }
195
- if (this->scan_set_param_failed_) {
196
- ESP_LOGE(TAG, "Scan set param failed: %d", this->scan_set_param_failed_);
197
- this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS;
198
- }
146
+ this->handle_scanner_failure_();
199
147
  }
200
148
  /*
201
149
 
@@ -210,13 +158,12 @@ void ESP32BLETracker::loop() {
210
158
  https://github.com/espressif/esp-idf/issues/6688
211
159
 
212
160
  */
213
- if (this->scanner_state_ == ScannerState::IDLE && !connecting && !disconnecting && !promote_to_connecting) {
161
+ bool promote_to_connecting = counts.discovered && !counts.searching && !counts.connecting;
162
+
163
+ if (this->scanner_state_ == ScannerState::IDLE && !counts.connecting && !counts.disconnecting &&
164
+ !promote_to_connecting) {
214
165
  #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
215
- if (this->coex_prefer_ble_) {
216
- this->coex_prefer_ble_ = false;
217
- ESP_LOGD(TAG, "Setting coexistence preference to balanced.");
218
- esp_coex_preference_set(ESP_COEX_PREFER_BALANCE); // Reset to default
219
- }
166
+ this->update_coex_preference_(false);
220
167
  #endif
221
168
  if (this->scan_continuous_) {
222
169
  this->start_scan_(false); // first = false
@@ -224,31 +171,13 @@ void ESP32BLETracker::loop() {
224
171
  }
225
172
  // If there is a discovered client and no connecting
226
173
  // clients and no clients using the scanner to search for
227
- // devices, then stop scanning and promote the discovered
228
- // client to ready to connect.
174
+ // devices, then promote the discovered client to ready to connect.
175
+ // We check both RUNNING and IDLE states because:
176
+ // - RUNNING: gap_scan_event_handler initiates stop_scan_() but promotion can happen immediately
177
+ // - IDLE: Scanner has already stopped (naturally or by gap_scan_event_handler)
229
178
  if (promote_to_connecting &&
230
179
  (this->scanner_state_ == ScannerState::RUNNING || this->scanner_state_ == ScannerState::IDLE)) {
231
- for (auto *client : this->clients_) {
232
- if (client->state() == ClientState::DISCOVERED) {
233
- if (this->scanner_state_ == ScannerState::RUNNING) {
234
- ESP_LOGD(TAG, "Stopping scan to make connection");
235
- this->stop_scan_();
236
- } else if (this->scanner_state_ == ScannerState::IDLE) {
237
- ESP_LOGD(TAG, "Promoting client to connect");
238
- // We only want to promote one client at a time.
239
- // once the scanner is fully stopped.
240
- #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
241
- ESP_LOGD(TAG, "Setting coexistence to Bluetooth to make connection.");
242
- if (!this->coex_prefer_ble_) {
243
- this->coex_prefer_ble_ = true;
244
- esp_coex_preference_set(ESP_COEX_PREFER_BT); // Prioritize Bluetooth
245
- }
246
- #endif
247
- client->set_state(ClientState::READY_TO_CONNECT);
248
- }
249
- break;
250
- }
251
- }
180
+ this->try_promote_discovered_clients_();
252
181
  }
253
182
  }
254
183
 
@@ -264,18 +193,11 @@ void ESP32BLETracker::ble_before_disabled_event_handler() { this->stop_scan_();
264
193
 
265
194
  void ESP32BLETracker::stop_scan_() {
266
195
  if (this->scanner_state_ != ScannerState::RUNNING && this->scanner_state_ != ScannerState::FAILED) {
267
- if (this->scanner_state_ == ScannerState::IDLE) {
268
- ESP_LOGE(TAG, "Scan is already stopped while trying to stop.");
269
- } else if (this->scanner_state_ == ScannerState::STARTING) {
270
- ESP_LOGE(TAG, "Scan is starting while trying to stop.");
271
- } else if (this->scanner_state_ == ScannerState::STOPPING) {
272
- ESP_LOGE(TAG, "Scan is already stopping while trying to stop.");
273
- } else if (this->scanner_state_ == ScannerState::STOPPED) {
274
- ESP_LOGE(TAG, "Scan is already stopped while trying to stop.");
275
- }
196
+ ESP_LOGE(TAG, "Cannot stop scan: %s", this->scanner_state_to_string_(this->scanner_state_));
276
197
  return;
277
198
  }
278
- this->cancel_timeout("scan");
199
+ // Reset timeout state machine when stopping scan
200
+ this->scan_timeout_state_ = ScanTimeoutState::INACTIVE;
279
201
  this->set_scanner_state_(ScannerState::STOPPING);
280
202
  esp_err_t err = esp_ble_gap_stop_scanning();
281
203
  if (err != ESP_OK) {
@@ -290,17 +212,7 @@ void ESP32BLETracker::start_scan_(bool first) {
290
212
  return;
291
213
  }
292
214
  if (this->scanner_state_ != ScannerState::IDLE) {
293
- if (this->scanner_state_ == ScannerState::STARTING) {
294
- ESP_LOGE(TAG, "Cannot start scan while already starting.");
295
- } else if (this->scanner_state_ == ScannerState::RUNNING) {
296
- ESP_LOGE(TAG, "Cannot start scan while already running.");
297
- } else if (this->scanner_state_ == ScannerState::STOPPING) {
298
- ESP_LOGE(TAG, "Cannot start scan while already stopping.");
299
- } else if (this->scanner_state_ == ScannerState::FAILED) {
300
- ESP_LOGE(TAG, "Cannot start scan while already failed.");
301
- } else if (this->scanner_state_ == ScannerState::STOPPED) {
302
- ESP_LOGE(TAG, "Cannot start scan while already stopped.");
303
- }
215
+ this->log_unexpected_state_("start scan", ScannerState::IDLE);
304
216
  return;
305
217
  }
306
218
  this->set_scanner_state_(ScannerState::STARTING);
@@ -309,18 +221,19 @@ void ESP32BLETracker::start_scan_(bool first) {
309
221
  for (auto *listener : this->listeners_)
310
222
  listener->on_scan_end();
311
223
  }
224
+ #ifdef USE_ESP32_BLE_DEVICE
312
225
  this->already_discovered_.clear();
226
+ #endif
313
227
  this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE;
314
228
  this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
315
229
  this->scan_params_.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
316
230
  this->scan_params_.scan_interval = this->scan_interval_;
317
231
  this->scan_params_.scan_window = this->scan_window_;
318
232
 
319
- // Start timeout before scan is started. Otherwise scan never starts if any error.
320
- this->set_timeout("scan", this->scan_duration_ * 2000, []() {
321
- ESP_LOGE(TAG, "Scan never terminated, rebooting to restore stack (IDF)");
322
- App.reboot();
323
- });
233
+ // Start timeout monitoring in loop() instead of using scheduler
234
+ // This prevents false reboots when the loop is blocked
235
+ this->scan_start_time_ = App.get_loop_component_start_time();
236
+ this->scan_timeout_state_ = ScanTimeoutState::MONITORING;
324
237
 
325
238
  esp_err_t err = esp_ble_gap_set_scan_params(&this->scan_params_);
326
239
  if (err != ESP_OK) {
@@ -334,21 +247,6 @@ void ESP32BLETracker::start_scan_(bool first) {
334
247
  }
335
248
  }
336
249
 
337
- void ESP32BLETracker::end_of_scan_() {
338
- // The lock must be held when calling this function.
339
- if (this->scanner_state_ != ScannerState::STOPPED) {
340
- ESP_LOGE(TAG, "end_of_scan_ called while scanner is not stopped.");
341
- return;
342
- }
343
- ESP_LOGD(TAG, "End of scan, set scanner state to IDLE.");
344
- this->already_discovered_.clear();
345
- this->cancel_timeout("scan");
346
-
347
- for (auto *listener : this->listeners_)
348
- listener->on_scan_end();
349
- this->set_scanner_state_(ScannerState::IDLE);
350
- }
351
-
352
250
  void ESP32BLETracker::register_client(ESPBTClient *client) {
353
251
  client->app_id = ++this->app_id_;
354
252
  this->clients_.push_back(client);
@@ -381,6 +279,8 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() {
381
279
  }
382
280
 
383
281
  void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
282
+ // Note: This handler is called from the main loop context, not directly from the BT task.
283
+ // The esp32_ble component queues events via enqueue_ble_event() and processes them in loop().
384
284
  switch (event) {
385
285
  case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
386
286
  this->gap_scan_set_param_complete_(param->scan_param_cmpl);
@@ -401,51 +301,32 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga
401
301
  }
402
302
 
403
303
  void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) {
304
+ // Note: This handler is called from the main loop context via esp32_ble's event queue.
305
+ // We process advertisements immediately instead of buffering them.
404
306
  ESP_LOGV(TAG, "gap_scan_result - event %d", scan_result.search_evt);
405
307
 
406
308
  if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
407
- // Lock-free SPSC ring buffer write (Producer side)
408
- // This runs in the ESP-IDF Bluetooth stack callback thread
409
- // IMPORTANT: Only this thread writes to ring_write_index_
410
-
411
- // Load our own index with relaxed ordering (we're the only writer)
412
- uint8_t write_idx = this->ring_write_index_.load(std::memory_order_relaxed);
413
- uint8_t next_write_idx = (write_idx + 1) % SCAN_RESULT_BUFFER_SIZE;
414
-
415
- // Load consumer's index with acquire to see their latest updates
416
- uint8_t read_idx = this->ring_read_index_.load(std::memory_order_acquire);
417
-
418
- // Check if buffer is full
419
- if (next_write_idx != read_idx) {
420
- // Write to ring buffer
421
- this->scan_ring_buffer_[write_idx] = scan_result;
422
-
423
- // Store with release to ensure the write is visible before index update
424
- this->ring_write_index_.store(next_write_idx, std::memory_order_release);
425
- } else {
426
- // Buffer full, track dropped results
427
- this->scan_results_dropped_.fetch_add(1, std::memory_order_relaxed);
309
+ // Process the scan result immediately
310
+ bool found_discovered_client = this->process_scan_result_(scan_result);
311
+
312
+ // If we found a discovered client that needs promotion, stop scanning
313
+ // This replaces the promote_to_connecting logic from loop()
314
+ if (found_discovered_client && this->scanner_state_ == ScannerState::RUNNING) {
315
+ ESP_LOGD(TAG, "Found discovered client, stopping scan for connection");
316
+ this->stop_scan_();
428
317
  }
429
318
  } else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
430
319
  // Scan finished on its own
431
320
  if (this->scanner_state_ != ScannerState::RUNNING) {
432
- if (this->scanner_state_ == ScannerState::STOPPING) {
433
- ESP_LOGE(TAG, "Scan was not running when scan completed.");
434
- } else if (this->scanner_state_ == ScannerState::STARTING) {
435
- ESP_LOGE(TAG, "Scan was not started when scan completed.");
436
- } else if (this->scanner_state_ == ScannerState::FAILED) {
437
- ESP_LOGE(TAG, "Scan was in failed state when scan completed.");
438
- } else if (this->scanner_state_ == ScannerState::IDLE) {
439
- ESP_LOGE(TAG, "Scan was idle when scan completed.");
440
- } else if (this->scanner_state_ == ScannerState::STOPPED) {
441
- ESP_LOGE(TAG, "Scan was stopped when scan completed.");
442
- }
321
+ this->log_unexpected_state_("scan complete", ScannerState::RUNNING);
443
322
  }
444
- this->set_scanner_state_(ScannerState::STOPPED);
323
+ // Scan completed naturally, perform cleanup and transition to IDLE
324
+ this->cleanup_scan_state_(false);
445
325
  }
446
326
  }
447
327
 
448
328
  void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param &param) {
329
+ // Called from main loop context via gap_event_handler after being queued from BT task
449
330
  ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status);
450
331
  if (param.status == ESP_BT_STATUS_DONE) {
451
332
  this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS;
@@ -455,20 +336,11 @@ void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t:
455
336
  }
456
337
 
457
338
  void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param &param) {
339
+ // Called from main loop context via gap_event_handler after being queued from BT task
458
340
  ESP_LOGV(TAG, "gap_scan_start_complete - status %d", param.status);
459
341
  this->scan_start_failed_ = param.status;
460
342
  if (this->scanner_state_ != ScannerState::STARTING) {
461
- if (this->scanner_state_ == ScannerState::RUNNING) {
462
- ESP_LOGE(TAG, "Scan was already running when start complete.");
463
- } else if (this->scanner_state_ == ScannerState::STOPPING) {
464
- ESP_LOGE(TAG, "Scan was stopping when start complete.");
465
- } else if (this->scanner_state_ == ScannerState::FAILED) {
466
- ESP_LOGE(TAG, "Scan was in failed state when start complete.");
467
- } else if (this->scanner_state_ == ScannerState::IDLE) {
468
- ESP_LOGE(TAG, "Scan was idle when start complete.");
469
- } else if (this->scanner_state_ == ScannerState::STOPPED) {
470
- ESP_LOGE(TAG, "Scan was stopped when start complete.");
471
- }
343
+ this->log_unexpected_state_("start complete", ScannerState::STARTING);
472
344
  }
473
345
  if (param.status == ESP_BT_STATUS_SUCCESS) {
474
346
  this->scan_start_fail_count_ = 0;
@@ -482,21 +354,15 @@ void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble
482
354
  }
483
355
 
484
356
  void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param &param) {
357
+ // Called from main loop context via gap_event_handler after being queued from BT task
358
+ // This allows us to safely transition to IDLE state and perform cleanup without race conditions
485
359
  ESP_LOGV(TAG, "gap_scan_stop_complete - status %d", param.status);
486
360
  if (this->scanner_state_ != ScannerState::STOPPING) {
487
- if (this->scanner_state_ == ScannerState::RUNNING) {
488
- ESP_LOGE(TAG, "Scan was not running when stop complete.");
489
- } else if (this->scanner_state_ == ScannerState::STARTING) {
490
- ESP_LOGE(TAG, "Scan was not started when stop complete.");
491
- } else if (this->scanner_state_ == ScannerState::FAILED) {
492
- ESP_LOGE(TAG, "Scan was in failed state when stop complete.");
493
- } else if (this->scanner_state_ == ScannerState::IDLE) {
494
- ESP_LOGE(TAG, "Scan was idle when stop complete.");
495
- } else if (this->scanner_state_ == ScannerState::STOPPED) {
496
- ESP_LOGE(TAG, "Scan was stopped when stop complete.");
497
- }
361
+ this->log_unexpected_state_("stop complete", ScannerState::STOPPING);
498
362
  }
499
- this->set_scanner_state_(ScannerState::STOPPED);
363
+
364
+ // Perform cleanup and transition to IDLE
365
+ this->cleanup_scan_state_(true);
500
366
  }
501
367
 
502
368
  void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
@@ -511,6 +377,7 @@ void ESP32BLETracker::set_scanner_state_(ScannerState state) {
511
377
  this->scanner_state_callbacks_.call(state);
512
378
  }
513
379
 
380
+ #ifdef USE_ESP32_BLE_DEVICE
514
381
  ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
515
382
  optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
516
383
  if (!data.uuid.contains(0x4C, 0x00))
@@ -751,13 +618,16 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) {
751
618
  }
752
619
  }
753
620
  }
621
+
754
622
  std::string ESPBTDevice::address_str() const {
755
623
  char mac[24];
756
624
  snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2],
757
625
  this->address_[3], this->address_[4], this->address_[5]);
758
626
  return mac;
759
627
  }
628
+
760
629
  uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); }
630
+ #endif // USE_ESP32_BLE_DEVICE
761
631
 
762
632
  void ESP32BLETracker::dump_config() {
763
633
  ESP_LOGCONFIG(TAG, "BLE Tracker:");
@@ -769,33 +639,16 @@ void ESP32BLETracker::dump_config() {
769
639
  " Continuous Scanning: %s",
770
640
  this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
771
641
  this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
772
- switch (this->scanner_state_) {
773
- case ScannerState::IDLE:
774
- ESP_LOGCONFIG(TAG, " Scanner State: IDLE");
775
- break;
776
- case ScannerState::STARTING:
777
- ESP_LOGCONFIG(TAG, " Scanner State: STARTING");
778
- break;
779
- case ScannerState::RUNNING:
780
- ESP_LOGCONFIG(TAG, " Scanner State: RUNNING");
781
- break;
782
- case ScannerState::STOPPING:
783
- ESP_LOGCONFIG(TAG, " Scanner State: STOPPING");
784
- break;
785
- case ScannerState::STOPPED:
786
- ESP_LOGCONFIG(TAG, " Scanner State: STOPPED");
787
- break;
788
- case ScannerState::FAILED:
789
- ESP_LOGCONFIG(TAG, " Scanner State: FAILED");
790
- break;
791
- }
792
- ESP_LOGCONFIG(TAG, " Connecting: %d, discovered: %d, searching: %d, disconnecting: %d", connecting_, discovered_,
793
- searching_, disconnecting_);
642
+ ESP_LOGCONFIG(TAG, " Scanner State: %s", this->scanner_state_to_string_(this->scanner_state_));
643
+ ESP_LOGCONFIG(TAG, " Connecting: %d, discovered: %d, searching: %d, disconnecting: %d",
644
+ this->client_state_counts_.connecting, this->client_state_counts_.discovered,
645
+ this->client_state_counts_.searching, this->client_state_counts_.disconnecting);
794
646
  if (this->scan_start_fail_count_) {
795
647
  ESP_LOGCONFIG(TAG, " Scan Start Fail Count: %d", this->scan_start_fail_count_);
796
648
  }
797
649
  }
798
650
 
651
+ #ifdef USE_ESP32_BLE_DEVICE
799
652
  void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
800
653
  const uint64_t address = device.address_uint64();
801
654
  for (auto &disc : this->already_discovered_) {
@@ -867,7 +720,157 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
867
720
  ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
868
721
  }
869
722
 
870
- } // namespace esp32_ble_tracker
871
- } // namespace esphome
723
+ bool ESP32BLETracker::has_connecting_clients_() const {
724
+ for (auto *client : this->clients_) {
725
+ auto state = client->state();
726
+ if (state == ClientState::CONNECTING || state == ClientState::READY_TO_CONNECT) {
727
+ return true;
728
+ }
729
+ }
730
+ return false;
731
+ }
732
+ #endif // USE_ESP32_BLE_DEVICE
733
+
734
+ bool ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
735
+ bool found_discovered_client = false;
736
+
737
+ // Process raw advertisements
738
+ if (this->raw_advertisements_) {
739
+ for (auto *listener : this->listeners_) {
740
+ listener->parse_devices(&scan_result, 1);
741
+ }
742
+ for (auto *client : this->clients_) {
743
+ client->parse_devices(&scan_result, 1);
744
+ }
745
+ }
746
+
747
+ // Process parsed advertisements
748
+ if (this->parse_advertisements_) {
749
+ #ifdef USE_ESP32_BLE_DEVICE
750
+ ESPBTDevice device;
751
+ device.parse_scan_rst(scan_result);
752
+
753
+ bool found = false;
754
+ for (auto *listener : this->listeners_) {
755
+ if (listener->parse_device(device))
756
+ found = true;
757
+ }
758
+
759
+ for (auto *client : this->clients_) {
760
+ if (client->parse_device(device)) {
761
+ found = true;
762
+ // Check if this client is discovered and needs promotion
763
+ if (client->state() == ClientState::DISCOVERED) {
764
+ // Only check for connecting clients if we found a discovered client
765
+ // This matches the original logic: !connecting && client->state() == DISCOVERED
766
+ if (!this->has_connecting_clients_()) {
767
+ found_discovered_client = true;
768
+ }
769
+ }
770
+ }
771
+ }
772
+
773
+ if (!found && !this->scan_continuous_) {
774
+ this->print_bt_device_info(device);
775
+ }
776
+ #endif // USE_ESP32_BLE_DEVICE
777
+ }
778
+
779
+ return found_discovered_client;
780
+ }
781
+
782
+ void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) {
783
+ ESP_LOGD(TAG, "Scan %scomplete, set scanner state to IDLE.", is_stop_complete ? "stop " : "");
784
+ #ifdef USE_ESP32_BLE_DEVICE
785
+ this->already_discovered_.clear();
786
+ #endif
787
+ // Reset timeout state machine instead of cancelling scheduler timeout
788
+ this->scan_timeout_state_ = ScanTimeoutState::INACTIVE;
789
+
790
+ for (auto *listener : this->listeners_)
791
+ listener->on_scan_end();
792
+
793
+ this->set_scanner_state_(ScannerState::IDLE);
794
+ }
795
+
796
+ void ESP32BLETracker::handle_scanner_failure_() {
797
+ this->stop_scan_();
798
+ if (this->scan_start_fail_count_ == std::numeric_limits<uint8_t>::max()) {
799
+ ESP_LOGE(TAG, "Scan could not restart after %d attempts, rebooting to restore stack (IDF)",
800
+ std::numeric_limits<uint8_t>::max());
801
+ App.reboot();
802
+ }
803
+ if (this->scan_start_failed_) {
804
+ ESP_LOGE(TAG, "Scan start failed: %d", this->scan_start_failed_);
805
+ this->scan_start_failed_ = ESP_BT_STATUS_SUCCESS;
806
+ }
807
+ if (this->scan_set_param_failed_) {
808
+ ESP_LOGE(TAG, "Scan set param failed: %d", this->scan_set_param_failed_);
809
+ this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS;
810
+ }
811
+ }
812
+
813
+ void ESP32BLETracker::try_promote_discovered_clients_() {
814
+ // Only promote the first discovered client to avoid multiple simultaneous connections
815
+ for (auto *client : this->clients_) {
816
+ if (client->state() != ClientState::DISCOVERED) {
817
+ continue;
818
+ }
819
+
820
+ if (this->scanner_state_ == ScannerState::RUNNING) {
821
+ ESP_LOGD(TAG, "Stopping scan to make connection");
822
+ this->stop_scan_();
823
+ // Don't wait for scan stop complete - promote immediately.
824
+ // This is safe because ESP-IDF processes BLE commands sequentially through its internal mailbox queue.
825
+ // This guarantees that the stop scan command will be fully processed before any subsequent connect command,
826
+ // preventing race conditions or overlapping operations.
827
+ }
872
828
 
829
+ ESP_LOGD(TAG, "Promoting client to connect");
830
+ #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
831
+ this->update_coex_preference_(true);
873
832
  #endif
833
+ client->set_state(ClientState::READY_TO_CONNECT);
834
+ break;
835
+ }
836
+ }
837
+
838
+ const char *ESP32BLETracker::scanner_state_to_string_(ScannerState state) const {
839
+ switch (state) {
840
+ case ScannerState::IDLE:
841
+ return "IDLE";
842
+ case ScannerState::STARTING:
843
+ return "STARTING";
844
+ case ScannerState::RUNNING:
845
+ return "RUNNING";
846
+ case ScannerState::STOPPING:
847
+ return "STOPPING";
848
+ case ScannerState::FAILED:
849
+ return "FAILED";
850
+ default:
851
+ return "UNKNOWN";
852
+ }
853
+ }
854
+
855
+ void ESP32BLETracker::log_unexpected_state_(const char *operation, ScannerState expected_state) const {
856
+ ESP_LOGE(TAG, "Unexpected state: %s on %s, expected: %s", this->scanner_state_to_string_(this->scanner_state_),
857
+ operation, this->scanner_state_to_string_(expected_state));
858
+ }
859
+
860
+ #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE
861
+ void ESP32BLETracker::update_coex_preference_(bool force_ble) {
862
+ if (force_ble && !this->coex_prefer_ble_) {
863
+ ESP_LOGD(TAG, "Setting coexistence to Bluetooth to make connection.");
864
+ this->coex_prefer_ble_ = true;
865
+ esp_coex_preference_set(ESP_COEX_PREFER_BT); // Prioritize Bluetooth
866
+ } else if (!force_ble && this->coex_prefer_ble_) {
867
+ ESP_LOGD(TAG, "Setting coexistence preference to balanced.");
868
+ this->coex_prefer_ble_ = false;
869
+ esp_coex_preference_set(ESP_COEX_PREFER_BALANCE); // Reset to default
870
+ }
871
+ }
872
+ #endif
873
+
874
+ } // namespace esphome::esp32_ble_tracker
875
+
876
+ #endif // USE_ESP32