esphome 2025.6.2__py3-none-any.whl → 2025.7.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 (601) hide show
  1. esphome/__main__.py +1 -3
  2. esphome/codegen.py +2 -0
  3. esphome/components/ac_dimmer/ac_dimmer.cpp +6 -6
  4. esphome/components/adc/__init__.py +25 -1
  5. esphome/components/adc/adc_sensor.h +11 -11
  6. esphome/components/adc/adc_sensor_common.cpp +1 -1
  7. esphome/components/adc/adc_sensor_esp32.cpp +16 -8
  8. esphome/components/ade7880/ade7880.h +0 -2
  9. esphome/components/ads1115/ads1115.h +0 -1
  10. esphome/components/ads1118/ads1118.h +0 -1
  11. esphome/components/ags10/ags10.h +0 -2
  12. esphome/components/aic3204/aic3204.h +0 -1
  13. esphome/components/alarm_control_panel/__init__.py +5 -2
  14. esphome/components/alpha3/alpha3.h +0 -1
  15. esphome/components/am43/cover/am43_cover.h +0 -1
  16. esphome/components/am43/sensor/am43_sensor.h +0 -1
  17. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +0 -2
  18. esphome/components/anova/anova.cpp +5 -1
  19. esphome/components/anova/anova.h +0 -1
  20. esphome/components/apds9960/apds9960.cpp +1 -1
  21. esphome/components/api/__init__.py +42 -20
  22. esphome/components/api/api_connection.cpp +318 -391
  23. esphome/components/api/api_connection.h +206 -126
  24. esphome/components/api/api_frame_helper.cpp +89 -124
  25. esphome/components/api/api_frame_helper.h +57 -45
  26. esphome/components/api/api_pb2.cpp +414 -4350
  27. esphome/components/api/api_pb2.h +287 -198
  28. esphome/components/api/api_pb2_dump.cpp +4333 -0
  29. esphome/components/api/api_pb2_service.cpp +180 -425
  30. esphome/components/api/api_pb2_service.h +7 -6
  31. esphome/components/api/api_pb2_size.h +2 -4
  32. esphome/components/api/api_server.cpp +138 -167
  33. esphome/components/api/api_server.h +66 -12
  34. esphome/components/api/client.py +10 -4
  35. esphome/components/api/list_entities.cpp +36 -105
  36. esphome/components/api/list_entities.h +31 -23
  37. esphome/components/api/proto.h +26 -3
  38. esphome/components/api/subscribe_state.cpp +23 -29
  39. esphome/components/api/subscribe_state.h +26 -19
  40. esphome/components/as5600/as5600.h +0 -1
  41. esphome/components/async_tcp/__init__.py +14 -5
  42. esphome/components/atc_mithermometer/atc_mithermometer.h +0 -1
  43. esphome/components/atm90e32/atm90e32.cpp +2 -1
  44. esphome/components/audio/audio_decoder.cpp +1 -1
  45. esphome/components/audio/audio_transfer_buffer.cpp +2 -2
  46. esphome/components/b_parasite/b_parasite.h +0 -1
  47. esphome/components/bedjet/bedjet_hub.cpp +5 -1
  48. esphome/components/bedjet/climate/bedjet_climate.cpp +5 -1
  49. esphome/components/beken_spi_led_strip/led_strip.cpp +4 -2
  50. esphome/components/bh1750/bh1750.cpp +5 -5
  51. esphome/components/binary_sensor/__init__.py +82 -5
  52. esphome/components/binary_sensor/automation.h +19 -1
  53. esphome/components/binary_sensor/binary_sensor.cpp +12 -30
  54. esphome/components/binary_sensor/binary_sensor.h +11 -25
  55. esphome/components/binary_sensor/filter.cpp +29 -24
  56. esphome/components/binary_sensor/filter.h +20 -10
  57. esphome/components/ble_client/output/ble_binary_output.h +0 -1
  58. esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +5 -1
  59. esphome/components/ble_client/sensor/ble_rssi_sensor.h +0 -1
  60. esphome/components/ble_client/sensor/ble_sensor.cpp +5 -1
  61. esphome/components/ble_client/sensor/ble_sensor.h +0 -1
  62. esphome/components/ble_client/switch/ble_switch.h +0 -1
  63. esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +5 -1
  64. esphome/components/ble_client/text_sensor/ble_text_sensor.h +0 -1
  65. esphome/components/ble_presence/ble_presence_device.h +0 -1
  66. esphome/components/ble_rssi/ble_rssi_sensor.h +0 -1
  67. esphome/components/ble_scanner/ble_scanner.h +0 -1
  68. esphome/components/bluetooth_proxy/bluetooth_connection.h +9 -2
  69. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +16 -6
  70. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -2
  71. esphome/components/bme680/sensor.py +1 -1
  72. esphome/components/bmp581/bmp581.h +0 -2
  73. esphome/components/button/__init__.py +5 -2
  74. esphome/components/camera/__init__.py +1 -0
  75. esphome/components/camera/camera.cpp +22 -0
  76. esphome/components/camera/camera.h +80 -0
  77. esphome/components/canbus/__init__.py +1 -0
  78. esphome/components/cap1188/cap1188.h +0 -1
  79. esphome/components/captive_portal/__init__.py +12 -2
  80. esphome/components/captive_portal/captive_portal.cpp +12 -2
  81. esphome/components/captive_portal/captive_portal.h +5 -2
  82. esphome/components/ccs811/ccs811.h +0 -2
  83. esphome/components/climate/__init__.py +5 -2
  84. esphome/components/cm1106/sensor.py +2 -2
  85. esphome/components/const/__init__.py +2 -0
  86. esphome/components/copy/binary_sensor/copy_binary_sensor.h +0 -1
  87. esphome/components/copy/button/copy_button.h +0 -1
  88. esphome/components/copy/cover/copy_cover.h +0 -1
  89. esphome/components/copy/fan/copy_fan.h +0 -1
  90. esphome/components/copy/lock/copy_lock.h +0 -1
  91. esphome/components/copy/number/copy_number.h +0 -1
  92. esphome/components/copy/select/copy_select.h +0 -1
  93. esphome/components/copy/sensor/copy_sensor.h +0 -1
  94. esphome/components/copy/switch/copy_switch.h +0 -1
  95. esphome/components/copy/text/copy_text.h +0 -1
  96. esphome/components/copy/text_sensor/copy_text_sensor.h +0 -1
  97. esphome/components/cover/__init__.py +5 -2
  98. esphome/components/cs5460a/cs5460a.h +0 -1
  99. esphome/components/datetime/__init__.py +4 -2
  100. esphome/components/debug/__init__.py +20 -0
  101. esphome/components/deep_sleep/__init__.py +43 -9
  102. esphome/components/demo/__init__.py +2 -2
  103. esphome/components/display/display.cpp +4 -3
  104. esphome/components/display/display.h +0 -2
  105. esphome/components/display/display_buffer.cpp +1 -1
  106. esphome/components/ds2484/__init__.py +1 -0
  107. esphome/components/ds2484/ds2484.cpp +209 -0
  108. esphome/components/ds2484/ds2484.h +43 -0
  109. esphome/components/ds2484/one_wire.py +37 -0
  110. esphome/components/duty_time/duty_time_sensor.h +0 -1
  111. esphome/components/ens160_base/ens160_base.h +0 -1
  112. esphome/components/es7210/es7210.h +0 -1
  113. esphome/components/es7243e/es7243e.h +0 -1
  114. esphome/components/es8156/es8156.h +0 -1
  115. esphome/components/es8311/es8311.h +0 -1
  116. esphome/components/es8388/es8388.h +0 -1
  117. esphome/components/esp32/__init__.py +102 -135
  118. esphome/components/esp32/core.cpp +0 -4
  119. esphome/components/esp32/gpio.h +1 -1
  120. esphome/components/esp32/helpers.cpp +69 -0
  121. esphome/components/esp32_ble/ble.cpp +5 -6
  122. esphome/components/esp32_ble/ble.h +29 -14
  123. esphome/components/esp32_ble/ble_event.h +6 -6
  124. esphome/components/esp32_ble_client/ble_client_base.cpp +21 -6
  125. esphome/components/esp32_ble_client/ble_client_base.h +24 -9
  126. esphome/components/esp32_ble_tracker/__init__.py +2 -8
  127. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +5 -5
  128. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +11 -7
  129. esphome/components/esp32_camera/__init__.py +111 -97
  130. esphome/components/esp32_camera/esp32_camera.cpp +41 -31
  131. esphome/components/esp32_camera/esp32_camera.h +35 -30
  132. esphome/components/esp32_camera_web_server/__init__.py +2 -1
  133. esphome/components/esp32_camera_web_server/camera_web_server.cpp +8 -8
  134. esphome/components/esp32_camera_web_server/camera_web_server.h +3 -3
  135. esphome/components/esp32_hall/sensor.py +2 -21
  136. esphome/components/esp32_hosted/__init__.py +101 -0
  137. esphome/components/esp32_hosted/esp32_hosted.py.script +12 -0
  138. esphome/components/esp32_improv/esp32_improv_component.cpp +3 -0
  139. esphome/components/esp32_rmt/__init__.py +0 -58
  140. esphome/components/esp32_rmt_led_strip/led_strip.cpp +77 -63
  141. esphome/components/esp32_rmt_led_strip/led_strip.h +11 -17
  142. esphome/components/esp32_rmt_led_strip/light.py +14 -76
  143. esphome/components/esp32_touch/esp32_touch.h +174 -28
  144. esphome/components/esp32_touch/esp32_touch_common.cpp +162 -0
  145. esphome/components/esp32_touch/esp32_touch_v1.cpp +238 -0
  146. esphome/components/esp32_touch/esp32_touch_v2.cpp +397 -0
  147. esphome/components/esp8266/__init__.py +1 -0
  148. esphome/components/esp8266/gpio.cpp +10 -10
  149. esphome/components/esp8266/helpers.cpp +31 -0
  150. esphome/components/esphome/ota/__init__.py +1 -0
  151. esphome/components/esphome/ota/ota_esphome.cpp +24 -19
  152. esphome/components/ethernet/__init__.py +42 -23
  153. esphome/components/ethernet/esp_eth_phy_jl1101.c +0 -16
  154. esphome/components/ethernet/ethernet_component.cpp +69 -29
  155. esphome/components/ethernet/ethernet_component.h +18 -10
  156. esphome/components/event/__init__.py +5 -2
  157. esphome/components/ezo/ezo.h +0 -1
  158. esphome/components/ezo_pmp/ezo_pmp.h +0 -1
  159. esphome/components/fan/__init__.py +5 -2
  160. esphome/components/feedback/feedback_cover.h +0 -1
  161. esphome/components/font/__init__.py +92 -82
  162. esphome/components/font/font.cpp +9 -2
  163. esphome/components/font/font.h +20 -5
  164. esphome/components/fs3000/fs3000.h +0 -1
  165. esphome/components/gcja5/gcja5.h +0 -1
  166. esphome/components/gl_r01_i2c/__init__.py +0 -0
  167. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +68 -0
  168. esphome/components/gl_r01_i2c/gl_r01_i2c.h +22 -0
  169. esphome/components/gl_r01_i2c/sensor.py +36 -0
  170. esphome/components/gp8403/gp8403.h +0 -1
  171. esphome/components/gpio/binary_sensor/__init__.py +17 -0
  172. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +77 -3
  173. esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +40 -0
  174. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +0 -2
  175. esphome/components/he60r/he60r.h +0 -1
  176. esphome/components/heatpumpir/climate.py +2 -1
  177. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  178. esphome/components/heatpumpir/heatpumpir.h +1 -0
  179. esphome/components/honeywellabp2_i2c/honeywellabp2.h +0 -1
  180. esphome/components/host/__init__.py +2 -1
  181. esphome/components/host/helpers.cpp +57 -0
  182. esphome/components/http_request/__init__.py +19 -1
  183. esphome/components/http_request/http_request.h +1 -1
  184. esphome/components/http_request/http_request_arduino.cpp +0 -1
  185. esphome/components/http_request/http_request_arduino.h +1 -0
  186. esphome/components/http_request/http_request_idf.cpp +0 -1
  187. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  188. esphome/components/http_request/update/http_request_update.cpp +28 -9
  189. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +3 -9
  190. esphome/components/hydreon_rgxx/sensor.py +1 -1
  191. esphome/components/i2c/__init__.py +23 -11
  192. esphome/components/i2c/i2c_bus.h +8 -1
  193. esphome/components/i2c/i2c_bus_arduino.cpp +4 -3
  194. esphome/components/i2c/i2c_bus_arduino.h +6 -3
  195. esphome/components/i2c/i2c_bus_esp_idf.h +5 -3
  196. esphome/components/i2c_device/i2c_device.h +0 -1
  197. esphome/components/i2s_audio/__init__.py +2 -10
  198. esphome/components/i2s_audio/i2s_audio.cpp +1 -5
  199. esphome/components/i2s_audio/media_player/__init__.py +2 -2
  200. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +2 -2
  201. esphome/components/iaqcore/iaqcore.h +0 -2
  202. esphome/components/image/__init__.py +123 -24
  203. esphome/components/improv_serial/improv_serial_component.cpp +0 -4
  204. esphome/components/ina219/ina219.cpp +7 -0
  205. esphome/components/ina219/ina219.h +1 -0
  206. esphome/components/ina260/ina260.h +0 -2
  207. esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +0 -1
  208. esphome/components/inkplate6/display.py +15 -0
  209. esphome/components/inkplate6/inkplate.cpp +2 -2
  210. esphome/components/integration/integration_sensor.h +0 -1
  211. esphome/components/internal_temperature/internal_temperature.cpp +8 -27
  212. esphome/components/internal_temperature/sensor.py +0 -26
  213. esphome/components/interval/interval.h +0 -2
  214. esphome/components/ld2410/button/__init__.py +3 -3
  215. esphome/components/ld2410/button/factory_reset_button.cpp +9 -0
  216. esphome/components/ld2410/button/{reset_button.h → factory_reset_button.h} +2 -2
  217. esphome/components/ld2410/ld2410.cpp +430 -261
  218. esphome/components/ld2410/ld2410.h +44 -146
  219. esphome/components/ld2410/number/__init__.py +2 -2
  220. esphome/components/ld2410/sensor.py +1 -1
  221. esphome/components/ld2410/switch/__init__.py +1 -1
  222. esphome/components/ld2420/ld2420.cpp +196 -100
  223. esphome/components/ld2420/ld2420.h +46 -118
  224. esphome/components/ld2420/number/__init__.py +2 -2
  225. esphome/components/ld2420/sensor/__init__.py +6 -2
  226. esphome/components/ld2420/sensor/ld2420_sensor.h +1 -1
  227. esphome/components/ld2450/button/__init__.py +3 -3
  228. esphome/components/ld2450/button/factory_reset_button.cpp +9 -0
  229. esphome/components/ld2450/button/{reset_button.h → factory_reset_button.h} +2 -2
  230. esphome/components/ld2450/ld2450.cpp +384 -232
  231. esphome/components/ld2450/ld2450.h +60 -69
  232. esphome/components/ld2450/switch/__init__.py +1 -1
  233. esphome/components/ledc/ledc_output.cpp +1 -63
  234. esphome/components/libretiny/__init__.py +4 -3
  235. esphome/components/libretiny/const.py +5 -0
  236. esphome/components/libretiny/generate_components.py +1 -0
  237. esphome/components/libretiny/helpers.cpp +35 -0
  238. esphome/components/libretiny/lt_component.cpp +5 -3
  239. esphome/components/light/__init__.py +4 -2
  240. esphome/components/light/addressable_light.h +3 -3
  241. esphome/components/light/light_call.cpp +180 -243
  242. esphome/components/light/light_call.h +72 -20
  243. esphome/components/light/light_color_values.h +14 -14
  244. esphome/components/light/light_state.h +15 -13
  245. esphome/components/light/transformers.h +2 -2
  246. esphome/components/ln882x/__init__.py +52 -0
  247. esphome/components/ln882x/boards.py +285 -0
  248. esphome/components/lock/__init__.py +5 -2
  249. esphome/components/logger/__init__.py +40 -3
  250. esphome/components/logger/logger.cpp +47 -12
  251. esphome/components/logger/logger.h +80 -49
  252. esphome/components/logger/logger_esp32.cpp +3 -3
  253. esphome/components/lps22/__init__.py +0 -0
  254. esphome/components/lps22/lps22.cpp +75 -0
  255. esphome/components/lps22/lps22.h +27 -0
  256. esphome/components/lps22/sensor.py +58 -0
  257. esphome/components/ltr390/ltr390.h +0 -1
  258. esphome/components/ltr501/ltr501.h +0 -1
  259. esphome/components/ltr_als_ps/ltr_als_ps.h +0 -1
  260. esphome/components/lvgl/__init__.py +1 -1
  261. esphome/components/lvgl/schemas.py +66 -6
  262. esphome/components/lvgl/styles.py +24 -16
  263. esphome/components/lvgl/widgets/__init__.py +12 -2
  264. esphome/components/lvgl/widgets/lv_bar.py +40 -19
  265. esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +1 -1
  266. esphome/components/max9611/max9611.h +0 -1
  267. esphome/components/mcp23016/__init__.py +1 -1
  268. esphome/components/mcp23xxx_base/__init__.py +1 -1
  269. esphome/components/mcp4461/__init__.py +1 -1
  270. esphome/components/mcp4461/output/__init__.py +3 -2
  271. esphome/components/mcp9600/mcp9600.h +0 -2
  272. esphome/components/md5/md5.cpp +3 -3
  273. esphome/components/md5/md5.h +1 -6
  274. esphome/components/mdns/__init__.py +22 -11
  275. esphome/components/media_player/__init__.py +4 -3
  276. esphome/components/micro_wake_word/__init__.py +1 -5
  277. esphome/components/micro_wake_word/streaming_model.cpp +2 -2
  278. esphome/components/microphone/microphone.cpp +7 -9
  279. esphome/components/microphone/microphone.h +0 -2
  280. esphome/components/mipi_spi/display.py +1 -0
  281. esphome/components/mmc5603/mmc5603.cpp +1 -1
  282. esphome/components/modbus/modbus.cpp +33 -15
  283. esphome/components/modbus/modbus.h +9 -0
  284. esphome/components/modbus_controller/__init__.py +42 -10
  285. esphome/components/modbus_controller/modbus_controller.cpp +92 -11
  286. esphome/components/modbus_controller/modbus_controller.h +61 -7
  287. esphome/components/mopeka_pro_check/mopeka_pro_check.h +0 -1
  288. esphome/components/mopeka_std_check/mopeka_std_check.h +0 -1
  289. esphome/components/mpl3115a2/mpl3115a2.h +0 -2
  290. esphome/components/mqtt/__init__.py +16 -0
  291. esphome/components/mqtt/mqtt_backend.h +2 -1
  292. esphome/components/mqtt/mqtt_backend_esp32.cpp +126 -45
  293. esphome/components/mqtt/mqtt_backend_esp32.h +106 -4
  294. esphome/components/mqtt/mqtt_client.cpp +15 -9
  295. esphome/components/mqtt/mqtt_client.h +8 -3
  296. esphome/components/ms8607/ms8607.h +0 -1
  297. esphome/components/neopixelbus/light.py +4 -1
  298. esphome/components/neopixelbus/neopixelbus_light.h +1 -1
  299. esphome/components/network/__init__.py +4 -1
  300. esphome/components/network/ip_address.h +1 -0
  301. esphome/components/nextion/__init__.py +16 -0
  302. esphome/components/nextion/base_component.py +1 -0
  303. esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
  304. esphome/components/nextion/display.py +14 -4
  305. esphome/components/nextion/nextion.cpp +166 -101
  306. esphome/components/nextion/nextion.h +84 -53
  307. esphome/components/nextion/nextion_commands.cpp +11 -10
  308. esphome/components/nextion/nextion_component.cpp +28 -28
  309. esphome/components/nextion/nextion_component.h +53 -18
  310. esphome/components/nextion/nextion_component_base.h +3 -0
  311. esphome/components/nextion/nextion_upload.cpp +36 -0
  312. esphome/components/nextion/nextion_upload_arduino.cpp +10 -35
  313. esphome/components/nextion/nextion_upload_idf.cpp +9 -33
  314. esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
  315. esphome/components/nextion/switch/nextion_switch.cpp +1 -1
  316. esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
  317. esphome/components/nfc/nfc.cpp +3 -22
  318. esphome/components/nfc/nfc.h +3 -3
  319. esphome/components/number/__init__.py +5 -2
  320. esphome/components/online_image/__init__.py +5 -0
  321. esphome/components/online_image/online_image.cpp +6 -2
  322. esphome/components/online_image/online_image.h +4 -1
  323. esphome/components/opentherm/opentherm.cpp +7 -12
  324. esphome/components/openthread/__init__.py +47 -40
  325. esphome/components/openthread/const.py +1 -0
  326. esphome/components/openthread/openthread_esp.cpp +27 -5
  327. esphome/components/opt3001/__init__.py +0 -0
  328. esphome/components/opt3001/opt3001.cpp +122 -0
  329. esphome/components/opt3001/opt3001.h +27 -0
  330. esphome/components/opt3001/sensor.py +35 -0
  331. esphome/components/ota/__init__.py +17 -0
  332. esphome/components/ota/ota_backend.h +27 -1
  333. esphome/components/ota/ota_backend_arduino_esp32.cpp +12 -2
  334. esphome/components/ota/ota_backend_arduino_esp32.h +3 -0
  335. esphome/components/ota/ota_backend_arduino_esp8266.cpp +18 -4
  336. esphome/components/ota/ota_backend_arduino_esp8266.h +3 -0
  337. esphome/components/ota/ota_backend_arduino_libretiny.cpp +12 -2
  338. esphome/components/ota/ota_backend_arduino_libretiny.h +3 -0
  339. esphome/components/ota/ota_backend_arduino_rp2040.cpp +9 -2
  340. esphome/components/ota/ota_backend_arduino_rp2040.h +3 -0
  341. esphome/components/ota/ota_backend_esp_idf.cpp +10 -16
  342. esphome/components/ota/ota_backend_esp_idf.h +1 -0
  343. esphome/components/packages/__init__.py +5 -2
  344. esphome/components/packet_transport/binary_sensor.py +61 -4
  345. esphome/components/packet_transport/packet_transport.cpp +31 -1
  346. esphome/components/packet_transport/packet_transport.h +11 -5
  347. esphome/components/pcf8574/__init__.py +1 -1
  348. esphome/components/pi4ioe5v6408/__init__.py +84 -0
  349. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +171 -0
  350. esphome/components/pi4ioe5v6408/pi4ioe5v6408.h +70 -0
  351. esphome/components/pmsa003i/pmsa003i.h +0 -1
  352. esphome/components/pmsx003/pmsx003.h +0 -1
  353. esphome/components/pn7150/pn7150.cpp +7 -7
  354. esphome/components/pn7150/pn7150.h +0 -1
  355. esphome/components/pn7160/pn7160.cpp +7 -7
  356. esphome/components/pn7160/pn7160.h +0 -1
  357. esphome/components/preferences/syncer.h +2 -0
  358. esphome/components/prometheus/prometheus_handler.h +1 -1
  359. esphome/components/psram/psram.cpp +0 -20
  360. esphome/components/pulse_counter/pulse_counter_sensor.h +0 -1
  361. esphome/components/pulse_meter/pulse_meter_sensor.cpp +8 -4
  362. esphome/components/pulse_width/pulse_width.h +0 -1
  363. esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +0 -4
  364. esphome/components/pvvx_mithermometer/display/pvvx_display.h +0 -2
  365. esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +0 -1
  366. esphome/components/qr_code/__init__.py +13 -10
  367. esphome/components/qwiic_pir/qwiic_pir.h +0 -1
  368. esphome/components/radon_eye_ble/radon_eye_listener.cpp +1 -1
  369. esphome/components/rc522/rc522.h +0 -1
  370. esphome/components/rdm6300/rdm6300.h +0 -2
  371. esphome/components/remote_base/__init__.py +7 -5
  372. esphome/components/remote_base/remote_base.cpp +24 -21
  373. esphome/components/remote_base/remote_base.h +3 -26
  374. esphome/components/remote_receiver/__init__.py +40 -46
  375. esphome/components/remote_receiver/remote_receiver.h +4 -18
  376. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -87
  377. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +1 -1
  378. esphome/components/remote_transmitter/__init__.py +42 -43
  379. esphome/components/remote_transmitter/remote_transmitter.h +2 -14
  380. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -77
  381. esphome/components/resistance/resistance_sensor.h +0 -1
  382. esphome/components/rp2040/__init__.py +1 -0
  383. esphome/components/rp2040/helpers.cpp +55 -0
  384. esphome/components/rp2040_pio_led_strip/led_strip.cpp +2 -2
  385. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -4
  386. esphome/components/rtttl/__init__.py +4 -4
  387. esphome/components/rtttl/rtttl.cpp +10 -1
  388. esphome/components/ruuvitag/ruuvitag.h +0 -1
  389. esphome/components/safe_mode/safe_mode.cpp +2 -0
  390. esphome/components/safe_mode/safe_mode.h +4 -1
  391. esphome/components/scd30/scd30.h +0 -1
  392. esphome/components/scd30/sensor.py +2 -2
  393. esphome/components/scd4x/scd4x.cpp +61 -54
  394. esphome/components/scd4x/scd4x.h +17 -15
  395. esphome/components/scd4x/sensor.py +4 -4
  396. esphome/components/script/script.h +0 -2
  397. esphome/components/sdp3x/sensor.py +1 -1
  398. esphome/components/select/__init__.py +5 -2
  399. esphome/components/sen5x/sen5x.h +0 -1
  400. esphome/components/senseair/senseair.h +0 -1
  401. esphome/components/sensor/__init__.py +4 -2
  402. esphome/components/sensor/filter.cpp +1 -1
  403. esphome/components/sensor/sensor.cpp +12 -6
  404. esphome/components/sensor/sensor.h +13 -5
  405. esphome/components/servo/servo.h +0 -1
  406. esphome/components/sfa30/sfa30.h +0 -1
  407. esphome/components/sgp30/sgp30.h +0 -1
  408. esphome/components/sgp4x/sgp4x.h +0 -1
  409. esphome/components/shelly_dimmer/stm32flash.cpp +1 -2
  410. esphome/components/sht4x/sht4x.h +0 -1
  411. esphome/components/sm300d2/sm300d2.h +0 -2
  412. esphome/components/smt100/sensor.py +8 -4
  413. esphome/components/smt100/smt100.cpp +5 -5
  414. esphome/components/smt100/smt100.h +3 -3
  415. esphome/components/sn74hc595/__init__.py +1 -1
  416. esphome/components/sn74hc595/sn74hc595.cpp +5 -4
  417. esphome/components/sntp/sntp_component.cpp +9 -3
  418. esphome/components/sntp/time.py +2 -0
  419. esphome/components/socket/__init__.py +17 -0
  420. esphome/components/spi/__init__.py +27 -6
  421. esphome/components/spi/spi.cpp +3 -2
  422. esphome/components/spi/spi.h +9 -3
  423. esphome/components/spi/spi_arduino.cpp +3 -5
  424. esphome/components/spi/spi_esp_idf.cpp +40 -21
  425. esphome/components/spi_led_strip/spi_led_strip.cpp +1 -1
  426. esphome/components/sps30/sps30.h +0 -1
  427. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  428. esphome/components/st7701s/st7701s.cpp +0 -4
  429. esphome/components/status/status_binary_sensor.h +0 -2
  430. esphome/components/substitutions/__init__.py +76 -19
  431. esphome/components/substitutions/jinja.py +99 -0
  432. esphome/components/sun/sun.cpp +3 -4
  433. esphome/components/switch/__init__.py +5 -2
  434. esphome/components/switch/binary_sensor/switch_binary_sensor.h +0 -1
  435. esphome/components/sx126x/__init__.py +317 -0
  436. esphome/components/sx126x/automation.h +62 -0
  437. esphome/components/sx126x/packet_transport/__init__.py +26 -0
  438. esphome/components/sx126x/packet_transport/sx126x_transport.cpp +26 -0
  439. esphome/components/sx126x/packet_transport/sx126x_transport.h +25 -0
  440. esphome/components/sx126x/sx126x.cpp +523 -0
  441. esphome/components/sx126x/sx126x.h +140 -0
  442. esphome/components/sx126x/sx126x_reg.h +163 -0
  443. esphome/components/sx127x/__init__.py +325 -0
  444. esphome/components/sx127x/automation.h +62 -0
  445. esphome/components/sx127x/packet_transport/__init__.py +26 -0
  446. esphome/components/sx127x/packet_transport/sx127x_transport.cpp +26 -0
  447. esphome/components/sx127x/packet_transport/sx127x_transport.h +25 -0
  448. esphome/components/sx127x/sx127x.cpp +498 -0
  449. esphome/components/sx127x/sx127x.h +128 -0
  450. esphome/components/sx127x/sx127x_reg.h +295 -0
  451. esphome/components/syslog/esphome_syslog.cpp +5 -3
  452. esphome/components/syslog/esphome_syslog.h +1 -1
  453. esphome/components/tca9555/__init__.py +1 -1
  454. esphome/components/template/binary_sensor/template_binary_sensor.cpp +1 -9
  455. esphome/components/text/__init__.py +5 -2
  456. esphome/components/text_sensor/__init__.py +5 -2
  457. esphome/components/thermostat/thermostat_climate.cpp +34 -31
  458. esphome/components/thermostat/thermostat_climate.h +43 -39
  459. esphome/components/time/__init__.py +16 -2
  460. esphome/components/time/real_time_clock.cpp +4 -0
  461. esphome/components/time/real_time_clock.h +5 -1
  462. esphome/components/tlc5971/tlc5971.cpp +4 -1
  463. esphome/components/tmp1075/tmp1075.h +0 -2
  464. esphome/components/tof10120/tof10120_sensor.h +0 -1
  465. esphome/components/tormatic/tormatic_cover.h +0 -1
  466. esphome/components/total_daily_energy/total_daily_energy.h +0 -1
  467. esphome/components/tsl2591/tsl2591.cpp +1 -1
  468. esphome/components/ttp229_bsf/ttp229_bsf.h +0 -1
  469. esphome/components/ttp229_lsf/ttp229_lsf.h +0 -1
  470. esphome/components/tx20/tx20.cpp +2 -2
  471. esphome/components/uart/__init__.py +18 -0
  472. esphome/components/uart/uart_component_esp_idf.cpp +1 -5
  473. esphome/components/update/__init__.py +5 -2
  474. esphome/components/update/update_entity.h +8 -0
  475. esphome/components/usb_host/__init__.py +5 -2
  476. esphome/components/valve/__init__.py +5 -2
  477. esphome/components/vbus/vbus.h +0 -1
  478. esphome/components/veml3235/veml3235.h +0 -1
  479. esphome/components/veml7700/veml7700.h +0 -1
  480. esphome/components/vl53l0x/vl53l0x_sensor.h +0 -1
  481. esphome/components/voice_assistant/voice_assistant.cpp +4 -4
  482. esphome/components/watchdog/watchdog.cpp +0 -4
  483. esphome/components/waveshare_epaper/waveshare_epaper.cpp +6 -6
  484. esphome/components/web_server/__init__.py +34 -19
  485. esphome/components/web_server/ota/__init__.py +32 -0
  486. esphome/components/web_server/ota/ota_web_server.cpp +210 -0
  487. esphome/components/web_server/ota/ota_web_server.h +26 -0
  488. esphome/components/web_server/web_server.cpp +311 -430
  489. esphome/components/web_server/web_server.h +33 -23
  490. esphome/components/web_server/web_server_v1.cpp +4 -5
  491. esphome/components/web_server_base/__init__.py +5 -2
  492. esphome/components/web_server_base/web_server_base.cpp +2 -94
  493. esphome/components/web_server_base/web_server_base.h +5 -25
  494. esphome/components/web_server_idf/multipart.cpp +254 -0
  495. esphome/components/web_server_idf/multipart.h +86 -0
  496. esphome/components/web_server_idf/utils.cpp +32 -0
  497. esphome/components/web_server_idf/utils.h +10 -0
  498. esphome/components/web_server_idf/web_server_idf.cpp +162 -16
  499. esphome/components/web_server_idf/web_server_idf.h +11 -10
  500. esphome/components/wiegand/wiegand.cpp +2 -2
  501. esphome/components/wifi/__init__.py +18 -0
  502. esphome/components/wifi/wifi_component.cpp +17 -22
  503. esphome/components/wifi/wifi_component.h +27 -23
  504. esphome/components/wifi/wifi_component_esp32_arduino.cpp +52 -59
  505. esphome/components/wifi/wifi_component_esp8266.cpp +46 -46
  506. esphome/components/wifi/wifi_component_esp_idf.cpp +35 -36
  507. esphome/components/wifi/wifi_component_libretiny.cpp +26 -27
  508. esphome/components/wifi/wifi_component_pico_w.cpp +3 -3
  509. esphome/components/wifi_info/wifi_info_text_sensor.cpp +6 -6
  510. esphome/components/wireguard/__init__.py +2 -11
  511. esphome/components/xiaomi_ble/xiaomi_ble.cpp +13 -1
  512. esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
  513. esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +0 -1
  514. esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +0 -1
  515. esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +0 -1
  516. esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +0 -1
  517. esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +0 -1
  518. esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +0 -1
  519. esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h +0 -1
  520. esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +0 -1
  521. esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +0 -1
  522. esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +0 -1
  523. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +0 -1
  524. esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +0 -1
  525. esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +0 -1
  526. esphome/components/xiaomi_mhoc303/xiaomi_mhoc303.h +0 -1
  527. esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +0 -1
  528. esphome/components/xiaomi_miscale/xiaomi_miscale.h +0 -1
  529. esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +0 -1
  530. esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +0 -1
  531. esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +0 -1
  532. esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +0 -1
  533. esphome/components/xiaomi_xmwsdj04mmc/__init__.py +0 -0
  534. esphome/components/xiaomi_xmwsdj04mmc/sensor.py +77 -0
  535. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +77 -0
  536. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +36 -0
  537. esphome/components/zio_ultrasonic/zio_ultrasonic.h +0 -2
  538. esphome/components/zyaura/zyaura.h +0 -1
  539. esphome/config.py +88 -22
  540. esphome/config_helpers.py +74 -1
  541. esphome/config_validation.py +12 -1
  542. esphome/const.py +65 -10
  543. esphome/core/__init__.py +18 -2
  544. esphome/core/application.cpp +163 -10
  545. esphome/core/application.h +145 -165
  546. esphome/core/area.h +19 -0
  547. esphome/core/automation.h +58 -9
  548. esphome/core/color.cpp +3 -5
  549. esphome/core/color.h +16 -16
  550. esphome/core/component.cpp +151 -18
  551. esphome/core/component.h +98 -4
  552. esphome/core/component_iterator.cpp +7 -7
  553. esphome/core/component_iterator.h +9 -7
  554. esphome/core/config.py +155 -6
  555. esphome/core/controller.cpp +4 -2
  556. esphome/core/controller.h +1 -1
  557. esphome/core/datatypes.h +2 -2
  558. esphome/core/defines.h +17 -2
  559. esphome/core/device.h +20 -0
  560. esphome/core/entity_base.cpp +20 -15
  561. esphome/core/entity_base.h +76 -0
  562. esphome/core/entity_helpers.py +162 -1
  563. esphome/core/event_pool.h +81 -0
  564. esphome/core/helpers.cpp +75 -230
  565. esphome/core/helpers.h +164 -104
  566. esphome/core/lock_free_queue.h +151 -0
  567. esphome/core/log.cpp +2 -2
  568. esphome/core/log.h +2 -0
  569. esphome/core/optional.h +5 -0
  570. esphome/core/ring_buffer.cpp +2 -2
  571. esphome/core/scheduler.cpp +278 -103
  572. esphome/core/scheduler.h +157 -17
  573. esphome/core/time.cpp +5 -5
  574. esphome/core/time.h +5 -5
  575. esphome/cpp_generator.py +17 -0
  576. esphome/cpp_helpers.py +0 -22
  577. esphome/cpp_types.py +3 -1
  578. esphome/dashboard/entries.py +1 -1
  579. esphome/dashboard/util/text.py +5 -21
  580. esphome/dashboard/web_server.py +9 -1
  581. esphome/helpers.py +47 -0
  582. esphome/loader.py +15 -1
  583. esphome/pins.py +14 -8
  584. esphome/wizard.py +16 -3
  585. esphome/writer.py +21 -3
  586. esphome/yaml_util.py +0 -2
  587. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/METADATA +10 -9
  588. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/RECORD +593 -533
  589. esphome/components/esp32_ble/ble_event_pool.h +0 -72
  590. esphome/components/esp32_ble/queue.h +0 -85
  591. esphome/components/esp32_hall/esp32_hall.cpp +0 -25
  592. esphome/components/esp32_hall/esp32_hall.h +0 -23
  593. esphome/components/esp32_touch/esp32_touch.cpp +0 -355
  594. esphome/components/ld2410/button/reset_button.cpp +0 -9
  595. esphome/components/ld2450/button/reset_button.cpp +0 -9
  596. esphome/components/openthread/tlv.py +0 -65
  597. /esphome/{dashboard/enum.py → enum.py} +0 -0
  598. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/WHEEL +0 -0
  599. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/entry_points.txt +0 -0
  600. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/licenses/LICENSE +0 -0
  601. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/top_level.txt +0 -0
