esphome 2025.9.3__py3-none-any.whl → 2025.10.0b2__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 (351) hide show
  1. esphome/__main__.py +94 -31
  2. esphome/address_cache.py +142 -0
  3. esphome/automation.py +130 -32
  4. esphome/build_gen/platformio.py +1 -3
  5. esphome/codegen.py +1 -0
  6. esphome/components/animation/animation.cpp +2 -2
  7. esphome/components/api/__init__.py +166 -3
  8. esphome/components/api/api_connection.cpp +84 -41
  9. esphome/components/api/api_connection.h +22 -16
  10. esphome/components/api/api_frame_helper.cpp +33 -19
  11. esphome/components/api/api_frame_helper.h +19 -4
  12. esphome/components/api/api_frame_helper_noise.cpp +41 -53
  13. esphome/components/api/api_frame_helper_noise.h +1 -1
  14. esphome/components/api/api_frame_helper_plaintext.cpp +22 -31
  15. esphome/components/api/api_frame_helper_plaintext.h +1 -1
  16. esphome/components/api/api_pb2.cpp +189 -15
  17. esphome/components/api/api_pb2.h +132 -20
  18. esphome/components/api/api_pb2_dump.cpp +97 -9
  19. esphome/components/api/api_pb2_service.cpp +118 -160
  20. esphome/components/api/api_pb2_service.h +31 -3
  21. esphome/components/api/api_server.cpp +68 -10
  22. esphome/components/api/api_server.h +32 -4
  23. esphome/components/api/custom_api_device.h +8 -8
  24. esphome/components/api/homeassistant_service.h +123 -6
  25. esphome/components/api/proto.h +6 -2
  26. esphome/components/api/user_services.h +2 -2
  27. esphome/components/as7341/sensor.py +1 -1
  28. esphome/components/audio/__init__.py +1 -1
  29. esphome/components/audio/audio.cpp +1 -1
  30. esphome/components/audio/audio_decoder.cpp +9 -9
  31. esphome/components/bl0906/bl0906.cpp +2 -2
  32. esphome/components/bl0942/bl0942.cpp +2 -2
  33. esphome/components/ble_client/__init__.py +1 -1
  34. esphome/components/bluetooth_proxy/__init__.py +4 -30
  35. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +11 -4
  36. esphome/components/bluetooth_proxy/bluetooth_connection.h +2 -2
  37. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
  38. esphome/components/camera_encoder/__init__.py +2 -4
  39. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +4 -2
  40. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +3 -1
  41. esphome/components/canbus/canbus.cpp +7 -5
  42. esphome/components/canbus/canbus.h +7 -7
  43. esphome/components/captive_portal/__init__.py +18 -1
  44. esphome/components/captive_portal/captive_portal.cpp +40 -46
  45. esphome/components/captive_portal/captive_portal.h +20 -22
  46. esphome/components/captive_portal/dns_server_esp32_idf.cpp +205 -0
  47. esphome/components/captive_portal/dns_server_esp32_idf.h +27 -0
  48. esphome/components/ccs811/ccs811.cpp +1 -1
  49. esphome/components/climate/climate.cpp +10 -7
  50. esphome/components/cm1106/cm1106.cpp +1 -1
  51. esphome/components/copy/lock/copy_lock.cpp +1 -1
  52. esphome/components/cover/cover.cpp +1 -0
  53. esphome/components/daikin_arc/daikin_arc.cpp +19 -12
  54. esphome/components/dashboard_import/dashboard_import.cpp +1 -1
  55. esphome/components/dashboard_import/dashboard_import.h +1 -1
  56. esphome/components/deep_sleep/__init__.py +9 -2
  57. esphome/components/deep_sleep/deep_sleep_component.h +11 -9
  58. esphome/components/deep_sleep/deep_sleep_esp32.cpp +51 -27
  59. esphome/components/ektf2232/touchscreen/__init__.py +8 -5
  60. esphome/components/ektf2232/touchscreen/ektf2232.cpp +4 -4
  61. esphome/components/ektf2232/touchscreen/ektf2232.h +2 -2
  62. esphome/components/epaper_spi/__init__.py +1 -0
  63. esphome/components/epaper_spi/display.py +80 -0
  64. esphome/components/epaper_spi/epaper_spi.cpp +227 -0
  65. esphome/components/epaper_spi/epaper_spi.h +93 -0
  66. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp +42 -0
  67. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h +45 -0
  68. esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +135 -0
  69. esphome/components/epaper_spi/epaper_spi_spectra_e6.h +23 -0
  70. esphome/components/es7210/es7210.cpp +3 -3
  71. esphome/components/esp32/__init__.py +256 -340
  72. esphome/components/esp32/boards.py +81 -0
  73. esphome/components/esp32/preferences.cpp +23 -17
  74. esphome/components/esp32_ble/__init__.py +167 -44
  75. esphome/components/esp32_ble/ble.cpp +47 -3
  76. esphome/components/esp32_ble/ble.h +18 -0
  77. esphome/components/esp32_ble/ble_advertising.cpp +7 -3
  78. esphome/components/esp32_ble/ble_advertising.h +4 -0
  79. esphome/components/esp32_ble/ble_uuid.cpp +16 -42
  80. esphome/components/esp32_ble_beacon/__init__.py +3 -4
  81. esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +0 -4
  82. esphome/components/esp32_ble_client/ble_client_base.cpp +14 -12
  83. esphome/components/esp32_ble_server/__init__.py +28 -14
  84. esphome/components/esp32_ble_server/ble_characteristic.cpp +67 -57
  85. esphome/components/esp32_ble_server/ble_characteristic.h +27 -16
  86. esphome/components/esp32_ble_server/ble_descriptor.cpp +4 -3
  87. esphome/components/esp32_ble_server/ble_descriptor.h +13 -9
  88. esphome/components/esp32_ble_server/ble_server.cpp +59 -24
  89. esphome/components/esp32_ble_server/ble_server.h +38 -20
  90. esphome/components/esp32_ble_server/ble_server_automations.cpp +49 -33
  91. esphome/components/esp32_ble_server/ble_server_automations.h +39 -24
  92. esphome/components/esp32_ble_tracker/__init__.py +25 -80
  93. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -8
  94. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +0 -3
  95. esphome/components/esp32_camera/__init__.py +1 -3
  96. esphome/components/esp32_can/esp32_can.cpp +22 -4
  97. esphome/components/esp32_can/esp32_can.h +3 -0
  98. esphome/components/esp32_hosted/__init__.py +2 -1
  99. esphome/components/esp32_improv/esp32_improv_component.cpp +135 -65
  100. esphome/components/esp32_improv/esp32_improv_component.h +7 -1
  101. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  102. esphome/components/esp8266/__init__.py +3 -3
  103. esphome/components/esphome/ota/__init__.py +21 -2
  104. esphome/components/esphome/ota/ota_esphome.cpp +456 -146
  105. esphome/components/esphome/ota/ota_esphome.h +49 -2
  106. esphome/components/ethernet/__init__.py +39 -22
  107. esphome/components/ethernet/ethernet_component.cpp +28 -5
  108. esphome/components/ethernet/ethernet_component.h +5 -1
  109. esphome/components/external_components/__init__.py +8 -6
  110. esphome/components/fingerprint_grow/fingerprint_grow.cpp +1 -1
  111. esphome/components/fingerprint_grow/fingerprint_grow.h +2 -1
  112. esphome/components/font/__init__.py +5 -5
  113. esphome/components/graph/graph.cpp +1 -1
  114. esphome/components/graphical_display_menu/graphical_display_menu.cpp +3 -2
  115. esphome/components/haier/hon_climate.cpp +2 -2
  116. esphome/components/haier/hon_climate.h +1 -1
  117. esphome/components/hdc1080/hdc1080.cpp +42 -34
  118. esphome/components/hdc1080/hdc1080.h +1 -3
  119. esphome/components/homeassistant/number/homeassistant_number.cpp +2 -2
  120. esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
  121. esphome/components/http_request/__init__.py +3 -3
  122. esphome/components/htu21d/htu21d.cpp +13 -18
  123. esphome/components/htu21d/htu21d.h +1 -1
  124. esphome/components/i2s_audio/__init__.py +1 -2
  125. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  126. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -2
  127. esphome/components/improv_serial/improv_serial_component.cpp +12 -15
  128. esphome/components/improv_serial/improv_serial_component.h +6 -8
  129. esphome/components/json/json_util.cpp +42 -44
  130. esphome/components/json/json_util.h +57 -0
  131. esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +2 -2
  132. esphome/components/key_collector/key_collector.h +4 -4
  133. esphome/components/libretiny/__init__.py +6 -6
  134. esphome/components/libretiny/preferences.cpp +23 -16
  135. esphome/components/light/light_call.cpp +98 -120
  136. esphome/components/light/light_call.h +17 -7
  137. esphome/components/lm75b/__init__.py +0 -0
  138. esphome/components/lm75b/lm75b.cpp +39 -0
  139. esphome/components/lm75b/lm75b.h +19 -0
  140. esphome/components/lm75b/sensor.py +34 -0
  141. esphome/components/lock/lock.h +12 -6
  142. esphome/components/logger/__init__.py +15 -27
  143. esphome/components/logger/logger.cpp +10 -20
  144. esphome/components/logger/logger.h +105 -62
  145. esphome/components/logger/logger_esp32.cpp +0 -48
  146. esphome/components/logger/logger_zephyr.cpp +2 -3
  147. esphome/components/logger/select/logger_level_select.cpp +6 -7
  148. esphome/components/logger/select/logger_level_select.h +7 -0
  149. esphome/components/ltr501/ltr501.cpp +7 -6
  150. esphome/components/ltr_als_ps/ltr_als_ps.cpp +7 -6
  151. esphome/components/matrix_keypad/matrix_keypad.h +4 -4
  152. esphome/components/max7219digit/max7219digit.cpp +1 -1
  153. esphome/components/mcp23xxx_base/mcp23xxx_base.h +3 -3
  154. esphome/components/mcp2515/mcp2515.cpp +31 -3
  155. esphome/components/mcp2515/mcp2515_defs.h +3 -1
  156. esphome/components/md5/md5.cpp +0 -26
  157. esphome/components/md5/md5.h +10 -20
  158. esphome/components/mdns/__init__.py +93 -19
  159. esphome/components/mdns/mdns_component.cpp +57 -94
  160. esphome/components/mdns/mdns_component.h +35 -11
  161. esphome/components/mdns/mdns_esp32.cpp +7 -13
  162. esphome/components/mdns/mdns_esp8266.cpp +7 -7
  163. esphome/components/mdns/mdns_libretiny.cpp +3 -4
  164. esphome/components/mdns/mdns_rp2040.cpp +3 -4
  165. esphome/components/mipi/__init__.py +1 -5
  166. esphome/components/mipi_spi/display.py +24 -8
  167. esphome/components/mipi_spi/mipi_spi.h +3 -3
  168. esphome/components/mixer/speaker/mixer_speaker.cpp +3 -3
  169. esphome/components/mmc5603/mmc5603.cpp +3 -3
  170. esphome/components/modbus/modbus.cpp +27 -13
  171. esphome/components/modbus/modbus.h +5 -3
  172. esphome/components/modbus/modbus_definitions.h +86 -0
  173. esphome/components/modbus_controller/__init__.py +29 -1
  174. esphome/components/modbus_controller/const.py +4 -0
  175. esphome/components/modbus_controller/modbus_controller.cpp +38 -13
  176. esphome/components/modbus_controller/modbus_controller.h +18 -29
  177. esphome/components/mpr121/mpr121.cpp +41 -42
  178. esphome/components/mpr121/mpr121.h +0 -1
  179. esphome/components/nau7802/nau7802.cpp +2 -2
  180. esphome/components/network/__init__.py +7 -3
  181. esphome/components/nextion/display.py +4 -4
  182. esphome/components/nextion/nextion.cpp +8 -8
  183. esphome/components/number/__init__.py +2 -0
  184. esphome/components/number/number_call.cpp +23 -12
  185. esphome/components/number/number_call.h +5 -0
  186. esphome/components/online_image/bmp_image.cpp +2 -1
  187. esphome/components/online_image/jpeg_image.cpp +4 -2
  188. esphome/components/opentherm/opentherm.cpp +5 -5
  189. esphome/components/opentherm/opentherm.h +3 -3
  190. esphome/components/openthread/openthread.cpp +11 -10
  191. esphome/components/openthread/openthread.h +0 -1
  192. esphome/components/ota/ota_backend.h +1 -0
  193. esphome/components/packages/__init__.py +10 -8
  194. esphome/components/packet_transport/packet_transport.cpp +2 -0
  195. esphome/components/pid/pid_controller.cpp +1 -1
  196. esphome/components/prometheus/prometheus_handler.cpp +239 -239
  197. esphome/components/psram/__init__.py +30 -28
  198. esphome/components/qmc5883l/qmc5883l.cpp +15 -0
  199. esphome/components/qmc5883l/qmc5883l.h +3 -0
  200. esphome/components/qmc5883l/sensor.py +31 -12
  201. esphome/components/remote_base/gobox_protocol.cpp +3 -3
  202. esphome/components/remote_receiver/__init__.py +14 -2
  203. esphome/components/remote_receiver/{remote_receiver_esp8266.cpp → remote_receiver.cpp} +2 -2
  204. esphome/components/remote_receiver/remote_receiver.h +4 -0
  205. esphome/components/remote_receiver/remote_receiver_esp32.cpp +18 -1
  206. esphome/components/remote_transmitter/__init__.py +2 -2
  207. esphome/components/remote_transmitter/remote_transmitter.cpp +103 -0
  208. esphome/components/rp2040/__init__.py +11 -11
  209. esphome/components/rtttl/rtttl.cpp +2 -2
  210. esphome/components/scd30/sensor.py +1 -1
  211. esphome/components/script/__init__.py +1 -1
  212. esphome/components/script/script.h +7 -7
  213. esphome/components/select/select.cpp +5 -4
  214. esphome/components/select/select_call.cpp +1 -1
  215. esphome/components/sensirion_common/i2c_sensirion.cpp +2 -1
  216. esphome/components/sensor/__init__.py +2 -0
  217. esphome/components/sha256/__init__.py +22 -0
  218. esphome/components/sha256/sha256.cpp +116 -0
  219. esphome/components/sha256/sha256.h +60 -0
  220. esphome/components/socket/lwip_raw_tcp_impl.cpp +34 -6
  221. esphome/components/sonoff_d1/sonoff_d1.cpp +1 -1
  222. esphome/components/spi/__init__.py +0 -3
  223. esphome/components/split_buffer/__init__.py +5 -0
  224. esphome/components/split_buffer/split_buffer.cpp +133 -0
  225. esphome/components/split_buffer/split_buffer.h +40 -0
  226. esphome/components/sps30/sps30.cpp +14 -10
  227. esphome/components/sps30/sps30.h +2 -0
  228. esphome/components/st7567_i2c/st7567_i2c.cpp +3 -1
  229. esphome/components/st7789v/st7789v.cpp +3 -2
  230. esphome/components/statsd/statsd.cpp +1 -1
  231. esphome/components/substitutions/__init__.py +3 -1
  232. esphome/components/substitutions/jinja.py +13 -3
  233. esphome/components/sx126x/__init__.py +16 -0
  234. esphome/components/sx126x/sx126x.cpp +15 -1
  235. esphome/components/sx126x/sx126x.h +9 -1
  236. esphome/components/sx126x/sx126x_reg.h +2 -0
  237. esphome/components/text_sensor/text_sensor.cpp +16 -0
  238. esphome/components/text_sensor/text_sensor.h +3 -10
  239. esphome/components/tormatic/tormatic_cover.cpp +1 -1
  240. esphome/components/tuya/select/tuya_select.cpp +1 -1
  241. esphome/components/tuya/tuya.cpp +29 -4
  242. esphome/components/uart/__init__.py +37 -27
  243. esphome/components/uart/uart.h +6 -0
  244. esphome/components/uart/uart_component.cpp +8 -0
  245. esphome/components/uart/uart_component.h +28 -0
  246. esphome/components/uart/uart_component_esp_idf.cpp +64 -10
  247. esphome/components/uart/uart_component_esp_idf.h +5 -2
  248. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +1 -1
  249. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +1 -1
  250. esphome/components/uponor_smatrix/uponor_smatrix.cpp +3 -3
  251. esphome/components/usb_host/__init__.py +12 -2
  252. esphome/components/usb_host/usb_host.h +89 -14
  253. esphome/components/usb_host/usb_host_client.cpp +157 -22
  254. esphome/components/usb_host/usb_host_component.cpp +1 -1
  255. esphome/components/usb_uart/__init__.py +0 -1
  256. esphome/components/usb_uart/ch34x.cpp +4 -4
  257. esphome/components/usb_uart/cp210x.cpp +3 -3
  258. esphome/components/usb_uart/usb_uart.cpp +88 -32
  259. esphome/components/usb_uart/usb_uart.h +30 -6
  260. esphome/components/valve/valve.cpp +1 -0
  261. esphome/components/veml7700/veml7700.cpp +7 -6
  262. esphome/components/version/version_text_sensor.cpp +2 -1
  263. esphome/components/voice_assistant/voice_assistant.cpp +3 -2
  264. esphome/components/waveshare_epaper/waveshare_epaper.cpp +4 -4
  265. esphome/components/web_server/list_entities.cpp +3 -4
  266. esphome/components/web_server/list_entities.h +8 -10
  267. esphome/components/web_server/ota/__init__.py +1 -1
  268. esphome/components/web_server/ota/ota_web_server.cpp +9 -3
  269. esphome/components/web_server/web_server.cpp +509 -404
  270. esphome/components/web_server/web_server.h +5 -6
  271. esphome/components/web_server/web_server_v1.cpp +21 -19
  272. esphome/components/web_server_base/__init__.py +5 -2
  273. esphome/components/web_server_base/web_server_base.h +27 -7
  274. esphome/components/web_server_idf/__init__.py +1 -1
  275. esphome/components/web_server_idf/multipart.cpp +2 -2
  276. esphome/components/web_server_idf/multipart.h +2 -2
  277. esphome/components/web_server_idf/utils.cpp +2 -2
  278. esphome/components/web_server_idf/utils.h +2 -2
  279. esphome/components/web_server_idf/web_server_idf.cpp +118 -26
  280. esphome/components/web_server_idf/web_server_idf.h +12 -10
  281. esphome/components/wifi/__init__.py +13 -11
  282. esphome/components/wifi/wifi_component.cpp +74 -56
  283. esphome/components/wifi/wifi_component.h +4 -4
  284. esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
  285. esphome/components/wifi/wifi_component_esp_idf.cpp +24 -4
  286. esphome/components/wireguard/__init__.py +1 -1
  287. esphome/components/wts01/__init__.py +0 -0
  288. esphome/components/wts01/sensor.py +41 -0
  289. esphome/components/wts01/wts01.cpp +91 -0
  290. esphome/components/wts01/wts01.h +27 -0
  291. esphome/components/zephyr/__init__.py +5 -5
  292. esphome/components/zwave_proxy/__init__.py +43 -0
  293. esphome/components/zwave_proxy/zwave_proxy.cpp +346 -0
  294. esphome/components/zwave_proxy/zwave_proxy.h +93 -0
  295. esphome/config.py +79 -24
  296. esphome/config_validation.py +13 -15
  297. esphome/const.py +9 -2
  298. esphome/core/__init__.py +33 -22
  299. esphome/core/component.cpp +28 -18
  300. esphome/core/component_iterator.h +2 -1
  301. esphome/core/config.py +15 -15
  302. esphome/core/defines.h +21 -0
  303. esphome/core/entity_helpers.py +9 -6
  304. esphome/core/hash_base.h +56 -0
  305. esphome/core/helpers.cpp +19 -3
  306. esphome/core/helpers.h +26 -0
  307. esphome/core/scheduler.cpp +5 -21
  308. esphome/core/scheduler.h +19 -8
  309. esphome/core/string_ref.h +1 -1
  310. esphome/core/time.cpp +5 -5
  311. esphome/cpp_generator.py +4 -29
  312. esphome/dashboard/const.py +21 -4
  313. esphome/dashboard/core.py +10 -8
  314. esphome/dashboard/dns.py +15 -0
  315. esphome/dashboard/entries.py +15 -21
  316. esphome/dashboard/models.py +76 -0
  317. esphome/dashboard/settings.py +7 -7
  318. esphome/dashboard/status/mdns.py +46 -2
  319. esphome/dashboard/web_server.py +367 -93
  320. esphome/espota2.py +112 -32
  321. esphome/external_files.py +6 -7
  322. esphome/git.py +8 -0
  323. esphome/helpers.py +124 -77
  324. esphome/loader.py +8 -9
  325. esphome/pins.py +2 -2
  326. esphome/platformio_api.py +56 -18
  327. esphome/storage_json.py +26 -21
  328. esphome/types.py +30 -2
  329. esphome/util.py +32 -16
  330. esphome/vscode.py +8 -8
  331. esphome/wizard.py +10 -10
  332. esphome/writer.py +50 -15
  333. esphome/yaml_util.py +37 -31
  334. esphome/zeroconf.py +12 -3
  335. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/METADATA +12 -12
  336. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/RECORD +340 -320
  337. esphome/components/event_emitter/__init__.py +0 -5
  338. esphome/components/event_emitter/event_emitter.cpp +0 -14
  339. esphome/components/event_emitter/event_emitter.h +0 -63
  340. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -125
  341. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +0 -107
  342. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +0 -110
  343. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -214
  344. esphome/components/uart/uart_component_esp32_arduino.h +0 -60
  345. esphome/components/wifi/wifi_component_esp32_arduino.cpp +0 -860
  346. esphome/core/string_ref.cpp +0 -12
  347. esphome/dashboard/util/file.py +0 -63
  348. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/WHEEL +0 -0
  349. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/entry_points.txt +0 -0
  350. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/licenses/LICENSE +0 -0
  351. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@
