esphome 2025.7.5__py3-none-any.whl → 2025.8.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 (752) hide show
  1. esphome/__main__.py +189 -82
  2. esphome/automation.py +2 -4
  3. esphome/build_gen/__init__.py +0 -0
  4. esphome/build_gen/platformio.py +102 -0
  5. esphome/components/a4988/a4988.cpp +0 -1
  6. esphome/components/absolute_humidity/absolute_humidity.cpp +0 -2
  7. esphome/components/absolute_humidity/sensor.py +2 -2
  8. esphome/components/adc/__init__.py +123 -85
  9. esphome/components/adc/adc_sensor.h +98 -35
  10. esphome/components/adc/adc_sensor_common.cpp +10 -4
  11. esphome/components/adc/adc_sensor_esp32.cpp +291 -123
  12. esphome/components/adc/adc_sensor_esp8266.cpp +1 -4
  13. esphome/components/adc/adc_sensor_libretiny.cpp +1 -2
  14. esphome/components/adc/adc_sensor_rp2040.cpp +1 -2
  15. esphome/components/adc/adc_sensor_zephyr.cpp +207 -0
  16. esphome/components/adc/sensor.py +61 -27
  17. esphome/components/adc128s102/adc128s102.cpp +1 -4
  18. esphome/components/ade7880/sensor.py +75 -49
  19. esphome/components/ads1115/ads1115.cpp +0 -1
  20. esphome/components/ads1118/ads1118.cpp +0 -1
  21. esphome/components/ags10/ags10.cpp +0 -4
  22. esphome/components/aht10/aht10.cpp +0 -4
  23. esphome/components/aic3204/aic3204.cpp +0 -2
  24. esphome/components/airthings_wave_plus/__init__.py +1 -1
  25. esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +22 -4
  26. esphome/components/airthings_wave_plus/airthings_wave_plus.h +10 -1
  27. esphome/components/airthings_wave_plus/sensor.py +55 -28
  28. esphome/components/alarm_control_panel/__init__.py +4 -9
  29. esphome/components/am2315c/am2315c.cpp +0 -2
  30. esphome/components/am2320/am2320.cpp +0 -1
  31. esphome/components/animation/__init__.py +14 -11
  32. esphome/components/apds9306/apds9306.cpp +0 -4
  33. esphome/components/apds9960/apds9960.cpp +0 -1
  34. esphome/components/api/__init__.py +29 -4
  35. esphome/components/api/api_connection.cpp +378 -401
  36. esphome/components/api/api_connection.h +102 -52
  37. esphome/components/api/api_frame_helper.cpp +69 -896
  38. esphome/components/api/api_frame_helper.h +31 -126
  39. esphome/components/api/api_frame_helper_noise.cpp +583 -0
  40. esphome/components/api/api_frame_helper_noise.h +68 -0
  41. esphome/components/api/api_frame_helper_plaintext.cpp +290 -0
  42. esphome/components/api/api_frame_helper_plaintext.h +53 -0
  43. esphome/components/api/api_noise_context.h +2 -4
  44. esphome/components/api/api_pb2.cpp +1601 -1808
  45. esphome/components/api/api_pb2.h +367 -323
  46. esphome/components/api/api_pb2_dump.cpp +1137 -3466
  47. esphome/components/api/api_pb2_includes.h +34 -0
  48. esphome/components/api/api_pb2_service.cpp +94 -105
  49. esphome/components/api/api_pb2_service.h +27 -16
  50. esphome/components/api/api_server.cpp +18 -17
  51. esphome/components/api/api_server.h +8 -5
  52. esphome/components/api/client.py +16 -8
  53. esphome/components/api/custom_api_device.h +68 -14
  54. esphome/components/api/homeassistant_service.h +24 -19
  55. esphome/components/api/list_entities.cpp +3 -5
  56. esphome/components/api/list_entities.h +2 -4
  57. esphome/components/api/proto.cpp +3 -5
  58. esphome/components/api/proto.h +239 -274
  59. esphome/components/api/subscribe_state.cpp +2 -4
  60. esphome/components/api/subscribe_state.h +2 -4
  61. esphome/components/api/user_services.cpp +2 -4
  62. esphome/components/api/user_services.h +8 -8
  63. esphome/components/as3935/as3935.cpp +0 -2
  64. esphome/components/as3935_spi/as3935_spi.cpp +0 -2
  65. esphome/components/as5600/__init__.py +1 -1
  66. esphome/components/as5600/as5600.cpp +0 -2
  67. esphome/components/as5600/sensor/__init__.py +0 -1
  68. esphome/components/as7341/as7341.cpp +0 -1
  69. esphome/components/async_tcp/__init__.py +1 -1
  70. esphome/components/at581x/at581x.cpp +1 -1
  71. esphome/components/atm90e26/atm90e26.cpp +0 -1
  72. esphome/components/atm90e32/atm90e32.cpp +488 -118
  73. esphome/components/atm90e32/atm90e32.h +44 -5
  74. esphome/components/audio/audio.h +2 -2
  75. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +0 -2
  76. esphome/components/beken_spi_led_strip/led_strip.cpp +0 -2
  77. esphome/components/bh1750/bh1750.cpp +0 -1
  78. esphome/components/binary_sensor/__init__.py +14 -12
  79. esphome/components/ble_client/__init__.py +4 -7
  80. esphome/components/bluetooth_proxy/__init__.py +40 -3
  81. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +387 -82
  82. esphome/components/bluetooth_proxy/bluetooth_connection.h +16 -5
  83. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +102 -311
  84. esphome/components/bluetooth_proxy/bluetooth_proxy.h +30 -14
  85. esphome/components/bme280_base/bme280_base.cpp +15 -16
  86. esphome/components/bme680/bme680.cpp +2 -3
  87. esphome/components/bme680_bsec/bme680_bsec.cpp +0 -2
  88. esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +0 -2
  89. esphome/components/bmi160/bmi160.cpp +0 -1
  90. esphome/components/bmp085/bmp085.cpp +0 -1
  91. esphome/components/bmp280_base/bmp280_base.cpp +13 -14
  92. esphome/components/bmp3xx_base/bmp3xx_base.cpp +0 -1
  93. esphome/components/bmp581/bmp581.cpp +0 -2
  94. esphome/components/bp1658cj/bp1658cj.cpp +0 -1
  95. esphome/components/bp5758d/bp5758d.cpp +0 -1
  96. esphome/components/button/__init__.py +0 -1
  97. esphome/components/canbus/__init__.py +2 -3
  98. esphome/components/canbus/canbus.cpp +0 -1
  99. esphome/components/cap1188/cap1188.cpp +0 -2
  100. esphome/components/captive_portal/__init__.py +1 -1
  101. esphome/components/cd74hc4067/cd74hc4067.cpp +0 -2
  102. esphome/components/ch422g/ch422g.cpp +0 -1
  103. esphome/components/chsc6x/chsc6x_touchscreen.cpp +0 -3
  104. esphome/components/climate/__init__.py +0 -1
  105. esphome/components/climate/climate_traits.h +24 -0
  106. esphome/components/cm1106/cm1106.cpp +0 -1
  107. esphome/components/const/__init__.py +6 -0
  108. esphome/components/cover/__init__.py +0 -1
  109. esphome/components/cover/cover.cpp +9 -13
  110. esphome/components/cs5460a/cs5460a.cpp +0 -2
  111. esphome/components/cse7761/cse7761.cpp +0 -1
  112. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +0 -2
  113. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +0 -2
  114. esphome/components/dac7678/dac7678_output.cpp +0 -2
  115. esphome/components/dallas_temp/dallas_temp.cpp +0 -1
  116. esphome/components/datetime/__init__.py +0 -2
  117. esphome/components/debug/__init__.py +15 -1
  118. esphome/components/debug/debug_zephyr.cpp +281 -0
  119. esphome/components/debug/sensor.py +2 -1
  120. esphome/components/deep_sleep/deep_sleep_component.cpp +0 -1
  121. esphome/components/deep_sleep/deep_sleep_esp32.cpp +20 -1
  122. esphome/components/dfrobot_sen0395/__init__.py +1 -2
  123. esphome/components/dht/dht.cpp +0 -1
  124. esphome/components/dht12/dht12.cpp +0 -1
  125. esphome/components/display/__init__.py +16 -3
  126. esphome/components/display_menu_base/__init__.py +1 -1
  127. esphome/components/dps310/dps310.cpp +0 -2
  128. esphome/components/ds1307/ds1307.cpp +0 -1
  129. esphome/components/ds2484/ds2484.cpp +0 -1
  130. esphome/components/duty_cycle/duty_cycle_sensor.cpp +0 -1
  131. esphome/components/ee895/ee895.cpp +0 -1
  132. esphome/components/ektf2232/touchscreen/ektf2232.cpp +0 -1
  133. esphome/components/emc2101/emc2101.cpp +0 -2
  134. esphome/components/ens160_base/ens160_base.cpp +0 -2
  135. esphome/components/ens210/ens210.cpp +0 -1
  136. esphome/components/es7210/es7210.cpp +0 -2
  137. esphome/components/es7243e/es7243e.cpp +0 -2
  138. esphome/components/es8156/es8156.cpp +0 -2
  139. esphome/components/es8311/es8311.cpp +0 -2
  140. esphome/components/es8388/es8388.cpp +0 -2
  141. esphome/components/esp32/__init__.py +199 -58
  142. esphome/components/esp32/boards.py +17 -0
  143. esphome/components/esp32/gpio.cpp +0 -1
  144. esphome/components/esp32/gpio.py +1 -2
  145. esphome/components/esp32/gpio_esp32_h2.py +2 -7
  146. esphome/components/esp32/gpio_esp32_p4.py +2 -7
  147. esphome/components/esp32/post_build.py.script +112 -61
  148. esphome/components/esp32_ble/__init__.py +40 -2
  149. esphome/components/esp32_ble/ble.cpp +12 -8
  150. esphome/components/esp32_ble/ble.h +18 -18
  151. esphome/components/esp32_ble/ble_advertising.cpp +5 -5
  152. esphome/components/esp32_ble/ble_advertising.h +7 -5
  153. esphome/components/esp32_ble/ble_event.h +2 -4
  154. esphome/components/esp32_ble/ble_scan_result.h +2 -4
  155. esphome/components/esp32_ble/ble_uuid.cpp +5 -5
  156. esphome/components/esp32_ble/ble_uuid.h +6 -5
  157. esphome/components/esp32_ble_beacon/__init__.py +4 -0
  158. esphome/components/esp32_ble_client/__init__.py +1 -1
  159. esphome/components/esp32_ble_client/ble_characteristic.cpp +4 -4
  160. esphome/components/esp32_ble_client/ble_characteristic.h +6 -4
  161. esphome/components/esp32_ble_client/ble_client_base.cpp +155 -104
  162. esphome/components/esp32_ble_client/ble_client_base.h +17 -6
  163. esphome/components/esp32_ble_client/ble_descriptor.h +6 -4
  164. esphome/components/esp32_ble_client/ble_service.cpp +4 -4
  165. esphome/components/esp32_ble_client/ble_service.h +6 -4
  166. esphome/components/esp32_ble_server/__init__.py +15 -12
  167. esphome/components/esp32_ble_tracker/__init__.py +79 -11
  168. esphome/components/esp32_ble_tracker/automation.h +4 -4
  169. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +264 -261
  170. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +103 -37
  171. esphome/components/esp32_camera/__init__.py +13 -1
  172. esphome/components/esp32_camera/esp32_camera.cpp +7 -2
  173. esphome/components/esp32_camera/esp32_camera.h +1 -0
  174. esphome/components/esp32_dac/esp32_dac.cpp +3 -19
  175. esphome/components/esp32_dac/esp32_dac.h +4 -8
  176. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -6
  177. esphome/components/esp32_rmt_led_strip/light.py +1 -1
  178. esphome/components/esp32_touch/__init__.py +2 -3
  179. esphome/components/esp32_touch/esp32_touch.h +9 -6
  180. esphome/components/esp32_touch/esp32_touch_common.cpp +2 -0
  181. esphome/components/esp32_touch/esp32_touch_v1.cpp +7 -9
  182. esphome/components/esp32_touch/esp32_touch_v2.cpp +10 -6
  183. esphome/components/esp8266/__init__.py +3 -1
  184. esphome/components/esp8266_pwm/esp8266_pwm.cpp +0 -1
  185. esphome/components/esphome/ota/__init__.py +1 -2
  186. esphome/components/esphome/ota/ota_esphome.cpp +150 -77
  187. esphome/components/esphome/ota/ota_esphome.h +8 -1
  188. esphome/components/espnow/__init__.py +309 -0
  189. esphome/components/espnow/automation.h +175 -0
  190. esphome/components/espnow/espnow_component.cpp +468 -0
  191. esphome/components/espnow/espnow_component.h +183 -0
  192. esphome/components/espnow/espnow_err.h +19 -0
  193. esphome/components/espnow/espnow_packet.h +166 -0
  194. esphome/components/ethernet/__init__.py +7 -1
  195. esphome/components/ethernet/esp_eth_phy_jl1101.c +5 -0
  196. esphome/components/ethernet/ethernet_component.cpp +0 -1
  197. esphome/components/ethernet/ethernet_component.h +4 -0
  198. esphome/components/ethernet_info/ethernet_info_text_sensor.h +0 -3
  199. esphome/components/event/__init__.py +0 -1
  200. esphome/components/factory_reset/__init__.py +92 -0
  201. esphome/components/factory_reset/factory_reset.cpp +76 -0
  202. esphome/components/factory_reset/factory_reset.h +43 -0
  203. esphome/components/fan/__init__.py +0 -1
  204. esphome/components/fan/fan_traits.h +16 -0
  205. esphome/components/fastled_base/fastled_light.cpp +0 -1
  206. esphome/components/fastled_spi/light.py +1 -3
  207. esphome/components/fingerprint_grow/fingerprint_grow.cpp +0 -2
  208. esphome/components/fs3000/fs3000.cpp +0 -2
  209. esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +0 -2
  210. esphome/components/ft63x6/ft63x6.cpp +0 -1
  211. esphome/components/gdk101/gdk101.cpp +0 -1
  212. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +0 -1
  213. esphome/components/gl_r01_i2c/sensor.py +1 -1
  214. esphome/components/gpio/one_wire/gpio_one_wire.cpp +0 -1
  215. esphome/components/gpio/switch/gpio_switch.cpp +0 -2
  216. esphome/components/gpio_expander/cached_gpio.h +24 -15
  217. esphome/components/gps/__init__.py +6 -2
  218. esphome/components/gps/gps.cpp +50 -49
  219. esphome/components/gps/gps.h +4 -8
  220. esphome/components/gps/time/gps_time.cpp +3 -9
  221. esphome/components/gps/time/gps_time.h +4 -7
  222. esphome/components/graph/__init__.py +1 -1
  223. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +0 -1
  224. esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +0 -1
  225. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +21 -12
  226. esphome/components/gt911/touchscreen/gt911_touchscreen.h +26 -2
  227. esphome/components/haier/climate.py +5 -10
  228. esphome/components/haier/haier_base.cpp +0 -1
  229. esphome/components/hbridge/switch/hbridge_switch.cpp +0 -2
  230. esphome/components/hdc1080/hdc1080.cpp +0 -2
  231. esphome/components/heatpumpir/climate.py +2 -2
  232. esphome/components/hlw8012/hlw8012.cpp +0 -1
  233. esphome/components/hm3301/hm3301.cpp +0 -1
  234. esphome/components/hmc5883l/hmc5883l.cpp +0 -1
  235. esphome/components/homeassistant/__init__.py +1 -0
  236. esphome/components/homeassistant/number/__init__.py +1 -0
  237. esphome/components/homeassistant/number/homeassistant_number.cpp +11 -7
  238. esphome/components/homeassistant/switch/__init__.py +1 -0
  239. esphome/components/homeassistant/switch/homeassistant_switch.cpp +9 -5
  240. esphome/components/honeywellabp/honeywellabp.cpp +1 -4
  241. esphome/components/host/__init__.py +2 -0
  242. esphome/components/hte501/hte501.cpp +0 -1
  243. esphome/components/http_request/__init__.py +2 -3
  244. esphome/components/http_request/http_request_idf.cpp +2 -2
  245. esphome/components/htu21d/htu21d.cpp +0 -2
  246. esphome/components/htu31d/htu31d.cpp +0 -2
  247. esphome/components/hx711/hx711.cpp +0 -1
  248. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +0 -1
  249. esphome/components/hydreon_rgxx/sensor.py +4 -5
  250. esphome/components/i2c/i2c_bus.h +1 -1
  251. esphome/components/i2c/i2c_bus_arduino.cpp +1 -2
  252. esphome/components/i2c/i2c_bus_esp_idf.cpp +192 -17
  253. esphome/components/i2c/i2c_bus_esp_idf.h +11 -1
  254. esphome/components/i2s_audio/__init__.py +6 -5
  255. esphome/components/i2s_audio/i2s_audio.cpp +0 -2
  256. esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +1 -4
  257. esphome/components/i2s_audio/microphone/__init__.py +4 -6
  258. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +0 -1
  259. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +2 -2
  260. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +273 -269
  261. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +19 -34
  262. esphome/components/ili9xxx/display.py +4 -3
  263. esphome/components/ili9xxx/ili9xxx_display.cpp +0 -2
  264. esphome/components/image/__init__.py +123 -92
  265. esphome/components/improv_serial/__init__.py +7 -8
  266. esphome/components/ina219/ina219.cpp +0 -1
  267. esphome/components/ina226/ina226.cpp +0 -2
  268. esphome/components/ina260/ina260.cpp +0 -2
  269. esphome/components/ina2xx_base/__init__.py +2 -5
  270. esphome/components/ina2xx_base/ina2xx_base.cpp +0 -2
  271. esphome/components/ina3221/ina3221.cpp +0 -1
  272. esphome/components/internal_temperature/internal_temperature.cpp +0 -2
  273. esphome/components/interval/interval.h +5 -9
  274. esphome/components/json/__init__.py +1 -1
  275. esphome/components/kmeteriso/kmeteriso.cpp +0 -2
  276. esphome/components/lc709203f/lc709203f.cpp +0 -2
  277. esphome/components/lcd_gpio/display.py +1 -3
  278. esphome/components/lcd_gpio/gpio_lcd_display.cpp +0 -1
  279. esphome/components/lcd_pcf8574/pcf8574_display.cpp +0 -1
  280. esphome/components/ld2410/__init__.py +4 -6
  281. esphome/components/ld2410/binary_sensor.py +4 -0
  282. esphome/components/ld2410/ld2410.cpp +56 -100
  283. esphome/components/ld2410/ld2410.h +17 -15
  284. esphome/components/ld2410/sensor.py +24 -10
  285. esphome/components/ld2412/__init__.py +46 -0
  286. esphome/components/ld2412/binary_sensor.py +70 -0
  287. esphome/components/ld2412/button/__init__.py +74 -0
  288. esphome/components/ld2412/button/factory_reset_button.cpp +9 -0
  289. esphome/components/ld2412/button/factory_reset_button.h +18 -0
  290. esphome/components/ld2412/button/query_button.cpp +9 -0
  291. esphome/components/ld2412/button/query_button.h +18 -0
  292. esphome/components/ld2412/button/restart_button.cpp +9 -0
  293. esphome/components/ld2412/button/restart_button.h +18 -0
  294. esphome/components/ld2412/button/start_dynamic_background_correction_button.cpp +11 -0
  295. esphome/components/ld2412/button/start_dynamic_background_correction_button.h +18 -0
  296. esphome/components/ld2412/ld2412.cpp +861 -0
  297. esphome/components/ld2412/ld2412.h +141 -0
  298. esphome/components/ld2412/number/__init__.py +126 -0
  299. esphome/components/ld2412/number/gate_threshold_number.cpp +14 -0
  300. esphome/components/ld2412/number/gate_threshold_number.h +19 -0
  301. esphome/components/ld2412/number/light_threshold_number.cpp +12 -0
  302. esphome/components/ld2412/number/light_threshold_number.h +18 -0
  303. esphome/components/ld2412/number/max_distance_timeout_number.cpp +12 -0
  304. esphome/components/ld2412/number/max_distance_timeout_number.h +18 -0
  305. esphome/components/ld2412/select/__init__.py +82 -0
  306. esphome/components/ld2412/select/baud_rate_select.cpp +12 -0
  307. esphome/components/ld2412/select/baud_rate_select.h +18 -0
  308. esphome/components/ld2412/select/distance_resolution_select.cpp +12 -0
  309. esphome/components/ld2412/select/distance_resolution_select.h +18 -0
  310. esphome/components/ld2412/select/light_out_control_select.cpp +12 -0
  311. esphome/components/ld2412/select/light_out_control_select.h +18 -0
  312. esphome/components/ld2412/sensor.py +124 -0
  313. esphome/components/ld2412/switch/__init__.py +45 -0
  314. esphome/components/ld2412/switch/bluetooth_switch.cpp +12 -0
  315. esphome/components/ld2412/switch/bluetooth_switch.h +18 -0
  316. esphome/components/ld2412/switch/engineering_mode_switch.cpp +12 -0
  317. esphome/components/ld2412/switch/engineering_mode_switch.h +18 -0
  318. esphome/components/ld2412/text_sensor.py +34 -0
  319. esphome/components/ld2420/ld2420.cpp +0 -1
  320. esphome/components/ld2450/__init__.py +3 -4
  321. esphome/components/ld2450/binary_sensor.py +3 -0
  322. esphome/components/ld2450/ld2450.cpp +77 -165
  323. esphome/components/ld2450/ld2450.h +26 -54
  324. esphome/components/ld2450/sensor.py +120 -6
  325. esphome/components/ld2450/text_sensor.py +5 -4
  326. esphome/components/ld24xx/__init__.py +1 -0
  327. esphome/components/ld24xx/ld24xx.h +65 -0
  328. esphome/components/ledc/ledc_output.cpp +0 -1
  329. esphome/components/libretiny/__init__.py +2 -0
  330. esphome/components/light/__init__.py +0 -1
  331. esphome/components/light/effects.py +70 -45
  332. esphome/components/light/light_call.cpp +101 -66
  333. esphome/components/light/light_color_values.h +16 -11
  334. esphome/components/light/light_json_schema.cpp +46 -44
  335. esphome/components/light/light_state.cpp +8 -11
  336. esphome/components/light/light_traits.h +17 -0
  337. esphome/components/lightwaverf/lightwaverf.cpp +0 -2
  338. esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +0 -1
  339. esphome/components/lock/__init__.py +0 -1
  340. esphome/components/logger/__init__.py +31 -9
  341. esphome/components/logger/logger.cpp +12 -7
  342. esphome/components/logger/logger.h +25 -14
  343. esphome/components/logger/logger_esp32.cpp +2 -7
  344. esphome/components/logger/logger_esp8266.cpp +2 -4
  345. esphome/components/logger/logger_host.cpp +2 -4
  346. esphome/components/logger/logger_libretiny.cpp +2 -4
  347. esphome/components/logger/logger_rp2040.cpp +2 -4
  348. esphome/components/logger/logger_zephyr.cpp +86 -0
  349. esphome/components/logger/select/logger_level_select.cpp +2 -4
  350. esphome/components/logger/select/logger_level_select.h +2 -4
  351. esphome/components/logger/task_log_buffer.cpp +2 -4
  352. esphome/components/logger/task_log_buffer.h +2 -4
  353. esphome/components/lps22/sensor.py +5 -5
  354. esphome/components/ltr390/ltr390.cpp +0 -2
  355. esphome/components/ltr501/ltr501.cpp +0 -1
  356. esphome/components/ltr_als_ps/ltr_als_ps.cpp +0 -1
  357. esphome/components/lvgl/__init__.py +14 -13
  358. esphome/components/lvgl/automation.py +2 -4
  359. esphome/components/lvgl/defines.py +0 -2
  360. esphome/components/lvgl/helpers.py +1 -1
  361. esphome/components/lvgl/lv_validation.py +7 -4
  362. esphome/components/lvgl/lvgl_esphome.cpp +2 -3
  363. esphome/components/lvgl/styles.py +2 -2
  364. esphome/components/lvgl/types.py +1 -1
  365. esphome/components/lvgl/widgets/__init__.py +2 -2
  366. esphome/components/lvgl/widgets/arc.py +14 -11
  367. esphome/components/lvgl/widgets/buttonmatrix.py +1 -1
  368. esphome/components/lvgl/widgets/qrcode.py +7 -7
  369. esphome/components/lvgl/widgets/spinner.py +6 -6
  370. esphome/components/lvgl/widgets/switch.py +2 -2
  371. esphome/components/lvgl/widgets/tabview.py +3 -3
  372. esphome/components/m5stack_8angle/m5stack_8angle.cpp +0 -1
  373. esphome/components/matrix_keypad/__init__.py +4 -3
  374. esphome/components/max17043/max17043.cpp +0 -2
  375. esphome/components/max31855/max31855.cpp +1 -4
  376. esphome/components/max31856/max31856.cpp +0 -4
  377. esphome/components/max31865/max31865.cpp +0 -1
  378. esphome/components/max44009/max44009.cpp +0 -1
  379. esphome/components/max6675/max6675.cpp +1 -4
  380. esphome/components/max6956/max6956.cpp +0 -1
  381. esphome/components/max7219/max7219.cpp +0 -1
  382. esphome/components/max7219digit/display.py +1 -1
  383. esphome/components/max7219digit/max7219digit.cpp +0 -1
  384. esphome/components/max9611/max9611.cpp +0 -1
  385. esphome/components/mcp23008/__init__.py +1 -1
  386. esphome/components/mcp23008/mcp23008.cpp +0 -1
  387. esphome/components/mcp23016/mcp23016.cpp +0 -1
  388. esphome/components/mcp23017/__init__.py +1 -1
  389. esphome/components/mcp23017/mcp23017.cpp +0 -1
  390. esphome/components/mcp23s08/__init__.py +1 -1
  391. esphome/components/mcp23s08/mcp23s08.cpp +0 -1
  392. esphome/components/mcp23s17/__init__.py +1 -1
  393. esphome/components/mcp23s17/mcp23s17.cpp +0 -1
  394. esphome/components/mcp23x08_base/__init__.py +2 -0
  395. esphome/components/mcp23x08_base/mcp23x08_base.cpp +9 -7
  396. esphome/components/mcp23x08_base/mcp23x08_base.h +9 -4
  397. esphome/components/mcp23x17_base/__init__.py +2 -0
  398. esphome/components/mcp23x17_base/mcp23x17_base.cpp +20 -7
  399. esphome/components/mcp23x17_base/mcp23x17_base.h +9 -4
  400. esphome/components/mcp23xxx_base/__init__.py +11 -5
  401. esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +15 -12
  402. esphome/components/mcp23xxx_base/mcp23xxx_base.h +8 -7
  403. esphome/components/mcp3008/mcp3008.cpp +1 -4
  404. esphome/components/mcp3204/mcp3204.cpp +1 -4
  405. esphome/components/mcp4461/mcp4461.cpp +0 -1
  406. esphome/components/mcp4725/mcp4725.cpp +0 -1
  407. esphome/components/mcp4728/mcp4728.cpp +0 -1
  408. esphome/components/mcp9600/mcp9600.cpp +0 -2
  409. esphome/components/mcp9808/mcp9808.cpp +0 -2
  410. esphome/components/mdns/__init__.py +3 -0
  411. esphome/components/mdns/mdns_component.cpp +2 -0
  412. esphome/components/mdns/mdns_component.h +4 -0
  413. esphome/components/media_player/__init__.py +40 -0
  414. esphome/components/media_player/automation.h +16 -0
  415. esphome/components/media_player/media_player.cpp +13 -0
  416. esphome/components/media_player/media_player.h +50 -3
  417. esphome/components/micro_wake_word/micro_wake_word.cpp +0 -3
  418. esphome/components/mics_4514/mics_4514.cpp +1 -6
  419. esphome/components/midea/ir_transmitter.h +4 -4
  420. esphome/components/mipi/__init__.py +416 -0
  421. esphome/components/mipi_dsi/__init__.py +5 -0
  422. esphome/components/mipi_dsi/display.py +233 -0
  423. esphome/components/mipi_dsi/mipi_dsi.cpp +379 -0
  424. esphome/components/mipi_dsi/mipi_dsi.h +123 -0
  425. esphome/components/mipi_dsi/models/__init__.py +0 -0
  426. esphome/components/mipi_dsi/models/guition.py +38 -0
  427. esphome/components/mipi_dsi/models/m5stack.py +57 -0
  428. esphome/components/mipi_dsi/models/waveshare.py +105 -0
  429. esphome/components/mipi_rgb/models/lilygo.py +0 -0
  430. esphome/components/mipi_spi/__init__.py +0 -9
  431. esphome/components/mipi_spi/display.py +220 -256
  432. esphome/components/mipi_spi/mipi_spi.cpp +1 -485
  433. esphome/components/mipi_spi/mipi_spi.h +556 -108
  434. esphome/components/mipi_spi/models/__init__.py +0 -65
  435. esphome/components/mipi_spi/models/adafruit.py +30 -0
  436. esphome/components/mipi_spi/models/amoled.py +41 -5
  437. esphome/components/mipi_spi/models/ili.py +5 -5
  438. esphome/components/mipi_spi/models/jc.py +1 -3
  439. esphome/components/mipi_spi/models/lilygo.py +1 -1
  440. esphome/components/mipi_spi/models/waveshare.py +16 -1
  441. esphome/components/mixer/speaker/__init__.py +4 -5
  442. esphome/components/mlx90393/sensor.py +7 -5
  443. esphome/components/mlx90393/sensor_mlx90393.cpp +0 -1
  444. esphome/components/mlx90614/mlx90614.cpp +0 -1
  445. esphome/components/mmc5603/mmc5603.cpp +0 -1
  446. esphome/components/mmc5983/mmc5983.cpp +0 -2
  447. esphome/components/mpl3115a2/mpl3115a2.cpp +0 -2
  448. esphome/components/mpr121/__init__.py +7 -6
  449. esphome/components/mpr121/mpr121.cpp +0 -1
  450. esphome/components/mpu6050/mpu6050.cpp +0 -1
  451. esphome/components/mpu6886/mpu6886.cpp +0 -1
  452. esphome/components/mqtt/__init__.py +1 -2
  453. esphome/components/mqtt/mqtt_button.cpp +1 -1
  454. esphome/components/mqtt/mqtt_client.cpp +0 -1
  455. esphome/components/mqtt/mqtt_component.cpp +8 -14
  456. esphome/components/mqtt/mqtt_component.h +0 -7
  457. esphome/components/mqtt/mqtt_sensor.cpp +0 -1
  458. esphome/components/mqtt/mqtt_sensor.h +0 -1
  459. esphome/components/mqtt/mqtt_text_sensor.cpp +0 -1
  460. esphome/components/mqtt/mqtt_text_sensor.h +0 -1
  461. esphome/components/ms5611/ms5611.cpp +0 -1
  462. esphome/components/ms8607/ms8607.cpp +0 -1
  463. esphome/components/msa3xx/msa3xx.cpp +0 -2
  464. esphome/components/my9231/my9231.cpp +0 -2
  465. esphome/components/nau7802/nau7802.cpp +0 -1
  466. esphome/components/neopixelbus/light.py +3 -0
  467. esphome/components/network/util.cpp +29 -0
  468. esphome/components/nextion/nextion.cpp +0 -1
  469. esphome/components/nfc/binary_sensor/{binary_sensor.cpp → nfc_binary_sensor.cpp} +1 -1
  470. esphome/components/npi19/npi19.cpp +0 -2
  471. esphome/components/nrf52/__init__.py +223 -0
  472. esphome/components/nrf52/boards.py +34 -0
  473. esphome/components/nrf52/const.py +18 -0
  474. esphome/components/nrf52/gpio.py +79 -0
  475. esphome/components/number/__init__.py +2 -1
  476. esphome/components/one_wire/__init__.py +1 -2
  477. esphome/components/one_wire/one_wire.cpp +0 -2
  478. esphome/components/one_wire/one_wire.h +0 -2
  479. esphome/components/opentherm/hub.cpp +0 -1
  480. esphome/components/opentherm/number/__init__.py +2 -2
  481. esphome/components/openthread/__init__.py +2 -3
  482. esphome/components/openthread/openthread.cpp +30 -13
  483. esphome/components/openthread/openthread.h +3 -0
  484. esphome/components/openthread/openthread_esp.cpp +3 -1
  485. esphome/components/opt3001/sensor.py +2 -6
  486. esphome/components/output/__init__.py +38 -0
  487. esphome/components/output/automation.h +24 -0
  488. esphome/components/output/switch/output_switch.cpp +0 -2
  489. esphome/components/packages/__init__.py +1 -2
  490. esphome/components/packet_transport/__init__.py +4 -3
  491. esphome/components/pca6416a/pca6416a.cpp +0 -1
  492. esphome/components/pca9554/pca9554.cpp +0 -1
  493. esphome/components/pca9685/pca9685_output.cpp +0 -2
  494. esphome/components/pcf85063/pcf85063.cpp +0 -1
  495. esphome/components/pcf8563/pcf8563.cpp +0 -1
  496. esphome/components/pcf8574/pcf8574.cpp +0 -1
  497. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +0 -1
  498. esphome/components/pipsolar/sensor/__init__.py +1 -1
  499. esphome/components/pm2005/pm2005.cpp +0 -1
  500. esphome/components/pmsa003i/pmsa003i.cpp +0 -2
  501. esphome/components/pmwcs3/sensor.py +1 -2
  502. esphome/components/pn532/pn532.cpp +0 -2
  503. esphome/components/pn532_spi/pn532_spi.cpp +0 -2
  504. esphome/components/power_supply/power_supply.cpp +7 -10
  505. esphome/components/power_supply/power_supply.h +1 -1
  506. esphome/components/psram/__init__.py +2 -1
  507. esphome/components/pulse_counter/pulse_counter_sensor.cpp +0 -1
  508. esphome/components/pulse_counter/sensor.py +9 -6
  509. esphome/components/pylontech/pylontech.cpp +0 -1
  510. esphome/components/qmc5883l/qmc5883l.cpp +0 -1
  511. esphome/components/qmp6988/qmp6988.cpp +0 -2
  512. esphome/components/qspi_dbi/display.py +2 -3
  513. esphome/components/qspi_dbi/qspi_dbi.cpp +0 -2
  514. esphome/components/qwiic_pir/binary_sensor.py +2 -3
  515. esphome/components/qwiic_pir/qwiic_pir.cpp +0 -2
  516. esphome/components/rc522/rc522.cpp +9 -31
  517. esphome/components/rc522_spi/rc522_spi.cpp +0 -1
  518. esphome/components/remote_base/__init__.py +5 -6
  519. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -1
  520. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +0 -1
  521. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -1
  522. esphome/components/remote_transmitter/__init__.py +26 -0
  523. esphome/components/remote_transmitter/automation.h +18 -0
  524. esphome/components/remote_transmitter/remote_transmitter.h +2 -1
  525. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -1
  526. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  527. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  528. esphome/components/resampler/speaker/__init__.py +4 -5
  529. esphome/components/rf_bridge/__init__.py +4 -8
  530. esphome/components/rotary_encoder/rotary_encoder.cpp +0 -2
  531. esphome/components/rp2040/__init__.py +3 -1
  532. esphome/components/rp2040_pio_led_strip/led_strip.cpp +0 -2
  533. esphome/components/rp2040_pio_led_strip/light.py +1 -2
  534. esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -5
  535. esphome/components/rpi_dpi_rgb/display.py +13 -15
  536. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -3
  537. esphome/components/runtime_stats/__init__.py +34 -0
  538. esphome/components/runtime_stats/runtime_stats.cpp +102 -0
  539. esphome/components/runtime_stats/runtime_stats.h +132 -0
  540. esphome/components/scd30/scd30.cpp +0 -2
  541. esphome/components/scd30/sensor.py +1 -2
  542. esphome/components/scd4x/scd4x.cpp +0 -1
  543. esphome/components/scd4x/sensor.py +1 -3
  544. esphome/components/sdl/display.py +3 -1
  545. esphome/components/sdl/sdl_esphome.cpp +0 -2
  546. esphome/components/sdp3x/sdp3x.cpp +0 -2
  547. esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +0 -2
  548. esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +0 -3
  549. esphome/components/select/__init__.py +2 -3
  550. esphome/components/select/select_traits.cpp +1 -1
  551. esphome/components/select/select_traits.h +1 -1
  552. esphome/components/sen0321/sen0321.cpp +0 -1
  553. esphome/components/sen5x/sen5x.cpp +0 -2
  554. esphome/components/sensor/__init__.py +36 -4
  555. esphome/components/sensor/filter.cpp +49 -10
  556. esphome/components/sensor/filter.h +22 -7
  557. esphome/components/sensor/sensor.cpp +0 -1
  558. esphome/components/sensor/sensor.h +0 -9
  559. esphome/components/sfa30/sfa30.cpp +0 -4
  560. esphome/components/sgp30/sgp30.cpp +0 -2
  561. esphome/components/sgp4x/sensor.py +1 -1
  562. esphome/components/sgp4x/sgp4x.cpp +0 -2
  563. esphome/components/shelly_dimmer/shelly_dimmer.cpp +0 -2
  564. esphome/components/sht3xd/sht3xd.cpp +0 -2
  565. esphome/components/sht4x/sht4x.cpp +0 -2
  566. esphome/components/shtcx/shtcx.cpp +0 -1
  567. esphome/components/sim800l/__init__.py +2 -4
  568. esphome/components/sm16716/sm16716.cpp +0 -1
  569. esphome/components/sm2135/sm2135.cpp +0 -1
  570. esphome/components/sm2235/sm2235.cpp +0 -1
  571. esphome/components/sm2335/sm2335.cpp +0 -1
  572. esphome/components/sn74hc165/sn74hc165.cpp +0 -1
  573. esphome/components/sn74hc595/sn74hc595.cpp +0 -1
  574. esphome/components/sntp/sntp_component.cpp +0 -1
  575. esphome/components/sound_level/sensor.py +1 -1
  576. esphome/components/speaker/media_player/__init__.py +21 -33
  577. esphome/components/speaker/media_player/audio_pipeline.cpp +4 -7
  578. esphome/components/spi/__init__.py +29 -13
  579. esphome/components/spi/spi.cpp +0 -2
  580. esphome/components/spi_device/spi_device.cpp +1 -4
  581. esphome/components/sprinkler/__init__.py +4 -4
  582. esphome/components/sps30/sps30.cpp +0 -1
  583. esphome/components/ssd1306_base/__init__.py +11 -11
  584. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  585. esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +0 -1
  586. esphome/components/ssd1306_spi/ssd1306_spi.cpp +0 -1
  587. esphome/components/ssd1322_spi/ssd1322_spi.cpp +0 -1
  588. esphome/components/ssd1325_spi/ssd1325_spi.cpp +0 -1
  589. esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +0 -1
  590. esphome/components/ssd1327_spi/ssd1327_spi.cpp +0 -1
  591. esphome/components/ssd1331_spi/ssd1331_spi.cpp +0 -1
  592. esphome/components/ssd1351_spi/ssd1351_spi.cpp +0 -1
  593. esphome/components/st7567_i2c/st7567_i2c.cpp +0 -1
  594. esphome/components/st7567_spi/st7567_spi.cpp +0 -1
  595. esphome/components/st7701s/display.py +10 -14
  596. esphome/components/st7701s/st7701s.cpp +0 -3
  597. esphome/components/st7735/st7735.cpp +0 -1
  598. esphome/components/st7789v/st7789v.cpp +0 -1
  599. esphome/components/st7920/st7920.cpp +0 -1
  600. esphome/components/status_led/light/status_led_light.cpp +0 -2
  601. esphome/components/status_led/status_led.cpp +0 -1
  602. esphome/components/stepper/__init__.py +2 -4
  603. esphome/components/sts3x/sts3x.cpp +0 -1
  604. esphome/components/substitutions/__init__.py +10 -16
  605. esphome/components/substitutions/jinja.py +24 -1
  606. esphome/components/sun/__init__.py +2 -3
  607. esphome/components/switch/__init__.py +31 -1
  608. esphome/components/switch/automation.h +24 -0
  609. esphome/components/switch/switch.cpp +8 -0
  610. esphome/components/switch/switch.h +8 -0
  611. esphome/components/sx126x/sx126x.cpp +0 -2
  612. esphome/components/sx127x/sx127x.cpp +0 -2
  613. esphome/components/sx1509/__init__.py +7 -5
  614. esphome/components/sx1509/output/sx1509_float_output.cpp +1 -1
  615. esphome/components/sx1509/sx1509.cpp +0 -2
  616. esphome/components/syslog/esphome_syslog.cpp +1 -1
  617. esphome/components/tc74/tc74.cpp +0 -1
  618. esphome/components/tca9548a/tca9548a.cpp +0 -1
  619. esphome/components/tca9555/tca9555.cpp +0 -1
  620. esphome/components/tcs34725/tcs34725.cpp +0 -1
  621. esphome/components/tee501/tee501.cpp +0 -1
  622. esphome/components/tem3200/tem3200.cpp +0 -2
  623. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +0 -1
  624. esphome/components/template/cover/template_cover.cpp +0 -1
  625. esphome/components/template/select/template_select.cpp +0 -1
  626. esphome/components/template/text/template_text.cpp +0 -2
  627. esphome/components/template/valve/template_valve.cpp +0 -1
  628. esphome/components/text/__init__.py +0 -1
  629. esphome/components/text/text_traits.h +2 -0
  630. esphome/components/text_sensor/__init__.py +2 -1
  631. esphome/components/text_sensor/text_sensor.cpp +0 -2
  632. esphome/components/text_sensor/text_sensor.h +0 -8
  633. esphome/components/thermostat/climate.py +4 -4
  634. esphome/components/time/__init__.py +7 -4
  635. esphome/components/time/real_time_clock.cpp +16 -3
  636. esphome/components/tlc59208f/tlc59208f_output.cpp +0 -2
  637. esphome/components/tlc5947/tlc5947.cpp +0 -2
  638. esphome/components/tlc5971/tlc5971.cpp +0 -2
  639. esphome/components/tm1621/tm1621.cpp +0 -2
  640. esphome/components/tm1637/tm1637.cpp +0 -2
  641. esphome/components/tm1638/tm1638.cpp +0 -2
  642. esphome/components/tm1651/__init__.py +45 -48
  643. esphome/components/tm1651/tm1651.cpp +213 -47
  644. esphome/components/tm1651/tm1651.h +37 -32
  645. esphome/components/tmp117/tmp117.cpp +0 -2
  646. esphome/components/tsl2561/tsl2561.cpp +0 -1
  647. esphome/components/tsl2591/tsl2591.cpp +0 -1
  648. esphome/components/tt21100/touchscreen/tt21100.cpp +0 -2
  649. esphome/components/ttp229_bsf/ttp229_bsf.cpp +0 -1
  650. esphome/components/ttp229_lsf/ttp229_lsf.cpp +0 -1
  651. esphome/components/tuya/climate/__init__.py +9 -10
  652. esphome/components/tuya/number/__init__.py +8 -6
  653. esphome/components/tx20/tx20.cpp +0 -1
  654. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -1
  655. esphome/components/uart/uart_component_esp8266.cpp +0 -1
  656. esphome/components/uart/uart_component_esp_idf.cpp +0 -2
  657. esphome/components/uart/uart_component_libretiny.cpp +0 -2
  658. esphome/components/uart/uart_component_rp2040.cpp +0 -2
  659. esphome/components/udp/__init__.py +1 -1
  660. esphome/components/ufire_ec/sensor.py +1 -2
  661. esphome/components/ufire_ec/ufire_ec.cpp +0 -2
  662. esphome/components/ufire_ise/sensor.py +1 -2
  663. esphome/components/ufire_ise/ufire_ise.cpp +0 -2
  664. esphome/components/ultrasonic/ultrasonic_sensor.cpp +0 -1
  665. esphome/components/update/__init__.py +0 -1
  666. esphome/components/uptime/sensor/uptime_seconds_sensor.cpp +0 -1
  667. esphome/components/uptime/sensor/uptime_seconds_sensor.h +0 -2
  668. esphome/components/usb_host/usb_host_client.cpp +0 -1
  669. esphome/components/usb_host/usb_host_component.cpp +0 -1
  670. esphome/components/valve/__init__.py +0 -1
  671. esphome/components/veml3235/veml3235.cpp +0 -3
  672. esphome/components/veml7700/veml7700.cpp +0 -2
  673. esphome/components/version/version_text_sensor.cpp +0 -1
  674. esphome/components/version/version_text_sensor.h +0 -1
  675. esphome/components/vl53l0x/vl53l0x_sensor.cpp +0 -4
  676. esphome/components/voice_assistant/voice_assistant.cpp +9 -8
  677. esphome/components/web_server/__init__.py +13 -0
  678. esphome/components/web_server/web_server.cpp +187 -352
  679. esphome/components/web_server/web_server.h +61 -1
  680. esphome/components/web_server_base/__init__.py +1 -1
  681. esphome/components/web_server_base/web_server_base.cpp +2 -0
  682. esphome/components/web_server_base/web_server_base.h +6 -0
  683. esphome/components/web_server_idf/web_server_idf.cpp +10 -8
  684. esphome/components/web_server_idf/web_server_idf.h +2 -0
  685. esphome/components/weikai_i2c/weikai_i2c.cpp +1 -2
  686. esphome/components/weikai_spi/weikai_spi.cpp +1 -1
  687. esphome/components/wifi/__init__.py +8 -43
  688. esphome/components/wifi/wifi_component.cpp +100 -36
  689. esphome/components/wifi/wifi_component.h +5 -1
  690. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -0
  691. esphome/components/wifi/wifi_component_esp_idf.cpp +30 -0
  692. esphome/components/wifi_info/wifi_info_text_sensor.h +0 -6
  693. esphome/components/wifi_signal/wifi_signal_sensor.h +0 -1
  694. esphome/components/wireguard/wireguard.cpp +0 -2
  695. esphome/components/x9c/x9c.cpp +0 -2
  696. esphome/components/xgzp68xx/xgzp68xx.cpp +0 -1
  697. esphome/components/xl9535/xl9535.cpp +0 -2
  698. esphome/components/zephyr/__init__.py +252 -0
  699. esphome/components/zephyr/const.py +16 -0
  700. esphome/components/zephyr/core.cpp +90 -0
  701. esphome/components/zephyr/gpio.cpp +120 -0
  702. esphome/components/zephyr/gpio.h +38 -0
  703. esphome/components/zephyr/pre_build.py.script +4 -0
  704. esphome/components/zephyr/preferences.cpp +156 -0
  705. esphome/components/zephyr/preferences.h +13 -0
  706. esphome/config.py +38 -16
  707. esphome/config_helpers.py +1 -2
  708. esphome/config_validation.py +8 -15
  709. esphome/const.py +26 -1
  710. esphome/core/__init__.py +88 -51
  711. esphome/core/application.cpp +75 -21
  712. esphome/core/application.h +106 -171
  713. esphome/core/color.h +10 -0
  714. esphome/core/component.cpp +41 -25
  715. esphome/core/component.h +9 -6
  716. esphome/core/component_iterator.cpp +61 -261
  717. esphome/core/component_iterator.h +15 -0
  718. esphome/core/config.py +26 -11
  719. esphome/core/defines.h +40 -2
  720. esphome/core/entity_base.h +18 -0
  721. esphome/core/entity_helpers.py +41 -6
  722. esphome/core/helpers.cpp +8 -15
  723. esphome/core/helpers.h +60 -6
  724. esphome/core/lock_free_queue.h +1 -1
  725. esphome/core/scheduler.cpp +277 -74
  726. esphome/core/scheduler.h +89 -27
  727. esphome/cpp_generator.py +2 -6
  728. esphome/cpp_helpers.py +1 -1
  729. esphome/dashboard/dashboard.py +2 -3
  730. esphome/dashboard/dns.py +2 -8
  731. esphome/dashboard/web_server.py +34 -19
  732. esphome/espota2.py +1 -4
  733. esphome/git.py +3 -1
  734. esphome/helpers.py +23 -4
  735. esphome/log.py +3 -1
  736. esphome/mqtt.py +3 -5
  737. esphome/platformio_api.py +7 -4
  738. esphome/types.py +12 -0
  739. esphome/util.py +20 -8
  740. esphome/voluptuous_schema.py +4 -3
  741. esphome/vscode.py +1 -2
  742. esphome/wizard.py +1 -4
  743. esphome/writer.py +5 -107
  744. esphome/yaml_util.py +7 -5
  745. {esphome-2025.7.5.dist-info → esphome-2025.8.0b1.dist-info}/METADATA +12 -13
  746. {esphome-2025.7.5.dist-info → esphome-2025.8.0b1.dist-info}/RECORD +751 -671
  747. esphome/components/mipi_spi/models/commands.py +0 -82
  748. /esphome/components/nfc/binary_sensor/{binary_sensor.h → nfc_binary_sensor.h} +0 -0
  749. {esphome-2025.7.5.dist-info → esphome-2025.8.0b1.dist-info}/WHEEL +0 -0
  750. {esphome-2025.7.5.dist-info → esphome-2025.8.0b1.dist-info}/entry_points.txt +0 -0
  751. {esphome-2025.7.5.dist-info → esphome-2025.8.0b1.dist-info}/licenses/LICENSE +0 -0
  752. {esphome-2025.7.5.dist-info → esphome-2025.8.0b1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,11 @@
1
1
  #include "api_connection.h"
2
2
  #ifdef USE_API
3
+ #ifdef USE_API_NOISE
4
+ #include "api_frame_helper_noise.h"
5
+ #endif
6
+ #ifdef USE_API_PLAINTEXT
7
+ #include "api_frame_helper_plaintext.h"
8
+ #endif
3
9
  #include <cerrno>
4
10
  #include <cinttypes>
5
11
  #include <utility>
@@ -25,8 +31,7 @@
25
31
  #include "esphome/components/voice_assistant/voice_assistant.h"
26
32
  #endif
27
33
 
28
- namespace esphome {
29
- namespace api {
34
+ namespace esphome::api {
30
35
 
31
36
  // Read a maximum of 5 messages per loop iteration to prevent starving other components.
32
37
  // This is a balance between API responsiveness and allowing other components to run.
@@ -42,32 +47,53 @@ static const char *const TAG = "api.connection";
42
47
  static const int CAMERA_STOP_STREAM = 5000;
43
48
  #endif
44
49
 
45
- // Helper macro for entity command handlers - gets entity by key, returns if not found, and creates call object
50
+ #ifdef USE_DEVICES
51
+ // Helper macro for entity command handlers - gets entity by key and device_id, returns if not found, and creates call
52
+ // object
53
+ #define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
54
+ entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
55
+ if ((entity_var) == nullptr) \
56
+ return; \
57
+ auto call = (entity_var)->make_call();
58
+
59
+ // Helper macro for entity command handlers that don't use make_call() - gets entity by key and device_id and returns if
60
+ // not found
61
+ #define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
62
+ entity_type *entity_var = App.get_##getter_name##_by_key(msg.key, msg.device_id); \
63
+ if ((entity_var) == nullptr) \
64
+ return;
65
+ #else // No device support, use simpler macros
66
+ // Helper macro for entity command handlers - gets entity by key, returns if not found, and creates call
67
+ // object
46
68
  #define ENTITY_COMMAND_MAKE_CALL(entity_type, entity_var, getter_name) \
47
69
  entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
48
70
  if ((entity_var) == nullptr) \
49
71
  return; \
50
72
  auto call = (entity_var)->make_call();
51
73
 
52
- // Helper macro for entity command handlers that don't use make_call() - gets entity by key and returns if not found
74
+ // Helper macro for entity command handlers that don't use make_call() - gets entity by key and returns if
75
+ // not found
53
76
  #define ENTITY_COMMAND_GET(entity_type, entity_var, getter_name) \
54
77
  entity_type *entity_var = App.get_##getter_name##_by_key(msg.key); \
55
78
  if ((entity_var) == nullptr) \
56
79
  return;
80
+ #endif // USE_DEVICES
57
81
 
58
82
  APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
59
83
  : parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
60
84
  #if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
61
85
  auto noise_ctx = parent->get_noise_ctx();
62
86
  if (noise_ctx->has_psk()) {
63
- this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
87
+ this->helper_ =
88
+ std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx, &this->client_info_)};
64
89
  } else {
65
- this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
90
+ this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
66
91
  }
67
92
  #elif defined(USE_API_PLAINTEXT)
68
- this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
93
+ this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
69
94
  #elif defined(USE_API_NOISE)
70
- this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
95
+ this->helper_ = std::unique_ptr<APIFrameHelper>{
96
+ new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx(), &this->client_info_)};
71
97
  #else
72
98
  #error "No frame helper defined"
73
99
  #endif
@@ -86,13 +112,11 @@ void APIConnection::start() {
86
112
  APIError err = this->helper_->init();
87
113
  if (err != APIError::OK) {
88
114
  on_fatal_error();
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);
115
+ this->log_warning_("Helper init failed", err);
91
116
  return;
92
117
  }
93
- this->client_info_ = helper_->getpeername();
94
- this->client_peername_ = this->client_info_;
95
- this->helper_->set_log_info(this->client_info_);
118
+ this->client_info_.peername = helper_->getpeername();
119
+ this->client_info_.name = this->client_info_.peername;
96
120
  }