@@ -28,8 +28,32 @@
28
28
  namespace esphome {
29
29
  namespace api {
30
30
 
31
+ // Read a maximum of 5 messages per loop iteration to prevent starving other components.
32
+ // This is a balance between API responsiveness and allowing other components to run.
33
+ // Since each message could contain multiple protobuf messages when using packet batching,
34
+ // this limits the number of messages processed, not the number of TCP packets.
35
+ static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 5;
36
+ static constexpr uint8_t MAX_PING_RETRIES = 60;
37
+ static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
38
+ static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
39
+
31
40
  static const char *const TAG = "api.connection";
32
- static const int ESP32_CAMERA_STOP_STREAM = 5000;
41
+ #ifdef USE_CAMERA
42
+ static const int CAMERA_STOP_STREAM = 5000;
43
+ #endif
44
+
45
+ // Helper macro for entity command handlers - gets entity by key, returns if not found, and creates call object
46
+ #define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
47
+ entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
48
+ if ((entity_var) == nullptr) \
49
+ return; \
50
+ auto call = (entity_var)->make_call();
51
+
52
+ // Helper macro for entity command handlers that don't use make_call() - gets entity by key and returns if not found
53
+ #define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
54
+ entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
55
+ if ((entity_var) == nullptr) \
56
+ return;
33
57
 
34
58
  APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
35
59
  : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
@@ -47,6 +71,11 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
47
71
  #else
48
72
  #error "No frame helper defined"
49
73
  #endif
74
+ #ifdef USE_CAMERA
75
+ if (camera::Camera::instance() != nullptr) {
76
+ this->image_reader_ = std::unique_ptr<camera::CameraImageReader>{camera::Camera::instance()->create_image_reader()};
77
+ }
78
+ #endif
50
79
  }
51
80
 
52
81
  uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
@@ -54,15 +83,11 @@ uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_
54
83
  void APIConnection::start() {
55
84
  this->last_traffic_ = App.get_loop_component_start_time();
56
85
 
57
- // Set next_ping_retry_ to prevent immediate ping
58
- // This ensures the first ping happens after the keepalive period
59
- this->next_ping_retry_ = this->last_traffic_ + KEEPALIVE_TIMEOUT_MS;
60
-
61
86
  APIError err = this->helper_->init();
62
87
  if (err != APIError::OK) {
63
88
  on_fatal_error();
64
- ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
65
- errno);
89
+ ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->get_client_combined_info().c_str(),
90
+ api_error_to_str(err), errno);
66
91
  return;
67
92
  }
