esphome 2025.4.1__py3-none-any.whl → 2025.5.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 (457) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  3. esphome/components/adc/__init__.py +51 -34
  4. esphome/components/airthings_wave_base/__init__.py +1 -1
  5. esphome/components/alarm_control_panel/__init__.py +37 -2
  6. esphome/components/am43/cover/__init__.py +4 -5
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  8. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  9. esphome/components/analog_threshold/binary_sensor.py +10 -8
  10. esphome/components/anova/climate.py +4 -5
  11. esphome/components/api/__init__.py +25 -8
  12. esphome/components/api/api_connection.cpp +416 -662
  13. esphome/components/api/api_connection.h +256 -57
  14. esphome/components/api/api_frame_helper.cpp +232 -177
  15. esphome/components/api/api_frame_helper.h +61 -8
  16. esphome/components/api/api_noise_context.h +13 -4
  17. esphome/components/api/api_pb2.cpp +1422 -1
  18. esphome/components/api/api_pb2.h +255 -1
  19. esphome/components/api/api_pb2_service.cpp +162 -49
  20. esphome/components/api/api_pb2_service.h +90 -51
  21. esphome/components/api/api_pb2_size.h +361 -0
  22. esphome/components/api/api_server.cpp +110 -34
  23. esphome/components/api/api_server.h +8 -0
  24. esphome/components/api/proto.h +86 -17
  25. esphome/components/as3935_i2c/as3935_i2c.h +0 -3
  26. esphome/components/as7341/as7341.h +1 -1
  27. esphome/components/at581x/at581x.h +4 -4
  28. esphome/components/atm90e32/__init__.py +1 -0
  29. esphome/components/atm90e32/atm90e32.cpp +576 -199
  30. esphome/components/atm90e32/atm90e32.h +128 -31
  31. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  32. esphome/components/atm90e32/button/__init__.py +62 -10
  33. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  34. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  35. esphome/components/atm90e32/number/__init__.py +130 -0
  36. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  37. esphome/components/atm90e32/sensor.py +21 -4
  38. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  39. esphome/components/audio/__init__.py +96 -49
  40. esphome/components/audio/audio.h +48 -0
  41. esphome/components/audio/audio_decoder.cpp +1 -1
  42. esphome/components/audio/audio_resampler.cpp +2 -0
  43. esphome/components/audio/audio_resampler.h +1 -0
  44. esphome/components/ballu/climate.py +2 -9
  45. esphome/components/bang_bang/climate.py +5 -6
  46. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  47. esphome/components/bedjet/climate/__init__.py +3 -8
  48. esphome/components/bedjet/fan/__init__.py +2 -11
  49. esphome/components/binary/fan/__init__.py +13 -16
  50. esphome/components/binary_sensor/__init__.py +13 -10
  51. esphome/components/bl0906/constants.h +16 -16
  52. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  53. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  55. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  56. esphome/components/button/__init__.py +11 -8
  57. esphome/components/canbus/canbus.cpp +3 -0
  58. esphome/components/canbus/canbus.h +16 -0
  59. esphome/components/ccs811/sensor.py +9 -6
  60. esphome/components/climate/__init__.py +35 -2
  61. esphome/components/climate/climate_mode.h +1 -1
  62. esphome/components/climate/climate_traits.h +63 -57
  63. esphome/components/climate_ir/__init__.py +57 -17
  64. esphome/components/climate_ir_lg/climate.py +2 -5
  65. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  66. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  67. esphome/components/color/__init__.py +2 -0
  68. esphome/components/const/__init__.py +5 -0
  69. esphome/components/coolix/climate.py +2 -9
  70. esphome/components/copy/cover/__init__.py +10 -9
  71. esphome/components/copy/fan/__init__.py +11 -9
  72. esphome/components/copy/lock/__init__.py +11 -9
  73. esphome/components/copy/text/__init__.py +9 -6
  74. esphome/components/cover/__init__.py +37 -2
  75. esphome/components/cse7766/cse7766.cpp +2 -1
  76. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  77. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  78. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  80. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  81. esphome/components/current_based/cover.py +37 -36
  82. esphome/components/current_based/current_based_cover.cpp +2 -1
  83. esphome/components/daikin/climate.py +2 -9
  84. esphome/components/daikin/daikin.cpp +15 -9
  85. esphome/components/daikin/daikin.h +5 -5
  86. esphome/components/daikin_arc/climate.py +2 -7
  87. esphome/components/daikin_brc/climate.py +3 -5
  88. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  89. esphome/components/dallas_temp/dallas_temp.h +0 -1
  90. esphome/components/daly_bms/daly_bms.cpp +2 -1
  91. esphome/components/debug/debug_component.cpp +6 -1
  92. esphome/components/debug/debug_component.h +8 -0
  93. esphome/components/debug/debug_esp32.cpp +109 -254
  94. esphome/components/debug/sensor.py +14 -0
  95. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  96. esphome/components/delonghi/climate.py +2 -9
  97. esphome/components/demo/__init__.py +18 -20
  98. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  99. esphome/components/display/rect.cpp +4 -9
  100. esphome/components/display/rect.h +1 -1
  101. esphome/components/dps310/sensor.py +6 -6
  102. esphome/components/ee895/sensor.py +9 -9
  103. esphome/components/emmeti/climate.py +2 -9
  104. esphome/components/endstop/cover.py +17 -16
  105. esphome/components/endstop/endstop_cover.cpp +2 -1
  106. esphome/components/ens160_base/__init__.py +12 -9
  107. esphome/components/esp32/__init__.py +60 -3
  108. esphome/components/esp32/core.cpp +11 -5
  109. esphome/components/esp32/gpio.cpp +86 -24
  110. esphome/components/esp32/gpio.py +15 -16
  111. esphome/components/esp32/gpio_esp32.py +1 -2
  112. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  113. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  114. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  115. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  116. esphome/components/esp32_ble/ble.cpp +1 -8
  117. esphome/components/esp32_ble/ble.h +5 -3
  118. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  119. esphome/components/esp32_ble/ble_advertising.h +1 -0
  120. esphome/components/esp32_ble_server/__init__.py +3 -0
  121. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  122. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  123. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  124. esphome/components/esp32_camera/__init__.py +1 -1
  125. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  126. esphome/components/esp32_camera/esp32_camera.h +1 -1
  127. esphome/components/esp32_can/esp32_can.cpp +1 -1
  128. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  129. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  130. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  131. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  132. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  133. esphome/components/esp8266/gpio.cpp +69 -8
  134. esphome/components/ethernet/ethernet_component.cpp +1 -1
  135. esphome/components/event/__init__.py +13 -10
  136. esphome/components/factory_reset/switch/__init__.py +7 -21
  137. esphome/components/fan/__init__.py +52 -5
  138. esphome/components/fastled_base/__init__.py +1 -4
  139. esphome/components/fastled_base/fastled_light.cpp +1 -1
  140. esphome/components/feedback/cover.py +38 -33
  141. esphome/components/feedback/feedback_cover.cpp +2 -1
  142. esphome/components/fujitsu_general/climate.py +2 -9
  143. esphome/components/gcja5/gcja5.cpp +2 -1
  144. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  145. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  146. esphome/components/gpio_expander/cached_gpio.h +22 -7
  147. esphome/components/gps/__init__.py +47 -17
  148. esphome/components/gps/gps.cpp +42 -23
  149. esphome/components/gps/gps.h +17 -13
  150. esphome/components/graph/__init__.py +1 -2
  151. esphome/components/gree/climate.py +4 -6
  152. esphome/components/gree/gree.cpp +16 -2
  153. esphome/components/gree/gree.h +2 -2
  154. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  155. esphome/components/haier/climate.py +37 -34
  156. esphome/components/hbridge/fan/__init__.py +19 -17
  157. esphome/components/he60r/cover.py +4 -5
  158. esphome/components/heatpumpir/climate.py +3 -6
  159. esphome/components/hitachi_ac344/climate.py +2 -9
  160. esphome/components/hitachi_ac424/climate.py +2 -9
  161. esphome/components/hlw8012/hlw8012.cpp +1 -1
  162. esphome/components/hm3301/hm3301.h +1 -1
  163. esphome/components/hte501/sensor.py +6 -6
  164. esphome/components/http_request/__init__.py +39 -6
  165. esphome/components/http_request/http_request.cpp +20 -0
  166. esphome/components/http_request/http_request.h +57 -15
  167. esphome/components/http_request/http_request_arduino.cpp +22 -6
  168. esphome/components/http_request/http_request_arduino.h +4 -3
  169. esphome/components/http_request/http_request_host.cpp +141 -0
  170. esphome/components/http_request/http_request_host.h +37 -0
  171. esphome/components/http_request/http_request_idf.cpp +35 -3
  172. esphome/components/http_request/http_request_idf.h +10 -3
  173. esphome/components/http_request/httplib.h +9691 -0
  174. esphome/components/http_request/update/__init__.py +11 -8
  175. esphome/components/hyt271/sensor.py +6 -6
  176. esphome/components/i2c/i2c.h +4 -0
  177. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  178. esphome/components/i2s_audio/__init__.py +131 -22
  179. esphome/components/i2s_audio/i2s_audio.h +44 -4
  180. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  181. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  182. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  183. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  184. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  185. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  186. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  187. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  188. esphome/components/image/__init__.py +37 -17
  189. esphome/components/image/image.cpp +25 -8
  190. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  191. esphome/components/key_collector/__init__.py +35 -0
  192. esphome/components/key_collector/key_collector.cpp +8 -0
  193. esphome/components/key_collector/key_collector.h +10 -0
  194. esphome/components/kuntze/kuntze.cpp +2 -1
  195. esphome/components/ld2410/ld2410.h +1 -1
  196. esphome/components/ld2450/ld2450.h +1 -1
  197. esphome/components/light/__init__.py +57 -0
  198. esphome/components/lock/__init__.py +51 -4
  199. esphome/components/lock/automation.h +2 -13
  200. esphome/components/logger/__init__.py +22 -0
  201. esphome/components/logger/logger.cpp +154 -103
  202. esphome/components/logger/logger.h +211 -36
  203. esphome/components/logger/task_log_buffer.cpp +138 -0
  204. esphome/components/logger/task_log_buffer.h +69 -0
  205. esphome/components/lvgl/__init__.py +13 -5
  206. esphome/components/lvgl/automation.py +50 -1
  207. esphome/components/lvgl/defines.py +0 -1
  208. esphome/components/lvgl/lv_validation.py +10 -1
  209. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  210. esphome/components/lvgl/schemas.py +14 -14
  211. esphome/components/lvgl/text/__init__.py +1 -2
  212. esphome/components/lvgl/widgets/arc.py +7 -6
  213. esphome/components/lvgl/widgets/buttonmatrix.py +3 -3
  214. esphome/components/lvgl/widgets/checkbox.py +2 -2
  215. esphome/components/lvgl/widgets/dropdown.py +2 -1
  216. esphome/components/lvgl/widgets/img.py +15 -12
  217. esphome/components/mapping/__init__.py +134 -0
  218. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  219. esphome/components/max7219digit/max7219digit.cpp +28 -27
  220. esphome/components/mdns/__init__.py +11 -5
  221. esphome/components/mdns/mdns_component.cpp +11 -5
  222. esphome/components/mdns/mdns_component.h +3 -2
  223. esphome/components/mdns/mdns_esp32.cpp +4 -3
  224. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  225. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  226. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  227. esphome/components/media_player/__init__.py +40 -6
  228. esphome/components/mhz19/sensor.py +11 -7
  229. esphome/components/micro_wake_word/__init__.py +99 -31
  230. esphome/components/micro_wake_word/automation.h +54 -0
  231. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  232. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  233. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  234. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  235. esphome/components/micro_wake_word/streaming_model.h +85 -13
  236. esphome/components/microphone/__init__.py +139 -9
  237. esphome/components/microphone/automation.h +14 -2
  238. esphome/components/microphone/microphone.cpp +21 -0
  239. esphome/components/microphone/microphone.h +14 -5
  240. esphome/components/microphone/microphone_source.cpp +95 -0
  241. esphome/components/microphone/microphone_source.h +80 -0
  242. esphome/components/mics_4514/sensor.py +25 -14
  243. esphome/components/midea/climate.py +3 -4
  244. esphome/components/midea_ir/climate.py +3 -5
  245. esphome/components/mipi_spi/__init__.py +15 -0
  246. esphome/components/mipi_spi/display.py +474 -0
  247. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  248. esphome/components/mipi_spi/mipi_spi.h +171 -0
  249. esphome/components/mipi_spi/models/__init__.py +65 -0
  250. esphome/components/mipi_spi/models/amoled.py +72 -0
  251. esphome/components/mipi_spi/models/commands.py +82 -0
  252. esphome/components/mipi_spi/models/cyd.py +10 -0
  253. esphome/components/mipi_spi/models/ili.py +749 -0
  254. esphome/components/mipi_spi/models/jc.py +260 -0
  255. esphome/components/mipi_spi/models/lanbon.py +15 -0
  256. esphome/components/mipi_spi/models/lilygo.py +60 -0
  257. esphome/components/mipi_spi/models/waveshare.py +139 -0
  258. esphome/components/mitsubishi/climate.py +2 -5
  259. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  260. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  261. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  262. esphome/components/mlx90393/sensor.py +5 -0
  263. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  264. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  265. esphome/components/modbus/modbus.cpp +2 -1
  266. esphome/components/mqtt/__init__.py +1 -1
  267. esphome/components/mqtt/mqtt_client.cpp +6 -2
  268. esphome/components/mqtt/mqtt_const.h +4 -0
  269. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  270. esphome/components/mqtt/mqtt_fan.h +2 -0
  271. esphome/components/ms5611/sensor.py +6 -6
  272. esphome/components/ms8607/sensor.py +3 -3
  273. esphome/components/network/__init__.py +1 -1
  274. esphome/components/nextion/base_component.py +17 -16
  275. esphome/components/nextion/display.py +11 -2
  276. esphome/components/nextion/nextion.cpp +39 -1
  277. esphome/components/nextion/nextion.h +50 -0
  278. esphome/components/noblex/climate.py +2 -9
  279. esphome/components/number/__init__.py +12 -9
  280. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  281. esphome/components/one_wire/one_wire_bus.h +14 -8
  282. esphome/components/online_image/bmp_image.cpp +48 -11
  283. esphome/components/online_image/bmp_image.h +2 -0
  284. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  285. esphome/components/opentherm/number/__init__.py +11 -20
  286. esphome/components/opentherm/sensor/__init__.py +3 -3
  287. esphome/components/opentherm/switch/__init__.py +3 -5
  288. esphome/components/output/lock/__init__.py +11 -9
  289. esphome/components/packages/__init__.py +33 -31
  290. esphome/components/packet_transport/__init__.py +201 -0
  291. esphome/components/packet_transport/binary_sensor.py +19 -0
  292. esphome/components/packet_transport/packet_transport.cpp +534 -0
  293. esphome/components/packet_transport/packet_transport.h +154 -0
  294. esphome/components/packet_transport/sensor.py +19 -0
  295. esphome/components/pca9685/pca9685_output.cpp +2 -1
  296. esphome/components/pid/climate.py +2 -4
  297. esphome/components/pm2005/__init__.py +1 -0
  298. esphome/components/pm2005/pm2005.cpp +123 -0
  299. esphome/components/pm2005/pm2005.h +46 -0
  300. esphome/components/pm2005/sensor.py +86 -0
  301. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  302. esphome/components/pmsa003i/pmsa003i.h +25 -25
  303. esphome/components/pmsx003/pmsx003.cpp +195 -230
  304. esphome/components/pmsx003/pmsx003.h +51 -33
  305. esphome/components/pmsx003/sensor.py +21 -11
  306. esphome/components/pn7150/pn7150.h +2 -2
  307. esphome/components/pn7160/pn7160.h +2 -2
  308. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  309. esphome/components/prometheus/prometheus_handler.h +17 -0
  310. esphome/components/psram/__init__.py +7 -5
  311. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  312. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  313. esphome/components/pzem004t/pzem004t.cpp +2 -1
  314. esphome/components/qspi_dbi/__init__.py +0 -1
  315. esphome/components/qspi_dbi/display.py +2 -1
  316. esphome/components/qspi_dbi/models.py +1 -2
  317. esphome/components/remote_base/__init__.py +91 -0
  318. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  319. esphome/components/remote_base/beo4_protocol.h +43 -0
  320. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  321. esphome/components/remote_base/gobox_protocol.h +54 -0
  322. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  323. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  324. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  325. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  326. esphome/components/scd30/sensor.py +2 -3
  327. esphome/components/scd4x/sensor.py +4 -5
  328. esphome/components/sdp3x/sensor.py +2 -1
  329. esphome/components/sds011/sds011.cpp +2 -1
  330. esphome/components/select/__init__.py +19 -20
  331. esphome/components/sen5x/sen5x.cpp +55 -36
  332. esphome/components/sen5x/sensor.py +1 -1
  333. esphome/components/senseair/sensor.py +3 -3
  334. esphome/components/sensor/__init__.py +158 -14
  335. esphome/components/sensor/filter.cpp +23 -0
  336. esphome/components/sensor/filter.h +22 -0
  337. esphome/components/sgp30/sensor.py +14 -16
  338. esphome/components/sgp4x/sensor.py +1 -1
  339. esphome/components/sht4x/sht4x.cpp +43 -22
  340. esphome/components/sht4x/sht4x.h +1 -1
  341. esphome/components/shtcx/sensor.py +6 -6
  342. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  343. esphome/components/sml/text_sensor/__init__.py +4 -6
  344. esphome/components/sound_level/__init__.py +0 -0
  345. esphome/components/sound_level/sensor.py +97 -0
  346. esphome/components/sound_level/sound_level.cpp +194 -0
  347. esphome/components/sound_level/sound_level.h +73 -0
  348. esphome/components/speaker/media_player/__init__.py +4 -8
  349. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  350. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  351. esphome/components/speaker/speaker.h +4 -7
  352. esphome/components/speed/fan/__init__.py +17 -16
  353. esphome/components/spi/spi.h +11 -1
  354. esphome/components/sprinkler/__init__.py +18 -19
  355. esphome/components/sprinkler/sprinkler.cpp +6 -5
  356. esphome/components/switch/__init__.py +32 -42
  357. esphome/components/syslog/__init__.py +41 -0
  358. esphome/components/syslog/esphome_syslog.cpp +49 -0
  359. esphome/components/syslog/esphome_syslog.h +27 -0
  360. esphome/components/t6615/sensor.py +3 -3
  361. esphome/components/t6615/t6615.cpp +2 -1
  362. esphome/components/tca9555/tca9555.cpp +11 -6
  363. esphome/components/tcl112/climate.py +2 -9
  364. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  365. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  366. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  367. esphome/components/template/cover/__init__.py +27 -21
  368. esphome/components/template/fan/__init__.py +14 -12
  369. esphome/components/template/lock/__init__.py +20 -25
  370. esphome/components/template/lock/automation.h +18 -0
  371. esphome/components/template/text/__init__.py +4 -3
  372. esphome/components/template/valve/__init__.py +32 -21
  373. esphome/components/template/valve/automation.h +24 -0
  374. esphome/components/text/__init__.py +32 -1
  375. esphome/components/text_sensor/__init__.py +24 -29
  376. esphome/components/thermostat/climate.py +5 -5
  377. esphome/components/time_based/cover.py +17 -16
  378. esphome/components/time_based/time_based_cover.cpp +2 -1
  379. esphome/components/tm1638/switch/__init__.py +10 -7
  380. esphome/components/tormatic/cover.py +4 -5
  381. esphome/components/toshiba/climate.py +3 -5
  382. esphome/components/touchscreen/touchscreen.cpp +3 -1
  383. esphome/components/tt21100/touchscreen/tt21100.cpp +1 -1
  384. esphome/components/tuya/climate/__init__.py +5 -6
  385. esphome/components/tuya/cover/__init__.py +6 -11
  386. esphome/components/tuya/select/__init__.py +15 -5
  387. esphome/components/tuya/select/tuya_select.cpp +6 -1
  388. esphome/components/tuya/select/tuya_select.h +5 -1
  389. esphome/components/uart/packet_transport/__init__.py +20 -0
  390. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  391. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  392. esphome/components/uart/switch/uart_switch.cpp +2 -1
  393. esphome/components/udp/__init__.py +126 -128
  394. esphome/components/udp/automation.h +40 -0
  395. esphome/components/udp/binary_sensor.py +3 -25
  396. esphome/components/udp/packet_transport/__init__.py +29 -0
  397. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  398. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  399. esphome/components/udp/sensor.py +3 -25
  400. esphome/components/udp/udp_component.cpp +26 -470
  401. esphome/components/udp/udp_component.h +21 -128
  402. esphome/components/update/__init__.py +31 -1
  403. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  404. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  405. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  406. esphome/components/uptime/text_sensor/__init__.py +47 -7
  407. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  408. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  409. esphome/components/valve/__init__.py +34 -3
  410. esphome/components/valve/automation.h +1 -19
  411. esphome/components/vl53l0x/sensor.py +11 -0
  412. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  413. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  414. esphome/components/voice_assistant/__init__.py +36 -10
  415. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  416. esphome/components/voice_assistant/voice_assistant.h +26 -25
  417. esphome/components/waveshare_epaper/display.py +6 -0
  418. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  419. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  420. esphome/components/weikai/weikai.cpp +0 -52
  421. esphome/components/whirlpool/climate.py +3 -5
  422. esphome/components/whynter/climate.py +3 -5
  423. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  424. esphome/components/yashima/climate.py +6 -6
  425. esphome/components/zhlt01/climate.py +2 -7
  426. esphome/config.py +13 -13
  427. esphome/config_validation.py +38 -58
  428. esphome/const.py +15 -1
  429. esphome/core/__init__.py +2 -0
  430. esphome/core/application.cpp +27 -10
  431. esphome/core/application.h +9 -1
  432. esphome/core/automation.h +4 -3
  433. esphome/core/component.cpp +28 -7
  434. esphome/core/component.h +10 -1
  435. esphome/core/defines.h +23 -17
  436. esphome/core/doxygen.h +13 -0
  437. esphome/core/macros.h +4 -0
  438. esphome/core/scheduler.cpp +7 -1
  439. esphome/cpp_generator.py +6 -2
  440. esphome/dashboard/web_server.py +3 -3
  441. esphome/helpers.py +39 -0
  442. esphome/loader.py +4 -0
  443. esphome/log.py +15 -19
  444. esphome/mqtt.py +23 -10
  445. esphome/platformio_api.py +1 -1
  446. esphome/schema_extractors.py +0 -1
  447. esphome/voluptuous_schema.py +3 -1
  448. esphome/vscode.py +15 -0
  449. esphome/wizard.py +47 -37
  450. esphome/zeroconf.py +7 -3
  451. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  452. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/RECORD +456 -396
  453. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  454. esphome/components/esp32_ble/const_esp32c6.h +0 -74
  455. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  456. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  457. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -8,6 +8,7 @@
