esphome 2025.7.4__py3-none-any.whl → 2025.8.0__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 (758) hide show
  1. esphome/__main__.py +190 -83
  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 +112 -56
  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 +475 -116
  73. esphome/components/atm90e32/atm90e32.h +43 -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 +392 -81
  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 +203 -60
  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 +41 -2
  149. esphome/components/esp32_ble/ble.cpp +14 -10
  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 +139 -73
  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/font/__init__.py +9 -1
  209. esphome/components/fs3000/fs3000.cpp +0 -2
  210. esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +0 -2
  211. esphome/components/ft63x6/ft63x6.cpp +0 -1
  212. esphome/components/gdk101/gdk101.cpp +0 -1
  213. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +0 -1
  214. esphome/components/gl_r01_i2c/sensor.py +1 -1
  215. esphome/components/gpio/one_wire/gpio_one_wire.cpp +0 -1
  216. esphome/components/gpio/switch/gpio_switch.cpp +0 -2
  217. esphome/components/gpio_expander/cached_gpio.h +24 -15
  218. esphome/components/gps/__init__.py +6 -2
  219. esphome/components/gps/gps.cpp +50 -49
  220. esphome/components/gps/gps.h +4 -8
  221. esphome/components/gps/time/gps_time.cpp +3 -9
  222. esphome/components/gps/time/gps_time.h +4 -7
  223. esphome/components/graph/__init__.py +1 -1
  224. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +0 -1
  225. esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +0 -1
  226. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +21 -12
  227. esphome/components/gt911/touchscreen/gt911_touchscreen.h +26 -2
  228. esphome/components/haier/climate.py +5 -10
  229. esphome/components/haier/haier_base.cpp +0 -1
  230. esphome/components/hbridge/switch/hbridge_switch.cpp +0 -2
  231. esphome/components/hdc1080/hdc1080.cpp +0 -2
  232. esphome/components/heatpumpir/climate.py +2 -2
  233. esphome/components/hlw8012/hlw8012.cpp +0 -1
  234. esphome/components/hm3301/hm3301.cpp +0 -1
  235. esphome/components/hmc5883l/hmc5883l.cpp +0 -1
  236. esphome/components/homeassistant/__init__.py +1 -0
  237. esphome/components/homeassistant/number/__init__.py +1 -0
  238. esphome/components/homeassistant/number/homeassistant_number.cpp +11 -7
  239. esphome/components/homeassistant/switch/__init__.py +1 -0
  240. esphome/components/homeassistant/switch/homeassistant_switch.cpp +9 -5
  241. esphome/components/honeywellabp/honeywellabp.cpp +1 -4
  242. esphome/components/host/__init__.py +2 -0
  243. esphome/components/hte501/hte501.cpp +0 -1
  244. esphome/components/http_request/__init__.py +2 -3
  245. esphome/components/http_request/http_request_idf.cpp +2 -2
  246. esphome/components/htu21d/htu21d.cpp +0 -2
  247. esphome/components/htu31d/htu31d.cpp +0 -2
  248. esphome/components/hx711/hx711.cpp +0 -1
  249. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +0 -1
  250. esphome/components/hydreon_rgxx/sensor.py +4 -5
  251. esphome/components/i2c/i2c_bus.h +1 -1
  252. esphome/components/i2c/i2c_bus_arduino.cpp +1 -2
  253. esphome/components/i2c/i2c_bus_esp_idf.cpp +192 -17
  254. esphome/components/i2c/i2c_bus_esp_idf.h +11 -1
  255. esphome/components/i2s_audio/__init__.py +6 -5
  256. esphome/components/i2s_audio/i2s_audio.cpp +0 -2
  257. esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +1 -4
  258. esphome/components/i2s_audio/microphone/__init__.py +4 -6
  259. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +46 -19
  260. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +4 -3
  261. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +273 -269
  262. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +19 -34
  263. esphome/components/ili9xxx/display.py +4 -3
  264. esphome/components/ili9xxx/ili9xxx_display.cpp +0 -2
  265. esphome/components/image/__init__.py +123 -92
  266. esphome/components/improv_serial/__init__.py +7 -8
  267. esphome/components/ina219/ina219.cpp +0 -1
  268. esphome/components/ina226/ina226.cpp +0 -2
  269. esphome/components/ina260/ina260.cpp +0 -2
  270. esphome/components/ina2xx_base/__init__.py +2 -5
  271. esphome/components/ina2xx_base/ina2xx_base.cpp +0 -2
  272. esphome/components/ina3221/ina3221.cpp +0 -1
  273. esphome/components/internal_temperature/internal_temperature.cpp +0 -2
  274. esphome/components/interval/interval.h +5 -9
  275. esphome/components/json/__init__.py +1 -1
  276. esphome/components/kmeteriso/kmeteriso.cpp +0 -2
  277. esphome/components/lc709203f/lc709203f.cpp +0 -2
  278. esphome/components/lcd_gpio/display.py +1 -3
  279. esphome/components/lcd_gpio/gpio_lcd_display.cpp +0 -1
  280. esphome/components/lcd_pcf8574/pcf8574_display.cpp +0 -1
  281. esphome/components/ld2410/__init__.py +4 -6
  282. esphome/components/ld2410/binary_sensor.py +4 -0
  283. esphome/components/ld2410/ld2410.cpp +56 -100
  284. esphome/components/ld2410/ld2410.h +17 -15
  285. esphome/components/ld2410/sensor.py +24 -10
  286. esphome/components/ld2412/__init__.py +46 -0
  287. esphome/components/ld2412/binary_sensor.py +70 -0
  288. esphome/components/ld2412/button/__init__.py +74 -0
  289. esphome/components/ld2412/button/factory_reset_button.cpp +9 -0
  290. esphome/components/ld2412/button/factory_reset_button.h +18 -0
  291. esphome/components/ld2412/button/query_button.cpp +9 -0
  292. esphome/components/ld2412/button/query_button.h +18 -0
  293. esphome/components/ld2412/button/restart_button.cpp +9 -0
  294. esphome/components/ld2412/button/restart_button.h +18 -0
  295. esphome/components/ld2412/button/start_dynamic_background_correction_button.cpp +11 -0
  296. esphome/components/ld2412/button/start_dynamic_background_correction_button.h +18 -0
  297. esphome/components/ld2412/ld2412.cpp +861 -0
  298. esphome/components/ld2412/ld2412.h +141 -0
  299. esphome/components/ld2412/number/__init__.py +126 -0
  300. esphome/components/ld2412/number/gate_threshold_number.cpp +14 -0
  301. esphome/components/ld2412/number/gate_threshold_number.h +19 -0
  302. esphome/components/ld2412/number/light_threshold_number.cpp +12 -0
  303. esphome/components/ld2412/number/light_threshold_number.h +18 -0
  304. esphome/components/ld2412/number/max_distance_timeout_number.cpp +12 -0
  305. esphome/components/ld2412/number/max_distance_timeout_number.h +18 -0
  306. esphome/components/ld2412/select/__init__.py +82 -0
  307. esphome/components/ld2412/select/baud_rate_select.cpp +12 -0
  308. esphome/components/ld2412/select/baud_rate_select.h +18 -0
  309. esphome/components/ld2412/select/distance_resolution_select.cpp +12 -0
  310. esphome/components/ld2412/select/distance_resolution_select.h +18 -0
  311. esphome/components/ld2412/select/light_out_control_select.cpp +12 -0
  312. esphome/components/ld2412/select/light_out_control_select.h +18 -0
  313. esphome/components/ld2412/sensor.py +124 -0
  314. esphome/components/ld2412/switch/__init__.py +45 -0
  315. esphome/components/ld2412/switch/bluetooth_switch.cpp +12 -0
  316. esphome/components/ld2412/switch/bluetooth_switch.h +18 -0
  317. esphome/components/ld2412/switch/engineering_mode_switch.cpp +12 -0
  318. esphome/components/ld2412/switch/engineering_mode_switch.h +18 -0
  319. esphome/components/ld2412/text_sensor.py +34 -0
  320. esphome/components/ld2420/ld2420.cpp +0 -1
  321. esphome/components/ld2450/__init__.py +3 -4
  322. esphome/components/ld2450/binary_sensor.py +3 -0
  323. esphome/components/ld2450/ld2450.cpp +77 -165
  324. esphome/components/ld2450/ld2450.h +26 -54
  325. esphome/components/ld2450/sensor.py +120 -6
  326. esphome/components/ld2450/text_sensor.py +5 -4
  327. esphome/components/ld24xx/__init__.py +1 -0
  328. esphome/components/ld24xx/ld24xx.h +65 -0
  329. esphome/components/ledc/ledc_output.cpp +0 -1
  330. esphome/components/libretiny/__init__.py +2 -0
  331. esphome/components/light/__init__.py +0 -1
  332. esphome/components/light/effects.py +70 -45
  333. esphome/components/light/light_call.cpp +101 -66
  334. esphome/components/light/light_color_values.h +16 -11
  335. esphome/components/light/light_json_schema.cpp +46 -44
  336. esphome/components/light/light_state.cpp +8 -11
  337. esphome/components/light/light_traits.h +17 -0
  338. esphome/components/lightwaverf/lightwaverf.cpp +0 -2
  339. esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +0 -1
  340. esphome/components/lock/__init__.py +0 -1
  341. esphome/components/logger/__init__.py +31 -9
  342. esphome/components/logger/logger.cpp +12 -7
  343. esphome/components/logger/logger.h +25 -14
  344. esphome/components/logger/logger_esp32.cpp +2 -7
  345. esphome/components/logger/logger_esp8266.cpp +2 -4
  346. esphome/components/logger/logger_host.cpp +2 -4
  347. esphome/components/logger/logger_libretiny.cpp +2 -4
  348. esphome/components/logger/logger_rp2040.cpp +2 -4
  349. esphome/components/logger/logger_zephyr.cpp +86 -0
  350. esphome/components/logger/select/logger_level_select.cpp +2 -4
  351. esphome/components/logger/select/logger_level_select.h +2 -4
  352. esphome/components/logger/task_log_buffer.cpp +2 -4
  353. esphome/components/logger/task_log_buffer.h +2 -4
  354. esphome/components/lps22/sensor.py +5 -5
  355. esphome/components/ltr390/ltr390.cpp +0 -2
  356. esphome/components/ltr501/ltr501.cpp +0 -1
  357. esphome/components/ltr_als_ps/ltr_als_ps.cpp +0 -1
  358. esphome/components/lvgl/__init__.py +14 -13
  359. esphome/components/lvgl/automation.py +2 -4
  360. esphome/components/lvgl/defines.py +0 -2
  361. esphome/components/lvgl/helpers.py +1 -1
  362. esphome/components/lvgl/lv_validation.py +7 -4
  363. esphome/components/lvgl/lvgl_esphome.cpp +2 -3
  364. esphome/components/lvgl/styles.py +2 -2
  365. esphome/components/lvgl/types.py +1 -1
  366. esphome/components/lvgl/widgets/__init__.py +2 -2
  367. esphome/components/lvgl/widgets/arc.py +14 -11
  368. esphome/components/lvgl/widgets/buttonmatrix.py +1 -1
  369. esphome/components/lvgl/widgets/qrcode.py +7 -7
  370. esphome/components/lvgl/widgets/spinner.py +6 -6
  371. esphome/components/lvgl/widgets/switch.py +2 -2
  372. esphome/components/lvgl/widgets/tabview.py +3 -3
  373. esphome/components/lvgl/widgets/tileview.py +15 -7
  374. esphome/components/m5stack_8angle/m5stack_8angle.cpp +0 -1
  375. esphome/components/matrix_keypad/__init__.py +4 -3
  376. esphome/components/max17043/max17043.cpp +0 -2
  377. esphome/components/max31855/max31855.cpp +1 -4
  378. esphome/components/max31856/max31856.cpp +0 -4
  379. esphome/components/max31865/max31865.cpp +0 -1
  380. esphome/components/max44009/max44009.cpp +0 -1
  381. esphome/components/max6675/max6675.cpp +1 -4
  382. esphome/components/max6956/max6956.cpp +0 -1
  383. esphome/components/max7219/max7219.cpp +0 -1
  384. esphome/components/max7219digit/display.py +1 -1
  385. esphome/components/max7219digit/max7219digit.cpp +0 -1
  386. esphome/components/max9611/max9611.cpp +0 -1
  387. esphome/components/mcp23008/__init__.py +1 -1
  388. esphome/components/mcp23008/mcp23008.cpp +0 -1
  389. esphome/components/mcp23016/mcp23016.cpp +0 -1
  390. esphome/components/mcp23017/__init__.py +1 -1
  391. esphome/components/mcp23017/mcp23017.cpp +0 -1
  392. esphome/components/mcp23s08/__init__.py +1 -1
  393. esphome/components/mcp23s08/mcp23s08.cpp +0 -1
  394. esphome/components/mcp23s17/__init__.py +1 -1
  395. esphome/components/mcp23s17/mcp23s17.cpp +0 -1
  396. esphome/components/mcp23x08_base/__init__.py +2 -0
  397. esphome/components/mcp23x08_base/mcp23x08_base.cpp +9 -7
  398. esphome/components/mcp23x08_base/mcp23x08_base.h +9 -4
  399. esphome/components/mcp23x17_base/__init__.py +2 -0
  400. esphome/components/mcp23x17_base/mcp23x17_base.cpp +20 -7
  401. esphome/components/mcp23x17_base/mcp23x17_base.h +9 -4
  402. esphome/components/mcp23xxx_base/__init__.py +11 -5
  403. esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +15 -12
  404. esphome/components/mcp23xxx_base/mcp23xxx_base.h +8 -7
  405. esphome/components/mcp3008/mcp3008.cpp +1 -4
  406. esphome/components/mcp3204/mcp3204.cpp +1 -4
  407. esphome/components/mcp4461/mcp4461.cpp +0 -1
  408. esphome/components/mcp4725/mcp4725.cpp +0 -1
  409. esphome/components/mcp4728/mcp4728.cpp +0 -1
  410. esphome/components/mcp9600/mcp9600.cpp +0 -2
  411. esphome/components/mcp9808/mcp9808.cpp +0 -2
  412. esphome/components/mdns/__init__.py +3 -0
  413. esphome/components/mdns/mdns_component.cpp +2 -0
  414. esphome/components/mdns/mdns_component.h +4 -0
  415. esphome/components/media_player/__init__.py +40 -0
  416. esphome/components/media_player/automation.h +16 -0
  417. esphome/components/media_player/media_player.cpp +13 -0
  418. esphome/components/media_player/media_player.h +50 -3
  419. esphome/components/micro_wake_word/micro_wake_word.cpp +0 -3
  420. esphome/components/mics_4514/mics_4514.cpp +1 -6
  421. esphome/components/midea/ir_transmitter.h +4 -4
  422. esphome/components/mipi/__init__.py +416 -0
  423. esphome/components/mipi_dsi/__init__.py +5 -0
  424. esphome/components/mipi_dsi/display.py +233 -0
  425. esphome/components/mipi_dsi/mipi_dsi.cpp +379 -0
  426. esphome/components/mipi_dsi/mipi_dsi.h +123 -0
  427. esphome/components/mipi_dsi/models/__init__.py +0 -0
  428. esphome/components/mipi_dsi/models/guition.py +38 -0
  429. esphome/components/mipi_dsi/models/m5stack.py +57 -0
  430. esphome/components/mipi_dsi/models/waveshare.py +105 -0
  431. esphome/components/mipi_rgb/models/lilygo.py +0 -0
  432. esphome/components/mipi_spi/__init__.py +0 -9
  433. esphome/components/mipi_spi/display.py +220 -256
  434. esphome/components/mipi_spi/mipi_spi.cpp +1 -485
  435. esphome/components/mipi_spi/mipi_spi.h +556 -108
  436. esphome/components/mipi_spi/models/__init__.py +0 -65
  437. esphome/components/mipi_spi/models/adafruit.py +30 -0
  438. esphome/components/mipi_spi/models/amoled.py +41 -5
  439. esphome/components/mipi_spi/models/ili.py +5 -5
  440. esphome/components/mipi_spi/models/jc.py +1 -3
  441. esphome/components/mipi_spi/models/lilygo.py +1 -1
  442. esphome/components/mipi_spi/models/waveshare.py +16 -1
  443. esphome/components/mixer/speaker/__init__.py +4 -5
  444. esphome/components/mlx90393/sensor.py +7 -5
  445. esphome/components/mlx90393/sensor_mlx90393.cpp +0 -1
  446. esphome/components/mlx90614/mlx90614.cpp +0 -1
  447. esphome/components/mmc5603/mmc5603.cpp +0 -1
  448. esphome/components/mmc5983/mmc5983.cpp +0 -2
  449. esphome/components/mpl3115a2/mpl3115a2.cpp +0 -2
  450. esphome/components/mpr121/__init__.py +7 -6
  451. esphome/components/mpr121/mpr121.cpp +0 -1
  452. esphome/components/mpu6050/mpu6050.cpp +0 -1
  453. esphome/components/mpu6886/mpu6886.cpp +0 -1
  454. esphome/components/mqtt/__init__.py +1 -2
  455. esphome/components/mqtt/mqtt_button.cpp +1 -1
  456. esphome/components/mqtt/mqtt_client.cpp +0 -1
  457. esphome/components/mqtt/mqtt_component.cpp +8 -14
  458. esphome/components/mqtt/mqtt_component.h +0 -7
  459. esphome/components/mqtt/mqtt_sensor.cpp +0 -1
  460. esphome/components/mqtt/mqtt_sensor.h +0 -1
  461. esphome/components/mqtt/mqtt_text_sensor.cpp +0 -1
  462. esphome/components/mqtt/mqtt_text_sensor.h +0 -1
  463. esphome/components/ms5611/ms5611.cpp +0 -1
  464. esphome/components/ms8607/ms8607.cpp +0 -1
  465. esphome/components/msa3xx/msa3xx.cpp +0 -2
  466. esphome/components/my9231/my9231.cpp +0 -2
  467. esphome/components/nau7802/nau7802.cpp +0 -1
  468. esphome/components/neopixelbus/light.py +3 -0
  469. esphome/components/network/util.cpp +29 -0
  470. esphome/components/nextion/nextion.cpp +2 -2
  471. esphome/components/nfc/binary_sensor/{binary_sensor.cpp → nfc_binary_sensor.cpp} +1 -1
  472. esphome/components/npi19/npi19.cpp +0 -2
  473. esphome/components/nrf52/__init__.py +223 -0
  474. esphome/components/nrf52/boards.py +34 -0
  475. esphome/components/nrf52/const.py +18 -0
  476. esphome/components/nrf52/gpio.py +79 -0
  477. esphome/components/number/__init__.py +2 -1
  478. esphome/components/one_wire/__init__.py +1 -2
  479. esphome/components/one_wire/one_wire.cpp +0 -2
  480. esphome/components/one_wire/one_wire.h +0 -2
  481. esphome/components/opentherm/hub.cpp +0 -1
  482. esphome/components/opentherm/number/__init__.py +2 -2
  483. esphome/components/openthread/__init__.py +2 -3
  484. esphome/components/openthread/openthread.cpp +30 -13
  485. esphome/components/openthread/openthread.h +3 -0
  486. esphome/components/openthread/openthread_esp.cpp +3 -1
  487. esphome/components/opt3001/sensor.py +2 -6
  488. esphome/components/output/__init__.py +38 -0
  489. esphome/components/output/automation.h +24 -0
  490. esphome/components/output/switch/output_switch.cpp +0 -2
  491. esphome/components/packages/__init__.py +1 -2
  492. esphome/components/packet_transport/__init__.py +4 -3
  493. esphome/components/pca6416a/pca6416a.cpp +0 -1
  494. esphome/components/pca9554/pca9554.cpp +0 -1
  495. esphome/components/pca9685/pca9685_output.cpp +0 -2
  496. esphome/components/pcf85063/pcf85063.cpp +0 -1
  497. esphome/components/pcf8563/pcf8563.cpp +0 -1
  498. esphome/components/pcf8574/pcf8574.cpp +0 -1
  499. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +0 -1
  500. esphome/components/pipsolar/pipsolar.cpp +54 -42
  501. esphome/components/pipsolar/pipsolar.h +5 -4
  502. esphome/components/pipsolar/sensor/__init__.py +1 -1
  503. esphome/components/pm2005/pm2005.cpp +0 -1
  504. esphome/components/pmsa003i/pmsa003i.cpp +0 -2
  505. esphome/components/pmwcs3/sensor.py +1 -2
  506. esphome/components/pn532/pn532.cpp +0 -2
  507. esphome/components/pn532_spi/pn532_spi.cpp +0 -2
  508. esphome/components/power_supply/power_supply.cpp +7 -10
  509. esphome/components/power_supply/power_supply.h +1 -1
  510. esphome/components/psram/__init__.py +6 -1
  511. esphome/components/pulse_counter/pulse_counter_sensor.cpp +0 -1
  512. esphome/components/pulse_counter/sensor.py +9 -6
  513. esphome/components/pylontech/pylontech.cpp +0 -1
  514. esphome/components/qmc5883l/qmc5883l.cpp +0 -1
  515. esphome/components/qmp6988/qmp6988.cpp +0 -2
  516. esphome/components/qspi_dbi/display.py +2 -3
  517. esphome/components/qspi_dbi/qspi_dbi.cpp +0 -2
  518. esphome/components/qwiic_pir/binary_sensor.py +2 -3
  519. esphome/components/qwiic_pir/qwiic_pir.cpp +0 -2
  520. esphome/components/rc522/rc522.cpp +9 -31
  521. esphome/components/rc522_spi/rc522_spi.cpp +0 -1
  522. esphome/components/remote_base/__init__.py +5 -6
  523. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -1
  524. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +0 -1
  525. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -1
  526. esphome/components/remote_transmitter/__init__.py +26 -0
  527. esphome/components/remote_transmitter/automation.h +18 -0
  528. esphome/components/remote_transmitter/remote_transmitter.h +2 -1
  529. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -1
  530. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  531. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  532. esphome/components/resampler/speaker/__init__.py +4 -5
  533. esphome/components/rf_bridge/__init__.py +4 -8
  534. esphome/components/rotary_encoder/rotary_encoder.cpp +0 -2
  535. esphome/components/rp2040/__init__.py +3 -1
  536. esphome/components/rp2040_pio_led_strip/led_strip.cpp +0 -2
  537. esphome/components/rp2040_pio_led_strip/light.py +1 -2
  538. esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -5
  539. esphome/components/rpi_dpi_rgb/display.py +13 -15
  540. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -3
  541. esphome/components/runtime_stats/__init__.py +34 -0
  542. esphome/components/runtime_stats/runtime_stats.cpp +102 -0
  543. esphome/components/runtime_stats/runtime_stats.h +132 -0
  544. esphome/components/scd30/scd30.cpp +0 -2
  545. esphome/components/scd30/sensor.py +1 -2
  546. esphome/components/scd4x/scd4x.cpp +0 -1
  547. esphome/components/scd4x/sensor.py +1 -3
  548. esphome/components/sdl/display.py +3 -1
  549. esphome/components/sdl/sdl_esphome.cpp +0 -2
  550. esphome/components/sdp3x/sdp3x.cpp +0 -2
  551. esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +0 -2
  552. esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +0 -3
  553. esphome/components/select/__init__.py +2 -3
  554. esphome/components/select/select_traits.cpp +1 -1
  555. esphome/components/select/select_traits.h +1 -1
  556. esphome/components/sen0321/sen0321.cpp +0 -1
  557. esphome/components/sen5x/sen5x.cpp +0 -2
  558. esphome/components/senseair/senseair.cpp +7 -3
  559. esphome/components/senseair/senseair.h +11 -0
  560. esphome/components/sensor/__init__.py +36 -4
  561. esphome/components/sensor/filter.cpp +49 -10
  562. esphome/components/sensor/filter.h +22 -7
  563. esphome/components/sensor/sensor.cpp +0 -1
  564. esphome/components/sensor/sensor.h +0 -9
  565. esphome/components/sfa30/sfa30.cpp +0 -4
  566. esphome/components/sgp30/sgp30.cpp +0 -2
  567. esphome/components/sgp4x/sensor.py +1 -1
  568. esphome/components/sgp4x/sgp4x.cpp +0 -2
  569. esphome/components/shelly_dimmer/shelly_dimmer.cpp +0 -2
  570. esphome/components/sht3xd/sht3xd.cpp +0 -2
  571. esphome/components/sht4x/sht4x.cpp +0 -2
  572. esphome/components/shtcx/shtcx.cpp +0 -1
  573. esphome/components/sim800l/__init__.py +2 -4
  574. esphome/components/sm16716/sm16716.cpp +0 -1
  575. esphome/components/sm2135/sm2135.cpp +0 -1
  576. esphome/components/sm2235/sm2235.cpp +0 -1
  577. esphome/components/sm2335/sm2335.cpp +0 -1
  578. esphome/components/sn74hc165/sn74hc165.cpp +0 -1
  579. esphome/components/sn74hc595/sn74hc595.cpp +0 -1
  580. esphome/components/sntp/sntp_component.cpp +0 -1
  581. esphome/components/sound_level/sensor.py +1 -1
  582. esphome/components/speaker/media_player/__init__.py +21 -33
  583. esphome/components/speaker/media_player/audio_pipeline.cpp +4 -7
  584. esphome/components/spi/__init__.py +29 -13
  585. esphome/components/spi/spi.cpp +0 -2
  586. esphome/components/spi_device/spi_device.cpp +1 -4
  587. esphome/components/sprinkler/__init__.py +4 -4
  588. esphome/components/sps30/sps30.cpp +0 -1
  589. esphome/components/ssd1306_base/__init__.py +11 -11
  590. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  591. esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +0 -1
  592. esphome/components/ssd1306_spi/ssd1306_spi.cpp +0 -1
  593. esphome/components/ssd1322_spi/ssd1322_spi.cpp +0 -1
  594. esphome/components/ssd1325_spi/ssd1325_spi.cpp +0 -1
  595. esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +0 -1
  596. esphome/components/ssd1327_spi/ssd1327_spi.cpp +0 -1
  597. esphome/components/ssd1331_spi/ssd1331_spi.cpp +0 -1
  598. esphome/components/ssd1351_spi/ssd1351_spi.cpp +0 -1
  599. esphome/components/st7567_i2c/st7567_i2c.cpp +0 -1
  600. esphome/components/st7567_spi/st7567_spi.cpp +0 -1
  601. esphome/components/st7701s/display.py +10 -14
  602. esphome/components/st7701s/st7701s.cpp +0 -3
  603. esphome/components/st7735/st7735.cpp +0 -1
  604. esphome/components/st7789v/st7789v.cpp +0 -1
  605. esphome/components/st7920/st7920.cpp +0 -1
  606. esphome/components/status_led/light/status_led_light.cpp +0 -2
  607. esphome/components/status_led/status_led.cpp +0 -1
  608. esphome/components/stepper/__init__.py +2 -4
  609. esphome/components/sts3x/sts3x.cpp +0 -1
  610. esphome/components/substitutions/__init__.py +10 -16
  611. esphome/components/substitutions/jinja.py +24 -1
  612. esphome/components/sun/__init__.py +2 -3
  613. esphome/components/switch/__init__.py +31 -1
  614. esphome/components/switch/automation.h +24 -0
  615. esphome/components/switch/switch.cpp +8 -0
  616. esphome/components/switch/switch.h +8 -0
  617. esphome/components/sx126x/sx126x.cpp +0 -2
  618. esphome/components/sx127x/sx127x.cpp +0 -2
  619. esphome/components/sx1509/__init__.py +7 -5
  620. esphome/components/sx1509/output/sx1509_float_output.cpp +1 -1
  621. esphome/components/sx1509/sx1509.cpp +0 -2
  622. esphome/components/syslog/esphome_syslog.cpp +1 -1
  623. esphome/components/tc74/tc74.cpp +0 -1
  624. esphome/components/tca9548a/tca9548a.cpp +0 -1
  625. esphome/components/tca9555/tca9555.cpp +0 -1
  626. esphome/components/tcs34725/tcs34725.cpp +0 -1
  627. esphome/components/tee501/tee501.cpp +0 -1
  628. esphome/components/tem3200/tem3200.cpp +0 -2
  629. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +0 -1
  630. esphome/components/template/cover/template_cover.cpp +0 -1
  631. esphome/components/template/select/template_select.cpp +0 -1
  632. esphome/components/template/text/template_text.cpp +0 -2
  633. esphome/components/template/valve/template_valve.cpp +0 -1
  634. esphome/components/text/__init__.py +0 -1
  635. esphome/components/text/text_traits.h +2 -0
  636. esphome/components/text_sensor/__init__.py +2 -1
  637. esphome/components/text_sensor/text_sensor.cpp +0 -2
  638. esphome/components/text_sensor/text_sensor.h +0 -8
  639. esphome/components/thermostat/climate.py +4 -4
  640. esphome/components/time/__init__.py +7 -4
  641. esphome/components/time/real_time_clock.cpp +16 -3
  642. esphome/components/tlc59208f/tlc59208f_output.cpp +0 -2
  643. esphome/components/tlc5947/tlc5947.cpp +0 -2
  644. esphome/components/tlc5971/tlc5971.cpp +0 -2
  645. esphome/components/tm1621/tm1621.cpp +0 -2
  646. esphome/components/tm1637/tm1637.cpp +0 -2
  647. esphome/components/tm1638/tm1638.cpp +0 -2
  648. esphome/components/tm1651/__init__.py +45 -48
  649. esphome/components/tm1651/tm1651.cpp +213 -47
  650. esphome/components/tm1651/tm1651.h +37 -32
  651. esphome/components/tmp117/tmp117.cpp +0 -2
  652. esphome/components/tsl2561/tsl2561.cpp +0 -1
  653. esphome/components/tsl2591/tsl2591.cpp +0 -1
  654. esphome/components/tt21100/touchscreen/tt21100.cpp +0 -2
  655. esphome/components/ttp229_bsf/ttp229_bsf.cpp +0 -1
  656. esphome/components/ttp229_lsf/ttp229_lsf.cpp +0 -1
  657. esphome/components/tuya/climate/__init__.py +9 -10
  658. esphome/components/tuya/number/__init__.py +8 -6
  659. esphome/components/tx20/tx20.cpp +0 -1
  660. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -1
  661. esphome/components/uart/uart_component_esp8266.cpp +0 -1
  662. esphome/components/uart/uart_component_esp_idf.cpp +0 -2
  663. esphome/components/uart/uart_component_libretiny.cpp +0 -2
  664. esphome/components/uart/uart_component_rp2040.cpp +0 -2
  665. esphome/components/udp/__init__.py +1 -1
  666. esphome/components/ufire_ec/sensor.py +1 -2
  667. esphome/components/ufire_ec/ufire_ec.cpp +0 -2
  668. esphome/components/ufire_ise/sensor.py +1 -2
  669. esphome/components/ufire_ise/ufire_ise.cpp +0 -2
  670. esphome/components/ultrasonic/ultrasonic_sensor.cpp +0 -1
  671. esphome/components/update/__init__.py +0 -1
  672. esphome/components/uptime/sensor/uptime_seconds_sensor.cpp +0 -1
  673. esphome/components/uptime/sensor/uptime_seconds_sensor.h +0 -2
  674. esphome/components/usb_host/usb_host_client.cpp +0 -1
  675. esphome/components/usb_host/usb_host_component.cpp +0 -1
  676. esphome/components/valve/__init__.py +0 -1
  677. esphome/components/veml3235/veml3235.cpp +0 -3
  678. esphome/components/veml7700/veml7700.cpp +0 -2
  679. esphome/components/version/version_text_sensor.cpp +0 -1
  680. esphome/components/version/version_text_sensor.h +0 -1
  681. esphome/components/vl53l0x/vl53l0x_sensor.cpp +0 -4
  682. esphome/components/voice_assistant/voice_assistant.cpp +9 -8
  683. esphome/components/web_server/__init__.py +13 -0
  684. esphome/components/web_server/web_server.cpp +188 -353
  685. esphome/components/web_server/web_server.h +61 -1
  686. esphome/components/web_server_base/__init__.py +1 -1
  687. esphome/components/web_server_base/web_server_base.cpp +2 -0
  688. esphome/components/web_server_base/web_server_base.h +6 -0
  689. esphome/components/web_server_idf/web_server_idf.cpp +10 -8
  690. esphome/components/web_server_idf/web_server_idf.h +2 -0
  691. esphome/components/weikai_i2c/weikai_i2c.cpp +1 -2
  692. esphome/components/weikai_spi/weikai_spi.cpp +1 -1
  693. esphome/components/wifi/__init__.py +17 -43
  694. esphome/components/wifi/wifi_component.cpp +100 -36
  695. esphome/components/wifi/wifi_component.h +5 -1
  696. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -0
  697. esphome/components/wifi/wifi_component_esp_idf.cpp +30 -0
  698. esphome/components/wifi_info/wifi_info_text_sensor.h +0 -6
  699. esphome/components/wifi_signal/wifi_signal_sensor.h +0 -1
  700. esphome/components/wireguard/wireguard.cpp +0 -2
  701. esphome/components/x9c/x9c.cpp +0 -2
  702. esphome/components/xgzp68xx/xgzp68xx.cpp +0 -1
  703. esphome/components/xl9535/xl9535.cpp +0 -2
  704. esphome/components/zephyr/__init__.py +252 -0
  705. esphome/components/zephyr/const.py +16 -0
  706. esphome/components/zephyr/core.cpp +90 -0
  707. esphome/components/zephyr/gpio.cpp +120 -0
  708. esphome/components/zephyr/gpio.h +38 -0
  709. esphome/components/zephyr/pre_build.py.script +4 -0
  710. esphome/components/zephyr/preferences.cpp +156 -0
  711. esphome/components/zephyr/preferences.h +13 -0
  712. esphome/config.py +38 -16
  713. esphome/config_helpers.py +1 -2
  714. esphome/config_validation.py +12 -16
  715. esphome/const.py +26 -1
  716. esphome/core/__init__.py +92 -51
  717. esphome/core/application.cpp +75 -21
  718. esphome/core/application.h +106 -171
  719. esphome/core/color.h +10 -0
  720. esphome/core/component.cpp +41 -25
  721. esphome/core/component.h +9 -6
  722. esphome/core/component_iterator.cpp +61 -261
  723. esphome/core/component_iterator.h +15 -0
  724. esphome/core/config.py +26 -11
  725. esphome/core/defines.h +40 -2
  726. esphome/core/entity_base.h +18 -0
  727. esphome/core/entity_helpers.py +45 -10
  728. esphome/core/helpers.cpp +8 -15
  729. esphome/core/helpers.h +60 -6
  730. esphome/core/lock_free_queue.h +1 -1
  731. esphome/core/scheduler.cpp +311 -77
  732. esphome/core/scheduler.h +141 -28
  733. esphome/cpp_generator.py +2 -6
  734. esphome/cpp_helpers.py +1 -1
  735. esphome/dashboard/dashboard.py +2 -3
  736. esphome/dashboard/dns.py +2 -8
  737. esphome/dashboard/web_server.py +34 -19
  738. esphome/espota2.py +1 -4
  739. esphome/git.py +3 -1
  740. esphome/helpers.py +23 -4
  741. esphome/log.py +3 -1
  742. esphome/mqtt.py +3 -5
  743. esphome/platformio_api.py +7 -4
  744. esphome/types.py +12 -0
  745. esphome/util.py +20 -8
  746. esphome/voluptuous_schema.py +4 -3
  747. esphome/vscode.py +1 -2
  748. esphome/wizard.py +1 -4
  749. esphome/writer.py +16 -108
  750. esphome/yaml_util.py +7 -5
  751. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/METADATA +13 -14
  752. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/RECORD +757 -677
  753. esphome/components/mipi_spi/models/commands.py +0 -82
  754. /esphome/components/nfc/binary_sensor/{binary_sensor.h → nfc_binary_sensor.h} +0 -0
  755. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/WHEEL +0 -0
  756. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/entry_points.txt +0 -0
  757. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/licenses/LICENSE +0 -0
  758. {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  #include "api_frame_helper.h"
2
2
  #ifdef USE_API
3
+ #include "api_connection.h" // For ClientInfo struct
3
4
  #include "esphome/core/application.h"
4
5
  #include "esphome/core/hal.h"
5
6
  #include "esphome/core/helpers.h"
@@ -8,10 +9,19 @@
8
9
  #include <cstring>
9
10
  #include <cinttypes>
10
11
 
11
- namespace esphome {
12
- namespace api {
12
+ namespace esphome::api {
13
13
 
14
- static const char *const TAG = "api.socket";
14
+ static const char *const TAG = "api.frame_helper";
15
+
16
+ #define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->client_info_->get_combined_info().c_str(), ##__VA_ARGS__)
17
+
18
+ #ifdef HELPER_LOG_PACKETS
19
+ #define LOG_PACKET_RECEIVED(buffer) ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(buffer).c_str())
20
+ #define LOG_PACKET_SENDING(data, len) ESP_LOGVV(TAG, "Sending raw: %s", format_hex_pretty(data, len).c_str())
21
+ #else
22
+ #define LOG_PACKET_RECEIVED(buffer) ((void) 0)
23
+ #define LOG_PACKET_SENDING(data, len) ((void) 0)
24
+ #endif
15
25
 
16
26
  const char *api_error_to_str(APIError err) {
17
27
  // not using switch to ensure compiler doesn't try to build a big table out of it
@@ -19,8 +29,6 @@ const char *api_error_to_str(APIError err) {
19
29
  return "OK";
20
30
  } else if (err == APIError::WOULD_BLOCK) {
21
31
  return "WOULD_BLOCK";
22
- } else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) {
23
- return "BAD_HANDSHAKE_PACKET_LEN";
24
32
  } else if (err == APIError::BAD_INDICATOR) {
25
33
  return "BAD_INDICATOR";
26
34
  } else if (err == APIError::BAD_DATA_PACKET) {
@@ -41,6 +49,14 @@ const char *api_error_to_str(APIError err) {
41
49
  return "SOCKET_READ_FAILED";
42
50
  } else if (err == APIError::SOCKET_WRITE_FAILED) {
43
51
  return "SOCKET_WRITE_FAILED";
52
+ } else if (err == APIError::OUT_OF_MEMORY) {
53
+ return "OUT_OF_MEMORY";
54
+ } else if (err == APIError::CONNECTION_CLOSED) {
55
+ return "CONNECTION_CLOSED";
56
+ }
57
+ #ifdef USE_API_NOISE
58
+ else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) {
59
+ return "BAD_HANDSHAKE_PACKET_LEN";
44
60
  } else if (err == APIError::HANDSHAKESTATE_READ_FAILED) {
45
61
  return "HANDSHAKESTATE_READ_FAILED";
46
62
  } else if (err == APIError::HANDSHAKESTATE_WRITE_FAILED) {
@@ -51,17 +67,14 @@ const char *api_error_to_str(APIError err) {
51
67
  return "CIPHERSTATE_DECRYPT_FAILED";
52
68
  } else if (err == APIError::CIPHERSTATE_ENCRYPT_FAILED) {
53
69
  return "CIPHERSTATE_ENCRYPT_FAILED";
54
- } else if (err == APIError::OUT_OF_MEMORY) {
55
- return "OUT_OF_MEMORY";
56
70
  } else if (err == APIError::HANDSHAKESTATE_SETUP_FAILED) {
57
71
  return "HANDSHAKESTATE_SETUP_FAILED";
58
72
  } else if (err == APIError::HANDSHAKESTATE_SPLIT_FAILED) {
59
73
  return "HANDSHAKESTATE_SPLIT_FAILED";
60
74
  } else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
61
75
  return "BAD_HANDSHAKE_ERROR_BYTE";
62
- } else if (err == APIError::CONNECTION_CLOSED) {
63
- return "CONNECTION_CLOSED";
64
76
  }
77
+ #endif
65
78
  return "UNKNOWN";
66
79
  }
67
80
 
@@ -76,33 +89,55 @@ APIError APIFrameHelper::loop() {
76
89
  return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
77
90
  }
78
91
 
92
+ // Common socket write error handling
93
+ APIError APIFrameHelper::handle_socket_write_error_() {
94
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
95
+ return APIError::WOULD_BLOCK;
96
+ }
97
+ HELPER_LOG("Socket write failed with errno %d", errno);
98
+ this->state_ = State::FAILED;
99
+ return APIError::SOCKET_WRITE_FAILED;
100
+ }
101
+
79
102
  // Helper method to buffer data from IOVs
80
- void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
103
+ void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len,
104
+ uint16_t offset) {
81
105
  SendBuffer buffer;
82
- buffer.data.reserve(total_write_len);
106
+ buffer.size = total_write_len - offset;
107
+ buffer.data = std::make_unique<uint8_t[]>(buffer.size);
108
+
109
+ uint16_t to_skip = offset;
110
+ uint16_t write_pos = 0;
111
+
83
112
  for (int i = 0; i < iovcnt; i++) {
84
- const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base);
85
- buffer.data.insert(buffer.data.end(), data, data + iov[i].iov_len);
113
+ if (to_skip >= iov[i].iov_len) {
114
+ // Skip this entire segment
115
+ to_skip -= static_cast<uint16_t>(iov[i].iov_len);
116
+ } else {
117
+ // Include this segment (partially or fully)
118
+ const uint8_t *src = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_skip;
119
+ uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_skip;
120
+ std::memcpy(buffer.data.get() + write_pos, src, len);
121
+ write_pos += len;
122
+ to_skip = 0;
123
+ }
86
124
  }
87
125
  this->tx_buf_.push_back(std::move(buffer));
88
126
  }