68
93
  this->client_info_ = helper_->getpeername();
@@ -84,104 +109,99 @@ APIConnection::~APIConnection() {
84
109
  }
85
110
 
86
111
  void APIConnection::loop() {
87
- if (this->remove_)
88
- return;
89
-
90
- if (!network::is_connected()) {
91
- // when network is disconnected force disconnect immediately
92
- // don't wait for timeout
93
- this->on_fatal_error();
94
- ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->client_combined_info_.c_str());
95
- return;
96
- }
97
- if (this->next_close_) {
112
+ if (this->flags_.next_close) {
98
113
  // requested a disconnect
99
114
  this->helper_->close();
100
- this->remove_ = true;
115
+ this->flags_.remove = true;
101
116
  return;
102
117
  }
103
118
 
104
119
  APIError err = this->helper_->loop();
105
120
  if (err != APIError::OK) {
106
121
  on_fatal_error();
107
- ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
122
+ ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
108
123
  api_error_to_str(err), errno);
109
124
  return;
110
125
  }
111
126
 
127
+ const uint32_t now = App.get_loop_component_start_time();
112
128
  // Check if socket has data ready before attempting to read
113
129
  if (this->helper_->is_socket_ready()) {
114
- ReadPacketBuffer buffer;
115
- err = this->helper_->read_packet(&buffer);
116
- if (err == APIError::WOULD_BLOCK) {
117
- // pass
118
- } else if (err != APIError::OK) {
119
- on_fatal_error();
120
- if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
121
- ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
122
- } else if (err == APIError::CONNECTION_CLOSED) {
123
- ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
124
- } else {
125
- ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
126
- errno);
127
- }
128
- return;
129
- } else {
130
- this->last_traffic_ = App.get_loop_component_start_time();
131
- // read a packet
132
- if (buffer.data_len > 0) {
133
- this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
130
+ // Read up to MAX_MESSAGES_PER_LOOP messages per loop to improve throughput
131
+ for (uint8_t message_count = 0; message_count < MAX_MESSAGES_PER_LOOP; message_count++) {
132
+ ReadPacketBuffer buffer;
133
+ err = this->helper_->read_packet(&buffer);
134
+ if (err == APIError::WOULD_BLOCK) {
135
+ // No more data available
136
+ break;
137
+ } else if (err != APIError::OK) {
138
+ on_fatal_error();
139
+ if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
140
+ ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
141
+ } else if (err == APIError::CONNECTION_CLOSED) {
142
+ ESP_LOGW(TAG, "%s: Connection closed", this->get_client_combined_info().c_str());
143
+ } else {
144
+ ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->get_client_combined_info().c_str(),
145
+ api_error_to_str(err), errno);
146
+ }
147
+ return;
134
148
  } else {
135
- this->read_message(0, buffer.type, nullptr);
149
+ this->last_traffic_ = now;
150
+ // read a packet
151
+ if (buffer.data_len > 0) {
152
+ this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
153
+ } else {
154
+ this->read_message(0, buffer.type, nullptr);
155
+ }
156
+ if (this->flags_.remove)
157
+ return;
136
158
  }
137
- if (this->remove_)
138
- return;
139
159
  }