97
121
 
98
122
  APIConnection::~APIConnection() {
@@ -119,8 +143,7 @@ void APIConnection::loop() {
119
143
  APIError err = this->helper_->loop();
120
144
  if (err != APIError::OK) {
121
145
  on_fatal_error();
122
- ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
123
- api_error_to_str(err), errno);
146
+ this->log_socket_operation_failed_(err);
124
147
  return;
125
148
  }
126
149
 
@@ -136,14 +159,7 @@ void APIConnection::loop() {
136
159
  break;
137
160
  } else if (err != APIError::OK) {
138
161
  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
- }
162
+ this->log_warning_("Reading failed", err);
147
163
  return;
148
164
  } else {
149
165
  this->last_traffic_ = now;
@@ -186,9 +202,11 @@ void APIConnection::loop() {
186
202
  on_fatal_error();
187
203
  ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
188
204
  }
189
- } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
205
+ } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && !this->flags_.remove) {
206
+ // Only send ping if we're not disconnecting
190
207
  ESP_LOGVV(TAG, "Sending keepalive PING");
191
- this->flags_.sent_ping = this->send_message(PingRequest());
208
+ PingRequest req;
209
+ this->flags_.sent_ping = this->send_message(req, PingRequest::MESSAGE_TYPE);
192
210
  if (!this->flags_.sent_ping) {
193
211
  // If we can't send the ping request directly (tx_buffer full),
194
212
  // schedule it at the front of the batch so it will be sent with priority
@@ -203,24 +221,16 @@ void APIConnection::loop() {
203
221
  if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
204
222
  uint32_t to_send = std::min((size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
205
223
  bool done = this->image_reader_->available() == to_send;
206
- uint32_t msg_size = 0;
207
- ProtoSize::add_fixed_field<4>(msg_size, 1, true);
208
- // partial message size calculated manually since its a special case
209
- // 1 for the data field, varint for the data size, and the data itself
210
- msg_size += 1 + ProtoSize::varint(to_send) + to_send;
211
- ProtoSize::add_bool_field(msg_size, 1, done);
212
-
213
- auto buffer = this->create_buffer(msg_size);
214
- // fixed32 key = 1;
215
- buffer.encode_fixed32(1, camera::Camera::instance()->get_object_id_hash());
216
- // bytes data = 2;
217
- buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send);
218
- // bool done = 3;
219
- buffer.encode_bool(3, done);
220
-
221
- bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
222
-
223
- if (success) {
224
+
225
+ CameraImageResponse msg;
226
+ msg.key = camera::Camera::instance()->get_object_id_hash();
227
+ msg.set_data(this->image_reader_->peek_data_buffer(), to_send);
228
+ msg.done = done;
229
+ #ifdef USE_DEVICES
230
+ msg.device_id = camera::Camera::instance()->get_device_id();
231
+ #endif
232
+
233
+ if (this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) {
224
234
  this->image_reader_->consume_data(to_send);
225
235
  if (done) {
226
236
  this->image_reader_->return_image();
@@ -229,35 +239,21 @@ void APIConnection::loop() {
229
239
  }
230
240
  #endif
231
241
 
242
+ #ifdef USE_API_HOMEASSISTANT_STATES
232
243
  if (state_subs_at_ >= 0) {
233
- const auto &subs = this->parent_->get_state_subs();
234
- if (state_subs_at_ < static_cast<int>(subs.size())) {
235
- auto &it = subs[state_subs_at_];
236
- SubscribeHomeAssistantStateResponse resp;
237
- resp.entity_id = it.entity_id;
238
- resp.attribute = it.attribute.value();
239
- resp.once = it.once;
240
- if (this->send_message(resp)) {
241
- state_subs_at_++;
242
- }
243
- } else {
244
- state_subs_at_ = -1;
245
- }
244
+ this->process_state_subscriptions_();
246
245
  }
246
+ #endif
247
247
  }
248
248
 
249
- std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
250
- return App.get_name() + component_type + entity->get_object_id();
251
- }
252
-
253
- DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
249
+ bool APIConnection::send_disconnect_response(const DisconnectRequest &msg) {
254
250
  // remote initiated disconnect_client
255
251
  // don't close yet, we still need to send the disconnect response
256
252
  // close will happen on next loop
257
253
  ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
258
254
  this->flags_.next_close = true;
259
255
  DisconnectResponse resp;
260
- return resp;
256
+ return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
261
257
  }
262
258
  void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
263
259
  this->helper_->close();
@@ -277,8 +273,9 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
277
273
  #endif
278
274
 
279
275
  // Calculate size
280
- uint32_t calculated_size = 0;
281
- msg.calculate_size(calculated_size);
276
+ ProtoSize size_calc;
277
+ msg.calculate_size(size_calc);
278
+ uint32_t calculated_size = size_calc.get_size();
282
279
 
283
280
  // Cache frame sizes to avoid repeated virtual calls
284
281
  const uint8_t header_padding = conn->helper_->frame_header_padding();
@@ -326,19 +323,18 @@ uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConn
326
323
  BinarySensorStateResponse resp;
327
324
  resp.state = binary_sensor->state;
328
325
  resp.missing_state = !binary_sensor->has_state();
329
- fill_entity_state_base(binary_sensor, resp);
330
- return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
326
+ return fill_and_encode_entity_state(binary_sensor, resp, BinarySensorStateResponse::MESSAGE_TYPE, conn,
327
+ remaining_size, is_single);
331
328
  }
332
329
 
333
330
  uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
334
331
  bool is_single) {
335
332
  auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
336
333
  ListEntitiesBinarySensorResponse msg;
337
- msg.device_class = binary_sensor->get_device_class();
334
+ msg.set_device_class(binary_sensor->get_device_class_ref());
338
335
  msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
339
- msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
340
- fill_entity_info_base(binary_sensor, msg);
341
- return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
336
+ return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
337
+ remaining_size, is_single);
342
338
  }
343
339
  #endif
344
340
 
@@ -352,14 +348,11 @@ uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *
352
348
  auto *cover = static_cast<cover::Cover *>(entity);
353
349
  CoverStateResponse msg;
354
350
  auto traits = cover->get_traits();
355
- msg.legacy_state =
356
- (cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
357
351
  msg.position = cover->position;
358
352
  if (traits.get_supports_tilt())
359
353
  msg.tilt = cover->tilt;
360
354
  msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
361
- fill_entity_state_base(cover, msg);
362
- return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
355
+ return fill_and_encode_entity_state(cover, msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
363
356
  }
364
357
  uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
365
358
  bool is_single) {
@@ -370,26 +363,12 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
370
363
  msg.supports_position = traits.get_supports_position();
371
364
  msg.supports_tilt = traits.get_supports_tilt();
372
365
  msg.supports_stop = traits.get_supports_stop();
373
- msg.device_class = cover->get_device_class();
374
- msg.unique_id = get_default_unique_id("cover", cover);
375
- fill_entity_info_base(cover, msg);
376
- return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
366
+ msg.set_device_class(cover->get_device_class_ref());
367
+ return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size,
368
+ is_single);
377
369
  }
378
370
  void APIConnection::cover_command(const CoverCommandRequest &msg) {
379
371
  ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
380
- if (msg.has_legacy_command) {
381
- switch (msg.legacy_command) {
382
- case enums::LEGACY_COVER_COMMAND_OPEN:
383
- call.set_command_open();
384
- break;
385
- case enums::LEGACY_COVER_COMMAND_CLOSE:
386
- call.set_command_close();
387
- break;
388
- case enums::LEGACY_COVER_COMMAND_STOP:
389
- call.set_command_stop();
390
- break;
391
- }
392
- }
393
372
  if (msg.has_position)
394
373
  call.set_position(msg.position);
395
374
  if (msg.has_tilt)
@@ -419,9 +398,8 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co
419
398
  if (traits.supports_direction())
420
399
  msg.direction = static_cast<enums::FanDirection>(fan->direction);
421
400
  if (traits.supports_preset_modes())
422
- msg.preset_mode = fan->preset_mode;
423
- fill_entity_state_base(fan, msg);
424
- return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
401
+ msg.set_preset_mode(StringRef(fan->preset_mode));
402
+ return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
425
403
  }
426
404
  uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
427
405
  bool is_single) {
@@ -432,11 +410,8 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
432
410
  msg.supports_speed = traits.supports_speed();
433
411
  msg.supports_direction = traits.supports_direction();
434
412
  msg.supported_speed_count = traits.supported_speed_count();
435
- for (auto const &preset : traits.supported_preset_modes())
436
- msg.supported_preset_modes.push_back(preset);
437
- msg.unique_id = get_default_unique_id("fan", fan);
438
- fill_entity_info_base(fan, msg);
439
- return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
413
+ msg.supported_preset_modes = &traits.supported_preset_modes_for_api_();
414
+ return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
440
415
  }