8
8
  #include "esphome/core/log.h"
9
9
  #include "esphome/core/util.h"
10
10
 
11
- #ifdef USE_ARDUINO
11
+ #if !defined(USE_ESP32) && defined(USE_ARDUINO)
12
12
  #include "StreamString.h"
13
13
  #endif
14
14
 
@@ -103,19 +103,19 @@ static UrlMatch match_url(const char *url_ptr, size_t url_len, bool only_domain)
103
103
  return match;
104
104
  }
105
105
 
106
- #ifdef USE_ARDUINO
106
+ #if !defined(USE_ESP32) && defined(USE_ARDUINO)
107
107
  // helper for allowing only unique entries in the queue
108
108
  void DeferredUpdateEventSource::deq_push_back_with_dedup_(void *source, message_generator_t *message_generator) {
109
109
  DeferredEvent item(source, message_generator);
110
110
 
111
- auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(),
112
- [&item](const DeferredEvent &test) -> bool { return test == item; });
113
-
114
- if (iter != this->deferred_queue_.end()) {
115
- (*iter) = item;
116
- } else {
117
- this->deferred_queue_.push_back(item);
111
+ // Use range-based for loop instead of std::find_if to reduce template instantiation overhead and binary size
112
+ for (auto &event : this->deferred_queue_) {
113
+ if (event == item) {
114
+ event = item;
115
+ return;
116
+ }
118
117
  }
118
+ this->deferred_queue_.push_back(item);
119
119
  }
