esphome 2025.8.4__py3-none-any.whl → 2025.9.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. esphome/__main__.py +36 -42
  2. esphome/components/absolute_humidity/absolute_humidity.cpp +3 -5
  3. esphome/components/adc/adc_sensor_esp32.cpp +29 -6
  4. esphome/components/ags10/ags10.cpp +3 -18
  5. esphome/components/ags10/ags10.h +2 -12
  6. esphome/components/aht10/aht10.cpp +3 -3
  7. esphome/components/airthings_ble/__init__.py +2 -2
  8. esphome/components/alarm_control_panel/__init__.py +2 -2
  9. esphome/components/am2315c/am2315c.cpp +1 -17
  10. esphome/components/am2315c/am2315c.h +2 -3
  11. esphome/components/api/__init__.py +2 -2
  12. esphome/components/api/api_connection.cpp +34 -23
  13. esphome/components/api/api_connection.h +20 -39
  14. esphome/components/api/api_frame_helper.cpp +25 -25
  15. esphome/components/api/api_frame_helper.h +3 -3
  16. esphome/components/api/api_frame_helper_noise.cpp +75 -40
  17. esphome/components/api/api_frame_helper_noise.h +3 -7
  18. esphome/components/api/api_frame_helper_plaintext.cpp +17 -4
  19. esphome/components/api/api_frame_helper_plaintext.h +1 -4
  20. esphome/components/api/api_pb2.cpp +20 -2
  21. esphome/components/api/api_pb2.h +146 -141
  22. esphome/components/api/api_pb2_dump.cpp +12 -1
  23. esphome/components/api/proto.cpp +33 -37
  24. esphome/components/async_tcp/__init__.py +2 -2
  25. esphome/components/atm90e26/sensor.py +2 -0
  26. esphome/components/atm90e32/sensor.py +4 -2
  27. esphome/components/audio_adc/__init__.py +2 -2
  28. esphome/components/audio_dac/__init__.py +2 -2
  29. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +1 -1
  30. esphome/components/bedjet/bedjet_hub.cpp +1 -1
  31. esphome/components/binary_sensor/__init__.py +2 -2
  32. esphome/components/binary_sensor/binary_sensor.cpp +13 -0
  33. esphome/components/binary_sensor/binary_sensor.h +4 -7
  34. esphome/components/bl0940/__init__.py +6 -1
  35. esphome/components/bl0940/bl0940.cpp +178 -41
  36. esphome/components/bl0940/bl0940.h +121 -76
  37. esphome/components/bl0940/button/__init__.py +27 -0
  38. esphome/components/bl0940/button/calibration_reset_button.cpp +20 -0
  39. esphome/components/bl0940/button/calibration_reset_button.h +19 -0
  40. esphome/components/bl0940/number/__init__.py +94 -0
  41. esphome/components/bl0940/number/calibration_number.cpp +29 -0
  42. esphome/components/bl0940/number/calibration_number.h +26 -0
  43. esphome/components/bl0940/sensor.py +151 -2
  44. esphome/components/bl0942/bl0942.cpp +1 -1
  45. esphome/components/ble_client/output/__init__.py +4 -4
  46. esphome/components/bluetooth_proxy/__init__.py +1 -1
  47. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -1
  48. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +15 -7
  49. esphome/components/bluetooth_proxy/bluetooth_proxy.h +3 -2
  50. esphome/components/button/__init__.py +2 -2
  51. esphome/components/button/button.cpp +13 -0
  52. esphome/components/button/button.h +4 -7
  53. esphome/components/camera/buffer.h +18 -0
  54. esphome/components/camera/buffer_impl.cpp +20 -0
  55. esphome/components/camera/buffer_impl.h +26 -0
  56. esphome/components/camera/camera.h +43 -0
  57. esphome/components/camera/encoder.h +69 -0
  58. esphome/components/camera_encoder/__init__.py +62 -0
  59. esphome/components/camera_encoder/encoder_buffer_impl.cpp +23 -0
  60. esphome/components/camera_encoder/encoder_buffer_impl.h +25 -0
  61. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +82 -0
  62. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +39 -0
  63. esphome/components/captive_portal/__init__.py +2 -2
  64. esphome/components/captive_portal/captive_portal.cpp +35 -12
  65. esphome/components/captive_portal/captive_portal.h +3 -3
  66. esphome/components/ccs811/ccs811.cpp +3 -3
  67. esphome/components/climate/__init__.py +2 -2
  68. esphome/components/climate/climate.cpp +1 -1
  69. esphome/components/cover/__init__.py +5 -5
  70. esphome/components/cover/cover.cpp +1 -1
  71. esphome/components/cover/cover.h +2 -2
  72. esphome/components/dallas_temp/dallas_temp.cpp +2 -2
  73. esphome/components/datetime/__init__.py +2 -2
  74. esphome/components/datetime/date_entity.h +2 -2
  75. esphome/components/datetime/datetime_entity.h +2 -2
  76. esphome/components/datetime/time_entity.h +2 -2
  77. esphome/components/debug/debug_esp32.cpp +1 -1
  78. esphome/components/display/__init__.py +4 -4
  79. esphome/components/duty_time/duty_time_sensor.cpp +1 -1
  80. esphome/components/esp32/__init__.py +0 -5
  81. esphome/components/esp32/gpio.cpp +27 -23
  82. esphome/components/esp32/gpio.h +26 -11
  83. esphome/components/esp32/preferences.cpp +8 -4
  84. esphome/components/esp32_ble/__init__.py +7 -2
  85. esphome/components/esp32_ble_client/ble_client_base.cpp +7 -3
  86. esphome/components/esp32_ble_tracker/__init__.py +2 -2
  87. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +9 -44
  88. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -14
  89. esphome/components/esp8266/__init__.py +2 -2
  90. esphome/components/esp8266/core.cpp +2 -2
  91. esphome/components/esp8266/gpio.py +4 -4
  92. esphome/components/esp8266/preferences.cpp +30 -28
  93. esphome/components/esphome/ota/__init__.py +2 -2
  94. esphome/components/esphome/ota/ota_esphome.cpp +21 -19
  95. esphome/components/esphome/ota/ota_esphome.h +6 -5
  96. esphome/components/ethernet/__init__.py +7 -2
  97. esphome/components/ethernet/ethernet_component.cpp +1 -1
  98. esphome/components/event/__init__.py +2 -2
  99. esphome/components/event/event.h +4 -4
  100. esphome/components/fan/__init__.py +2 -2
  101. esphome/components/fan/fan.cpp +2 -1
  102. esphome/components/gdk101/gdk101.cpp +4 -4
  103. esphome/components/globals/__init__.py +2 -2
  104. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +19 -18
  105. esphome/components/gpio_expander/cached_gpio.h +36 -16
  106. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +5 -5
  107. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +1 -1
  108. esphome/components/haier/haier_base.cpp +1 -1
  109. esphome/components/haier/hon_climate.cpp +1 -1
  110. esphome/components/hlw8012/hlw8012.cpp +5 -5
  111. esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +4 -4
  112. esphome/components/host/preferences.h +3 -2
  113. esphome/components/hte501/hte501.cpp +3 -21
  114. esphome/components/hte501/hte501.h +2 -3
  115. esphome/components/http_request/ota/__init__.py +2 -2
  116. esphome/components/i2c/__init__.py +2 -2
  117. esphome/components/i2c/i2c.cpp +13 -9
  118. esphome/components/i2c/i2c_bus.h +36 -6
  119. esphome/components/i2s_audio/__init__.py +8 -2
  120. esphome/components/i2s_audio/media_player/__init__.py +1 -1
  121. esphome/components/i2s_audio/microphone/__init__.py +1 -1
  122. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  123. esphome/components/inkplate/__init__.py +1 -0
  124. esphome/components/inkplate/const.py +105 -0
  125. esphome/components/inkplate/display.py +238 -0
  126. esphome/components/{inkplate6 → inkplate}/inkplate.cpp +156 -74
  127. esphome/components/{inkplate6 → inkplate}/inkplate.h +28 -68
  128. esphome/components/inkplate6/__init__.py +0 -1
  129. esphome/components/inkplate6/display.py +2 -211
  130. esphome/components/integration/integration_sensor.cpp +1 -1
  131. esphome/components/json/__init__.py +2 -2
  132. esphome/components/lc709203f/lc709203f.cpp +4 -17
  133. esphome/components/lc709203f/lc709203f.h +2 -3
  134. esphome/components/ld2420/text_sensor/{text_sensor.cpp → ld2420_text_sensor.cpp} +1 -1
  135. esphome/components/ld2450/ld2450.cpp +1 -1
  136. esphome/components/libretiny/preferences.cpp +13 -5
  137. esphome/components/light/__init__.py +2 -2
  138. esphome/components/light/addressable_light_effect.h +7 -0
  139. esphome/components/light/base_light_effects.h +8 -0
  140. esphome/components/light/light_call.cpp +22 -20
  141. esphome/components/light/light_effect.cpp +36 -0
  142. esphome/components/light/light_effect.h +14 -0
  143. esphome/components/light/light_json_schema.cpp +9 -1
  144. esphome/components/light/light_state.cpp +2 -2
  145. esphome/components/light/light_state.h +38 -0
  146. esphome/components/lock/__init__.py +2 -2
  147. esphome/components/lock/lock.h +2 -2
  148. esphome/components/logger/__init__.py +2 -2
  149. esphome/components/logger/logger.cpp +25 -4
  150. esphome/components/logger/logger.h +1 -1
  151. esphome/components/logger/logger_esp32.cpp +16 -8
  152. esphome/components/logger/logger_esp8266.cpp +11 -3
  153. esphome/components/logger/logger_libretiny.cpp +13 -3
  154. esphome/components/logger/logger_rp2040.cpp +14 -3
  155. esphome/components/logger/logger_zephyr.cpp +15 -4
  156. esphome/components/lvgl/defines.py +1 -0
  157. esphome/components/lvgl/hello_world.py +96 -33
  158. esphome/components/lvgl/number/lvgl_number.h +1 -1
  159. esphome/components/lvgl/select/lvgl_select.h +1 -1
  160. esphome/components/lvgl/widgets/__init__.py +0 -1
  161. esphome/components/lvgl/widgets/spinbox.py +20 -11
  162. esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp +1 -1
  163. esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp +1 -1
  164. esphome/components/mapping/__init__.py +13 -5
  165. esphome/components/mapping/mapping.h +69 -0
  166. esphome/components/max17043/max17043.cpp +2 -2
  167. esphome/components/mcp23016/__init__.py +1 -0
  168. esphome/components/mcp23016/mcp23016.cpp +20 -5
  169. esphome/components/mcp23016/mcp23016.h +10 -4
  170. esphome/components/mcp23x08_base/mcp23x08_base.cpp +1 -1
  171. esphome/components/mcp23x17_base/mcp23x17_base.cpp +2 -2
  172. esphome/components/mdns/__init__.py +2 -2
  173. esphome/components/mdns/mdns_component.cpp +145 -54
  174. esphome/components/media_player/__init__.py +2 -2
  175. esphome/components/micro_wake_word/__init__.py +2 -2
  176. esphome/components/microphone/__init__.py +2 -2
  177. esphome/components/mipi/__init__.py +77 -33
  178. esphome/components/mipi_rgb/__init__.py +2 -0
  179. esphome/components/mipi_rgb/display.py +321 -0
  180. esphome/components/mipi_rgb/mipi_rgb.cpp +388 -0
  181. esphome/components/mipi_rgb/mipi_rgb.h +127 -0
  182. esphome/components/mipi_rgb/models/guition.py +24 -0
  183. esphome/components/mipi_rgb/models/lilygo.py +228 -0
  184. esphome/components/mipi_rgb/models/rpi.py +9 -0
  185. esphome/components/mipi_rgb/models/st7701s.py +214 -0
  186. esphome/components/mipi_rgb/models/waveshare.py +64 -0
  187. esphome/components/mipi_spi/models/jc.py +229 -0
  188. esphome/components/mlx90614/mlx90614.cpp +1 -16
  189. esphome/components/mlx90614/mlx90614.h +0 -1
  190. esphome/components/mqtt/__init__.py +2 -2
  191. esphome/components/mqtt/mqtt_sensor.cpp +7 -2
  192. esphome/components/ms5611/ms5611.cpp +7 -6
  193. esphome/components/network/__init__.py +2 -2
  194. esphome/components/nextion/nextion_upload.cpp +4 -1
  195. esphome/components/nrf52/__init__.py +49 -6
  196. esphome/components/nrf52/const.py +1 -0
  197. esphome/components/nrf52/dfu.cpp +51 -0
  198. esphome/components/nrf52/dfu.h +24 -0
  199. esphome/components/ntc/ntc.cpp +1 -1
  200. esphome/components/number/__init__.py +2 -2
  201. esphome/components/number/automation.cpp +1 -1
  202. esphome/components/number/number.cpp +21 -0
  203. esphome/components/number/number.h +4 -13
  204. esphome/components/opentherm/hub.h +6 -6
  205. esphome/components/opentherm/number/{number.cpp → opentherm_number.cpp} +2 -2
  206. esphome/components/opentherm/output/{output.cpp → opentherm_output.cpp} +1 -1
  207. esphome/components/opentherm/switch/{switch.cpp → opentherm_switch.cpp} +1 -1
  208. esphome/components/ota/__init__.py +2 -2
  209. esphome/components/pca6416a/__init__.py +1 -0
  210. esphome/components/pca6416a/pca6416a.cpp +20 -5
  211. esphome/components/pca6416a/pca6416a.h +12 -5
  212. esphome/components/pca9554/__init__.py +2 -1
  213. esphome/components/pca9554/pca9554.cpp +12 -18
  214. esphome/components/pca9554/pca9554.h +10 -9
  215. esphome/components/pcf8574/__init__.py +1 -0
  216. esphome/components/pcf8574/pcf8574.cpp +14 -5
  217. esphome/components/pcf8574/pcf8574.h +13 -6
  218. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +7 -7
  219. esphome/components/pipsolar/__init__.py +3 -3
  220. esphome/components/pipsolar/output/__init__.py +4 -4
  221. esphome/components/pulse_width/pulse_width.cpp +2 -2
  222. esphome/components/qmp6988/qmp6988.cpp +81 -126
  223. esphome/components/qmp6988/qmp6988.h +31 -37
  224. esphome/components/radon_eye_ble/__init__.py +2 -2
  225. esphome/components/remote_base/__init__.py +6 -8
  226. esphome/components/rotary_encoder/rotary_encoder.cpp +1 -1
  227. esphome/components/rp2040/__init__.py +2 -2
  228. esphome/components/runtime_stats/runtime_stats.cpp +10 -23
  229. esphome/components/runtime_stats/runtime_stats.h +4 -10
  230. esphome/components/safe_mode/__init__.py +2 -2
  231. esphome/components/safe_mode/safe_mode.cpp +33 -31
  232. esphome/components/script/script.cpp +6 -0
  233. esphome/components/script/script.h +19 -5
  234. esphome/components/sdm_meter/sensor.py +3 -1
  235. esphome/components/select/__init__.py +2 -2
  236. esphome/components/select/select.h +2 -2
  237. esphome/components/sen5x/sen5x.cpp +57 -55
  238. esphome/components/sen5x/sen5x.h +21 -15
  239. esphome/components/sen5x/sensor.py +67 -44
  240. esphome/components/sensirion_common/i2c_sensirion.cpp +18 -47
  241. esphome/components/sensirion_common/i2c_sensirion.h +39 -55
  242. esphome/components/sensor/__init__.py +2 -2
  243. esphome/components/sensor/automation.h +1 -1
  244. esphome/components/sensor/sensor.cpp +34 -6
  245. esphome/components/sensor/sensor.h +4 -21
  246. esphome/components/sgp30/sgp30.cpp +34 -35
  247. esphome/components/sgp30/sgp30.h +11 -10
  248. esphome/components/sgp4x/sgp4x.cpp +2 -2
  249. esphome/components/shelly_dimmer/light.py +7 -7
  250. esphome/components/sht4x/sht4x.cpp +1 -1
  251. esphome/components/sntp/sntp_component.cpp +36 -9
  252. esphome/components/sntp/sntp_component.h +7 -0
  253. esphome/components/sound_level/sound_level.cpp +1 -1
  254. esphome/components/speaker/__init__.py +2 -2
  255. esphome/components/speaker/media_player/__init__.py +2 -2
  256. esphome/components/speaker/media_player/speaker_media_player.cpp +1 -1
  257. esphome/components/spi/__init__.py +2 -2
  258. esphome/components/sprinkler/sprinkler.cpp +1 -1
  259. esphome/components/sps30/sps30.cpp +18 -23
  260. esphome/components/sps30/sps30.h +3 -3
  261. esphome/components/status_led/__init__.py +2 -2
  262. esphome/components/stepper/__init__.py +2 -2
  263. esphome/components/switch/__init__.py +2 -2
  264. esphome/components/switch/switch.cpp +5 -5
  265. esphome/components/sx1509/__init__.py +1 -1
  266. esphome/components/sx1509/sx1509.cpp +12 -7
  267. esphome/components/sx1509/sx1509.h +11 -4
  268. esphome/components/tca9555/tca9555.cpp +5 -5
  269. esphome/components/tee501/tee501.cpp +2 -21
  270. esphome/components/tee501/tee501.h +2 -4
  271. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +1 -1
  272. esphome/components/template/datetime/template_date.cpp +1 -1
  273. esphome/components/template/datetime/template_datetime.cpp +2 -2
  274. esphome/components/template/datetime/template_time.cpp +1 -1
  275. esphome/components/template/number/template_number.cpp +1 -1
  276. esphome/components/template/select/template_select.cpp +1 -1
  277. esphome/components/template/text/template_text.cpp +1 -1
  278. esphome/components/text/__init__.py +2 -2
  279. esphome/components/text/text.h +2 -2
  280. esphome/components/text_sensor/__init__.py +2 -2
  281. esphome/components/text_sensor/text_sensor.h +4 -4
  282. esphome/components/thermostat/climate.py +11 -7
  283. esphome/components/thermostat/thermostat_climate.cpp +237 -206
  284. esphome/components/thermostat/thermostat_climate.h +52 -41
  285. esphome/components/time/__init__.py +2 -2
  286. esphome/components/tmp1075/tmp1075.cpp +1 -1
  287. esphome/components/total_daily_energy/total_daily_energy.cpp +1 -1
  288. esphome/components/touchscreen/__init__.py +2 -2
  289. esphome/components/tuya/number/tuya_number.cpp +1 -1
  290. esphome/components/udp/udp_component.cpp +3 -3
  291. esphome/components/ufire_ec/ufire_ec.cpp +4 -4
  292. esphome/components/ufire_ise/ufire_ise.cpp +4 -4
  293. esphome/components/update/__init__.py +2 -2
  294. esphome/components/usb_uart/usb_uart.cpp +1 -1
  295. esphome/components/valve/__init__.py +5 -5
  296. esphome/components/valve/valve.cpp +1 -1
  297. esphome/components/valve/valve.h +2 -2
  298. esphome/components/wake_on_lan/wake_on_lan.cpp +2 -2
  299. esphome/components/waveshare_epaper/waveshare_213v3.cpp +1 -1
  300. esphome/components/web_server/__init__.py +2 -2
  301. esphome/components/web_server/ota/__init__.py +2 -2
  302. esphome/components/web_server/ota/ota_web_server.cpp +11 -0
  303. esphome/components/web_server/web_server.cpp +58 -12
  304. esphome/components/web_server_base/__init__.py +2 -2
  305. esphome/components/wifi/__init__.py +5 -5
  306. esphome/components/wifi/wifi_component.cpp +3 -3
  307. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -0
  308. esphome/config_validation.py +2 -2
  309. esphome/const.py +2 -1
  310. esphome/core/__init__.py +1 -0
  311. esphome/core/application.cpp +89 -51
  312. esphome/core/application.h +1 -0
  313. esphome/core/component.cpp +41 -19
  314. esphome/core/component.h +17 -13
  315. esphome/core/config.py +7 -7
  316. esphome/core/defines.h +4 -0
  317. esphome/core/entity_base.cpp +22 -8
  318. esphome/core/entity_base.h +43 -0
  319. esphome/core/helpers.cpp +26 -13
  320. esphome/core/helpers.h +4 -3
  321. esphome/core/ring_buffer.cpp +6 -2
  322. esphome/core/ring_buffer.h +2 -1
  323. esphome/core/scheduler.cpp +175 -94
  324. esphome/core/scheduler.h +66 -35
  325. esphome/core/time.cpp +6 -20
  326. esphome/coroutine.py +80 -3
  327. esphome/cpp_generator.py +13 -0
  328. esphome/cpp_helpers.py +2 -2
  329. esphome/dashboard/web_server.py +67 -10
  330. esphome/espota2.py +13 -6
  331. esphome/helpers.py +68 -83
  332. esphome/resolver.py +67 -0
  333. esphome/util.py +9 -6
  334. esphome/wizard.py +39 -26
  335. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/METADATA +9 -9
  336. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/RECORD +344 -313
  337. /esphome/components/ld2420/text_sensor/{text_sensor.h → ld2420_text_sensor.h} +0 -0
  338. /esphome/components/opentherm/number/{number.h → opentherm_number.h} +0 -0
  339. /esphome/components/opentherm/output/{output.h → opentherm_output.h} +0 -0
  340. /esphome/components/opentherm/switch/{switch.h → opentherm_switch.h} +0 -0
  341. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/WHEEL +0 -0
  342. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/entry_points.txt +0 -0
  343. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/licenses/LICENSE +0 -0
  344. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/top_level.txt +0 -0