8
8
  #include "esphome/core/hal.h"
9
9
  #include "esphome/core/log.h"
10
10
  #include "esphome/core/version.h"
11
+ #include "esphome/core/application.h"
11
12
 
12
13
  #ifdef USE_DEEP_SLEEP
13
14
  #include "esphome/components/deep_sleep/deep_sleep_component.h"
@@ -29,7 +30,7 @@ static const char *const TAG = "api.connection";
29
30
  static const int ESP32_CAMERA_STOP_STREAM = 5000;
30
31
 
31
32
  // helper for allowing only unique entries in the queue
32
- void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t *send_message) {
33
+ void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t send_message) {
33
34
  DeferredMessage item(source, send_message);
34
35
 
35
36
  auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(),
@@ -45,7 +46,7 @@ void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_
45
46
  void DeferredMessageQueue::process_queue() {
46
47
  while (!deferred_queue_.empty()) {
47
48
  DeferredMessage &de = deferred_queue_.front();
48
- if (de.send_message_(this->api_connection_, de.source_)) {
49
+ if ((this->api_connection_->*(de.send_message_))(de.source_)) {
49
50
  // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
50
51
  deferred_queue_.erase(deferred_queue_.begin());
51
52
  } else {
@@ -54,7 +55,7 @@ void DeferredMessageQueue::process_queue() {
54
55
  }
55
56
  }
56
57
 
57
- void DeferredMessageQueue::defer(void *source, send_message_t *send_message) {
58
+ void DeferredMessageQueue::defer(void *source, send_message_t send_message) {
58
59
  this->dmq_push_back_with_dedup_(source, send_message);
59
60
  }
60
61
 
@@ -62,7 +63,14 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
62
63
  : parent_(parent), deferred_message_queue_(this), initial_state_iterator_(this), list_entities_iterator_(this) {
63
64
  this->proto_write_buffer_.reserve(64);
64
65
 
65
- #if defined(USE_API_PLAINTEXT)
66
+ #if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
67
+ auto noise_ctx = parent->get_noise_ctx();
68
+ if (noise_ctx->has_psk()) {
69
+ this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
70
+ } else {
71
+ this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
72
+ }
73
+ #elif defined(USE_API_PLAINTEXT)
66
74
  this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
67
75
  #elif defined(USE_API_NOISE)
68
76
  this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
@@ -71,7 +79,11 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
71
79
  #endif
72
80
  }
73
81
  void APIConnection::start() {
74
- this->last_traffic_ = millis();
82
+ this->last_traffic_ = App.get_loop_component_start_time();
83
+
84
+ // Set next_ping_retry_ to prevent immediate ping
85
+ // This ensures the first ping happens after the keepalive period
86
+ this->next_ping_retry_ = this->last_traffic_ + KEEPALIVE_TIMEOUT_MS;
75
87
 
76
88
  APIError err = this->helper_->init();
77
89
  if (err != APIError::OK) {
@@ -139,31 +151,32 @@ void APIConnection::loop() {
139
151
  }
140
152
  return;
141
153
  } else {
142
- this->last_traffic_ = millis();
154
+ this->last_traffic_ = App.get_loop_component_start_time();
143
155
  // read a packet
144
156
  this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
145
157
  if (this->remove_)
146
158
  return;
147
159
  }
148
160
 
149
- this->deferred_message_queue_.process_queue();
161
+ if (!this->deferred_message_queue_.empty() && this->helper_->can_write_without_blocking()) {
162
+ this->deferred_message_queue_.process_queue();
163
+ }
150
164
 
151
165
  if (!this->list_entities_iterator_.completed())
152
166
  this->list_entities_iterator_.advance();
153
167
  if (!this->initial_state_iterator_.completed() && this->list_entities_iterator_.completed())
154
168
  this->initial_state_iterator_.advance();
155
169
 
156
- static uint32_t keepalive = 60000;
157
170
  static uint8_t max_ping_retries = 60;
158
171
  static uint16_t ping_retry_interval = 1000;
159
- const uint32_t now = millis();
172
+ const uint32_t now = App.get_loop_component_start_time();
160
173
  if (this->sent_ping_) {
161
174
  // Disconnect if not responded within 2.5*keepalive
162
- if (now - this->last_traffic_ > (keepalive * 5) / 2) {
175
+ if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
163
176
  on_fatal_error();
164
177
  ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str());
165
178
  }
166
- } else if (now - this->last_traffic_ > keepalive && now > this->next_ping_retry_) {
179
+ } else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
167
180
  ESP_LOGVV(TAG, "Sending keepalive PING...");
168
181
  this->sent_ping_ = this->send_ping_request(PingRequest());
169
182
  if (!this->sent_ping_) {
@@ -185,15 +198,34 @@ void APIConnection::loop() {
185
198
 
186
199
  #ifdef USE_ESP32_CAMERA
187
200
  if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
188
- uint32_t to_send = std::min((size_t) 1024, this->image_reader_.available());
189
- auto buffer = this->create_buffer();
201
+ // Message will use 8 more bytes than the minimum size, and typical
202
+ // MTU is 1500. Sometimes users will see as low as 1460 MTU.
203
+ // If its IPv6 the header is 40 bytes, and if its IPv4
204
+ // the header is 20 bytes. So we have 1460 - 40 = 1420 bytes
205
+ // available for the payload. But we also need to add the size of
206
+ // the protobuf overhead, which is 8 bytes.
207
+ //
208
+ // To be safe we pick 1390 bytes as the maximum size
209
+ // to send in one go. This is the maximum size of a single packet
210
+ // that can be sent over the network.
211
+ // This is to avoid fragmentation of the packet.
212
+ uint32_t to_send = std::min((size_t) 1390, this->image_reader_.available());
213
+ bool done = this->image_reader_.available() == to_send;
214
+ uint32_t msg_size = 0;
215
+ ProtoSize::add_fixed_field<4>(msg_size, 1, true);
216
+ // partial message size calculated manually since its a special case
217
+ // 1 for the data field, varint for the data size, and the data itself
218
+ msg_size += 1 + ProtoSize::varint(to_send) + to_send;
219
+ ProtoSize::add_bool_field(msg_size, 1, done);
220
+
221
+ auto buffer = this->create_buffer(msg_size);
190
222
  // fixed32 key = 1;
191
223
  buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
192
224
  // bytes data = 2;
193
225
  buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
194
226
  // bool done = 3;
195
- bool done = this->image_reader_.available() == to_send;
196
227
  buffer.encode_bool(3, done);
228
+
197
229
  bool success = this->send_buffer(buffer, 44);
198
230
 
199
231
  if (success) {
@@ -241,96 +273,65 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
241
273
 
242
274
  #ifdef USE_BINARY_SENSOR
243
275
  bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) {
244
- if (!this->state_subscription_)
245
- return false;
246
-
247
- if (!APIConnection::try_send_binary_sensor_state(this, binary_sensor, state)) {
248
- this->deferred_message_queue_.defer(binary_sensor, try_send_binary_sensor_state);
249
- }
250
-
251
- return true;
276
+ return this->send_state_with_value_(binary_sensor, &APIConnection::try_send_binary_sensor_state_,
277
+ &APIConnection::try_send_binary_sensor_state_, state);
252
278
  }
253
279
  void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
254
- if (!APIConnection::try_send_binary_sensor_info(this, binary_sensor)) {
255
- this->deferred_message_queue_.defer(binary_sensor, try_send_binary_sensor_info);
256
- }
280
+ this->send_info_(static_cast<EntityBase *>(binary_sensor),
281
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_binary_sensor_info_));
257
282
  }
258
- bool APIConnection::try_send_binary_sensor_state(APIConnection *api, void *v_binary_sensor) {
259
- binary_sensor::BinarySensor *binary_sensor = reinterpret_cast<binary_sensor::BinarySensor *>(v_binary_sensor);
260
- return APIConnection::try_send_binary_sensor_state(api, binary_sensor, binary_sensor->state);
283
+ bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor) {
284
+ return this->try_send_binary_sensor_state_(binary_sensor, binary_sensor->state);
261
285
  }
262
- bool APIConnection::try_send_binary_sensor_state(APIConnection *api, binary_sensor::BinarySensor *binary_sensor,
263
- bool state) {
264
- BinarySensorStateResponse resp;
265
- resp.key = binary_sensor->get_object_id_hash();
266
- resp.state = state;
267
- resp.missing_state = !binary_sensor->has_state();
268
- return api->send_binary_sensor_state_response(resp);
286
+ bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state) {
287
+ BinarySensorStateResponse msg;
288
+ msg.state = state;
289
+ msg.missing_state = !binary_sensor->has_state();
290
+ msg.key = binary_sensor->get_object_id_hash();
291
+ return this->send_binary_sensor_state_response(msg);
269
292
  }
270
- bool APIConnection::try_send_binary_sensor_info(APIConnection *api, void *v_binary_sensor) {
271
- binary_sensor::BinarySensor *binary_sensor = reinterpret_cast<binary_sensor::BinarySensor *>(v_binary_sensor);
293
+ bool APIConnection::try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor) {
272
294
  ListEntitiesBinarySensorResponse msg;
273
- msg.object_id = binary_sensor->get_object_id();
274
- msg.key = binary_sensor->get_object_id_hash();
275
- if (binary_sensor->has_own_name())
276
- msg.name = binary_sensor->get_name();
277
- msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
278
295
  msg.device_class = binary_sensor->get_device_class();
279
296
  msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
280
- msg.disabled_by_default = binary_sensor->is_disabled_by_default();
281
- msg.icon = binary_sensor->get_icon();
282
- msg.entity_category = static_cast<enums::EntityCategory>(binary_sensor->get_entity_category());
283
- return api->send_list_entities_binary_sensor_response(msg);
297
+ msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
298
+ return this->try_send_entity_info_(static_cast<EntityBase *>(binary_sensor), msg,
299
+ &APIConnection::send_list_entities_binary_sensor_response);
284
300
  }
285
301
  #endif
286
302
 
287
303
  #ifdef USE_COVER
288
304
  bool APIConnection::send_cover_state(cover::Cover *cover) {
289
- if (!this->state_subscription_)
290
- return false;
291
-
292
- if (!APIConnection::try_send_cover_state(this, cover)) {
293
- this->deferred_message_queue_.defer(cover, try_send_cover_state);
294
- }
295
-
296
- return true;
305
+ return this->send_state_(static_cast<EntityBase *>(cover),
306
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_state_));
297
307
  }
298
308
  void APIConnection::send_cover_info(cover::Cover *cover) {
299
- if (!APIConnection::try_send_cover_info(this, cover)) {
300
- this->deferred_message_queue_.defer(cover, try_send_cover_info);
301
- }
309
+ this->send_info_(static_cast<EntityBase *>(cover),
310
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_info_));
302
311
  }
303
- bool APIConnection::try_send_cover_state(APIConnection *api, void *v_cover) {
304
- cover::Cover *cover = reinterpret_cast<cover::Cover *>(v_cover);
312
+ bool APIConnection::try_send_cover_state_(cover::Cover *cover) {
313
+ CoverStateResponse msg;
305
314
  auto traits = cover->get_traits();
306
- CoverStateResponse resp{};
307
- resp.key = cover->get_object_id_hash();
308
- resp.legacy_state =
315
+ msg.legacy_state =
309
316
  (cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
310
- resp.position = cover->position;
317
+ msg.position = cover->position;
311
318
  if (traits.get_supports_tilt())
312
- resp.tilt = cover->tilt;
313
- resp.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
314
- return api->send_cover_state_response(resp);
319
+ msg.tilt = cover->tilt;
320
+ msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
321
+ msg.key = cover->get_object_id_hash();
322
+ return this->send_cover_state_response(msg);
315
323
  }
316
- bool APIConnection::try_send_cover_info(APIConnection *api, void *v_cover) {
317
- cover::Cover *cover = reinterpret_cast<cover::Cover *>(v_cover);
318
- auto traits = cover->get_traits();
324
+ bool APIConnection::try_send_cover_info_(cover::Cover *cover) {
319
325
  ListEntitiesCoverResponse msg;
320
- msg.key = cover->get_object_id_hash();
321
- msg.object_id = cover->get_object_id();
322
- if (cover->has_own_name())
323
- msg.name = cover->get_name();
324
- msg.unique_id = get_default_unique_id("cover", cover);
326
+ auto traits = cover->get_traits();
325
327
  msg.assumed_state = traits.get_is_assumed_state();
326
328
  msg.supports_position = traits.get_supports_position();
327
329
  msg.supports_tilt = traits.get_supports_tilt();
328
330
  msg.supports_stop = traits.get_supports_stop();
329
331
  msg.device_class = cover->get_device_class();
330
- msg.disabled_by_default = cover->is_disabled_by_default();
331
- msg.icon = cover->get_icon();
332
- msg.entity_category = static_cast<enums::EntityCategory>(cover->get_entity_category());
333
- return api->send_list_entities_cover_response(msg);
332
+ msg.unique_id = get_default_unique_id("cover", cover);
333
+ return this->try_send_entity_info_(static_cast<EntityBase *>(cover), msg,
334
+ &APIConnection::send_list_entities_cover_response);
334
335
  }
335
336
  void APIConnection::cover_command(const CoverCommandRequest &msg) {
336
337
  cover::Cover *cover = App.get_cover_by_key(msg.key);
@@ -363,56 +364,41 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
363
364
 
364
365
  #ifdef USE_FAN
365
366
  bool APIConnection::send_fan_state(fan::Fan *fan) {
366
- if (!this->state_subscription_)
367
- return false;
368
-
369
- if (!APIConnection::try_send_fan_state(this, fan)) {
370
- this->deferred_message_queue_.defer(fan, try_send_fan_state);
371
- }
372
-
373
- return true;
367
+ return this->send_state_(static_cast<EntityBase *>(fan),
368
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_state_));
374
369
  }
375
370
  void APIConnection::send_fan_info(fan::Fan *fan) {
376
- if (!APIConnection::try_send_fan_info(this, fan)) {
377
- this->deferred_message_queue_.defer(fan, try_send_fan_info);
378
- }
371
+ this->send_info_(static_cast<EntityBase *>(fan),
372
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_info_));
379
373
  }
380
- bool APIConnection::try_send_fan_state(APIConnection *api, void *v_fan) {
381
- fan::Fan *fan = reinterpret_cast<fan::Fan *>(v_fan);
374
+ bool APIConnection::try_send_fan_state_(fan::Fan *fan) {
375
+ FanStateResponse msg;
382
376
  auto traits = fan->get_traits();
383
- FanStateResponse resp{};
384
- resp.key = fan->get_object_id_hash();
385
- resp.state = fan->state;
377
+ msg.state = fan->state;
386
378
  if (traits.supports_oscillation())
387
- resp.oscillating = fan->oscillating;
379
+ msg.oscillating = fan->oscillating;
388
380
  if (traits.supports_speed()) {
389
- resp.speed_level = fan->speed;
381
+ msg.speed_level = fan->speed;
390
382
  }
391
383
  if (traits.supports_direction())
392
- resp.direction = static_cast<enums::FanDirection>(fan->direction);
384
+ msg.direction = static_cast<enums::FanDirection>(fan->direction);
393
385
  if (traits.supports_preset_modes())
394
- resp.preset_mode = fan->preset_mode;
395
- return api->send_fan_state_response(resp);
386
+ msg.preset_mode = fan->preset_mode;
387
+ msg.key = fan->get_object_id_hash();
388
+ return this->send_fan_state_response(msg);
396
389
  }
397
- bool APIConnection::try_send_fan_info(APIConnection *api, void *v_fan) {
398
- fan::Fan *fan = reinterpret_cast<fan::Fan *>(v_fan);
399
- auto traits = fan->get_traits();
390
+ bool APIConnection::try_send_fan_info_(fan::Fan *fan) {
400
391
  ListEntitiesFanResponse msg;
401
- msg.key = fan->get_object_id_hash();
402
- msg.object_id = fan->get_object_id();
403
- if (fan->has_own_name())
404
- msg.name = fan->get_name();
405
- msg.unique_id = get_default_unique_id("fan", fan);
392
+ auto traits = fan->get_traits();
406
393
  msg.supports_oscillation = traits.supports_oscillation();
407
394
  msg.supports_speed = traits.supports_speed();
408
395
  msg.supports_direction = traits.supports_direction();
409
396
  msg.supported_speed_count = traits.supported_speed_count();
410
397
  for (auto const &preset : traits.supported_preset_modes())
411
398
  msg.supported_preset_modes.push_back(preset);
412
- msg.disabled_by_default = fan->is_disabled_by_default();
413
- msg.icon = fan->get_icon();
414
- msg.entity_category = static_cast<enums::EntityCategory>(fan->get_entity_category());
415
- return api->send_list_entities_fan_response(msg);
399
+ msg.unique_id = get_default_unique_id("fan", fan);
400
+ return this->try_send_entity_info_(static_cast<EntityBase *>(fan), msg,
401
+ &APIConnection::send_list_entities_fan_response);
416
402
  }
417
403
  void APIConnection::fan_command(const FanCommandRequest &msg) {
418
404
  fan::Fan *fan = App.get_fan_by_key(msg.key);
@@ -438,28 +424,18 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
438
424
 
439
425
  #ifdef USE_LIGHT
440
426
  bool APIConnection::send_light_state(light::LightState *light) {
441
- if (!this->state_subscription_)
442
- return false;
443
-
444
- if (!APIConnection::try_send_light_state(this, light)) {
445
- this->deferred_message_queue_.defer(light, try_send_light_state);
446
- }
447
-
448
- return true;
427
+ return this->send_state_(static_cast<EntityBase *>(light),
428
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_light_state_));
449
429
  }
450
430
  void APIConnection::send_light_info(light::LightState *light) {
451
- if (!APIConnection::try_send_light_info(this, light)) {
452
- this->deferred_message_queue_.defer(light, try_send_light_info);
453
- }
431
+ this->send_info_(static_cast<EntityBase *>(light),
432
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_light_info_));
454
433
  }
455
- bool APIConnection::try_send_light_state(APIConnection *api, void *v_light) {
456
- light::LightState *light = reinterpret_cast<light::LightState *>(v_light);
434
+ bool APIConnection::try_send_light_state_(light::LightState *light) {
435
+ LightStateResponse resp;
457
436
  auto traits = light->get_traits();
458
437
  auto values = light->remote_values;
459
438
  auto color_mode = values.get_color_mode();
460
- LightStateResponse resp{};
461
-
462
- resp.key = light->get_object_id_hash();
463
439
  resp.state = values.is_on();
464
440
  resp.color_mode = static_cast<enums::ColorMode>(color_mode);
465
441
  resp.brightness = values.get_brightness();
@@ -473,25 +449,14 @@ bool APIConnection::try_send_light_state(APIConnection *api, void *v_light) {
473
449
  resp.warm_white = values.get_warm_white();
474
450
  if (light->supports_effects())
475
451
  resp.effect = light->get_effect_name();
476
- return api->send_light_state_response(resp);
452
+ resp.key = light->get_object_id_hash();
453
+ return this->send_light_state_response(resp);
477
454
  }
478
- bool APIConnection::try_send_light_info(APIConnection *api, void *v_light) {
479
- light::LightState *light = reinterpret_cast<light::LightState *>(v_light);
480
- auto traits = light->get_traits();
455
+ bool APIConnection::try_send_light_info_(light::LightState *light) {
481
456
  ListEntitiesLightResponse msg;
482
- msg.key = light->get_object_id_hash();
483
- msg.object_id = light->get_object_id();
484
- if (light->has_own_name())
485
- msg.name = light->get_name();
486
- msg.unique_id = get_default_unique_id("light", light);
487
-
488
- msg.disabled_by_default = light->is_disabled_by_default();
489
- msg.icon = light->get_icon();
490
- msg.entity_category = static_cast<enums::EntityCategory>(light->get_entity_category());
491
-
457
+ auto traits = light->get_traits();
492
458
  for (auto mode : traits.get_supported_color_modes())
493
459
  msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
494
-
495
460
  msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
496
461
  msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
497
462
  msg.legacy_supports_white_value =
@@ -499,17 +464,19 @@ bool APIConnection::try_send_light_info(APIConnection *api, void *v_light) {
499
464
  traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
500
465
  msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
501
466
  traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
502
-
503
467
  if (msg.legacy_supports_color_temperature) {
504
468
  msg.min_mireds = traits.get_min_mireds();
505
469
  msg.max_mireds = traits.get_max_mireds();
506
470
  }
507
471
  if (light->supports_effects()) {
508
472
  msg.effects.emplace_back("None");
509
- for (auto *effect : light->get_effects())
473
+ for (auto *effect : light->get_effects()) {
510
474
  msg.effects.push_back(effect->get_name());
475
+ }
511
476
  }
512
- return api->send_list_entities_light_response(msg);
477
+ msg.unique_id = get_default_unique_id("light", light);
478
+ return this->try_send_entity_info_(static_cast<EntityBase *>(light), msg,
479
+ &APIConnection::send_list_entities_light_response);
513
480
  }
514
481
  void APIConnection::light_command(const LightCommandRequest &msg) {
515
482
  light::LightState *light = App.get_light_by_key(msg.key);
@@ -550,93 +517,65 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
550
517
 
551
518
  #ifdef USE_SENSOR
552
519
  bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
553
- if (!this->state_subscription_)
554
- return false;
555
-
556
- if (!APIConnection::try_send_sensor_state(this, sensor, state)) {
557
- this->deferred_message_queue_.defer(sensor, try_send_sensor_state);
558
- }
559
-
560
- return true;
520
+ return this->send_state_with_value_(sensor, &APIConnection::try_send_sensor_state_,
521
+ &APIConnection::try_send_sensor_state_, state);
561
522
  }
562
523
  void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
563
- if (!APIConnection::try_send_sensor_info(this, sensor)) {
564
- this->deferred_message_queue_.defer(sensor, try_send_sensor_info);
565
- }
524
+ this->send_info_(static_cast<EntityBase *>(sensor),
525
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_sensor_info_));
566
526
  }
567
- bool APIConnection::try_send_sensor_state(APIConnection *api, void *v_sensor) {
568
- sensor::Sensor *sensor = reinterpret_cast<sensor::Sensor *>(v_sensor);
569
- return APIConnection::try_send_sensor_state(api, sensor, sensor->state);
527
+ bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor) {
528
+ return this->try_send_sensor_state_(sensor, sensor->state);
570
529
  }
571
- bool APIConnection::try_send_sensor_state(APIConnection *api, sensor::Sensor *sensor, float state) {
572
- SensorStateResponse resp{};
573
- resp.key = sensor->get_object_id_hash();
530
+ bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor, float state) {
531
+ SensorStateResponse resp;
574
532
  resp.state = state;
575
533
  resp.missing_state = !sensor->has_state();
576
- return api->send_sensor_state_response(resp);
534
+
535
+ resp.key = sensor->get_object_id_hash();
536
+ return this->send_sensor_state_response(resp);
577
537
  }
578
- bool APIConnection::try_send_sensor_info(APIConnection *api, void *v_sensor) {
579
- sensor::Sensor *sensor = reinterpret_cast<sensor::Sensor *>(v_sensor);
538
+ bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) {
580
539
  ListEntitiesSensorResponse msg;
581
- msg.key = sensor->get_object_id_hash();
582
- msg.object_id = sensor->get_object_id();
583
- if (sensor->has_own_name())
584
- msg.name = sensor->get_name();
585
- msg.unique_id = sensor->unique_id();
586
- if (msg.unique_id.empty())
587
- msg.unique_id = get_default_unique_id("sensor", sensor);
588
- msg.icon = sensor->get_icon();
589
540
  msg.unit_of_measurement = sensor->get_unit_of_measurement();
590
541
  msg.accuracy_decimals = sensor->get_accuracy_decimals();
591
542
  msg.force_update = sensor->get_force_update();
592
543
  msg.device_class = sensor->get_device_class();
593
544
  msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
594
- msg.disabled_by_default = sensor->is_disabled_by_default();
595
- msg.entity_category = static_cast<enums::EntityCategory>(sensor->get_entity_category());
596
- return api->send_list_entities_sensor_response(msg);
545
+ msg.unique_id = sensor->unique_id();
546
+ if (msg.unique_id.empty())
547
+ msg.unique_id = get_default_unique_id("sensor", sensor);
548
+ return this->try_send_entity_info_(static_cast<EntityBase *>(sensor), msg,
549
+ &APIConnection::send_list_entities_sensor_response);
597
550
  }
598
551
  #endif
599
552
 
600
553
  #ifdef USE_SWITCH
601
554
  bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) {
602
- if (!this->state_subscription_)
603
- return false;
604
-
605
- if (!APIConnection::try_send_switch_state(this, a_switch, state)) {
606
- this->deferred_message_queue_.defer(a_switch, try_send_switch_state);
607
- }
608
-
609
- return true;
555
+ return this->send_state_with_value_(a_switch, &APIConnection::try_send_switch_state_,
556
+ &APIConnection::try_send_switch_state_, state);
610
557
  }
611
558
  void APIConnection::send_switch_info(switch_::Switch *a_switch) {
612
- if (!APIConnection::try_send_switch_info(this, a_switch)) {
613
- this->deferred_message_queue_.defer(a_switch, try_send_switch_info);
614
- }
559
+ this->send_info_(static_cast<EntityBase *>(a_switch),
560
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_switch_info_));
615
561
  }
616
- bool APIConnection::try_send_switch_state(APIConnection *api, void *v_a_switch) {
617
- switch_::Switch *a_switch = reinterpret_cast<switch_::Switch *>(v_a_switch);
618
- return APIConnection::try_send_switch_state(api, a_switch, a_switch->state);
562
+ bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch) {
563
+ return this->try_send_switch_state_(a_switch, a_switch->state);
619
564
  }
620
- bool APIConnection::try_send_switch_state(APIConnection *api, switch_::Switch *a_switch, bool state) {
621
- SwitchStateResponse resp{};
622
- resp.key = a_switch->get_object_id_hash();
565
+ bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch, bool state) {
566
+ SwitchStateResponse resp;
623
567
  resp.state = state;
624
- return api->send_switch_state_response(resp);
568
+
569
+ resp.key = a_switch->get_object_id_hash();
570
+ return this->send_switch_state_response(resp);
625
571
  }
626
- bool APIConnection::try_send_switch_info(APIConnection *api, void *v_a_switch) {
627
- switch_::Switch *a_switch = reinterpret_cast<switch_::Switch *>(v_a_switch);
572
+ bool APIConnection::try_send_switch_info_(switch_::Switch *a_switch) {
628
573
  ListEntitiesSwitchResponse msg;
629
- msg.key = a_switch->get_object_id_hash();
630
- msg.object_id = a_switch->get_object_id();
631
- if (a_switch->has_own_name())
632
- msg.name = a_switch->get_name();
633
- msg.unique_id = get_default_unique_id("switch", a_switch);
634
- msg.icon = a_switch->get_icon();
635
574
  msg.assumed_state = a_switch->assumed_state();
636
- msg.disabled_by_default = a_switch->is_disabled_by_default();
637
- msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
638
575
  msg.device_class = a_switch->get_device_class();
639
- return api->send_list_entities_switch_response(msg);
576
+ msg.unique_id = get_default_unique_id("switch", a_switch);
577
+ return this->try_send_entity_info_(static_cast<EntityBase *>(a_switch), msg,
578
+ &APIConnection::send_list_entities_switch_response);
640
579
  }
641
580
  void APIConnection::switch_command(const SwitchCommandRequest &msg) {
642
581
  switch_::Switch *a_switch = App.get_switch_by_key(msg.key);
@@ -653,70 +592,48 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
653
592
 
654
593
  #ifdef USE_TEXT_SENSOR
655
594
  bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) {
656
- if (!this->state_subscription_)
657
- return false;
658
-
659
- if (!APIConnection::try_send_text_sensor_state(this, text_sensor, std::move(state))) {
660
- this->deferred_message_queue_.defer(text_sensor, try_send_text_sensor_state);
661
- }
662
-
663
- return true;
595
+ return this->send_state_with_value_(text_sensor, &APIConnection::try_send_text_sensor_state_,
596
+ &APIConnection::try_send_text_sensor_state_, std::move(state));
664
597
  }
665
598
  void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
666
- if (!APIConnection::try_send_text_sensor_info(this, text_sensor)) {
667
- this->deferred_message_queue_.defer(text_sensor, try_send_text_sensor_info);
668
- }
599
+ this->send_info_(static_cast<EntityBase *>(text_sensor),
600
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_text_sensor_info_));
669
601
  }
670
- bool APIConnection::try_send_text_sensor_state(APIConnection *api, void *v_text_sensor) {
671
- text_sensor::TextSensor *text_sensor = reinterpret_cast<text_sensor::TextSensor *>(v_text_sensor);
672
- return APIConnection::try_send_text_sensor_state(api, text_sensor, text_sensor->state);
602
+ bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor) {
603
+ return this->try_send_text_sensor_state_(text_sensor, text_sensor->state);
673
604
  }
674
- bool APIConnection::try_send_text_sensor_state(APIConnection *api, text_sensor::TextSensor *text_sensor,
675
- std::string state) {
676
- TextSensorStateResponse resp{};
677
- resp.key = text_sensor->get_object_id_hash();
605
+ bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state) {
606
+ TextSensorStateResponse resp;
678
607
  resp.state = std::move(state);
679
608
  resp.missing_state = !text_sensor->has_state();
680
- return api->send_text_sensor_state_response(resp);
609
+
610
+ resp.key = text_sensor->get_object_id_hash();
611
+ return this->send_text_sensor_state_response(resp);
681
612
  }
682
- bool APIConnection::try_send_text_sensor_info(APIConnection *api, void *v_text_sensor) {
683
- text_sensor::TextSensor *text_sensor = reinterpret_cast<text_sensor::TextSensor *>(v_text_sensor);
613
+ bool APIConnection::try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor) {
684
614
  ListEntitiesTextSensorResponse msg;
685
- msg.key = text_sensor->get_object_id_hash();
686
- msg.object_id = text_sensor->get_object_id();
687
- msg.name = text_sensor->get_name();
615
+ msg.device_class = text_sensor->get_device_class();
688
616
  msg.unique_id = text_sensor->unique_id();
689
617
  if (msg.unique_id.empty())
690
618
  msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
691
- msg.icon = text_sensor->get_icon();
692
- msg.disabled_by_default = text_sensor->is_disabled_by_default();
693
- msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
694
- msg.device_class = text_sensor->get_device_class();
695
- return api->send_list_entities_text_sensor_response(msg);
619
+ return this->try_send_entity_info_(static_cast<EntityBase *>(text_sensor), msg,
620
+ &APIConnection::send_list_entities_text_sensor_response);
696
621
  }
697
622
  #endif
698
623
 
699
624
  #ifdef USE_CLIMATE
700
625
  bool APIConnection::send_climate_state(climate::Climate *climate) {
701
- if (!this->state_subscription_)
702
- return false;
703
-
704
- if (!APIConnection::try_send_climate_state(this, climate)) {
705
- this->deferred_message_queue_.defer(climate, try_send_climate_state);
706
- }
707
-
708
- return true;
626
+ return this->send_state_(static_cast<EntityBase *>(climate),
627
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_state_));
709
628
  }
710
629
  void APIConnection::send_climate_info(climate::Climate *climate) {
711
- if (!APIConnection::try_send_climate_info(this, climate)) {
712
- this->deferred_message_queue_.defer(climate, try_send_climate_info);
713
- }
630
+ this->send_info_(static_cast<EntityBase *>(climate),
631
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_info_));
714
632
  }
715
- bool APIConnection::try_send_climate_state(APIConnection *api, void *v_climate) {
716
- climate::Climate *climate = reinterpret_cast<climate::Climate *>(v_climate);
717
- auto traits = climate->get_traits();
718
- ClimateStateResponse resp{};
633
+ bool APIConnection::try_send_climate_state_(climate::Climate *climate) {
634
+ ClimateStateResponse resp;
719
635
  resp.key = climate->get_object_id_hash();
636
+ auto traits = climate->get_traits();
720
637
  resp.mode = static_cast<enums::ClimateMode>(climate->mode);
721
638
  resp.action = static_cast<enums::ClimateAction>(climate->action);
722
639
  if (traits.get_supports_current_temperature())
@@ -742,40 +659,25 @@ bool APIConnection::try_send_climate_state(APIConnection *api, void *v_climate)
742
659
  resp.current_humidity = climate->current_humidity;
743
660
  if (traits.get_supports_target_humidity())
744
661
  resp.target_humidity = climate->target_humidity;
745
- return api->send_climate_state_response(resp);
662
+ return this->send_climate_state_response(resp);
746
663
  }
747
- bool APIConnection::try_send_climate_info(APIConnection *api, void *v_climate) {
748
- climate::Climate *climate = reinterpret_cast<climate::Climate *>(v_climate);
749
- auto traits = climate->get_traits();
664
+ bool APIConnection::try_send_climate_info_(climate::Climate *climate) {
750
665
  ListEntitiesClimateResponse msg;
751
- msg.key = climate->get_object_id_hash();
752
- msg.object_id = climate->get_object_id();
753
- if (climate->has_own_name())
754
- msg.name = climate->get_name();
755
- msg.unique_id = get_default_unique_id("climate", climate);
756
-
757
- msg.disabled_by_default = climate->is_disabled_by_default();
758
- msg.icon = climate->get_icon();
759
- msg.entity_category = static_cast<enums::EntityCategory>(climate->get_entity_category());
760
-
666
+ auto traits = climate->get_traits();
761
667
  msg.supports_current_temperature = traits.get_supports_current_temperature();
762
668
  msg.supports_current_humidity = traits.get_supports_current_humidity();
763
669
  msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
764
670
  msg.supports_target_humidity = traits.get_supports_target_humidity();
765
-
766
671
  for (auto mode : traits.get_supported_modes())
767
672
  msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
768
-
769
673
  msg.visual_min_temperature = traits.get_visual_min_temperature();
770
674
  msg.visual_max_temperature = traits.get_visual_max_temperature();
771
675
  msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
772
676
  msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
773
677
  msg.visual_min_humidity = traits.get_visual_min_humidity();
774
678
  msg.visual_max_humidity = traits.get_visual_max_humidity();
775
-
776
679
  msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
777
680
  msg.supports_action = traits.get_supports_action();
778
-
779
681
  for (auto fan_mode : traits.get_supported_fan_modes())
780
682
  msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
781
683
  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
@@ -786,7 +688,9 @@ bool APIConnection::try_send_climate_info(APIConnection *api, void *v_climate) {
786
688
  msg.supported_custom_presets.push_back(custom_preset);
787
689
  for (auto swing_mode : traits.get_supported_swing_modes())
788
690
  msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
789
- return api->send_list_entities_climate_response(msg);
691
+ msg.unique_id = get_default_unique_id("climate", climate);
692
+ return this->try_send_entity_info_(static_cast<EntityBase *>(climate), msg,
693
+ &APIConnection::send_list_entities_climate_response);
790
694
  }
791
695
  void APIConnection::climate_command(const ClimateCommandRequest &msg) {
792
696
  climate::Climate *climate = App.get_climate_by_key(msg.key);
@@ -820,51 +724,35 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
820
724
 
821
725
  #ifdef USE_NUMBER
822
726
  bool APIConnection::send_number_state(number::Number *number, float state) {
823
- if (!this->state_subscription_)
824
- return false;
825
-
826
- if (!APIConnection::try_send_number_state(this, number, state)) {
827
- this->deferred_message_queue_.defer(number, try_send_number_state);
828
- }
829
-
830
- return true;
727
+ return this->send_state_with_value_(number, &APIConnection::try_send_number_state_,
728
+ &APIConnection::try_send_number_state_, state);
831
729
  }
832
730
  void APIConnection::send_number_info(number::Number *number) {
833
- if (!APIConnection::try_send_number_info(this, number)) {
834
- this->deferred_message_queue_.defer(number, try_send_number_info);
835
- }
731
+ this->send_info_(static_cast<EntityBase *>(number),
732
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_number_info_));
836
733
  }
837
- bool APIConnection::try_send_number_state(APIConnection *api, void *v_number) {
838
- number::Number *number = reinterpret_cast<number::Number *>(v_number);
839
- return APIConnection::try_send_number_state(api, number, number->state);
734
+ bool APIConnection::try_send_number_state_(number::Number *number) {
735
+ return this->try_send_number_state_(number, number->state);
840
736
  }
841
- bool APIConnection::try_send_number_state(APIConnection *api, number::Number *number, float state) {
842
- NumberStateResponse resp{};
843
- resp.key = number->get_object_id_hash();
737
+ bool APIConnection::try_send_number_state_(number::Number *number, float state) {
738
+ NumberStateResponse resp;
844
739
  resp.state = state;
845
740
  resp.missing_state = !number->has_state();
846
- return api->send_number_state_response(resp);
741
+
742
+ resp.key = number->get_object_id_hash();
743
+ return this->send_number_state_response(resp);
847
744
  }
848
- bool APIConnection::try_send_number_info(APIConnection *api, void *v_number) {
849
- number::Number *number = reinterpret_cast<number::Number *>(v_number);
745
+ bool APIConnection::try_send_number_info_(number::Number *number) {
850
746
  ListEntitiesNumberResponse msg;
851
- msg.key = number->get_object_id_hash();
852
- msg.object_id = number->get_object_id();
853
- if (number->has_own_name())
854
- msg.name = number->get_name();
855
- msg.unique_id = get_default_unique_id("number", number);
856
- msg.icon = number->get_icon();
857
- msg.disabled_by_default = number->is_disabled_by_default();
858
- msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
859
747
  msg.unit_of_measurement = number->traits.get_unit_of_measurement();
860
748
  msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
861
749
  msg.device_class = number->traits.get_device_class();
862
-
863
750
  msg.min_value = number->traits.get_min_value();
864
751
  msg.max_value = number->traits.get_max_value();
865
752
  msg.step = number->traits.get_step();
866
-
867
- return api->send_list_entities_number_response(msg);
753
+ msg.unique_id = get_default_unique_id("number", number);
754
+ return this->try_send_entity_info_(static_cast<EntityBase *>(number), msg,
755
+ &APIConnection::send_list_entities_number_response);
868
756
  }
869
757
  void APIConnection::number_command(const NumberCommandRequest &msg) {
870
758
  number::Number *number = App.get_number_by_key(msg.key);
@@ -879,43 +767,28 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
879
767
 
880
768
  #ifdef USE_DATETIME_DATE
881
769
  bool APIConnection::send_date_state(datetime::DateEntity *date) {
882
- if (!this->state_subscription_)
883
- return false;
884
-
885
- if (!APIConnection::try_send_date_state(this, date)) {
886
- this->deferred_message_queue_.defer(date, try_send_date_state);
887
- }
888
-
889
- return true;
770
+ return this->send_state_(static_cast<EntityBase *>(date),
771
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_date_state_));
890
772
  }
891
773
  void APIConnection::send_date_info(datetime::DateEntity *date) {
892
- if (!APIConnection::try_send_date_info(this, date)) {
893
- this->deferred_message_queue_.defer(date, try_send_date_info);
894
- }
774
+ this->send_info_(static_cast<EntityBase *>(date),
775
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_date_info_));
895
776
  }
896
- bool APIConnection::try_send_date_state(APIConnection *api, void *v_date) {
897
- datetime::DateEntity *date = reinterpret_cast<datetime::DateEntity *>(v_date);
898
- DateStateResponse resp{};
899
- resp.key = date->get_object_id_hash();
777
+ bool APIConnection::try_send_date_state_(datetime::DateEntity *date) {
778
+ DateStateResponse resp;
900
779
  resp.missing_state = !date->has_state();
901
780
  resp.year = date->year;
902
781
  resp.month = date->month;
903
782
  resp.day = date->day;
904
- return api->send_date_state_response(resp);
783
+
784
+ resp.key = date->get_object_id_hash();
785
+ return this->send_date_state_response(resp);
905
786
  }
906
- bool APIConnection::try_send_date_info(APIConnection *api, void *v_date) {
907
- datetime::DateEntity *date = reinterpret_cast<datetime::DateEntity *>(v_date);
787
+ bool APIConnection::try_send_date_info_(datetime::DateEntity *date) {
908
788
  ListEntitiesDateResponse msg;
909
- msg.key = date->get_object_id_hash();
910
- msg.object_id = date->get_object_id();
911
- if (date->has_own_name())
912
- msg.name = date->get_name();
913
789
  msg.unique_id = get_default_unique_id("date", date);
914
- msg.icon = date->get_icon();
915
- msg.disabled_by_default = date->is_disabled_by_default();
916
- msg.entity_category = static_cast<enums::EntityCategory>(date->get_entity_category());
917
-
918
- return api->send_list_entities_date_response(msg);
790
+ return this->try_send_entity_info_(static_cast<EntityBase *>(date), msg,
791
+ &APIConnection::send_list_entities_date_response);
919
792
  }
920
793
  void APIConnection::date_command(const DateCommandRequest &msg) {
921
794
  datetime::DateEntity *date = App.get_date_by_key(msg.key);
@@ -930,43 +803,28 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
930
803
 
931
804
  #ifdef USE_DATETIME_TIME
932
805
  bool APIConnection::send_time_state(datetime::TimeEntity *time) {
933
- if (!this->state_subscription_)
934
- return false;
935
-
936
- if (!APIConnection::try_send_time_state(this, time)) {
937
- this->deferred_message_queue_.defer(time, try_send_time_state);
938
- }
939
-
940
- return true;
806
+ return this->send_state_(static_cast<EntityBase *>(time),
807
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_time_state_));
941
808
  }
942
809
  void APIConnection::send_time_info(datetime::TimeEntity *time) {
943
- if (!APIConnection::try_send_time_info(this, time)) {
944
- this->deferred_message_queue_.defer(time, try_send_time_info);
945
- }
810
+ this->send_info_(static_cast<EntityBase *>(time),
811
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_time_info_));
946
812
  }
947
- bool APIConnection::try_send_time_state(APIConnection *api, void *v_time) {
948
- datetime::TimeEntity *time = reinterpret_cast<datetime::TimeEntity *>(v_time);
949
- TimeStateResponse resp{};
950
- resp.key = time->get_object_id_hash();
813
+ bool APIConnection::try_send_time_state_(datetime::TimeEntity *time) {
814
+ TimeStateResponse resp;
951
815
  resp.missing_state = !time->has_state();
952
816
  resp.hour = time->hour;
953
817
  resp.minute = time->minute;
954
818
  resp.second = time->second;
955
- return api->send_time_state_response(resp);
819
+
820
+ resp.key = time->get_object_id_hash();
821
+ return this->send_time_state_response(resp);
956
822
  }
957
- bool APIConnection::try_send_time_info(APIConnection *api, void *v_time) {
958
- datetime::TimeEntity *time = reinterpret_cast<datetime::TimeEntity *>(v_time);
823
+ bool APIConnection::try_send_time_info_(datetime::TimeEntity *time) {
959
824
  ListEntitiesTimeResponse msg;
960
- msg.key = time->get_object_id_hash();
961
- msg.object_id = time->get_object_id();
962
- if (time->has_own_name())
963
- msg.name = time->get_name();
964
825
  msg.unique_id = get_default_unique_id("time", time);
965
- msg.icon = time->get_icon();
966
- msg.disabled_by_default = time->is_disabled_by_default();
967
- msg.entity_category = static_cast<enums::EntityCategory>(time->get_entity_category());
968
-
969
- return api->send_list_entities_time_response(msg);
826
+ return this->try_send_entity_info_(static_cast<EntityBase *>(time), msg,
827
+ &APIConnection::send_list_entities_time_response);
970
828
  }
971
829
  void APIConnection::time_command(const TimeCommandRequest &msg) {
972
830
  datetime::TimeEntity *time = App.get_time_by_key(msg.key);
@@ -981,44 +839,29 @@ void APIConnection::time_command(const TimeCommandRequest &msg) {
981
839
 
982
840
  #ifdef USE_DATETIME_DATETIME
983
841
  bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
984
- if (!this->state_subscription_)
985
- return false;
986
-
987
- if (!APIConnection::try_send_datetime_state(this, datetime)) {
988
- this->deferred_message_queue_.defer(datetime, try_send_datetime_state);
989
- }
990
-
991
- return true;
842
+ return this->send_state_(static_cast<EntityBase *>(datetime),
843
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_state_));
992
844
  }
993
845
  void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
994
- if (!APIConnection::try_send_datetime_info(this, datetime)) {
995
- this->deferred_message_queue_.defer(datetime, try_send_datetime_info);
996
- }
846
+ this->send_info_(static_cast<EntityBase *>(datetime),
847
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_info_));
997
848
  }
998
- bool APIConnection::try_send_datetime_state(APIConnection *api, void *v_datetime) {
999
- datetime::DateTimeEntity *datetime = reinterpret_cast<datetime::DateTimeEntity *>(v_datetime);
1000
- DateTimeStateResponse resp{};
1001
- resp.key = datetime->get_object_id_hash();
849
+ bool APIConnection::try_send_datetime_state_(datetime::DateTimeEntity *datetime) {
850
+ DateTimeStateResponse resp;
1002
851
  resp.missing_state = !datetime->has_state();
1003
852
  if (datetime->has_state()) {
1004
853
  ESPTime state = datetime->state_as_esptime();
1005
854
  resp.epoch_seconds = state.timestamp;
1006
855
  }
1007
- return api->send_date_time_state_response(resp);
856
+
857
+ resp.key = datetime->get_object_id_hash();
858
+ return this->send_date_time_state_response(resp);
1008
859
  }
1009
- bool APIConnection::try_send_datetime_info(APIConnection *api, void *v_datetime) {
1010
- datetime::DateTimeEntity *datetime = reinterpret_cast<datetime::DateTimeEntity *>(v_datetime);
860
+ bool APIConnection::try_send_datetime_info_(datetime::DateTimeEntity *datetime) {
1011
861
  ListEntitiesDateTimeResponse msg;
1012
- msg.key = datetime->get_object_id_hash();
1013
- msg.object_id = datetime->get_object_id();
1014
- if (datetime->has_own_name())
1015
- msg.name = datetime->get_name();
1016
862
  msg.unique_id = get_default_unique_id("datetime", datetime);
1017
- msg.icon = datetime->get_icon();
1018
- msg.disabled_by_default = datetime->is_disabled_by_default();
1019
- msg.entity_category = static_cast<enums::EntityCategory>(datetime->get_entity_category());
1020
-
1021
- return api->send_list_entities_date_time_response(msg);
863
+ return this->try_send_entity_info_(static_cast<EntityBase *>(datetime), msg,
864
+ &APIConnection::send_list_entities_date_time_response);
1022
865
  }
1023
866
  void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
1024
867
  datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key);
@@ -1033,47 +876,31 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
1033
876
 
1034
877
  #ifdef USE_TEXT
1035
878
  bool APIConnection::send_text_state(text::Text *text, std::string state) {
1036
- if (!this->state_subscription_)
1037
- return false;
1038
-
1039
- if (!APIConnection::try_send_text_state(this, text, std::move(state))) {
1040
- this->deferred_message_queue_.defer(text, try_send_text_state);
1041
- }
1042
-
1043
- return true;
879
+ return this->send_state_with_value_(text, &APIConnection::try_send_text_state_, &APIConnection::try_send_text_state_,
880
+ std::move(state));
1044
881
  }
1045
882
  void APIConnection::send_text_info(text::Text *text) {
1046
- if (!APIConnection::try_send_text_info(this, text)) {
1047
- this->deferred_message_queue_.defer(text, try_send_text_info);
1048
- }
1049
- }
1050
- bool APIConnection::try_send_text_state(APIConnection *api, void *v_text) {
1051
- text::Text *text = reinterpret_cast<text::Text *>(v_text);
1052
- return APIConnection::try_send_text_state(api, text, text->state);
883
+ this->send_info_(static_cast<EntityBase *>(text),
884
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_text_info_));
1053
885
  }
1054
- bool APIConnection::try_send_text_state(APIConnection *api, text::Text *text, std::string state) {
1055
- TextStateResponse resp{};
1056
- resp.key = text->get_object_id_hash();
886
+ bool APIConnection::try_send_text_state_(text::Text *text) { return this->try_send_text_state_(text, text->state); }
887
+ bool APIConnection::try_send_text_state_(text::Text *text, std::string state) {
888
+ TextStateResponse resp;
1057
889
  resp.state = std::move(state);
1058
890
  resp.missing_state = !text->has_state();
1059
- return api->send_text_state_response(resp);
891
+
892
+ resp.key = text->get_object_id_hash();
893
+ return this->send_text_state_response(resp);
1060
894
  }
1061
- bool APIConnection::try_send_text_info(APIConnection *api, void *v_text) {
1062
- text::Text *text = reinterpret_cast<text::Text *>(v_text);
895
+ bool APIConnection::try_send_text_info_(text::Text *text) {
1063
896
  ListEntitiesTextResponse msg;
1064
- msg.key = text->get_object_id_hash();
1065
- msg.object_id = text->get_object_id();
1066
- msg.name = text->get_name();
1067
- msg.icon = text->get_icon();
1068
- msg.disabled_by_default = text->is_disabled_by_default();
1069
- msg.entity_category = static_cast<enums::EntityCategory>(text->get_entity_category());
1070
897
  msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
1071
-
1072
898
  msg.min_length = text->traits.get_min_length();
1073
899
  msg.max_length = text->traits.get_max_length();
1074
900
  msg.pattern = text->traits.get_pattern();
1075
-
1076
- return api->send_list_entities_text_response(msg);
901
+ msg.unique_id = get_default_unique_id("text", text);
902
+ return this->try_send_entity_info_(static_cast<EntityBase *>(text), msg,
903
+ &APIConnection::send_list_entities_text_response);
1077
904
  }
1078
905
  void APIConnection::text_command(const TextCommandRequest &msg) {
1079
906
  text::Text *text = App.get_text_by_key(msg.key);
@@ -1088,47 +915,31 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
1088
915
 
1089
916
  #ifdef USE_SELECT
1090
917
  bool APIConnection::send_select_state(select::Select *select, std::string state) {
1091
- if (!this->state_subscription_)
1092
- return false;
1093
-
1094
- if (!APIConnection::try_send_select_state(this, select, std::move(state))) {
1095
- this->deferred_message_queue_.defer(select, try_send_select_state);
1096
- }
1097
-
1098
- return true;
918
+ return this->send_state_with_value_(select, &APIConnection::try_send_select_state_,
919
+ &APIConnection::try_send_select_state_, std::move(state));
1099
920
  }
1100
921
  void APIConnection::send_select_info(select::Select *select) {
1101
- if (!APIConnection::try_send_select_info(this, select)) {
1102
- this->deferred_message_queue_.defer(select, try_send_select_info);
1103
- }
922
+ this->send_info_(static_cast<EntityBase *>(select),
923
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_select_info_));
1104
924
  }
1105
- bool APIConnection::try_send_select_state(APIConnection *api, void *v_select) {
1106
- select::Select *select = reinterpret_cast<select::Select *>(v_select);
1107
- return APIConnection::try_send_select_state(api, select, select->state);
925
+ bool APIConnection::try_send_select_state_(select::Select *select) {
926
+ return this->try_send_select_state_(select, select->state);
1108
927
  }
1109
- bool APIConnection::try_send_select_state(APIConnection *api, select::Select *select, std::string state) {
1110
- SelectStateResponse resp{};
1111
- resp.key = select->get_object_id_hash();
928
+ bool APIConnection::try_send_select_state_(select::Select *select, std::string state) {
929
+ SelectStateResponse resp;
1112
930
  resp.state = std::move(state);
1113
931
  resp.missing_state = !select->has_state();
1114
- return api->send_select_state_response(resp);
932
+
933
+ resp.key = select->get_object_id_hash();
934
+ return this->send_select_state_response(resp);
1115
935
  }
1116
- bool APIConnection::try_send_select_info(APIConnection *api, void *v_select) {
1117
- select::Select *select = reinterpret_cast<select::Select *>(v_select);
936
+ bool APIConnection::try_send_select_info_(select::Select *select) {
1118
937
  ListEntitiesSelectResponse msg;
1119
- msg.key = select->get_object_id_hash();
1120
- msg.object_id = select->get_object_id();
1121
- if (select->has_own_name())
1122
- msg.name = select->get_name();
1123
- msg.unique_id = get_default_unique_id("select", select);
1124
- msg.icon = select->get_icon();
1125
- msg.disabled_by_default = select->is_disabled_by_default();
1126
- msg.entity_category = static_cast<enums::EntityCategory>(select->get_entity_category());
1127
-
1128
938
  for (const auto &option : select->traits.get_options())
1129
939
  msg.options.push_back(option);
1130
-
1131
- return api->send_list_entities_select_response(msg);
940
+ msg.unique_id = get_default_unique_id("select", select);
941
+ return this->try_send_entity_info_(static_cast<EntityBase *>(select), msg,
942
+ &APIConnection::send_list_entities_select_response);
1132
943
  }
1133
944
  void APIConnection::select_command(const SelectCommandRequest &msg) {
1134
945
  select::Select *select = App.get_select_by_key(msg.key);
@@ -1142,26 +953,18 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
1142
953
  #endif
1143
954
 
1144
955
  #ifdef USE_BUTTON
1145
- void APIConnection::send_button_info(button::Button *button) {
1146
- if (!APIConnection::try_send_button_info(this, button)) {
1147
- this->deferred_message_queue_.defer(button, try_send_button_info);
1148
- }
956
+ void esphome::api::APIConnection::send_button_info(button::Button *button) {
957
+ this->send_info_(static_cast<EntityBase *>(button),
958
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_button_info_));
1149
959
  }
1150
- bool APIConnection::try_send_button_info(APIConnection *api, void *v_button) {
1151
- button::Button *button = reinterpret_cast<button::Button *>(v_button);
960
+ bool esphome::api::APIConnection::try_send_button_info_(button::Button *button) {
1152
961
  ListEntitiesButtonResponse msg;
1153
- msg.key = button->get_object_id_hash();
1154
- msg.object_id = button->get_object_id();
1155
- if (button->has_own_name())
1156
- msg.name = button->get_name();
1157
- msg.unique_id = get_default_unique_id("button", button);
1158
- msg.icon = button->get_icon();
1159
- msg.disabled_by_default = button->is_disabled_by_default();
1160
- msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
1161
962
  msg.device_class = button->get_device_class();
1162
- return api->send_list_entities_button_response(msg);
963
+ msg.unique_id = get_default_unique_id("button", button);
964
+ return this->try_send_entity_info_(static_cast<EntityBase *>(button), msg,
965
+ &APIConnection::send_list_entities_button_response);
1163
966
  }
1164
- void APIConnection::button_command(const ButtonCommandRequest &msg) {
967
+ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
1165
968
  button::Button *button = App.get_button_by_key(msg.key);
1166
969
  if (button == nullptr)
1167
970
  return;
@@ -1172,45 +975,31 @@ void APIConnection::button_command(const ButtonCommandRequest &msg) {
1172
975
 
1173
976
  #ifdef USE_LOCK
1174
977
  bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
1175
- if (!this->state_subscription_)
1176
- return false;
1177
-
1178
- if (!APIConnection::try_send_lock_state(this, a_lock, state)) {
1179
- this->deferred_message_queue_.defer(a_lock, try_send_lock_state);
1180
- }
1181
-
1182
- return true;
978
+ return this->send_state_with_value_(a_lock, &APIConnection::try_send_lock_state_,
979
+ &APIConnection::try_send_lock_state_, state);
1183
980
  }
1184
981
  void APIConnection::send_lock_info(lock::Lock *a_lock) {
1185
- if (!APIConnection::try_send_lock_info(this, a_lock)) {
1186
- this->deferred_message_queue_.defer(a_lock, try_send_lock_info);
1187
- }
982
+ this->send_info_(static_cast<EntityBase *>(a_lock),
983
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_lock_info_));
1188
984
  }
1189
- bool APIConnection::try_send_lock_state(APIConnection *api, void *v_a_lock) {
1190
- lock::Lock *a_lock = reinterpret_cast<lock::Lock *>(v_a_lock);
1191
- return APIConnection::try_send_lock_state(api, a_lock, a_lock->state);
985
+ bool APIConnection::try_send_lock_state_(lock::Lock *a_lock) {
986
+ return this->try_send_lock_state_(a_lock, a_lock->state);
1192
987
  }
1193
- bool APIConnection::try_send_lock_state(APIConnection *api, lock::Lock *a_lock, lock::LockState state) {
1194
- LockStateResponse resp{};
1195
- resp.key = a_lock->get_object_id_hash();
988
+ bool APIConnection::try_send_lock_state_(lock::Lock *a_lock, lock::LockState state) {
989
+ LockStateResponse resp;
1196
990
  resp.state = static_cast<enums::LockState>(state);
1197
- return api->send_lock_state_response(resp);
991
+
992
+ resp.key = a_lock->get_object_id_hash();
993
+ return this->send_lock_state_response(resp);
1198
994
  }
1199
- bool APIConnection::try_send_lock_info(APIConnection *api, void *v_a_lock) {
1200
- lock::Lock *a_lock = reinterpret_cast<lock::Lock *>(v_a_lock);
995
+ bool APIConnection::try_send_lock_info_(lock::Lock *a_lock) {
1201
996
  ListEntitiesLockResponse msg;
1202
- msg.key = a_lock->get_object_id_hash();
1203
- msg.object_id = a_lock->get_object_id();
1204
- if (a_lock->has_own_name())
1205
- msg.name = a_lock->get_name();
1206
- msg.unique_id = get_default_unique_id("lock", a_lock);
1207
- msg.icon = a_lock->get_icon();
1208
997
  msg.assumed_state = a_lock->traits.get_assumed_state();
1209
- msg.disabled_by_default = a_lock->is_disabled_by_default();
1210
- msg.entity_category = static_cast<enums::EntityCategory>(a_lock->get_entity_category());
1211
998
  msg.supports_open = a_lock->traits.get_supports_open();
1212
999
  msg.requires_code = a_lock->traits.get_requires_code();
1213
- return api->send_list_entities_lock_response(msg);
1000
+ msg.unique_id = get_default_unique_id("lock", a_lock);
1001
+ return this->try_send_entity_info_(static_cast<EntityBase *>(a_lock), msg,
1002
+ &APIConnection::send_list_entities_lock_response);
1214
1003
  }
1215
1004
  void APIConnection::lock_command(const LockCommandRequest &msg) {
1216
1005
  lock::Lock *a_lock = App.get_lock_by_key(msg.key);
@@ -1233,45 +1022,31 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
1233
1022
 
1234
1023
  #ifdef USE_VALVE
1235
1024
  bool APIConnection::send_valve_state(valve::Valve *valve) {
1236
- if (!this->state_subscription_)
1237
- return false;
1238
-
1239
- if (!APIConnection::try_send_valve_state(this, valve)) {
1240
- this->deferred_message_queue_.defer(valve, try_send_valve_state);
1241
- }
1242
-
1243
- return true;
1025
+ return this->send_state_(static_cast<EntityBase *>(valve),
1026
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_state_));
1244
1027
  }
1245
1028
  void APIConnection::send_valve_info(valve::Valve *valve) {
1246
- if (!APIConnection::try_send_valve_info(this, valve)) {
1247
- this->deferred_message_queue_.defer(valve, try_send_valve_info);
1248
- }
1029
+ this->send_info_(static_cast<EntityBase *>(valve),
1030
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_info_));
1249
1031
  }
1250
- bool APIConnection::try_send_valve_state(APIConnection *api, void *v_valve) {
1251
- valve::Valve *valve = reinterpret_cast<valve::Valve *>(v_valve);
1252
- ValveStateResponse resp{};
1253
- resp.key = valve->get_object_id_hash();
1032
+ bool APIConnection::try_send_valve_state_(valve::Valve *valve) {
1033
+ ValveStateResponse resp;
1254
1034
  resp.position = valve->position;
1255
1035
  resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
1256
- return api->send_valve_state_response(resp);
1036
+
1037
+ resp.key = valve->get_object_id_hash();
1038
+ return this->send_valve_state_response(resp);
1257
1039
  }
1258
- bool APIConnection::try_send_valve_info(APIConnection *api, void *v_valve) {
1259
- valve::Valve *valve = reinterpret_cast<valve::Valve *>(v_valve);
1260
- auto traits = valve->get_traits();
1040
+ bool APIConnection::try_send_valve_info_(valve::Valve *valve) {
1261
1041
  ListEntitiesValveResponse msg;
1262
- msg.key = valve->get_object_id_hash();
1263
- msg.object_id = valve->get_object_id();
1264
- if (valve->has_own_name())
1265
- msg.name = valve->get_name();
1266
- msg.unique_id = get_default_unique_id("valve", valve);
1267
- msg.icon = valve->get_icon();
1268
- msg.disabled_by_default = valve->is_disabled_by_default();
1269
- msg.entity_category = static_cast<enums::EntityCategory>(valve->get_entity_category());
1042
+ auto traits = valve->get_traits();
1270
1043
  msg.device_class = valve->get_device_class();
1271
1044
  msg.assumed_state = traits.get_is_assumed_state();
1272
1045
  msg.supports_position = traits.get_supports_position();
1273
1046
  msg.supports_stop = traits.get_supports_stop();
1274
- return api->send_list_entities_valve_response(msg);
1047
+ msg.unique_id = get_default_unique_id("valve", valve);
1048
+ return this->try_send_entity_info_(static_cast<EntityBase *>(valve), msg,
1049
+ &APIConnection::send_list_entities_valve_response);
1275
1050
  }
1276
1051
  void APIConnection::valve_command(const ValveCommandRequest &msg) {
1277
1052
  valve::Valve *valve = App.get_valve_by_key(msg.key);
@@ -1289,48 +1064,29 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) {
1289
1064
 
1290
1065
  #ifdef USE_MEDIA_PLAYER
1291
1066
  bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
1292
- if (!this->state_subscription_)
1293
- return false;
1294
-
1295
- if (!APIConnection::try_send_media_player_state(this, media_player)) {
1296
- this->deferred_message_queue_.defer(media_player, try_send_media_player_state);
1297
- }
1298
-
1299
- return true;
1067
+ return this->send_state_(static_cast<EntityBase *>(media_player),
1068
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_state_));
1300
1069
  }
1301
1070
  void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
1302
- if (!APIConnection::try_send_media_player_info(this, media_player)) {
1303
- this->deferred_message_queue_.defer(media_player, try_send_media_player_info);
1304
- }
1071
+ this->send_info_(static_cast<EntityBase *>(media_player),
1072
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_info_));
1305
1073
  }
1306
- bool APIConnection::try_send_media_player_state(APIConnection *api, void *v_media_player) {
1307
- media_player::MediaPlayer *media_player = reinterpret_cast<media_player::MediaPlayer *>(v_media_player);
1308
- MediaPlayerStateResponse resp{};
1309
- resp.key = media_player->get_object_id_hash();
1310
-
1074
+ bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *media_player) {
1075
+ MediaPlayerStateResponse resp;
1311
1076
  media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
1312
1077
  ? media_player::MEDIA_PLAYER_STATE_PLAYING
1313
1078
  : media_player->state;
1314
1079
  resp.state = static_cast<enums::MediaPlayerState>(report_state);
1315
1080
  resp.volume = media_player->volume;
1316
1081
  resp.muted = media_player->is_muted();
1317
- return api->send_media_player_state_response(resp);
1082
+
1083
+ resp.key = media_player->get_object_id_hash();
1084
+ return this->send_media_player_state_response(resp);
1318
1085
  }
1319
- bool APIConnection::try_send_media_player_info(APIConnection *api, void *v_media_player) {
1320
- media_player::MediaPlayer *media_player = reinterpret_cast<media_player::MediaPlayer *>(v_media_player);
1086
+ bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media_player) {
1321
1087
  ListEntitiesMediaPlayerResponse msg;
1322
- msg.key = media_player->get_object_id_hash();
1323
- msg.object_id = media_player->get_object_id();
1324
- if (media_player->has_own_name())
1325
- msg.name = media_player->get_name();
1326
- msg.unique_id = get_default_unique_id("media_player", media_player);
1327
- msg.icon = media_player->get_icon();
1328
- msg.disabled_by_default = media_player->is_disabled_by_default();
1329
- msg.entity_category = static_cast<enums::EntityCategory>(media_player->get_entity_category());
1330
-
1331
1088
  auto traits = media_player->get_traits();
1332
1089
  msg.supports_pause = traits.get_supports_pause();
1333
-
1334
1090
  for (auto &supported_format : traits.get_supported_formats()) {
1335
1091
  MediaPlayerSupportedFormat media_format;
1336
1092
  media_format.format = supported_format.format;
@@ -1340,8 +1096,9 @@ bool APIConnection::try_send_media_player_info(APIConnection *api, void *v_media
1340
1096
  media_format.sample_bytes = supported_format.sample_bytes;
1341
1097
  msg.supported_formats.push_back(media_format);
1342
1098
  }
1343
-
1344
- return api->send_list_entities_media_player_response(msg);
1099
+ msg.unique_id = get_default_unique_id("media_player", media_player);
1100
+ return this->try_send_entity_info_(static_cast<EntityBase *>(media_player), msg,
1101
+ &APIConnection::send_list_entities_media_player_response);
1345
1102
  }
1346
1103
  void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
1347
1104
  media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
@@ -1376,22 +1133,14 @@ void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage>
1376
1133
  this->image_reader_.set_image(std::move(image));
1377
1134
  }
1378
1135
  void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
1379
- if (!APIConnection::try_send_camera_info(this, camera)) {
1380
- this->deferred_message_queue_.defer(camera, try_send_camera_info);
1381
- }
1136
+ this->send_info_(static_cast<EntityBase *>(camera),
1137
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_camera_info_));
1382
1138
  }
1383
- bool APIConnection::try_send_camera_info(APIConnection *api, void *v_camera) {
1384
- esp32_camera::ESP32Camera *camera = reinterpret_cast<esp32_camera::ESP32Camera *>(v_camera);
1139
+ bool APIConnection::try_send_camera_info_(esp32_camera::ESP32Camera *camera) {
1385
1140
  ListEntitiesCameraResponse msg;
1386
- msg.key = camera->get_object_id_hash();
1387
- msg.object_id = camera->get_object_id();
1388
- if (camera->has_own_name())
1389
- msg.name = camera->get_name();
1390
1141
  msg.unique_id = get_default_unique_id("camera", camera);
1391
- msg.disabled_by_default = camera->is_disabled_by_default();
1392
- msg.icon = camera->get_icon();
1393
- msg.entity_category = static_cast<enums::EntityCategory>(camera->get_entity_category());
1394
- return api->send_list_entities_camera_response(msg);
1142
+ return this->try_send_entity_info_(static_cast<EntityBase *>(camera), msg,
1143
+ &APIConnection::send_list_entities_camera_response);
1395
1144
  }
1396
1145
  void APIConnection::camera_image(const CameraImageRequest &msg) {
1397
1146
  if (esp32_camera::global_esp32_camera == nullptr)
@@ -1468,6 +1217,11 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
1468
1217
  resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit();
1469
1218
  return resp;
1470
1219
  }
1220
+
1221
+ void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) {
1222
+ bluetooth_proxy::global_bluetooth_proxy->bluetooth_scanner_set_mode(
1223
+ msg.mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
1224
+ }
1471
1225
  #endif
1472
1226
 
1473
1227
  #ifdef USE_VOICE_ASSISTANT
@@ -1575,43 +1329,28 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
1575
1329
 
1576
1330
  #ifdef USE_ALARM_CONTROL_PANEL
1577
1331
  bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1578
- if (!this->state_subscription_)
1579
- return false;
1580
-
1581
- if (!APIConnection::try_send_alarm_control_panel_state(this, a_alarm_control_panel)) {
1582
- this->deferred_message_queue_.defer(a_alarm_control_panel, try_send_alarm_control_panel_state);
1583
- }
1584
-
1585
- return true;
1332
+ return this->send_state_(static_cast<EntityBase *>(a_alarm_control_panel),
1333
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_state_));
1586
1334
  }
1587
1335
  void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1588
- if (!APIConnection::try_send_alarm_control_panel_info(this, a_alarm_control_panel)) {
1589
- this->deferred_message_queue_.defer(a_alarm_control_panel, try_send_alarm_control_panel_info);
1590
- }
1336
+ this->send_info_(static_cast<EntityBase *>(a_alarm_control_panel),
1337
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_info_));
1591
1338
  }
1592
- bool APIConnection::try_send_alarm_control_panel_state(APIConnection *api, void *v_a_alarm_control_panel) {
1593
- alarm_control_panel::AlarmControlPanel *a_alarm_control_panel =
1594
- reinterpret_cast<alarm_control_panel::AlarmControlPanel *>(v_a_alarm_control_panel);
1595
- AlarmControlPanelStateResponse resp{};
1596
- resp.key = a_alarm_control_panel->get_object_id_hash();
1339
+ bool APIConnection::try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1340
+ AlarmControlPanelStateResponse resp;
1597
1341
  resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
1598
- return api->send_alarm_control_panel_state_response(resp);
1342
+
1343
+ resp.key = a_alarm_control_panel->get_object_id_hash();
1344
+ return this->send_alarm_control_panel_state_response(resp);
1599
1345
  }
1600
- bool APIConnection::try_send_alarm_control_panel_info(APIConnection *api, void *v_a_alarm_control_panel) {
1601
- alarm_control_panel::AlarmControlPanel *a_alarm_control_panel =
1602
- reinterpret_cast<alarm_control_panel::AlarmControlPanel *>(v_a_alarm_control_panel);
1346
+ bool APIConnection::try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
1603
1347
  ListEntitiesAlarmControlPanelResponse msg;
1604
- msg.key = a_alarm_control_panel->get_object_id_hash();
1605
- msg.object_id = a_alarm_control_panel->get_object_id();
1606
- msg.name = a_alarm_control_panel->get_name();
1607
- msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
1608
- msg.icon = a_alarm_control_panel->get_icon();
1609
- msg.disabled_by_default = a_alarm_control_panel->is_disabled_by_default();
1610
- msg.entity_category = static_cast<enums::EntityCategory>(a_alarm_control_panel->get_entity_category());
1611
1348
  msg.supported_features = a_alarm_control_panel->get_supported_features();
1612
1349
  msg.requires_code = a_alarm_control_panel->get_requires_code();
1613
1350
  msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
1614
- return api->send_list_entities_alarm_control_panel_response(msg);
1351
+ msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
1352
+ return this->try_send_entity_info_(static_cast<EntityBase *>(a_alarm_control_panel), msg,
1353
+ &APIConnection::send_list_entities_alarm_control_panel_response);
1615
1354
  }
1616
1355
  void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
1617
1356
  alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key);
@@ -1649,63 +1388,45 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
1649
1388
 
1650
1389
  #ifdef USE_EVENT
1651
1390
  void APIConnection::send_event(event::Event *event, std::string event_type) {
1652
- if (!APIConnection::try_send_event(this, event, std::move(event_type))) {
1653
- this->deferred_message_queue_.defer(event, try_send_event);
1654
- }
1391
+ this->send_state_with_value_(event, &APIConnection::try_send_event_, &APIConnection::try_send_event_,
1392
+ std::move(event_type));
1655
1393
  }
1656
1394
  void APIConnection::send_event_info(event::Event *event) {
1657
- if (!APIConnection::try_send_event_info(this, event)) {
1658
- this->deferred_message_queue_.defer(event, try_send_event_info);
1659
- }
1395
+ this->send_info_(static_cast<EntityBase *>(event),
1396
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_event_info_));
1660
1397
  }
1661
- bool APIConnection::try_send_event(APIConnection *api, void *v_event) {
1662
- event::Event *event = reinterpret_cast<event::Event *>(v_event);
1663
- return APIConnection::try_send_event(api, event, *(event->last_event_type));
1398
+ bool APIConnection::try_send_event_(event::Event *event) {
1399
+ return this->try_send_event_(event, *(event->last_event_type));
1664
1400
  }
1665
- bool APIConnection::try_send_event(APIConnection *api, event::Event *event, std::string event_type) {
1666
- EventResponse resp{};
1667
- resp.key = event->get_object_id_hash();
1401
+ bool APIConnection::try_send_event_(event::Event *event, std::string event_type) {
1402
+ EventResponse resp;
1668
1403
  resp.event_type = std::move(event_type);
1669
- return api->send_event_response(resp);
1404
+
1405
+ resp.key = event->get_object_id_hash();
1406
+ return this->send_event_response(resp);
1670
1407
  }
1671
- bool APIConnection::try_send_event_info(APIConnection *api, void *v_event) {
1672
- event::Event *event = reinterpret_cast<event::Event *>(v_event);
1408
+ bool APIConnection::try_send_event_info_(event::Event *event) {
1673
1409
  ListEntitiesEventResponse msg;
1674
- msg.key = event->get_object_id_hash();
1675
- msg.object_id = event->get_object_id();
1676
- if (event->has_own_name())
1677
- msg.name = event->get_name();
1678
- msg.unique_id = get_default_unique_id("event", event);
1679
- msg.icon = event->get_icon();
1680
- msg.disabled_by_default = event->is_disabled_by_default();
1681
- msg.entity_category = static_cast<enums::EntityCategory>(event->get_entity_category());
1682
1410
  msg.device_class = event->get_device_class();
1683
1411
  for (const auto &event_type : event->get_event_types())
1684
1412
  msg.event_types.push_back(event_type);
1685
- return api->send_list_entities_event_response(msg);
1413
+ msg.unique_id = get_default_unique_id("event", event);
1414
+ return this->try_send_entity_info_(static_cast<EntityBase *>(event), msg,
1415
+ &APIConnection::send_list_entities_event_response);
1686
1416
  }
1687
1417
  #endif
1688
1418
 
1689
1419
  #ifdef USE_UPDATE
1690
1420
  bool APIConnection::send_update_state(update::UpdateEntity *update) {
1691
- if (!this->state_subscription_)
1692
- return false;
1693
-
1694
- if (!APIConnection::try_send_update_state(this, update)) {
1695
- this->deferred_message_queue_.defer(update, try_send_update_state);
1696
- }
1697
-
1698
- return true;
1421
+ return this->send_state_(static_cast<EntityBase *>(update),
1422
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_update_state_));
1699
1423
  }
1700
1424
  void APIConnection::send_update_info(update::UpdateEntity *update) {
1701
- if (!APIConnection::try_send_update_info(this, update)) {
1702
- this->deferred_message_queue_.defer(update, try_send_update_info);
1703
- }
1425
+ this->send_info_(static_cast<EntityBase *>(update),
1426
+ reinterpret_cast<send_message_t>(&APIConnection::try_send_update_info_));
1704
1427
  }
1705
- bool APIConnection::try_send_update_state(APIConnection *api, void *v_update) {
1706
- update::UpdateEntity *update = reinterpret_cast<update::UpdateEntity *>(v_update);
1707
- UpdateStateResponse resp{};
1708
- resp.key = update->get_object_id_hash();
1428
+ bool APIConnection::try_send_update_state_(update::UpdateEntity *update) {
1429
+ UpdateStateResponse resp;
1709
1430
  resp.missing_state = !update->has_state();
1710
1431
  if (update->has_state()) {
1711
1432
  resp.in_progress = update->state == update::UpdateState::UPDATE_STATE_INSTALLING;
@@ -1720,21 +1441,15 @@ bool APIConnection::try_send_update_state(APIConnection *api, void *v_update) {
1720
1441
  resp.release_url = update->update_info.release_url;
1721
1442
  }
1722
1443
 
1723
- return api->send_update_state_response(resp);
1444
+ resp.key = update->get_object_id_hash();
1445
+ return this->send_update_state_response(resp);
1724
1446
  }
1725
- bool APIConnection::try_send_update_info(APIConnection *api, void *v_update) {
1726
- update::UpdateEntity *update = reinterpret_cast<update::UpdateEntity *>(v_update);
1447
+ bool APIConnection::try_send_update_info_(update::UpdateEntity *update) {
1727
1448
  ListEntitiesUpdateResponse msg;
1728
- msg.key = update->get_object_id_hash();
1729
- msg.object_id = update->get_object_id();
1730
- if (update->has_own_name())
1731
- msg.name = update->get_name();
1732
- msg.unique_id = get_default_unique_id("update", update);
1733
- msg.icon = update->get_icon();
1734
- msg.disabled_by_default = update->is_disabled_by_default();
1735
- msg.entity_category = static_cast<enums::EntityCategory>(update->get_entity_category());
1736
1449
  msg.device_class = update->get_device_class();
1737
- return api->send_list_entities_update_response(msg);
1450
+ msg.unique_id = get_default_unique_id("update", update);
1451
+ return this->try_send_entity_info_(static_cast<EntityBase *>(update), msg,
1452
+ &APIConnection::send_list_entities_update_response);
1738
1453
  }
1739
1454
  void APIConnection::update_command(const UpdateCommandRequest &msg) {
1740
1455
  update::UpdateEntity *update = App.get_update_by_key(msg.key);
@@ -1762,12 +1477,25 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
1762
1477
  if (this->log_subscription_ < level)
1763
1478
  return false;
1764
1479
 
1765
- // Send raw so that we don't copy too much
1766
- auto buffer = this->create_buffer();
1767
- // LogLevel level = 1;
1768
- buffer.encode_uint32(1, static_cast<uint32_t>(level));
1769
- // string message = 3;
1770
- buffer.encode_string(3, line, strlen(line));
1480
+ // Pre-calculate message size to avoid reallocations
1481
+ const size_t line_length = strlen(line);
1482
+ uint32_t msg_size = 0;
1483
+
1484
+ // Add size for level field (field ID 1, varint type)
1485
+ // 1 byte for field tag + size of the level varint
1486
+ msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(level));
1487
+
1488
+ // Add size for string field (field ID 3, string type)
1489
+ // 1 byte for field tag + size of length varint + string length
1490
+ msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(line_length)) + line_length;
1491
+
1492
+ // Create a pre-sized buffer
1493
+ auto buffer = this->create_buffer(msg_size);
1494
+
1495
+ // Encode the message (SubscribeLogsResponse)
1496
+ buffer.encode_uint32(1, static_cast<uint32_t>(level)); // LogLevel level = 1
1497
+ buffer.encode_string(3, line, line_length); // string message = 3
1498
+
1771
1499
  // SubscribeLogsResponse - 29
1772
1500
  return this->send_buffer(buffer, 29);
1773
1501
  }
@@ -1848,6 +1576,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
1848
1576
  #ifdef USE_VOICE_ASSISTANT
1849
1577
  resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
1850
1578
  resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
1579
+ #endif
1580
+ #ifdef USE_API_NOISE
1581
+ resp.api_encryption_supported = true;
1851
1582
  #endif
1852
1583
  return resp;
1853
1584
  }
@@ -1869,32 +1600,55 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
1869
1600
  ESP_LOGV(TAG, "Could not find matching service!");
1870
1601
  }
1871
1602
  }
1603
+ #ifdef USE_API_NOISE
1604
+ NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) {
1605
+ psk_t psk{};
1606
+ NoiseEncryptionSetKeyResponse resp;
1607
+ if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
1608
+ ESP_LOGW(TAG, "Invalid encryption key length");
1609
+ resp.success = false;
1610
+ return resp;
1611
+ }
1612
+
1613
+ if (!this->parent_->save_noise_psk(psk, true)) {
1614
+ ESP_LOGW(TAG, "Failed to save encryption key");
1615
+ resp.success = false;
1616
+ return resp;
1617
+ }
1618
+
1619
+ resp.success = true;
1620
+ return resp;
1621
+ }
1622
+ #endif
1872
1623
  void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
1873
1624
  state_subs_at_ = 0;
1874
1625
  }
1875
- bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
1626
+ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
1876
1627
  if (this->remove_)
1877
1628
  return false;
1878
- if (!this->helper_->can_write_without_blocking()) {
1879
- delay(0);
1880
- APIError err = this->helper_->loop();
1881
- if (err != APIError::OK) {
1882
- on_fatal_error();
1883
- ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
1884
- api_error_to_str(err), errno);
1885
- return false;
1886
- }
1887
- if (!this->helper_->can_write_without_blocking()) {
1888
- // SubscribeLogsResponse
1889
- if (message_type != 29) {
1890
- ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
1891
- }
1892
- delay(0);
1893
- return false;
1894
- }
1629
+ if (this->helper_->can_write_without_blocking())
1630
+ return true;
1631
+ delay(0);
1632
+ APIError err = this->helper_->loop();
1633
+ if (err != APIError::OK) {
1634
+ on_fatal_error();
1635
+ ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
1636
+ api_error_to_str(err), errno);
1637
+ return false;
1638
+ }
1639
+ if (this->helper_->can_write_without_blocking())
1640
+ return true;
1641
+ if (log_out_of_space) {
1642
+ ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
1643
+ }
1644
+ return false;
1645
+ }
1646
+ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
1647
+ if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse
1648
+ return false;
1895
1649
  }
1896
1650
 
1897
- APIError err = this->helper_->write_packet(message_type, buffer.get_buffer()->data(), buffer.get_buffer()->size());
1651
+ APIError err = this->helper_->write_protobuf_packet(message_type, buffer);
1898
1652
  if (err == APIError::WOULD_BLOCK)
1899
1653
  return false;
1900
1654
  if (err != APIError::OK) {