120
120
 
121
121
  void DeferredUpdateEventSource::process_deferred_queue_() {
@@ -127,6 +127,10 @@ void DeferredUpdateEventSource::process_deferred_queue_() {
127
127
  deferred_queue_.erase(deferred_queue_.begin());
128
128
  this->consecutive_send_failures_ = 0; // Reset failure count on successful send
129
129
  } else {
130
+ // NOTE: Similar logic exists in web_server_idf/web_server_idf.cpp in AsyncEventSourceResponse::process_buffer_()
131
+ // The implementations differ due to platform-specific APIs (DISCARDED vs HTTPD_SOCK_ERR_TIMEOUT, close() vs
132
+ // fd_.store(0)), but the failure counting and timeout logic should be kept in sync. If you change this logic,
133
+ // also update the ESP-IDF implementation.
130
134
  this->consecutive_send_failures_++;
131
135
  if (this->consecutive_send_failures_ >= MAX_CONSECUTIVE_SEND_FAILURES) {
132
136
  // Too many failures, connection is likely dead
@@ -228,10 +232,11 @@ void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUp
228
232
 
229
233
  #ifdef USE_WEBSERVER_SORTING
230
234
  for (auto &group : ws->sorting_groups_) {
231
- message = json::build_json([group](JsonObject root) {
232
- root["name"] = group.second.name;
233
- root["sorting_weight"] = group.second.weight;
234
- });
235
+ json::JsonBuilder builder;
236
+ JsonObject root = builder.root();
237
+ root["name"] = group.second.name;
238
+ root["sorting_weight"] = group.second.weight;
239
+ message = builder.serialize();
235
240
 
236
241
  // up to 31 groups should be able to be queued initially without defer
237
242
  source->try_send_nodefer(message.c_str(), "sorting_group");
@@ -265,17 +270,20 @@ void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_
265
270
  #endif
266
271
 
267
272
  std::string WebServer::get_config_json() {
268
- return json::build_json([this](JsonObject root) {
269
- root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
270
- root["comment"] = App.get_comment();
273
+ json::JsonBuilder builder;
274
+ JsonObject root = builder.root();
275
+
276
+ root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
277
+ root["comment"] = App.get_comment();
271
278
  #if defined(USE_WEBSERVER_OTA_DISABLED) || !defined(USE_WEBSERVER_OTA)
272
- root["ota"] = false; // Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal
279
+ root["ota"] = false; // Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal
273
280
  #else
274
- root["ota"] = true;
281
+ root["ota"] = true;
275
282
  #endif
276
- root["log"] = this->expose_log_;
277
- root["lang"] = "en";
278
- });
283
+ root["log"] = this->expose_log_;
284
+ root["lang"] = "en";
285
+
286
+ return builder.serialize();
279
287
  }
280
288
 
281
289
  void WebServer::setup() {
@@ -293,7 +301,7 @@ void WebServer::setup() {
293
301
  }
294
302
  #endif
295
303
 
296
- #ifdef USE_ESP_IDF
304
+ #ifdef USE_ESP32
297
305
  this->base_->add_handler(&this->events_);
298
306
  #endif
299
307
  this->base_->add_handler(this);
@@ -377,11 +385,14 @@ void WebServer::handle_js_request(AsyncWebServerRequest *request) {
377
385
  #endif
378
386
 
379
387
  // Helper functions to reduce code size by avoiding macro expansion
380
- static void set_json_id(JsonObject &root, EntityBase *obj, const std::string &id, JsonDetail start_config) {
381
- root["id"] = id;
388
+ static void set_json_id(JsonObject &root, EntityBase *obj, const char *prefix, JsonDetail start_config) {
389
+ char id_buf[160]; // object_id can be up to 128 chars + prefix + dash + null
390
+ const auto &object_id = obj->get_object_id();
391
+ snprintf(id_buf, sizeof(id_buf), "%s-%s", prefix, object_id.c_str());
392
+ root["id"] = id_buf;
382
393
  if (start_config == DETAIL_ALL) {
383
394
  root["name"] = obj->get_name();
384
- root["icon"] = obj->get_icon();
395
+ root["icon"] = obj->get_icon_ref();
385
396
  root["entity_category"] = obj->get_entity_category();
386
397
  bool is_disabled = obj->is_disabled_by_default();
387
398
  if (is_disabled)
@@ -389,17 +400,19 @@ static void set_json_id(JsonObject &root, EntityBase *obj, const std::string &id
389
400
  }
390
401
  }
391
402
 
403
+ // Keep as separate function even though only used once: reduces code size by ~48 bytes
404
+ // by allowing compiler to share code between template instantiations (bool, float, etc.)
392
405
  template<typename T>
393
- static void set_json_value(JsonObject &root, EntityBase *obj, const std::string &id, const T &value,
406
+ static void set_json_value(JsonObject &root, EntityBase *obj, const char *prefix, const T &value,
394
407
  JsonDetail start_config) {
395
- set_json_id(root, obj, id, start_config);
408
+ set_json_id(root, obj, prefix, start_config);
396
409
  root["value"] = value;
397
410
  }
398
411
 
399
412
  template<typename T>
400
- static void set_json_icon_state_value(JsonObject &root, EntityBase *obj, const std::string &id,
401
- const std::string &state, const T &value, JsonDetail start_config) {
402
- set_json_value(root, obj, id, value, start_config);
413
+ static void set_json_icon_state_value(JsonObject &root, EntityBase *obj, const char *prefix, const std::string &state,
414
+ const T &value, JsonDetail start_config) {
415
+ set_json_value(root, obj, prefix, value, start_config);
403
416
  root["state"] = state;
404
417
  }
405
418
 
@@ -435,22 +448,26 @@ std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *so
435
448
  return web_server->sensor_json((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_ALL);
436
449
  }
437
450
  std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
438
- return json::build_json([this, obj, value, start_config](JsonObject root) {
439
- std::string state;
440
- if (std::isnan(value)) {
441
- state = "NA";
442
- } else {
443
- state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
444
- if (!obj->get_unit_of_measurement().empty())
445
- state += " " + obj->get_unit_of_measurement();
446
- }
447
- set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
448
- if (start_config == DETAIL_ALL) {
449
- this->add_sorting_info_(root, obj);
450
- if (!obj->get_unit_of_measurement().empty())
451
- root["uom"] = obj->get_unit_of_measurement();
452
- }
453
- });
451
+ json::JsonBuilder builder;
452
+ JsonObject root = builder.root();
453
+
454
+ const auto uom_ref = obj->get_unit_of_measurement_ref();
455
+
456
+ // Build JSON directly inline
457
+ std::string state;
458
+ if (std::isnan(value)) {
459
+ state = "NA";
460
+ } else {
461
+ state = value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref);
462
+ }
463
+ set_json_icon_state_value(root, obj, "sensor", state, value, start_config);
464
+ if (start_config == DETAIL_ALL) {
465
+ this->add_sorting_info_(root, obj);
466
+ if (!uom_ref.empty())
467
+ root["uom"] = uom_ref;
468
+ }
469
+
470
+ return builder.serialize();
454
471
  }
455
472
  #endif
456
473
 
@@ -483,12 +500,15 @@ std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, voi
483
500
  }
484
501
  std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std::string &value,
485
502
  JsonDetail start_config) {
486
- return json::build_json([this, obj, value, start_config](JsonObject root) {
487
- set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
488
- if (start_config == DETAIL_ALL) {
489
- this->add_sorting_info_(root, obj);
490
- }
491
- });
503
+ json::JsonBuilder builder;
504
+ JsonObject root = builder.root();
505
+
506
+ set_json_icon_state_value(root, obj, "text_sensor", value, value, start_config);
507
+ if (start_config == DETAIL_ALL) {
508
+ this->add_sorting_info_(root, obj);
509
+ }
510
+
511
+ return builder.serialize();
492
512
  }
493
513
  #endif
494
514
 
@@ -553,13 +573,16 @@ std::string WebServer::switch_all_json_generator(WebServer *web_server, void *so
553
573
  return web_server->switch_json((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_ALL);
554
574
  }
555
575
  std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail start_config) {
556
- return json::build_json([this, obj, value, start_config](JsonObject root) {
557
- set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
558
- if (start_config == DETAIL_ALL) {
559
- root["assumed_state"] = obj->assumed_state();
560
- this->add_sorting_info_(root, obj);
561
- }
562
- });
576
+ json::JsonBuilder builder;
577
+ JsonObject root = builder.root();
578
+
579
+ set_json_icon_state_value(root, obj, "switch", value ? "ON" : "OFF", value, start_config);
580
+ if (start_config == DETAIL_ALL) {
581
+ root["assumed_state"] = obj->assumed_state();
582
+ this->add_sorting_info_(root, obj);
583
+ }
584
+
585
+ return builder.serialize();
563
586
  }
564
587
  #endif
565
588
 
@@ -590,12 +613,15 @@ std::string WebServer::button_all_json_generator(WebServer *web_server, void *so
590
613
  return web_server->button_json((button::Button *) (source), DETAIL_ALL);
591
614
  }
592
615
  std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) {
593
- return json::build_json([this, obj, start_config](JsonObject root) {
594
- set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
595
- if (start_config == DETAIL_ALL) {
596
- this->add_sorting_info_(root, obj);
597
- }
598
- });
616
+ json::JsonBuilder builder;
617
+ JsonObject root = builder.root();
618
+
619
+ set_json_id(root, obj, "button", start_config);
620
+ if (start_config == DETAIL_ALL) {
621
+ this->add_sorting_info_(root, obj);
622
+ }
623
+
624
+ return builder.serialize();
599
625
  }
600
626
  #endif
601
627
 
@@ -627,13 +653,15 @@ std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, v
627
653
  ((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL);
628
654
  }
629
655
  std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
630
- return json::build_json([this, obj, value, start_config](JsonObject root) {
631
- set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
632
- start_config);
633
- if (start_config == DETAIL_ALL) {
634
- this->add_sorting_info_(root, obj);
635
- }
636
- });
656
+ json::JsonBuilder builder;
657
+ JsonObject root = builder.root();
658
+
659
+ set_json_icon_state_value(root, obj, "binary_sensor", value ? "ON" : "OFF", value, start_config);
660
+ if (start_config == DETAIL_ALL) {
661
+ this->add_sorting_info_(root, obj);
662
+ }
663
+
664
+ return builder.serialize();
637
665
  }
638
666
  #endif
639
667
 
@@ -694,20 +722,22 @@ std::string WebServer::fan_all_json_generator(WebServer *web_server, void *sourc
694
722
  return web_server->fan_json((fan::Fan *) (source), DETAIL_ALL);
695
723
  }
696
724
  std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
697
- return json::build_json([this, obj, start_config](JsonObject root) {
698
- set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
699
- start_config);
700
- const auto traits = obj->get_traits();
701
- if (traits.supports_speed()) {
702
- root["speed_level"] = obj->speed;
703
- root["speed_count"] = traits.supported_speed_count();
704
- }
705
- if (obj->get_traits().supports_oscillation())
706
- root["oscillation"] = obj->oscillating;
707
- if (start_config == DETAIL_ALL) {
708
- this->add_sorting_info_(root, obj);
709
- }
710
- });
725
+ json::JsonBuilder builder;
726
+ JsonObject root = builder.root();
727
+
728
+ set_json_icon_state_value(root, obj, "fan", obj->state ? "ON" : "OFF", obj->state, start_config);
729
+ const auto traits = obj->get_traits();
730
+ if (traits.supports_speed()) {
731
+ root["speed_level"] = obj->speed;
732
+ root["speed_count"] = traits.supported_speed_count();
733
+ }
734
+ if (obj->get_traits().supports_oscillation())
735
+ root["oscillation"] = obj->oscillating;
736
+ if (start_config == DETAIL_ALL) {
737
+ this->add_sorting_info_(root, obj);
738
+ }
739
+
740
+ return builder.serialize();
711
741
  }
712
742
  #endif
713
743
 
@@ -767,20 +797,23 @@ std::string WebServer::light_all_json_generator(WebServer *web_server, void *sou
767
797
  return web_server->light_json((light::LightState *) (source), DETAIL_ALL);
768
798
  }
769
799
  std::string WebServer::light_json(light::LightState *obj, JsonDetail start_config) {
770
- return json::build_json([this, obj, start_config](JsonObject root) {
771
- set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
772
- root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
773
-
774
- light::LightJSONSchema::dump_json(*obj, root);
775
- if (start_config == DETAIL_ALL) {
776
- JsonArray opt = root["effects"].to<JsonArray>();
777
- opt.add("None");
778
- for (auto const &option : obj->get_effects()) {
779
- opt.add(option->get_name());
780
- }
781
- this->add_sorting_info_(root, obj);
800
+ json::JsonBuilder builder;
801
+ JsonObject root = builder.root();
802
+
803
+ set_json_id(root, obj, "light", start_config);
804
+ root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
805
+
806
+ light::LightJSONSchema::dump_json(*obj, root);
807
+ if (start_config == DETAIL_ALL) {
808
+ JsonArray opt = root["effects"].to<JsonArray>();
809
+ opt.add("None");
810
+ for (auto const &option : obj->get_effects()) {
811
+ opt.add(option->get_name());
782
812
  }
783
- });
813
+ this->add_sorting_info_(root, obj);
814
+ }
815
+
816
+ return builder.serialize();
784
817
  }
785
818
  #endif
786
819
 
@@ -803,15 +836,28 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
803
836
  }
804
837
 
805
838
  auto call = obj->make_call();
806
- if (match.method_equals("open")) {
807
- call.set_command_open();
808
- } else if (match.method_equals("close")) {
809
- call.set_command_close();
810
- } else if (match.method_equals("stop")) {
811
- call.set_command_stop();
812
- } else if (match.method_equals("toggle")) {
813
- call.set_command_toggle();
814
- } else if (!match.method_equals("set")) {
839
+
840
+ // Lookup table for cover methods
841
+ static const struct {
842
+ const char *name;
843
+ cover::CoverCall &(cover::CoverCall::*action)();
844
+ } METHODS[] = {
845
+ {"open", &cover::CoverCall::set_command_open},
846
+ {"close", &cover::CoverCall::set_command_close},
847
+ {"stop", &cover::CoverCall::set_command_stop},
848
+ {"toggle", &cover::CoverCall::set_command_toggle},
849
+ };
850
+
851
+ bool found = false;
852
+ for (const auto &method : METHODS) {
853
+ if (match.method_equals(method.name)) {
854
+ (call.*method.action)();
855
+ found = true;
856
+ break;
857
+ }
858
+ }
859
+
860
+ if (!found && !match.method_equals("set")) {
815
861
  request->send(404);
816
862
  return;
817
863
  }
@@ -839,19 +885,22 @@ std::string WebServer::cover_all_json_generator(WebServer *web_server, void *sou
839
885
  return web_server->cover_json((cover::Cover *) (source), DETAIL_ALL);
840
886
  }
841
887
  std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
842
- return json::build_json([this, obj, start_config](JsonObject root) {
843
- set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
844
- obj->position, start_config);
845
- root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
846
-
847
- if (obj->get_traits().get_supports_position())
848
- root["position"] = obj->position;
849
- if (obj->get_traits().get_supports_tilt())
850
- root["tilt"] = obj->tilt;
851
- if (start_config == DETAIL_ALL) {
852
- this->add_sorting_info_(root, obj);
853
- }
854
- });
888
+ json::JsonBuilder builder;
889
+ JsonObject root = builder.root();
890
+
891
+ set_json_icon_state_value(root, obj, "cover", obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position,
892
+ start_config);
893
+ root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
894
+
895
+ if (obj->get_traits().get_supports_position())
896
+ root["position"] = obj->position;
897
+ if (obj->get_traits().get_supports_tilt())
898
+ root["tilt"] = obj->tilt;
899
+ if (start_config == DETAIL_ALL) {
900
+ this->add_sorting_info_(root, obj);
901
+ }
902
+
903
+ return builder.serialize();
855
904
  }