esphome/__main__.py CHANGED
@@ -396,27 +396,29 @@ def check_permissions(port: str):
396
396
  )
397
397
 
398
398
 
399
- def upload_program(config: ConfigType, args: ArgsProtocol, host: str) -> int | str:
399
+ def upload_program(
400
+ config: ConfigType, args: ArgsProtocol, devices: list[str]
401
+ ) -> tuple[int, str | None]:
402
+ host = devices[0]
400
403
  try:
401
404
  module = importlib.import_module("esphome.components." + CORE.target_platform)
402
405
  if getattr(module, "upload_program")(config, args, host):
403
- return 0
406
+ return 0, host
404
407
  except AttributeError:
405
408
  pass
406
409
 
407
410
  if get_port_type(host) == "SERIAL":
408
411
  check_permissions(host)
412
+
413
+ exit_code = 1
409
414
  if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
410
415
  file = getattr(args, "file", None)
411
- return upload_using_esptool(config, host, file, args.upload_speed)
412
-
413
- if CORE.target_platform in (PLATFORM_RP2040):
414
- return upload_using_platformio(config, host)
416
+ exit_code = upload_using_esptool(config, host, file, args.upload_speed)
417
+ elif CORE.target_platform == PLATFORM_RP2040 or CORE.is_libretiny:
418
+ exit_code = upload_using_platformio(config, host)
419
+ # else: Unknown target platform, exit_code remains 1
415
420
 