441
416
  void APIConnection::fan_command(const FanCommandRequest &msg) {
442
417
  ENTITY_COMMAND_MAKE_CALL(fan::Fan, fan, fan)
@@ -479,26 +454,21 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *
479
454
  resp.color_temperature = values.get_color_temperature();
480
455
  resp.cold_white = values.get_cold_white();
481
456
  resp.warm_white = values.get_warm_white();
482
- if (light->supports_effects())
483
- resp.effect = light->get_effect_name();
484
- fill_entity_state_base(light, resp);
485
- return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
457
+ if (light->supports_effects()) {
458
+ // get_effect_name() returns temporary std::string - must store it
459
+ std::string effect_name = light->get_effect_name();
460
+ resp.set_effect(StringRef(effect_name));
461
+ }
462
+ return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
486
463
  }
487
464
  uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
488
465
  bool is_single) {
489
466
  auto *light = static_cast<light::LightState *>(entity);
490
467
  ListEntitiesLightResponse msg;
491
468
  auto traits = light->get_traits();
492
- for (auto mode : traits.get_supported_color_modes())
493
- msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
494
- msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
495
- msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
496
- msg.legacy_supports_white_value =
497
- msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
498
- traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
499
- msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
500
- traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
501
- if (msg.legacy_supports_color_temperature) {
469
+ msg.supported_color_modes = &traits.get_supported_color_modes_for_api_();
470
+ if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
471
+ traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) {
502
472
  msg.min_mireds = traits.get_min_mireds();
503
473
  msg.max_mireds = traits.get_max_mireds();
504
474
  }
@@ -508,9 +478,8 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
508
478
  msg.effects.push_back(effect->get_name());
509
479
  }