856
905
  #endif
857
906
 
@@ -894,31 +943,33 @@ std::string WebServer::number_all_json_generator(WebServer *web_server, void *so
894
943
  return web_server->number_json((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_ALL);
895
944
  }
896
945
  std::string WebServer::number_json(number::Number *obj, float value, JsonDetail start_config) {
897
- return json::build_json([this, obj, value, start_config](JsonObject root) {
898
- set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
899
- if (start_config == DETAIL_ALL) {
900
- root["min_value"] =
901
- value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step()));
902
- root["max_value"] =
903
- value_accuracy_to_string(obj->traits.get_max_value(), step_to_accuracy_decimals(obj->traits.get_step()));
904
- root["step"] =
905
- value_accuracy_to_string(obj->traits.get_step(), step_to_accuracy_decimals(obj->traits.get_step()));
906
- root["mode"] = (int) obj->traits.get_mode();
907
- if (!obj->traits.get_unit_of_measurement().empty())
908
- root["uom"] = obj->traits.get_unit_of_measurement();
909
- this->add_sorting_info_(root, obj);
910
- }
911
- if (std::isnan(value)) {
912
- root["value"] = "\"NaN\"";
913
- root["state"] = "NA";
914
- } else {
915
- root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
916
- std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
917
- if (!obj->traits.get_unit_of_measurement().empty())
918
- state += " " + obj->traits.get_unit_of_measurement();
919
- root["state"] = state;
920
- }
921
- });
946
+ json::JsonBuilder builder;
947
+ JsonObject root = builder.root();
948
+
949
+ const auto uom_ref = obj->traits.get_unit_of_measurement_ref();
950
+
951
+ set_json_id(root, obj, "number", start_config);
952
+ if (start_config == DETAIL_ALL) {
953
+ root["min_value"] =
954
+ value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step()));
955
+ root["max_value"] =
956
+ value_accuracy_to_string(obj->traits.get_max_value(), step_to_accuracy_decimals(obj->traits.get_step()));
957
+ root["step"] = value_accuracy_to_string(obj->traits.get_step(), step_to_accuracy_decimals(obj->traits.get_step()));
958
+ root["mode"] = (int) obj->traits.get_mode();
959
+ if (!uom_ref.empty())
960
+ root["uom"] = uom_ref;
961
+ this->add_sorting_info_(root, obj);
962
+ }
963
+ if (std::isnan(value)) {
964
+ root["value"] = "\"NaN\"";
965
+ root["state"] = "NA";
966
+ } else {
967
+ root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
968
+ root["state"] =
969
+ value_accuracy_with_uom_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref);
970
+ }
971
+
972
+ return builder.serialize();
922
973
  }