416
- if CORE.is_libretiny:
417
- return upload_using_platformio(config, host)
418
-
419
- return 1 # Unknown target platform
421
+ return exit_code, host if exit_code == 0 else None
420
422
 
421
423
  ota_conf = {}
422
424
  for ota_item in config.get(CONF_OTA, []):
@@ -433,10 +435,10 @@ def upload_program(config: ConfigType, args: ArgsProtocol, host: str) -> int | s
433
435
 
434
436
  remote_port = int(ota_conf[CONF_PORT])
435
437
  password = ota_conf.get(CONF_PASSWORD, "")
438
+ binary = args.file if getattr(args, "file", None) is not None else CORE.firmware_bin
436
439
 
437
440
  # Check if we should use MQTT for address resolution
438
441
  # This happens when no device was specified, or the current host is "MQTT"/"OTA"
439
- devices: list[str] = args.device or []
440
442
  if (
441
443
  CONF_MQTT in config # pylint: disable=too-many-boolean-expressions
442
444
  and (not devices or host in ("MQTT", "OTA"))
@@ -447,17 +449,23 @@ def upload_program(config: ConfigType, args: ArgsProtocol, host: str) -> int | s
447
449
  ):
448
450
  from esphome import mqtt
449
451
 
450
- host = mqtt.get_esphome_device_ip(
451
- config, args.username, args.password, args.client_id
452
- )
453
-
454
- if getattr(args, "file", None) is not None:
455
- return espota2.run_ota(host, remote_port, password, args.file)
452
+ devices = [
453
+ mqtt.get_esphome_device_ip(
454
+ config, args.username, args.password, args.client_id
455
+ )
456
+ ]
456
457
 