510
480
  }
511
- msg.unique_id = get_default_unique_id("light", light);
512
- fill_entity_info_base(light, msg);
513
- return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
481
+ return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size,
482
+ is_single);
514
483
  }
515
484
  void APIConnection::light_command(const LightCommandRequest &msg) {
516
485
  ENTITY_COMMAND_MAKE_CALL(light::LightState, light, light)
@@ -557,24 +526,20 @@ uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection
557
526
  SensorStateResponse resp;
558
527
  resp.state = sensor->state;
559
528
  resp.missing_state = !sensor->has_state();
560
- fill_entity_state_base(sensor, resp);
561
- return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
529
+ return fill_and_encode_entity_state(sensor, resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
562
530
  }
563
531
 
564
532
  uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
565
533
  bool is_single) {
566
534
  auto *sensor = static_cast<sensor::Sensor *>(entity);
567
535
  ListEntitiesSensorResponse msg;
568
- msg.unit_of_measurement = sensor->get_unit_of_measurement();
536
+ msg.set_unit_of_measurement(sensor->get_unit_of_measurement_ref());
569
537
  msg.accuracy_decimals = sensor->get_accuracy_decimals();
570
538
  msg.force_update = sensor->get_force_update();
571
- msg.device_class = sensor->get_device_class();
539
+ msg.set_device_class(sensor->get_device_class_ref());
572
540
  msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
573
- msg.unique_id = sensor->unique_id();
574
- if (msg.unique_id.empty())
575
- msg.unique_id = get_default_unique_id("sensor", sensor);
576
- fill_entity_info_base(sensor, msg);
577
- return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
541
+ return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size,
542
+ is_single);
578
543
  }