923
974
  #endif
924
975
 
@@ -966,15 +1017,18 @@ std::string WebServer::date_all_json_generator(WebServer *web_server, void *sour
966
1017
  return web_server->date_json((datetime::DateEntity *) (source), DETAIL_ALL);
967
1018
  }
968
1019
  std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_config) {
969
- return json::build_json([this, obj, start_config](JsonObject root) {
970
- set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
971
- std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
972
- root["value"] = value;
973
- root["state"] = value;
974
- if (start_config == DETAIL_ALL) {
975
- this->add_sorting_info_(root, obj);
976
- }
977
- });
1020
+ json::JsonBuilder builder;
1021
+ JsonObject root = builder.root();
1022
+
1023
+ set_json_id(root, obj, "date", start_config);
1024
+ std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
1025
+ root["value"] = value;
1026
+ root["state"] = value;
1027
+ if (start_config == DETAIL_ALL) {
1028
+ this->add_sorting_info_(root, obj);
1029
+ }
1030
+
1031
+ return builder.serialize();
978
1032
  }
979
1033
  #endif // USE_DATETIME_DATE
980
1034
 
@@ -1021,15 +1075,18 @@ std::string WebServer::time_all_json_generator(WebServer *web_server, void *sour
1021
1075
  return web_server->time_json((datetime::TimeEntity *) (source), DETAIL_ALL);
1022
1076
  }
1023
1077
  std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_config) {
1024
- return json::build_json([this, obj, start_config](JsonObject root) {
1025
- set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
1026
- std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
1027
- root["value"] = value;
1028
- root["state"] = value;
1029
- if (start_config == DETAIL_ALL) {
1030
- this->add_sorting_info_(root, obj);
1031
- }
1032
- });
1078
+ json::JsonBuilder builder;
1079
+ JsonObject root = builder.root();
1080
+
1081
+ set_json_id(root, obj, "time", start_config);
1082
+ std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
1083
+ root["value"] = value;
1084
+ root["state"] = value;
1085
+ if (start_config == DETAIL_ALL) {
1086
+ this->add_sorting_info_(root, obj);
1087
+ }
1088
+
1089
+ return builder.serialize();
1033
1090
  }
1034
1091
  #endif // USE_DATETIME_TIME
1035
1092
 
@@ -1076,16 +1133,19 @@ std::string WebServer::datetime_all_json_generator(WebServer *web_server, void *
1076
1133
  return web_server->datetime_json((datetime::DateTimeEntity *) (source), DETAIL_ALL);
1077
1134
  }
1078
1135
  std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail start_config) {
1079
- return json::build_json([this, obj, start_config](JsonObject root) {
1080
- set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
1081
- std::string value = str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour,
1082
- obj->minute, obj->second);
1083
- root["value"] = value;
1084
- root["state"] = value;
1085
- if (start_config == DETAIL_ALL) {
1086
- this->add_sorting_info_(root, obj);
1087
- }
1088
- });
1136
+ json::JsonBuilder builder;
1137
+ JsonObject root = builder.root();
1138
+
1139
+ set_json_id(root, obj, "datetime", start_config);
1140
+ std::string value =
1141
+ str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second);
1142
+ root["value"] = value;
1143
+ root["state"] = value;
1144
+ if (start_config == DETAIL_ALL) {
1145
+ this->add_sorting_info_(root, obj);
1146
+ }
1147
+
1148
+ return builder.serialize();
1089
1149
  }