457
- return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
458
+ return espota2.run_ota(devices, remote_port, password, binary)
458
459
 
459
460
 
460
461
  def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None:
462
+ try:
463
+ module = importlib.import_module("esphome.components." + CORE.target_platform)
464
+ if getattr(module, "show_logs")(config, args, devices):
465
+ return 0
466
+ except AttributeError:
467
+ pass
468
+
461
469
  if "logger" not in config:
462
470
  raise EsphomeError("Logger is not configured!")
463
471
 
@@ -551,17 +559,11 @@ def command_upload(args: ArgsProtocol, config: ConfigType) -> int | None:
551
559
  purpose="uploading",
552
560
  )
553
561
 
554
- # Try each device until one succeeds
555
- exit_code = 1
556
- for device in devices:
557
- _LOGGER.info("Uploading to %s", device)
558
- exit_code = upload_program(config, args, device)
559
- if exit_code == 0:
560
- _LOGGER.info("Successfully uploaded program.")
561
- return 0
562
- if len(devices) > 1:
563
- _LOGGER.warning("Failed to upload to %s", device)
564
-
562
+ exit_code, _ = upload_program(config, args, devices)
563
+ if exit_code == 0:
564
+ _LOGGER.info("Successfully uploaded program.")
565
+ else:
566
+ _LOGGER.warning("Failed to upload to %s", devices)
565
567
  return exit_code