579
544
  #endif
580
545
 
@@ -589,8 +554,8 @@ uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection
589
554
  auto *a_switch = static_cast<switch_::Switch *>(entity);
590
555
  SwitchStateResponse resp;
591
556
  resp.state = a_switch->state;
592
- fill_entity_state_base(a_switch, resp);
593
- return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
557
+ return fill_and_encode_entity_state(a_switch, resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size,
558
+ is_single);
594
559
  }
595
560
 
596
561
  uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -598,10 +563,9 @@ uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *
598
563
  auto *a_switch = static_cast<switch_::Switch *>(entity);
599
564
  ListEntitiesSwitchResponse msg;
600
565
  msg.assumed_state = a_switch->assumed_state();
601
- msg.device_class = a_switch->get_device_class();
602
- msg.unique_id = get_default_unique_id("switch", a_switch);
603
- fill_entity_info_base(a_switch, msg);
604
- return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
566
+ msg.set_device_class(a_switch->get_device_class_ref());
567
+ return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size,
568
+ is_single);
605
569
  }
606
570
  void APIConnection::switch_command(const SwitchCommandRequest &msg) {
607
571
  ENTITY_COMMAND_GET(switch_::Switch, a_switch, switch)
@@ -624,21 +588,18 @@ uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnec
624
588
  bool is_single) {
625
589
  auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
626
590
  TextSensorStateResponse resp;
627
- resp.state = text_sensor->state;
591
+ resp.set_state(StringRef(text_sensor->state));
628
592
  resp.missing_state = !text_sensor->has_state();
629
- fill_entity_state_base(text_sensor, resp);
630
- return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
593
+ return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size,
594
+ is_single);
631
595
  }
632
596
  uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
633
597
  bool is_single) {
634
598
  auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
635
599
  ListEntitiesTextSensorResponse msg;
636
- msg.device_class = text_sensor->get_device_class();
637
- msg.unique_id = text_sensor->unique_id();
638
- if (msg.unique_id.empty())
639
- msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
640
- fill_entity_info_base(text_sensor, msg);
641
- return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
600
+ msg.set_device_class(text_sensor->get_device_class_ref());
601
+ return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
602
+ remaining_size, is_single);
642
603
  }
643
604
  #endif
644
605
 
@@ -651,7 +612,6 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
651
612
  bool is_single) {
652
613
  auto *climate = static_cast<climate::Climate *>(entity);
653
614
  ClimateStateResponse resp;
654
- fill_entity_state_base(climate, resp);
655
615
  auto traits = climate->get_traits();
656
616
  resp.mode = static_cast<enums::ClimateMode>(climate->mode);
657
617
  resp.action = static_cast<enums::ClimateAction>(climate->action);
@@ -665,20 +625,23 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
665
625
  }
666
626
  if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
667
627
  resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
668
- if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
669
- resp.custom_fan_mode = climate->custom_fan_mode.value();
628
+ if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) {
629
+ resp.set_custom_fan_mode(StringRef(climate->custom_fan_mode.value()));
630
+ }
670
631
  if (traits.get_supports_presets() && climate->preset.has_value()) {
671
632
  resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
672
633
  }
673
- if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
674
- resp.custom_preset = climate->custom_preset.value();
634
+ if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) {
635
+ resp.set_custom_preset(StringRef(climate->custom_preset.value()));
636
+ }
675
637
  if (traits.get_supports_swing_modes())
676
638
  resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
677
639
  if (traits.get_supports_current_humidity())
678
640
  resp.current_humidity = climate->current_humidity;
679
641
  if (traits.get_supports_target_humidity())
680
642
  resp.target_humidity = climate->target_humidity;
681
- return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
643
+ return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size,
644
+ is_single);
682
645
  }
683
646
  uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
684
647
  bool is_single) {
@@ -689,29 +652,21 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
689
652
  msg.supports_current_humidity = traits.get_supports_current_humidity();
690
653
  msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
691
654
  msg.supports_target_humidity = traits.get_supports_target_humidity();
692
- for (auto mode : traits.get_supported_modes())
693
- msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
655
+ msg.supported_modes = &traits.get_supported_modes_for_api_();
694
656
  msg.visual_min_temperature = traits.get_visual_min_temperature();
695
657
  msg.visual_max_temperature = traits.get_visual_max_temperature();
696
658
  msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
697
659
  msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
698
660
  msg.visual_min_humidity = traits.get_visual_min_humidity();
699
661
  msg.visual_max_humidity = traits.get_visual_max_humidity();
700
- msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
701
662
  msg.supports_action = traits.get_supports_action();
702
- for (auto fan_mode : traits.get_supported_fan_modes())
703
- msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
704
- for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
705
- msg.supported_custom_fan_modes.push_back(custom_fan_mode);
706
- for (auto preset : traits.get_supported_presets())
707
- msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
708
- for (auto const &custom_preset : traits.get_supported_custom_presets())
709
- msg.supported_custom_presets.push_back(custom_preset);
710
- for (auto swing_mode : traits.get_supported_swing_modes())
711
- msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
712
- msg.unique_id = get_default_unique_id("climate", climate);
713
- fill_entity_info_base(climate, msg);
714
- return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
663
+ msg.supported_fan_modes = &traits.get_supported_fan_modes_for_api_();
664
+ msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes_for_api_();
665
+ msg.supported_presets = &traits.get_supported_presets_for_api_();
666
+ msg.supported_custom_presets = &traits.get_supported_custom_presets_for_api_();
667
+ msg.supported_swing_modes = &traits.get_supported_swing_modes_for_api_();
668
+ return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size,
669
+ is_single);
715
670
  }
