esphome 2025.9.2__py3-none-any.whl → 2025.10.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. esphome/__main__.py +87 -31
  2. esphome/address_cache.py +142 -0
  3. esphome/automation.py +130 -32
  4. esphome/build_gen/platformio.py +1 -3
  5. esphome/codegen.py +1 -0
  6. esphome/components/animation/animation.cpp +2 -2
  7. esphome/components/api/__init__.py +167 -3
  8. esphome/components/api/api_connection.cpp +84 -41
  9. esphome/components/api/api_connection.h +22 -16
  10. esphome/components/api/api_frame_helper.cpp +33 -19
  11. esphome/components/api/api_frame_helper.h +19 -4
  12. esphome/components/api/api_frame_helper_noise.cpp +41 -53
  13. esphome/components/api/api_frame_helper_noise.h +1 -1
  14. esphome/components/api/api_frame_helper_plaintext.cpp +22 -31
  15. esphome/components/api/api_frame_helper_plaintext.h +1 -1
  16. esphome/components/api/api_pb2.cpp +189 -15
  17. esphome/components/api/api_pb2.h +132 -20
  18. esphome/components/api/api_pb2_dump.cpp +97 -9
  19. esphome/components/api/api_pb2_service.cpp +118 -160
  20. esphome/components/api/api_pb2_service.h +31 -3
  21. esphome/components/api/api_server.cpp +78 -11
  22. esphome/components/api/api_server.h +32 -4
  23. esphome/components/api/custom_api_device.h +8 -8
  24. esphome/components/api/homeassistant_service.h +123 -6
  25. esphome/components/api/proto.h +6 -2
  26. esphome/components/api/user_services.h +2 -2
  27. esphome/components/as7341/sensor.py +1 -1
  28. esphome/components/audio/__init__.py +1 -1
  29. esphome/components/audio/audio.cpp +1 -1
  30. esphome/components/audio/audio_decoder.cpp +9 -9
  31. esphome/components/bl0906/bl0906.cpp +2 -2
  32. esphome/components/bl0942/bl0942.cpp +2 -2
  33. esphome/components/ble_client/__init__.py +1 -1
  34. esphome/components/bluetooth_proxy/__init__.py +4 -30
  35. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +11 -4
  36. esphome/components/bluetooth_proxy/bluetooth_connection.h +2 -2
  37. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
  38. esphome/components/camera_encoder/__init__.py +2 -4
  39. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +4 -2
  40. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +3 -1
  41. esphome/components/canbus/canbus.cpp +7 -5
  42. esphome/components/canbus/canbus.h +4 -4
  43. esphome/components/captive_portal/__init__.py +18 -1
  44. esphome/components/captive_portal/captive_portal.cpp +40 -46
  45. esphome/components/captive_portal/captive_portal.h +20 -22
  46. esphome/components/captive_portal/dns_server_esp32_idf.cpp +205 -0
  47. esphome/components/captive_portal/dns_server_esp32_idf.h +27 -0
  48. esphome/components/ccs811/ccs811.cpp +1 -1
  49. esphome/components/climate/climate.cpp +10 -7
  50. esphome/components/cm1106/cm1106.cpp +1 -1
  51. esphome/components/copy/lock/copy_lock.cpp +1 -1
  52. esphome/components/cover/cover.cpp +1 -0
  53. esphome/components/daikin_arc/daikin_arc.cpp +19 -12
  54. esphome/components/deep_sleep/__init__.py +9 -2
  55. esphome/components/deep_sleep/deep_sleep_component.h +11 -9
  56. esphome/components/deep_sleep/deep_sleep_esp32.cpp +51 -27
  57. esphome/components/ektf2232/touchscreen/__init__.py +8 -5
  58. esphome/components/ektf2232/touchscreen/ektf2232.cpp +4 -4
  59. esphome/components/ektf2232/touchscreen/ektf2232.h +2 -2
  60. esphome/components/epaper_spi/__init__.py +1 -0
  61. esphome/components/epaper_spi/display.py +80 -0
  62. esphome/components/epaper_spi/epaper_spi.cpp +227 -0
  63. esphome/components/epaper_spi/epaper_spi.h +93 -0
  64. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp +42 -0
  65. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h +45 -0
  66. esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +135 -0
  67. esphome/components/epaper_spi/epaper_spi_spectra_e6.h +23 -0
  68. esphome/components/es7210/es7210.cpp +3 -3
  69. esphome/components/esp32/__init__.py +254 -339
  70. esphome/components/esp32/boards.py +81 -0
  71. esphome/components/esp32/preferences.cpp +23 -17
  72. esphome/components/esp32_ble/__init__.py +159 -44
  73. esphome/components/esp32_ble/ble.cpp +47 -3
  74. esphome/components/esp32_ble/ble.h +18 -0
  75. esphome/components/esp32_ble/ble_advertising.cpp +7 -3
  76. esphome/components/esp32_ble/ble_advertising.h +4 -0
  77. esphome/components/esp32_ble/ble_uuid.cpp +16 -42
  78. esphome/components/esp32_ble_beacon/__init__.py +3 -4
  79. esphome/components/esp32_ble_client/ble_client_base.cpp +14 -12
  80. esphome/components/esp32_ble_server/__init__.py +28 -14
  81. esphome/components/esp32_ble_server/ble_characteristic.cpp +67 -57
  82. esphome/components/esp32_ble_server/ble_characteristic.h +27 -16
  83. esphome/components/esp32_ble_server/ble_descriptor.cpp +4 -3
  84. esphome/components/esp32_ble_server/ble_descriptor.h +13 -9
  85. esphome/components/esp32_ble_server/ble_server.cpp +59 -24
  86. esphome/components/esp32_ble_server/ble_server.h +38 -20
  87. esphome/components/esp32_ble_server/ble_server_automations.cpp +49 -33
  88. esphome/components/esp32_ble_server/ble_server_automations.h +39 -24
  89. esphome/components/esp32_ble_tracker/__init__.py +25 -80
  90. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -4
  91. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +0 -3
  92. esphome/components/esp32_camera/__init__.py +1 -3
  93. esphome/components/esp32_can/esp32_can.cpp +22 -4
  94. esphome/components/esp32_can/esp32_can.h +3 -0
  95. esphome/components/esp32_hosted/__init__.py +2 -1
  96. esphome/components/esp32_improv/esp32_improv_component.cpp +102 -44
  97. esphome/components/esp32_improv/esp32_improv_component.h +6 -1
  98. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  99. esphome/components/esp8266/__init__.py +3 -3
  100. esphome/components/esphome/ota/__init__.py +21 -2
  101. esphome/components/esphome/ota/ota_esphome.cpp +455 -145
  102. esphome/components/esphome/ota/ota_esphome.h +49 -2
  103. esphome/components/ethernet/__init__.py +39 -22
  104. esphome/components/ethernet/ethernet_component.cpp +28 -5
  105. esphome/components/ethernet/ethernet_component.h +5 -1
  106. esphome/components/external_components/__init__.py +8 -6
  107. esphome/components/fingerprint_grow/fingerprint_grow.cpp +1 -1
  108. esphome/components/fingerprint_grow/fingerprint_grow.h +2 -1
  109. esphome/components/font/__init__.py +5 -5
  110. esphome/components/graph/graph.cpp +1 -1
  111. esphome/components/graphical_display_menu/graphical_display_menu.cpp +3 -2
  112. esphome/components/haier/hon_climate.cpp +2 -2
  113. esphome/components/haier/hon_climate.h +1 -1
  114. esphome/components/hdc1080/hdc1080.cpp +42 -34
  115. esphome/components/hdc1080/hdc1080.h +1 -3
  116. esphome/components/homeassistant/number/homeassistant_number.cpp +2 -2
  117. esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
  118. esphome/components/http_request/__init__.py +3 -3
  119. esphome/components/htu21d/htu21d.cpp +13 -18
  120. esphome/components/htu21d/htu21d.h +1 -1
  121. esphome/components/i2s_audio/__init__.py +1 -2
  122. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  123. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -2
  124. esphome/components/improv_serial/improv_serial_component.cpp +12 -15
  125. esphome/components/improv_serial/improv_serial_component.h +6 -8
  126. esphome/components/json/json_util.cpp +35 -43
  127. esphome/components/json/json_util.h +57 -0
  128. esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +2 -2
  129. esphome/components/key_collector/key_collector.h +4 -4
  130. esphome/components/libretiny/__init__.py +6 -6
  131. esphome/components/libretiny/preferences.cpp +23 -16
  132. esphome/components/light/light_call.cpp +98 -120
  133. esphome/components/light/light_call.h +17 -7
  134. esphome/components/lm75b/__init__.py +0 -0
  135. esphome/components/lm75b/lm75b.cpp +39 -0
  136. esphome/components/lm75b/lm75b.h +19 -0
  137. esphome/components/lm75b/sensor.py +34 -0
  138. esphome/components/lock/lock.h +12 -6
  139. esphome/components/logger/__init__.py +15 -27
  140. esphome/components/logger/logger.cpp +10 -20
  141. esphome/components/logger/logger.h +105 -62
  142. esphome/components/logger/logger_esp32.cpp +0 -48
  143. esphome/components/logger/logger_zephyr.cpp +2 -3
  144. esphome/components/logger/select/logger_level_select.cpp +6 -7
  145. esphome/components/logger/select/logger_level_select.h +7 -0
  146. esphome/components/ltr501/ltr501.cpp +7 -6
  147. esphome/components/ltr_als_ps/ltr_als_ps.cpp +7 -6
  148. esphome/components/matrix_keypad/matrix_keypad.h +4 -4
  149. esphome/components/max7219digit/max7219digit.cpp +1 -1
  150. esphome/components/mcp2515/mcp2515.cpp +31 -3
  151. esphome/components/mcp2515/mcp2515_defs.h +3 -1
  152. esphome/components/md5/md5.cpp +0 -26
  153. esphome/components/md5/md5.h +10 -20
  154. esphome/components/mdns/__init__.py +19 -6
  155. esphome/components/mdns/mdns_component.cpp +27 -59
  156. esphome/components/mdns/mdns_component.h +23 -10
  157. esphome/components/mdns/mdns_esp32.cpp +7 -7
  158. esphome/components/mdns/mdns_esp8266.cpp +6 -6
  159. esphome/components/mdns/mdns_libretiny.cpp +3 -3
  160. esphome/components/mdns/mdns_rp2040.cpp +3 -3
  161. esphome/components/mipi/__init__.py +1 -5
  162. esphome/components/mipi_spi/display.py +24 -8
  163. esphome/components/mipi_spi/mipi_spi.h +3 -3
  164. esphome/components/mixer/speaker/mixer_speaker.cpp +3 -3
  165. esphome/components/mmc5603/mmc5603.cpp +3 -3
  166. esphome/components/modbus/modbus.cpp +27 -13
  167. esphome/components/modbus/modbus.h +5 -3
  168. esphome/components/modbus/modbus_definitions.h +86 -0
  169. esphome/components/modbus_controller/__init__.py +29 -1
  170. esphome/components/modbus_controller/const.py +4 -0
  171. esphome/components/modbus_controller/modbus_controller.cpp +38 -13
  172. esphome/components/modbus_controller/modbus_controller.h +18 -29
  173. esphome/components/mpr121/mpr121.cpp +41 -42
  174. esphome/components/mpr121/mpr121.h +0 -1
  175. esphome/components/nau7802/nau7802.cpp +2 -2
  176. esphome/components/network/__init__.py +7 -3
  177. esphome/components/nextion/display.py +4 -4
  178. esphome/components/nextion/nextion.cpp +8 -8
  179. esphome/components/number/__init__.py +2 -0
  180. esphome/components/number/number_call.cpp +23 -12
  181. esphome/components/number/number_call.h +5 -0
  182. esphome/components/online_image/bmp_image.cpp +2 -1
  183. esphome/components/online_image/jpeg_image.cpp +4 -2
  184. esphome/components/openthread/openthread.cpp +6 -7
  185. esphome/components/openthread/openthread.h +0 -1
  186. esphome/components/ota/ota_backend.h +1 -0
  187. esphome/components/packages/__init__.py +10 -8
  188. esphome/components/packet_transport/packet_transport.cpp +2 -0
  189. esphome/components/pid/pid_controller.cpp +1 -1
  190. esphome/components/prometheus/prometheus_handler.cpp +239 -239
  191. esphome/components/psram/__init__.py +30 -28
  192. esphome/components/qmc5883l/qmc5883l.cpp +15 -0
  193. esphome/components/qmc5883l/qmc5883l.h +3 -0
  194. esphome/components/qmc5883l/sensor.py +31 -12
  195. esphome/components/remote_base/gobox_protocol.cpp +3 -3
  196. esphome/components/remote_receiver/__init__.py +14 -2
  197. esphome/components/remote_receiver/{remote_receiver_esp8266.cpp → remote_receiver.cpp} +2 -2
  198. esphome/components/remote_receiver/remote_receiver.h +4 -0
  199. esphome/components/remote_receiver/remote_receiver_esp32.cpp +18 -1
  200. esphome/components/remote_transmitter/__init__.py +2 -2
  201. esphome/components/remote_transmitter/remote_transmitter.cpp +103 -0
  202. esphome/components/rp2040/__init__.py +11 -11
  203. esphome/components/rtttl/rtttl.cpp +2 -2
  204. esphome/components/scd30/sensor.py +1 -1
  205. esphome/components/script/__init__.py +1 -1
  206. esphome/components/script/script.h +7 -7
  207. esphome/components/select/select.cpp +5 -4
  208. esphome/components/select/select_call.cpp +1 -1
  209. esphome/components/sensirion_common/i2c_sensirion.cpp +2 -1
  210. esphome/components/sensor/__init__.py +2 -0
  211. esphome/components/sha256/__init__.py +22 -0
  212. esphome/components/sha256/sha256.cpp +116 -0
  213. esphome/components/sha256/sha256.h +60 -0
  214. esphome/components/sim800l/sim800l.cpp +8 -4
  215. esphome/components/socket/lwip_raw_tcp_impl.cpp +34 -6
  216. esphome/components/sonoff_d1/sonoff_d1.cpp +1 -1
  217. esphome/components/spi/__init__.py +0 -3
  218. esphome/components/split_buffer/__init__.py +5 -0
  219. esphome/components/split_buffer/split_buffer.cpp +133 -0
  220. esphome/components/split_buffer/split_buffer.h +40 -0
  221. esphome/components/sps30/sps30.cpp +14 -10
  222. esphome/components/sps30/sps30.h +2 -0
  223. esphome/components/st7567_i2c/st7567_i2c.cpp +3 -1
  224. esphome/components/st7789v/st7789v.cpp +3 -2
  225. esphome/components/statsd/statsd.cpp +1 -1
  226. esphome/components/substitutions/__init__.py +3 -1
  227. esphome/components/substitutions/jinja.py +13 -3
  228. esphome/components/sx126x/__init__.py +16 -0
  229. esphome/components/sx126x/sx126x.cpp +15 -1
  230. esphome/components/sx126x/sx126x.h +9 -1
  231. esphome/components/sx126x/sx126x_reg.h +2 -0
  232. esphome/components/text_sensor/text_sensor.cpp +16 -0
  233. esphome/components/text_sensor/text_sensor.h +3 -10
  234. esphome/components/tormatic/tormatic_cover.cpp +1 -1
  235. esphome/components/tuya/select/tuya_select.cpp +1 -1
  236. esphome/components/tuya/tuya.cpp +29 -4
  237. esphome/components/uart/__init__.py +36 -26
  238. esphome/components/uart/uart.h +6 -0
  239. esphome/components/uart/uart_component.cpp +8 -0
  240. esphome/components/uart/uart_component.h +28 -0
  241. esphome/components/uart/uart_component_esp_idf.cpp +64 -10
  242. esphome/components/uart/uart_component_esp_idf.h +5 -2
  243. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +1 -1
  244. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +1 -1
  245. esphome/components/uponor_smatrix/uponor_smatrix.cpp +3 -3
  246. esphome/components/usb_host/__init__.py +2 -1
  247. esphome/components/usb_host/usb_host.h +82 -13
  248. esphome/components/usb_host/usb_host_client.cpp +180 -24
  249. esphome/components/usb_host/usb_host_component.cpp +1 -1
  250. esphome/components/usb_uart/__init__.py +0 -1
  251. esphome/components/usb_uart/ch34x.cpp +4 -4
  252. esphome/components/usb_uart/cp210x.cpp +3 -3
  253. esphome/components/usb_uart/usb_uart.cpp +88 -32
  254. esphome/components/usb_uart/usb_uart.h +30 -6
  255. esphome/components/valve/valve.cpp +1 -0
  256. esphome/components/veml7700/veml7700.cpp +7 -6
  257. esphome/components/version/version_text_sensor.cpp +2 -1
  258. esphome/components/voice_assistant/voice_assistant.cpp +3 -3
  259. esphome/components/waveshare_epaper/waveshare_epaper.cpp +4 -4
  260. esphome/components/web_server/list_entities.cpp +3 -4
  261. esphome/components/web_server/list_entities.h +8 -10
  262. esphome/components/web_server/ota/__init__.py +1 -1
  263. esphome/components/web_server/ota/ota_web_server.cpp +9 -3
  264. esphome/components/web_server/web_server.cpp +509 -404
  265. esphome/components/web_server/web_server.h +5 -6
  266. esphome/components/web_server/web_server_v1.cpp +21 -19
  267. esphome/components/web_server_base/__init__.py +5 -2
  268. esphome/components/web_server_base/web_server_base.h +27 -7
  269. esphome/components/web_server_idf/__init__.py +1 -1
  270. esphome/components/web_server_idf/multipart.cpp +2 -2
  271. esphome/components/web_server_idf/multipart.h +2 -2
  272. esphome/components/web_server_idf/utils.cpp +2 -2
  273. esphome/components/web_server_idf/utils.h +2 -2
  274. esphome/components/web_server_idf/web_server_idf.cpp +118 -26
  275. esphome/components/web_server_idf/web_server_idf.h +12 -10
  276. esphome/components/wifi/__init__.py +13 -11
  277. esphome/components/wifi/wifi_component.cpp +73 -56
  278. esphome/components/wifi/wifi_component.h +4 -4
  279. esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
  280. esphome/components/wifi/wifi_component_esp_idf.cpp +24 -4
  281. esphome/components/wireguard/__init__.py +1 -1
  282. esphome/components/wts01/__init__.py +0 -0
  283. esphome/components/wts01/sensor.py +41 -0
  284. esphome/components/wts01/wts01.cpp +91 -0
  285. esphome/components/wts01/wts01.h +27 -0
  286. esphome/components/zephyr/__init__.py +5 -5
  287. esphome/components/zwave_proxy/__init__.py +43 -0
  288. esphome/components/zwave_proxy/zwave_proxy.cpp +346 -0
  289. esphome/components/zwave_proxy/zwave_proxy.h +93 -0
  290. esphome/config.py +79 -24
  291. esphome/config_validation.py +13 -15
  292. esphome/const.py +9 -2
  293. esphome/core/__init__.py +31 -22
  294. esphome/core/component.cpp +28 -18
  295. esphome/core/component_iterator.h +2 -1
  296. esphome/core/config.py +15 -15
  297. esphome/core/defines.h +19 -0
  298. esphome/core/hash_base.h +56 -0
  299. esphome/core/helpers.cpp +19 -3
  300. esphome/core/helpers.h +26 -0
  301. esphome/core/scheduler.cpp +5 -21
  302. esphome/core/scheduler.h +19 -8
  303. esphome/core/string_ref.h +1 -1
  304. esphome/core/time.cpp +5 -5
  305. esphome/cpp_generator.py +4 -29
  306. esphome/dashboard/const.py +21 -4
  307. esphome/dashboard/core.py +10 -8
  308. esphome/dashboard/dns.py +15 -0
  309. esphome/dashboard/entries.py +15 -21
  310. esphome/dashboard/models.py +76 -0
  311. esphome/dashboard/settings.py +7 -7
  312. esphome/dashboard/status/mdns.py +46 -2
  313. esphome/dashboard/web_server.py +367 -93
  314. esphome/espota2.py +111 -31
  315. esphome/external_files.py +6 -7
  316. esphome/git.py +8 -0
  317. esphome/helpers.py +124 -77
  318. esphome/loader.py +8 -9
  319. esphome/platformio_api.py +25 -18
  320. esphome/storage_json.py +26 -21
  321. esphome/types.py +30 -2
  322. esphome/util.py +32 -16
  323. esphome/vscode.py +8 -8
  324. esphome/wizard.py +10 -10
  325. esphome/writer.py +50 -15
  326. esphome/yaml_util.py +37 -31
  327. esphome/zeroconf.py +12 -3
  328. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/METADATA +11 -11
  329. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/RECORD +333 -313
  330. esphome/components/event_emitter/__init__.py +0 -5
  331. esphome/components/event_emitter/event_emitter.cpp +0 -14
  332. esphome/components/event_emitter/event_emitter.h +0 -63
  333. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -125
  334. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +0 -107
  335. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +0 -110
  336. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -214
  337. esphome/components/uart/uart_component_esp32_arduino.h +0 -60
  338. esphome/components/wifi/wifi_component_esp32_arduino.cpp +0 -860
  339. esphome/core/string_ref.cpp +0 -12
  340. esphome/dashboard/util/file.py +0 -63
  341. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/WHEEL +0 -0
  342. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/entry_points.txt +0 -0
  343. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/licenses/LICENSE +0 -0
  344. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  // Should not be needed, but it's required to pass CI clang-tidy checks
