esphome 2025.6.2__py3-none-any.whl → 2025.7.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 (648) hide show
  1. esphome/__main__.py +1 -3
  2. esphome/codegen.py +2 -0
  3. esphome/components/ac_dimmer/ac_dimmer.cpp +6 -6
  4. esphome/components/adc/__init__.py +25 -1
  5. esphome/components/adc/adc_sensor.h +11 -11
  6. esphome/components/adc/adc_sensor_common.cpp +1 -1
  7. esphome/components/adc/adc_sensor_esp32.cpp +16 -8
  8. esphome/components/ade7880/ade7880.h +0 -2
  9. esphome/components/ads1115/ads1115.h +0 -1
  10. esphome/components/ads1118/ads1118.h +0 -1
  11. esphome/components/ags10/ags10.h +0 -2
  12. esphome/components/aic3204/aic3204.h +0 -1
  13. esphome/components/alarm_control_panel/__init__.py +5 -2
  14. esphome/components/alpha3/alpha3.h +0 -1
  15. esphome/components/am43/cover/am43_cover.h +0 -1
  16. esphome/components/am43/sensor/am43_sensor.h +0 -1
  17. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +0 -2
  18. esphome/components/anova/anova.cpp +5 -1
  19. esphome/components/anova/anova.h +0 -1
  20. esphome/components/apds9960/apds9960.cpp +1 -1
  21. esphome/components/api/__init__.py +57 -21
  22. esphome/components/api/api_connection.cpp +344 -539
  23. esphome/components/api/api_connection.h +224 -141
  24. esphome/components/api/api_frame_helper.cpp +91 -127
  25. esphome/components/api/api_frame_helper.h +64 -54
  26. esphome/components/api/api_pb2.cpp +1837 -9044
  27. esphome/components/api/api_pb2.h +532 -685
  28. esphome/components/api/api_pb2_dump.cpp +4432 -0
  29. esphome/components/api/api_pb2_service.cpp +184 -425
  30. esphome/components/api/api_pb2_service.h +13 -6
  31. esphome/components/api/api_server.cpp +131 -167
  32. esphome/components/api/api_server.h +38 -10
  33. esphome/components/api/client.py +10 -4
  34. esphome/components/api/custom_api_device.h +8 -0
  35. esphome/components/api/list_entities.cpp +37 -104
  36. esphome/components/api/list_entities.h +33 -23
  37. esphome/components/api/proto.h +532 -26
  38. esphome/components/api/subscribe_state.cpp +23 -29
  39. esphome/components/api/subscribe_state.h +26 -19
  40. esphome/components/api/user_services.h +2 -0
  41. esphome/components/as3935_spi/as3935_spi.h +0 -2
  42. esphome/components/as5600/as5600.h +0 -1
  43. esphome/components/async_tcp/__init__.py +14 -5
  44. esphome/components/atc_mithermometer/atc_mithermometer.h +0 -1
  45. esphome/components/atm90e32/atm90e32.cpp +2 -1
  46. esphome/components/audio/audio_decoder.cpp +1 -1
  47. esphome/components/audio/audio_transfer_buffer.cpp +2 -2
  48. esphome/components/b_parasite/b_parasite.h +0 -1
  49. esphome/components/bedjet/bedjet_hub.cpp +5 -1
  50. esphome/components/bedjet/climate/bedjet_climate.cpp +5 -1
  51. esphome/components/beken_spi_led_strip/led_strip.cpp +4 -2
  52. esphome/components/bh1750/bh1750.cpp +5 -5
  53. esphome/components/binary_sensor/__init__.py +82 -5
  54. esphome/components/binary_sensor/automation.h +19 -1
  55. esphome/components/binary_sensor/binary_sensor.cpp +12 -30
  56. esphome/components/binary_sensor/binary_sensor.h +11 -25
  57. esphome/components/binary_sensor/filter.cpp +29 -24
  58. esphome/components/binary_sensor/filter.h +20 -10
  59. esphome/components/ble_client/output/ble_binary_output.h +0 -1
  60. esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +5 -1
  61. esphome/components/ble_client/sensor/ble_rssi_sensor.h +0 -1
  62. esphome/components/ble_client/sensor/ble_sensor.cpp +5 -1
  63. esphome/components/ble_client/sensor/ble_sensor.h +0 -1
  64. esphome/components/ble_client/switch/ble_switch.h +0 -1
  65. esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +5 -1
  66. esphome/components/ble_client/text_sensor/ble_text_sensor.h +0 -1
  67. esphome/components/ble_presence/ble_presence_device.h +0 -1
  68. esphome/components/ble_rssi/ble_rssi_sensor.h +0 -1
  69. esphome/components/ble_scanner/ble_scanner.h +0 -1
  70. esphome/components/bluetooth_proxy/bluetooth_connection.h +9 -2
  71. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +16 -6
  72. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -2
  73. esphome/components/bme680/sensor.py +1 -1
  74. esphome/components/bmp581/bmp581.h +0 -2
  75. esphome/components/button/__init__.py +5 -2
  76. esphome/components/camera/__init__.py +1 -0
  77. esphome/components/camera/camera.cpp +22 -0
  78. esphome/components/camera/camera.h +80 -0
  79. esphome/components/canbus/__init__.py +1 -0
  80. esphome/components/cap1188/cap1188.h +0 -1
  81. esphome/components/captive_portal/__init__.py +12 -2
  82. esphome/components/captive_portal/captive_portal.cpp +12 -2
  83. esphome/components/captive_portal/captive_portal.h +5 -2
  84. esphome/components/ccs811/ccs811.h +0 -2
  85. esphome/components/climate/__init__.py +5 -2
  86. esphome/components/cm1106/sensor.py +2 -2
  87. esphome/components/const/__init__.py +2 -0
  88. esphome/components/copy/binary_sensor/copy_binary_sensor.h +0 -1
  89. esphome/components/copy/button/copy_button.h +0 -1
  90. esphome/components/copy/cover/copy_cover.h +0 -1
  91. esphome/components/copy/fan/copy_fan.h +0 -1
  92. esphome/components/copy/lock/copy_lock.h +0 -1
  93. esphome/components/copy/number/copy_number.h +0 -1
  94. esphome/components/copy/select/copy_select.h +0 -1
  95. esphome/components/copy/sensor/copy_sensor.h +0 -1
  96. esphome/components/copy/switch/copy_switch.h +0 -1
  97. esphome/components/copy/text/copy_text.h +0 -1
  98. esphome/components/copy/text_sensor/copy_text_sensor.h +0 -1
  99. esphome/components/cover/__init__.py +5 -2
  100. esphome/components/cs5460a/cs5460a.h +0 -1
  101. esphome/components/datetime/__init__.py +4 -2
  102. esphome/components/debug/__init__.py +20 -0
  103. esphome/components/debug/debug_esp32.cpp +2 -0
  104. esphome/components/deep_sleep/__init__.py +43 -9
  105. esphome/components/demo/__init__.py +2 -2
  106. esphome/components/display/display.cpp +4 -3
  107. esphome/components/display/display.h +0 -2
  108. esphome/components/display/display_buffer.cpp +1 -1
  109. esphome/components/ds2484/__init__.py +1 -0
  110. esphome/components/ds2484/ds2484.cpp +209 -0
  111. esphome/components/ds2484/ds2484.h +43 -0
  112. esphome/components/ds2484/one_wire.py +37 -0
  113. esphome/components/duty_time/duty_time_sensor.h +0 -1
  114. esphome/components/ens160_base/ens160_base.h +0 -1
  115. esphome/components/es7210/es7210.h +0 -1
  116. esphome/components/es7243e/es7243e.h +0 -1
  117. esphome/components/es8156/es8156.h +0 -1
  118. esphome/components/es8311/es8311.h +0 -1
  119. esphome/components/es8388/es8388.h +0 -1
  120. esphome/components/esp32/__init__.py +103 -135
  121. esphome/components/esp32/core.cpp +0 -4
  122. esphome/components/esp32/gpio.h +1 -1
  123. esphome/components/esp32/helpers.cpp +69 -0
  124. esphome/components/esp32_ble/ble.cpp +5 -6
  125. esphome/components/esp32_ble/ble.h +29 -14
  126. esphome/components/esp32_ble/ble_event.h +6 -6
  127. esphome/components/esp32_ble_client/ble_client_base.cpp +21 -6
  128. esphome/components/esp32_ble_client/ble_client_base.h +24 -9
  129. esphome/components/esp32_ble_tracker/__init__.py +2 -8
  130. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +5 -5
  131. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +11 -7
  132. esphome/components/esp32_camera/__init__.py +112 -98
  133. esphome/components/esp32_camera/esp32_camera.cpp +41 -31
  134. esphome/components/esp32_camera/esp32_camera.h +35 -30
  135. esphome/components/esp32_camera_web_server/__init__.py +2 -1
  136. esphome/components/esp32_camera_web_server/camera_web_server.cpp +8 -8
  137. esphome/components/esp32_camera_web_server/camera_web_server.h +3 -3
  138. esphome/components/esp32_hall/sensor.py +2 -21
  139. esphome/components/esp32_hosted/__init__.py +101 -0
  140. esphome/components/esp32_hosted/esp32_hosted.py.script +12 -0
  141. esphome/components/esp32_improv/esp32_improv_component.cpp +3 -0
  142. esphome/components/esp32_rmt/__init__.py +0 -58
  143. esphome/components/esp32_rmt_led_strip/led_strip.cpp +77 -63
  144. esphome/components/esp32_rmt_led_strip/led_strip.h +11 -17
  145. esphome/components/esp32_rmt_led_strip/light.py +14 -76
  146. esphome/components/esp32_touch/esp32_touch.h +174 -28
  147. esphome/components/esp32_touch/esp32_touch_common.cpp +162 -0
  148. esphome/components/esp32_touch/esp32_touch_v1.cpp +240 -0
  149. esphome/components/esp32_touch/esp32_touch_v2.cpp +397 -0
  150. esphome/components/esp8266/__init__.py +2 -0
  151. esphome/components/esp8266/gpio.cpp +10 -10
  152. esphome/components/esp8266/helpers.cpp +31 -0
  153. esphome/components/esp_ldo/__init__.py +10 -8
  154. esphome/components/esp_ldo/esp_ldo.h +3 -0
  155. esphome/components/esphome/ota/__init__.py +1 -0
  156. esphome/components/esphome/ota/ota_esphome.cpp +24 -19
  157. esphome/components/ethernet/__init__.py +42 -23
  158. esphome/components/ethernet/esp_eth_phy_jl1101.c +0 -16
  159. esphome/components/ethernet/ethernet_component.cpp +69 -29
  160. esphome/components/ethernet/ethernet_component.h +18 -10
  161. esphome/components/event/__init__.py +5 -2
  162. esphome/components/ezo/ezo.h +0 -1
  163. esphome/components/ezo_pmp/ezo_pmp.h +0 -1
  164. esphome/components/fan/__init__.py +5 -2
  165. esphome/components/fan/fan.cpp +4 -0
  166. esphome/components/feedback/feedback_cover.h +0 -1
  167. esphome/components/font/__init__.py +92 -82
  168. esphome/components/font/font.cpp +9 -2
  169. esphome/components/font/font.h +20 -5
  170. esphome/components/fs3000/fs3000.h +0 -1
  171. esphome/components/gcja5/gcja5.h +0 -1
  172. esphome/components/gl_r01_i2c/__init__.py +0 -0
  173. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +68 -0
  174. esphome/components/gl_r01_i2c/gl_r01_i2c.h +22 -0
  175. esphome/components/gl_r01_i2c/sensor.py +36 -0
  176. esphome/components/gp8403/gp8403.h +0 -1
  177. esphome/components/gpio/binary_sensor/__init__.py +39 -1
  178. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +77 -3
  179. esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +40 -0
  180. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +0 -2
  181. esphome/components/he60r/he60r.h +0 -1
  182. esphome/components/heatpumpir/climate.py +2 -1
  183. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  184. esphome/components/heatpumpir/heatpumpir.h +1 -0
  185. esphome/components/honeywellabp2_i2c/honeywellabp2.h +0 -1
  186. esphome/components/host/__init__.py +3 -1
  187. esphome/components/host/helpers.cpp +57 -0
  188. esphome/components/http_request/__init__.py +19 -1
  189. esphome/components/http_request/http_request.h +1 -1
  190. esphome/components/http_request/http_request_arduino.cpp +0 -1
  191. esphome/components/http_request/http_request_arduino.h +1 -0
  192. esphome/components/http_request/http_request_idf.cpp +0 -1
  193. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  194. esphome/components/http_request/update/http_request_update.cpp +35 -16
  195. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +3 -9
  196. esphome/components/hydreon_rgxx/sensor.py +1 -1
  197. esphome/components/i2c/__init__.py +23 -11
  198. esphome/components/i2c/i2c_bus.h +8 -1
  199. esphome/components/i2c/i2c_bus_arduino.cpp +4 -3
  200. esphome/components/i2c/i2c_bus_arduino.h +6 -3
  201. esphome/components/i2c/i2c_bus_esp_idf.h +5 -3
  202. esphome/components/i2c_device/i2c_device.h +0 -1
  203. esphome/components/i2s_audio/__init__.py +2 -10
  204. esphome/components/i2s_audio/i2s_audio.cpp +1 -5
  205. esphome/components/i2s_audio/media_player/__init__.py +2 -2
  206. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  207. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +2 -2
  208. esphome/components/iaqcore/iaqcore.h +0 -2
  209. esphome/components/image/__init__.py +123 -24
  210. esphome/components/improv_serial/improv_serial_component.cpp +0 -4
  211. esphome/components/ina219/ina219.cpp +7 -0
  212. esphome/components/ina219/ina219.h +1 -0
  213. esphome/components/ina260/ina260.h +0 -2
  214. esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +0 -1
  215. esphome/components/inkplate6/display.py +15 -0
  216. esphome/components/inkplate6/inkplate.cpp +2 -2
  217. esphome/components/integration/integration_sensor.h +0 -1
  218. esphome/components/internal_temperature/internal_temperature.cpp +8 -27
  219. esphome/components/internal_temperature/sensor.py +0 -26
  220. esphome/components/interval/interval.h +0 -2
  221. esphome/components/json/__init__.py +1 -1
  222. esphome/components/json/json_util.cpp +56 -63
  223. esphome/components/ld2410/button/__init__.py +3 -3
  224. esphome/components/ld2410/button/factory_reset_button.cpp +9 -0
  225. esphome/components/ld2410/button/{reset_button.h → factory_reset_button.h} +2 -2
  226. esphome/components/ld2410/ld2410.cpp +421 -268
  227. esphome/components/ld2410/ld2410.h +44 -146
  228. esphome/components/ld2410/number/__init__.py +2 -2
  229. esphome/components/ld2410/sensor.py +1 -1
  230. esphome/components/ld2410/switch/__init__.py +1 -1
  231. esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp +2 -2
  232. esphome/components/ld2420/button/reconfig_buttons.cpp +1 -1
  233. esphome/components/ld2420/ld2420.cpp +252 -147
  234. esphome/components/ld2420/ld2420.h +52 -126
  235. esphome/components/ld2420/number/__init__.py +2 -2
  236. esphome/components/ld2420/number/gate_config_number.cpp +1 -1
  237. esphome/components/ld2420/select/operating_mode_select.cpp +1 -1
  238. esphome/components/ld2420/sensor/__init__.py +6 -2
  239. esphome/components/ld2420/sensor/ld2420_sensor.cpp +2 -2
  240. esphome/components/ld2420/sensor/ld2420_sensor.h +1 -1
  241. esphome/components/ld2420/text_sensor/text_sensor.cpp +2 -2
  242. esphome/components/ld2450/button/__init__.py +3 -3
  243. esphome/components/ld2450/button/factory_reset_button.cpp +9 -0
  244. esphome/components/ld2450/button/{reset_button.h → factory_reset_button.h} +2 -2
  245. esphome/components/ld2450/ld2450.cpp +384 -232
  246. esphome/components/ld2450/ld2450.h +60 -69
  247. esphome/components/ld2450/switch/__init__.py +1 -1
  248. esphome/components/ledc/ledc_output.cpp +1 -63
  249. esphome/components/libretiny/__init__.py +5 -3
  250. esphome/components/libretiny/const.py +5 -0
  251. esphome/components/libretiny/generate_components.py +1 -0
  252. esphome/components/libretiny/helpers.cpp +35 -0
  253. esphome/components/libretiny/lt_component.cpp +5 -3
  254. esphome/components/light/__init__.py +4 -2
  255. esphome/components/light/addressable_light.h +3 -3
  256. esphome/components/light/light_call.cpp +180 -243
  257. esphome/components/light/light_call.h +72 -20
  258. esphome/components/light/light_color_values.h +14 -14
  259. esphome/components/light/light_json_schema.cpp +17 -16
  260. esphome/components/light/light_state.h +15 -13
  261. esphome/components/light/transformers.h +2 -2
  262. esphome/components/ln882x/__init__.py +52 -0
  263. esphome/components/ln882x/boards.py +285 -0
  264. esphome/components/lock/__init__.py +5 -2
  265. esphome/components/logger/__init__.py +40 -3
  266. esphome/components/logger/logger.cpp +47 -12
  267. esphome/components/logger/logger.h +80 -49
  268. esphome/components/logger/logger_esp32.cpp +3 -3
  269. esphome/components/lps22/__init__.py +0 -0
  270. esphome/components/lps22/lps22.cpp +75 -0
  271. esphome/components/lps22/lps22.h +27 -0
  272. esphome/components/lps22/sensor.py +58 -0
  273. esphome/components/ltr390/ltr390.h +0 -1
  274. esphome/components/ltr501/ltr501.h +0 -1
  275. esphome/components/ltr_als_ps/ltr_als_ps.h +0 -1
  276. esphome/components/lvgl/__init__.py +1 -1
  277. esphome/components/lvgl/schemas.py +66 -6
  278. esphome/components/lvgl/styles.py +24 -16
  279. esphome/components/lvgl/widgets/__init__.py +12 -2
  280. esphome/components/lvgl/widgets/lv_bar.py +40 -19
  281. esphome/components/lvgl/widgets/meter.py +20 -13
  282. esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +1 -1
  283. esphome/components/max9611/max9611.h +0 -1
  284. esphome/components/mcp23016/__init__.py +1 -1
  285. esphome/components/mcp23xxx_base/__init__.py +1 -1
  286. esphome/components/mcp4461/__init__.py +1 -1
  287. esphome/components/mcp4461/output/__init__.py +3 -2
  288. esphome/components/mcp9600/mcp9600.h +0 -2
  289. esphome/components/md5/md5.cpp +3 -3
  290. esphome/components/md5/md5.h +1 -6
  291. esphome/components/mdns/__init__.py +22 -11
  292. esphome/components/media_player/__init__.py +4 -3
  293. esphome/components/micro_wake_word/__init__.py +1 -5
  294. esphome/components/micro_wake_word/streaming_model.cpp +2 -2
  295. esphome/components/microphone/microphone.cpp +7 -9
  296. esphome/components/microphone/microphone.h +0 -2
  297. esphome/components/mipi_spi/display.py +1 -0
  298. esphome/components/mmc5603/mmc5603.cpp +1 -1
  299. esphome/components/modbus/modbus.cpp +33 -15
  300. esphome/components/modbus/modbus.h +9 -0
  301. esphome/components/modbus_controller/__init__.py +42 -10
  302. esphome/components/modbus_controller/modbus_controller.cpp +92 -11
  303. esphome/components/modbus_controller/modbus_controller.h +61 -7
  304. esphome/components/mopeka_pro_check/mopeka_pro_check.h +0 -1
  305. esphome/components/mopeka_std_check/mopeka_std_check.h +0 -1
  306. esphome/components/mpl3115a2/mpl3115a2.h +0 -2
  307. esphome/components/mqtt/__init__.py +16 -0
  308. esphome/components/mqtt/mqtt_alarm_control_panel.cpp +2 -1
  309. esphome/components/mqtt/mqtt_backend.h +2 -1
  310. esphome/components/mqtt/mqtt_backend_esp32.cpp +132 -47
  311. esphome/components/mqtt/mqtt_backend_esp32.h +106 -4
  312. esphome/components/mqtt/mqtt_binary_sensor.cpp +1 -0
  313. esphome/components/mqtt/mqtt_button.cpp +4 -1
  314. esphome/components/mqtt/mqtt_client.cpp +17 -9
  315. esphome/components/mqtt/mqtt_client.h +8 -3
  316. esphome/components/mqtt/mqtt_climate.cpp +6 -4
  317. esphome/components/mqtt/mqtt_component.cpp +3 -1
  318. esphome/components/mqtt/mqtt_cover.cpp +1 -0
  319. esphome/components/mqtt/mqtt_date.cpp +4 -3
  320. esphome/components/mqtt/mqtt_datetime.cpp +7 -6
  321. esphome/components/mqtt/mqtt_event.cpp +6 -3
  322. esphome/components/mqtt/mqtt_fan.cpp +1 -0
  323. esphome/components/mqtt/mqtt_light.cpp +8 -4
  324. esphome/components/mqtt/mqtt_lock.cpp +3 -1
  325. esphome/components/mqtt/mqtt_number.cpp +1 -0
  326. esphome/components/mqtt/mqtt_select.cpp +2 -1
  327. esphome/components/mqtt/mqtt_sensor.cpp +3 -1
  328. esphome/components/mqtt/mqtt_switch.cpp +3 -1
  329. esphome/components/mqtt/mqtt_text.cpp +1 -0
  330. esphome/components/mqtt/mqtt_text_sensor.cpp +3 -1
  331. esphome/components/mqtt/mqtt_time.cpp +4 -3
  332. esphome/components/mqtt/mqtt_update.cpp +1 -0
  333. esphome/components/mqtt/mqtt_valve.cpp +3 -1
  334. esphome/components/ms8607/ms8607.cpp +1 -1
  335. esphome/components/ms8607/ms8607.h +0 -1
  336. esphome/components/neopixelbus/light.py +4 -1
  337. esphome/components/neopixelbus/neopixelbus_light.h +1 -1
  338. esphome/components/network/__init__.py +4 -1
  339. esphome/components/network/ip_address.h +1 -0
  340. esphome/components/nextion/__init__.py +16 -0
  341. esphome/components/nextion/base_component.py +1 -0
  342. esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
  343. esphome/components/nextion/display.py +14 -4
  344. esphome/components/nextion/nextion.cpp +166 -101
  345. esphome/components/nextion/nextion.h +84 -53
  346. esphome/components/nextion/nextion_commands.cpp +11 -10
  347. esphome/components/nextion/nextion_component.cpp +28 -28
  348. esphome/components/nextion/nextion_component.h +53 -18
  349. esphome/components/nextion/nextion_component_base.h +3 -0
  350. esphome/components/nextion/nextion_upload.cpp +36 -0
  351. esphome/components/nextion/nextion_upload_arduino.cpp +10 -35
  352. esphome/components/nextion/nextion_upload_idf.cpp +9 -33
  353. esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
  354. esphome/components/nextion/switch/nextion_switch.cpp +1 -1
  355. esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
  356. esphome/components/nfc/nfc.cpp +3 -22
  357. esphome/components/nfc/nfc.h +3 -3
  358. esphome/components/number/__init__.py +5 -2
  359. esphome/components/online_image/__init__.py +9 -1
  360. esphome/components/online_image/online_image.cpp +17 -7
  361. esphome/components/online_image/online_image.h +10 -2
  362. esphome/components/opentherm/opentherm.cpp +7 -12
  363. esphome/components/opentherm/output/output.cpp +1 -1
  364. esphome/components/openthread/__init__.py +47 -40
  365. esphome/components/openthread/const.py +1 -0
  366. esphome/components/openthread/openthread_esp.cpp +27 -5
  367. esphome/components/opt3001/__init__.py +0 -0
  368. esphome/components/opt3001/opt3001.cpp +122 -0
  369. esphome/components/opt3001/opt3001.h +27 -0
  370. esphome/components/opt3001/sensor.py +35 -0
  371. esphome/components/ota/__init__.py +17 -0
  372. esphome/components/ota/ota_backend.h +27 -1
  373. esphome/components/ota/ota_backend_arduino_esp32.cpp +12 -2
  374. esphome/components/ota/ota_backend_arduino_esp32.h +3 -0
  375. esphome/components/ota/ota_backend_arduino_esp8266.cpp +18 -4
  376. esphome/components/ota/ota_backend_arduino_esp8266.h +3 -0
  377. esphome/components/ota/ota_backend_arduino_libretiny.cpp +12 -2
  378. esphome/components/ota/ota_backend_arduino_libretiny.h +3 -0
  379. esphome/components/ota/ota_backend_arduino_rp2040.cpp +9 -2
  380. esphome/components/ota/ota_backend_arduino_rp2040.h +3 -0
  381. esphome/components/ota/ota_backend_esp_idf.cpp +10 -16
  382. esphome/components/ota/ota_backend_esp_idf.h +1 -0
  383. esphome/components/packages/__init__.py +5 -2
  384. esphome/components/packet_transport/binary_sensor.py +61 -4
  385. esphome/components/packet_transport/packet_transport.cpp +34 -1
  386. esphome/components/packet_transport/packet_transport.h +11 -5
  387. esphome/components/pcf8574/__init__.py +1 -1
  388. esphome/components/pi4ioe5v6408/__init__.py +84 -0
  389. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +171 -0
  390. esphome/components/pi4ioe5v6408/pi4ioe5v6408.h +70 -0
  391. esphome/components/pmsa003i/pmsa003i.h +0 -1
  392. esphome/components/pmsx003/pmsx003.h +0 -1
  393. esphome/components/pn7150/pn7150.cpp +7 -7
  394. esphome/components/pn7150/pn7150.h +0 -1
  395. esphome/components/pn7160/pn7160.cpp +7 -7
  396. esphome/components/pn7160/pn7160.h +0 -1
  397. esphome/components/preferences/syncer.h +2 -0
  398. esphome/components/prometheus/prometheus_handler.h +1 -1
  399. esphome/components/psram/psram.cpp +0 -20
  400. esphome/components/pulse_counter/pulse_counter_sensor.h +0 -1
  401. esphome/components/pulse_meter/pulse_meter_sensor.cpp +8 -4
  402. esphome/components/pulse_width/pulse_width.h +0 -1
  403. esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +0 -4
  404. esphome/components/pvvx_mithermometer/display/pvvx_display.h +0 -2
  405. esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +0 -1
  406. esphome/components/qr_code/__init__.py +13 -10
  407. esphome/components/qwiic_pir/qwiic_pir.h +0 -1
  408. esphome/components/radon_eye_ble/radon_eye_listener.cpp +1 -1
  409. esphome/components/rc522/rc522.h +0 -1
  410. esphome/components/rdm6300/rdm6300.h +0 -2
  411. esphome/components/remote_base/__init__.py +7 -5
  412. esphome/components/remote_base/remote_base.cpp +24 -21
  413. esphome/components/remote_base/remote_base.h +3 -26
  414. esphome/components/remote_receiver/__init__.py +40 -46
  415. esphome/components/remote_receiver/remote_receiver.h +4 -18
  416. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -87
  417. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +1 -1
  418. esphome/components/remote_transmitter/__init__.py +42 -43
  419. esphome/components/remote_transmitter/remote_transmitter.h +2 -14
  420. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -77
  421. esphome/components/resistance/resistance_sensor.h +0 -1
  422. esphome/components/rp2040/__init__.py +2 -0
  423. esphome/components/rp2040/helpers.cpp +55 -0
  424. esphome/components/rp2040_pio_led_strip/led_strip.cpp +2 -2
  425. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -4
  426. esphome/components/rtttl/__init__.py +4 -4
  427. esphome/components/rtttl/rtttl.cpp +10 -1
  428. esphome/components/ruuvitag/ruuvitag.h +0 -1
  429. esphome/components/safe_mode/safe_mode.cpp +2 -0
  430. esphome/components/safe_mode/safe_mode.h +4 -1
  431. esphome/components/scd30/scd30.h +0 -1
  432. esphome/components/scd30/sensor.py +2 -2
  433. esphome/components/scd4x/scd4x.cpp +61 -54
  434. esphome/components/scd4x/scd4x.h +17 -15
  435. esphome/components/scd4x/sensor.py +4 -4
  436. esphome/components/script/script.h +0 -2
  437. esphome/components/sdp3x/sensor.py +1 -1
  438. esphome/components/select/__init__.py +5 -2
  439. esphome/components/sen5x/sen5x.h +0 -1
  440. esphome/components/senseair/senseair.h +0 -1
  441. esphome/components/sensor/__init__.py +4 -2
  442. esphome/components/sensor/filter.cpp +1 -1
  443. esphome/components/sensor/sensor.cpp +12 -6
  444. esphome/components/sensor/sensor.h +13 -5
  445. esphome/components/servo/servo.cpp +2 -2
  446. esphome/components/servo/servo.h +0 -1
  447. esphome/components/sfa30/sfa30.h +0 -1
  448. esphome/components/sgp30/sgp30.h +0 -1
  449. esphome/components/sgp4x/sgp4x.h +0 -1
  450. esphome/components/shelly_dimmer/stm32flash.cpp +1 -2
  451. esphome/components/sht4x/sht4x.h +0 -1
  452. esphome/components/sm300d2/sm300d2.h +0 -2
  453. esphome/components/smt100/sensor.py +8 -4
  454. esphome/components/smt100/smt100.cpp +5 -5
  455. esphome/components/smt100/smt100.h +3 -3
  456. esphome/components/sn74hc595/__init__.py +1 -1
  457. esphome/components/sn74hc595/sn74hc595.cpp +5 -4
  458. esphome/components/sntp/sntp_component.cpp +9 -3
  459. esphome/components/sntp/time.py +2 -0
  460. esphome/components/socket/__init__.py +17 -0
  461. esphome/components/spi/__init__.py +27 -6
  462. esphome/components/spi/spi.cpp +3 -2
  463. esphome/components/spi/spi.h +9 -3
  464. esphome/components/spi/spi_arduino.cpp +3 -5
  465. esphome/components/spi/spi_esp_idf.cpp +40 -21
  466. esphome/components/spi_led_strip/spi_led_strip.cpp +1 -1
  467. esphome/components/sps30/sps30.h +0 -1
  468. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  469. esphome/components/st7701s/st7701s.cpp +0 -4
  470. esphome/components/status/status_binary_sensor.h +0 -2
  471. esphome/components/substitutions/__init__.py +81 -21
  472. esphome/components/substitutions/jinja.py +99 -0
  473. esphome/components/sun/sun.cpp +3 -4
  474. esphome/components/switch/__init__.py +5 -2
  475. esphome/components/switch/binary_sensor/switch_binary_sensor.h +0 -1
  476. esphome/components/sx126x/__init__.py +317 -0
  477. esphome/components/sx126x/automation.h +62 -0
  478. esphome/components/sx126x/packet_transport/__init__.py +26 -0
  479. esphome/components/sx126x/packet_transport/sx126x_transport.cpp +26 -0
  480. esphome/components/sx126x/packet_transport/sx126x_transport.h +25 -0
  481. esphome/components/sx126x/sx126x.cpp +523 -0
  482. esphome/components/sx126x/sx126x.h +140 -0
  483. esphome/components/sx126x/sx126x_reg.h +163 -0
  484. esphome/components/sx127x/__init__.py +325 -0
  485. esphome/components/sx127x/automation.h +62 -0
  486. esphome/components/sx127x/packet_transport/__init__.py +26 -0
  487. esphome/components/sx127x/packet_transport/sx127x_transport.cpp +26 -0
  488. esphome/components/sx127x/packet_transport/sx127x_transport.h +25 -0
  489. esphome/components/sx127x/sx127x.cpp +498 -0
  490. esphome/components/sx127x/sx127x.h +128 -0
  491. esphome/components/sx127x/sx127x_reg.h +295 -0
  492. esphome/components/syslog/esphome_syslog.cpp +5 -3
  493. esphome/components/syslog/esphome_syslog.h +1 -1
  494. esphome/components/tca9555/__init__.py +1 -1
  495. esphome/components/template/binary_sensor/template_binary_sensor.cpp +1 -9
  496. esphome/components/text/__init__.py +5 -2
  497. esphome/components/text_sensor/__init__.py +5 -2
  498. esphome/components/thermostat/thermostat_climate.cpp +34 -31
  499. esphome/components/thermostat/thermostat_climate.h +43 -39
  500. esphome/components/time/__init__.py +16 -2
  501. esphome/components/time/real_time_clock.cpp +4 -0
  502. esphome/components/time/real_time_clock.h +5 -1
  503. esphome/components/tlc5971/tlc5971.cpp +4 -1
  504. esphome/components/tmp1075/tmp1075.h +0 -2
  505. esphome/components/tof10120/tof10120_sensor.h +0 -1
  506. esphome/components/tormatic/tormatic_cover.h +0 -1
  507. esphome/components/total_daily_energy/total_daily_energy.h +0 -1
  508. esphome/components/tsl2591/tsl2591.cpp +1 -1
  509. esphome/components/ttp229_bsf/ttp229_bsf.h +0 -1
  510. esphome/components/ttp229_lsf/ttp229_lsf.h +0 -1
  511. esphome/components/tx20/tx20.cpp +2 -2
  512. esphome/components/uart/__init__.py +18 -0
  513. esphome/components/uart/uart_component_esp_idf.cpp +1 -5
  514. esphome/components/update/__init__.py +5 -2
  515. esphome/components/update/update_entity.h +8 -0
  516. esphome/components/usb_host/__init__.py +5 -2
  517. esphome/components/usb_host/usb_host_client.cpp +10 -10
  518. esphome/components/usb_uart/cp210x.cpp +1 -1
  519. esphome/components/usb_uart/usb_uart.cpp +41 -44
  520. esphome/components/usb_uart/usb_uart.h +4 -3
  521. esphome/components/valve/__init__.py +5 -2
  522. esphome/components/vbus/vbus.h +0 -1
  523. esphome/components/veml3235/veml3235.h +0 -1
  524. esphome/components/veml7700/veml7700.h +0 -1
  525. esphome/components/vl53l0x/vl53l0x_sensor.h +0 -1
  526. esphome/components/voice_assistant/voice_assistant.cpp +4 -4
  527. esphome/components/watchdog/watchdog.cpp +0 -4
  528. esphome/components/waveshare_epaper/waveshare_epaper.cpp +6 -6
  529. esphome/components/web_server/__init__.py +34 -19
  530. esphome/components/web_server/ota/__init__.py +32 -0
  531. esphome/components/web_server/ota/ota_web_server.cpp +210 -0
  532. esphome/components/web_server/ota/ota_web_server.h +26 -0
  533. esphome/components/web_server/web_server.cpp +324 -439
  534. esphome/components/web_server/web_server.h +33 -23
  535. esphome/components/web_server/web_server_v1.cpp +4 -5
  536. esphome/components/web_server_base/__init__.py +5 -2
  537. esphome/components/web_server_base/web_server_base.cpp +2 -94
  538. esphome/components/web_server_base/web_server_base.h +5 -25
  539. esphome/components/web_server_idf/multipart.cpp +254 -0
  540. esphome/components/web_server_idf/multipart.h +86 -0
  541. esphome/components/web_server_idf/utils.cpp +32 -0
  542. esphome/components/web_server_idf/utils.h +10 -0
  543. esphome/components/web_server_idf/web_server_idf.cpp +164 -16
  544. esphome/components/web_server_idf/web_server_idf.h +11 -10
  545. esphome/components/wiegand/wiegand.cpp +2 -2
  546. esphome/components/wifi/__init__.py +18 -0
  547. esphome/components/wifi/wifi_component.cpp +17 -22
  548. esphome/components/wifi/wifi_component.h +27 -23
  549. esphome/components/wifi/wifi_component_esp32_arduino.cpp +52 -59
  550. esphome/components/wifi/wifi_component_esp8266.cpp +46 -46
  551. esphome/components/wifi/wifi_component_esp_idf.cpp +35 -36
  552. esphome/components/wifi/wifi_component_libretiny.cpp +26 -27
  553. esphome/components/wifi/wifi_component_pico_w.cpp +3 -3
  554. esphome/components/wifi_info/wifi_info_text_sensor.cpp +6 -6
  555. esphome/components/wireguard/__init__.py +2 -11
  556. esphome/components/xiaomi_ble/xiaomi_ble.cpp +13 -1
  557. esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
  558. esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +0 -1
  559. esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +0 -1
  560. esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +0 -1
  561. esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +0 -1
  562. esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +0 -1
  563. esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +0 -1
  564. esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h +0 -1
  565. esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +0 -1
  566. esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +0 -1
  567. esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +0 -1
  568. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +0 -1
  569. esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +0 -1
  570. esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +0 -1
  571. esphome/components/xiaomi_mhoc303/xiaomi_mhoc303.h +0 -1
  572. esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +0 -1
  573. esphome/components/xiaomi_miscale/xiaomi_miscale.h +0 -1
  574. esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +0 -1
  575. esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +0 -1
  576. esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +0 -1
  577. esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +0 -1
  578. esphome/components/xiaomi_xmwsdj04mmc/__init__.py +0 -0
  579. esphome/components/xiaomi_xmwsdj04mmc/sensor.py +77 -0
  580. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +77 -0
  581. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +36 -0
  582. esphome/components/zio_ultrasonic/zio_ultrasonic.h +0 -2
  583. esphome/components/zyaura/zyaura.h +0 -1
  584. esphome/config.py +88 -22
  585. esphome/config_helpers.py +74 -1
  586. esphome/config_validation.py +12 -1
  587. esphome/const.py +65 -10
  588. esphome/core/__init__.py +18 -2
  589. esphome/core/application.cpp +169 -10
  590. esphome/core/application.h +145 -165
  591. esphome/core/area.h +19 -0
  592. esphome/core/automation.h +58 -9
  593. esphome/core/color.cpp +3 -5
  594. esphome/core/color.h +16 -16
  595. esphome/core/component.cpp +156 -22
  596. esphome/core/component.h +98 -4
  597. esphome/core/component_iterator.cpp +11 -9
  598. esphome/core/component_iterator.h +12 -10
  599. esphome/core/config.py +155 -6
  600. esphome/core/controller.cpp +4 -2
  601. esphome/core/controller.h +1 -1
  602. esphome/core/datatypes.h +2 -2
  603. esphome/core/defines.h +17 -2
  604. esphome/core/device.h +20 -0
  605. esphome/core/entity_base.cpp +20 -15
  606. esphome/core/entity_base.h +76 -0
  607. esphome/core/entity_helpers.py +168 -1
  608. esphome/core/event_pool.h +81 -0
  609. esphome/core/helpers.cpp +75 -230
  610. esphome/core/helpers.h +165 -105
  611. esphome/core/lock_free_queue.h +151 -0
  612. esphome/core/log.cpp +2 -2
  613. esphome/core/log.h +2 -0
  614. esphome/core/optional.h +5 -0
  615. esphome/core/ring_buffer.cpp +2 -2
  616. esphome/core/scheduler.cpp +275 -103
  617. esphome/core/scheduler.h +154 -17
  618. esphome/core/time.cpp +5 -5
  619. esphome/core/time.h +5 -5
  620. esphome/cpp_generator.py +17 -0
  621. esphome/cpp_helpers.py +0 -22
  622. esphome/cpp_types.py +3 -1
  623. esphome/dashboard/entries.py +1 -1
  624. esphome/dashboard/util/text.py +5 -21
  625. esphome/dashboard/web_server.py +9 -1
  626. esphome/helpers.py +47 -0
  627. esphome/loader.py +15 -1
  628. esphome/pins.py +14 -8
  629. esphome/platformio_api.py +2 -0
  630. esphome/wizard.py +17 -4
  631. esphome/writer.py +44 -3
  632. esphome/yaml_util.py +0 -2
  633. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/METADATA +10 -9
  634. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/RECORD +639 -580
  635. esphome/components/api/api_pb2_size.h +0 -361
  636. esphome/components/esp32_ble/ble_event_pool.h +0 -72
  637. esphome/components/esp32_ble/queue.h +0 -85
  638. esphome/components/esp32_hall/esp32_hall.cpp +0 -25
  639. esphome/components/esp32_hall/esp32_hall.h +0 -23
  640. esphome/components/esp32_touch/esp32_touch.cpp +0 -355
  641. esphome/components/ld2410/button/reset_button.cpp +0 -9
  642. esphome/components/ld2450/button/reset_button.cpp +0 -9
  643. esphome/components/openthread/tlv.py +0 -65
  644. /esphome/{dashboard/enum.py → enum.py} +0 -0
  645. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/WHEEL +0 -0
  646. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/entry_points.txt +0 -0
  647. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/licenses/LICENSE +0 -0
  648. {esphome-2025.6.2.dist-info → esphome-2025.7.0.dist-info}/top_level.txt +0 -0