716
671
  void APIConnection::climate_command(const ClimateCommandRequest &msg) {
717
672
  ENTITY_COMMAND_MAKE_CALL(climate::Climate, climate, climate)
@@ -751,23 +706,21 @@ uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection
751
706
  NumberStateResponse resp;
752
707
  resp.state = number->state;
753
708
  resp.missing_state = !number->has_state();
754
- fill_entity_state_base(number, resp);
755
- return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
709
+ return fill_and_encode_entity_state(number, resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
756
710
  }
757
711
 
758
712
  uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
759
713
  bool is_single) {
760
714
  auto *number = static_cast<number::Number *>(entity);
761
715
  ListEntitiesNumberResponse msg;
762
- msg.unit_of_measurement = number->traits.get_unit_of_measurement();
716
+ msg.set_unit_of_measurement(number->traits.get_unit_of_measurement_ref());
763
717
  msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
764
- msg.device_class = number->traits.get_device_class();
718
+ msg.set_device_class(number->traits.get_device_class_ref());
765
719
  msg.min_value = number->traits.get_min_value();
766
720
  msg.max_value = number->traits.get_max_value();
767
721
  msg.step = number->traits.get_step();
768
- msg.unique_id = get_default_unique_id("number", number);
769
- fill_entity_info_base(number, msg);
770
- return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
722
+ return fill_and_encode_entity_info(number, msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size,
723
+ is_single);
771
724
  }
772
725
  void APIConnection::number_command(const NumberCommandRequest &msg) {
773
726
  ENTITY_COMMAND_MAKE_CALL(number::Number, number, number)
@@ -789,16 +742,14 @@ uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *c
789
742
  resp.year = date->year;
790
743
  resp.month = date->month;
791
744
  resp.day = date->day;
792
- fill_entity_state_base(date, resp);
793
- return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
745
+ return fill_and_encode_entity_state(date, resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
794
746
  }
795
747
  uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
796
748
  bool is_single) {
797
749
  auto *date = static_cast<datetime::DateEntity *>(entity);
798
750
  ListEntitiesDateResponse msg;
799
- msg.unique_id = get_default_unique_id("date", date);
800
- fill_entity_info_base(date, msg);
801
- return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
751
+ return fill_and_encode_entity_info(date, msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size,
752
+ is_single);
802
753
  }
803
754
  void APIConnection::date_command(const DateCommandRequest &msg) {
804
755
  ENTITY_COMMAND_MAKE_CALL(datetime::DateEntity, date, date)
@@ -820,16 +771,14 @@ uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *c
820
771
  resp.hour = time->hour;
821
772
  resp.minute = time->minute;
822
773
  resp.second = time->second;
823
- fill_entity_state_base(time, resp);
824
- return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
774
+ return fill_and_encode_entity_state(time, resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
825
775
  }
826
776
  uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
827
777
  bool is_single) {
828
778
  auto *time = static_cast<datetime::TimeEntity *>(entity);
829
779
  ListEntitiesTimeResponse msg;
830
- msg.unique_id = get_default_unique_id("time", time);
831
- fill_entity_info_base(time, msg);
832
- return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
780
+ return fill_and_encode_entity_info(time, msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size,
781
+ is_single);
833
782
  }
834
783
  void APIConnection::time_command(const TimeCommandRequest &msg) {
835
784
  ENTITY_COMMAND_MAKE_CALL(datetime::TimeEntity, time, time)
@@ -852,16 +801,15 @@ uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnectio
852
801
  ESPTime state = datetime->state_as_esptime();
853
802
  resp.epoch_seconds = state.timestamp;
854
803
  }
855
- fill_entity_state_base(datetime, resp);
856
- return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
804
+ return fill_and_encode_entity_state(datetime, resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size,
805
+ is_single);
857
806
  }
858
807
  uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
859
808
  bool is_single) {
860
809
  auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
861
810
  ListEntitiesDateTimeResponse msg;
862
- msg.unique_id = get_default_unique_id("datetime", datetime);
863
- fill_entity_info_base(datetime, msg);
864
- return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
811
+ return fill_and_encode_entity_info(datetime, msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size,
812
+ is_single);
865
813
  }
866
814
  void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
867
815
  ENTITY_COMMAND_MAKE_CALL(datetime::DateTimeEntity, datetime, datetime)
@@ -880,10 +828,9 @@ uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *c
880
828
  bool is_single) {
881
829
  auto *text = static_cast<text::Text *>(entity);
882
830
  TextStateResponse resp;
883
- resp.state = text->state;
831
+ resp.set_state(StringRef(text->state));
884
832
  resp.missing_state = !text->has_state();
885
- fill_entity_state_base(text, resp);
886
- return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
833
+ return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
887
834
  }
888
835
 
889
836
  uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -893,10 +840,9 @@ uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *co
893
840
  msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
894
841
  msg.min_length = text->traits.get_min_length();
895
842
  msg.max_length = text->traits.get_max_length();
896
- msg.pattern = text->traits.get_pattern();
897
- msg.unique_id = get_default_unique_id("text", text);
898
- fill_entity_info_base(text, msg);
899
- return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
843
+ msg.set_pattern(text->traits.get_pattern_ref());
844
+ return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size,
845
+ is_single);
900
846
  }
901
847
  void APIConnection::text_command(const TextCommandRequest &msg) {
902
848
  ENTITY_COMMAND_MAKE_CALL(text::Text, text, text)
@@ -915,21 +861,18 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection
915
861
  bool is_single) {
916
862
  auto *select = static_cast<select::Select *>(entity);
917
863
  SelectStateResponse resp;
918
- resp.state = select->state;
864
+ resp.set_state(StringRef(select->state));
919
865
  resp.missing_state = !select->has_state();
920
- fill_entity_state_base(select, resp);
921
- return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
866
+ return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
922
867
  }
923
868
 
924
869
  uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
925
870
  bool is_single) {
926
871
  auto *select = static_cast<select::Select *>(entity);
927
872
  ListEntitiesSelectResponse msg;
928
- for (const auto &option : select->traits.get_options())
929
- msg.options.push_back(option);
930
- msg.unique_id = get_default_unique_id("select", select);
931
- fill_entity_info_base(select, msg);
932
- return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
873
+ msg.options = &select->traits.get_options();
874
+ return fill_and_encode_entity_info(select, msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size,
875
+ is_single);
933
876
  }
934
877
  void APIConnection::select_command(const SelectCommandRequest &msg) {
935
878
  ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
@@ -943,10 +886,9 @@ uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *
943
886
  bool is_single) {
944
887
  auto *button = static_cast<button::Button *>(entity);
945
888
  ListEntitiesButtonResponse msg;
946
- msg.device_class = button->get_device_class();
947
- msg.unique_id = get_default_unique_id("button", button);
948
- fill_entity_info_base(button, msg);
949
- return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
889
+ msg.set_device_class(button->get_device_class_ref());
890
+ return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size,
891
+ is_single);
950
892
  }
951
893
  void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
952
894
  ENTITY_COMMAND_GET(button::Button, button, button)
@@ -965,8 +907,7 @@ uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *c
965
907
  auto *a_lock = static_cast<lock::Lock *>(entity);
966
908
  LockStateResponse resp;
967
909
  resp.state = static_cast<enums::LockState>(a_lock->state);
968
- fill_entity_state_base(a_lock, resp);
969
- return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
910
+ return fill_and_encode_entity_state(a_lock, resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
970
911
  }
971
912
 
972
913
  uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -976,9 +917,8 @@ uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *co
976
917
  msg.assumed_state = a_lock->traits.get_assumed_state();
977
918
  msg.supports_open = a_lock->traits.get_supports_open();
978
919
  msg.requires_code = a_lock->traits.get_requires_code();
979
- msg.unique_id = get_default_unique_id("lock", a_lock);
980
- fill_entity_info_base(a_lock, msg);
981
- return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
920
+ return fill_and_encode_entity_info(a_lock, msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size,
921
+ is_single);
982
922
  }
983
923
  void APIConnection::lock_command(const LockCommandRequest &msg) {
984
924
  ENTITY_COMMAND_GET(lock::Lock, a_lock, lock)
@@ -1008,21 +948,19 @@ uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *
1008
948
  ValveStateResponse resp;
1009
949
  resp.position = valve->position;
1010
950
  resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
1011
- fill_entity_state_base(valve, resp);
1012
- return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
951
+ return fill_and_encode_entity_state(valve, resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1013
952
  }
1014
953
  uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1015
954
  bool is_single) {
1016
955
  auto *valve = static_cast<valve::Valve *>(entity);
1017
956
  ListEntitiesValveResponse msg;
1018
957
  auto traits = valve->get_traits();
1019
- msg.device_class = valve->get_device_class();
958
+ msg.set_device_class(valve->get_device_class_ref());
1020
959
  msg.assumed_state = traits.get_is_assumed_state();
1021
960
  msg.supports_position = traits.get_supports_position();
1022
961
  msg.supports_stop = traits.get_supports_stop();
1023
- msg.unique_id = get_default_unique_id("valve", valve);
1024
- fill_entity_info_base(valve, msg);
1025
- return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
962
+ return fill_and_encode_entity_info(valve, msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size,
963
+ is_single);
1026
964
  }
1027
965
  void APIConnection::valve_command(const ValveCommandRequest &msg) {
1028
966
  ENTITY_COMMAND_MAKE_CALL(valve::Valve, valve, valve)
@@ -1049,8 +987,8 @@ uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConne
1049
987
  resp.state = static_cast<enums::MediaPlayerState>(report_state);
1050
988
  resp.volume = media_player->volume;
1051
989
  resp.muted = media_player->is_muted();
1052
- fill_entity_state_base(media_player, resp);
1053
- return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
990
+ return fill_and_encode_entity_state(media_player, resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size,
991
+ is_single);
1054
992
  }
1055
993
  uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1056
994
  bool is_single) {
@@ -1058,18 +996,18 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
1058
996
  ListEntitiesMediaPlayerResponse msg;
1059
997
  auto traits = media_player->get_traits();
1060
998
  msg.supports_pause = traits.get_supports_pause();
999
+ msg.feature_flags = traits.get_feature_flags();
1061
1000
  for (auto &supported_format : traits.get_supported_formats()) {
1062
- MediaPlayerSupportedFormat media_format;
1063
- media_format.format = supported_format.format;
1001
+ msg.supported_formats.emplace_back();
1002
+ auto &media_format = msg.supported_formats.back();
1003
+ media_format.set_format(StringRef(supported_format.format));
1064
1004
  media_format.sample_rate = supported_format.sample_rate;
1065
1005
  media_format.num_channels = supported_format.num_channels;
1066
1006
  media_format.purpose = static_cast<enums::MediaPlayerFormatPurpose>(supported_format.purpose);
1067
1007
  media_format.sample_bytes = supported_format.sample_bytes;
1068
- msg.supported_formats.push_back(media_format);
1069
1008
  }
1070
- msg.unique_id = get_default_unique_id("media_player", media_player);
1071
- fill_entity_info_base(media_player, msg);
1072
- return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1009
+ return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
1010
+ remaining_size, is_single);
1073
1011
  }
1074
1012
  void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
1075
1013
  ENTITY_COMMAND_MAKE_CALL(media_player::MediaPlayer, media_player, media_player)
@@ -1104,9 +1042,8 @@ uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *
1104
1042
  bool is_single) {
1105
1043
  auto *camera = static_cast<camera::Camera *>(entity);
1106
1044
  ListEntitiesCameraResponse msg;
1107
- msg.unique_id = get_default_unique_id("camera", camera);
1108
- fill_entity_info_base(camera, msg);
1109
- return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1045
+ return fill_and_encode_entity_info(camera, msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size,
1046
+ is_single);
1110
1047
  }
1111
1048
  void APIConnection::camera_image(const CameraImageRequest &msg) {
1112
1049
  if (camera::Camera::instance() == nullptr)
@@ -1130,6 +1067,12 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
1130
1067
  }
1131
1068
  #endif
1132
1069
 
1070
+ bool APIConnection::send_get_time_response(const GetTimeRequest &msg) {
1071
+ GetTimeResponse resp;
1072
+ resp.epoch_seconds = ::time(nullptr);
1073
+ return this->send_message(resp, GetTimeResponse::MESSAGE_TYPE);
1074
+ }
1075
+
1133
1076
  #ifdef USE_BLUETOOTH_PROXY
1134
1077
  void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
1135
1078
  bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
@@ -1137,21 +1080,6 @@ void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoo
1137
1080
  void APIConnection::unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
1138
1081
  bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
1139
1082
  }
1140
- bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) {
1141
- if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
1142
- BluetoothLEAdvertisementResponse resp = msg;
1143
- for (auto &service : resp.service_data) {
1144
- service.legacy_data.assign(service.data.begin(), service.data.end());
1145
- service.data.clear();
1146
- }
1147
- for (auto &manufacturer_data : resp.manufacturer_data) {
1148
- manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
1149
- manufacturer_data.data.clear();
1150
- }
1151
- return this->send_message(resp);
1152
- }
1153
- return this->send_message(msg);
1154
- }
1155
1083
  void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
1156
1084
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
1157
1085
  }
@@ -1175,12 +1103,10 @@ void APIConnection::bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg)
1175
1103
  bluetooth_proxy::global_bluetooth_proxy->bluetooth_gatt_notify(msg);
1176
1104
  }
1177
1105
 
1178
- BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_free(
1106
+ bool APIConnection::send_subscribe_bluetooth_connections_free_response(
1179
1107
  const SubscribeBluetoothConnectionsFreeRequest &msg) {
1180
- BluetoothConnectionsFreeResponse resp;
1181
- resp.free = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_free();
1182
- resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit();
1183
- return resp;
1108
+ bluetooth_proxy::global_bluetooth_proxy->send_connections_free(this);
1109
+ return true;
1184
1110
  }
1185
1111
 
1186
1112
  void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) {
@@ -1241,28 +1167,25 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno
1241
1167
  }
1242
1168
  }
1243
1169
 
1244
- VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration(
1245
- const VoiceAssistantConfigurationRequest &msg) {
1170
+ bool APIConnection::send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) {
1246
1171
  VoiceAssistantConfigurationResponse resp;
1247
1172
  if (!this->check_voice_assistant_api_connection_()) {
1248
- return resp;
1173
+ return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
1249
1174
  }
1250
1175
 
1251
1176
  auto &config = voice_assistant::global_voice_assistant->get_configuration();
1252
1177
  for (auto &wake_word : config.available_wake_words) {
1253
- VoiceAssistantWakeWord resp_wake_word;
1254
- resp_wake_word.id = wake_word.id;
1255
- resp_wake_word.wake_word = wake_word.wake_word;
1178
+ resp.available_wake_words.emplace_back();
1179
+ auto &resp_wake_word = resp.available_wake_words.back();
1180
+ resp_wake_word.set_id(StringRef(wake_word.id));
1181
+ resp_wake_word.set_wake_word(StringRef(wake_word.wake_word));
1256
1182
  for (const auto &lang : wake_word.trained_languages) {
1257
1183
  resp_wake_word.trained_languages.push_back(lang);
1258
1184
  }
1259
- resp.available_wake_words.push_back(std::move(resp_wake_word));
1260
- }
1261
- for (auto &wake_word_id : config.active_wake_words) {
1262
- resp.active_wake_words.push_back(wake_word_id);
1263
1185
  }
1186
+ resp.active_wake_words = &config.active_wake_words;
1264
1187
  resp.max_active_wake_words = config.max_active_wake_words;
1265
- return resp;
1188
+ return this->send_message(resp, VoiceAssistantConfigurationResponse::MESSAGE_TYPE);
1266
1189
  }