140
160
  }
141
161
 
142
- // Process deferred batch if scheduled
143
- if (this->deferred_batch_.batch_scheduled &&
144
- App.get_loop_component_start_time() - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
162
+ // Process deferred batch if scheduled and timer has expired
163
+ if (this->flags_.batch_scheduled && now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
145
164
  this->process_batch_();
146
165
  }
147
166
 
148
- if (!this->list_entities_iterator_.completed())
149
- this->list_entities_iterator_.advance();
150
- if (!this->initial_state_iterator_.completed() && this->list_entities_iterator_.completed())
151
- this->initial_state_iterator_.advance();
167
+ if (!this->list_entities_iterator_.completed()) {
168
+ this->process_iterator_batch_(this->list_entities_iterator_);
169
+ } else if (!this->initial_state_iterator_.completed()) {
170
+ this->process_iterator_batch_(this->initial_state_iterator_);
152
171
 
153
- static uint8_t max_ping_retries = 60;
154
- static uint16_t ping_retry_interval = 1000;
155
- const uint32_t now = App.get_loop_component_start_time();
156
- if (this->sent_ping_) {
172
+ // If we've completed initial states, process any remaining and clear the flag
173
+ if (this->initial_state_iterator_.completed()) {
174
+ // Process any remaining batched messages immediately
175
+ if (!this->deferred_batch_.empty()) {
176
+ this->process_batch_();
177
+ }
178
+ // Now that everything is sent, enable immediate sending for future state changes
179
+ this->flags_.should_try_send_immediately = true;
180
+ }
181
+ }
182
+
183
+ if (this->flags_.sent_ping) {
157
184
  // Disconnect if not responded within 2.5*keepalive
158
- if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
185
+ if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
159
186
  on_fatal_error();
160
- ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->client_combined_info_.c_str());
187
+ ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
161
188
  }
162
- } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
189
+ } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
163
190
  ESP_LOGVV(TAG, "Sending keepalive PING");
164
- this->sent_ping_ = this->send_message(PingRequest());
165
- if (!this->sent_ping_) {
166
- this->next_ping_retry_ = now + ping_retry_interval;
167
- this->ping_retries_++;
168
- std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);",
169
- this->client_combined_info_.c_str(), this->ping_retries_);
170
- if (this->ping_retries_ >= max_ping_retries) {
171
- on_fatal_error();
172
- ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str());
173
- } else if (this->ping_retries_ >= 10) {
174
- ESP_LOGW(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
175
- } else {
176
- ESP_LOGD(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
177
- }
191
+ this->flags_.sent_ping = this->send_message(PingRequest());
192
+ if (!this->flags_.sent_ping) {
193
+ // If we can't send the ping request directly (tx_buffer full),
194
+ // schedule it at the front of the batch so it will be sent with priority
195
+ ESP_LOGW(TAG, "Buffer full, ping queued");
196
+ this->schedule_message_front_(nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE);
197
+ this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings
178
198
  }
179
199
  }
180
200
 
181
- #ifdef USE_ESP32_CAMERA
182
- if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
183
- uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_.available());
184
- bool done = this->image_reader_.available() == to_send;
201
+ #ifdef USE_CAMERA
202
+ if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
203
+ uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_->available());
204
+ bool done = this->image_reader_->available() == to_send;
185
205
  uint32_t msg_size = 0;
186
206
  ProtoSize::add_fixed_field<4>(msg_size, 1, true);
187
207
  // partial message size calculated manually since its a special case
@@ -191,28 +211,26 @@ void APIConnection::loop() {
191
211
 
192
212
  auto buffer = this->create_buffer(msg_size);
193
213
  // fixed32 key = 1;
194
- buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
214
+ buffer.encode_fixed32(1, camera::Camera::instance()->get_object_id_hash());
195
215
  // bytes data = 2;
196
- buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
216
+ buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send);
197
217
  // bool done = 3;
198
218
  buffer.encode_bool(3, done);
199
219
 
200
- bool success = this->send_buffer(buffer, 44);
220
+ bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
201
221
 
202
222
  if (success) {
203
- this->image_reader_.consume_data(to_send);
204
- }
205
- if (success && done) {
206
- this->image_reader_.return_image();
223
+ this->image_reader_->consume_data(to_send);
224
+ if (done) {
225
+ this->image_reader_->return_image();
226
+ }
207
227
  }
208
228
  }
209
229
  #endif
210
230
 