1090
1150
  #endif // USE_DATETIME_DATETIME
1091
1151
 
@@ -1128,22 +1188,25 @@ std::string WebServer::text_all_json_generator(WebServer *web_server, void *sour
1128
1188
  return web_server->text_json((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_ALL);
1129
1189
  }
1130
1190
  std::string WebServer::text_json(text::Text *obj, const std::string &value, JsonDetail start_config) {
1131
- return json::build_json([this, obj, value, start_config](JsonObject root) {
1132
- set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
1133
- root["min_length"] = obj->traits.get_min_length();
1134
- root["max_length"] = obj->traits.get_max_length();
1135
- root["pattern"] = obj->traits.get_pattern();
1136
- if (obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD) {
1137
- root["state"] = "********";
1138
- } else {
1139
- root["state"] = value;
1140
- }
1141
- root["value"] = value;
1142
- if (start_config == DETAIL_ALL) {
1143
- root["mode"] = (int) obj->traits.get_mode();
1144
- this->add_sorting_info_(root, obj);
1145
- }
1146
- });
1191
+ json::JsonBuilder builder;
1192
+ JsonObject root = builder.root();
1193
+
1194
+ set_json_id(root, obj, "text", start_config);
1195
+ root["min_length"] = obj->traits.get_min_length();
1196
+ root["max_length"] = obj->traits.get_max_length();
1197
+ root["pattern"] = obj->traits.get_pattern();
1198
+ if (obj->traits.get_mode() == text::TextMode::TEXT_MODE_PASSWORD) {
1199
+ root["state"] = "********";
1200
+ } else {
1201
+ root["state"] = value;
1202
+ }
1203
+ root["value"] = value;
1204
+ if (start_config == DETAIL_ALL) {
1205
+ root["mode"] = (int) obj->traits.get_mode();
1206
+ this->add_sorting_info_(root, obj);
1207
+ }
1208
+
1209
+ return builder.serialize();
1147
1210
  }
1148
1211
  #endif
1149
1212
 
@@ -1186,21 +1249,24 @@ std::string WebServer::select_all_json_generator(WebServer *web_server, void *so
1186
1249
  return web_server->select_json((select::Select *) (source), ((select::Select *) (source))->state, DETAIL_ALL);
1187
1250
  }
1188
1251
  std::string WebServer::select_json(select::Select *obj, const std::string &value, JsonDetail start_config) {
1189
- return json::build_json([this, obj, value, start_config](JsonObject root) {
1190
- set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
1191
- if (start_config == DETAIL_ALL) {
1192
- JsonArray opt = root["option"].to<JsonArray>();
1193
- for (auto &option : obj->traits.get_options()) {
1194
- opt.add(option);
1195
- }
1196
- this->add_sorting_info_(root, obj);
1252
+ json::JsonBuilder builder;
1253
+ JsonObject root = builder.root();
1254
+
1255
+ set_json_icon_state_value(root, obj, "select", value, value, start_config);
1256
+ if (start_config == DETAIL_ALL) {
1257
+ JsonArray opt = root["option"].to<JsonArray>();
1258
+ for (auto &option : obj->traits.get_options()) {
1259
+ opt.add(option);
1197
1260
  }
1198
- });
1261
+ this->add_sorting_info_(root, obj);
1262
+ }
1263
+
1264
+ return builder.serialize();
1199
1265
  }
1200
1266
  #endif
1201
1267
 
1202
1268
  // Longest: HORIZONTAL
1203
- #define PSTR_LOCAL(mode_s) strncpy_P(buf, (PGM_P) ((mode_s)), 15)
1269
+ #define PSTR_LOCAL(mode_s) ESPHOME_strncpy_P(buf, (ESPHOME_PGM_P) ((mode_s)), 15)
1204
1270
 
1205
1271
  #ifdef USE_CLIMATE
1206
1272
  void WebServer::on_climate_update(climate::Climate *obj) {
@@ -1244,98 +1310,102 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
1244
1310
  request->send(404);
1245
1311
  }
1246
1312
  std::string WebServer::climate_state_json_generator(WebServer *web_server, void *source) {
1313
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1247
1314
  return web_server->climate_json((climate::Climate *) (source), DETAIL_STATE);
1248
1315
  }
1249
1316
  std::string WebServer::climate_all_json_generator(WebServer *web_server, void *source) {
1317
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1250
1318
  return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
1251
1319
  }
1252
1320
  std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
1253
1321
  // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1254
- return json::build_json([this, obj, start_config](JsonObject root) {
1255
- set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
1256
- const auto traits = obj->get_traits();
1257
- int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
1258
- int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
1259
- char buf[16];
1260
-
1261
- if (start_config == DETAIL_ALL) {
1262
- JsonArray opt = root["modes"].to<JsonArray>();
1263
- for (climate::ClimateMode m : traits.get_supported_modes())
1264
- opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
1265
- if (!traits.get_supported_custom_fan_modes().empty()) {
1266
- JsonArray opt = root["fan_modes"].to<JsonArray>();
1267
- for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
1268
- opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
1269
- }
1322
+ json::JsonBuilder builder;
1323
+ JsonObject root = builder.root();
1324
+ set_json_id(root, obj, "climate", start_config);
1325
+ const auto traits = obj->get_traits();
1326
+ int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
1327
+ int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
1328
+ char buf[16];
1270
1329
 
1271
- if (!traits.get_supported_custom_fan_modes().empty()) {
1272
- JsonArray opt = root["custom_fan_modes"].to<JsonArray>();
1273
- for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
1274
- opt.add(custom_fan_mode);
1275
- }
1276
- if (traits.get_supports_swing_modes()) {
1277
- JsonArray opt = root["swing_modes"].to<JsonArray>();
1278
- for (auto swing_mode : traits.get_supported_swing_modes())
1279
- opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
1280
- }
1281
- if (traits.get_supports_presets() && obj->preset.has_value()) {
1282
- JsonArray opt = root["presets"].to<JsonArray>();
1283
- for (climate::ClimatePreset m : traits.get_supported_presets())
1284
- opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
1285
- }
1286
- if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1287
- JsonArray opt = root["custom_presets"].to<JsonArray>();
1288
- for (auto const &custom_preset : traits.get_supported_custom_presets())
1289
- opt.add(custom_preset);
1290
- }
1291
- this->add_sorting_info_(root, obj);
1330
+ if (start_config == DETAIL_ALL) {
1331
+ JsonArray opt = root["modes"].to<JsonArray>();
1332
+ for (climate::ClimateMode m : traits.get_supported_modes())
1333
+ opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
1334
+ if (!traits.get_supported_custom_fan_modes().empty()) {
1335
+ JsonArray opt = root["fan_modes"].to<JsonArray>();
1336
+ for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
1337
+ opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
1292
1338
  }
1293
1339
 
1294
- bool has_state = false;
1295
- root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
1296
- root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
1297
- root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
1298
- root["step"] = traits.get_visual_target_temperature_step();
1299
- if (traits.get_supports_action()) {
1300
- root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
1301
- root["state"] = root["action"];
1302
- has_state = true;
1340
+ if (!traits.get_supported_custom_fan_modes().empty()) {
1341
+ JsonArray opt = root["custom_fan_modes"].to<JsonArray>();
1342
+ for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
1343
+ opt.add(custom_fan_mode);
1303
1344
  }
1304
- if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
1305
- root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
1306
- }
1307
- if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
1308
- root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
1345
+ if (traits.get_supports_swing_modes()) {
1346
+ JsonArray opt = root["swing_modes"].to<JsonArray>();
1347
+ for (auto swing_mode : traits.get_supported_swing_modes())
1348
+ opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
1309
1349
  }
1310
1350
  if (traits.get_supports_presets() && obj->preset.has_value()) {
1311
- root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
1351
+ JsonArray opt = root["presets"].to<JsonArray>();
1352
+ for (climate::ClimatePreset m : traits.get_supported_presets())
1353
+ opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
1312
1354
  }
1313
1355
  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1314
- root["custom_preset"] = obj->custom_preset.value().c_str();
1356
+ JsonArray opt = root["custom_presets"].to<JsonArray>();
1357
+ for (auto const &custom_preset : traits.get_supported_custom_presets())
1358
+ opt.add(custom_preset);
1315
1359
  }
1316
- if (traits.get_supports_swing_modes()) {
1317
- root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
1318
- }
1319
- if (traits.get_supports_current_temperature()) {
1320
- if (!std::isnan(obj->current_temperature)) {
1321
- root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
1322
- } else {
1323
- root["current_temperature"] = "NA";
1324
- }
1325
- }
1326
- if (traits.get_supports_two_point_target_temperature()) {
1327
- root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
1328
- root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
1329
- if (!has_state) {
1330
- root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
1331
- target_accuracy);
1332
- }
1360
+ this->add_sorting_info_(root, obj);
1361
+ }
1362
+
1363
+ bool has_state = false;
1364
+ root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
1365
+ root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
1366
+ root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
1367
+ root["step"] = traits.get_visual_target_temperature_step();
1368
+ if (traits.get_supports_action()) {
1369
+ root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
1370
+ root["state"] = root["action"];
1371
+ has_state = true;
1372
+ }
1373
+ if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
1374
+ root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
1375
+ }
1376
+ if (!traits.get_supported_custom_fan_modes().empty() && obj->custom_fan_mode.has_value()) {
1377
+ root["custom_fan_mode"] = obj->custom_fan_mode.value().c_str();
1378
+ }
1379
+ if (traits.get_supports_presets() && obj->preset.has_value()) {
1380
+ root["preset"] = PSTR_LOCAL(climate_preset_to_string(obj->preset.value()));
1381
+ }
1382
+ if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1383
+ root["custom_preset"] = obj->custom_preset.value().c_str();
1384
+ }
1385
+ if (traits.get_supports_swing_modes()) {
1386
+ root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
1387
+ }
1388
+ if (traits.get_supports_current_temperature()) {
1389
+ if (!std::isnan(obj->current_temperature)) {
1390
+ root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
1333
1391
  } else {
1334
- root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
1335
- if (!has_state)
1336
- root["state"] = root["target_temperature"];
1392
+ root["current_temperature"] = "NA";
1337
1393
  }
1338
- });
1394
+ }
1395
+ if (traits.get_supports_two_point_target_temperature()) {
1396
+ root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
1397
+ root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
1398
+ if (!has_state) {
1399
+ root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
1400
+ target_accuracy);
1401
+ }
1402
+ } else {
1403
+ root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
1404
+ if (!has_state)
1405
+ root["state"] = root["target_temperature"];
1406
+ }
1407
+
1408
+ return builder.serialize();
1339
1409
  // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
