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
@@ -8,9 +8,9 @@
8
8
  #endif
9
9
  #include <cerrno>
10
10
  #include <cinttypes>
11
- #include <utility>
12
11
  #include <functional>
13
12
  #include <limits>
13
+ #include <utility>
14
14
  #include "esphome/components/network/util.h"
15
15
  #include "esphome/core/application.h"
16
16
  #include "esphome/core/entity_base.h"
@@ -30,6 +30,9 @@
30
30
  #ifdef USE_VOICE_ASSISTANT
31
31
  #include "esphome/components/voice_assistant/voice_assistant.h"
32
32
  #endif
33
+ #ifdef USE_ZWAVE_PROXY
34
+ #include "esphome/components/zwave_proxy/zwave_proxy.h"
35
+ #endif
33
36
 
34
37
  namespace esphome::api {
35
38
 
@@ -113,8 +116,7 @@ void APIConnection::start() {
113
116
 
114
117
  APIError err = this->helper_->init();
115
118
  if (err != APIError::OK) {
116
- on_fatal_error();
117
- this->log_warning_(LOG_STR("Helper init failed"), err);
119
+ this->fatal_error_with_log_(LOG_STR("Helper init failed"), err);
118
120
  return;
119
121
  }
120
122
  this->client_info_.peername = helper_->getpeername();
@@ -144,8 +146,7 @@ void APIConnection::loop() {
144
146
 
145
147
  APIError err = this->helper_->loop();
146
148
  if (err != APIError::OK) {
147
- on_fatal_error();
148
- this->log_socket_operation_failed_(err);
149
+ this->fatal_error_with_log_(LOG_STR("Socket operation failed"), err);
149
150
  return;
150
151
  }
151
152
 
@@ -160,17 +161,13 @@ void APIConnection::loop() {
160
161
  // No more data available
161
162
  break;
162
163
  } else if (err != APIError::OK) {
163
- on_fatal_error();
164
- this->log_warning_(LOG_STR("Reading failed"), err);
164
+ this->fatal_error_with_log_(LOG_STR("Reading failed"), err);
165
165
  return;
166
166
  } else {
167
167
  this->last_traffic_ = now;
168
168
  // read a packet
169
- if (buffer.data_len > 0) {
170
- this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
171
- } else {
172
- this->read_message(0, buffer.type, nullptr);
173
- }
169
+ this->read_message(buffer.data_len, buffer.type,
170
+ buffer.data_len > 0 ? &buffer.container[buffer.data_offset] : nullptr);
174
171
  if (this->flags_.remove)
175
172
  return;
176
173
  }
@@ -202,7 +199,8 @@ void APIConnection::loop() {
202
199
  // Disconnect if not responded within 2.5*keepalive
203
200
  if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
204
201
  on_fatal_error();
205
- ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
202
+ ESP_LOGW(TAG, "%s (%s) is unresponsive; disconnecting", this->client_info_.name.c_str(),
203
+ this->client_info_.peername.c_str());
206
204
  }
207
205
  } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && !this->flags_.remove) {
208
206
  // Only send ping if we're not disconnecting
@@ -252,7 +250,7 @@ bool APIConnection::send_disconnect_response(const DisconnectRequest &msg) {
252
250
  // remote initiated disconnect_client
253
251
  // don't close yet, we still need to send the disconnect response
254
252
  // close will happen on next loop
255
- ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
253
+ ESP_LOGD(TAG, "%s (%s) disconnected", this->client_info_.name.c_str(), this->client_info_.peername.c_str());
256
254
  this->flags_.next_close = true;
257
255
  DisconnectResponse resp;
258
256
  return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
@@ -1075,8 +1073,14 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
1075
1073
  if (homeassistant::global_homeassistant_time != nullptr) {
1076
1074
  homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds);
1077
1075
  #ifdef USE_TIME_TIMEZONE
1078
- if (!value.timezone.empty() && value.timezone != homeassistant::global_homeassistant_time->get_timezone()) {
1079
- homeassistant::global_homeassistant_time->set_timezone(value.timezone);
1076
+ if (value.timezone_len > 0) {
1077
+ const std::string &current_tz = homeassistant::global_homeassistant_time->get_timezone();
1078
+ // Compare without allocating a string
1079
+ if (current_tz.length() != value.timezone_len ||
1080
+ memcmp(current_tz.c_str(), value.timezone, value.timezone_len) != 0) {
1081
+ homeassistant::global_homeassistant_time->set_timezone(
1082
+ std::string(reinterpret_cast<const char *>(value.timezone), value.timezone_len));
1083
+ }
1080
1084
  }
1081
1085
  #endif
1082
1086
  }
@@ -1193,6 +1197,23 @@ bool APIConnection::send_voice_assistant_get_configuration_response(const VoiceA
1193
1197
  resp_wake_word.trained_languages.push_back(lang);
1194
1198
  }
1195
1199
  }
1200
+
1201
+ // Filter external wake words
1202
+ for (auto &wake_word : msg.external_wake_words) {
1203
+ if (wake_word.model_type != "micro") {
1204
+ // microWakeWord only
1205
+ continue;
1206
+ }
1207
+
1208
+ resp.available_wake_words.emplace_back();
1209
+ auto &resp_wake_word = resp.available_wake_words.back();
1210
+ resp_wake_word.set_id(StringRef(wake_word.id));
1211
+ resp_wake_word.set_wake_word(StringRef(wake_word.wake_word));
1212
+ for (const auto &lang : wake_word.trained_languages) {
1213
+ resp_wake_word.trained_languages.push_back(lang);
1214
+ }
1215
+ }
1216
+
1196
1217
  resp.active_wake_words = &config.active_wake_words;
1197
1218
  resp.max_active_wake_words = config.max_active_wake_words;
1198
1219
  return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
@@ -1203,7 +1224,16 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
1203
1224
  voice_assistant::global_voice_assistant->on_set_configuration(msg.active_wake_words);
1204
1225
  }
1205
1226
  }