211
- if (state_subs_at_ != -1) {
231
+ if (state_subs_at_ >= 0) {
212
232
  const auto &subs = this->parent_->get_state_subs();
213
- if (state_subs_at_ >= (int) subs.size()) {
214
- state_subs_at_ = -1;
215
- } else {
233
+ if (state_subs_at_ < static_cast<int>(subs.size())) {
216
234
  auto &it = subs[state_subs_at_];
217
235
  SubscribeHomeAssistantStateResponse resp;
218
236
  resp.entity_id = it.entity_id;
@@ -221,6 +239,8 @@ void APIConnection::loop() {
221
239
  if (this->send_message(resp)) {
222
240
  state_subs_at_++;
223
241
  }
242
+ } else {
243
+ state_subs_at_ = -1;
224
244
  }
225
245
  }
226
246
  }
@@ -233,20 +253,28 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
233
253
  // remote initiated disconnect_client
234
254
  // don't close yet, we still need to send the disconnect response
235
255
  // close will happen on next loop
236
- ESP_LOGD(TAG, "%s disconnected", this->client_combined_info_.c_str());
237
- this->next_close_ = true;
256
+ ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
257
+ this->flags_.next_close = true;
238
258
  DisconnectResponse resp;
239
259
  return resp;
240
260
  }
241
261
  void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
242
262
  this->helper_->close();
243
- this->remove_ = true;
263
+ this->flags_.remove = true;
244
264
  }
245
265
 
246
266
  // Encodes a message to the buffer and returns the total number of bytes used,
247
267
  // including header and footer overhead. Returns 0 if the message doesn't fit.
248
268
  uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
249
269
  uint32_t remaining_size, bool is_single) {
270
+ #ifdef HAS_PROTO_MESSAGE_DUMP
271
+ // If in log-only mode, just log and return
272
+ if (conn->flags_.log_only_mode) {
273
+ conn->log_send_message_(msg.message_name(), msg.dump());
274
+ return 1; // Return non-zero to indicate "success" for logging
275
+ }
276
+ #endif
277
+
250
278
  // Calculate size
251
279
  uint32_t calculated_size = 0;
252
280
  msg.calculate_size(calculated_size);
@@ -287,12 +315,8 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t mes
287
315
 
288
316
  #ifdef USE_BINARY_SENSOR
289
317
  bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) {
290
- return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
291
- BinarySensorStateResponse::MESSAGE_TYPE);
292
- }
293
- void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
294
- this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info,
295
- ListEntitiesBinarySensorResponse::MESSAGE_TYPE);
318
+ return this->send_message_smart_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
319
+ BinarySensorStateResponse::MESSAGE_TYPE);
296
320
  }
297
321
 
298
322
  uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -319,10 +343,7 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne
319
343
 
320
344
  #ifdef USE_COVER
321
345
  bool APIConnection::send_cover_state(cover::Cover *cover) {
322
- return this->schedule_message_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE);
323
- }
324
- void APIConnection::send_cover_info(cover::Cover *cover) {
325
- this->schedule_message_(cover, &APIConnection::try_send_cover_info, ListEntitiesCoverResponse::MESSAGE_TYPE);
346
+ return this->send_message_smart_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE);
326
347
  }
327
348
  uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
328
349
  bool is_single) {
@@ -353,11 +374,7 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
353
374
  return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
354
375
  }
355
376
  void APIConnection::cover_command(const CoverCommandRequest &msg) {
356
- cover::Cover *cover = App.get_cover_by_key(msg.key);
357
- if (cover == nullptr)
358
- return;
359
-
360
- auto call = cover->make_call();
377
+ ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
361
378
  if (msg.has_legacy_command) {
362
379
  switch (msg.legacy_command) {
363
380
  case enums::LEGACY_COVER_COMMAND_OPEN:
@@ -383,10 +400,7 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
383
400
 
384
401
  #ifdef USE_FAN
385
402
  bool APIConnection::send_fan_state(fan::Fan *fan) {
386
- return this->schedule_message_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE);
387
- }
388
- void APIConnection::send_fan_info(fan::Fan *fan) {
389
- this->schedule_message_(fan, &APIConnection::try_send_fan_info, ListEntitiesFanResponse::MESSAGE_TYPE);
403
+ return this->send_message_smart_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE);
390
404
  }
391
405
  uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
392
406
  bool is_single) {
@@ -422,11 +436,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
422
436
  return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
423
437
  }
424
438
  void APIConnection::fan_command(const FanCommandRequest &msg) {
425
- fan::Fan *fan = App.get_fan_by_key(msg.key);
426
- if (fan == nullptr)
427
- return;
428
-
429
- auto call = fan->make_call();
439
+ ENTITY_COMMAND_MAKE_CALL(fan::Fan, fan, fan)
430
440
  if (msg.has_state)
431
441
  call.set_state(msg.state);
432
442
  if (msg.has_oscillating)
@@ -445,10 +455,7 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
445
455
 
446
456
  #ifdef USE_LIGHT
447
457
  bool APIConnection::send_light_state(light::LightState *light) {
448
- return this->schedule_message_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE);
449
- }
450
- void APIConnection::send_light_info(light::LightState *light) {
451
- this->schedule_message_(light, &APIConnection::try_send_light_info, ListEntitiesLightResponse::MESSAGE_TYPE);
458
+ return this->send_message_smart_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE);
452
459
  }
453
460
  uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
454
461
  bool is_single) {
@@ -502,11 +509,7 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
502
509
  return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
503
510
  }
504
511
  void APIConnection::light_command(const LightCommandRequest &msg) {
505
- light::LightState *light = App.get_light_by_key(msg.key);
506
- if (light == nullptr)
507
- return;
508
-
509
- auto call = light->make_call();
512
+ ENTITY_COMMAND_MAKE_CALL(light::LightState, light, light)
510
513
  if (msg.has_state)
511
514
  call.set_state(msg.state);
512
515
  if (msg.has_brightness)
@@ -540,10 +543,7 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
540
543
 
541
544
  #ifdef USE_SENSOR
542
545
  bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
543
- return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE);
544
- }
545
- void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
546
- this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE);
546
+ return this->send_message_smart_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE);
547
547
  }
548
548
 
549
549
  uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -575,10 +575,7 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *
575
575
 
576
576
  #ifdef USE_SWITCH
577
577
  bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
578
- return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE);
579
- }
580
- void APIConnection::send_switch_info(switch_::Switch *a_switch) {
581
- this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE);
578
+ return this->send_message_smart_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE);
582
579
  }
583
580
 
584
581
  uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -601,9 +598,7 @@ uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *
601
598
  return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
602
599
  }
603
600
  void APIConnection::switch_command(const SwitchCommandRequest &msg) {
604
- switch_::Switch *a_switch = App.get_switch_by_key(msg.key);
605
- if (a_switch == nullptr)
606
- return;
601
+ ENTITY_COMMAND_GET(switch_::Switch, a_switch, switch)
607
602
 
608
603
  if (msg.state) {
609
604
  a_switch->turn_on();
@@ -615,12 +610,8 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
615
610
 
616
611
  #ifdef USE_TEXT_SENSOR
617
612
  bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) {
618
- return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state,
619
- TextSensorStateResponse::MESSAGE_TYPE);
620
- }
621
- void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
622
- this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info,
623
- ListEntitiesTextSensorResponse::MESSAGE_TYPE);
613
+ return this->send_message_smart_(text_sensor, &APIConnection::try_send_text_sensor_state,
614
+ TextSensorStateResponse::MESSAGE_TYPE);
624
615
  }
625
616
 
626
617
  uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -647,7 +638,7 @@ uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnect
647
638
 
648
639
  #ifdef USE_CLIMATE
649
640
  bool APIConnection::send_climate_state(climate::Climate *climate) {
650
- return this->schedule_message_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE);
641
+ return this->send_message_smart_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE);
651
642
  }
652
643
  uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
653
644
  bool is_single) {
@@ -682,9 +673,6 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
682
673
  resp.target_humidity = climate->target_humidity;
683
674
  return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
684
675
  }
685
- void APIConnection::send_climate_info(climate::Climate *climate) {
686
- this->schedule_message_(climate, &APIConnection::try_send_climate_info, ListEntitiesClimateResponse::MESSAGE_TYPE);
687
- }
688
676
  uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
689
677
  bool is_single) {
690
678
  auto *climate = static_cast<climate::Climate *>(entity);
@@ -719,11 +707,7 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
719
707
  return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
720
708
  }
721
709
  void APIConnection::climate_command(const ClimateCommandRequest &msg) {
722
- climate::Climate *climate = App.get_climate_by_key(msg.key);
723
- if (climate == nullptr)
724
- return;
725
-
726
- auto call = climate->make_call();
710
+ ENTITY_COMMAND_MAKE_CALL(climate::Climate, climate, climate)
727
711
  if (msg.has_mode)
728
712
  call.set_mode(static_cast<climate::ClimateMode>(msg.mode));
729
713
  if (msg.has_target_temperature)
@@ -750,10 +734,7 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
750
734
 
751
735
  #ifdef USE_NUMBER
752
736
  bool APIConnection::send_number_state(number::Number *number) {
753
- return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE);
754
- }
755
- void APIConnection::send_number_info(number::Number *number) {
756
- this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE);
737
+ return this->send_message_smart_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE);
757
738
  }
758
739
 
759
740
  uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -781,11 +762,7 @@ uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *
781
762
  return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
782
763
  }
783
764
  void APIConnection::number_command(const NumberCommandRequest &msg) {
784
- number::Number *number = App.get_number_by_key(msg.key);
785
- if (number == nullptr)
786
- return;
787
-
788
- auto call = number->make_call();
765
+ ENTITY_COMMAND_MAKE_CALL(number::Number, number, number)
789
766
  call.set_value(msg.state);
790
767
  call.perform();
791
768
  }
@@ -793,7 +770,7 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
793
770
 
794
771
  #ifdef USE_DATETIME_DATE
795
772
  bool APIConnection::send_date_state(datetime::DateEntity *date) {
796
- return this->schedule_message_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE);
773
+ return this->send_message_smart_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE);
797
774
  }
798
775
  uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
799
776
  bool is_single) {
@@ -806,9 +783,6 @@ uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *c
806
783
  fill_entity_state_base(date, resp);
807
784
  return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
808
785
  }
809
- void APIConnection::send_date_info(datetime::DateEntity *date) {
810
- this->schedule_message_(date, &APIConnection::try_send_date_info, ListEntitiesDateResponse::MESSAGE_TYPE);
811
- }
812
786
  uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
813
787
  bool is_single) {
814
788
  auto *date = static_cast<datetime::DateEntity *>(entity);
@@ -818,11 +792,7 @@ uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *co
818
792
  return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
819
793
  }
820
794
  void APIConnection::date_command(const DateCommandRequest &msg) {
821
- datetime::DateEntity *date = App.get_date_by_key(msg.key);
822
- if (date == nullptr)
823
- return;
824
-
825
- auto call = date->make_call();
795
+ ENTITY_COMMAND_MAKE_CALL(datetime::DateEntity, date, date)
826
796
  call.set_date(msg.year, msg.month, msg.day);
827
797
  call.perform();
828
798
  }
@@ -830,7 +800,7 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
830
800
 
831
801
  #ifdef USE_DATETIME_TIME
832
802
  bool APIConnection::send_time_state(datetime::TimeEntity *time) {
833
- return this->schedule_message_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE);
803
+ return this->send_message_smart_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE);
834
804
  }
835
805
  uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
836
806
  bool is_single) {
@@ -843,9 +813,6 @@ uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *c
843
813
  fill_entity_state_base(time, resp);
844
814
  return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
845
815
  }
846
- void APIConnection::send_time_info(datetime::TimeEntity *time) {
847
- this->schedule_message_(time, &APIConnection::try_send_time_info, ListEntitiesTimeResponse::MESSAGE_TYPE);
848
- }
849
816
  uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
850
817
  bool is_single) {
851
818
  auto *time = static_cast<datetime::TimeEntity *>(entity);
@@ -855,11 +822,7 @@ uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *co
855
822
  return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
856
823
  }