1340
1410
  }
1341
1411
  #endif
@@ -1401,13 +1471,15 @@ std::string WebServer::lock_all_json_generator(WebServer *web_server, void *sour
1401
1471
  return web_server->lock_json((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_ALL);
1402
1472
  }
1403
1473
  std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
1404
- return json::build_json([this, obj, value, start_config](JsonObject root) {
1405
- set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
1406
- start_config);
1407
- if (start_config == DETAIL_ALL) {
1408
- this->add_sorting_info_(root, obj);
1409
- }
1410
- });
1474
+ json::JsonBuilder builder;
1475
+ JsonObject root = builder.root();
1476
+
1477
+ set_json_icon_state_value(root, obj, "lock", lock::lock_state_to_string(value), value, start_config);
1478
+ if (start_config == DETAIL_ALL) {
1479
+ this->add_sorting_info_(root, obj);
1480
+ }
1481
+
1482
+ return builder.serialize();
1411
1483
  }
1412
1484
  #endif
1413
1485
 
@@ -1430,15 +1502,28 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
1430
1502
  }
1431
1503
 
1432
1504
  auto call = obj->make_call();
1433
- if (match.method_equals("open")) {
1434
- call.set_command_open();
1435
- } else if (match.method_equals("close")) {
1436
- call.set_command_close();
1437
- } else if (match.method_equals("stop")) {
1438
- call.set_command_stop();
1439
- } else if (match.method_equals("toggle")) {
1440
- call.set_command_toggle();
1441
- } else if (!match.method_equals("set")) {
1505
+
1506
+ // Lookup table for valve methods
1507
+ static const struct {
1508
+ const char *name;
1509
+ valve::ValveCall &(valve::ValveCall::*action)();
1510
+ } METHODS[] = {
1511
+ {"open", &valve::ValveCall::set_command_open},
1512
+ {"close", &valve::ValveCall::set_command_close},
1513
+ {"stop", &valve::ValveCall::set_command_stop},
1514
+ {"toggle", &valve::ValveCall::set_command_toggle},
1515
+ };
1516
+
1517
+ bool found = false;
1518
+ for (const auto &method : METHODS) {
1519
+ if (match.method_equals(method.name)) {
1520
+ (call.*method.action)();
1521
+ found = true;
1522
+ break;
1523
+ }
1524
+ }
1525
+
1526
+ if (!found && !match.method_equals("set")) {
1442
1527
  request->send(404);
1443
1528
  return;
1444
1529
  }
@@ -1464,17 +1549,20 @@ std::string WebServer::valve_all_json_generator(WebServer *web_server, void *sou
1464
1549
  return web_server->valve_json((valve::Valve *) (source), DETAIL_ALL);
1465
1550
  }
1466
1551
  std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
1467
- return json::build_json([this, obj, start_config](JsonObject root) {
1468
- set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
1469
- obj->position, start_config);
1470
- root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
1471
-
1472
- if (obj->get_traits().get_supports_position())
1473
- root["position"] = obj->position;
1474
- if (start_config == DETAIL_ALL) {
1475
- this->add_sorting_info_(root, obj);
1476
- }
1477
- });
1552
+ json::JsonBuilder builder;
1553
+ JsonObject root = builder.root();
1554
+
1555
+ set_json_icon_state_value(root, obj, "valve", obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position,
1556
+ start_config);
1557
+ root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
1558
+
1559
+ if (obj->get_traits().get_supports_position())
1560
+ root["position"] = obj->position;
1561
+ if (start_config == DETAIL_ALL) {
1562
+ this->add_sorting_info_(root, obj);
1563
+ }
1564
+
1565
+ return builder.serialize();
1478
1566
  }
1479
1567
  #endif
1480
1568
 
@@ -1499,17 +1587,28 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
1499
1587
  auto call = obj->make_call();
1500
1588
  parse_string_param_(request, "code", call, &decltype(call)::set_code);
1501
1589
 
1502
- if (match.method_equals("disarm")) {
1503
- call.disarm();
1504
- } else if (match.method_equals("arm_away")) {
1505
- call.arm_away();
1506
- } else if (match.method_equals("arm_home")) {
1507
- call.arm_home();
1508
- } else if (match.method_equals("arm_night")) {
1509
- call.arm_night();
1510
- } else if (match.method_equals("arm_vacation")) {
1511
- call.arm_vacation();
1512
- } else {
1590
+ // Lookup table for alarm control panel methods
1591
+ static const struct {
1592
+ const char *name;
1593
+ alarm_control_panel::AlarmControlPanelCall &(alarm_control_panel::AlarmControlPanelCall::*action)();
1594
+ } METHODS[] = {
1595
+ {"disarm", &alarm_control_panel::AlarmControlPanelCall::disarm},
1596
+ {"arm_away", &alarm_control_panel::AlarmControlPanelCall::arm_away},
1597
+ {"arm_home", &alarm_control_panel::AlarmControlPanelCall::arm_home},
1598
+ {"arm_night", &alarm_control_panel::AlarmControlPanelCall::arm_night},
1599
+ {"arm_vacation", &alarm_control_panel::AlarmControlPanelCall::arm_vacation},
1600
+ };
1601
+
1602
+ bool found = false;
1603
+ for (const auto &method : METHODS) {
1604
+ if (match.method_equals(method.name)) {
1605
+ (call.*method.action)();
1606
+ found = true;
1607
+ break;
1608
+ }
1609
+ }
1610
+
1611
+ if (!found) {
1513
1612
  request->send(404);
1514
1613
  return;
1515
1614
  }
@@ -1533,14 +1632,17 @@ std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_ser
1533
1632
  std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmControlPanel *obj,
1534
1633
  alarm_control_panel::AlarmControlPanelState value,
1535
1634
  JsonDetail start_config) {
1536
- return json::build_json([this, obj, value, start_config](JsonObject root) {
1537
- char buf[16];
1538
- set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
1539
- PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
1540
- if (start_config == DETAIL_ALL) {
1541
- this->add_sorting_info_(root, obj);
1542
- }
1543
- });
1635
+ json::JsonBuilder builder;
1636
+ JsonObject root = builder.root();
1637
+
1638
+ char buf[16];
1639
+ set_json_icon_state_value(root, obj, "alarm-control-panel", PSTR_LOCAL(alarm_control_panel_state_to_string(value)),
1640
+ value, start_config);
1641
+ if (start_config == DETAIL_ALL) {
1642
+ this->add_sorting_info_(root, obj);
1643
+ }
1644
+
1645
+ return builder.serialize();
1544
1646
  }
1545
1647
  #endif
1546
1648
 
@@ -1577,20 +1679,23 @@ std::string WebServer::event_all_json_generator(WebServer *web_server, void *sou
1577
1679
  return web_server->event_json(event, get_event_type(event), DETAIL_ALL);
1578
1680
  }
1579
1681
  std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