1227
+ #endif
1228
+
1229
+ #ifdef USE_ZWAVE_PROXY
1230
+ void APIConnection::zwave_proxy_frame(const ZWaveProxyFrame &msg) {
1231
+ zwave_proxy::global_zwave_proxy->send_frame(msg.data, msg.data_len);
1232
+ }
1206
1233
 
1234
+ void APIConnection::zwave_proxy_request(const ZWaveProxyRequest &msg) {
1235
+ zwave_proxy::global_zwave_proxy->zwave_proxy_request(this, msg.type);
1236
+ }
1207
1237
  #endif
1208
1238
 
1209
1239
  #ifdef USE_ALARM_CONTROL_PANEL
@@ -1350,7 +1380,7 @@ void APIConnection::complete_authentication_() {
1350
1380
  }
1351
1381
 
1352
1382
  this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
1353
- ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
1383
+ ESP_LOGD(TAG, "%s (%s) connected", this->client_info_.name.c_str(), this->client_info_.peername.c_str());
1354
1384
  #ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1355
1385
  this->parent_->get_client_connected_trigger()->trigger(this->client_info_.name, this->client_info_.peername);
1356
1386
  #endif
@@ -1359,10 +1389,15 @@ void APIConnection::complete_authentication_() {
1359
1389
  this->send_time_request();
1360
1390
  }
1361
1391
  #endif
1392
+ #ifdef USE_ZWAVE_PROXY
1393
+ if (zwave_proxy::global_zwave_proxy != nullptr) {
1394
+ zwave_proxy::global_zwave_proxy->api_connection_authenticated(this);
1395
+ }
1396
+ #endif
1362
1397
  }
1363
1398
 