857
824
  void APIConnection::time_command(const TimeCommandRequest &msg) {
858
- datetime::TimeEntity *time = App.get_time_by_key(msg.key);
859
- if (time == nullptr)
860
- return;
861
-
862
- auto call = time->make_call();
825
+ ENTITY_COMMAND_MAKE_CALL(datetime::TimeEntity, time, time)
863
826
  call.set_time(msg.hour, msg.minute, msg.second);
864
827
  call.perform();
865
828
  }
@@ -867,8 +830,8 @@ void APIConnection::time_command(const TimeCommandRequest &msg) {
867
830
 
868
831
  #ifdef USE_DATETIME_DATETIME
869
832
  bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
870
- return this->schedule_message_(datetime, &APIConnection::try_send_datetime_state,
871
- DateTimeStateResponse::MESSAGE_TYPE);
833
+ return this->send_message_smart_(datetime, &APIConnection::try_send_datetime_state,
834
+ DateTimeStateResponse::MESSAGE_TYPE);
872
835
  }
873
836
  uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
874
837
  bool is_single) {
@@ -882,9 +845,6 @@ uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnectio
882
845
  fill_entity_state_base(datetime, resp);
883
846
  return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
884
847
  }
885
- void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
886
- this->schedule_message_(datetime, &APIConnection::try_send_datetime_info, ListEntitiesDateTimeResponse::MESSAGE_TYPE);
887
- }
888
848
  uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
889
849
  bool is_single) {
890
850
  auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
@@ -894,11 +854,7 @@ uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection
894
854
  return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
895
855
  }
896
856
  void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
897
- datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key);
898
- if (datetime == nullptr)
899
- return;
900
-
901
- auto call = datetime->make_call();
857
+ ENTITY_COMMAND_MAKE_CALL(datetime::DateTimeEntity, datetime, datetime)
902
858
  call.set_datetime(msg.epoch_seconds);
903
859
  call.perform();
904
860
  }
@@ -906,10 +862,7 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
906
862
 
907
863
  #ifdef USE_TEXT
908
864
  bool APIConnection::send_text_state(text::Text *text) {
909
- return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE);
910
- }
911
- void APIConnection::send_text_info(text::Text *text) {
912
- this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE);
865
+ return this->send_message_smart_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE);
913
866
  }
914
867
 
915
868
  uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -935,11 +888,7 @@ uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *co
935
888
  return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
936
889
  }
937
890
  void APIConnection::text_command(const TextCommandRequest &msg) {
938
- text::Text *text = App.get_text_by_key(msg.key);
939
- if (text == nullptr)
940
- return;
941
-
942
- auto call = text->make_call();
891
+ ENTITY_COMMAND_MAKE_CALL(text::Text, text, text)
943
892
  call.set_value(msg.state);
944
893
  call.perform();
945
894
  }
@@ -947,10 +896,7 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
947
896
 
948
897
  #ifdef USE_SELECT
949
898
  bool APIConnection::send_select_state(select::Select *select) {
950
- return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE);
951
- }
952
- void APIConnection::send_select_info(select::Select *select) {
953
- this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE);
899
+ return this->send_message_smart_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE);
954
900
  }
955
901
 
956
902
  uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -974,20 +920,13 @@ uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *
974
920
  return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
975
921
  }
976
922
  void APIConnection::select_command(const SelectCommandRequest &msg) {
977
- select::Select *select = App.get_select_by_key(msg.key);
978
- if (select == nullptr)
979
- return;
980
-
981
- auto call = select->make_call();
923
+ ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
982
924
  call.set_option(msg.state);
983
925
  call.perform();
984
926
  }
985
927
  #endif
986
928
 
987
929
  #ifdef USE_BUTTON
988
- void esphome::api::APIConnection::send_button_info(button::Button *button) {
989
- this->schedule_message_(button, &APIConnection::try_send_button_info, ListEntitiesButtonResponse::MESSAGE_TYPE);
990
- }
991
930
  uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
992
931
  bool is_single) {
993
932
  auto *button = static_cast<button::Button *>(entity);
@@ -998,20 +937,14 @@ uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *
998
937
  return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
999
938
  }
1000
939
  void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
1001
- button::Button *button = App.get_button_by_key(msg.key);
1002
- if (button == nullptr)
1003
- return;
1004
-
940
+ ENTITY_COMMAND_GET(button::Button, button, button)
1005
941
  button->press();
1006
942
  }
1007
943
  #endif
1008
944
 
1009
945
  #ifdef USE_LOCK
1010
946
  bool APIConnection::send_lock_state(lock::Lock *a_lock) {
1011
- return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE);
1012
- }
1013
- void APIConnection::send_lock_info(lock::Lock *a_lock) {
1014
- this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE);
947
+ return this->send_message_smart_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE);
1015
948
  }
1016
949
 
1017
950
  uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -1035,9 +968,7 @@ uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *co
1035
968
  return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1036
969
  }
1037
970
  void APIConnection::lock_command(const LockCommandRequest &msg) {
1038
- lock::Lock *a_lock = App.get_lock_by_key(msg.key);
1039
- if (a_lock == nullptr)
1040
- return;
971
+ ENTITY_COMMAND_GET(lock::Lock, a_lock, lock)
1041
972
 
1042
973
  switch (msg.command) {
1043
974
  case enums::LOCK_UNLOCK:
@@ -1055,7 +986,7 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
1055
986
 
1056
987
  #ifdef USE_VALVE
1057
988
  bool APIConnection::send_valve_state(valve::Valve *valve) {
1058
- return this->schedule_message_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE);
989
+ return this->send_message_smart_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE);
1059
990
  }
1060
991
  uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1061
992
  bool is_single) {
@@ -1066,9 +997,6 @@ uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *
1066
997
  fill_entity_state_base(valve, resp);
1067
998
  return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1068
999
  }
1069
- void APIConnection::send_valve_info(valve::Valve *valve) {
1070
- this->schedule_message_(valve, &APIConnection::try_send_valve_info, ListEntitiesValveResponse::MESSAGE_TYPE);
1071
- }
1072
1000
  uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1073
1001
  bool is_single) {
1074
1002
  auto *valve = static_cast<valve::Valve *>(entity);
@@ -1083,11 +1011,7 @@ uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *c
1083
1011
  return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1084
1012
  }
1085
1013
  void APIConnection::valve_command(const ValveCommandRequest &msg) {
1086
- valve::Valve *valve = App.get_valve_by_key(msg.key);
1087
- if (valve == nullptr)
1088
- return;
1089
-
1090
- auto call = valve->make_call();
1014
+ ENTITY_COMMAND_MAKE_CALL(valve::Valve, valve, valve)
1091
1015
  if (msg.has_position)
1092
1016
  call.set_position(msg.position);
1093
1017
  if (msg.stop)
@@ -1098,8 +1022,8 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) {
1098
1022
 
1099
1023
  #ifdef USE_MEDIA_PLAYER
1100
1024
  bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
1101
- return this->schedule_message_(media_player, &APIConnection::try_send_media_player_state,
1102
- MediaPlayerStateResponse::MESSAGE_TYPE);
1025
+ return this->send_message_smart_(media_player, &APIConnection::try_send_media_player_state,
1026
+ MediaPlayerStateResponse::MESSAGE_TYPE);
1103
1027
  }
1104
1028
  uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1105
1029
  bool is_single) {
@@ -1114,10 +1038,6 @@ uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConne
1114
1038
  fill_entity_state_base(media_player, resp);
1115
1039
  return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1116
1040
  }
1117
- void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
1118
- this->schedule_message_(media_player, &APIConnection::try_send_media_player_info,
1119
- ListEntitiesMediaPlayerResponse::MESSAGE_TYPE);
1120
- }
1121
1041
  uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1122
1042
  bool is_single) {
1123
1043
  auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
@@ -1138,11 +1058,7 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
1138
1058
  return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1139
1059
  }
1140
1060
  void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
1141
- media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
1142
- if (media_player == nullptr)
1143
- return;
1144
-
1145
- auto call = media_player->make_call();
1061
+ ENTITY_COMMAND_MAKE_CALL(media_player::MediaPlayer, media_player, media_player)
1146
1062
  if (msg.has_command) {
1147
1063
  call.set_command(static_cast<media_player::MediaPlayerCommand>(msg.command));
1148
1064
  }
@@ -1159,39 +1075,36 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
1159
1075
  }
1160
1076
  #endif
1161
1077
 
1162
- #ifdef USE_ESP32_CAMERA
1163
- void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
1164
- if (!this->state_subscription_)
1078
+ #ifdef USE_CAMERA
1079
+ void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) {
1080
+ if (!this->flags_.state_subscription)
1165
1081
  return;
1166
- if (this->image_reader_.available())
1082
+ if (!this->image_reader_)
1167
1083
  return;
1168
- if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
1169
- image->was_requested_by(esphome::esp32_camera::IDLE))
1170
- this->image_reader_.set_image(std::move(image));
1171
- }
1172
- void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
1173
- this->schedule_message_(camera, &APIConnection::try_send_camera_info, ListEntitiesCameraResponse::MESSAGE_TYPE);
1084
+ if (this->image_reader_->available())
1085
+ return;
1086
+ if (image->was_requested_by(esphome::camera::API_REQUESTER) || image->was_requested_by(esphome::camera::IDLE))
1087
+ this->image_reader_->set_image(std::move(image));
1174
1088
  }
1175
1089
  uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1176
1090
  bool is_single) {
1177
- auto *camera = static_cast<esp32_camera::ESP32Camera *>(entity);
1091
+ auto *camera = static_cast<camera::Camera *>(entity);
1178
1092
  ListEntitiesCameraResponse msg;
1179
1093
  msg.unique_id = get_default_unique_id("camera", camera);
1180
1094
  fill_entity_info_base(camera, msg);
1181
1095
  return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1182
1096
  }
1183
1097
  void APIConnection::camera_image(const CameraImageRequest &msg) {
1184
- if (esp32_camera::global_esp32_camera == nullptr)
1098
+ if (camera::Camera::instance() == nullptr)
1185
1099
  return;
1186
1100
 
1187
1101
  if (msg.single)
1188
- esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
1102
+ camera::Camera::instance()->request_image(esphome::camera::API_REQUESTER);
1189
1103
  if (msg.stream) {
1190
- esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::API_REQUESTER);
1104
+ camera::Camera::instance()->start_stream(esphome::camera::API_REQUESTER);
1191
1105
 
1192
- App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() {
1193
- esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER);
1194
- });
1106
+ App.scheduler.set_timeout(this->parent_, "api_camera_stop_stream", CAMERA_STOP_STREAM,
1107
+ []() { camera::Camera::instance()->stop_stream(esphome::camera::API_REQUESTER); });
1195
1108
  }
1196
1109
  }
1197
1110
  #endif
@@ -1263,66 +1176,53 @@ void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequ
1263
1176
  #endif
1264
1177
 
1265
1178
  #ifdef USE_VOICE_ASSISTANT
1179
+ bool APIConnection::check_voice_assistant_api_connection_() const {
1180
+ return voice_assistant::global_voice_assistant != nullptr &&
1181
+ voice_assistant::global_voice_assistant->get_api_connection() == this;
1182
+ }
1183
+
1266
1184
  void APIConnection::subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) {
1267
1185
  if (voice_assistant::global_voice_assistant != nullptr) {
1268
1186
  voice_assistant::global_voice_assistant->client_subscription(this, msg.subscribe);
1269
1187
  }
1270
1188
  }
1271
1189
  void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
1272
- if (voice_assistant::global_voice_assistant != nullptr) {
1273
- if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1274
- return;
1275
- }
1190
+ if (!this->check_voice_assistant_api_connection_()) {
1191
+ return;
1192
+ }
1276
1193
 