@@ -46,29 +46,60 @@ static const char *const HEADER_CORS_REQ_PNA = "Access-Control-Request-Private-N
46
46
  static const char *const HEADER_CORS_ALLOW_PNA = "Access-Control-Allow-Private-Network";
47
47
  #endif
48
48
 
49
- UrlMatch match_url(const std::string &url, bool only_domain = false) {
50
- UrlMatch match;
51
- match.valid = false;
52
- size_t domain_end = url.find('/', 1);
53
- if (domain_end == std::string::npos)
54
- return match;
55
- match.domain = url.substr(1, domain_end - 1);
56
- if (only_domain) {
57
- match.valid = true;
49
+ // Parse URL and return match info
50
+ static UrlMatch match_url(const char *url_ptr, size_t url_len, bool only_domain) {
51
+ UrlMatch match{};
52
+
53
+ // URL must start with '/'
54
+ if (url_len < 2 || url_ptr[0] != '/') {
58
55
  return match;
59
56
  }
60
- if (url.length() == domain_end - 1)
57
+
58
+ // Skip leading '/'
59
+ const char *start = url_ptr + 1;
60
+ const char *end = url_ptr + url_len;
61
+
62
+ // Find domain (everything up to next '/' or end)
63
+ const char *domain_end = (const char *) memchr(start, '/', end - start);
64
+ if (!domain_end) {
65
+ // No second slash found - original behavior returns invalid
61
66
  return match;
62
- size_t id_begin = domain_end + 1;
63
- size_t id_end = url.find('/', id_begin);
67
+ }
68
+
69
+ // Set domain
70
+ match.domain = start;
71
+ match.domain_len = domain_end - start;
64
72
  match.valid = true;
65
- if (id_end == std::string::npos) {
66
- match.id = url.substr(id_begin, url.length() - id_begin);
73
+
74
+ if (only_domain) {
75
+ return match;
76
+ }
77
+
78
+ // Parse ID if present
79
+ if (domain_end + 1 >= end) {
80
+ return match; // Nothing after domain slash
81
+ }
82
+
83
+ const char *id_start = domain_end + 1;
84
+ const char *id_end = (const char *) memchr(id_start, '/', end - id_start);
85
+
86
+ if (!id_end) {
87
+ // No more slashes, entire remaining string is ID
88
+ match.id = id_start;
89
+ match.id_len = end - id_start;
67
90
  return match;
68
91
  }
69
- match.id = url.substr(id_begin, id_end - id_begin);
70
- size_t method_begin = id_end + 1;
71
- match.method = url.substr(method_begin, url.length() - method_begin);
92
+
93
+ // Set ID
94
+ match.id = id_start;
95
+ match.id_len = id_end - id_start;
96
+
97
+ // Parse method if present
98
+ if (id_end + 1 < end) {
99
+ match.method = id_end + 1;
100
+ match.method_len = end - (id_end + 1);
101
+ }
102
+
72
103
  return match;
73
104
  }
74
105
 
@@ -91,10 +122,19 @@ void DeferredUpdateEventSource::process_deferred_queue_() {
91
122
  while (!deferred_queue_.empty()) {
92
123
  DeferredEvent &de = deferred_queue_.front();
93
124
  std::string message = de.message_generator_(web_server_, de.source_);
94
- if (this->try_send(message.c_str(), "state")) {
125
+ if (this->send(message.c_str(), "state") != DISCARDED) {
95
126
  // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
96
127
  deferred_queue_.erase(deferred_queue_.begin());
128
+ this->consecutive_send_failures_ = 0; // Reset failure count on successful send
97
129
  } else {
130
+ this->consecutive_send_failures_++;
131
+ if (this->consecutive_send_failures_ >= MAX_CONSECUTIVE_SEND_FAILURES) {
132
+ // Too many failures, connection is likely dead
133
+ ESP_LOGW(TAG, "Closing stuck EventSource connection after %" PRIu16 " failed sends",
134
+ this->consecutive_send_failures_);
135
+ this->close();
136
+ this->deferred_queue_.clear();
137
+ }
98
138
  break;
99
139
  }
100
140
  }
@@ -131,8 +171,10 @@ void DeferredUpdateEventSource::deferrable_send_state(void *source, const char *
131
171
  deq_push_back_with_dedup_(source, message_generator);
132
172
  } else {
133
173
  std::string message = message_generator(web_server_, source);
134
- if (!this->try_send(message.c_str(), "state")) {
174
+ if (this->send(message.c_str(), "state") == DISCARDED) {
135
175
  deq_push_back_with_dedup_(source, message_generator);
176
+ } else {
177
+ this->consecutive_send_failures_ = 0; // Reset failure count on successful send
136
178
  }
137
179
  }
138
180
  }
@@ -171,8 +213,8 @@ void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServer
171
213
  ws->defer([this, ws, es]() { this->on_client_connect_(ws, es); });
172
214
  });
173
215
 
174
- es->onDisconnect([this, ws](AsyncEventSource *source, AsyncEventSourceClient *client) {
175
- ws->defer([this, source]() { this->on_client_disconnect_((DeferredUpdateEventSource *) source); });
216
+ es->onDisconnect([this, ws, es](AsyncEventSourceClient *client) {
217
+ ws->defer([this, es]() { this->on_client_disconnect_((DeferredUpdateEventSource *) es); });
176
218
  });
177
219
 
178
220
  es->handleRequest(request);
@@ -184,6 +226,7 @@ void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUp
184
226
  std::string message = ws->get_config_json();
185
227
  source->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
186
228
 
229
+ #ifdef USE_WEBSERVER_SORTING
187
230
  for (auto &group : ws->sorting_groups_) {
188
231
  message = json::build_json([group](JsonObject root) {
189
232
  root["name"] = group.second.name;
@@ -193,6 +236,7 @@ void DeferredUpdateEventSourceList::on_client_connect_(WebServer *ws, DeferredUp
193
236
  // up to 31 groups should be able to be queued initially without defer
194
237
  source->try_send_nodefer(message.c_str(), "sorting_group");
195
238
  }
239
+ #endif
196
240
 
197
241
  source->entities_iterator_.begin(ws->include_internal_);
198
242
 
@@ -211,11 +255,7 @@ void DeferredUpdateEventSourceList::on_client_disconnect_(DeferredUpdateEventSou
211
255
  }
212
256
  #endif
213
257
 
214
- WebServer::WebServer(web_server_base::WebServerBase *base) : base_(base) {
215
- #ifdef USE_ESP32
216
- to_schedule_lock_ = xSemaphoreCreateMutex();
217
- #endif
218
- }
258
+ WebServer::WebServer(web_server_base::WebServerBase *base) : base_(base) {}
219
259
 
220
260
  #ifdef USE_WEBSERVER_CSS_INCLUDE
221
261
  void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; }
@@ -228,7 +268,11 @@ std::string WebServer::get_config_json() {
228
268
  return json::build_json([this](JsonObject root) {
229
269
  root["title"] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
230
270
  root["comment"] = App.get_comment();
231
- root["ota"] = this->allow_ota_;
271
+ #ifdef USE_WEBSERVER_OTA
272
+ root["ota"] = true; // web_server OTA platform is configured
273
+ #else
274
+ root["ota"] = false;
275
+ #endif
232
276
  root["log"] = this->expose_log_;
233
277
  root["lang"] = "en";
234
278
  });
@@ -243,7 +287,8 @@ void WebServer::setup() {
243
287
  if (logger::global_logger != nullptr && this->expose_log_) {
244
288
  logger::global_logger->add_on_log_callback(
245
289
  // logs are not deferred, the memory overhead would be too large
246
- [this](int level, const char *tag, const char *message) {
290
+ [this](int level, const char *tag, const char *message, size_t message_len) {
291
+ (void) message_len;
247
292
  this->events_.try_send_nodefer(message, "log", millis());
248
293
  });
249
294
  }
@@ -254,33 +299,13 @@ void WebServer::setup() {
254
299
  #endif
255
300
  this->base_->add_handler(this);
256
301
 
257
- if (this->allow_ota_)
258
- this->base_->add_ota_handler();
302
+ // OTA is now handled by the web_server OTA platform
259
303
 
260
304
  // doesn't need defer functionality - if the queue is full, the client JS knows it's alive because it's clearly
261
305
  // getting a lot of events
262
306
  this->set_interval(10000, [this]() { this->events_.try_send_nodefer("", "ping", millis(), 30000); });
263
307
  }
264
- void WebServer::loop() {
265
- #ifdef USE_ESP32
266
- if (xSemaphoreTake(this->to_schedule_lock_, 0L)) {
267
- std::function<void()> fn;
268
- if (!to_schedule_.empty()) {
269
- // scheduler execute things out of order which may lead to incorrect state
270
- // this->defer(std::move(to_schedule_.front()));
271
- // let's execute it directly from the loop
272
- fn = std::move(to_schedule_.front());
273
- to_schedule_.pop_front();
274
- }
275
- xSemaphoreGive(this->to_schedule_lock_);
276
- if (fn) {
277
- fn();
278
- }
279
- }
280
- #endif
281
-
282
- this->events_.loop();
283
- }
308
+ void WebServer::loop() { this->events_.loop(); }
284
309
  void WebServer::dump_config() {
285
310
  ESP_LOGCONFIG(TAG,
286
311
  "Web Server:\n"
@@ -291,14 +316,23 @@ float WebServer::get_setup_priority() const { return setup_priority::WIFI - 1.0f
291
316
 
292
317
  #ifdef USE_WEBSERVER_LOCAL
293
318
  void WebServer::handle_index_request(AsyncWebServerRequest *request) {
319
+ #ifndef USE_ESP8266
320
+ AsyncWebServerResponse *response = request->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
321
+ #else
294
322
  AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
323
+ #endif
295
324
  response->addHeader("Content-Encoding", "gzip");
296
325
  request->send(response);
297
326
  }
298
327
  #elif USE_WEBSERVER_VERSION >= 2
299
328
  void WebServer::handle_index_request(AsyncWebServerRequest *request) {
329
+ #ifndef USE_ESP8266
330
+ AsyncWebServerResponse *response =
331
+ request->beginResponse(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
332
+ #else
300
333
  AsyncWebServerResponse *response =
301
334
  request->beginResponse_P(200, "text/html", ESPHOME_WEBSERVER_INDEX_HTML, ESPHOME_WEBSERVER_INDEX_HTML_SIZE);
335
+ #endif
302
336
  // No gzip header here because the HTML file is so small
303
337
  request->send(response);
304
338
  }
@@ -317,8 +351,13 @@ void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) {
317
351
 
318
352
  #ifdef USE_WEBSERVER_CSS_INCLUDE
319
353
  void WebServer::handle_css_request(AsyncWebServerRequest *request) {
354
+ #ifndef USE_ESP8266
355
+ AsyncWebServerResponse *response =
356
+ request->beginResponse(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
357
+ #else
320
358
  AsyncWebServerResponse *response =
321
359
  request->beginResponse_P(200, "text/css", ESPHOME_WEBSERVER_CSS_INCLUDE, ESPHOME_WEBSERVER_CSS_INCLUDE_SIZE);
360
+ #endif
322
361
  response->addHeader("Content-Encoding", "gzip");
323
362
  request->send(response);
324
363
  }
@@ -326,8 +365,13 @@ void WebServer::handle_css_request(AsyncWebServerRequest *request) {
326
365
 
327
366
  #ifdef USE_WEBSERVER_JS_INCLUDE
328
367
  void WebServer::handle_js_request(AsyncWebServerRequest *request) {
368
+ #ifndef USE_ESP8266
369
+ AsyncWebServerResponse *response =
370
+ request->beginResponse(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
371
+ #else
329
372
  AsyncWebServerResponse *response =
330
373
  request->beginResponse_P(200, "text/javascript", ESPHOME_WEBSERVER_JS_INCLUDE, ESPHOME_WEBSERVER_JS_INCLUDE_SIZE);
374
+ #endif
331
375
  response->addHeader("Content-Encoding", "gzip");
332
376
  request->send(response);
333
377
  }
@@ -351,6 +395,12 @@ void WebServer::handle_js_request(AsyncWebServerRequest *request) {
351
395
  set_json_value(root, obj, sensor, value, start_config); \
352
396
  (root)["state"] = state;
353
397
 
398
+ // Helper to get request detail parameter
399
+ static JsonDetail get_request_detail(AsyncWebServerRequest *request) {
400
+ auto *param = request->getParam("detail");
401
+ return (param && param->value() == "all") ? DETAIL_ALL : DETAIL_STATE;
402
+ }
403
+
354
404
  #ifdef USE_SENSOR
355
405
  void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
356
406
  if (this->events_.empty())
@@ -359,14 +409,10 @@ void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
359
409
  }
360
410
  void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
361
411
  for (sensor::Sensor *obj : App.get_sensors()) {
362
- if (obj->get_object_id() != match.id)
412
+ if (!match.id_equals(obj->get_object_id()))
363
413
  continue;
364
- if (request->method() == HTTP_GET && match.method.empty()) {
365
- auto detail = DETAIL_STATE;
366
- auto *param = request->getParam("detail");
367
- if (param && param->value() == "all") {
368
- detail = DETAIL_ALL;
369
- }
414
+ if (request->method() == HTTP_GET && match.method_empty()) {
415
+ auto detail = get_request_detail(request);
370
416
  std::string data = this->sensor_json(obj, obj->state, detail);
371
417
  request->send(200, "application/json", data.c_str());
372
418
  return;
@@ -392,12 +438,7 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
392
438
  }
393
439
  set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
394
440
  if (start_config == DETAIL_ALL) {
395
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
396
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
397
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
398
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
399
- }
400
- }
441
+ this->add_sorting_info_(root, obj);
401
442
  if (!obj->get_unit_of_measurement().empty())
402
443
  root["uom"] = obj->get_unit_of_measurement();
403
444
  }
@@ -413,14 +454,10 @@ void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
413
454
  }
414
455
  void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
415
456
  for (text_sensor::TextSensor *obj : App.get_text_sensors()) {
416
- if (obj->get_object_id() != match.id)
457
+ if (!match.id_equals(obj->get_object_id()))
417
458
  continue;
418
- if (request->method() == HTTP_GET && match.method.empty()) {
419
- auto detail = DETAIL_STATE;
420
- auto *param = request->getParam("detail");
421
- if (param && param->value() == "all") {
422
- detail = DETAIL_ALL;
423
- }
459
+ if (request->method() == HTTP_GET && match.method_empty()) {
460
+ auto detail = get_request_detail(request);
424
461
  std::string data = this->text_sensor_json(obj, obj->state, detail);
425
462
  request->send(200, "application/json", data.c_str());
426
463
  return;
@@ -441,12 +478,7 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std:
441
478
  return json::build_json([this, obj, value, start_config](JsonObject root) {
442
479
  set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
443
480
  if (start_config == DETAIL_ALL) {
444
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
445
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
446
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
447
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
448
- }
449
- }
481
+ this->add_sorting_info_(root, obj);
450
482
  }
451
483
  });
452
484
  }
@@ -460,25 +492,21 @@ void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
460
492
  }
461
493
  void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
462
494
  for (switch_::Switch *obj : App.get_switches()) {
463
- if (obj->get_object_id() != match.id)
495
+ if (!match.id_equals(obj->get_object_id()))
464
496
  continue;
465
497
 
466
- if (request->method() == HTTP_GET && match.method.empty()) {
467
- auto detail = DETAIL_STATE;
468
- auto *param = request->getParam("detail");
469
- if (param && param->value() == "all") {
470
- detail = DETAIL_ALL;
471
- }
498
+ if (request->method() == HTTP_GET && match.method_empty()) {
499
+ auto detail = get_request_detail(request);
472
500
  std::string data = this->switch_json(obj, obj->state, detail);
473
501
  request->send(200, "application/json", data.c_str());
474
- } else if (match.method == "toggle") {
475
- this->schedule_([obj]() { obj->toggle(); });
502
+ } else if (match.method_equals("toggle")) {
503
+ this->defer([obj]() { obj->toggle(); });
476
504
  request->send(200);
477
- } else if (match.method == "turn_on") {
478
- this->schedule_([obj]() { obj->turn_on(); });
505
+ } else if (match.method_equals("turn_on")) {
506
+ this->defer([obj]() { obj->turn_on(); });
479
507
  request->send(200);
480
- } else if (match.method == "turn_off") {
481
- this->schedule_([obj]() { obj->turn_off(); });
508
+ } else if (match.method_equals("turn_off")) {
509
+ this->defer([obj]() { obj->turn_off(); });
482
510
  request->send(200);
483
511
  } else {
484
512
  request->send(404);
@@ -498,12 +526,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail
498
526
  set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
499
527
  if (start_config == DETAIL_ALL) {
500
528
  root["assumed_state"] = obj->assumed_state();
501
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
502
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
503
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
504
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
505
- }
506
- }
529
+ this->add_sorting_info_(root, obj);
507
530
  }
508
531
  });
509
532
  }
@@ -512,18 +535,14 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail
512
535
  #ifdef USE_BUTTON
513
536
  void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match) {
514
537
  for (button::Button *obj : App.get_buttons()) {
515
- if (obj->get_object_id() != match.id)
538
+ if (!match.id_equals(obj->get_object_id()))
516
539
  continue;
517
- if (request->method() == HTTP_GET && match.method.empty()) {
518
- auto detail = DETAIL_STATE;
519
- auto *param = request->getParam("detail");
520
- if (param && param->value() == "all") {
521
- detail = DETAIL_ALL;
522
- }
540
+ if (request->method() == HTTP_GET && match.method_empty()) {
541
+ auto detail = get_request_detail(request);
523
542
  std::string data = this->button_json(obj, detail);
524
543
  request->send(200, "application/json", data.c_str());
525
- } else if (match.method == "press") {
526
- this->schedule_([obj]() { obj->press(); });
544
+ } else if (match.method_equals("press")) {
545
+ this->defer([obj]() { obj->press(); });
527
546
  request->send(200);
528
547
  return;
529
548
  } else {
@@ -543,33 +562,24 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config)
543
562
  return json::build_json([this, obj, start_config](JsonObject root) {
544
563
  set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
545
564
  if (start_config == DETAIL_ALL) {
546
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
547
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
548
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
549
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
550
- }
551
- }
565
+ this->add_sorting_info_(root, obj);
552
566
  }
553
567
  });
554
568
  }
555
569
  #endif
556
570
 
557
571
  #ifdef USE_BINARY_SENSOR
558
- void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
572
+ void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
559
573
  if (this->events_.empty())
560
574
  return;
561
575
  this->events_.deferrable_send_state(obj, "state", binary_sensor_state_json_generator);
562
576
  }
563
577
  void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
564
578
  for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) {
565
- if (obj->get_object_id() != match.id)
579
+ if (!match.id_equals(obj->get_object_id()))
566
580
  continue;
567
- if (request->method() == HTTP_GET && match.method.empty()) {
568
- auto detail = DETAIL_STATE;
569
- auto *param = request->getParam("detail");
570
- if (param && param->value() == "all") {
571
- detail = DETAIL_ALL;
572
- }
581
+ if (request->method() == HTTP_GET && match.method_empty()) {
582
+ auto detail = get_request_detail(request);
573
583
  std::string data = this->binary_sensor_json(obj, obj->state, detail);
574
584
  request->send(200, "application/json", data.c_str());
575
585
  return;
@@ -590,12 +600,7 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool
590
600
  set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
591
601
  start_config);
592
602
  if (start_config == DETAIL_ALL) {
593
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
594
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
595
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
596
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
597
- }
598
- }
603
+ this->add_sorting_info_(root, obj);
599
604
  }
600
605
  });
601
606
  }
@@ -609,22 +614,18 @@ void WebServer::on_fan_update(fan::Fan *obj) {
609
614
  }
610
615
  void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
611
616
  for (fan::Fan *obj : App.get_fans()) {
612
- if (obj->get_object_id() != match.id)
617
+ if (!match.id_equals(obj->get_object_id()))
613
618
  continue;
614
619
 
615
- if (request->method() == HTTP_GET && match.method.empty()) {
616
- auto detail = DETAIL_STATE;
617
- auto *param = request->getParam("detail");
618
- if (param && param->value() == "all") {
619
- detail = DETAIL_ALL;
620
- }
620
+ if (request->method() == HTTP_GET && match.method_empty()) {
621
+ auto detail = get_request_detail(request);
621
622
  std::string data = this->fan_json(obj, detail);
622
623
  request->send(200, "application/json", data.c_str());
623
- } else if (match.method == "toggle") {
624
- this->schedule_([obj]() { obj->toggle().perform(); });
624
+ } else if (match.method_equals("toggle")) {
625
+ this->defer([obj]() { obj->toggle().perform(); });
625
626
  request->send(200);
626
- } else if (match.method == "turn_on" || match.method == "turn_off") {
627
- auto call = match.method == "turn_on" ? obj->turn_on() : obj->turn_off();
627
+ } else if (match.method_equals("turn_on") || match.method_equals("turn_off")) {
628
+ auto call = match.method_equals("turn_on") ? obj->turn_on() : obj->turn_off();
628
629
 
629
630
  if (request->hasParam("speed_level")) {
630
631
  auto speed_level = request->getParam("speed_level")->value();
@@ -653,7 +654,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
653
654
  return;
654
655
  }
655
656
  }
656
- this->schedule_([call]() mutable { call.perform(); });
657
+ this->defer([call]() mutable { call.perform(); });
657
658
  request->send(200);
658
659
  } else {
659
660
  request->send(404);
@@ -680,12 +681,7 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
680
681
  if (obj->get_traits().supports_oscillation())
681
682
  root["oscillation"] = obj->oscillating;
682
683
  if (start_config == DETAIL_ALL) {
683
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
684
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
685
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
686
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
687
- }
688
- }
684
+ this->add_sorting_info_(root, obj);
689
685
  }
690
686
  });
691
687
  }
@@ -699,21 +695,17 @@ void WebServer::on_light_update(light::LightState *obj) {
699
695
  }
700
696
  void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
701
697
  for (light::LightState *obj : App.get_lights()) {
702
- if (obj->get_object_id() != match.id)
698
+ if (!match.id_equals(obj->get_object_id()))
703
699
  continue;
704
700
 
705
- if (request->method() == HTTP_GET && match.method.empty()) {
706
- auto detail = DETAIL_STATE;
707
- auto *param = request->getParam("detail");
708
- if (param && param->value() == "all") {
709
- detail = DETAIL_ALL;
710
- }
701
+ if (request->method() == HTTP_GET && match.method_empty()) {
702
+ auto detail = get_request_detail(request);
711
703
  std::string data = this->light_json(obj, detail);
712
704
  request->send(200, "application/json", data.c_str());
713
- } else if (match.method == "toggle") {
714
- this->schedule_([obj]() { obj->toggle().perform(); });
705
+ } else if (match.method_equals("toggle")) {
706
+ this->defer([obj]() { obj->toggle().perform(); });
715
707
  request->send(200);
716
- } else if (match.method == "turn_on") {
708
+ } else if (match.method_equals("turn_on")) {
717
709
  auto call = obj->turn_on();
718
710
  if (request->hasParam("brightness")) {
719
711
  auto brightness = parse_number<float>(request->getParam("brightness")->value().c_str());
@@ -768,9 +760,9 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
768
760
  call.set_effect(effect);
769
761
  }
770
762
 
771
- this->schedule_([call]() mutable { call.perform(); });
763
+ this->defer([call]() mutable { call.perform(); });
772
764
  request->send(200);
773
- } else if (match.method == "turn_off") {
765
+ } else if (match.method_equals("turn_off")) {
774
766
  auto call = obj->turn_off();
775
767
  if (request->hasParam("transition")) {
776
768
  auto transition = parse_number<uint32_t>(request->getParam("transition")->value().c_str());
@@ -778,7 +770,7 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
778
770
  call.set_transition_length(*transition * 1000);
779
771
  }
780
772
  }
781
- this->schedule_([call]() mutable { call.perform(); });
773
+ this->defer([call]() mutable { call.perform(); });
782
774
  request->send(200);
783
775
  } else {
784
776
  request->send(404);
@@ -800,17 +792,12 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
800
792
 
801
793
  light::LightJSONSchema::dump_json(*obj, root);
802
794
  if (start_config == DETAIL_ALL) {
803
- JsonArray opt = root.createNestedArray("effects");
795
+ JsonArray opt = root["effects"].to<JsonArray>();
804
796
  opt.add("None");
805
797
  for (auto const &option : obj->get_effects()) {
806
798
  opt.add(option->get_name());
807
799
  }
808
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
809
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
810
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
811
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
812
- }
813
- }
800
+ this->add_sorting_info_(root, obj);
814
801
  }
815
802
  });
816
803
  }
@@ -824,30 +811,26 @@ void WebServer::on_cover_update(cover::Cover *obj) {
824
811
  }
825
812
  void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
826
813
  for (cover::Cover *obj : App.get_covers()) {
827
- if (obj->get_object_id() != match.id)
814
+ if (!match.id_equals(obj->get_object_id()))
828
815
  continue;
829
816
 
830
- if (request->method() == HTTP_GET && match.method.empty()) {
831
- auto detail = DETAIL_STATE;
832
- auto *param = request->getParam("detail");
833
- if (param && param->value() == "all") {
834
- detail = DETAIL_ALL;
835
- }
817
+ if (request->method() == HTTP_GET && match.method_empty()) {
818
+ auto detail = get_request_detail(request);
836
819
  std::string data = this->cover_json(obj, detail);
837
820
  request->send(200, "application/json", data.c_str());
838
821
  return;
839
822
  }
840
823
 
841
824
  auto call = obj->make_call();
842
- if (match.method == "open") {
825
+ if (match.method_equals("open")) {
843
826
  call.set_command_open();
844
- } else if (match.method == "close") {
827
+ } else if (match.method_equals("close")) {
845
828
  call.set_command_close();
846
- } else if (match.method == "stop") {
829
+ } else if (match.method_equals("stop")) {
847
830
  call.set_command_stop();
848
- } else if (match.method == "toggle") {
831
+ } else if (match.method_equals("toggle")) {
849
832
  call.set_command_toggle();
850
- } else if (match.method != "set") {
833
+ } else if (!match.method_equals("set")) {
851
834
  request->send(404);
852
835
  return;
853
836
  }
@@ -872,7 +855,7 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
872
855
  }
873
856
  }
874
857
 
875
- this->schedule_([call]() mutable { call.perform(); });
858
+ this->defer([call]() mutable { call.perform(); });
876
859
  request->send(200);
877
860
  return;
878
861
  }
@@ -895,12 +878,7 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
895
878
  if (obj->get_traits().get_supports_tilt())
896
879
  root["tilt"] = obj->tilt;
897
880
  if (start_config == DETAIL_ALL) {
898
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
899
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
900
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
901
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
902
- }
903
- }
881
+ this->add_sorting_info_(root, obj);
904
882
  }