566
568
 
567
569
 
@@ -614,19 +616,11 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
614
616
  purpose="uploading",
615
617
  )
616
618
 
617
- # Try each device for upload until one succeeds
618
- successful_device: str | None = None
619
- for device in devices:
620
- _LOGGER.info("Uploading to %s", device)
621
- exit_code = upload_program(config, args, device)
622
- if exit_code == 0:
623
- _LOGGER.info("Successfully uploaded program.")
624
- successful_device = device
625
- break
626
- if len(devices) > 1:
627
- _LOGGER.warning("Failed to upload to %s", device)
628
-
629
- if successful_device is None:
619
+ exit_code, successful_device = upload_program(config, args, devices)
620
+ if exit_code == 0:
621
+ _LOGGER.info("Successfully uploaded program.")
622
+ else:
623
+ _LOGGER.warning("Failed to upload to %s", devices)
630
624
  return exit_code
631
625
 
632
626
  if args.no_logs:
@@ -61,11 +61,10 @@ void AbsoluteHumidityComponent::loop() {
61
61
  ESP_LOGW(TAG, "No valid state from temperature sensor!");
62
62
  }
63
63
  if (no_humidity) {
64
- ESP_LOGW(TAG, "No valid state from temperature sensor!");
64
+ ESP_LOGW(TAG, "No valid state from humidity sensor!");
65
65
  }
66
- ESP_LOGW(TAG, "Unable to calculate absolute humidity.");
67
66
  this->publish_state(NAN);
68
- this->status_set_warning();
67
+ this->status_set_warning(LOG_STR("Unable to calculate absolute humidity."));
69
68
  return;
70
69
  }
71
70
 
@@ -87,9 +86,8 @@ void AbsoluteHumidityComponent::loop() {
87
86
  es = es_wobus(temperature_c);
88
87
  break;
89
88
  default:
90
- ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
91
89
  this->publish_state(NAN);
92
- this->status_set_error();
90
+ this->status_set_error("Invalid saturation vapor pressure equation selection!");
93
91
  return;
94
92
  }
95
93
  ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