2
- #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
2
+ #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4)
3
3
  #include "usb_uart.h"
4
4
  #include "esphome/core/log.h"
5
5
  #include "esphome/components/uart/uart_debugger.h"
@@ -130,7 +130,7 @@ size_t RingBuffer::pop(uint8_t *data, size_t len) {
130
130
  return len;
131
131
  }
132
132
  void USBUartChannel::write_array(const uint8_t *data, size_t len) {
133
- if (!this->initialised_) {
133
+ if (!this->initialised_.load()) {
134
134
  ESP_LOGV(TAG, "Channel not initialised - write ignored");
135
135
  return;
136
136
  }
@@ -152,7 +152,7 @@ bool USBUartChannel::peek_byte(uint8_t *data) {
152
152
  return true;
153
153
  }
154
154
  bool USBUartChannel::read_array(uint8_t *data, size_t len) {
155
- if (!this->initialised_) {
155
+ if (!this->initialised_.load()) {
156
156
  ESP_LOGV(TAG, "Channel not initialised - read ignored");
157
157
  return false;
158
158
  }
@@ -170,7 +170,34 @@ bool USBUartChannel::read_array(uint8_t *data, size_t len) {
170
170
  return status;
171
171
  }
172
172
  void USBUartComponent::setup() { USBClient::setup(); }
173
- void USBUartComponent::loop() { USBClient::loop(); }
173
+ void USBUartComponent::loop() {
174
+ USBClient::loop();
175
+
176
+ // Process USB data from the lock-free queue
177
+ UsbDataChunk *chunk;
178
+ while ((chunk = this->usb_data_queue_.pop()) != nullptr) {
179
+ auto *channel = chunk->channel;
180
+
181
+ #ifdef USE_UART_DEBUGGER
182
+ if (channel->debug_) {
183
+ uart::UARTDebug::log_hex(uart::UART_DIRECTION_RX, std::vector<uint8_t>(chunk->data, chunk->data + chunk->length),
184
+ ','); // NOLINT()
185
+ }
186
+ #endif
187
+
188
+ // Push data to ring buffer (now safe in main loop)
189
+ channel->input_buffer_.push(chunk->data, chunk->length);
190
+
191
+ // Return chunk to pool for reuse
192
+ this->chunk_pool_.release(chunk);
193
+ }
194
+
195
+ // Log dropped USB data periodically
196
+ uint16_t dropped = this->usb_data_queue_.get_and_reset_dropped_count();
197
+ if (dropped > 0) {
198
+ ESP_LOGW(TAG, "Dropped %u USB data chunks due to buffer overflow", dropped);
199
+ }
200
+ }
174
201
  void USBUartComponent::dump_config() {
175
202
  USBClient::dump_config();
176
203
  for (auto &channel : this->channels_) {
@@ -187,49 +214,77 @@ void USBUartComponent::dump_config() {
187
214
  }
188
215
  }
189
216
  void USBUartComponent::start_input(USBUartChannel *channel) {
190
- if (!channel->initialised_ || channel->input_started_ ||
191
- channel->input_buffer_.get_free_space() < channel->cdc_dev_.in_ep->wMaxPacketSize)
217
+ if (!channel->initialised_.load() || channel->input_started_.load())
192
218
  return;
219
+ // THREAD CONTEXT: Called from both USB task and main loop threads
220
+ // - USB task: Immediate restart after successful transfer for continuous data flow
221
+ // - Main loop: Controlled restart after consuming data (backpressure mechanism)
222
+ //
223
+ // This dual-thread access is intentional for performance:
224
+ // - USB task restarts avoid context switch delays for high-speed data
225
+ // - Main loop restarts provide flow control when buffers are full
226
+ //
227
+ // The underlying transfer_in() uses lock-free atomic allocation from the
228
+ // TransferRequest pool, making this multi-threaded access safe
193
229
  const auto *ep = channel->cdc_dev_.in_ep;
230
+ // CALLBACK CONTEXT: This lambda is executed in USB task via transfer_callback
194
231
  auto callback = [this, channel](const usb_host::TransferStatus &status) {
195
232
  ESP_LOGV(TAG, "Transfer result: length: %u; status %X", status.data_len, status.error_code);
196
233
  if (!status.success) {
197
234
  ESP_LOGE(TAG, "Control transfer failed, status=%s", esp_err_to_name(status.error_code));
235
+ // On failure, don't restart - let next read_array() trigger it
236
+ channel->input_started_.store(false);
198
237
  return;
199
238
  }
200
- #ifdef USE_UART_DEBUGGER
201
- if (channel->debug_) {
202
- uart::UARTDebug::log_hex(uart::UART_DIRECTION_RX,
203
- std::vector<uint8_t>(status.data, status.data + status.data_len), ','); // NOLINT()
204
- }
205
- #endif
206
- channel->input_started_ = false;
207
- if (!channel->dummy_receiver_) {
208
- for (size_t i = 0; i != status.data_len; i++) {
209
- channel->input_buffer_.push(status.data[i]);
239
+
240
+ if (!channel->dummy_receiver_ && status.data_len > 0) {
241
+ // Allocate a chunk from the pool
242
+ UsbDataChunk *chunk = this->chunk_pool_.allocate();
243
+ if (chunk == nullptr) {
244
+ // No chunks available - queue is full or we're out of memory
245
+ this->usb_data_queue_.increment_dropped_count();
246
+ // Mark input as not started so we can retry
247
+ channel->input_started_.store(false);
248
+ return;
210
249
  }
250
+
251
+ // Copy data to chunk (this is fast, happens in USB task)
252
+ memcpy(chunk->data, status.data, status.data_len);
253
+ chunk->length = status.data_len;
254
+ chunk->channel = channel;
255
+
256
+ // Push to lock-free queue for main loop processing
257
+ // Push always succeeds because pool size == queue size
258
+ this->usb_data_queue_.push(chunk);
211
259
  }
212
- if (channel->input_buffer_.get_free_space() >= channel->cdc_dev_.in_ep->wMaxPacketSize) {
213
- this->defer([this, channel] { this->start_input(channel); });
214
- }
260
+
261
+ // On success, restart input immediately from USB task for performance
262
+ // The lock-free queue will handle backpressure
263
+ channel->input_started_.store(false);
264
+ this->start_input(channel);
215
265
  };
216
- channel->input_started_ = true;
266
+ channel->input_started_.store(true);
217
267
  this->transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize);
218
268
  }
219
269
 
220
270
  void USBUartComponent::start_output(USBUartChannel *channel) {
221
- if (channel->output_started_)
271
+ // IMPORTANT: This function must only be called from the main loop!
272
+ // The output_buffer_ is not thread-safe and can only be accessed from main loop.
273
+ // USB callbacks use defer() to ensure this function runs in the correct context.
274
+ if (channel->output_started_.load())
222
275
  return;
223
276
  if (channel->output_buffer_.is_empty()) {
224
277
  return;
225
278
  }
226
279
  const auto *ep = channel->cdc_dev_.out_ep;
280
+ // CALLBACK CONTEXT: This lambda is executed in USB task via transfer_callback
227
281
  auto callback = [this, channel](const usb_host::TransferStatus &status) {
228
282
  ESP_LOGV(TAG, "Output Transfer result: length: %u; status %X", status.data_len, status.error_code);
229
- channel->output_started_ = false;
283
+ channel->output_started_.store(false);
284
+ // Defer restart to main loop (defer is thread-safe)
230
285
  this->defer([this, channel] { this->start_output(channel); });
231
286
  };
232
- channel->output_started_ = true;
287
+ channel->output_started_.store(true);
233
288
  uint8_t data[ep->wMaxPacketSize];
234
289
  auto len = channel->output_buffer_.pop(data, ep->wMaxPacketSize);
235
290
  this->transfer_out(ep->bEndpointAddress, callback, data, len);
@@ -249,7 +304,8 @@ static void fix_mps(const usb_ep_desc_t *ep) {
249
304
  if (ep != nullptr) {
250
305
  auto *ep_mutable = const_cast<usb_ep_desc_t *>(ep);
251
306
  if (ep->wMaxPacketSize > 64) {
252
- ESP_LOGW(TAG, "Corrected MPS of EP %u from %u to 64", ep->bEndpointAddress, ep->wMaxPacketSize);
307
+ ESP_LOGW(TAG, "Corrected MPS of EP 0x%02X from %u to 64", static_cast<uint8_t>(ep->bEndpointAddress & 0xFF),
308
+ ep->wMaxPacketSize);
253
309
  ep_mutable->wMaxPacketSize = 64;
254
310
  }
255
311
  }
@@ -266,13 +322,13 @@ void USBUartTypeCdcAcm::on_connected() {
266
322
  for (auto *channel : this->channels_) {
267
323
  if (i == cdc_devs.size()) {
268
324
  ESP_LOGE(TAG, "No configuration found for channel %d", channel->index_);
269
- this->status_set_warning(LOG_STR("No configuration found for channel"));
325
+ this->status_set_warning("No configuration found for channel");
270
326
  break;
271
327
  }
272
328
  channel->cdc_dev_ = cdc_devs[i++];
273
329
  fix_mps(channel->cdc_dev_.in_ep);
274
330
  fix_mps(channel->cdc_dev_.out_ep);
275
- channel->initialised_ = true;
331
+ channel->initialised_.store(true);
276
332
  auto err =
277
333
  usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number, 0);
278
334
  if (err != ESP_OK) {
@@ -301,9 +357,9 @@ void USBUartTypeCdcAcm::on_disconnected() {
301
357
  usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress);
302
358
  }
303
359
  usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number);
304
- channel->initialised_ = false;
305
- channel->input_started_ = false;
306
- channel->output_started_ = false;
360
+ channel->initialised_.store(false);
361
+ channel->input_started_.store(false);
362
+ channel->output_started_.store(false);
307
363
  channel->input_buffer_.clear();
308
364
  channel->output_buffer_.clear();
309
365
  }
@@ -312,10 +368,10 @@ void USBUartTypeCdcAcm::on_disconnected() {
312
368
 
313
369
  void USBUartTypeCdcAcm::enable_channels() {
314
370
  for (auto *channel : this->channels_) {
315
- if (!channel->initialised_)
371
+ if (!channel->initialised_.load())
316
372
  continue;
317
- channel->input_started_ = false;
318
- channel->output_started_ = false;
373
+ channel->input_started_.store(false);
374
+ channel->output_started_.store(false);
319
375
  this->start_input(channel);
320
376
  }
321
377
  }
@@ -1,15 +1,19 @@
1
1
  #pragma once
2
2
 
3
- #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
3
+ #if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4)
4
4
  #include "esphome/core/component.h"
5
5
  #include "esphome/core/helpers.h"
6
6
  #include "esphome/components/uart/uart_component.h"
7
7
  #include "esphome/components/usb_host/usb_host.h"
8
+ #include "esphome/core/lock_free_queue.h"
9
+ #include "esphome/core/event_pool.h"
10
+ #include <atomic>
8
11
 
9
12
  namespace esphome {
10
13
  namespace usb_uart {
11
14
  class USBUartTypeCdcAcm;
12
15
  class USBUartComponent;
16
+ class USBUartChannel;
13
17
 
14
18
  static const char *const TAG = "usb_uart";
15
19
 
@@ -68,6 +72,17 @@ class RingBuffer {
68
72
  uint8_t *buffer_;
69
73
  };
70
74
 
75
+ // Structure for queuing received USB data chunks
76
+ struct UsbDataChunk {
77
+ static constexpr size_t MAX_CHUNK_SIZE = 64; // USB packet size
78
+ uint8_t data[MAX_CHUNK_SIZE];
79
+ uint8_t length; // Max 64 bytes, so uint8_t is sufficient
80
+ USBUartChannel *channel;
81
+
82
+ // Required for EventPool - no cleanup needed for POD types
83
+ void release() {}
84
+ };
85
+
71
86
  class USBUartChannel : public uart::UARTComponent, public Parented<USBUartComponent> {
72
87
  friend class USBUartComponent;
73
88
  friend class USBUartTypeCdcAcm;
@@ -90,16 +105,20 @@ class USBUartChannel : public uart::UARTComponent, public Parented<USBUartCompon
90
105
  void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; }
91
106
 
92
107
  protected:
93
- const uint8_t index_;
108
+ // Larger structures first for better alignment
94
109
  RingBuffer input_buffer_;
95
110
  RingBuffer output_buffer_;
96
- UARTParityOptions parity_{UART_CONFIG_PARITY_NONE};
97
- bool input_started_{true};
98
- bool output_started_{true};
99
111
  CdcEps cdc_dev_{};
112
+ // Enum (likely 4 bytes)
113
+ UARTParityOptions parity_{UART_CONFIG_PARITY_NONE};
114
+ // Group atomics together (each 1 byte)
115
+ std::atomic<bool> input_started_{true};
116
+ std::atomic<bool> output_started_{true};
117
+ std::atomic<bool> initialised_{false};
118
+ // Group regular bytes together to minimize padding
119
+ const uint8_t index_;
100
120
  bool debug_{};
101
121
  bool dummy_receiver_{};
102
- bool initialised_{};
103
122
  };
104
123
 
105
124
  class USBUartComponent : public usb_host::USBClient {
@@ -115,6 +134,11 @@ class USBUartComponent : public usb_host::USBClient {
115
134
  void start_input(USBUartChannel *channel);
116
135
  void start_output(USBUartChannel *channel);
117
136
 
137
+ // Lock-free data transfer from USB task to main loop
138
+ static constexpr int USB_DATA_QUEUE_SIZE = 32;
139
+ LockFreeQueue<UsbDataChunk, USB_DATA_QUEUE_SIZE> usb_data_queue_;
140
+ EventPool<UsbDataChunk, USB_DATA_QUEUE_SIZE> chunk_pool_;
141
+
118
142
  protected:
119
143
  std::vector<USBUartChannel *> channels_{};
120
144
  };
@@ -1,5 +1,6 @@
1
1
  #include "valve.h"
2
2
  #include "esphome/core/log.h"
3
+ #include <strings.h>
3
4
 
4
5
  namespace esphome {
5
6
  namespace valve {
@@ -1,6 +1,7 @@
1
1
  #include "veml7700.h"
2
2
  #include "esphome/core/application.h"
3
3
  #include "esphome/core/log.h"
4
+ #include <limits>
4
5
 
5
6
  namespace esphome {
6
7
  namespace veml7700 {
@@ -12,30 +13,30 @@ static float reduce_to_zero(float a, float b) { return (a > b) ? (a - b) : 0; }
12
13
 
13
14
  template<typename T, size_t size> T get_next(const T (&array)[size], const T val) {
14
15
  size_t i = 0;
15
- size_t idx = -1;
16
- while (idx == -1 && i < size) {
16
+ size_t idx = std::numeric_limits<size_t>::max();
17
+ while (idx == std::numeric_limits<size_t>::max() && i < size) {
17
18
  if (array[i] == val) {
18
19
  idx = i;
19
20
  break;
20
21
  }
21
22
  i++;
22
23
  }
23
- if (idx == -1 || i + 1 >= size)
24
+ if (idx == std::numeric_limits<size_t>::max() || i + 1 >= size)
24
25
  return val;
25
26
  return array[i + 1];
26
27
  }
27
28
 
28
29
  template<typename T, size_t size> T get_prev(const T (&array)[size], const T val) {
29
30
  size_t i = size - 1;
30
- size_t idx = -1;
31
- while (idx == -1 && i > 0) {
31
+ size_t idx = std::numeric_limits<size_t>::max();
32
+ while (idx == std::numeric_limits<size_t>::max() && i > 0) {
32
33
  if (array[i] == val) {
33
34
  idx = i;
34
35
  break;
35
36
  }
36
37
  i--;
37
38
  }
38
- if (idx == -1 || i == 0)
39
+ if (idx == std::numeric_limits<size_t>::max() || i == 0)
39
40
  return val;
40
41
  return array[i - 1];
41
42
  }
@@ -2,6 +2,7 @@
2
2
  #include "esphome/core/log.h"
3
3
  #include "esphome/core/application.h"
4
4
  #include "esphome/core/version.h"
5
+ #include "esphome/core/helpers.h"
5
6
 
6
7
  namespace esphome {
7
8
  namespace version {
@@ -12,7 +13,7 @@ void VersionTextSensor::setup() {
12
13
  if (this->hide_timestamp_) {
13
14
  this->publish_state(ESPHOME_VERSION);
14
15
  } else {
15
- this->publish_state(ESPHOME_VERSION " " + App.get_compilation_time());
16
+ this->publish_state(str_sprintf(ESPHOME_VERSION " %s", App.get_compilation_time().c_str()));
16
17
  }
17
18
  }
18
19
  float VersionTextSensor::get_setup_priority() const { return setup_priority::DATA; }
@@ -242,7 +242,6 @@ void VoiceAssistant::loop() {
242
242
  msg.flags = flags;
243
243
  msg.audio_settings = audio_settings;
244
244
  msg.set_wake_word_phrase(StringRef(this->wake_word_));
245
- this->wake_word_ = "";
246
245
 
247
246
  // Reset media player state tracking
248
247
  #ifdef USE_MEDIA_PLAYER
@@ -430,8 +429,9 @@ void VoiceAssistant::client_subscription(api::APIConnection *client, bool subscr
430
429
 
431
430
  if (this->api_client_ != nullptr) {
432
431
  ESP_LOGE(TAG, "Multiple API Clients attempting to connect to Voice Assistant");
433
- ESP_LOGE(TAG, "Current client: %s", this->api_client_->get_client_combined_info().c_str());
434
- ESP_LOGE(TAG, "New client: %s", client->get_client_combined_info().c_str());
432
+ ESP_LOGE(TAG, "Current client: %s (%s)", this->api_client_->get_name().c_str(),
433
+ this->api_client_->get_peername().c_str());
434
+ ESP_LOGE(TAG, "New client: %s (%s)", client->get_name().c_str(), client->get_peername().c_str());
435
435
  return;
436
436
  }
437
437
 
@@ -2274,11 +2274,11 @@ void GDEW0154M09::clear_() {
2274
2274
  uint32_t pixsize = this->get_buffer_length_();
2275
2275
  for (uint8_t j = 0; j < 2; j++) {
2276
2276
  this->command(CMD_DTM1_DATA_START_TRANS);
2277
- for (int count = 0; count < pixsize; count++) {
2277
+ for (uint32_t count = 0; count < pixsize; count++) {
2278
2278
  this->data(0x00);
2279
2279
  }
2280
2280
  this->command(CMD_DTM2_DATA_START_TRANS2);
2281
- for (int count = 0; count < pixsize; count++) {
2281
+ for (uint32_t count = 0; count < pixsize; count++) {
2282
2282
  this->data(0xff);
2283
2283
  }
2284
2284
  this->command(CMD_DISPLAY_REFRESH);
@@ -2291,11 +2291,11 @@ void HOT GDEW0154M09::display() {
2291
2291
  this->init_internal_();
2292
2292
  // "Mode 0 display" for now
2293
2293
  this->command(CMD_DTM1_DATA_START_TRANS);
2294
- for (int i = 0; i < this->get_buffer_length_(); i++) {
2294
+ for (uint32_t i = 0; i < this->get_buffer_length_(); i++) {
2295
2295
  this->data(0xff);
2296
2296
  }
2297
2297
  this->command(CMD_DTM2_DATA_START_TRANS2); // write 'new' data to SRAM
2298
- for (int i = 0; i < this->get_buffer_length_(); i++) {
2298
+ for (uint32_t i = 0; i < this->get_buffer_length_(); i++) {
2299
2299
  this->data(this->buffer_[i]);
2300
2300
  }
2301
2301
  this->command(CMD_DISPLAY_REFRESH);
@@ -9,13 +9,12 @@
9
9
  namespace esphome {
10
10
  namespace web_server {
11
11
 
12
- #ifdef USE_ARDUINO
12
+ #ifdef USE_ESP32
13
+ ListEntitiesIterator::ListEntitiesIterator(const WebServer *ws, AsyncEventSource *es) : web_server_(ws), events_(es) {}
14
+ #elif USE_ARDUINO
13
15
  ListEntitiesIterator::ListEntitiesIterator(const WebServer *ws, DeferredUpdateEventSource *es)
14
16
  : web_server_(ws), events_(es) {}
15
17
  #endif
16
- #ifdef USE_ESP_IDF
17
- ListEntitiesIterator::ListEntitiesIterator(const WebServer *ws, AsyncEventSource *es) : web_server_(ws), events_(es) {}
18
- #endif
19
18
  ListEntitiesIterator::~ListEntitiesIterator() {}
20
19
 
21
20
  #ifdef USE_BINARY_SENSOR
@@ -5,25 +5,24 @@
5
5
  #include "esphome/core/component.h"
6
6
  #include "esphome/core/component_iterator.h"
7
7
  namespace esphome {
8
- #ifdef USE_ESP_IDF
8
+ #ifdef USE_ESP32
9
9
  namespace web_server_idf {
10
10
  class AsyncEventSource;
11
11
  }
12
12
  #endif
13
13
  namespace web_server {
14
14
 
15
- #ifdef USE_ARDUINO
15
+ #if !defined(USE_ESP32) && defined(USE_ARDUINO)
16
16
  class DeferredUpdateEventSource;
17
17
  #endif
18
18
  class WebServer;
19
19
 
20
20
  class ListEntitiesIterator : public ComponentIterator {
21
21
  public:
22
- #ifdef USE_ARDUINO
23
- ListEntitiesIterator(const WebServer *ws, DeferredUpdateEventSource *es);
24
- #endif
25
- #ifdef USE_ESP_IDF
22
+ #ifdef USE_ESP32
26
23
  ListEntitiesIterator(const WebServer *ws, esphome::web_server_idf::AsyncEventSource *es);
24
+ #elif defined(USE_ARDUINO)
25
+ ListEntitiesIterator(const WebServer *ws, DeferredUpdateEventSource *es);
27
26
  #endif
28
27
  virtual ~ListEntitiesIterator();
29
28
  #ifdef USE_BINARY_SENSOR
@@ -90,11 +89,10 @@ class ListEntitiesIterator : public ComponentIterator {
90
89
 
91
90
  protected:
92
91
  const WebServer *web_server_;
93
- #ifdef USE_ARDUINO
94
- DeferredUpdateEventSource *events_;
95
- #endif
96
- #ifdef USE_ESP_IDF
92
+ #ifdef USE_ESP32
97
93
  esphome::web_server_idf::AsyncEventSource *events_;
94
+ #elif USE_ARDUINO
95
+ DeferredUpdateEventSource *events_;
98
96
  #endif
99
97
  };
100
98
 
@@ -29,5 +29,5 @@ async def to_code(config):
29
29
  await ota_to_code(var, config)
30
30
  await cg.register_component(var, config)
31
31
  cg.add_define("USE_WEBSERVER_OTA")
32
- if CORE.using_esp_idf:
32
+ if CORE.is_esp32:
33
33
  add_idf_component(name="zorxx/multipart-parser", ref="1.0.1")
@@ -17,6 +17,12 @@
17
17
  #endif
18
18
  #endif // USE_ARDUINO
19
19
 
20
+ #if USE_ESP32
21
+ using PlatformString = std::string;
22
+ #elif USE_ARDUINO
23
+ using PlatformString = String;
24
+ #endif
25
+
20
26
  namespace esphome {
21
27
  namespace web_server {
22
28
 
@@ -26,8 +32,8 @@ class OTARequestHandler : public AsyncWebHandler {
26
32
  public:
27
33
  OTARequestHandler(WebServerOTAComponent *parent) : parent_(parent) {}
28
34
  void handleRequest(AsyncWebServerRequest *request) override;
29
- void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
30
- bool final) override;
35
+ void handleUpload(AsyncWebServerRequest *request, const PlatformString &filename, size_t index, uint8_t *data,
36
+ size_t len, bool final) override;
31
37
  bool canHandle(AsyncWebServerRequest *request) const override {
32
38
  // Check if this is an OTA update request
33
39
  bool is_ota_request = request->url() == "/update" && request->method() == HTTP_POST;
@@ -100,7 +106,7 @@ void OTARequestHandler::ota_init_(const char *filename) {
100
106
  this->ota_success_ = false;
101
107
  }
102
108
 
103
- void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index,
109
+ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const PlatformString &filename, size_t index,
104
110
  uint8_t *data, size_t len, bool final) {
105
111
  ota::OTAResponseTypes error_code = ota::OTA_RESPONSE_OK;
106
112