905
883
  });
906
884
  }
@@ -914,20 +892,16 @@ void WebServer::on_number_update(number::Number *obj, float state) {
914
892
  }
915
893
  void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
916
894
  for (auto *obj : App.get_numbers()) {
917
- if (obj->get_object_id() != match.id)
895
+ if (!match.id_equals(obj->get_object_id()))
918
896
  continue;
919
897
 
920
- if (request->method() == HTTP_GET && match.method.empty()) {
921
- auto detail = DETAIL_STATE;
922
- auto *param = request->getParam("detail");
923
- if (param && param->value() == "all") {
924
- detail = DETAIL_ALL;
925
- }
898
+ if (request->method() == HTTP_GET && match.method_empty()) {
899
+ auto detail = get_request_detail(request);
926
900
  std::string data = this->number_json(obj, obj->state, detail);
927
901
  request->send(200, "application/json", data.c_str());
928
902
  return;
929
903
  }
930
- if (match.method != "set") {
904
+ if (!match.method_equals("set")) {
931
905
  request->send(404);
932
906
  return;
933
907
  }
@@ -939,7 +913,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
939
913
  call.set_value(*value);
940
914
  }
941
915
 
942
- this->schedule_([call]() mutable { call.perform(); });
916
+ this->defer([call]() mutable { call.perform(); });
943
917
  request->send(200);
944
918
  return;
945
919
  }
@@ -965,12 +939,7 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
965
939
  root["mode"] = (int) obj->traits.get_mode();
966
940
  if (!obj->traits.get_unit_of_measurement().empty())
967
941
  root["uom"] = obj->traits.get_unit_of_measurement();
968
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
969
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
970
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
971
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
972
- }
973
- }
942
+ this->add_sorting_info_(root, obj);
974
943
  }