@@ -241,6 +241,8 @@ float ADCSensor::sample_autorange_() {
241
241
  cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
242
242
 
243
243
  err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
244
+ ESP_LOGVV(TAG, "Autorange atten=%d: Calibration handle creation %s (err=%d)", atten,
245
+ (err == ESP_OK) ? "SUCCESS" : "FAILED", err);
244
246
  #else
245
247
  adc_cali_line_fitting_config_t cali_config = {
246
248
  .unit_id = this->adc_unit_,
@@ -251,10 +253,14 @@ float ADCSensor::sample_autorange_() {
251
253
  #endif
252
254
  };
253
255
  err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
256
+ ESP_LOGVV(TAG, "Autorange atten=%d: Calibration handle creation %s (err=%d)", atten,
257
+ (err == ESP_OK) ? "SUCCESS" : "FAILED", err);
254
258
  #endif
255
259
 
256
260
  int raw;
257
261
  err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
262
+ ESP_LOGVV(TAG, "Autorange atten=%d: Raw ADC read %s, value=%d (err=%d)", atten,
263
+ (err == ESP_OK) ? "SUCCESS" : "FAILED", raw, err);
258
264
 
259
265
  if (err != ESP_OK) {
260
266
  ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
@@ -275,8 +281,10 @@ float ADCSensor::sample_autorange_() {
275
281
  err = adc_cali_raw_to_voltage(handle, raw, &voltage_mv);
276
282
  if (err == ESP_OK) {
277
283
  voltage = voltage_mv / 1000.0f;
284
+ ESP_LOGVV(TAG, "Autorange atten=%d: CALIBRATED - raw=%d -> %dmV -> %.6fV", atten, raw, voltage_mv, voltage);
278
285
  } else {
279
286
  voltage = raw * 3.3f / 4095.0f;
287
+ ESP_LOGVV(TAG, "Autorange atten=%d: UNCALIBRATED FALLBACK - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage);
280
288
  }
281
289
  // Clean up calibration handle
282
290
  #if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
@@ -287,6 +295,7 @@ float ADCSensor::sample_autorange_() {
287
295
  #endif
288
296
  } else {
289
297
  voltage = raw * 3.3f / 4095.0f;
298
+ ESP_LOGVV(TAG, "Autorange atten=%d: NO CALIBRATION - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage);
290
299
  }
291
300
 
292
301
  return {raw, voltage};
@@ -324,18 +333,32 @@ float ADCSensor::sample_autorange_() {
324
333
  }
325
334
 
326
335
  const int adc_half = 2048;
327
- uint32_t c12 = std::min(raw12, adc_half);
328
- uint32_t c6 = adc_half - std::abs(raw6 - adc_half);
329
- uint32_t c2 = adc_half - std::abs(raw2 - adc_half);
330
- uint32_t c0 = std::min(4095 - raw0, adc_half);
331
- uint32_t csum = c12 + c6 + c2 + c0;
336
+ const uint32_t c12 = std::min(raw12, adc_half);
337
+
338
+ const int32_t c6_signed = adc_half - std::abs(raw6 - adc_half);
339
+ const uint32_t c6 = (c6_signed > 0) ? c6_signed : 0; // Clamp to prevent underflow
340
+
341
+ const int32_t c2_signed = adc_half - std::abs(raw2 - adc_half);
342
+ const uint32_t c2 = (c2_signed > 0) ? c2_signed : 0; // Clamp to prevent underflow
343
+
344
+ const uint32_t c0 = std::min(4095 - raw0, adc_half);
345
+ const uint32_t csum = c12 + c6 + c2 + c0;
346
+
347
+ ESP_LOGVV(TAG, "Autorange summary:");
348
+ ESP_LOGVV(TAG, " Raw readings: 12db=%d, 6db=%d, 2.5db=%d, 0db=%d", raw12, raw6, raw2, raw0);
349
+ ESP_LOGVV(TAG, " Voltages: 12db=%.6f, 6db=%.6f, 2.5db=%.6f, 0db=%.6f", mv12, mv6, mv2, mv0);
350
+ ESP_LOGVV(TAG, " Coefficients: c12=%u, c6=%u, c2=%u, c0=%u, sum=%u", c12, c6, c2, c0, csum);
332
351
 
333
352
  if (csum == 0) {
334
353
  ESP_LOGE(TAG, "Invalid weight sum in autorange calculation");
335
354
  return NAN;
336
355
  }
337
356
 
338
- return (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum;
357
+ const float final_result = (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum;
358
+ ESP_LOGV(TAG, "Autorange final: (%.6f*%u + %.6f*%u + %.6f*%u + %.6f*%u)/%u = %.6fV", mv12, c12, mv6, c6, mv2, c2, mv0,
359
+ c0, csum, final_result);
360
+
361
+ return final_result;
339
362
  }
340
363
 
341
364
  } // namespace adc
@@ -89,7 +89,7 @@ void AGS10Component::dump_config() {
89
89
  bool AGS10Component::new_i2c_address(uint8_t newaddress) {
90
90
  uint8_t rev_newaddress = ~newaddress;
91
91
  std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0};
92
- data[4] = calc_crc8_(data, 4);
92
+ data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
93
93
  if (!this->write_bytes(REG_ADDRESS, data)) {
94
94
  this->error_code_ = COMMUNICATION_FAILED;
95
95
  this->status_set_warning();
@@ -109,7 +109,7 @@ bool AGS10Component::set_zero_point_with_current_resistance() { return this->set
109
109
 
110
110
  bool AGS10Component::set_zero_point_with(uint16_t value) {
111
111
  std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
112
- data[4] = calc_crc8_(data, 4);
112
+ data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
113
113
  if (!this->write_bytes(REG_CALIBRATION, data)) {
114
114
  this->error_code_ = COMMUNICATION_FAILED;
115
115
  this->status_set_warning();
@@ -184,7 +184,7 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
184
184
  auto res = *data;
185
185
  auto crc_byte = res[len];
186
186
 
187
- if (crc_byte != calc_crc8_(res, len)) {
187
+ if (crc_byte != crc8(res.data(), len, 0xFF, 0x31, true)) {
188
188
  this->error_code_ = CRC_CHECK_FAILED;
189
189
  ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!");
190
190
  return optional<std::array<uint8_t, N>>();
@@ -192,20 +192,5 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
192
192
 
193
193
  return data;
194
194
  }
195
-
196
- template<size_t N> uint8_t AGS10Component::calc_crc8_(std::array<uint8_t, N> dat, uint8_t num) {
197
- uint8_t i, byte1, crc = 0xFF;
198
- for (byte1 = 0; byte1 < num; byte1++) {
199
- crc ^= (dat[byte1]);
200
- for (i = 0; i < 8; i++) {
201
- if (crc & 0x80) {
202
- crc = (crc << 1) ^ 0x31;
203
- } else {
204
- crc = (crc << 1);
205
- }
206
- }
207
- }
208
- return crc;
209
- }
210
195
  } // namespace ags10
211
196
  } // namespace esphome
@@ -1,9 +1,9 @@
1
1
  #pragma once
2
2
 
3
+ #include "esphome/components/i2c/i2c.h"
4
+ #include "esphome/components/sensor/sensor.h"
3
5
  #include "esphome/core/automation.h"
4
6
  #include "esphome/core/component.h"
5
- #include "esphome/components/sensor/sensor.h"
6
- #include "esphome/components/i2c/i2c.h"
7
7
 
8
8
  namespace esphome {
9
9
  namespace ags10 {
@@ -99,16 +99,6 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice {
99
99
  * Read, checks and returns data from the sensor.
100
100
  */
101
101
  template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
102
-
103
- /**
104
- * Calculates CRC8 value.
105
- *
106
- * CRC8 calculation, initial value: 0xFF, polynomial: 0x31 (x8+ x5+ x4+1)
107
- *
108
- * @param[in] dat the data buffer
109
- * @param num number of bytes in the buffer
110
- */
111
- template<size_t N> uint8_t calc_crc8_(std::array<uint8_t, N> dat, uint8_t num);
112
102
  };
113
103
 
114
104
  template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {
@@ -96,7 +96,7 @@ void AHT10Component::read_data_() {
96
96
  ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
97
97
  }
98
98
  if (this->read(data, 6) != i2c::ERROR_OK) {
99
- this->status_set_warning("Read failed, will retry");
99
+ this->status_set_warning(LOG_STR("Read failed, will retry"));
100
100
  this->restart_read_();
101
101
  return;
102
102
  }
@@ -113,7 +113,7 @@ void AHT10Component::read_data_() {
113
113
  } else {
114
114
  ESP_LOGD(TAG, "Invalid humidity, retrying");
115
115
  if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
116
- this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
116
+ this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
117
117
  }
118
118
  this->restart_read_();
119
119
  return;
@@ -144,7 +144,7 @@ void AHT10Component::update() {
144
144
  return;
145
145
  this->start_time_ = millis();
146
146
  if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
147
- this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
147
+ this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
148
148
  return;
149
149
  }
150
150
  this->restart_read_();
@@ -18,6 +18,6 @@ CONFIG_SCHEMA = cv.Schema(
18
18
  ).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
19
19
 
20
20
 
21
- def to_code(config):
21
+ async def to_code(config):
22
22
  var = cg.new_Pvariable(config[CONF_ID])
23
- yield esp32_ble_tracker.register_ble_device(var, config)
23
+ await esp32_ble_tracker.register_ble_device(var, config)
@@ -13,7 +13,7 @@ from esphome.const import (
13
13
  CONF_TRIGGER_ID,
14
14
  CONF_WEB_SERVER,
15
15
  )
16
- from esphome.core import CORE, coroutine_with_priority
16
+ from esphome.core import CORE, CoroPriority, coroutine_with_priority
17
17
  from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
18
18
  from esphome.cpp_generator import MockObjClass
19
19
 
@@ -345,6 +345,6 @@ async def alarm_control_panel_is_armed_to_code(
345
345
  return cg.new_Pvariable(condition_id, template_arg, paren)
346
346
 
347
347
 
348
- @coroutine_with_priority(100.0)
348
+ @coroutine_with_priority(CoroPriority.CORE)
349
349
  async def to_code(config):
350
350
  cg.add_global(alarm_control_panel_ns.using)
@@ -29,22 +29,6 @@ namespace am2315c {
29
29
 
30
30
  static const char *const TAG = "am2315c";
31
31
 
32
- uint8_t AM2315C::crc8_(uint8_t *data, uint8_t len) {
33
- uint8_t crc = 0xFF;
34
- while (len--) {
35
- crc ^= *data++;
36
- for (uint8_t i = 0; i < 8; i++) {
37
- if (crc & 0x80) {
38
- crc <<= 1;
39
- crc ^= 0x31;
40
- } else {
41
- crc <<= 1;
42
- }
43
- }
44
- }
45
- return crc;
46
- }
47
-
48
32
  bool AM2315C::reset_register_(uint8_t reg) {
49
33
  // code based on demo code sent by www.aosong.com
50
34
  // no further documentation.
@@ -86,7 +70,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
86
70
  humidity = raw * 9.5367431640625e-5;
87
71
  raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
88
72
  temperature = raw * 1.9073486328125e-4 - 50;
89
- return this->crc8_(data, 6) == data[6];
73
+ return crc8(data, 6, 0xFF, 0x31, true) == data[6];
90
74
  }
91
75
 
92
76
  void AM2315C::setup() {
@@ -21,9 +21,9 @@
21
21
  // SOFTWARE.
22
22
  #pragma once
23
23
 
24
- #include "esphome/core/component.h"
25
- #include "esphome/components/sensor/sensor.h"
26
24
  #include "esphome/components/i2c/i2c.h"
25
+ #include "esphome/components/sensor/sensor.h"
26
+ #include "esphome/core/component.h"
27
27
 
28
28
  namespace esphome {
29
29
  namespace am2315c {
@@ -39,7 +39,6 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
39
39
  void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
40
40
 
41
41
  protected:
42
- uint8_t crc8_(uint8_t *data, uint8_t len);
43
42
  bool convert_(uint8_t *data, float &humidity, float &temperature);
44
43
  bool reset_register_(uint8_t reg);
45
44
 
@@ -24,7 +24,7 @@ from esphome.const import (
24
24
  CONF_TRIGGER_ID,
25
25
  CONF_VARIABLES,
26
26
  )
27
- from esphome.core import CORE, coroutine_with_priority
27
+ from esphome.core import CORE, CoroPriority, coroutine_with_priority
28
28
 
29
29
  DOMAIN = "api"
30
30
  DEPENDENCIES = ["network"]
@@ -134,7 +134,7 @@ CONFIG_SCHEMA = cv.All(
134
134
  )
135
135
 
136
136
 
137
- @coroutine_with_priority(40.0)
137
+ @coroutine_with_priority(CoroPriority.WEB)
138
138
  async def to_code(config):
139
139
  var = cg.new_Pvariable(config[CONF_ID])
140
140
  await cg.register_component(var, config)
@@ -112,7 +112,7 @@ void APIConnection::start() {
112
112
  APIError err = this->helper_->init();
113
113
  if (err != APIError::OK) {
114
114
  on_fatal_error();
115
- this->log_warning_("Helper init failed", err);
115
+ this->log_warning_(LOG_STR("Helper init failed"), err);
116
116
  return;
117
117
  }
118
118
  this->client_info_.peername = helper_->getpeername();
@@ -159,7 +159,7 @@ void APIConnection::loop() {
159
159
  break;
160
160
  } else if (err != APIError::OK) {
161
161
  on_fatal_error();
162
- this->log_warning_("Reading failed", err);
162
+ this->log_warning_(LOG_STR("Reading failed"), err);
163
163
  return;
164
164
  } else {
165
165
  this->last_traffic_ = now;
@@ -289,16 +289,26 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
289
289
  return 0; // Doesn't fit
290
290
  }
291
291
 
292
- // Allocate buffer space - pass payload size, allocation functions add header/footer space
293
- ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size)
294
- : conn->allocate_batch_message_buffer(calculated_size);
295
-
296
292
  // Get buffer size after allocation (which includes header padding)
297
293
  std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
298
- size_t size_before_encode = shared_buf.size();
294
+
295
+ if (is_single || conn->flags_.batch_first_message) {
296
+ // Single message or first batch message
297
+ conn->prepare_first_message_buffer(shared_buf, header_padding, total_calculated_size);
298
+ if (conn->flags_.batch_first_message) {
299
+ conn->flags_.batch_first_message = false;
300
+ }
301
+ } else {
302
+ // Batch message second or later
303
+ // Add padding for previous message footer + this message header
304
+ size_t current_size = shared_buf.size();
305
+ shared_buf.reserve(current_size + total_calculated_size);
306
+ shared_buf.resize(current_size + footer_size + header_padding);
307
+ }
299
308
 
300
309
  // Encode directly into buffer
301
- msg.encode(buffer);
310
+ size_t size_before_encode = shared_buf.size();
311
+ msg.encode({&shared_buf});
302
312
 
303
313
  // Calculate actual encoded size (not including header that was already added)
304
314
  size_t actual_payload_size = shared_buf.size() - size_before_encode;
@@ -1060,8 +1070,14 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
1060
1070
 
1061
1071
  #ifdef USE_HOMEASSISTANT_TIME
1062
1072
  void APIConnection::on_get_time_response(const GetTimeResponse &value) {
1063
- if (homeassistant::global_homeassistant_time != nullptr)
1073
+ if (homeassistant::global_homeassistant_time != nullptr) {
1064
1074
  homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds);
1075
+ #ifdef USE_TIME_TIMEZONE
1076
+ if (!value.timezone.empty() && value.timezone != homeassistant::global_homeassistant_time->get_timezone()) {
1077
+ homeassistant::global_homeassistant_time->set_timezone(value.timezone);
1078
+ }
1079
+ #endif
1080
+ }
1065
1081
  }
1066
1082
  #endif
1067
1083
 
@@ -1555,7 +1571,7 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
1555
1571
  return false;
1556
1572
  if (err != APIError::OK) {
1557
1573
  on_fatal_error();
1558
- this->log_warning_("Packet write failed", err);
1574
+ this->log_warning_(LOG_STR("Packet write failed"), err);
1559
1575
  return false;
1560
1576
  }
1561
1577
  // Do not set last_traffic_ on send
@@ -1616,14 +1632,6 @@ bool APIConnection::schedule_batch_() {
1616
1632
  return true;
1617
1633
  }
1618
1634
 
1619
- ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); }
1620
-
1621
- ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
1622
- ProtoWriteBuffer result = this->prepare_message_buffer(size, this->flags_.batch_first_message);
1623
- this->flags_.batch_first_message = false;
1624
- return result;
1625
- }
1626
-
1627
1635
  void APIConnection::process_batch_() {
1628
1636
  // Ensure PacketInfo remains trivially destructible for our placement new approach
1629
1637
  static_assert(std::is_trivially_destructible<PacketInfo>::value,
@@ -1731,7 +1739,7 @@ void APIConnection::process_batch_() {
1731
1739
  }
1732
1740
  remaining_size -= payload_size;
1733
1741
  // Calculate where the next message's header padding will start
1734
- // Current buffer size + footer space (that prepare_message_buffer will add for this message)
1742
+ // Current buffer size + footer space for this message
1735
1743
  current_offset = shared_buf.size() + footer_size;
1736
1744
  }
1737
1745
 
@@ -1750,7 +1758,7 @@ void APIConnection::process_batch_() {
1750
1758
  std::span<const PacketInfo>(packet_info, packet_count));
1751
1759
  if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
1752
1760
  on_fatal_error();
1753
- this->log_warning_("Batch write failed", err);
1761
+ this->log_warning_(LOG_STR("Batch write failed"), err);
1754
1762
  }
1755
1763
 
1756
1764
  #ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1828,11 +1836,14 @@ void APIConnection::process_state_subscriptions_() {
1828
1836
  }
1829
1837
  #endif // USE_API_HOMEASSISTANT_STATES
1830
1838
 
1831
- void APIConnection::log_warning_(const char *message, APIError err) {
1832
- ESP_LOGW(TAG, "%s: %s %s errno=%d", this->get_client_combined_info().c_str(), message, api_error_to_str(err), errno);
1839
+ void APIConnection::log_warning_(const LogString *message, APIError err) {
1840
+ ESP_LOGW(TAG, "%s: %s %s errno=%d", this->get_client_combined_info().c_str(), LOG_STR_ARG(message),
1841
+ LOG_STR_ARG(api_error_to_logstr(err)), errno);
1833
1842
  }
1834
1843
 
1835
- void APIConnection::log_socket_operation_failed_(APIError err) { this->log_warning_("Socket operation failed", err); }
1844
+ void APIConnection::log_socket_operation_failed_(APIError err) {
1845
+ this->log_warning_(LOG_STR("Socket operation failed"), err);
1846
+ }
1836
1847
 
1837
1848
  } // namespace esphome::api
1838
1849
  #endif
@@ -44,7 +44,7 @@ static constexpr size_t MAX_PACKETS_PER_BATCH = 64; // ESP32 has 8KB+ stack, HO
44
44
  static constexpr size_t MAX_PACKETS_PER_BATCH = 32; // ESP8266/RP2040/etc have smaller stacks
45
45
  #endif
46
46
 
47
- class APIConnection : public APIServerConnection {
47
+ class APIConnection final : public APIServerConnection {
48
48
  public:
49
49
  friend class APIServer;
50
50
  friend class ListEntitiesIterator;
@@ -252,44 +252,21 @@ class APIConnection : public APIServerConnection {
252
252
 
253
253
  // Get header padding size - used for both reserve and insert
254
254
  uint8_t header_padding = this->helper_->frame_header_padding();
255
-
256
255
  // Get shared buffer from parent server
257
256
  std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
257
+ this->prepare_first_message_buffer(shared_buf, header_padding,
258
+ reserve_size + header_padding + this->helper_->frame_footer_size());
259
+ return {&shared_buf};
260
+ }
261
+
262
+ void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t header_padding, size_t total_size) {
258
263
  shared_buf.clear();
259
264
  // Reserve space for header padding + message + footer
260
265
  // - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
261
266
  // - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
262
- shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
267
+ shared_buf.reserve(total_size);
263
268
  // Resize to add header padding so message encoding starts at the correct position
264
269
  shared_buf.resize(header_padding);
265
- return {&shared_buf};
266
- }
267
-
268
- // Prepare buffer for next message in batch
269
- ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
270
- // Get reference to shared buffer (it maintains state between batch messages)
271
- std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
272
-
273
- if (is_first_message) {
274
- shared_buf.clear();
275
- }
276
-
277
- size_t current_size = shared_buf.size();
278
-
279
- // Calculate padding to add:
280
- // - First message: just header padding
281
- // - Subsequent messages: footer for previous message + header padding for this message
282
- size_t padding_to_add = is_first_message
283
- ? this->helper_->frame_header_padding()
284
- : this->helper_->frame_header_padding() + this->helper_->frame_footer_size();
285
-
286
- // Reserve space for padding + message
287
- shared_buf.reserve(current_size + padding_to_add + message_size);
288
-
289
- // Resize to add the padding bytes
290
- shared_buf.resize(current_size + padding_to_add);
291
-
292
- return {&shared_buf};
293
270
  }
294
271
 
295
272
  bool try_to_clear_buffer(bool log_out_of_space);
@@ -297,10 +274,6 @@ class APIConnection : public APIServerConnection {
297
274
 
298
275
  std::string get_client_combined_info() const { return this->client_info_.get_combined_info(); }
299
276
 
300
- // Buffer allocator methods for batch processing
301
- ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
302
- ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
303
-
304
277
  protected:
305
278
  // Helper function to handle authentication completion
306
279
  void complete_authentication_();
@@ -328,9 +301,17 @@ class APIConnection : public APIServerConnection {
328
301
  APIConnection *conn, uint32_t remaining_size, bool is_single) {
329
302
  // Set common fields that are shared by all entity types
330
303
  msg.key = entity->get_object_id_hash();
331
- // IMPORTANT: get_object_id() may return a temporary std::string
332
- std::string object_id = entity->get_object_id();
333
- msg.set_object_id(StringRef(object_id));
304
+ // Try to use static reference first to avoid allocation
305
+ StringRef static_ref = entity->get_object_id_ref_for_api_();
306
+ // Store dynamic string outside the if-else to maintain lifetime
307
+ std::string object_id;
308
+ if (!static_ref.empty()) {
309
+ msg.set_object_id(static_ref);
310
+ } else {
311
+ // Dynamic case - need to allocate
312
+ object_id = entity->get_object_id();
313
+ msg.set_object_id(StringRef(object_id));
314
+ }
334
315
 
335
316
  if (entity->has_own_name()) {
336
317
  msg.set_name(entity->get_name());
@@ -751,7 +732,7 @@ class APIConnection : public APIServerConnection {
751
732
  }
752
733
 
753
734
  // Helper function to log API errors with errno
754
- void log_warning_(const char *message, APIError err);
735
+ void log_warning_(const LogString *message, APIError err);
755
736
  // Specific helper for duplicated error message
756
737
  void log_socket_operation_failed_(APIError err);
757
738
  };