1267
1190
 
1268
1191
  void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
@@ -1284,8 +1207,8 @@ uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, A
1284
1207
  auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
1285
1208
  AlarmControlPanelStateResponse resp;
1286
1209
  resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
1287
- fill_entity_state_base(a_alarm_control_panel, resp);
1288
- return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1210
+ return fill_and_encode_entity_state(a_alarm_control_panel, resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn,
1211
+ remaining_size, is_single);
1289
1212
  }
1290
1213
  uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
1291
1214
  uint32_t remaining_size, bool is_single) {
@@ -1294,10 +1217,8 @@ uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, AP
1294
1217
  msg.supported_features = a_alarm_control_panel->get_supported_features();
1295
1218
  msg.requires_code = a_alarm_control_panel->get_requires_code();
1296
1219
  msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
1297
- msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
1298
- fill_entity_info_base(a_alarm_control_panel, msg);
1299
- return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size,
1300
- is_single);
1220
+ return fill_and_encode_entity_info(a_alarm_control_panel, msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE,
1221
+ conn, remaining_size, is_single);
1301
1222
  }
1302
1223
  void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
1303
1224
  ENTITY_COMMAND_MAKE_CALL(alarm_control_panel::AlarmControlPanel, a_alarm_control_panel, alarm_control_panel)
@@ -1337,21 +1258,19 @@ void APIConnection::send_event(event::Event *event, const std::string &event_typ
1337
1258
  uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
1338
1259
  uint32_t remaining_size, bool is_single) {
1339
1260
  EventResponse resp;
1340
- resp.event_type = event_type;
1341
- fill_entity_state_base(event, resp);
1342
- return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1261
+ resp.set_event_type(StringRef(event_type));
1262
+ return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1343
1263
  }
1344
1264
 
1345
1265
  uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1346
1266
  bool is_single) {
1347
1267
  auto *event = static_cast<event::Event *>(entity);
1348
1268
  ListEntitiesEventResponse msg;
1349
- msg.device_class = event->get_device_class();
1269
+ msg.set_device_class(event->get_device_class_ref());
1350
1270
  for (const auto &event_type : event->get_event_types())
1351
1271
  msg.event_types.push_back(event_type);
1352
- msg.unique_id = get_default_unique_id("event", event);
1353
- fill_entity_info_base(event, msg);
1354
- return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1272
+ return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
1273
+ is_single);
1355
1274
  }
1356
1275
  #endif
1357
1276
 
@@ -1371,23 +1290,21 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection
1371
1290
  resp.has_progress = true;
1372
1291
  resp.progress = update->update_info.progress;
1373
1292
  }
1374
- resp.current_version = update->update_info.current_version;
1375
- resp.latest_version = update->update_info.latest_version;
1376
- resp.title = update->update_info.title;
1377
- resp.release_summary = update->update_info.summary;
1378
- resp.release_url = update->update_info.release_url;
1293
+ resp.set_current_version(StringRef(update->update_info.current_version));
1294
+ resp.set_latest_version(StringRef(update->update_info.latest_version));
1295
+ resp.set_title(StringRef(update->update_info.title));
1296
+ resp.set_release_summary(StringRef(update->update_info.summary));
1297
+ resp.set_release_url(StringRef(update->update_info.release_url));
1379
1298
  }
1380
- fill_entity_state_base(update, resp);
1381
- return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1299
+ return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1382
1300
  }
1383
1301
  uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
1384
1302
  bool is_single) {
1385
1303
  auto *update = static_cast<update::UpdateEntity *>(entity);
1386
1304
  ListEntitiesUpdateResponse msg;
1387
- msg.device_class = update->get_device_class();
1388
- msg.unique_id = get_default_unique_id("update", update);
1389
- fill_entity_info_base(update, msg);
1390
- return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
1305
+ msg.set_device_class(update->get_device_class_ref());
1306
+ return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size,
1307
+ is_single);
1391
1308
  }
1392
1309
  void APIConnection::update_command(const UpdateCommandRequest &msg) {
1393
1310
  ENTITY_COMMAND_GET(update::UpdateEntity, update, update)
@@ -1410,50 +1327,57 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
1410
1327
  #endif
1411
1328
 
1412
1329
  bool APIConnection::try_send_log_message(int level, const char *tag, const char *line, size_t message_len) {
1413
- if (this->flags_.log_subscription < level)
1414
- return false;
1415
-
1416
- // Pre-calculate message size to avoid reallocations
1417
- uint32_t msg_size = 0;
1418
-
1419
- // Add size for level field (field ID 1, varint type)
1420
- // 1 byte for field tag + size of the level varint
1421
- msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(level));
1422
-
1423
- // Add size for string field (field ID 3, string type)
1424
- // 1 byte for field tag + size of length varint + string length
1425
- msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(message_len)) + message_len;
1426
-
1427
- // Create a pre-sized buffer
1428
- auto buffer = this->create_buffer(msg_size);
1330
+ SubscribeLogsResponse msg;
1331
+ msg.level = static_cast<enums::LogLevel>(level);
1332
+ msg.set_message(reinterpret_cast<const uint8_t *>(line), message_len);
1333
+ return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE);
1334
+ }
1429
1335
 
1430
- // Encode the message (SubscribeLogsResponse)
1431
- buffer.encode_uint32(1, static_cast<uint32_t>(level)); // LogLevel level = 1
1432
- buffer.encode_string(3, line, message_len); // string message = 3
1336
+ void APIConnection::complete_authentication_() {
1337
+ // Early return if already authenticated
1338
+ if (this->flags_.connection_state == static_cast<uint8_t>(ConnectionState::AUTHENTICATED)) {
1339
+ return;
1340
+ }
1433
1341
 
1434
- // SubscribeLogsResponse - 29
1435
- return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
1342
+ this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
1343
+ ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
1344
+ #ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1345
+ this->parent_->get_client_connected_trigger()->trigger(this->client_info_.name, this->client_info_.peername);
1346
+ #endif
1347
+ #ifdef USE_HOMEASSISTANT_TIME
1348
+ if (homeassistant::global_homeassistant_time != nullptr) {
1349
+ this->send_time_request();
1350
+ }
1351
+ #endif
1436
1352
  }
1437
1353
 
1438
- HelloResponse APIConnection::hello(const HelloRequest &msg) {
1439
- this->client_info_ = msg.client_info;
1440
- this->client_peername_ = this->helper_->getpeername();
1441
- this->helper_->set_log_info(this->get_client_combined_info());
1354
+ bool APIConnection::send_hello_response(const HelloRequest &msg) {
1355
+ this->client_info_.name = msg.client_info;
1356
+ this->client_info_.peername = this->helper_->getpeername();
1442
1357
  this->client_api_version_major_ = msg.api_version_major;
1443
1358
  this->client_api_version_minor_ = msg.api_version_minor;
1444
- ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
1445
- this->client_peername_.c_str(), this->client_api_version_major_, this->client_api_version_minor_);
1359
+ ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.name.c_str(),
1360
+ this->client_info_.peername.c_str(), this->client_api_version_major_, this->client_api_version_minor_);
1446
1361
 
1447
1362
  HelloResponse resp;
1448
1363
  resp.api_version_major = 1;
1449
- resp.api_version_minor = 10;
1450
- resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
1451
- resp.name = App.get_name();
1364
+ resp.api_version_minor = 12;
1365
+ // Temporary string for concatenation - will be valid during send_message call
1366
+ std::string server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
1367
+ resp.set_server_info(StringRef(server_info));
1368
+ resp.set_name(StringRef(App.get_name()));
1452
1369
 
1370
+ #ifdef USE_API_PASSWORD
1371
+ // Password required - wait for authentication
1453
1372
  this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::CONNECTED);
1454
- return resp;
1373
+ #else
1374
+ // No password configured - auto-authenticate
1375
+ this->complete_authentication_();
1376
+ #endif
1377
+
1378
+ return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
1455
1379
  }
1456
- ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
1380
+ bool APIConnection::send_connect_response(const ConnectRequest &msg) {
1457
1381
  bool correct = true;
1458
1382
  #ifdef USE_API_PASSWORD
1459
1383
  correct = this->parent_->check_password(msg.password);
@@ -1463,87 +1387,106 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
1463
1387
  // bool invalid_password = 1;
1464
1388
  resp.invalid_password = !correct;
1465
1389
  if (correct) {
1466
- ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
1467
- this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
1468
- #ifdef USE_API_CLIENT_CONNECTED_TRIGGER
1469
- this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
1470
- #endif
1471
- #ifdef USE_HOMEASSISTANT_TIME
1472
- if (homeassistant::global_homeassistant_time != nullptr) {
1473
- this->send_time_request();
1474
- }
1475
- #endif
1390
+ this->complete_authentication_();
1476
1391
  }
1477
- return resp;
1392
+ return this->send_message(resp, ConnectResponse::MESSAGE_TYPE);
1393
+ }
1394
+
1395
+ bool APIConnection::send_ping_response(const PingRequest &msg) {
1396
+ PingResponse resp;
1397
+ return this->send_message(resp, PingResponse::MESSAGE_TYPE);
1478
1398
  }
1479
- DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
1399
+
1400
+ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
1480
1401
  DeviceInfoResponse resp{};
1481
1402
  #ifdef USE_API_PASSWORD
1482
- resp.uses_password = this->parent_->uses_password();
1483
- #else
1484
- resp.uses_password = false;
1485
- #endif
1486
- resp.name = App.get_name();
1487
- resp.friendly_name = App.get_friendly_name();
1488
- resp.suggested_area = App.get_area();
1489
- resp.mac_address = get_mac_address_pretty();
1490
- resp.esphome_version = ESPHOME_VERSION;
1491
- resp.compilation_time = App.get_compilation_time();
1403
+ resp.uses_password = true;
1404
+ #endif
1405
+ resp.set_name(StringRef(App.get_name()));
1406
+ resp.set_friendly_name(StringRef(App.get_friendly_name()));
1407
+ #ifdef USE_AREAS
1408
+ resp.set_suggested_area(StringRef(App.get_area()));
1409
+ #endif
1410
+ // mac_address must store temporary string - will be valid during send_message call
1411
+ std::string mac_address = get_mac_address_pretty();
1412
+ resp.set_mac_address(StringRef(mac_address));
1413
+
1414
+ // Compile-time StringRef constants
1415
+ static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION);
1416
+ resp.set_esphome_version(ESPHOME_VERSION_REF);
1417
+
1418
+ // get_compilation_time() returns temporary std::string - must store it
1419
+ std::string compilation_time = App.get_compilation_time();
1420
+ resp.set_compilation_time(StringRef(compilation_time));
1421
+
1422
+ // Compile-time StringRef constants for manufacturers
1492
1423
  #if defined(USE_ESP8266) || defined(USE_ESP32)
1493
- resp.manufacturer = "Espressif";
1424
+ static constexpr auto MANUFACTURER = StringRef::from_lit("Espressif");
1494
1425
  #elif defined(USE_RP2040)
1495
- resp.manufacturer = "Raspberry Pi";
1426
+ static constexpr auto MANUFACTURER = StringRef::from_lit("Raspberry Pi");
1496
1427
  #elif defined(USE_BK72XX)
1497
- resp.manufacturer = "Beken";
1428
+ static constexpr auto MANUFACTURER = StringRef::from_lit("Beken");
1498
1429
  #elif defined(USE_LN882X)
1499
- resp.manufacturer = "Lightning";
1430
+ static constexpr auto MANUFACTURER = StringRef::from_lit("Lightning");
1500
1431
  #elif defined(USE_RTL87XX)
1501
- resp.manufacturer = "Realtek";
1432
+ static constexpr auto MANUFACTURER = StringRef::from_lit("Realtek");
1502
1433
  #elif defined(USE_HOST)
1503
- resp.manufacturer = "Host";
1434
+ static constexpr auto MANUFACTURER = StringRef::from_lit("Host");
1504
1435
  #endif
1505
- resp.model = ESPHOME_BOARD;
1436
+ resp.set_manufacturer(MANUFACTURER);
1437
+
1438
+ static constexpr auto MODEL = StringRef::from_lit(ESPHOME_BOARD);
1439
+ resp.set_model(MODEL);
1506
1440
  #ifdef USE_DEEP_SLEEP
1507
1441
  resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
1508
1442
  #endif
1509
1443
  #ifdef ESPHOME_PROJECT_NAME
1510
- resp.project_name = ESPHOME_PROJECT_NAME;
1511
- resp.project_version = ESPHOME_PROJECT_VERSION;
1444
+ static constexpr auto PROJECT_NAME = StringRef::from_lit(ESPHOME_PROJECT_NAME);
1445
+ static constexpr auto PROJECT_VERSION = StringRef::from_lit(ESPHOME_PROJECT_VERSION);
1446
+ resp.set_project_name(PROJECT_NAME);
1447
+ resp.set_project_version(PROJECT_VERSION);
1512
1448
  #endif
1513
1449
  #ifdef USE_WEBSERVER
1514
1450
  resp.webserver_port = USE_WEBSERVER_PORT;
1515
1451
  #endif
1516
1452
  #ifdef USE_BLUETOOTH_PROXY
1517
- resp.legacy_bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->get_legacy_version();
1518
1453
  resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
1519
- resp.bluetooth_mac_address = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty();
1454
+ // bt_mac must store temporary string - will be valid during send_message call
1455
+ std::string bluetooth_mac = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty();
1456
+ resp.set_bluetooth_mac_address(StringRef(bluetooth_mac));
1520
1457
  #endif
1521
1458
  #ifdef USE_VOICE_ASSISTANT
1522
- resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
1523
1459
  resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
1524
1460
  #endif
1525
1461
  #ifdef USE_API_NOISE