975
944
  if (std::isnan(value)) {
976
945
  root["value"] = "\"NaN\"";
@@ -994,19 +963,15 @@ void WebServer::on_date_update(datetime::DateEntity *obj) {
994
963
  }
995
964
  void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
996
965
  for (auto *obj : App.get_dates()) {
997
- if (obj->get_object_id() != match.id)
966
+ if (!match.id_equals(obj->get_object_id()))
998
967
  continue;
999
- if (request->method() == HTTP_GET && match.method.empty()) {
1000
- auto detail = DETAIL_STATE;
1001
- auto *param = request->getParam("detail");
1002
- if (param && param->value() == "all") {
1003
- detail = DETAIL_ALL;
1004
- }
968
+ if (request->method() == HTTP_GET && match.method_empty()) {
969
+ auto detail = get_request_detail(request);
1005
970
  std::string data = this->date_json(obj, detail);
1006
971
  request->send(200, "application/json", data.c_str());
1007
972
  return;
1008
973
  }
1009
- if (match.method != "set") {
974
+ if (!match.method_equals("set")) {
1010
975
  request->send(404);
1011
976
  return;
1012
977
  }
@@ -1023,7 +988,7 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
1023
988
  call.set_date(value);
1024
989
  }
1025
990
 
1026
- this->schedule_([call]() mutable { call.perform(); });
991
+ this->defer([call]() mutable { call.perform(); });
1027
992
  request->send(200);
1028
993
  return;
1029
994
  }
@@ -1043,12 +1008,7 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con
1043
1008
  root["value"] = value;
1044
1009
  root["state"] = value;
1045
1010
  if (start_config == DETAIL_ALL) {
1046
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1047
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1048
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1049
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1050
- }
1051
- }
1011
+ this->add_sorting_info_(root, obj);
1052
1012
  }