1277
- if (msg.error) {
1278
- voice_assistant::global_voice_assistant->failed_to_start();
1279
- return;
1280
- }
1281
- if (msg.port == 0) {
1282
- // Use API Audio
1283
- voice_assistant::global_voice_assistant->start_streaming();
1284
- } else {
1285
- struct sockaddr_storage storage;
1286
- socklen_t len = sizeof(storage);
1287
- this->helper_->getpeername((struct sockaddr *) &storage, &len);
1288
- voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
1289
- }
1194
+ if (msg.error) {
1195
+ voice_assistant::global_voice_assistant->failed_to_start();
1196
+ return;
1197
+ }
1198
+ if (msg.port == 0) {
1199
+ // Use API Audio
1200
+ voice_assistant::global_voice_assistant->start_streaming();
1201
+ } else {
1202
+ struct sockaddr_storage storage;
1203
+ socklen_t len = sizeof(storage);
1204
+ this->helper_->getpeername((struct sockaddr *) &storage, &len);
1205
+ voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
1290
1206
  }
1291
1207
  };
1292
1208
  void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
1293
- if (voice_assistant::global_voice_assistant != nullptr) {
1294
- if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1295
- return;
1296
- }
1297
-
1209
+ if (this->check_voice_assistant_api_connection_()) {
1298
1210
  voice_assistant::global_voice_assistant->on_event(msg);
1299
1211
  }
1300
1212
  }
1301
1213
  void APIConnection::on_voice_assistant_audio(const VoiceAssistantAudio &msg) {
1302
- if (voice_assistant::global_voice_assistant != nullptr) {
1303
- if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1304
- return;
1305
- }
1306
-
1214
+ if (this->check_voice_assistant_api_connection_()) {
1307
1215
  voice_assistant::global_voice_assistant->on_audio(msg);
1308
1216
  }
1309
1217
  };
1310
1218
  void APIConnection::on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) {
1311
- if (voice_assistant::global_voice_assistant != nullptr) {
1312
- if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1313
- return;
1314
- }
1315
-
1219
+ if (this->check_voice_assistant_api_connection_()) {
1316
1220
  voice_assistant::global_voice_assistant->on_timer_event(msg);
1317
1221
  }
1318
1222
  };
1319
1223
 
1320
1224
  void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) {
1321
- if (voice_assistant::global_voice_assistant != nullptr) {
1322
- if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1323
- return;
1324
- }
1325
-
1225
+ if (this->check_voice_assistant_api_connection_()) {
1326
1226
  voice_assistant::global_voice_assistant->on_announce(msg);
1327
1227
  }
1328
1228
  }
@@ -1330,35 +1230,29 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno
1330
1230
  VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration(
1331
1231
  const VoiceAssistantConfigurationRequest &msg) {
1332
1232
  VoiceAssistantConfigurationResponse resp;
1333
- if (voice_assistant::global_voice_assistant != nullptr) {
1334
- if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1335
- return resp;
1336
- }
1233
+ if (!this->check_voice_assistant_api_connection_()) {
1234
+ return resp;
1235
+ }
1337
1236
 
1338
- auto &config = voice_assistant::global_voice_assistant->get_configuration();
1339
- for (auto &wake_word : config.available_wake_words) {
1340
- VoiceAssistantWakeWord resp_wake_word;
1341
- resp_wake_word.id = wake_word.id;
1342
- resp_wake_word.wake_word = wake_word.wake_word;
1343
- for (const auto &lang : wake_word.trained_languages) {
1344
- resp_wake_word.trained_languages.push_back(lang);
1345
- }
1346
- resp.available_wake_words.push_back(std::move(resp_wake_word));
1347
- }
1348
- for (auto &wake_word_id : config.active_wake_words) {
1349
- resp.active_wake_words.push_back(wake_word_id);
1237
+ auto &config = voice_assistant::global_voice_assistant->get_configuration();
1238
+ for (auto &wake_word : config.available_wake_words) {
1239
+ VoiceAssistantWakeWord resp_wake_word;
1240
+ resp_wake_word.id = wake_word.id;
1241
+ resp_wake_word.wake_word = wake_word.wake_word;
1242
+ for (const auto &lang : wake_word.trained_languages) {
1243
+ resp_wake_word.trained_languages.push_back(lang);
1350
1244
  }
1351
- resp.max_active_wake_words = config.max_active_wake_words;
1245
+ resp.available_wake_words.push_back(std::move(resp_wake_word));
1352
1246
  }
1247
+ for (auto &wake_word_id : config.active_wake_words) {
1248
+ resp.active_wake_words.push_back(wake_word_id);
1249
+ }
1250
+ resp.max_active_wake_words = config.max_active_wake_words;
1353
1251
  return resp;
1354
1252
  }
1355
1253
 
1356
1254
  void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
1357
- if (voice_assistant::global_voice_assistant != nullptr) {
1358
- if (voice_assistant::global_voice_assistant->get_api_connection() != this) {
1359
- return;
1360
- }
1361
-
1255
+ if (this->check_voice_assistant_api_connection_()) {
1362
1256
  voice_assistant::global_voice_assistant->on_set_configuration(msg.active_wake_words);
1363
1257
  }
1364
1258
  }
@@ -1367,8 +1261,8 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
1367
1261
 
1368
1262
  #ifdef USE_ALARM_CONTROL_PANEL
1369
1263
  bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1370
- return this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
1371
- AlarmControlPanelStateResponse::MESSAGE_TYPE);
1264
+ return this->send_message_smart_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
1265
+ AlarmControlPanelStateResponse::MESSAGE_TYPE);
1372
1266
  }
1373
1267
  uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
1374
1268
  uint32_t remaining_size, bool is_single) {
@@ -1378,10 +1272,6 @@ uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, A
1378
1272
  fill_entity_state_base(a_alarm_control_panel, resp);
1379
1273
  return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1380
1274
  }
1381
- void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1382
- this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info,
1383
- ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE);
1384
- }
1385
1275
  uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
1386
1276
  uint32_t remaining_size, bool is_single) {
1387
1277
  auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
@@ -1395,11 +1285,7 @@ uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, AP
1395
1285
  is_single);
1396
1286
  }
1397
1287
  void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
1398
- alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key);
1399
- if (a_alarm_control_panel == nullptr)
1400
- return;
1401
-
1402
- auto call = a_alarm_control_panel->make_call();
1288
+ ENTITY_COMMAND_MAKE_CALL(alarm_control_panel::AlarmControlPanel, a_alarm_control_panel, alarm_control_panel)
1403
1289
  switch (msg.command) {
1404
1290
  case enums::ALARM_CONTROL_PANEL_DISARM:
1405
1291
  call.disarm();
@@ -1430,10 +1316,7 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
1430
1316
 
1431
1317
  #ifdef USE_EVENT
1432
1318
  void APIConnection::send_event(event::Event *event, const std::string &event_type) {
1433
- this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE);
1434
- }
1435
- void APIConnection::send_event_info(event::Event *event) {
1436
- this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE);
1319
+ this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE);
1437
1320
  }
1438
1321
  uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
1439
1322
  uint32_t remaining_size, bool is_single) {
@@ -1458,7 +1341,7 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c
1458
1341
 
1459
1342
  #ifdef USE_UPDATE
1460
1343
  bool APIConnection::send_update_state(update::UpdateEntity *update) {
1461
- return this->schedule_message_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE);
1344
+ return this->send_message_smart_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE);
1462
1345
  }
1463
1346
  uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1464
1347
  bool is_single) {
@@ -1480,9 +1363,6 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection
1480
1363
  fill_entity_state_base(update, resp);
1481
1364
  return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1482
1365
  }
1483
- void APIConnection::send_update_info(update::UpdateEntity *update) {
1484
- this->schedule_message_(update, &APIConnection::try_send_update_info, ListEntitiesUpdateResponse::MESSAGE_TYPE);
1485
- }
1486
1366
  uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1487
1367
  bool is_single) {
1488
1368
  auto *update = static_cast<update::UpdateEntity *>(entity);
@@ -1493,9 +1373,7 @@ uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *
1493
1373
  return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1494
1374
  }
1495
1375
  void APIConnection::update_command(const UpdateCommandRequest &msg) {
1496
- update::UpdateEntity *update = App.get_update_by_key(msg.key);
1497
- if (update == nullptr)
1498
- return;
1376
+ ENTITY_COMMAND_GET(update::UpdateEntity, update, update)
1499
1377
 
1500
1378
  switch (msg.command) {
1501
1379
  case enums::UPDATE_COMMAND_UPDATE:
@@ -1514,12 +1392,11 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
1514
1392
  }
1515
1393
  #endif
1516
1394
 
1517
- bool APIConnection::try_send_log_message(int level, const char *tag, const char *line) {
1518
- if (this->log_subscription_ < level)
1395
+ bool APIConnection::try_send_log_message(int level, const char *tag, const char *line, size_t message_len) {
1396
+ if (this->flags_.log_subscription < level)
1519
1397
  return false;
1520
1398
 
1521
1399
  // Pre-calculate message size to avoid reallocations
1522
- const size_t line_length = strlen(line);
1523
1400
  uint32_t msg_size = 0;
1524
1401
 
1525
1402
  // Add size for level field (field ID 1, varint type)
@@ -1528,14 +1405,14 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
1528
1405
 
1529
1406
  // Add size for string field (field ID 3, string type)
1530
1407
  // 1 byte for field tag + size of length varint + string length
1531
- msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(line_length)) + line_length;
1408
+ msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(message_len)) + message_len;
1532
1409
 
1533
1410
  // Create a pre-sized buffer
1534
1411
  auto buffer = this->create_buffer(msg_size);
1535
1412
 
1536
1413
  // Encode the message (SubscribeLogsResponse)
1537
1414
  buffer.encode_uint32(1, static_cast<uint32_t>(level)); // LogLevel level = 1
1538
- buffer.encode_string(3, line, line_length); // string message = 3
1415
+ buffer.encode_string(3, line, message_len); // string message = 3
1539
1416
 
1540
1417
  // SubscribeLogsResponse - 29
1541
1418
  return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
@@ -1544,8 +1421,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
1544
1421
  HelloResponse APIConnection::hello(const HelloRequest &msg) {
1545
1422
  this->client_info_ = msg.client_info;
1546
1423
  this->client_peername_ = this->helper_->getpeername();
1547
- this->client_combined_info_ = this->client_info_ + " (" + this->client_peername_ + ")";
1548
- this->helper_->set_log_info(this->client_combined_info_);
1424
+ this->helper_->set_log_info(this->get_client_combined_info());
1549
1425
  this->client_api_version_major_ = msg.api_version_major;
1550
1426
  this->client_api_version_minor_ = msg.api_version_minor;
1551
1427
  ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
@@ -1557,19 +1433,24 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
1557
1433
  resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
1558
1434
  resp.name = App.get_name();
1559
1435
 
1560
- this->connection_state_ = ConnectionState::CONNECTED;
1436
+ this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::CONNECTED);
1561
1437
  return resp;
1562
1438
  }
1563
1439
  ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
1564
- bool correct = this->parent_->check_password(msg.password);
1440
+ bool correct = true;
1441
+ #ifdef USE_API_PASSWORD
1442
+ correct = this->parent_->check_password(msg.password);
1443
+ #endif
1565
1444
 
1566
1445
  ConnectResponse resp;
1567
1446
  // bool invalid_password = 1;
1568
1447
  resp.invalid_password = !correct;