89
127
 
90
128
  // This method writes data to socket or buffers it
91
- APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
129
+ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
92
130
  // Returns APIError::OK if successful (or would block, but data has been buffered)
93
131
  // Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to FAILED
94
132
 
95
133
  if (iovcnt == 0)
96
134
  return APIError::OK; // Nothing to do, success
97
135
 
98
- uint16_t total_write_len = 0;
99
- for (int i = 0; i < iovcnt; i++) {
100
136
  #ifdef HELPER_LOG_PACKETS
101
- ESP_LOGVV(TAG, "Sending raw: %s",
102
- format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
103
- #endif
104
- total_write_len += static_cast<uint16_t>(iov[i].iov_len);
137
+ for (int i = 0; i < iovcnt; i++) {
138
+ LOG_PACKET_SENDING(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
105
139
  }
140
+ #endif
106
141
 
107
142
  // Try to send any existing buffered data first if there is any
108
143
  if (!this->tx_buf_.empty()) {
@@ -115,46 +150,27 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
115
150
  // If there is still data in the buffer, we can't send, buffer
116
151
  // the new data and return
117
152
  if (!this->tx_buf_.empty()) {
118
- this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
153
+ this->buffer_data_from_iov_(iov, iovcnt, total_write_len, 0);
119
154
  return APIError::OK; // Success, data buffered
120
155
  }
121
156
  }
122
157
 
123
158
  // Try to send directly if no buffered data
124
- ssize_t sent = this->socket_->writev(iov, iovcnt);
159
+ // Optimize for single iovec case (common for plaintext API)
160
+ ssize_t sent =
161
+ (iovcnt == 1) ? this->socket_->write(iov[0].iov_base, iov[0].iov_len) : this->socket_->writev(iov, iovcnt);
125
162
 
126
163
  if (sent == -1) {
127
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
164
+ APIError err = this->handle_socket_write_error_();
165
+ if (err == APIError::WOULD_BLOCK) {
128
166
  // Socket would block, buffer the data
129
- this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
167
+ this->buffer_data_from_iov_(iov, iovcnt, total_write_len, 0);
130
168
  return APIError::OK; // Success, data buffered
131
169
  }
132
- // Socket error
133
- ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
134
- this->state_ = State::FAILED;
135
- return APIError::SOCKET_WRITE_FAILED; // Socket write failed
170
+ return err; // Socket write failed
136
171
  } else if (static_cast<uint16_t>(sent) < total_write_len) {
137
172
  // Partially sent, buffer the remaining data
138
- SendBuffer buffer;
139
- uint16_t to_consume = static_cast<uint16_t>(sent);
140
- uint16_t remaining = total_write_len - static_cast<uint16_t>(sent);
141
-
142
- buffer.data.reserve(remaining);
143
-
144
- for (int i = 0; i < iovcnt; i++) {
145
- if (to_consume >= iov[i].iov_len) {
146
- // This segment was fully sent
147
- to_consume -= static_cast<uint16_t>(iov[i].iov_len);
148
- } else {
149
- // This segment was partially sent or not sent at all
150
- const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume;
151
- uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_consume;
152
- buffer.data.insert(buffer.data.end(), data, data + len);
153
- to_consume = 0;
154
- }
155
- }
156
-
157
- this->tx_buf_.push_back(std::move(buffer));
173
+ this->buffer_data_from_iov_(iov, iovcnt, total_write_len, static_cast<uint16_t>(sent));
158
174
  }
159
175
 
160
176
  return APIError::OK; // Success, all data sent or buffered
@@ -173,14 +189,7 @@ APIError APIFrameHelper::try_send_tx_buf_() {
173
189
  ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining());
174
190
 
175
191
  if (sent == -1) {
176
- if (errno != EWOULDBLOCK && errno != EAGAIN) {
177
- // Real socket error (not just would block)
178
- ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
179
- this->state_ = State::FAILED;
180
- return APIError::SOCKET_WRITE_FAILED; // Socket write failed
181
- }
182
- // Socket would block, we'll try again later
183
- return APIError::WOULD_BLOCK;
192
+ return this->handle_socket_write_error_();
184
193
  } else if (sent == 0) {
185
194
  // Nothing sent but not an error
186
195
  return APIError::WOULD_BLOCK;
@@ -203,13 +212,13 @@ APIError APIFrameHelper::try_send_tx_buf_() {
203
212
 
204
213
  APIError APIFrameHelper::init_common_() {
205
214
  if (state_ != State::INITIALIZE || this->socket_ == nullptr) {
206
- ESP_LOGVV(TAG, "%s: Bad state for init %d", this->info_.c_str(), (int) state_);
215
+ HELPER_LOG("Bad state for init %d", (int) state_);
207
216
  return APIError::BAD_STATE;
208
217
  }
209
218
  int err = this->socket_->setblocking(false);
210
219
  if (err != 0) {
211
220
  state_ = State::FAILED;
212
- ESP_LOGVV(TAG, "%s: Setting nonblocking failed with errno %d", this->info_.c_str(), errno);
221
+ HELPER_LOG("Setting nonblocking failed with errno %d", errno);
213
222
  return APIError::TCP_NONBLOCKING_FAILED;
214
223
  }
215
224
 
@@ -217,14 +226,12 @@ APIError APIFrameHelper::init_common_() {
217
226
  err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
218
227
  if (err != 0) {
219
228
  state_ = State::FAILED;
220
- ESP_LOGVV(TAG, "%s: Setting nodelay failed with errno %d", this->info_.c_str(), errno);
229
+ HELPER_LOG("Setting nodelay failed with errno %d", errno);
221
230
  return APIError::TCP_NODELAY_FAILED;
222
231
  }
223
232
  return APIError::OK;
224
233
  }
225
234
 
226
- #define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__)
227
-
228
235
  APIError APIFrameHelper::handle_socket_read_result_(ssize_t received) {
229
236
  if (received == -1) {
230
237
  if (errno == EWOULDBLOCK || errno == EAGAIN) {
@@ -240,840 +247,6 @@ APIError APIFrameHelper::handle_socket_read_result_(ssize_t received) {
240
247
  }
241
248
  return APIError::OK;
242
249
  }
243
- // uncomment to log raw packets
244
- //#define HELPER_LOG_PACKETS
245
-
246
- #ifdef USE_API_NOISE
247
- static const char *const PROLOGUE_INIT = "NoiseAPIInit";
248
-
249
- /// Convert a noise error code to a readable error
250
- std::string noise_err_to_str(int err) {
251
- if (err == NOISE_ERROR_NO_MEMORY)
252
- return "NO_MEMORY";
253
- if (err == NOISE_ERROR_UNKNOWN_ID)
254
- return "UNKNOWN_ID";
255
- if (err == NOISE_ERROR_UNKNOWN_NAME)
256
- return "UNKNOWN_NAME";
257
- if (err == NOISE_ERROR_MAC_FAILURE)
258
- return "MAC_FAILURE";
259
- if (err == NOISE_ERROR_NOT_APPLICABLE)
260
- return "NOT_APPLICABLE";
261
- if (err == NOISE_ERROR_SYSTEM)
262
- return "SYSTEM";
263
- if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
264
- return "REMOTE_KEY_REQUIRED";
265
- if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
266
- return "LOCAL_KEY_REQUIRED";
267
- if (err == NOISE_ERROR_PSK_REQUIRED)
268
- return "PSK_REQUIRED";
269
- if (err == NOISE_ERROR_INVALID_LENGTH)
270
- return "INVALID_LENGTH";
271
- if (err == NOISE_ERROR_INVALID_PARAM)
272
- return "INVALID_PARAM";
273
- if (err == NOISE_ERROR_INVALID_STATE)
274
- return "INVALID_STATE";
275
- if (err == NOISE_ERROR_INVALID_NONCE)
276
- return "INVALID_NONCE";
277
- if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
278
- return "INVALID_PRIVATE_KEY";
279
- if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
280
- return "INVALID_PUBLIC_KEY";
281
- if (err == NOISE_ERROR_INVALID_FORMAT)
282
- return "INVALID_FORMAT";
283
- if (err == NOISE_ERROR_INVALID_SIGNATURE)
284
- return "INVALID_SIGNATURE";
285
- return to_string(err);
286
- }
287
-
288
- /// Initialize the frame helper, returns OK if successful.
289
- APIError APINoiseFrameHelper::init() {
290
- APIError err = init_common_();
291
- if (err != APIError::OK) {
292
- return err;
293
- }
294
-
295
- // init prologue
296
- prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
297
-
298
- state_ = State::CLIENT_HELLO;
299
- return APIError::OK;
300
- }
301
- /// Run through handshake messages (if in that phase)
302
- APIError APINoiseFrameHelper::loop() {
303
- // During handshake phase, process as many actions as possible until we can't progress
304
- // socket_->ready() stays true until next main loop, but state_action() will return
305
- // WOULD_BLOCK when no more data is available to read
306
- while (state_ != State::DATA && this->socket_->ready()) {
307
- APIError err = state_action_();
308
- if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
309
- return err;
310
- }
311
- if (err == APIError::WOULD_BLOCK) {
312
- break;
313
- }
314
- }
315
-
316
- // Use base class implementation for buffer sending
317
- return APIFrameHelper::loop();
318
- }
319
-
320
- /** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
321
- *
322
- * @param frame: The struct to hold the frame information in.
323
- * msg_start: points to the start of the payload - this pointer is only valid until the next
324
- * try_receive_raw_ call
325
- *
326
- * @return 0 if a full packet is in rx_buf_
327
- * @return -1 if error, check errno.
328
- *
329
- * errno EWOULDBLOCK: Packet could not be read without blocking. Try again later.
330
- * errno ENOMEM: Not enough memory for reading packet.
331
- * errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
332
- * errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
333
- */
334
- APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
335
- if (frame == nullptr) {
336
- HELPER_LOG("Bad argument for try_read_frame_");
337
- return APIError::BAD_ARG;
338
- }
339
-
340
- // read header
341
- if (rx_header_buf_len_ < 3) {
342
- // no header information yet
343
- uint8_t to_read = 3 - rx_header_buf_len_;
344
- ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
345
- APIError err = handle_socket_read_result_(received);
346
- if (err != APIError::OK) {
347
- return err;
348
- }
349
- rx_header_buf_len_ += static_cast<uint8_t>(received);
350
- if (static_cast<uint8_t>(received) != to_read) {
351
- // not a full read
352
- return APIError::WOULD_BLOCK;
353
- }
354
-
355
- if (rx_header_buf_[0] != 0x01) {
356
- state_ = State::FAILED;
357
- HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
358
- return APIError::BAD_INDICATOR;
359
- }
360
- // header reading done
361
- }
362
-
363
- // read body
364
- uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
365
-
366
- if (state_ != State::DATA && msg_size > 128) {
367
- // for handshake message only permit up to 128 bytes
368
- state_ = State::FAILED;
369
- HELPER_LOG("Bad packet len for handshake: %d", msg_size);
370
- return APIError::BAD_HANDSHAKE_PACKET_LEN;
371
- }
372
-
373
- // reserve space for body
374
- if (rx_buf_.size() != msg_size) {
375
- rx_buf_.resize(msg_size);
376
- }
377
-
378
- if (rx_buf_len_ < msg_size) {
379
- // more data to read
380
- uint16_t to_read = msg_size - rx_buf_len_;
381
- ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
382
- APIError err = handle_socket_read_result_(received);
383
- if (err != APIError::OK) {
384
- return err;
385
- }
386
- rx_buf_len_ += static_cast<uint16_t>(received);
387
- if (static_cast<uint16_t>(received) != to_read) {
388
- // not all read
389
- return APIError::WOULD_BLOCK;
390
- }
391
- }
392
-
393
- // uncomment for even more debugging
394
- #ifdef HELPER_LOG_PACKETS
395
- ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
396
- #endif
397
- frame->msg = std::move(rx_buf_);
398
- // consume msg
399
- rx_buf_ = {};
400
- rx_buf_len_ = 0;
401
- rx_header_buf_len_ = 0;
402
- return APIError::OK;
403
- }
404
-
405
- /** To be called from read/write methods.
406
- *
407
- * This method runs through the internal handshake methods, if in that state.
408
- *
409
- * If the handshake is still active when this method returns and a read/write can't take place at
410
- * the moment, returns WOULD_BLOCK.
411
- * If an error occurred, returns that error. Only returns OK if the transport is ready for data
412
- * traffic.
413
- */
414
- APIError APINoiseFrameHelper::state_action_() {
415
- int err;
416
- APIError aerr;
417
- if (state_ == State::INITIALIZE) {
418
- HELPER_LOG("Bad state for method: %d", (int) state_);
419
- return APIError::BAD_STATE;
420
- }
421
- if (state_ == State::CLIENT_HELLO) {
422
- // waiting for client hello
423
- ParsedFrame frame;
424
- aerr = try_read_frame_(&frame);
425
- if (aerr == APIError::BAD_INDICATOR) {
426
- send_explicit_handshake_reject_("Bad indicator byte");
427
- return aerr;
428
- }
429
- if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
430
- send_explicit_handshake_reject_("Bad handshake packet len");
431
- return aerr;
432
- }
433
- if (aerr != APIError::OK)
434
- return aerr;
435
- // ignore contents, may be used in future for flags
436
- // Reserve space for: existing prologue + 2 size bytes + frame data
437
- prologue_.reserve(prologue_.size() + 2 + frame.msg.size());
438
- prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
439
- prologue_.push_back((uint8_t) frame.msg.size());
440
- prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
441
-
442
- state_ = State::SERVER_HELLO;
443
- }
444
- if (state_ == State::SERVER_HELLO) {
445
- // send server hello
446
- const std::string &name = App.get_name();
447
- const std::string &mac = get_mac_address();
448
-
449
- std::vector<uint8_t> msg;
450
- // Reserve space for: 1 byte proto + name + null + mac + null
451
- msg.reserve(1 + name.size() + 1 + mac.size() + 1);
452
-
453
- // chosen proto
454
- msg.push_back(0x01);
455
-
456
- // node name, terminated by null byte
457
- const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
458
- msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
459
- // node mac, terminated by null byte
460
- const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
461
- msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
462
-
463
- aerr = write_frame_(msg.data(), msg.size());
464
- if (aerr != APIError::OK)
465
- return aerr;
466
-
467
- // start handshake
468
- aerr = init_handshake_();
469
- if (aerr != APIError::OK)
470
- return aerr;
471
-
472
- state_ = State::HANDSHAKE;
473
- }
474
- if (state_ == State::HANDSHAKE) {
475
- int action = noise_handshakestate_get_action(handshake_);
476
- if (action == NOISE_ACTION_READ_MESSAGE) {
477
- // waiting for handshake msg
478
- ParsedFrame frame;
479
- aerr = try_read_frame_(&frame);
480
- if (aerr == APIError::BAD_INDICATOR) {
481
- send_explicit_handshake_reject_("Bad indicator byte");
482
- return aerr;
483
- }
484
- if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
485
- send_explicit_handshake_reject_("Bad handshake packet len");
486
- return aerr;
487
- }
488
- if (aerr != APIError::OK)
489
- return aerr;
490
-
491
- if (frame.msg.empty()) {
492
- send_explicit_handshake_reject_("Empty handshake message");
493
- return APIError::BAD_HANDSHAKE_ERROR_BYTE;
494
- } else if (frame.msg[0] != 0x00) {
495
- HELPER_LOG("Bad handshake error byte: %u", frame.msg[0]);
496
- send_explicit_handshake_reject_("Bad handshake error byte");
497
- return APIError::BAD_HANDSHAKE_ERROR_BYTE;
498
- }
499
-
500
- NoiseBuffer mbuf;
501
- noise_buffer_init(mbuf);
502
- noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1);
503
- err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
504
- if (err != 0) {
505
- state_ = State::FAILED;
506
- HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str());
507
- if (err == NOISE_ERROR_MAC_FAILURE) {
508
- send_explicit_handshake_reject_("Handshake MAC failure");
509
- } else {
510
- send_explicit_handshake_reject_("Handshake error");
511
- }
512
- return APIError::HANDSHAKESTATE_READ_FAILED;
513
- }
514
-
515
- aerr = check_handshake_finished_();
516
- if (aerr != APIError::OK)
517
- return aerr;
518
- } else if (action == NOISE_ACTION_WRITE_MESSAGE) {
519
- uint8_t buffer[65];
520
- NoiseBuffer mbuf;
521
- noise_buffer_init(mbuf);
522
- noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
523
-
524
- err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
525
- if (err != 0) {
526
- state_ = State::FAILED;
527
- HELPER_LOG("noise_handshakestate_write_message failed: %s", noise_err_to_str(err).c_str());
528
- return APIError::HANDSHAKESTATE_WRITE_FAILED;
529
- }
530
- buffer[0] = 0x00; // success
531
-
532
- aerr = write_frame_(buffer, mbuf.size + 1);
533
- if (aerr != APIError::OK)
534
- return aerr;
535
- aerr = check_handshake_finished_();
536
- if (aerr != APIError::OK)
537
- return aerr;
538
- } else {
539
- // bad state for action
540
- state_ = State::FAILED;
541
- HELPER_LOG("Bad action for handshake: %d", action);
542
- return APIError::HANDSHAKESTATE_BAD_STATE;
543
- }
544
- }
545
- if (state_ == State::CLOSED || state_ == State::FAILED) {
546
- return APIError::BAD_STATE;
547
- }
548
- return APIError::OK;
549
- }
550
- void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &reason) {
551
- std::vector<uint8_t> data;
552
- data.resize(reason.length() + 1);
553
- data[0] = 0x01; // failure
554
-
555
- // Copy error message in bulk
556
- if (!reason.empty()) {
557
- std::memcpy(data.data() + 1, reason.c_str(), reason.length());
558
- }
559
-
560
- // temporarily remove failed state
561
- auto orig_state = state_;
562
- state_ = State::EXPLICIT_REJECT;
563
- write_frame_(data.data(), data.size());
564
- state_ = orig_state;
565
- }
566
- APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
567
- int err;
568
- APIError aerr;
569
- aerr = state_action_();
570
- if (aerr != APIError::OK) {
571
- return aerr;
572
- }
573
-
574
- if (state_ != State::DATA) {
575
- return APIError::WOULD_BLOCK;
576
- }
577
-
578
- ParsedFrame frame;
579
- aerr = try_read_frame_(&frame);
580
- if (aerr != APIError::OK)
581
- return aerr;
582
-
583
- NoiseBuffer mbuf;
584
- noise_buffer_init(mbuf);
585
- noise_buffer_set_inout(mbuf, frame.msg.data(), frame.msg.size(), frame.msg.size());
586
- err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
587
- if (err != 0) {
588
- state_ = State::FAILED;
589
- HELPER_LOG("noise_cipherstate_decrypt failed: %s", noise_err_to_str(err).c_str());
590
- return APIError::CIPHERSTATE_DECRYPT_FAILED;
591
- }
592
-
593
- uint16_t msg_size = mbuf.size;
594
- uint8_t *msg_data = frame.msg.data();
595
- if (msg_size < 4) {
596
- state_ = State::FAILED;
597
- HELPER_LOG("Bad data packet: size %d too short", msg_size);
598
- return APIError::BAD_DATA_PACKET;
599
- }
600
-
601
- uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
602
- uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
603
- if (data_len > msg_size - 4) {
604
- state_ = State::FAILED;
605
- HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
606
- return APIError::BAD_DATA_PACKET;
607
- }
608
-
609
- buffer->container = std::move(frame.msg);
610
- buffer->data_offset = 4;
611
- buffer->data_len = data_len;
612
- buffer->type = type;
613
- return APIError::OK;
614
- }
615
- APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
616
- // Resize to include MAC space (required for Noise encryption)
617
- buffer.get_buffer()->resize(buffer.get_buffer()->size() + frame_footer_size_);
618
- PacketInfo packet{type, 0,
619
- static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_ - frame_footer_size_)};
620
- return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
621
- }
622
-
623
- APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
624
- APIError aerr = state_action_();
625
- if (aerr != APIError::OK) {
626
- return aerr;
627
- }
628
-
629
- if (state_ != State::DATA) {
630
- return APIError::WOULD_BLOCK;
631
- }
632
-
633
- if (packets.empty()) {
634
- return APIError::OK;
635
- }
636
-
637
- std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
638
- uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
639
-
640
- this->reusable_iovs_.clear();
641
- this->reusable_iovs_.reserve(packets.size());
642
-
643
- // We need to encrypt each packet in place
644
- for (const auto &packet : packets) {
645
- // The buffer already has padding at offset
646
- uint8_t *buf_start = buffer_data + packet.offset;
647
-
648
- // Write noise header
649
- buf_start[0] = 0x01; // indicator
650
- // buf_start[1], buf_start[2] to be set after encryption
651
-
652
- // Write message header (to be encrypted)
653
- const uint8_t msg_offset = 3;
654
- buf_start[msg_offset] = static_cast<uint8_t>(packet.message_type >> 8); // type high byte
655
- buf_start[msg_offset + 1] = static_cast<uint8_t>(packet.message_type); // type low byte
656
- buf_start[msg_offset + 2] = static_cast<uint8_t>(packet.payload_size >> 8); // data_len high byte
657
- buf_start[msg_offset + 3] = static_cast<uint8_t>(packet.payload_size); // data_len low byte
658
- // payload data is already in the buffer starting at offset + 7
659
-
660
- // Make sure we have space for MAC
661
- // The buffer should already have been sized appropriately
662
-
663
- // Encrypt the message in place
664
- NoiseBuffer mbuf;
665
- noise_buffer_init(mbuf);
666
- noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + packet.payload_size,
667
- 4 + packet.payload_size + frame_footer_size_);
668
-
669
- int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
670
- if (err != 0) {
671
- state_ = State::FAILED;
672
- HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
673
- return APIError::CIPHERSTATE_ENCRYPT_FAILED;
674
- }
675
-
676
- // Fill in the encrypted size
677
- buf_start[1] = static_cast<uint8_t>(mbuf.size >> 8);
678
- buf_start[2] = static_cast<uint8_t>(mbuf.size);
679
-
680
- // Add iovec for this encrypted packet
681
- this->reusable_iovs_.push_back(
682
- {buf_start, static_cast<size_t>(3 + mbuf.size)}); // indicator + size + encrypted data
683
- }
684
-
685
- // Send all encrypted packets in one writev call
686
- return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
687
- }
688
-
689
- APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
690
- uint8_t header[3];
691
- header[0] = 0x01; // indicator
692
- header[1] = (uint8_t) (len >> 8);
693
- header[2] = (uint8_t) len;
694
-
695
- struct iovec iov[2];
696
- iov[0].iov_base = header;
697
- iov[0].iov_len = 3;
698
- if (len == 0) {
699
- return this->write_raw_(iov, 1);
700
- }
701
- iov[1].iov_base = const_cast<uint8_t *>(data);
702
- iov[1].iov_len = len;
703
-
704
- return this->write_raw_(iov, 2);
705
- }
706
-
707
- /** Initiate the data structures for the handshake.
708
- *
709
- * @return 0 on success, -1 on error (check errno)
710
- */
711
- APIError APINoiseFrameHelper::init_handshake_() {
712
- int err;
713
- memset(&nid_, 0, sizeof(nid_));
714
- // const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
715
- // err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
716
- nid_.pattern_id = NOISE_PATTERN_NN;
717
- nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
718
- nid_.dh_id = NOISE_DH_CURVE25519;
719
- nid_.prefix_id = NOISE_PREFIX_STANDARD;
720
- nid_.hybrid_id = NOISE_DH_NONE;
721
- nid_.hash_id = NOISE_HASH_SHA256;
722
- nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
723
-
724
- err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
725
- if (err != 0) {
726
- state_ = State::FAILED;
727
- HELPER_LOG("noise_handshakestate_new_by_id failed: %s", noise_err_to_str(err).c_str());
728
- return APIError::HANDSHAKESTATE_SETUP_FAILED;
729
- }
730
-
731
- const auto &psk = ctx_->get_psk();
732
- err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
733
- if (err != 0) {
734
- state_ = State::FAILED;
735
- HELPER_LOG("noise_handshakestate_set_pre_shared_key failed: %s", noise_err_to_str(err).c_str());
736
- return APIError::HANDSHAKESTATE_SETUP_FAILED;
737
- }
738
-
739
- err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
740
- if (err != 0) {
741
- state_ = State::FAILED;
742
- HELPER_LOG("noise_handshakestate_set_prologue failed: %s", noise_err_to_str(err).c_str());
743
- return APIError::HANDSHAKESTATE_SETUP_FAILED;
744
- }
745
- // set_prologue copies it into handshakestate, so we can get rid of it now
746
- prologue_ = {};
747
-
748
- err = noise_handshakestate_start(handshake_);
749
- if (err != 0) {
750
- state_ = State::FAILED;
751
- HELPER_LOG("noise_handshakestate_start failed: %s", noise_err_to_str(err).c_str());
752
- return APIError::HANDSHAKESTATE_SETUP_FAILED;
753
- }
754
- return APIError::OK;
755
- }
756
-
757
- APIError APINoiseFrameHelper::check_handshake_finished_() {
758
- assert(state_ == State::HANDSHAKE);
759
-
760
- int action = noise_handshakestate_get_action(handshake_);
761
- if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
762
- return APIError::OK;
763
- if (action != NOISE_ACTION_SPLIT) {
764
- state_ = State::FAILED;
765
- HELPER_LOG("Bad action for handshake: %d", action);
766
- return APIError::HANDSHAKESTATE_BAD_STATE;
767
- }
768
- int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
769
- if (err != 0) {
770
- state_ = State::FAILED;
771
- HELPER_LOG("noise_handshakestate_split failed: %s", noise_err_to_str(err).c_str());
772
- return APIError::HANDSHAKESTATE_SPLIT_FAILED;
773
- }
774
-
775
- frame_footer_size_ = noise_cipherstate_get_mac_length(send_cipher_);
776
-
777
- HELPER_LOG("Handshake complete!");
778
- noise_handshakestate_free(handshake_);
779
- handshake_ = nullptr;
780
- state_ = State::DATA;
781
- return APIError::OK;
782
- }
783
-
784
- APINoiseFrameHelper::~APINoiseFrameHelper() {
785
- if (handshake_ != nullptr) {
786
- noise_handshakestate_free(handshake_);
787
- handshake_ = nullptr;
788
- }
789
- if (send_cipher_ != nullptr) {
790
- noise_cipherstate_free(send_cipher_);
791
- send_cipher_ = nullptr;
792
- }
793
- if (recv_cipher_ != nullptr) {
794
- noise_cipherstate_free(recv_cipher_);
795
- recv_cipher_ = nullptr;
796
- }
797
- }
798
-
799
- extern "C" {
800
- // declare how noise generates random bytes (here with a good HWRNG based on the RF system)
801
- void noise_rand_bytes(void *output, size_t len) {
802
- if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
803
- ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
804
- arch_restart();
805
- }
806
- }
807
- }
808
-
809
- #endif // USE_API_NOISE
810
-
811
- #ifdef USE_API_PLAINTEXT
812
-
813
- /// Initialize the frame helper, returns OK if successful.
814
- APIError APIPlaintextFrameHelper::init() {
815
- APIError err = init_common_();
816
- if (err != APIError::OK) {
817
- return err;
818
- }
819
-
820
- state_ = State::DATA;
821
- return APIError::OK;
822
- }
823
- APIError APIPlaintextFrameHelper::loop() {
824
- if (state_ != State::DATA) {
825
- return APIError::BAD_STATE;
826
- }
827
- // Use base class implementation for buffer sending
828
- return APIFrameHelper::loop();
829
- }
830
-
831
- /** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
832
- *
833
- * @param frame: The struct to hold the frame information in.
834
- * msg: store the parsed frame in that struct
835
- *
836
- * @return See APIError
837
- *
838
- * error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
839
- */
840
- APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
841
- if (frame == nullptr) {
842
- HELPER_LOG("Bad argument for try_read_frame_");
843
- return APIError::BAD_ARG;
844
- }
845
-
846
- // read header
847
- while (!rx_header_parsed_) {
848
- // Now that we know when the socket is ready, we can read up to 3 bytes
849
- // into the rx_header_buf_ before we have to switch back to reading
850
- // one byte at a time to ensure we don't read past the message and
851
- // into the next one.
852
-
853
- // Read directly into rx_header_buf_ at the current position
854
- // Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time
855
- ssize_t received =
856
- this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1);
857
- APIError err = handle_socket_read_result_(received);
858
- if (err != APIError::OK) {
859
- return err;
860
- }
861
-
862
- // If this was the first read, validate the indicator byte
863
- if (rx_header_buf_pos_ == 0 && received > 0) {
864
- if (rx_header_buf_[0] != 0x00) {
865
- state_ = State::FAILED;
866
- HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
867
- return APIError::BAD_INDICATOR;
868
- }
869
- }
870
-
871
- rx_header_buf_pos_ += received;
872
-
873
- // Check for buffer overflow
874
- if (rx_header_buf_pos_ >= sizeof(rx_header_buf_)) {
875
- state_ = State::FAILED;
876
- HELPER_LOG("Header buffer overflow");
877
- return APIError::BAD_DATA_PACKET;
878
- }
879
-
880
- // Need at least 3 bytes total (indicator + 2 varint bytes) before trying to parse
881
- if (rx_header_buf_pos_ < 3) {
882
- continue;
883
- }
884
-
885
- // At this point, we have at least 3 bytes total:
886
- // - Validated indicator byte (0x00) stored at position 0
887
- // - At least 2 bytes in the buffer for the varints
888
- // Buffer layout:
889
- // [0]: indicator byte (0x00)
890
- // [1-3]: Message size varint (variable length)
891
- // - 2 bytes would only allow up to 16383, which is less than noise's UINT16_MAX (65535)
892
- // - 3 bytes allows up to 2097151, ensuring we support at least as much as noise
893
- // [2-5]: Message type varint (variable length)
894
- // We now attempt to parse both varints. If either is incomplete,
895
- // we'll continue reading more bytes.
896
-
897
- // Skip indicator byte at position 0
898
- uint8_t varint_pos = 1;
899
- uint32_t consumed = 0;
900
-
901
- auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
902
- if (!msg_size_varint.has_value()) {
903
- // not enough data there yet
904
- continue;
905
- }
906
-
907
- if (msg_size_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
908
- state_ = State::FAILED;
909
- HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(),
910
- std::numeric_limits<uint16_t>::max());
911
- return APIError::BAD_DATA_PACKET;
912
- }
913
- rx_header_parsed_len_ = msg_size_varint->as_uint16();
914
-
915
- // Move to next varint position
916
- varint_pos += consumed;
917
-
918
- auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
919
- if (!msg_type_varint.has_value()) {
920
- // not enough data there yet
921
- continue;
922
- }
923
- if (msg_type_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
924
- state_ = State::FAILED;
925
- HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u", msg_type_varint->as_uint32(),
926
- std::numeric_limits<uint16_t>::max());
927
- return APIError::BAD_DATA_PACKET;
928
- }
929
- rx_header_parsed_type_ = msg_type_varint->as_uint16();
930
- rx_header_parsed_ = true;
931
- }
932
- // header reading done
933
-
934
- // reserve space for body
935
- if (rx_buf_.size() != rx_header_parsed_len_) {
936
- rx_buf_.resize(rx_header_parsed_len_);
937
- }
938
-
939
- if (rx_buf_len_ < rx_header_parsed_len_) {
940
- // more data to read
941
- uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_;
942
- ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
943
- APIError err = handle_socket_read_result_(received);
944
- if (err != APIError::OK) {
945
- return err;
946
- }
947
- rx_buf_len_ += static_cast<uint16_t>(received);
948
- if (static_cast<uint16_t>(received) != to_read) {
949
- // not all read
950
- return APIError::WOULD_BLOCK;
951
- }
952
- }
953
-
954
- // uncomment for even more debugging
955
- #ifdef HELPER_LOG_PACKETS
956
- ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
957
- #endif
958
- frame->msg = std::move(rx_buf_);
959
- // consume msg
960
- rx_buf_ = {};
961
- rx_buf_len_ = 0;
962
- rx_header_buf_pos_ = 0;
963
- rx_header_parsed_ = false;
964
- return APIError::OK;
965
- }
966
- APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
967
- APIError aerr;
968
-
969
- if (state_ != State::DATA) {
970
- return APIError::WOULD_BLOCK;
971
- }
972
-
973
- ParsedFrame frame;
974
- aerr = try_read_frame_(&frame);
975
- if (aerr != APIError::OK) {
976
- if (aerr == APIError::BAD_INDICATOR) {
977
- // Make sure to tell the remote that we don't
978
- // understand the indicator byte so it knows
979
- // we do not support it.
980
- struct iovec iov[1];
981
- // The \x00 first byte is the marker for plaintext.
982
- //
983
- // The remote will know how to handle the indicator byte,
984
- // but it likely won't understand the rest of the message.
985
- //
986
- // We must send at least 3 bytes to be read, so we add
987
- // a message after the indicator byte to ensures its long
988
- // enough and can aid in debugging.
989
- const char msg[] = "\x00"
990
- "Bad indicator byte";
991
- iov[0].iov_base = (void *) msg;
992
- iov[0].iov_len = 19;
993
- this->write_raw_(iov, 1);
994
- }
995
- return aerr;
996
- }
997
-
998
- buffer->container = std::move(frame.msg);
999
- buffer->data_offset = 0;
1000
- buffer->data_len = rx_header_parsed_len_;
1001
- buffer->type = rx_header_parsed_type_;
1002
- return APIError::OK;
1003
- }
1004
- APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
1005
- PacketInfo packet{type, 0, static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_)};
1006
- return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
1007
- }
1008
-
1009
- APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
1010
- if (state_ != State::DATA) {
1011
- return APIError::BAD_STATE;
1012
- }
1013
-
1014
- if (packets.empty()) {
1015
- return APIError::OK;
1016
- }
1017
-
1018
- std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
1019
- uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
1020
-
1021
- this->reusable_iovs_.clear();
1022
- this->reusable_iovs_.reserve(packets.size());
1023
-
1024
- for (const auto &packet : packets) {
1025
- // Calculate varint sizes for header layout
1026
- uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.payload_size));
1027
- uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.message_type));
1028
- uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
1029
-
1030
- // Calculate where to start writing the header
1031
- // The header starts at the latest possible position to minimize unused padding
1032
- //
1033
- // Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
1034
- // [0-2] - Unused padding
1035
- // [3] - 0x00 indicator byte
1036
- // [4] - Payload size varint (1 byte, for sizes 0-127)
1037
- // [5] - Message type varint (1 byte, for types 0-127)
1038
- // [6...] - Actual payload data
1039
- //
1040
- // Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
1041
- // [0-1] - Unused padding
1042
- // [2] - 0x00 indicator byte
1043
- // [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
1044
- // [5] - Message type varint (1 byte, for types 0-127)
1045
- // [6...] - Actual payload data
1046
- //
1047
- // Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
1048
- // [0] - 0x00 indicator byte
1049
- // [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
1050
- // [4-5] - Message type varint (2 bytes, for types 128-32767)
1051
- // [6...] - Actual payload data
1052
- //
1053
- // The message starts at offset + frame_header_padding_
1054
- // So we write the header starting at offset + frame_header_padding_ - total_header_len
1055
- uint8_t *buf_start = buffer_data + packet.offset;
1056
- uint32_t header_offset = frame_header_padding_ - total_header_len;
1057
-
1058
- // Write the plaintext header
1059
- buf_start[header_offset] = 0x00; // indicator
1060
-
1061
- // Encode varints directly into buffer
1062
- ProtoVarInt(packet.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
1063
- ProtoVarInt(packet.message_type)
1064
- .encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
1065
-
1066
- // Add iovec for this packet (header + payload)
1067
- this->reusable_iovs_.push_back(
1068
- {buf_start + header_offset, static_cast<size_t>(total_header_len + packet.payload_size)});
1069
- }
1070
-
1071
- // Send all packets in one writev call
1072
- return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
1073
- }
1074
-
1075
- #endif // USE_API_PLAINTEXT
1076
250
 
1077
- } // namespace api
1078
- } // namespace esphome
251
+ } // namespace esphome::api
1079
252
  #endif