1364
1399
  bool APIConnection::send_hello_response(const HelloRequest &msg) {
1365
- this->client_info_.name = msg.client_info;
1400
+ this->client_info_.name.assign(reinterpret_cast<const char *>(msg.client_info), msg.client_info_len);
1366
1401
  this->client_info_.peername = this->helper_->getpeername();
1367
1402
  this->client_api_version_major_ = msg.api_version_major;
1368
1403
  this->client_api_version_minor_ = msg.api_version_minor;
@@ -1386,20 +1421,17 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) {
1386
1421
 
1387
1422
  return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
1388
1423
  }
1389
- bool APIConnection::send_connect_response(const ConnectRequest &msg) {
1390
- bool correct = true;
1391
1424
  #ifdef USE_API_PASSWORD
1392
- correct = this->parent_->check_password(msg.password);
1393
- #endif
1394
-
1395
- ConnectResponse resp;
1425
+ bool APIConnection::send_authenticate_response(const AuthenticationRequest &msg) {
1426
+ AuthenticationResponse resp;
1396
1427
  // bool invalid_password = 1;
1397
- resp.invalid_password = !correct;
1398
- if (correct) {
1428
+ resp.invalid_password = !this->parent_->check_password(msg.password, msg.password_len);
1429
+ if (!resp.invalid_password) {
1399
1430
  this->complete_authentication_();
1400
1431
  }
1401
- return this->send_message(resp, ConnectResponse::MESSAGE_TYPE);
1432
+ return this->send_message(resp, AuthenticationResponse::MESSAGE_TYPE);
1402
1433
  }
1434
+ #endif // USE_API_PASSWORD
1403
1435
 
1404
1436
  bool APIConnection::send_ping_response(const PingRequest &msg) {
1405
1437
  PingResponse resp;
@@ -1463,6 +1495,10 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
1463
1495
  #ifdef USE_VOICE_ASSISTANT
1464
1496
  resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
1465
1497
  #endif
1498
+ #ifdef USE_ZWAVE_PROXY
1499
+ resp.zwave_proxy_feature_flags = zwave_proxy::global_zwave_proxy->get_feature_flags();
1500
+ resp.zwave_home_id = zwave_proxy::global_zwave_proxy->get_home_id();
1501
+ #endif
1466
1502
  #ifdef USE_API_NOISE
1467
1503
  resp.api_encryption_supported = true;
1468
1504
  #endif
@@ -1513,6 +1549,20 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
1513
1549
  }
1514
1550
  }
1515
1551
  #endif
1552
+
1553
+ #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
1554
+ void APIConnection::on_homeassistant_action_response(const HomeassistantActionResponse &msg) {
1555
+ #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
1556
+ if (msg.response_data_len > 0) {
1557
+ this->parent_->handle_action_response(msg.call_id, msg.success, msg.error_message, msg.response_data,
1558
+ msg.response_data_len);
1559
+ } else
1560
+ #endif
1561
+ {
1562
+ this->parent_->handle_action_response(msg.call_id, msg.success, msg.error_message);
1563
+ }
1564
+ };
1565
+ #endif
1516
1566
  #ifdef USE_API_NOISE
1517
1567
  bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
1518
1568
  NoiseEncryptionSetKeyResponse resp;
@@ -1543,8 +1593,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
1543
1593
  delay(0);
1544
1594
  APIError err = this->helper_->loop();
1545
1595
  if (err != APIError::OK) {
1546
- on_fatal_error();
1547
- this->log_socket_operation_failed_(err);
1596
+ this->fatal_error_with_log_(LOG_STR("Socket operation failed"), err);
1548
1597
  return false;
1549
1598
  }
1550
1599
  if (this->helper_->can_write_without_blocking())
@@ -1563,8 +1612,7 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
1563
1612
  if (err == APIError::WOULD_BLOCK)
1564
1613
  return false;
1565
1614
  if (err != APIError::OK) {
1566
- on_fatal_error();
1567
- this->log_warning_(LOG_STR("Packet write failed"), err);
1615
+ this->fatal_error_with_log_(LOG_STR("Packet write failed"), err);
1568
1616
  return false;
1569
1617
  }
1570
1618
  // Do not set last_traffic_ on send
@@ -1573,12 +1621,12 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
1573
1621
  #ifdef USE_API_PASSWORD
1574
1622
  void APIConnection::on_unauthenticated_access() {
1575
1623
  this->on_fatal_error();
1576
- ESP_LOGD(TAG, "%s access without authentication", this->get_client_combined_info().c_str());
1624
+ ESP_LOGD(TAG, "%s (%s) no authentication", this->client_info_.name.c_str(), this->client_info_.peername.c_str());
1577
1625
  }
1578
1626
  #endif
1579
1627
  void APIConnection::on_no_setup_connection() {
1580
1628
  this->on_fatal_error();
1581
- ESP_LOGD(TAG, "%s access without full connection", this->get_client_combined_info().c_str());
1629
+ ESP_LOGD(TAG, "%s (%s) no connection setup", this->client_info_.name.c_str(), this->client_info_.peername.c_str());
1582
1630
  }
1583
1631
  void APIConnection::on_fatal_error() {
1584
1632
  this->helper_->close();
@@ -1750,8 +1798,7 @@ void APIConnection::process_batch_() {
1750
1798
  APIError err = this->helper_->write_protobuf_packets(ProtoWriteBuffer{&shared_buf},
1751
1799
  std::span<const PacketInfo>(packet_info, packet_count));
1752
1800
  if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
1753
- on_fatal_error();
1754
- this->log_warning_(LOG_STR("Batch write failed"), err);
1801
+ this->fatal_error_with_log_(LOG_STR("Batch write failed"), err);
1755
1802
  }
1756
1803
 
1757
1804
  #ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1830,12 +1877,8 @@ void APIConnection::process_state_subscriptions_() {
1830
1877
  #endif // USE_API_HOMEASSISTANT_STATES
1831
1878
 
1832
1879
  void APIConnection::log_warning_(const LogString *message, APIError err) {
1833
- ESP_LOGW(TAG, "%s: %s %s errno=%d", this->get_client_combined_info().c_str(), LOG_STR_ARG(message),
1834
- LOG_STR_ARG(api_error_to_logstr(err)), errno);
1835
- }
1836
-
1837
- void APIConnection::log_socket_operation_failed_(APIError err) {
1838
- this->log_warning_(LOG_STR("Socket operation failed"), err);
1880
+ ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->client_info_.name.c_str(), this->client_info_.peername.c_str(),
1881
+ LOG_STR_ARG(message), LOG_STR_ARG(api_error_to_logstr(err)), errno);
1839
1882
  }
1840
1883
 
1841
1884
  } // namespace esphome::api