1053
1013
  });
1054
1014
  }
@@ -1062,19 +1022,15 @@ void WebServer::on_time_update(datetime::TimeEntity *obj) {
1062
1022
  }
1063
1023
  void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1064
1024
  for (auto *obj : App.get_times()) {
1065
- if (obj->get_object_id() != match.id)
1025
+ if (!match.id_equals(obj->get_object_id()))
1066
1026
  continue;
1067
- if (request->method() == HTTP_GET && match.method.empty()) {
1068
- auto detail = DETAIL_STATE;
1069
- auto *param = request->getParam("detail");
1070
- if (param && param->value() == "all") {
1071
- detail = DETAIL_ALL;
1072
- }
1027
+ if (request->method() == HTTP_GET && match.method_empty()) {
1028
+ auto detail = get_request_detail(request);
1073
1029
  std::string data = this->time_json(obj, detail);
1074
1030
  request->send(200, "application/json", data.c_str());
1075
1031
  return;
1076
1032
  }
1077
- if (match.method != "set") {
1033
+ if (!match.method_equals("set")) {
1078
1034
  request->send(404);
1079
1035
  return;
1080
1036
  }
@@ -1091,7 +1047,7 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
1091
1047
  call.set_time(value);
1092
1048
  }
1093
1049
 
1094
- this->schedule_([call]() mutable { call.perform(); });
1050
+ this->defer([call]() mutable { call.perform(); });
1095
1051
  request->send(200);
1096
1052
  return;
1097
1053
  }
@@ -1110,12 +1066,7 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con
1110
1066
  root["value"] = value;
1111
1067
  root["state"] = value;
1112
1068
  if (start_config == DETAIL_ALL) {
1113
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1114
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1115
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1116
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1117
- }
1118
- }
1069
+ this->add_sorting_info_(root, obj);
1119
1070
  }