1569
1448
  if (correct) {
1570
- ESP_LOGD(TAG, "%s connected", this->client_combined_info_.c_str());
1571
- this->connection_state_ = ConnectionState::AUTHENTICATED;
1449
+ ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
1450
+ this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
1451
+ #ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1572
1452
  this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
1453
+ #endif
1573
1454
  #ifdef USE_HOMEASSISTANT_TIME
1574
1455
  if (homeassistant::global_homeassistant_time != nullptr) {
1575
1456
  this->send_time_request();
@@ -1580,7 +1461,11 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
1580
1461
  }
1581
1462
  DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
1582
1463
  DeviceInfoResponse resp{};
1464
+ #ifdef USE_API_PASSWORD
1583
1465
  resp.uses_password = this->parent_->uses_password();
1466
+ #else
1467
+ resp.uses_password = false;
1468
+ #endif
1584
1469
  resp.name = App.get_name();
1585
1470
  resp.friendly_name = App.get_friendly_name();
1586
1471
  resp.suggested_area = App.get_area();
@@ -1593,6 +1478,8 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
1593
1478
  resp.manufacturer = "Raspberry Pi";
1594
1479
  #elif defined(USE_BK72XX)
1595
1480
  resp.manufacturer = "Beken";
1481
+ #elif defined(USE_LN882X)
1482
+ resp.manufacturer = "Lightning";
1596
1483
  #elif defined(USE_RTL87XX)
1597
1484
  resp.manufacturer = "Realtek";
1598
1485
  #elif defined(USE_HOST)
@@ -1620,6 +1507,23 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
1620
1507
  #endif
1621
1508
  #ifdef USE_API_NOISE
1622
1509
  resp.api_encryption_supported = true;
1510
+ #endif
1511
+ #ifdef USE_DEVICES
1512
+ for (auto const &device : App.get_devices()) {
1513
+ DeviceInfo device_info;
1514
+ device_info.device_id = device->get_device_id();
1515
+ device_info.name = device->get_name();
1516
+ device_info.area_id = device->get_area_id();
1517
+ resp.devices.push_back(device_info);
1518
+ }
1519
+ #endif
1520
+ #ifdef USE_AREAS
1521
+ for (auto const &area : App.get_areas()) {
1522
+ AreaInfo area_info;
1523
+ area_info.area_id = area->get_area_id();
1524
+ area_info.name = area->get_name();
1525
+ resp.areas.push_back(area_info);
1526
+ }
1623
1527
  #endif
1624
1528
  return resp;
1625
1529
  }
@@ -1665,7 +1569,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
1665
1569
  state_subs_at_ = 0;
1666
1570
  }
1667
1571
  bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
1668
- if (this->remove_)
1572
+ if (this->flags_.remove)
1669
1573
  return false;
1670
1574
  if (this->helper_->can_write_without_blocking())
1671
1575
  return true;
@@ -1673,7 +1577,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
1673
1577
  APIError err = this->helper_->loop();
1674
1578
  if (err != APIError::OK) {
1675
1579
  on_fatal_error();
1676
- ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
1580
+ ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
1677
1581
  api_error_to_str(err), errno);
1678
1582
  return false;
1679
1583
  }
@@ -1695,10 +1599,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
1695
1599
  if (err != APIError::OK) {
1696
1600
  on_fatal_error();
1697
1601
  if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1698
- ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
1602
+ ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
1699
1603
  } else {
1700
- ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
1701
- errno);
1604
+ ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
1605
+ api_error_to_str(err), errno);
1702
1606
  }
1703
1607
  return false;
1704
1608
  }
@@ -1707,15 +1611,15 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
1707
1611
  }
1708
1612
  void APIConnection::on_unauthenticated_access() {
1709
1613
  this->on_fatal_error();
1710
- ESP_LOGD(TAG, "%s requested access without authentication", this->client_combined_info_.c_str());
1614
+ ESP_LOGD(TAG, "%s requested access without authentication", this->get_client_combined_info().c_str());
1711
1615
  }
1712
1616
  void APIConnection::on_no_setup_connection() {
1713
1617
  this->on_fatal_error();
1714
- ESP_LOGD(TAG, "%s requested access without full connection", this->client_combined_info_.c_str());
1618
+ ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str());
1715
1619
  }
1716
1620
  void APIConnection::on_fatal_error() {
1717
1621
  this->helper_->close();
1718
- this->remove_ = true;
1622
+ this->flags_.remove = true;
1719
1623
  }
1720
1624
 
1721
1625
  void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
@@ -1724,7 +1628,9 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c
1724
1628
  // O(n) but optimized for RAM and not performance.
1725
1629
  for (auto &item : items) {
1726
1630
  if (item.entity == entity && item.message_type == message_type) {
1727
- // Update the existing item with the new creator
1631
+ // Clean up old creator before replacing
1632
+ item.creator.cleanup(message_type);
1633
+ // Move assign the new creator
1728
1634
  item.creator = std::move(creator);
1729
1635
  return;
1730
1636
  }
@@ -1734,9 +1640,14 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c
1734
1640
  items.emplace_back(entity, std::move(creator), message_type);
1735
1641
  }
1736
1642
 
1643
+ void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
1644
+ // Insert at front for high priority messages (no deduplication check)
1645
+ items.insert(items.begin(), BatchItem(entity, std::move(creator), message_type));
1646
+ }
1647
+
1737
1648
  bool APIConnection::schedule_batch_() {
1738
- if (!this->deferred_batch_.batch_scheduled) {
1739
- this->deferred_batch_.batch_scheduled = true;
1649
+ if (!this->flags_.batch_scheduled) {
1650
+ this->flags_.batch_scheduled = true;
1740
1651
  this->deferred_batch_.batch_start_time = App.get_loop_component_start_time();
1741
1652
  }
1742
1653
  return true;
@@ -1745,14 +1656,14 @@ bool APIConnection::schedule_batch_() {
1745
1656
  ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); }
1746
1657
 
1747
1658
  ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
1748
- ProtoWriteBuffer result = this->prepare_message_buffer(size, this->batch_first_message_);
1749
- this->batch_first_message_ = false;
1659
+ ProtoWriteBuffer result = this->prepare_message_buffer(size, this->flags_.batch_first_message);
1660
+ this->flags_.batch_first_message = false;
1750
1661
  return result;
1751
1662
  }
1752
1663
 
1753
1664
  void APIConnection::process_batch_() {
1754
1665
  if (this->deferred_batch_.empty()) {
1755
- this->deferred_batch_.batch_scheduled = false;
1666
+ this->flags_.batch_scheduled = false;
1756
1667
  return;
1757
1668
  }
1758
1669
 
@@ -1762,22 +1673,28 @@ void APIConnection::process_batch_() {
1762
1673
  return;
1763
1674
  }
1764
1675
 
1765
- size_t num_items = this->deferred_batch_.items.size();
1676
+ size_t num_items = this->deferred_batch_.size();
1766
1677
 
1767
1678
  // Fast path for single message - allocate exact size needed
1768
1679
  if (num_items == 1) {
1769
- const auto &item = this->deferred_batch_.items[0];
1680
+ const auto &item = this->deferred_batch_[0];
1770
1681
 
1771
1682
  // Let the creator calculate size and encode if it fits
1772
- uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true);
1683
+ uint16_t payload_size =
1684
+ item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true, item.message_type);
1773
1685
 
1774
1686
  if (payload_size > 0 &&
1775
1687
  this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
1776
- this->deferred_batch_.clear();
1688
+ #ifdef HAS_PROTO_MESSAGE_DUMP
1689
+ // Log messages after send attempt for VV debugging
1690
+ // It's safe to use the buffer for logging at this point regardless of send result
1691
+ this->log_batch_item_(item);
1692
+ #endif
1693
+ this->clear_batch_();
1777
1694
  } else if (payload_size == 0) {
1778
1695
  // Message too large
1779
1696
  ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
1780
- this->deferred_batch_.clear();
1697
+ this->clear_batch_();
1781
1698
  }
1782
1699
  return;
1783
1700
  }
@@ -1795,7 +1712,8 @@ void APIConnection::process_batch_() {
1795
1712
 
1796
1713
  // Pre-calculate exact buffer size needed based on message types
1797
1714
  uint32_t total_estimated_size = 0;
1798
- for (const auto &item : this->deferred_batch_.items) {
1715
+ for (size_t i = 0; i < this->deferred_batch_.size(); i++) {
1716
+ const auto &item = this->deferred_batch_[i];
1799
1717
  total_estimated_size += get_estimated_message_size(item.message_type);
1800
1718
  }
1801
1719
 
@@ -1804,7 +1722,7 @@ void APIConnection::process_batch_() {
1804
1722
 
1805
1723
  // Reserve based on estimated size (much more accurate than 24-byte worst-case)
1806
1724
  this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead);
1807
- this->batch_first_message_ = true;
1725
+ this->flags_.batch_first_message = true;
1808
1726
 
1809
1727
  size_t items_processed = 0;
1810
1728
  uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
@@ -1816,10 +1734,11 @@ void APIConnection::process_batch_() {
1816
1734
  uint32_t current_offset = 0;
1817
1735
 
1818
1736
  // Process items and encode directly to buffer
1819
- for (const auto &item : this->deferred_batch_.items) {
1737
+ for (size_t i = 0; i < this->deferred_batch_.size(); i++) {
1738
+ const auto &item = this->deferred_batch_[i];
1820
1739
  // Try to encode message
1821
1740
  // The creator will calculate overhead to determine if the message fits
1822
- uint16_t payload_size = item.creator(item.entity, this, remaining_size, false);
1741
+ uint16_t payload_size = item.creator(item.entity, this, remaining_size, false, item.message_type);
1823
1742
 
1824
1743
  if (payload_size == 0) {
1825
1744
  // Message won't fit, stop processing
@@ -1860,44 +1779,46 @@ void APIConnection::process_batch_() {
1860
1779
  if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
1861
1780
  on_fatal_error();
1862
1781
  if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1863
- ESP_LOGW(TAG, "%s: Connection reset during batch write", this->client_combined_info_.c_str());
1782
+ ESP_LOGW(TAG, "%s: Connection reset during batch write", this->get_client_combined_info().c_str());
1864
1783
  } else {
1865
- ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
1866
- errno);
1784
+ ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(),
1785
+ api_error_to_str(err), errno);
1867
1786
  }
1868
1787
  }
1869
1788
 
1870
- // Handle remaining items more efficiently
1871
- if (items_processed < this->deferred_batch_.items.size()) {
1872
- // Remove processed items from the beginning
1873
- this->deferred_batch_.items.erase(this->deferred_batch_.items.begin(),
1874
- this->deferred_batch_.items.begin() + items_processed);
1789
+ #ifdef HAS_PROTO_MESSAGE_DUMP
1790
+ // Log messages after send attempt for VV debugging
1791
+ // It's safe to use the buffer for logging at this point regardless of send result
1792
+ for (size_t i = 0; i < items_processed; i++) {
1793
+ const auto &item = this->deferred_batch_[i];
1794
+ this->log_batch_item_(item);
1795
+ }
1796
+ #endif
1875
1797
 
1798
+ // Handle remaining items more efficiently
1799
+ if (items_processed < this->deferred_batch_.size()) {
1800
+ // Remove processed items from the beginning with proper cleanup
1801
+ this->deferred_batch_.remove_front(items_processed);
1876
1802
  // Reschedule for remaining items
1877
1803
  this->schedule_batch_();
1878
1804
  } else {
1879
1805
  // All items processed
1880
- this->deferred_batch_.clear();
1806
+ this->clear_batch_();
1881
1807
  }
1882
1808
  }
1883
1809
 
1884
1810
  uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1885
- bool is_single) const {
1886
- switch (message_type_) {
1887
- case 0: // Function pointer
1888
- return data_.ptr(entity, conn, remaining_size, is_single);
1889
-
1811
+ bool is_single, uint16_t message_type) const {
1890
1812
  #ifdef USE_EVENT
1891
- case EventResponse::MESSAGE_TYPE: {
1892
- auto *e = static_cast<event::Event *>(entity);
1893
- return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
1894
- }
1813
+ // Special case: EventResponse uses string pointer
1814
+ if (message_type == EventResponse::MESSAGE_TYPE) {
1815
+ auto *e = static_cast<event::Event *>(entity);
1816
+ return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
1817
+ }
1895
1818
  #endif
1896
1819
 
1897
- default:
1898
- // Should not happen, return 0 to indicate no message
1899
- return 0;
1900
- }
1820
+ // All other message types use function pointers
1821
+ return data_.function_ptr(entity, conn, remaining_size, is_single);
1901
1822
  }
1902
1823
 
1903
1824
  uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -1912,6 +1833,12 @@ uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConne
1912
1833
  return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
1913
1834
  }
1914
1835
 
1836
+ uint16_t APIConnection::try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1837
+ bool is_single) {
1838
+ PingRequest req;
1839
+ return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
1840
+ }
1841
+
1915
1842
  uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) {
1916
1843
  // Use generated ESTIMATED_SIZE constants from each message type
1917
1844
  switch (message_type) {