@@ -10,8 +10,8 @@
10
10
  #include "esphome/core/component.h"
11
11
  #include "esphome/core/entity_base.h"
12
12
 
13
- #include <vector>
14
13
  #include <functional>
14
+ #include <vector>
15
15
 
16
16
  namespace esphome::api {
17
17
 
@@ -19,14 +19,6 @@ namespace esphome::api {
19
19
  struct ClientInfo {
20
20
  std::string name; // Client name from Hello message
21
21
  std::string peername; // IP:port from socket
22
-
23
- std::string get_combined_info() const {
24
- if (name == peername) {
25
- // Before Hello message, both are the same
26
- return name;
27
- }
28
- return name + " (" + peername + ")";
29
- }
30
22
  };
31
23
 
32
24
  // Keepalive timeout in milliseconds
@@ -132,12 +124,15 @@ class APIConnection final : public APIServerConnection {
132
124
  #endif
133
125
  bool try_send_log_message(int level, const char *tag, const char *line, size_t message_len);
134
126
  #ifdef USE_API_HOMEASSISTANT_SERVICES
135
- void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
127
+ void send_homeassistant_action(const HomeassistantActionRequest &call) {
136
128
  if (!this->flags_.service_call_subscription)
137
129
  return;
138
- this->send_message(call, HomeassistantServiceResponse::MESSAGE_TYPE);
130
+ this->send_message(call, HomeassistantActionRequest::MESSAGE_TYPE);
139
131
  }
140
- #endif
132
+ #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
133
+ void on_homeassistant_action_response(const HomeassistantActionResponse &msg) override;
134
+ #endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
135
+ #endif // USE_API_HOMEASSISTANT_SERVICES
141
136
  #ifdef USE_BLUETOOTH_PROXY
142
137
  void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
143
138
  void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
@@ -171,6 +166,11 @@ class APIConnection final : public APIServerConnection {
171
166
  void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
172
167
  #endif
173
168
 
169
+ #ifdef USE_ZWAVE_PROXY
170
+ void zwave_proxy_frame(const ZWaveProxyFrame &msg) override;
171
+ void zwave_proxy_request(const ZWaveProxyRequest &msg) override;
172
+ #endif
173
+
174
174
  #ifdef USE_ALARM_CONTROL_PANEL
175
175
  bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
176
176
  void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
@@ -197,7 +197,9 @@ class APIConnection final : public APIServerConnection {
197
197
  void on_get_time_response(const GetTimeResponse &value) override;
198
198
  #endif
199
199
  bool send_hello_response(const HelloRequest &msg) override;
200
- bool send_connect_response(const ConnectRequest &msg) override;
200
+ #ifdef USE_API_PASSWORD
201
+ bool send_authenticate_response(const AuthenticationRequest &msg) override;
202
+ #endif
201
203
  bool send_disconnect_response(const DisconnectRequest &msg) override;
202
204
  bool send_ping_response(const PingRequest &msg) override;
203
205
  bool send_device_info_response(const DeviceInfoRequest &msg) override;
@@ -271,7 +273,8 @@ class APIConnection final : public APIServerConnection {
271
273
  bool try_to_clear_buffer(bool log_out_of_space);
272
274
  bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override;
273
275
 
274
- std::string get_client_combined_info() const { return this->client_info_.get_combined_info(); }
276
+ const std::string &get_name() const { return this->client_info_.name; }
277
+ const std::string &get_peername() const { return this->client_info_.peername; }
275
278
 
276
279
  protected:
277
280
  // Helper function to handle authentication completion
@@ -732,8 +735,11 @@ class APIConnection final : public APIServerConnection {
732
735
 
733
736
  // Helper function to log API errors with errno
734
737
  void log_warning_(const LogString *message, APIError err);
735
- // Specific helper for duplicated error message
736
- void log_socket_operation_failed_(APIError err);
738
+ // Helper to handle fatal errors with logging
739
+ inline void fatal_error_with_log_(const LogString *message, APIError err) {
740
+ this->on_fatal_error();
741
+ this->log_warning_(message, err);
742
+ }
737
743
  };
738
744
 
739
745
  } // namespace esphome::api
@@ -13,7 +13,8 @@ namespace esphome::api {
13
13
 
14
14
  static const char *const TAG = "api.frame_helper";
15
15
 
16
- #define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->client_info_->get_combined_info().c_str(), ##__VA_ARGS__)
16
+ #define HELPER_LOG(msg, ...) \
17
+ ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), this->client_info_->peername.c_str(), ##__VA_ARGS__)
17
18
 
18
19
  #ifdef HELPER_LOG_PACKETS
19
20
  #define LOG_PACKET_RECEIVED(buffer) ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(buffer).c_str())
@@ -80,7 +81,7 @@ const LogString *api_error_to_logstr(APIError err) {
80
81
 
81
82
  // Default implementation for loop - handles sending buffered data
82
83
  APIError APIFrameHelper::loop() {
83
- if (!this->tx_buf_.empty()) {
84
+ if (this->tx_buf_count_ > 0) {
84
85
  APIError err = try_send_tx_buf_();
85
86
  if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
86
87
  return err;
@@ -102,9 +103,20 @@ APIError APIFrameHelper::handle_socket_write_error_() {
102
103
  // Helper method to buffer data from IOVs
103
104
  void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len,
104
105
  uint16_t offset) {
105
- SendBuffer buffer;
106
- buffer.size = total_write_len - offset;
107
- buffer.data = std::make_unique<uint8_t[]>(buffer.size);
106
+ // Check if queue is full
107
+ if (this->tx_buf_count_ >= API_MAX_SEND_QUEUE) {
108
+ HELPER_LOG("Send queue full (%u buffers), dropping connection", this->tx_buf_count_);
109
+ this->state_ = State::FAILED;
110
+ return;
111
+ }
112
+
113
+ uint16_t buffer_size = total_write_len - offset;
114
+ auto &buffer = this->tx_buf_[this->tx_buf_tail_];
115
+ buffer = std::make_unique<SendBuffer>(SendBuffer{
116
+ .data = std::make_unique<uint8_t[]>(buffer_size),
117
+ .size = buffer_size,
118
+ .offset = 0,
119
+ });
108
120
 
109
121
  uint16_t to_skip = offset;
110
122
  uint16_t write_pos = 0;
@@ -117,12 +129,15 @@ void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt,
117
129
  // Include this segment (partially or fully)
118
130
  const uint8_t *src = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_skip;
119
131
  uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_skip;
120
- std::memcpy(buffer.data.get() + write_pos, src, len);
132
+ std::memcpy(buffer->data.get() + write_pos, src, len);
121
133
  write_pos += len;
122
134
  to_skip = 0;
123
135
  }
124
136
  }
125
- this->tx_buf_.push_back(std::move(buffer));
137
+
138
+ // Update circular buffer tracking
139
+ this->tx_buf_tail_ = (this->tx_buf_tail_ + 1) % API_MAX_SEND_QUEUE;
140
+ this->tx_buf_count_++;
126
141
  }
127
142
 
128
143
  // This method writes data to socket or buffers it
@@ -140,7 +155,7 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_
140
155
  #endif
141
156
 
142
157
  // Try to send any existing buffered data first if there is any
143
- if (!this->tx_buf_.empty()) {
158
+ if (this->tx_buf_count_ > 0) {
144
159
  APIError send_result = try_send_tx_buf_();
145
160
  // If real error occurred (not just WOULD_BLOCK), return it
146
161
  if (send_result != APIError::OK && send_result != APIError::WOULD_BLOCK) {
@@ -149,7 +164,7 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_
149
164
 
150
165
  // If there is still data in the buffer, we can't send, buffer
151
166
  // the new data and return
152
- if (!this->tx_buf_.empty()) {
167
+ if (this->tx_buf_count_ > 0) {
153
168
  this->buffer_data_from_iov_(iov, iovcnt, total_write_len, 0);
154
169
  return APIError::OK; // Success, data buffered
155
170
  }
@@ -177,32 +192,31 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_
177
192
  }
178
193
 
179
194
  // Common implementation for trying to send buffered data
180
- // IMPORTANT: Caller MUST ensure tx_buf_ is not empty before calling this method
195
+ // IMPORTANT: Caller MUST ensure tx_buf_count_ > 0 before calling this method
181
196
  APIError APIFrameHelper::try_send_tx_buf_() {
182
197
  // Try to send from tx_buf - we assume it's not empty as it's the caller's responsibility to check
183
- bool tx_buf_empty = false;
184
- while (!tx_buf_empty) {
198
+ while (this->tx_buf_count_ > 0) {
185
199
  // Get the first buffer in the queue
186
- SendBuffer &front_buffer = this->tx_buf_.front();
200
+ SendBuffer *front_buffer = this->tx_buf_[this->tx_buf_head_].get();
187
201
 
188
202
  // Try to send the remaining data in this buffer
189
- ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining());
203
+ ssize_t sent = this->socket_->write(front_buffer->current_data(), front_buffer->remaining());
190
204
 
191
205
  if (sent == -1) {
192
206
  return this->handle_socket_write_error_();
193
207
  } else if (sent == 0) {
194
208
  // Nothing sent but not an error
195
209
  return APIError::WOULD_BLOCK;
196
- } else if (static_cast<uint16_t>(sent) < front_buffer.remaining()) {
210
+ } else if (static_cast<uint16_t>(sent) < front_buffer->remaining()) {
197
211
  // Partially sent, update offset
198
212
  // Cast to ensure no overflow issues with uint16_t
199
- front_buffer.offset += static_cast<uint16_t>(sent);
213
+ front_buffer->offset += static_cast<uint16_t>(sent);
200
214
  return APIError::WOULD_BLOCK; // Stop processing more buffers if we couldn't send a complete buffer
201
215
  } else {
202
216
  // Buffer completely sent, remove it from the queue
203
- this->tx_buf_.pop_front();
204
- // Update empty status for the loop condition
205
- tx_buf_empty = this->tx_buf_.empty();
217
+ this->tx_buf_[this->tx_buf_head_].reset();
218
+ this->tx_buf_head_ = (this->tx_buf_head_ + 1) % API_MAX_SEND_QUEUE;
219
+ this->tx_buf_count_--;
206
220
  // Continue loop to try sending the next buffer
207
221
  }
208
222
  }
@@ -1,7 +1,8 @@
1
1
  #pragma once
2
+ #include <array>
2
3
  #include <cstdint>
3
- #include <deque>
4
4
  #include <limits>
5
+ #include <memory>
5
6
  #include <span>
6
7
  #include <utility>
7
8
  #include <vector>
@@ -17,6 +18,17 @@ namespace esphome::api {
17
18
  // uncomment to log raw packets
18
19
  //#define HELPER_LOG_PACKETS
19
20
 
21
+ // Maximum message size limits to prevent OOM on constrained devices
22
+ // Handshake messages are limited to a small size for security
23
+ static constexpr uint16_t MAX_HANDSHAKE_SIZE = 128;
24
+
25
+ // Data message limits vary by platform based on available memory
26
+ #ifdef USE_ESP8266
27
+ static constexpr uint16_t MAX_MESSAGE_SIZE = 8192; // 8 KiB for ESP8266
28
+ #else
29
+ static constexpr uint16_t MAX_MESSAGE_SIZE = 32768; // 32 KiB for ESP32 and other platforms
30
+ #endif
31
+
20
32
  // Forward declaration
21
33
  struct ClientInfo;
22
34
 
@@ -79,7 +91,7 @@ class APIFrameHelper {
79
91
  virtual APIError init() = 0;
80
92
  virtual APIError loop();
81
93
  virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
82
- bool can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
94
+ bool can_write_without_blocking() { return this->state_ == State::DATA && this->tx_buf_count_ == 0; }
83
95
  std::string getpeername() { return socket_->getpeername(); }
84
96
  int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); }
85
97
  APIError close() {
@@ -161,7 +173,7 @@ class APIFrameHelper {
161
173
  };
162
174
 
163
175
  // Containers (size varies, but typically 12+ bytes on 32-bit)
164
- std::deque<SendBuffer> tx_buf_;
176
+ std::array<std::unique_ptr<SendBuffer>, API_MAX_SEND_QUEUE> tx_buf_;
165
177
  std::vector<struct iovec> reusable_iovs_;
166
178
  std::vector<uint8_t> rx_buf_;
167
179
 
@@ -174,7 +186,10 @@ class APIFrameHelper {
174
186
  State state_{State::INITIALIZE};
175
187
  uint8_t frame_header_padding_{0};
176
188
  uint8_t frame_footer_size_{0};
177
- // 5 bytes total, 3 bytes padding
189
+ uint8_t tx_buf_head_{0};
190
+ uint8_t tx_buf_tail_{0};
191
+ uint8_t tx_buf_count_{0};
192
+ // 8 bytes total, 0 bytes padding
178
193
 
179
194
  // Common initialization for both plaintext and noise protocols
180
195
  APIError init_common_();