1120
1071
  });
1121
1072
  }
@@ -1129,19 +1080,15 @@ void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) {
1129
1080
  }
1130
1081
  void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1131
1082
  for (auto *obj : App.get_datetimes()) {
1132
- if (obj->get_object_id() != match.id)
1083
+ if (!match.id_equals(obj->get_object_id()))
1133
1084
  continue;
1134
- if (request->method() == HTTP_GET && match.method.empty()) {
1135
- auto detail = DETAIL_STATE;
1136
- auto *param = request->getParam("detail");
1137
- if (param && param->value() == "all") {
1138
- detail = DETAIL_ALL;
1139
- }
1085
+ if (request->method() == HTTP_GET && match.method_empty()) {
1086
+ auto detail = get_request_detail(request);
1140
1087
  std::string data = this->datetime_json(obj, detail);
1141
1088
  request->send(200, "application/json", data.c_str());
1142
1089
  return;
1143
1090
  }
1144
- if (match.method != "set") {
1091
+ if (!match.method_equals("set")) {
1145
1092
  request->send(404);
1146
1093
  return;
1147
1094
  }
@@ -1158,7 +1105,7 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
1158
1105
  call.set_datetime(value);
1159
1106
  }
1160
1107
 
1161
- this->schedule_([call]() mutable { call.perform(); });
1108
+ this->defer([call]() mutable { call.perform(); });
1162
1109
  request->send(200);
1163
1110
  return;
1164
1111
  }
@@ -1178,12 +1125,7 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s
1178
1125
  root["value"] = value;
1179
1126
  root["state"] = value;
1180
1127
  if (start_config == DETAIL_ALL) {
1181
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1182
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1183
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1184
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1185
- }
1186
- }
1128
+ this->add_sorting_info_(root, obj);
1187
1129
  }
1188
1130
  });
1189
1131
  }
@@ -1197,20 +1139,16 @@ void WebServer::on_text_update(text::Text *obj, const std::string &state) {
1197
1139
  }
1198
1140
  void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1199
1141
  for (auto *obj : App.get_texts()) {
1200
- if (obj->get_object_id() != match.id)
1142
+ if (!match.id_equals(obj->get_object_id()))
1201
1143
  continue;
1202
1144
 
1203
- if (request->method() == HTTP_GET && match.method.empty()) {
1204
- auto detail = DETAIL_STATE;
1205
- auto *param = request->getParam("detail");
1206
- if (param && param->value() == "all") {
1207
- detail = DETAIL_ALL;
1208
- }
1145
+ if (request->method() == HTTP_GET && match.method_empty()) {
1146
+ auto detail = get_request_detail(request);
1209
1147
  std::string data = this->text_json(obj, obj->state, detail);
1210
1148
  request->send(200, "application/json", data.c_str());
1211
1149
  return;
1212
1150
  }
1213
- if (match.method != "set") {
1151
+ if (!match.method_equals("set")) {
1214
1152
  request->send(404);
1215
1153
  return;
1216
1154
  }
@@ -1248,12 +1186,7 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
1248
1186
  root["value"] = value;
1249
1187
  if (start_config == DETAIL_ALL) {
1250
1188
  root["mode"] = (int) obj->traits.get_mode();
1251
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1252
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1253
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1254
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1255
- }
1256
- }
1189
+ this->add_sorting_info_(root, obj);
1257
1190
  }
1258
1191
  });
1259
1192
  }
@@ -1267,21 +1200,17 @@ void WebServer::on_select_update(select::Select *obj, const std::string &state,
1267
1200
  }
1268
1201
  void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1269
1202
  for (auto *obj : App.get_selects()) {
1270
- if (obj->get_object_id() != match.id)
1203
+ if (!match.id_equals(obj->get_object_id()))
1271
1204
  continue;
1272
1205
 
1273
- if (request->method() == HTTP_GET && match.method.empty()) {
1274
- auto detail = DETAIL_STATE;
1275
- auto *param = request->getParam("detail");
1276
- if (param && param->value() == "all") {
1277
- detail = DETAIL_ALL;
1278
- }
1206
+ if (request->method() == HTTP_GET && match.method_empty()) {
1207
+ auto detail = get_request_detail(request);
1279
1208
  std::string data = this->select_json(obj, obj->state, detail);
1280
1209
  request->send(200, "application/json", data.c_str());
1281
1210
  return;
1282
1211
  }
1283
1212
 
1284
- if (match.method != "set") {
1213
+ if (!match.method_equals("set")) {
1285
1214
  request->send(404);
1286
1215
  return;
1287
1216
  }
@@ -1293,7 +1222,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
1293
1222
  call.set_option(option.c_str()); // NOLINT
1294
1223
  }
1295
1224
 
1296
- this->schedule_([call]() mutable { call.perform(); });
1225
+ this->defer([call]() mutable { call.perform(); });
1297
1226
  request->send(200);
1298
1227
  return;
1299
1228
  }
@@ -1309,16 +1238,11 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
1309
1238
  return json::build_json([this, obj, value, start_config](JsonObject root) {
1310
1239
  set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
1311
1240
  if (start_config == DETAIL_ALL) {
1312
- JsonArray opt = root.createNestedArray("option");
1241
+ JsonArray opt = root["option"].to<JsonArray>();
1313
1242
  for (auto &option : obj->traits.get_options()) {
1314
1243
  opt.add(option);
1315
1244
  }
1316
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1317
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1318
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1319
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1320
- }
1321
- }
1245
+ this->add_sorting_info_(root, obj);
1322
1246
  }
1323
1247
  });
1324
1248
  }
@@ -1335,21 +1259,17 @@ void WebServer::on_climate_update(climate::Climate *obj) {
1335
1259
  }
1336
1260
  void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1337
1261
  for (auto *obj : App.get_climates()) {
1338
- if (obj->get_object_id() != match.id)
1262
+ if (!match.id_equals(obj->get_object_id()))
1339
1263
  continue;
1340
1264
 
1341
- if (request->method() == HTTP_GET && match.method.empty()) {
1342
- auto detail = DETAIL_STATE;
1343
- auto *param = request->getParam("detail");
1344
- if (param && param->value() == "all") {
1345
- detail = DETAIL_ALL;
1346
- }
1265
+ if (request->method() == HTTP_GET && match.method_empty()) {
1266
+ auto detail = get_request_detail(request);
1347
1267
  std::string data = this->climate_json(obj, detail);
1348
1268
  request->send(200, "application/json", data.c_str());
1349
1269
  return;
1350
1270
  }
1351
1271
 
1352
- if (match.method != "set") {
1272
+ if (!match.method_equals("set")) {
1353
1273
  request->send(404);
1354
1274
  return;
1355
1275
  }
@@ -1389,7 +1309,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
1389
1309
  call.set_target_temperature(*target_temperature);
1390
1310
  }
1391
1311
 
1392
- this->schedule_([call]() mutable { call.perform(); });
1312
+ this->defer([call]() mutable { call.perform(); });
1393
1313
  request->send(200);
1394
1314
  return;
1395
1315
  }
@@ -1402,6 +1322,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s
1402
1322
  return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
1403
1323
  }
1404
1324
  std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