1526
1462
  resp.api_encryption_supported = true;
1527
1463
  #endif
1528
1464
  #ifdef USE_DEVICES
1465
+ size_t device_index = 0;
1529
1466
  for (auto const &device : App.get_devices()) {
1530
- DeviceInfo device_info;
1467
+ if (device_index >= ESPHOME_DEVICE_COUNT)
1468
+ break;
1469
+ auto &device_info = resp.devices[device_index++];
1531
1470
  device_info.device_id = device->get_device_id();
1532
- device_info.name = device->get_name();
1471
+ device_info.set_name(StringRef(device->get_name()));
1533
1472
  device_info.area_id = device->get_area_id();
1534
- resp.devices.push_back(device_info);
1535
1473
  }
1536
1474
  #endif
1537
1475
  #ifdef USE_AREAS
1476
+ size_t area_index = 0;
1538
1477
  for (auto const &area : App.get_areas()) {
1539
- AreaInfo area_info;
1478
+ if (area_index >= ESPHOME_AREA_COUNT)
1479
+ break;
1480
+ auto &area_info = resp.areas[area_index++];
1540
1481
  area_info.area_id = area->get_area_id();
1541
- area_info.name = area->get_name();
1542
- resp.areas.push_back(area_info);
1482
+ area_info.set_name(StringRef(area->get_name()));
1543
1483
  }
1544
1484
  #endif
1545
- return resp;
1485
+
1486
+ return this->send_message(resp, DeviceInfoResponse::MESSAGE_TYPE);
1546
1487
  }
1488
+
1489
+ #ifdef USE_API_HOMEASSISTANT_STATES
1547
1490
  void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
1548
1491
  for (auto &it : this->parent_->get_state_subs()) {
1549
1492
  if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
@@ -1551,6 +1494,7 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
1551
1494
  }
1552
1495
  }
1553
1496
  }
1497
+ #endif
1554
1498
  #ifdef USE_API_SERVICES
1555
1499
  void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
1556
1500
  bool found = false;
@@ -1565,28 +1509,27 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
1565
1509
  }
1566
1510
  #endif
1567
1511
  #ifdef USE_API_NOISE
1568
- NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) {
1569
- psk_t psk{};
1512
+ bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) {
1570
1513
  NoiseEncryptionSetKeyResponse resp;
1514
+ resp.success = false;
1515
+
1516
+ psk_t psk{};
1571
1517
  if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
1572
1518
  ESP_LOGW(TAG, "Invalid encryption key length");
1573
- resp.success = false;
1574
- return resp;
1575
- }
1576
-
1577
- if (!this->parent_->save_noise_psk(psk, true)) {
1519
+ } else if (!this->parent_->save_noise_psk(psk, true)) {
1578
1520
  ESP_LOGW(TAG, "Failed to save encryption key");
1579
- resp.success = false;
1580
- return resp;
1521
+ } else {
1522
+ resp.success = true;
1581
1523
  }
1582
1524
 
1583
- resp.success = true;
1584
- return resp;
1525
+ return this->send_message(resp, NoiseEncryptionSetKeyResponse::MESSAGE_TYPE);
1585
1526
  }
1586
1527
  #endif
1528
+ #ifdef USE_API_HOMEASSISTANT_STATES
1587
1529
  void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
1588
1530
  state_subs_at_ = 0;
1589
1531
  }
1532
+ #endif
1590
1533
  bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
1591
1534
  if (this->flags_.remove)
1592
1535
  return false;
@@ -1596,8 +1539,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
1596
1539
  APIError err = this->helper_->loop();
1597
1540
  if (err != APIError::OK) {
1598
1541
  on_fatal_error();
1599
- ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
1600
- api_error_to_str(err), errno);
1542
+ this->log_socket_operation_failed_(err);
1601
1543
  return false;
1602
1544
  }
1603
1545
  if (this->helper_->can_write_without_blocking())
@@ -1617,24 +1559,21 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
1617
1559
  return false;
1618
1560
  if (err != APIError::OK) {
1619
1561
  on_fatal_error();
1620
- if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1621
- ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
1622
- } else {
1623
- ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
1624
- api_error_to_str(err), errno);
1625
- }
1562
+ this->log_warning_("Packet write failed", err);
1626
1563
  return false;
1627
1564
  }
1628
1565
  // Do not set last_traffic_ on send
1629
1566
  return true;
1630
1567
  }
1568
+ #ifdef USE_API_PASSWORD
1631
1569
  void APIConnection::on_unauthenticated_access() {
1632
1570
  this->on_fatal_error();
1633
- ESP_LOGD(TAG, "%s requested access without authentication", this->get_client_combined_info().c_str());
1571
+ ESP_LOGD(TAG, "%s access without authentication", this->get_client_combined_info().c_str());
1634
1572
  }
1573
+ #endif
1635
1574
  void APIConnection::on_no_setup_connection() {
1636
1575
  this->on_fatal_error();
1637
- ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str());
1576
+ ESP_LOGD(TAG, "%s access without full connection", this->get_client_combined_info().c_str());
1638
1577
  }
1639
1578
  void APIConnection::on_fatal_error() {
1640
1579
  this->helper_->close();
@@ -1662,8 +1601,15 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c
1662
1601
 
1663
1602
  void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type,
1664
1603
  uint8_t estimated_size) {
1665
- // Insert at front for high priority messages (no deduplication check)
1666
- items.insert(items.begin(), BatchItem(entity, std::move(creator), message_type, estimated_size));
1604
+ // Add high priority message and swap to front
1605
+ // This avoids expensive vector::insert which shifts all elements
1606
+ // Note: We only ever have one high-priority message at a time (ping OR disconnect)
1607
+ // If we're disconnecting, pings are blocked, so this simple swap is sufficient
1608
+ items.emplace_back(entity, std::move(creator), message_type, estimated_size);
1609
+ if (items.size() > 1) {
1610
+ // Swap the new high-priority item to the front
1611
+ std::swap(items.front(), items.back());
1612
+ }
1667
1613
  }
1668
1614
 
1669
1615
  bool APIConnection::schedule_batch_() {
@@ -1683,6 +1629,10 @@ ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
1683
1629
  }
1684
1630
 
1685
1631
  void APIConnection::process_batch_() {
1632
+ // Ensure PacketInfo remains trivially destructible for our placement new approach
1633
+ static_assert(std::is_trivially_destructible<PacketInfo>::value,
1634
+ "PacketInfo must remain trivially destructible with this placement-new approach");
1635
+
1686
1636
  if (this->deferred_batch_.empty()) {
1687
1637
  this->flags_.batch_scheduled = false;
1688
1638
  return;
@@ -1694,6 +1644,8 @@ void APIConnection::process_batch_() {
1694
1644
  return;
1695
1645
  }
1696
1646
 
1647
+ // Get shared buffer reference once to avoid multiple calls
1648
+ auto &shared_buf = this->parent_->get_shared_buffer_ref();
1697
1649
  size_t num_items = this->deferred_batch_.size();
1698
1650
 
1699
1651
  // Fast path for single message - allocate exact size needed
@@ -1704,8 +1656,7 @@ void APIConnection::process_batch_() {
1704
1656
  uint16_t payload_size =
1705
1657
  item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true, item.message_type);
1706
1658
 
1707
- if (payload_size > 0 &&
1708
- this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
1659
+ if (payload_size > 0 && this->send_buffer(ProtoWriteBuffer{&shared_buf}, item.message_type)) {
1709
1660
  #ifdef HAS_PROTO_MESSAGE_DUMP
1710
1661
  // Log messages after send attempt for VV debugging
1711
1662
  // It's safe to use the buffer for logging at this point regardless of send result
@@ -1720,29 +1671,30 @@ void APIConnection::process_batch_() {
1720
1671
  return;
1721
1672
  }
1722
1673
 
1723
- // Pre-allocate storage for packet info
1724
- std::vector<PacketInfo> packet_info;
1725
- packet_info.reserve(num_items);
1674
+ size_t packets_to_process = std::min(num_items, MAX_PACKETS_PER_BATCH);
1675
+
1676
+ // Stack-allocated array for packet info
1677
+ alignas(PacketInfo) char packet_info_storage[MAX_PACKETS_PER_BATCH * sizeof(PacketInfo)];
1678
+ PacketInfo *packet_info = reinterpret_cast<PacketInfo *>(packet_info_storage);
1679
+ size_t packet_count = 0;
1726
1680
 
1727
1681
  // Cache these values to avoid repeated virtual calls
1728
1682
  const uint8_t header_padding = this->helper_->frame_header_padding();
1729
1683
  const uint8_t footer_size = this->helper_->frame_footer_size();
1730
1684
 
1731
1685
  // Initialize buffer and tracking variables
1732
- this->parent_->get_shared_buffer_ref().clear();
1686
+ shared_buf.clear();
1733
1687
 
1734
1688
  // Pre-calculate exact buffer size needed based on message types
1735
- uint32_t total_estimated_size = 0;
1689
+ uint32_t total_estimated_size = num_items * (header_padding + footer_size);
1736
1690
  for (size_t i = 0; i < this->deferred_batch_.size(); i++) {
1737
1691
  const auto &item = this->deferred_batch_[i];
1738
1692
  total_estimated_size += item.estimated_size;
1739
1693
  }
1740
1694
 
1741
1695
  // Calculate total overhead for all messages
1742
- uint32_t total_overhead = (header_padding + footer_size) * num_items;
1743
-
1744
1696
  // Reserve based on estimated size (much more accurate than 24-byte worst-case)
1745
- this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead);
1697
+ shared_buf.reserve(total_estimated_size);
1746
1698
  this->flags_.batch_first_message = true;
1747
1699
 
1748
1700
  size_t items_processed = 0;
@@ -1754,8 +1706,8 @@ void APIConnection::process_batch_() {
1754
1706
  // The actual message data follows after the header padding
1755
1707
  uint32_t current_offset = 0;
1756
1708
 
1757
- // Process items and encode directly to buffer
1758
- for (size_t i = 0; i < this->deferred_batch_.size(); i++) {
1709
+ // Process items and encode directly to buffer (up to our limit)
1710
+ for (size_t i = 0; i < packets_to_process; i++) {
1759
1711
  const auto &item = this->deferred_batch_[i];
1760
1712
  // Try to encode message
1761
1713
  // The creator will calculate overhead to determine if the message fits
@@ -1769,7 +1721,11 @@ void APIConnection::process_batch_() {
1769
1721
  // Message was encoded successfully
1770
1722
  // payload_size is header_padding + actual payload size + footer_size
1771
1723
  uint16_t proto_payload_size = payload_size - header_padding - footer_size;
1772
- packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
1724
+ // Use placement new to construct PacketInfo in pre-allocated stack array
1725
+ // This avoids default-constructing all MAX_PACKETS_PER_BATCH elements
1726
+ // Explicit destruction is not needed because PacketInfo is trivially destructible,
1727
+ // as ensured by the static_assert in its definition.
1728
+ new (&packet_info[packet_count++]) PacketInfo(item.message_type, current_offset, proto_payload_size);
1773
1729
 
1774
1730
  // Update tracking variables
1775
1731
  items_processed++;
@@ -1780,7 +1736,7 @@ void APIConnection::process_batch_() {
1780
1736
  remaining_size -= payload_size;
1781
1737
  // Calculate where the next message's header padding will start
1782
1738
  // Current buffer size + footer space (that prepare_message_buffer will add for this message)
1783
- current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
1739
+ current_offset = shared_buf.size() + footer_size;
1784
1740
  }
1785
1741
 
1786
1742
  if (items_processed == 0) {
@@ -1790,21 +1746,15 @@ void APIConnection::process_batch_() {
1790
1746
 
1791
1747
  // Add footer space for the last message (for Noise protocol MAC)
1792
1748
  if (footer_size > 0) {
1793
- auto &shared_buf = this->parent_->get_shared_buffer_ref();
1794
1749
  shared_buf.resize(shared_buf.size() + footer_size);
1795
1750
  }
1796
1751
 
1797
1752
  // Send all collected packets
1798
- APIError err =
1799
- this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info);
1753
+ APIError err = this->helper_->write_protobuf_packets(ProtoWriteBuffer{&shared_buf},
1754
+ std::span<const PacketInfo>(packet_info, packet_count));
1800
1755
  if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
1801
1756
  on_fatal_error();
1802
- if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
1803
- ESP_LOGW(TAG, "%s: Connection reset during batch write", this->get_client_combined_info().c_str());
1804
- } else {
1805
- ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(),
1806
- api_error_to_str(err), errno);
1807
- }
1757
+ this->log_warning_("Batch write failed", err);
1808
1758
  }
1809
1759
 
1810
1760
  #ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1860,6 +1810,33 @@ uint16_t APIConnection::try_send_ping_request(EntityBase *entity, APIConnection
1860
1810
  return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
1861
1811
  }
1862
1812
 
1863
- } // namespace api
1864
- } // namespace esphome
1813
+ #ifdef USE_API_HOMEASSISTANT_STATES
1814
+ void APIConnection::process_state_subscriptions_() {
1815
+ const auto &subs = this->parent_->get_state_subs();
1816
+ if (this->state_subs_at_ >= static_cast<int>(subs.size())) {
1817
+ this->state_subs_at_ = -1;
1818
+ return;
1819
+ }
1820
+
1821
+ const auto &it = subs[this->state_subs_at_];
1822
+ SubscribeHomeAssistantStateResponse resp;
1823
+ resp.set_entity_id(StringRef(it.entity_id));
1824
+
1825
+ // Avoid string copy by directly using the optional's value if it exists
1826
+ resp.set_attribute(it.attribute.has_value() ? StringRef(it.attribute.value()) : StringRef(""));
1827
+
1828
+ resp.once = it.once;
1829
+ if (this->send_message(resp, SubscribeHomeAssistantStateResponse::MESSAGE_TYPE)) {
1830
+ this->state_subs_at_++;
1831
+ }
1832
+ }
1833
+ #endif // USE_API_HOMEASSISTANT_STATES
1834
+
1835
+ void APIConnection::log_warning_(const char *message, APIError err) {
1836
+ ESP_LOGW(TAG, "%s: %s %s errno=%d", this->get_client_combined_info().c_str(), message, api_error_to_str(err), errno);
1837
+ }
1838
+
1839
+ void APIConnection::log_socket_operation_failed_(APIError err) { this->log_warning_("Socket operation failed", err); }
1840
+
1841
+ } // namespace esphome::api
1865
1842
  #endif