1580
- return json::build_json([this, obj, event_type, start_config](JsonObject root) {
1581
- set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
1582
- if (!event_type.empty()) {
1583
- root["event_type"] = event_type;
1584
- }
1585
- if (start_config == DETAIL_ALL) {
1586
- JsonArray event_types = root["event_types"].to<JsonArray>();
1587
- for (auto const &event_type : obj->get_event_types()) {
1588
- event_types.add(event_type);
1589
- }
1590
- root["device_class"] = obj->get_device_class();
1591
- this->add_sorting_info_(root, obj);
1682
+ json::JsonBuilder builder;
1683
+ JsonObject root = builder.root();
1684
+
1685
+ set_json_id(root, obj, "event", start_config);
1686
+ if (!event_type.empty()) {
1687
+ root["event_type"] = event_type;
1688
+ }
1689
+ if (start_config == DETAIL_ALL) {
1690
+ JsonArray event_types = root["event_types"].to<JsonArray>();
1691
+ for (auto const &event_type : obj->get_event_types()) {
1692
+ event_types.add(event_type);
1592
1693
  }
1593
- });
1694
+ root["device_class"] = obj->get_device_class_ref();
1695
+ this->add_sorting_info_(root, obj);
1696
+ }
1697
+
1698
+ return builder.serialize();
1594
1699
  }
1595
1700
  #endif
1596
1701
 
@@ -1637,25 +1742,30 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
1637
1742
  request->send(404);
1638
1743
  }
1639
1744
  std::string WebServer::update_state_json_generator(WebServer *web_server, void *source) {
1745
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1640
1746
  return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1641
1747
  }
1642
1748
  std::string WebServer::update_all_json_generator(WebServer *web_server, void *source) {
1749
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1643
1750
  return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1644
1751
  }
1645
1752
  std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
1646
1753
  // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1647
- return json::build_json([this, obj, start_config](JsonObject root) {
1648
- set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
1649
- root["value"] = obj->update_info.latest_version;
1650
- root["state"] = update_state_to_string(obj->state);
1651
- if (start_config == DETAIL_ALL) {
1652
- root["current_version"] = obj->update_info.current_version;
1653
- root["title"] = obj->update_info.title;
1654
- root["summary"] = obj->update_info.summary;
1655
- root["release_url"] = obj->update_info.release_url;
1656
- this->add_sorting_info_(root, obj);
1657
- }
1658
- });
1754
+ json::JsonBuilder builder;
1755
+ JsonObject root = builder.root();
1756
+
1757
+ set_json_id(root, obj, "update", start_config);
1758
+ root["value"] = obj->update_info.latest_version;
1759
+ root["state"] = update_state_to_string(obj->state);
1760
+ if (start_config == DETAIL_ALL) {
1761
+ root["current_version"] = obj->update_info.current_version;
1762
+ root["title"] = obj->update_info.title;
1763
+ root["summary"] = obj->update_info.summary;
1764
+ root["release_url"] = obj->update_info.release_url;
1765
+ this->add_sorting_info_(root, obj);
1766
+ }
1767
+
1768
+ return builder.serialize();
1659
1769
  // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
1660
1770
  }
1661
1771
  #endif
@@ -1664,24 +1774,24 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) const {
1664
1774
  const auto &url = request->url();
1665
1775
  const auto method = request->method();
1666
1776
 
1667
- // Simple URL checks
1668
- if (url == "/")
1669
- return true;
1670
-
1671
- #ifdef USE_ARDUINO
1672
- if (url == "/events")
1673
- return true;
1777
+ // Static URL checks
1778
+ static const char *const STATIC_URLS[] = {
1779
+ "/",
1780
+ #if !defined(USE_ESP32) && defined(USE_ARDUINO)
1781
+ "/events",
1674
1782
  #endif
1675
-
1676
1783
  #ifdef USE_WEBSERVER_CSS_INCLUDE
1677
- if (url == "/0.css")
1678
- return true;
1784
+ "/0.css",
1679
1785
  #endif
1680
-
1681
1786
  #ifdef USE_WEBSERVER_JS_INCLUDE
1682
- if (url == "/0.js")
1683
- return true;
1787
+ "/0.js",
1684
1788
  #endif
1789
+ };
1790
+
1791
+ for (const auto &static_url : STATIC_URLS) {
1792
+ if (url == static_url)
1793
+ return true;
1794
+ }
1685
1795
 
1686
1796
  #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
1687
1797
  if (method == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA))
@@ -1701,92 +1811,87 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) const {
1701
1811
  if (!is_get_or_post)
1702
1812
  return false;
1703
1813
 
1704
- // GET-only components
1705
- if (is_get) {
1814
+ // Use lookup tables for domain checks
1815
+ static const char *const GET_ONLY_DOMAINS[] = {
1706
1816
  #ifdef USE_SENSOR
1707
- if (match.domain_equals("sensor"))
1708
- return true;
1817
+ "sensor",
1709
1818
  #endif
1710
1819
  #ifdef USE_BINARY_SENSOR
1711
- if (match.domain_equals("binary_sensor"))
1712
- return true;
1820
+ "binary_sensor",
1713
1821
  #endif
1714
1822
  #ifdef USE_TEXT_SENSOR
1715
- if (match.domain_equals("text_sensor"))
1716
- return true;
1823
+ "text_sensor",
1717
1824
  #endif
1718
1825
  #ifdef USE_EVENT
1719
- if (match.domain_equals("event"))
1720
- return true;
1826
+ "event",
1721
1827
  #endif
1722
- }
1828
+ };
1723
1829
 
1724
- // GET+POST components
1725
- if (is_get_or_post) {
1830
+ static const char *const GET_POST_DOMAINS[] = {
1726
1831
  #ifdef USE_SWITCH
1727
- if (match.domain_equals("switch"))
1728
- return true;
1832
+ "switch",
1729
1833
  #endif
1730
1834
  #ifdef USE_BUTTON
1731
- if (match.domain_equals("button"))
1732
- return true;
1835
+ "button",
1733
1836
  #endif
1734
1837
  #ifdef USE_FAN
1735
- if (match.domain_equals("fan"))
1736
- return true;
1838
+ "fan",
1737
1839
  #endif
1738
1840
  #ifdef USE_LIGHT
1739
- if (match.domain_equals("light"))
1740
- return true;
1841
+ "light",
1741
1842
  #endif
1742
1843
  #ifdef USE_COVER
1743
- if (match.domain_equals("cover"))
1744
- return true;
1844
+ "cover",
1745
1845
  #endif
1746
1846
  #ifdef USE_NUMBER
1747
- if (match.domain_equals("number"))
1748
- return true;
1847
+ "number",
1749
1848
  #endif
1750
1849
  #ifdef USE_DATETIME_DATE
1751
- if (match.domain_equals("date"))
1752
- return true;
1850
+ "date",
1753
1851
  #endif
1754
1852
  #ifdef USE_DATETIME_TIME
1755
- if (match.domain_equals("time"))
1756
- return true;
1853
+ "time",
1757
1854
  #endif
1758
1855
  #ifdef USE_DATETIME_DATETIME
1759
- if (match.domain_equals("datetime"))
1760
- return true;
1856
+ "datetime",
1761
1857
  #endif
1762
1858
  #ifdef USE_TEXT
1763
- if (match.domain_equals("text"))
1764
- return true;
1859
+ "text",
1765
1860
  #endif
1766
1861
  #ifdef USE_SELECT
1767
- if (match.domain_equals("select"))
1768
- return true;
1862
+ "select",
1769
1863
  #endif
1770
1864
  #ifdef USE_CLIMATE
1771
- if (match.domain_equals("climate"))
1772
- return true;
1865
+ "climate",
1773
1866
  #endif
1774
1867
  #ifdef USE_LOCK
1775
- if (match.domain_equals("lock"))
1776
- return true;
1868
+ "lock",
1777
1869
  #endif
1778
1870
  #ifdef USE_VALVE
1779
- if (match.domain_equals("valve"))
1780
- return true;
1871
+ "valve",
1781
1872
  #endif
1782
1873
  #ifdef USE_ALARM_CONTROL_PANEL
1783
- if (match.domain_equals("alarm_control_panel"))
1784
- return true;
1874
+ "alarm_control_panel",
1785
1875
  #endif
1786
1876
  #ifdef USE_UPDATE
1787
- if (match.domain_equals("update"))
1788
- return true;
1877
+ "update",
1789
1878
  #endif
1879
+ };
1880
+
1881
+ // Check GET-only domains
1882
+ if (is_get) {
1883
+ for (const auto &domain : GET_ONLY_DOMAINS) {
1884
+ if (match.domain_equals(domain))
1885
+ return true;
1886
+ }
1887
+ }
1888
+
1889
+ // Check GET+POST domains
1890
+ if (is_get_or_post) {
1891
+ for (const auto &domain : GET_POST_DOMAINS) {
1892
+ if (match.domain_equals(domain))
1893
+ return true;
1894
+ }
1790
1895
  }
1791
1896
 
1792
1897
  return false;
@@ -1800,7 +1905,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
1800
1905
  return;
1801
1906
  }
1802
1907
 
1803
- #ifdef USE_ARDUINO
1908
+ #if !defined(USE_ESP32) && defined(USE_ARDUINO)
1804
1909
  if (url == "/events") {
1805
1910
  this->events_.add_new_client(this, request);
1806
1911
  return;