1325
+ // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1405
1326
  return json::build_json([this, obj, start_config](JsonObject root) {
1406
1327
  set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
1407
1328
  const auto traits = obj->get_traits();
@@ -1410,41 +1331,36 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
1410
1331
  char buf[16];
1411
1332
 
1412
1333
  if (start_config == DETAIL_ALL) {
1413
- JsonArray opt = root.createNestedArray("modes");
1334
+ JsonArray opt = root["modes"].to<JsonArray>();
1414
1335
  for (climate::ClimateMode m : traits.get_supported_modes())
1415
1336
  opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
1416
1337
  if (!traits.get_supported_custom_fan_modes().empty()) {
1417
- JsonArray opt = root.createNestedArray("fan_modes");
1338
+ JsonArray opt = root["fan_modes"].to<JsonArray>();
1418
1339
  for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
1419
1340
  opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
1420
1341
  }
1421
1342
 
1422
1343
  if (!traits.get_supported_custom_fan_modes().empty()) {
1423
- JsonArray opt = root.createNestedArray("custom_fan_modes");
1344
+ JsonArray opt = root["custom_fan_modes"].to<JsonArray>();
1424
1345
  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
1425
1346
  opt.add(custom_fan_mode);
1426
1347
  }
1427
1348
  if (traits.get_supports_swing_modes()) {
1428
- JsonArray opt = root.createNestedArray("swing_modes");
1349
+ JsonArray opt = root["swing_modes"].to<JsonArray>();
1429
1350
  for (auto swing_mode : traits.get_supported_swing_modes())
1430
1351
  opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
1431
1352
  }
1432
1353
  if (traits.get_supports_presets() && obj->preset.has_value()) {
1433
- JsonArray opt = root.createNestedArray("presets");
1354
+ JsonArray opt = root["presets"].to<JsonArray>();
1434
1355
  for (climate::ClimatePreset m : traits.get_supported_presets())
1435
1356
  opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
1436
1357
  }
1437
1358
  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1438
- JsonArray opt = root.createNestedArray("custom_presets");
1359
+ JsonArray opt = root["custom_presets"].to<JsonArray>();
1439
1360
  for (auto const &custom_preset : traits.get_supported_custom_presets())
1440
1361
  opt.add(custom_preset);
1441
1362
  }
1442
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1443
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1444
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1445
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1446
- }
1447
- }
1363
+ this->add_sorting_info_(root, obj);
1448
1364
  }
1449
1365
 
1450
1366
  bool has_state = false;
@@ -1492,6 +1408,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
1492
1408
  root["state"] = root["target_temperature"];
1493
1409
  }
1494
1410
  });
1411
+ // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
1495
1412
  }
1496
1413
  #endif
1497
1414
 
@@ -1503,25 +1420,21 @@ void WebServer::on_lock_update(lock::Lock *obj) {
1503
1420
  }
1504
1421
  void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1505
1422
  for (lock::Lock *obj : App.get_locks()) {
1506
- if (obj->get_object_id() != match.id)
1423
+ if (!match.id_equals(obj->get_object_id()))
1507
1424
  continue;
1508
1425
 
1509
- if (request->method() == HTTP_GET && match.method.empty()) {
1510
- auto detail = DETAIL_STATE;
1511
- auto *param = request->getParam("detail");
1512
- if (param && param->value() == "all") {
1513
- detail = DETAIL_ALL;
1514
- }
1426
+ if (request->method() == HTTP_GET && match.method_empty()) {
1427
+ auto detail = get_request_detail(request);
1515
1428
  std::string data = this->lock_json(obj, obj->state, detail);
1516
1429
  request->send(200, "application/json", data.c_str());
1517
- } else if (match.method == "lock") {
1518
- this->schedule_([obj]() { obj->lock(); });
1430
+ } else if (match.method_equals("lock")) {
1431
+ this->defer([obj]() { obj->lock(); });
1519
1432
  request->send(200);
1520
- } else if (match.method == "unlock") {
1521
- this->schedule_([obj]() { obj->unlock(); });
1433
+ } else if (match.method_equals("unlock")) {
1434
+ this->defer([obj]() { obj->unlock(); });
1522
1435
  request->send(200);
1523
- } else if (match.method == "open") {
1524
- this->schedule_([obj]() { obj->open(); });
1436
+ } else if (match.method_equals("open")) {
1437
+ this->defer([obj]() { obj->open(); });
1525
1438
  request->send(200);
1526
1439
  } else {
1527
1440
  request->send(404);
@@ -1541,12 +1454,7 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet
1541
1454
  set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
1542
1455
  start_config);
1543
1456
  if (start_config == DETAIL_ALL) {
1544
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1545
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1546
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1547
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1548
- }
1549
- }
1457
+ this->add_sorting_info_(root, obj);
1550
1458
  }
1551
1459
  });
1552
1460
  }
@@ -1560,30 +1468,26 @@ void WebServer::on_valve_update(valve::Valve *obj) {
1560
1468
  }
1561
1469
  void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1562
1470
  for (valve::Valve *obj : App.get_valves()) {
1563
- if (obj->get_object_id() != match.id)
1471
+ if (!match.id_equals(obj->get_object_id()))
1564
1472
  continue;
1565
1473
 
1566
- if (request->method() == HTTP_GET && match.method.empty()) {
1567
- auto detail = DETAIL_STATE;
1568
- auto *param = request->getParam("detail");
1569
- if (param && param->value() == "all") {
1570
- detail = DETAIL_ALL;
1571
- }
1474
+ if (request->method() == HTTP_GET && match.method_empty()) {
1475
+ auto detail = get_request_detail(request);
1572
1476
  std::string data = this->valve_json(obj, detail);
1573
1477
  request->send(200, "application/json", data.c_str());
1574
1478
  return;
1575
1479
  }
1576
1480
 
1577
1481
  auto call = obj->make_call();
1578
- if (match.method == "open") {
1482
+ if (match.method_equals("open")) {
1579
1483
  call.set_command_open();
1580
- } else if (match.method == "close") {
1484
+ } else if (match.method_equals("close")) {
1581
1485
  call.set_command_close();
1582
- } else if (match.method == "stop") {
1486
+ } else if (match.method_equals("stop")) {
1583
1487
  call.set_command_stop();
1584
- } else if (match.method == "toggle") {
1488
+ } else if (match.method_equals("toggle")) {
1585
1489
  call.set_command_toggle();
1586
- } else if (match.method != "set") {
1490
+ } else if (!match.method_equals("set")) {
1587
1491
  request->send(404);
1588
1492
  return;
1589
1493
  }
@@ -1601,7 +1505,7 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
1601
1505
  }
1602
1506
  }
1603
1507
 
1604
- this->schedule_([call]() mutable { call.perform(); });
1508
+ this->defer([call]() mutable { call.perform(); });
1605
1509
  request->send(200);
1606
1510
  return;
1607
1511
  }
@@ -1622,12 +1526,7 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
1622
1526
  if (obj->get_traits().get_supports_position())
1623
1527
  root["position"] = obj->position;
1624
1528
  if (start_config == DETAIL_ALL) {
1625
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1626
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1627
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1628
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1629
- }
1630
- }
1529
+ this->add_sorting_info_(root, obj);
1631
1530
  }
1632
1531
  });
1633
1532
  }
@@ -1641,15 +1540,11 @@ void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP
1641
1540
  }
1642
1541
  void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1643
1542
  for (alarm_control_panel::AlarmControlPanel *obj : App.get_alarm_control_panels()) {
1644
- if (obj->get_object_id() != match.id)
1543
+ if (!match.id_equals(obj->get_object_id()))
1645
1544
  continue;
1646
1545
 
1647
- if (request->method() == HTTP_GET && match.method.empty()) {
1648
- auto detail = DETAIL_STATE;
1649
- auto *param = request->getParam("detail");
1650
- if (param && param->value() == "all") {
1651
- detail = DETAIL_ALL;
1652
- }
1546
+ if (request->method() == HTTP_GET && match.method_empty()) {
1547
+ auto detail = get_request_detail(request);
1653
1548
  std::string data = this->alarm_control_panel_json(obj, obj->get_state(), detail);
1654
1549
  request->send(200, "application/json", data.c_str());
1655
1550
  return;
@@ -1660,22 +1555,22 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
1660
1555
  call.set_code(request->getParam("code")->value().c_str()); // NOLINT
1661
1556
  }
1662
1557
 
1663
- if (match.method == "disarm") {
1558
+ if (match.method_equals("disarm")) {
1664
1559
  call.disarm();
1665
- } else if (match.method == "arm_away") {
1560
+ } else if (match.method_equals("arm_away")) {
1666
1561
  call.arm_away();
1667
- } else if (match.method == "arm_home") {
1562
+ } else if (match.method_equals("arm_home")) {
1668
1563
  call.arm_home();
1669
- } else if (match.method == "arm_night") {
1564
+ } else if (match.method_equals("arm_night")) {
1670
1565
  call.arm_night();
1671
- } else if (match.method == "arm_vacation") {
1566
+ } else if (match.method_equals("arm_vacation")) {
1672
1567
  call.arm_vacation();
1673
1568
  } else {
1674
1569
  request->send(404);
1675
1570
  return;
1676
1571
  }
1677
1572
 
1678
- this->schedule_([call]() mutable { call.perform(); });
1573
+ this->defer([call]() mutable { call.perform(); });
1679
1574
  request->send(200);
1680
1575
  return;
1681
1576
  }
@@ -1699,12 +1594,7 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro
1699
1594
  set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
1700
1595
  PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
1701
1596
  if (start_config == DETAIL_ALL) {
1702
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1703
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1704
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1705
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1706
- }
1707
- }
1597
+ this->add_sorting_info_(root, obj);
1708
1598
  }
1709
1599
  });
1710
1600
  }
@@ -1717,15 +1607,11 @@ void WebServer::on_event(event::Event *obj, const std::string &event_type) {
1717
1607
 
1718
1608
  void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1719
1609
  for (event::Event *obj : App.get_events()) {
1720
- if (obj->get_object_id() != match.id)
1610
+ if (!match.id_equals(obj->get_object_id()))
1721
1611
  continue;
1722
1612
 
1723
- if (request->method() == HTTP_GET && match.method.empty()) {
1724
- auto detail = DETAIL_STATE;
1725
- auto *param = request->getParam("detail");
1726
- if (param && param->value() == "all") {
1727
- detail = DETAIL_ALL;
1728
- }
1613
+ if (request->method() == HTTP_GET && match.method_empty()) {
1614
+ auto detail = get_request_detail(request);
1729
1615
  std::string data = this->event_json(obj, "", detail);
1730
1616
  request->send(200, "application/json", data.c_str());
1731
1617
  return;
@@ -1734,12 +1620,15 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa
1734
1620
  request->send(404);
1735
1621
  }
1736
1622
 
1623
+ static std::string get_event_type(event::Event *event) { return event->last_event_type ? *event->last_event_type : ""; }
1624
+
1737
1625
  std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) {
1738
- return web_server->event_json((event::Event *) (source), *(((event::Event *) (source))->last_event_type),
1739
- DETAIL_STATE);
1626
+ auto *event = static_cast<event::Event *>(source);
1627
+ return web_server->event_json(event, get_event_type(event), DETAIL_STATE);
1740
1628
  }
1741
1629
  std::string WebServer::event_all_json_generator(WebServer *web_server, void *source) {
1742
- return web_server->event_json((event::Event *) (source), *(((event::Event *) (source))->last_event_type), DETAIL_ALL);
1630
+ auto *event = static_cast<event::Event *>(source);
1631
+ return web_server->event_json(event, get_event_type(event), DETAIL_ALL);
1743
1632
  }
1744
1633
  std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) {
1745
1634
  return json::build_json([this, obj, event_type, start_config](JsonObject root) {
@@ -1748,17 +1637,12 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
1748
1637
  root["event_type"] = event_type;
1749
1638
  }
1750
1639
  if (start_config == DETAIL_ALL) {
1751
- JsonArray event_types = root.createNestedArray("event_types");
1640
+ JsonArray event_types = root["event_types"].to<JsonArray>();
1752
1641
  for (auto const &event_type : obj->get_event_types()) {
1753
1642
  event_types.add(event_type);
1754
1643
  }
1755
1644
  root["device_class"] = obj->get_device_class();
1756
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1757
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1758
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1759
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1760
- }
1761
- }
1645
+ this->add_sorting_info_(root, obj);
1762
1646
  }
1763
1647
  });
1764
1648
  }
@@ -1772,26 +1656,22 @@ void WebServer::on_update(update::UpdateEntity *obj) {
1772
1656
  }
1773
1657
  void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match) {
1774
1658
  for (update::UpdateEntity *obj : App.get_updates()) {
1775
- if (obj->get_object_id() != match.id)
1659
+ if (!match.id_equals(obj->get_object_id()))
1776
1660
  continue;
1777
1661
 
1778
- if (request->method() == HTTP_GET && match.method.empty()) {
1779
- auto detail = DETAIL_STATE;
1780
- auto *param = request->getParam("detail");
1781
- if (param && param->value() == "all") {
1782
- detail = DETAIL_ALL;
1783
- }
1662
+ if (request->method() == HTTP_GET && match.method_empty()) {
1663
+ auto detail = get_request_detail(request);
1784
1664
  std::string data = this->update_json(obj, detail);
1785
1665
  request->send(200, "application/json", data.c_str());
1786
1666
  return;
1787
1667
  }
1788
1668
 
1789
- if (match.method != "install") {
1669
+ if (!match.method_equals("install")) {
1790
1670
  request->send(404);
1791
1671
  return;
1792
1672
  }
1793
1673
 
1794
- this->schedule_([obj]() mutable { obj->perform(); });
1674
+ this->defer([obj]() mutable { obj->perform(); });
1795
1675
  request->send(200);
1796
1676
  return;
1797
1677
  }
@@ -1804,6 +1684,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so
1804
1684
  return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1805
1685
  }
1806
1686
  std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
1687
+ // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1807
1688
  return json::build_json([this, obj, start_config](JsonObject root) {
1808
1689
  set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
1809
1690
  root["value"] = obj->update_info.latest_version;
@@ -1826,18 +1707,14 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
1826
1707
  root["title"] = obj->update_info.title;
1827
1708
  root["summary"] = obj->update_info.summary;
1828
1709
  root["release_url"] = obj->update_info.release_url;
1829
- if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) {
1830
- root["sorting_weight"] = this->sorting_entitys_[obj].weight;
1831
- if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) {
1832
- root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name;
1833
- }
1834
- }
1710
+ this->add_sorting_info_(root, obj);
1835
1711
  }
1836
1712
  });
1713
+ // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
1837
1714
  }
1838
1715
  #endif
1839
1716
 
1840
- bool WebServer::canHandle(AsyncWebServerRequest *request) {
1717
+ bool WebServer::canHandle(AsyncWebServerRequest *request) const {
1841
1718
  if (request->url() == "/")
1842
1719
  return true;
1843
1720
 
@@ -1859,116 +1736,114 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
1859
1736
 
1860
1737
  #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
1861
1738
  if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
1862
- #ifdef USE_ARDUINO
1863
- // Header needs to be added to interesting header list for it to not be
1864
- // nuked by the time we handle the request later.
1865
- // Only required in Arduino framework.
1866
- request->addInterestingHeader(HEADER_CORS_REQ_PNA);
1867
- #endif
1868
1739
  return true;
1869
1740
  }
1870
1741
  #endif
1871
1742
 
1872
- UrlMatch match = match_url(request->url().c_str(), true); // NOLINT
1743
+ // Store the URL to prevent temporary string destruction
1744
+ // request->url() returns a reference to a String (on Arduino) or std::string (on ESP-IDF)
1745
+ // UrlMatch stores pointers to the string's data, so we must ensure the string outlives match_url()
1746
+ const auto &url = request->url();
1747
+ UrlMatch match = match_url(url.c_str(), url.length(), true);
1873
1748
  if (!match.valid)
1874
1749
  return false;
1875
1750
  #ifdef USE_SENSOR
1876
- if (request->method() == HTTP_GET && match.domain == "sensor")
1751
+ if (request->method() == HTTP_GET && match.domain_equals("sensor"))
1877
1752
  return true;
1878
1753
  #endif
1879
1754
 
1880
1755
  #ifdef USE_SWITCH
1881
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "switch")
1756
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("switch"))
1882
1757
  return true;
1883
1758
  #endif
1884
1759
 
1885
1760
  #ifdef USE_BUTTON
1886
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "button")
1761
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("button"))
1887
1762
  return true;
1888
1763
  #endif
1889
1764
 
1890
1765
  #ifdef USE_BINARY_SENSOR
1891
- if (request->method() == HTTP_GET && match.domain == "binary_sensor")
1766
+ if (request->method() == HTTP_GET && match.domain_equals("binary_sensor"))
1892
1767
  return true;
1893
1768
  #endif
1894
1769
 
1895
1770
  #ifdef USE_FAN
1896
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "fan")
1771
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("fan"))
1897
1772
  return true;
1898
1773
  #endif
1899
1774
 
1900
1775
  #ifdef USE_LIGHT
1901
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "light")
1776
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("light"))
1902
1777
  return true;
1903
1778
  #endif
1904
1779
 
1905
1780
  #ifdef USE_TEXT_SENSOR
1906
- if (request->method() == HTTP_GET && match.domain == "text_sensor")
1781
+ if (request->method() == HTTP_GET && match.domain_equals("text_sensor"))
1907
1782
  return true;
1908
1783
  #endif
1909
1784
 
1910
1785
  #ifdef USE_COVER
1911
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "cover")
1786
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("cover"))
1912
1787
  return true;
1913
1788
  #endif
1914
1789
 
1915
1790
  #ifdef USE_NUMBER
1916
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "number")
1791
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("number"))
1917
1792
  return true;
1918
1793
  #endif
1919
1794
 
1920
1795
  #ifdef USE_DATETIME_DATE
1921
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "date")
1796
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("date"))
1922
1797
  return true;
1923
1798
  #endif
1924
1799
 
1925
1800
  #ifdef USE_DATETIME_TIME
1926
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "time")
1801
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("time"))
1927
1802
  return true;
1928
1803
  #endif
1929
1804
 
1930
1805
  #ifdef USE_DATETIME_DATETIME
1931
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "datetime")
1806
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("datetime"))
1932
1807
  return true;
1933
1808
  #endif
1934
1809
 
1935
1810
  #ifdef USE_TEXT
1936
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "text")
1811
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("text"))
1937
1812
  return true;
1938
1813
  #endif
1939
1814
 
1940
1815
  #ifdef USE_SELECT
1941
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "select")
1816
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("select"))
1942
1817
  return true;
1943
1818
  #endif
1944
1819
 
1945
1820
  #ifdef USE_CLIMATE
1946
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "climate")
1821
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("climate"))
1947
1822
  return true;
1948
1823
  #endif
1949
1824
 
1950
1825
  #ifdef USE_LOCK
1951
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "lock")
1826
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("lock"))
1952
1827
  return true;
1953
1828
  #endif
1954
1829
 
1955
1830
  #ifdef USE_VALVE
1956
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "valve")
1831
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("valve"))
1957
1832
  return true;
1958
1833
  #endif
1959
1834
 
1960
1835
  #ifdef USE_ALARM_CONTROL_PANEL
1961
- if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.domain == "alarm_control_panel")
1836
+ if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.domain_equals("alarm_control_panel"))
1962
1837
  return true;
1963
1838
  #endif
1964
1839
 
1965
1840
  #ifdef USE_EVENT
1966
- if (request->method() == HTTP_GET && match.domain == "event")
1841
+ if (request->method() == HTTP_GET && match.domain_equals("event"))
1967
1842
  return true;
1968
1843
  #endif
1969
1844
 
1970
1845
  #ifdef USE_UPDATE
1971
- if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "update")
1846
+ if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("update"))
1972
1847
  return true;
1973
1848
  #endif
1974
1849
 
@@ -2008,114 +1883,117 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
2008
1883
  }
2009
1884
  #endif
2010
1885
 
2011
- UrlMatch match = match_url(request->url().c_str()); // NOLINT
1886
+ // See comment in canHandle() for why we store the URL reference
1887
+ const auto &url = request->url();
1888
+ UrlMatch match = match_url(url.c_str(), url.length(), false);
1889
+
2012
1890
  #ifdef USE_SENSOR
2013
- if (match.domain == "sensor") {
1891
+ if (match.domain_equals("sensor")) {
2014
1892
  this->handle_sensor_request(request, match);
2015
1893
  return;
2016
1894
  }
2017
1895
  #endif
2018
1896
 
2019
1897
  #ifdef USE_SWITCH
2020
- if (match.domain == "switch") {
1898
+ if (match.domain_equals("switch")) {
2021
1899
  this->handle_switch_request(request, match);
2022
1900
  return;
2023
1901
  }
2024
1902
  #endif
2025
1903
 
2026
1904
  #ifdef USE_BUTTON
2027
- if (match.domain == "button") {
1905
+ if (match.domain_equals("button")) {
2028
1906
  this->handle_button_request(request, match);
2029
1907
  return;
2030
1908
  }
2031
1909
  #endif
2032
1910
 
2033
1911
  #ifdef USE_BINARY_SENSOR
2034
- if (match.domain == "binary_sensor") {
1912
+ if (match.domain_equals("binary_sensor")) {
2035
1913
  this->handle_binary_sensor_request(request, match);
2036
1914
  return;
2037
1915
  }
2038
1916
  #endif
2039
1917
 
2040
1918
  #ifdef USE_FAN
2041
- if (match.domain == "fan") {
1919
+ if (match.domain_equals("fan")) {
2042
1920
  this->handle_fan_request(request, match);
2043
1921
  return;
2044
1922
  }
2045
1923
  #endif
2046
1924
 
2047
1925
  #ifdef USE_LIGHT
2048
- if (match.domain == "light") {
1926
+ if (match.domain_equals("light")) {
2049
1927
  this->handle_light_request(request, match);
2050
1928
  return;
2051
1929
  }
2052
1930
  #endif
2053
1931
 
2054
1932
  #ifdef USE_TEXT_SENSOR
2055
- if (match.domain == "text_sensor") {
1933
+ if (match.domain_equals("text_sensor")) {
2056
1934
  this->handle_text_sensor_request(request, match);
2057
1935
  return;
2058
1936
  }
2059
1937
  #endif
2060
1938
 
2061
1939
  #ifdef USE_COVER
2062
- if (match.domain == "cover") {
1940
+ if (match.domain_equals("cover")) {
2063
1941
  this->handle_cover_request(request, match);
2064
1942
  return;
2065
1943
  }
2066
1944
  #endif
2067
1945
 
2068
1946
  #ifdef USE_NUMBER
2069
- if (match.domain == "number") {
1947
+ if (match.domain_equals("number")) {
2070
1948
  this->handle_number_request(request, match);
2071
1949
  return;
2072
1950
  }
2073
1951
  #endif
2074
1952
 
2075
1953
  #ifdef USE_DATETIME_DATE
2076
- if (match.domain == "date") {
1954
+ if (match.domain_equals("date")) {
2077
1955
  this->handle_date_request(request, match);
2078
1956
  return;
2079
1957
  }
2080
1958
  #endif
2081
1959
 
2082
1960
  #ifdef USE_DATETIME_TIME
2083
- if (match.domain == "time") {
1961
+ if (match.domain_equals("time")) {
2084
1962
  this->handle_time_request(request, match);
2085
1963
  return;
2086
1964
  }
2087
1965
  #endif
2088
1966
 
2089
1967
  #ifdef USE_DATETIME_DATETIME
2090
- if (match.domain == "datetime") {
1968
+ if (match.domain_equals("datetime")) {
2091
1969
  this->handle_datetime_request(request, match);
2092
1970
  return;
2093
1971
  }
2094
1972
  #endif
2095
1973
 
2096
1974
  #ifdef USE_TEXT
2097
- if (match.domain == "text") {
1975
+ if (match.domain_equals("text")) {
2098
1976
  this->handle_text_request(request, match);
2099
1977
  return;
2100
1978
  }
2101
1979
  #endif
2102
1980
 
2103
1981
  #ifdef USE_SELECT
2104
- if (match.domain == "select") {
1982
+ if (match.domain_equals("select")) {
2105
1983
  this->handle_select_request(request, match);
2106
1984
  return;
2107
1985
  }
2108
1986
  #endif
2109
1987
 
2110
1988
  #ifdef USE_CLIMATE
2111
- if (match.domain == "climate") {
1989
+ if (match.domain_equals("climate")) {
2112
1990
  this->handle_climate_request(request, match);
2113
1991
  return;
2114
1992
  }
2115
1993
  #endif
2116
1994
 
2117
1995
  #ifdef USE_LOCK
2118
- if (match.domain == "lock") {
1996
+ if (match.domain_equals("lock")) {
2119
1997
  this->handle_lock_request(request, match);
2120
1998
 
2121
1999
  return;
@@ -2123,14 +2001,14 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
2123
2001
  #endif
2124
2002
 
2125
2003
  #ifdef USE_VALVE
2126
- if (match.domain == "valve") {
2004
+ if (match.domain_equals("valve")) {
2127
2005
  this->handle_valve_request(request, match);
2128
2006
  return;
2129
2007
  }
2130
2008
  #endif
2131
2009
 
2132
2010
  #ifdef USE_ALARM_CONTROL_PANEL
2133
- if (match.domain == "alarm_control_panel") {
2011
+ if (match.domain_equals("alarm_control_panel")) {
2134
2012
  this->handle_alarm_control_panel_request(request, match);
2135
2013
 
2136
2014
  return;
@@ -2138,15 +2016,31 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
2138
2016
  #endif
2139
2017
 
2140
2018
  #ifdef USE_UPDATE
2141
- if (match.domain == "update") {
2019
+ if (match.domain_equals("update")) {
2142
2020
  this->handle_update_request(request, match);
2143
2021
  return;
2144
2022
  }
2145
2023
  #endif
2024
+
2025
+ // No matching handler found - send 404
2026
+ ESP_LOGV(TAG, "Request for unknown URL: %s", request->url().c_str());
2027
+ request->send(404, "text/plain", "Not Found");
2146
2028
  }
2147
2029
 
2148
- bool WebServer::isRequestHandlerTrivial() { return false; }
2030
+ bool WebServer::isRequestHandlerTrivial() const { return false; }
2149
2031
 
2032
+ void WebServer::add_sorting_info_(JsonObject &root, EntityBase *entity) {
2033
+ #ifdef USE_WEBSERVER_SORTING
2034
+ if (this->sorting_entitys_.find(entity) != this->sorting_entitys_.end()) {
2035
+ root["sorting_weight"] = this->sorting_entitys_[entity].weight;
2036
+ if (this->sorting_groups_.find(this->sorting_entitys_[entity].group_id) != this->sorting_groups_.end()) {
2037
+ root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[entity].group_id].name;
2038
+ }
2039
+ }
2040
+ #endif
2041
+ }
2042
+
2043
+ #ifdef USE_WEBSERVER_SORTING
2150
2044
  void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) {
2151
2045
  this->sorting_entitys_[entity] = SortingComponents{weight, group};
2152
2046
  }
@@ -2154,16 +2048,7 @@ void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t gro
2154
2048
  void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_name, float weight) {
2155
2049
  this->sorting_groups_[group_id] = SortingGroup{group_name, weight};
2156
2050
  }
2157
-
2158
- void WebServer::schedule_(std::function<void()> &&f) {
2159
- #ifdef USE_ESP32
2160
- xSemaphoreTake(this->to_schedule_lock_, portMAX_DELAY);
2161
- to_schedule_.push_back(std::move(f));
2162
- xSemaphoreGive(this->to_schedule_lock_);
2163
- #else
2164
- this->defer(std::move(f));
2165
2051
  #endif
2166
- }
2167
2052
 
2168
2053
  } // namespace web_server
2169
2054
  } // namespace esphome