esphome 2025.9.2__py3-none-any.whl → 2025.10.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. esphome/__main__.py +87 -31
  2. esphome/address_cache.py +142 -0
  3. esphome/automation.py +130 -32
  4. esphome/build_gen/platformio.py +1 -3
  5. esphome/codegen.py +1 -0
  6. esphome/components/animation/animation.cpp +2 -2
  7. esphome/components/api/__init__.py +167 -3
  8. esphome/components/api/api_connection.cpp +84 -41
  9. esphome/components/api/api_connection.h +22 -16
  10. esphome/components/api/api_frame_helper.cpp +33 -19
  11. esphome/components/api/api_frame_helper.h +19 -4
  12. esphome/components/api/api_frame_helper_noise.cpp +41 -53
  13. esphome/components/api/api_frame_helper_noise.h +1 -1
  14. esphome/components/api/api_frame_helper_plaintext.cpp +22 -31
  15. esphome/components/api/api_frame_helper_plaintext.h +1 -1
  16. esphome/components/api/api_pb2.cpp +189 -15
  17. esphome/components/api/api_pb2.h +132 -20
  18. esphome/components/api/api_pb2_dump.cpp +97 -9
  19. esphome/components/api/api_pb2_service.cpp +118 -160
  20. esphome/components/api/api_pb2_service.h +31 -3
  21. esphome/components/api/api_server.cpp +78 -11
  22. esphome/components/api/api_server.h +32 -4
  23. esphome/components/api/custom_api_device.h +8 -8
  24. esphome/components/api/homeassistant_service.h +123 -6
  25. esphome/components/api/proto.h +6 -2
  26. esphome/components/api/user_services.h +2 -2
  27. esphome/components/as7341/sensor.py +1 -1
  28. esphome/components/audio/__init__.py +1 -1
  29. esphome/components/audio/audio.cpp +1 -1
  30. esphome/components/audio/audio_decoder.cpp +9 -9
  31. esphome/components/bl0906/bl0906.cpp +2 -2
  32. esphome/components/bl0942/bl0942.cpp +2 -2
  33. esphome/components/ble_client/__init__.py +1 -1
  34. esphome/components/bluetooth_proxy/__init__.py +4 -30
  35. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +11 -4
  36. esphome/components/bluetooth_proxy/bluetooth_connection.h +2 -2
  37. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
  38. esphome/components/camera_encoder/__init__.py +2 -4
  39. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +4 -2
  40. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +3 -1
  41. esphome/components/canbus/canbus.cpp +7 -5
  42. esphome/components/canbus/canbus.h +4 -4
  43. esphome/components/captive_portal/__init__.py +18 -1
  44. esphome/components/captive_portal/captive_portal.cpp +40 -46
  45. esphome/components/captive_portal/captive_portal.h +20 -22
  46. esphome/components/captive_portal/dns_server_esp32_idf.cpp +205 -0
  47. esphome/components/captive_portal/dns_server_esp32_idf.h +27 -0
  48. esphome/components/ccs811/ccs811.cpp +1 -1
  49. esphome/components/climate/climate.cpp +10 -7
  50. esphome/components/cm1106/cm1106.cpp +1 -1
  51. esphome/components/copy/lock/copy_lock.cpp +1 -1
  52. esphome/components/cover/cover.cpp +1 -0
  53. esphome/components/daikin_arc/daikin_arc.cpp +19 -12
  54. esphome/components/deep_sleep/__init__.py +9 -2
  55. esphome/components/deep_sleep/deep_sleep_component.h +11 -9
  56. esphome/components/deep_sleep/deep_sleep_esp32.cpp +51 -27
  57. esphome/components/ektf2232/touchscreen/__init__.py +8 -5
  58. esphome/components/ektf2232/touchscreen/ektf2232.cpp +4 -4
  59. esphome/components/ektf2232/touchscreen/ektf2232.h +2 -2
  60. esphome/components/epaper_spi/__init__.py +1 -0
  61. esphome/components/epaper_spi/display.py +80 -0
  62. esphome/components/epaper_spi/epaper_spi.cpp +227 -0
  63. esphome/components/epaper_spi/epaper_spi.h +93 -0
  64. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp +42 -0
  65. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h +45 -0
  66. esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +135 -0
  67. esphome/components/epaper_spi/epaper_spi_spectra_e6.h +23 -0
  68. esphome/components/es7210/es7210.cpp +3 -3
  69. esphome/components/esp32/__init__.py +254 -339
  70. esphome/components/esp32/boards.py +81 -0
  71. esphome/components/esp32/preferences.cpp +23 -17
  72. esphome/components/esp32_ble/__init__.py +159 -44
  73. esphome/components/esp32_ble/ble.cpp +47 -3
  74. esphome/components/esp32_ble/ble.h +18 -0
  75. esphome/components/esp32_ble/ble_advertising.cpp +7 -3
  76. esphome/components/esp32_ble/ble_advertising.h +4 -0
  77. esphome/components/esp32_ble/ble_uuid.cpp +16 -42
  78. esphome/components/esp32_ble_beacon/__init__.py +3 -4
  79. esphome/components/esp32_ble_client/ble_client_base.cpp +14 -12
  80. esphome/components/esp32_ble_server/__init__.py +28 -14
  81. esphome/components/esp32_ble_server/ble_characteristic.cpp +67 -57
  82. esphome/components/esp32_ble_server/ble_characteristic.h +27 -16
  83. esphome/components/esp32_ble_server/ble_descriptor.cpp +4 -3
  84. esphome/components/esp32_ble_server/ble_descriptor.h +13 -9
  85. esphome/components/esp32_ble_server/ble_server.cpp +59 -24
  86. esphome/components/esp32_ble_server/ble_server.h +38 -20
  87. esphome/components/esp32_ble_server/ble_server_automations.cpp +49 -33
  88. esphome/components/esp32_ble_server/ble_server_automations.h +39 -24
  89. esphome/components/esp32_ble_tracker/__init__.py +25 -80
  90. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -4
  91. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +0 -3
  92. esphome/components/esp32_camera/__init__.py +1 -3
  93. esphome/components/esp32_can/esp32_can.cpp +22 -4
  94. esphome/components/esp32_can/esp32_can.h +3 -0
  95. esphome/components/esp32_hosted/__init__.py +2 -1
  96. esphome/components/esp32_improv/esp32_improv_component.cpp +102 -44
  97. esphome/components/esp32_improv/esp32_improv_component.h +6 -1
  98. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  99. esphome/components/esp8266/__init__.py +3 -3
  100. esphome/components/esphome/ota/__init__.py +21 -2
  101. esphome/components/esphome/ota/ota_esphome.cpp +455 -145
  102. esphome/components/esphome/ota/ota_esphome.h +49 -2
  103. esphome/components/ethernet/__init__.py +39 -22
  104. esphome/components/ethernet/ethernet_component.cpp +28 -5
  105. esphome/components/ethernet/ethernet_component.h +5 -1
  106. esphome/components/external_components/__init__.py +8 -6
  107. esphome/components/fingerprint_grow/fingerprint_grow.cpp +1 -1
  108. esphome/components/fingerprint_grow/fingerprint_grow.h +2 -1
  109. esphome/components/font/__init__.py +5 -5
  110. esphome/components/graph/graph.cpp +1 -1
  111. esphome/components/graphical_display_menu/graphical_display_menu.cpp +3 -2
  112. esphome/components/haier/hon_climate.cpp +2 -2
  113. esphome/components/haier/hon_climate.h +1 -1
  114. esphome/components/hdc1080/hdc1080.cpp +42 -34
  115. esphome/components/hdc1080/hdc1080.h +1 -3
  116. esphome/components/homeassistant/number/homeassistant_number.cpp +2 -2
  117. esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
  118. esphome/components/http_request/__init__.py +3 -3
  119. esphome/components/htu21d/htu21d.cpp +13 -18
  120. esphome/components/htu21d/htu21d.h +1 -1
  121. esphome/components/i2s_audio/__init__.py +1 -2
  122. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  123. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -2
  124. esphome/components/improv_serial/improv_serial_component.cpp +12 -15
  125. esphome/components/improv_serial/improv_serial_component.h +6 -8
  126. esphome/components/json/json_util.cpp +35 -43
  127. esphome/components/json/json_util.h +57 -0
  128. esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +2 -2
  129. esphome/components/key_collector/key_collector.h +4 -4
  130. esphome/components/libretiny/__init__.py +6 -6
  131. esphome/components/libretiny/preferences.cpp +23 -16
  132. esphome/components/light/light_call.cpp +98 -120
  133. esphome/components/light/light_call.h +17 -7
  134. esphome/components/lm75b/__init__.py +0 -0
  135. esphome/components/lm75b/lm75b.cpp +39 -0
  136. esphome/components/lm75b/lm75b.h +19 -0
  137. esphome/components/lm75b/sensor.py +34 -0
  138. esphome/components/lock/lock.h +12 -6
  139. esphome/components/logger/__init__.py +15 -27
  140. esphome/components/logger/logger.cpp +10 -20
  141. esphome/components/logger/logger.h +105 -62
  142. esphome/components/logger/logger_esp32.cpp +0 -48
  143. esphome/components/logger/logger_zephyr.cpp +2 -3
  144. esphome/components/logger/select/logger_level_select.cpp +6 -7
  145. esphome/components/logger/select/logger_level_select.h +7 -0
  146. esphome/components/ltr501/ltr501.cpp +7 -6
  147. esphome/components/ltr_als_ps/ltr_als_ps.cpp +7 -6
  148. esphome/components/matrix_keypad/matrix_keypad.h +4 -4
  149. esphome/components/max7219digit/max7219digit.cpp +1 -1
  150. esphome/components/mcp2515/mcp2515.cpp +31 -3
  151. esphome/components/mcp2515/mcp2515_defs.h +3 -1
  152. esphome/components/md5/md5.cpp +0 -26
  153. esphome/components/md5/md5.h +10 -20
  154. esphome/components/mdns/__init__.py +19 -6
  155. esphome/components/mdns/mdns_component.cpp +27 -59
  156. esphome/components/mdns/mdns_component.h +23 -10
  157. esphome/components/mdns/mdns_esp32.cpp +7 -7
  158. esphome/components/mdns/mdns_esp8266.cpp +6 -6
  159. esphome/components/mdns/mdns_libretiny.cpp +3 -3
  160. esphome/components/mdns/mdns_rp2040.cpp +3 -3
  161. esphome/components/mipi/__init__.py +1 -5
  162. esphome/components/mipi_spi/display.py +24 -8
  163. esphome/components/mipi_spi/mipi_spi.h +3 -3
  164. esphome/components/mixer/speaker/mixer_speaker.cpp +3 -3
  165. esphome/components/mmc5603/mmc5603.cpp +3 -3
  166. esphome/components/modbus/modbus.cpp +27 -13
  167. esphome/components/modbus/modbus.h +5 -3
  168. esphome/components/modbus/modbus_definitions.h +86 -0
  169. esphome/components/modbus_controller/__init__.py +29 -1
  170. esphome/components/modbus_controller/const.py +4 -0
  171. esphome/components/modbus_controller/modbus_controller.cpp +38 -13
  172. esphome/components/modbus_controller/modbus_controller.h +18 -29
  173. esphome/components/mpr121/mpr121.cpp +41 -42
  174. esphome/components/mpr121/mpr121.h +0 -1
  175. esphome/components/nau7802/nau7802.cpp +2 -2
  176. esphome/components/network/__init__.py +7 -3
  177. esphome/components/nextion/display.py +4 -4
  178. esphome/components/nextion/nextion.cpp +8 -8
  179. esphome/components/number/__init__.py +2 -0
  180. esphome/components/number/number_call.cpp +23 -12
  181. esphome/components/number/number_call.h +5 -0
  182. esphome/components/online_image/bmp_image.cpp +2 -1
  183. esphome/components/online_image/jpeg_image.cpp +4 -2
  184. esphome/components/openthread/openthread.cpp +6 -7
  185. esphome/components/openthread/openthread.h +0 -1
  186. esphome/components/ota/ota_backend.h +1 -0
  187. esphome/components/packages/__init__.py +10 -8
  188. esphome/components/packet_transport/packet_transport.cpp +2 -0
  189. esphome/components/pid/pid_controller.cpp +1 -1
  190. esphome/components/prometheus/prometheus_handler.cpp +239 -239
  191. esphome/components/psram/__init__.py +30 -28
  192. esphome/components/qmc5883l/qmc5883l.cpp +15 -0
  193. esphome/components/qmc5883l/qmc5883l.h +3 -0
  194. esphome/components/qmc5883l/sensor.py +31 -12
  195. esphome/components/remote_base/gobox_protocol.cpp +3 -3
  196. esphome/components/remote_receiver/__init__.py +14 -2
  197. esphome/components/remote_receiver/{remote_receiver_esp8266.cpp → remote_receiver.cpp} +2 -2
  198. esphome/components/remote_receiver/remote_receiver.h +4 -0
  199. esphome/components/remote_receiver/remote_receiver_esp32.cpp +18 -1
  200. esphome/components/remote_transmitter/__init__.py +2 -2
  201. esphome/components/remote_transmitter/remote_transmitter.cpp +103 -0
  202. esphome/components/rp2040/__init__.py +11 -11
  203. esphome/components/rtttl/rtttl.cpp +2 -2
  204. esphome/components/scd30/sensor.py +1 -1
  205. esphome/components/script/__init__.py +1 -1
  206. esphome/components/script/script.h +7 -7
  207. esphome/components/select/select.cpp +5 -4
  208. esphome/components/select/select_call.cpp +1 -1
  209. esphome/components/sensirion_common/i2c_sensirion.cpp +2 -1
  210. esphome/components/sensor/__init__.py +2 -0
  211. esphome/components/sha256/__init__.py +22 -0
  212. esphome/components/sha256/sha256.cpp +116 -0
  213. esphome/components/sha256/sha256.h +60 -0
  214. esphome/components/sim800l/sim800l.cpp +8 -4
  215. esphome/components/socket/lwip_raw_tcp_impl.cpp +34 -6
  216. esphome/components/sonoff_d1/sonoff_d1.cpp +1 -1
  217. esphome/components/spi/__init__.py +0 -3
  218. esphome/components/split_buffer/__init__.py +5 -0
  219. esphome/components/split_buffer/split_buffer.cpp +133 -0
  220. esphome/components/split_buffer/split_buffer.h +40 -0
  221. esphome/components/sps30/sps30.cpp +14 -10
  222. esphome/components/sps30/sps30.h +2 -0
  223. esphome/components/st7567_i2c/st7567_i2c.cpp +3 -1
  224. esphome/components/st7789v/st7789v.cpp +3 -2
  225. esphome/components/statsd/statsd.cpp +1 -1
  226. esphome/components/substitutions/__init__.py +3 -1
  227. esphome/components/substitutions/jinja.py +13 -3
  228. esphome/components/sx126x/__init__.py +16 -0
  229. esphome/components/sx126x/sx126x.cpp +15 -1
  230. esphome/components/sx126x/sx126x.h +9 -1
  231. esphome/components/sx126x/sx126x_reg.h +2 -0
  232. esphome/components/text_sensor/text_sensor.cpp +16 -0
  233. esphome/components/text_sensor/text_sensor.h +3 -10
  234. esphome/components/tormatic/tormatic_cover.cpp +1 -1
  235. esphome/components/tuya/select/tuya_select.cpp +1 -1
  236. esphome/components/tuya/tuya.cpp +29 -4
  237. esphome/components/uart/__init__.py +36 -26
  238. esphome/components/uart/uart.h +6 -0
  239. esphome/components/uart/uart_component.cpp +8 -0
  240. esphome/components/uart/uart_component.h +28 -0
  241. esphome/components/uart/uart_component_esp_idf.cpp +64 -10
  242. esphome/components/uart/uart_component_esp_idf.h +5 -2
  243. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +1 -1
  244. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +1 -1
  245. esphome/components/uponor_smatrix/uponor_smatrix.cpp +3 -3
  246. esphome/components/usb_host/__init__.py +2 -1
  247. esphome/components/usb_host/usb_host.h +82 -13
  248. esphome/components/usb_host/usb_host_client.cpp +180 -24
  249. esphome/components/usb_host/usb_host_component.cpp +1 -1
  250. esphome/components/usb_uart/__init__.py +0 -1
  251. esphome/components/usb_uart/ch34x.cpp +4 -4
  252. esphome/components/usb_uart/cp210x.cpp +3 -3
  253. esphome/components/usb_uart/usb_uart.cpp +88 -32
  254. esphome/components/usb_uart/usb_uart.h +30 -6
  255. esphome/components/valve/valve.cpp +1 -0
  256. esphome/components/veml7700/veml7700.cpp +7 -6
  257. esphome/components/version/version_text_sensor.cpp +2 -1
  258. esphome/components/voice_assistant/voice_assistant.cpp +3 -3
  259. esphome/components/waveshare_epaper/waveshare_epaper.cpp +4 -4
  260. esphome/components/web_server/list_entities.cpp +3 -4
  261. esphome/components/web_server/list_entities.h +8 -10
  262. esphome/components/web_server/ota/__init__.py +1 -1
  263. esphome/components/web_server/ota/ota_web_server.cpp +9 -3
  264. esphome/components/web_server/web_server.cpp +509 -404
  265. esphome/components/web_server/web_server.h +5 -6
  266. esphome/components/web_server/web_server_v1.cpp +21 -19
  267. esphome/components/web_server_base/__init__.py +5 -2
  268. esphome/components/web_server_base/web_server_base.h +27 -7
  269. esphome/components/web_server_idf/__init__.py +1 -1
  270. esphome/components/web_server_idf/multipart.cpp +2 -2
  271. esphome/components/web_server_idf/multipart.h +2 -2
  272. esphome/components/web_server_idf/utils.cpp +2 -2
  273. esphome/components/web_server_idf/utils.h +2 -2
  274. esphome/components/web_server_idf/web_server_idf.cpp +118 -26
  275. esphome/components/web_server_idf/web_server_idf.h +12 -10
  276. esphome/components/wifi/__init__.py +13 -11
  277. esphome/components/wifi/wifi_component.cpp +73 -56
  278. esphome/components/wifi/wifi_component.h +4 -4
  279. esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
  280. esphome/components/wifi/wifi_component_esp_idf.cpp +24 -4
  281. esphome/components/wireguard/__init__.py +1 -1
  282. esphome/components/wts01/__init__.py +0 -0
  283. esphome/components/wts01/sensor.py +41 -0
  284. esphome/components/wts01/wts01.cpp +91 -0
  285. esphome/components/wts01/wts01.h +27 -0
  286. esphome/components/zephyr/__init__.py +5 -5
  287. esphome/components/zwave_proxy/__init__.py +43 -0
  288. esphome/components/zwave_proxy/zwave_proxy.cpp +346 -0
  289. esphome/components/zwave_proxy/zwave_proxy.h +93 -0
  290. esphome/config.py +79 -24
  291. esphome/config_validation.py +13 -15
  292. esphome/const.py +9 -2
  293. esphome/core/__init__.py +31 -22
  294. esphome/core/component.cpp +28 -18
  295. esphome/core/component_iterator.h +2 -1
  296. esphome/core/config.py +15 -15
  297. esphome/core/defines.h +19 -0
  298. esphome/core/hash_base.h +56 -0
  299. esphome/core/helpers.cpp +19 -3
  300. esphome/core/helpers.h +26 -0
  301. esphome/core/scheduler.cpp +5 -21
  302. esphome/core/scheduler.h +19 -8
  303. esphome/core/string_ref.h +1 -1
  304. esphome/core/time.cpp +5 -5
  305. esphome/cpp_generator.py +4 -29
  306. esphome/dashboard/const.py +21 -4
  307. esphome/dashboard/core.py +10 -8
  308. esphome/dashboard/dns.py +15 -0
  309. esphome/dashboard/entries.py +15 -21
  310. esphome/dashboard/models.py +76 -0
  311. esphome/dashboard/settings.py +7 -7
  312. esphome/dashboard/status/mdns.py +46 -2
  313. esphome/dashboard/web_server.py +367 -93
  314. esphome/espota2.py +111 -31
  315. esphome/external_files.py +6 -7
  316. esphome/git.py +8 -0
  317. esphome/helpers.py +124 -77
  318. esphome/loader.py +8 -9
  319. esphome/platformio_api.py +25 -18
  320. esphome/storage_json.py +26 -21
  321. esphome/types.py +30 -2
  322. esphome/util.py +32 -16
  323. esphome/vscode.py +8 -8
  324. esphome/wizard.py +10 -10
  325. esphome/writer.py +50 -15
  326. esphome/yaml_util.py +37 -31
  327. esphome/zeroconf.py +12 -3
  328. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/METADATA +11 -11
  329. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/RECORD +333 -313
  330. esphome/components/event_emitter/__init__.py +0 -5
  331. esphome/components/event_emitter/event_emitter.cpp +0 -14
  332. esphome/components/event_emitter/event_emitter.h +0 -63
  333. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -125
  334. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +0 -107
  335. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +0 -110
  336. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -214
  337. esphome/components/uart/uart_component_esp32_arduino.h +0 -60
  338. esphome/components/wifi/wifi_component_esp32_arduino.cpp +0 -860
  339. esphome/core/string_ref.cpp +0 -12
  340. esphome/dashboard/util/file.py +0 -63
  341. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/WHEEL +0 -0
  342. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/entry_points.txt +0 -0
  343. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/licenses/LICENSE +0 -0
  344. {esphome-2025.9.2.dist-info → esphome-2025.10.0b1.dist-info}/top_level.txt +0 -0
@@ -6,8 +6,6 @@ from esphome.components.esp32 import add_idf_sdkconfig_option
6
6
  from esphome.components.esp32_ble import BTLoggers
7
7
  import esphome.config_validation as cv
8
8
  from esphome.const import CONF_ACTIVE, CONF_ID
9
- from esphome.core import CORE
10
- from esphome.log import AnsiFore, color
11
9
 
12
10
  AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
13
11
  DEPENDENCIES = ["api", "esp32"]
@@ -44,29 +42,7 @@ def validate_connections(config):
44
42
  )
45
43
  elif config[CONF_ACTIVE]:
46
44
  connection_slots: int = config[CONF_CONNECTION_SLOTS]
47
- esp32_ble_tracker.consume_connection_slots(connection_slots, "bluetooth_proxy")(
48
- config
49
- )
50
-
51
- # Warn about connection slot waste when using Arduino framework
52
- if CORE.using_arduino and connection_slots:
53
- _LOGGER.warning(
54
- "Bluetooth Proxy with active connections on Arduino framework has suboptimal performance.\n"
55
- "If BLE connections fail, they can waste connection slots for 10 seconds because\n"
56
- "Arduino doesn't allow configuring the BLE connection timeout (fixed at 30s).\n"
57
- "ESP-IDF framework allows setting it to 20s to match client timeouts.\n"
58
- "\n"
59
- "To switch to ESP-IDF, add this to your YAML:\n"
60
- " esp32:\n"
61
- " framework:\n"
62
- " type: esp-idf\n"
63
- "\n"
64
- "For detailed migration instructions, see:\n"
65
- "%s",
66
- color(
67
- AnsiFore.BLUE, "https://esphome.io/guides/esp32_arduino_to_idf.html"
68
- ),
69
- )
45
+ esp32_ble.consume_connection_slots(connection_slots, "bluetooth_proxy")(config)
70
46
 
71
47
  return {
72
48
  **config,
@@ -81,19 +57,17 @@ CONFIG_SCHEMA = cv.All(
81
57
  {
82
58
  cv.GenerateID(): cv.declare_id(BluetoothProxy),
83
59
  cv.Optional(CONF_ACTIVE, default=True): cv.boolean,
84
- cv.SplitDefault(CONF_CACHE_SERVICES, esp32_idf=True): cv.All(
85
- cv.only_with_esp_idf, cv.boolean
86
- ),
60
+ cv.Optional(CONF_CACHE_SERVICES, default=True): cv.boolean,
87
61
  cv.Optional(
88
62
  CONF_CONNECTION_SLOTS,
89
63
  default=DEFAULT_CONNECTION_SLOTS,
90
64
  ): cv.All(
91
65
  cv.positive_int,
92
- cv.Range(min=1, max=esp32_ble_tracker.max_connections()),
66
+ cv.Range(min=1, max=esp32_ble.IDF_MAX_CONNECTIONS),
93
67
  ),
94
68
  cv.Optional(CONF_CONNECTIONS): cv.All(
95
69
  cv.ensure_list(CONNECTION_SCHEMA),
96
- cv.Length(min=1, max=esp32_ble_tracker.max_connections()),
70
+ cv.Length(min=1, max=esp32_ble.IDF_MAX_CONNECTIONS),
97
71
  ),
98
72
  }
99
73
  )
@@ -514,7 +514,8 @@ esp_err_t BluetoothConnection::read_characteristic(uint16_t handle) {
514
514
  return this->check_and_log_error_("esp_ble_gattc_read_char", err);
515
515
  }
516
516
 
517
- esp_err_t BluetoothConnection::write_characteristic(uint16_t handle, const std::string &data, bool response) {
517
+ esp_err_t BluetoothConnection::write_characteristic(uint16_t handle, const uint8_t *data, size_t length,
518
+ bool response) {
518
519
  if (!this->connected()) {
519
520
  this->log_gatt_not_connected_("write", "characteristic");
520
521
  return ESP_GATT_NOT_CONNECTED;
@@ -522,8 +523,11 @@ esp_err_t BluetoothConnection::write_characteristic(uint16_t handle, const std::
522
523
  ESP_LOGV(TAG, "[%d] [%s] Writing GATT characteristic handle %d", this->connection_index_, this->address_str_.c_str(),
523
524
  handle);
524
525
 
526
+ // ESP-IDF's API requires a non-const uint8_t* but it doesn't modify the data
527
+ // The BTC layer immediately copies the data to its own buffer (see btc_gattc.c)
528
+ // const_cast is safe here and was previously hidden by a C-style cast
525
529
  esp_err_t err =
526
- esp_ble_gattc_write_char(this->gattc_if_, this->conn_id_, handle, data.size(), (uint8_t *) data.data(),
530
+ esp_ble_gattc_write_char(this->gattc_if_, this->conn_id_, handle, length, const_cast<uint8_t *>(data),
527
531
  response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
528
532
  return this->check_and_log_error_("esp_ble_gattc_write_char", err);
529
533
  }
@@ -540,7 +544,7 @@ esp_err_t BluetoothConnection::read_descriptor(uint16_t handle) {
540
544
  return this->check_and_log_error_("esp_ble_gattc_read_char_descr", err);
541
545
  }
542
546
 
543
- esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const std::string &data, bool response) {
547
+ esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const uint8_t *data, size_t length, bool response) {
544
548
  if (!this->connected()) {
545
549
  this->log_gatt_not_connected_("write", "descriptor");
546
550
  return ESP_GATT_NOT_CONNECTED;
@@ -548,8 +552,11 @@ esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const std::stri
548
552
  ESP_LOGV(TAG, "[%d] [%s] Writing GATT descriptor handle %d", this->connection_index_, this->address_str_.c_str(),
549
553
  handle);
550
554
 
555
+ // ESP-IDF's API requires a non-const uint8_t* but it doesn't modify the data
556
+ // The BTC layer immediately copies the data to its own buffer (see btc_gattc.c)
557
+ // const_cast is safe here and was previously hidden by a C-style cast
551
558
  esp_err_t err = esp_ble_gattc_write_char_descr(
552
- this->gattc_if_, this->conn_id_, handle, data.size(), (uint8_t *) data.data(),
559
+ this->gattc_if_, this->conn_id_, handle, length, const_cast<uint8_t *>(data),
553
560
  response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
554
561
  return this->check_and_log_error_("esp_ble_gattc_write_char_descr", err);
555
562
  }
@@ -18,9 +18,9 @@ class BluetoothConnection final : public esp32_ble_client::BLEClientBase {
18
18
  esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
19
19
 
20
20
  esp_err_t read_characteristic(uint16_t handle);
21
- esp_err_t write_characteristic(uint16_t handle, const std::string &data, bool response);
21
+ esp_err_t write_characteristic(uint16_t handle, const uint8_t *data, size_t length, bool response);
22
22
  esp_err_t read_descriptor(uint16_t handle);
23
- esp_err_t write_descriptor(uint16_t handle, const std::string &data, bool response);
23
+ esp_err_t write_descriptor(uint16_t handle, const uint8_t *data, size_t length, bool response);
24
24
 
25
25
  esp_err_t notify_characteristic(uint16_t handle, bool enable);
26
26
 
@@ -305,7 +305,7 @@ void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &
305
305
  return;
306
306
  }
307
307
 
308
- auto err = connection->write_characteristic(msg.handle, msg.data, msg.response);
308
+ auto err = connection->write_characteristic(msg.handle, msg.data, msg.data_len, msg.response);
309
309
  if (err != ESP_OK) {
310
310
  this->send_gatt_error(msg.address, msg.handle, err);
311
311
  }
@@ -331,7 +331,7 @@ void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWri
331
331
  return;
332
332
  }
333
333
 
334
- auto err = connection->write_descriptor(msg.handle, msg.data, true);
334
+ auto err = connection->write_descriptor(msg.handle, msg.data, msg.data_len, true);
335
335
  if (err != ESP_OK) {
336
336
  this->send_gatt_error(msg.address, msg.handle, err);
337
337
  }
@@ -2,7 +2,6 @@ import esphome.codegen as cg
2
2
  from esphome.components.esp32 import add_idf_component
3
3
  import esphome.config_validation as cv
4
4
  from esphome.const import CONF_BUFFER_SIZE, CONF_ID, CONF_TYPE
5
- from esphome.core import CORE
6
5
  from esphome.types import ConfigType
7
6
 
8
7
  CODEOWNERS = ["@DT-art1"]
@@ -51,9 +50,8 @@ async def to_code(config: ConfigType) -> None:
51
50
  buffer = cg.new_Pvariable(config[CONF_ENCODER_BUFFER_ID])
52
51
  cg.add(buffer.set_buffer_size(config[CONF_BUFFER_SIZE]))
53
52
  if config[CONF_TYPE] == ESP32_CAMERA_ENCODER:
54
- if CORE.using_esp_idf:
55
- add_idf_component(name="espressif/esp32-camera", ref="2.1.0")
56
- cg.add_build_flag("-DUSE_ESP32_CAMERA_JPEG_ENCODER")
53
+ add_idf_component(name="espressif/esp32-camera", ref="2.1.1")
54
+ cg.add_define("USE_ESP32_CAMERA_JPEG_ENCODER")
57
55
  var = cg.new_Pvariable(
58
56
  config[CONF_ID],
59
57
  config[CONF_QUALITY],
@@ -1,3 +1,5 @@
1
+ #include "esphome/core/defines.h"
2
+
1
3
  #ifdef USE_ESP32_CAMERA_JPEG_ENCODER
2
4
 
3
5
  #include "esp32_camera_jpeg_encoder.h"
@@ -15,7 +17,7 @@ camera::EncoderError ESP32CameraJPEGEncoder::encode_pixels(camera::CameraImageSp
15
17
  this->bytes_written_ = 0;
16
18
  this->out_of_output_memory_ = false;
17
19
  bool success = fmt2jpg_cb(pixels->get_data_buffer(), pixels->get_data_length(), spec->width, spec->height,
18
- to_internal_(spec->format), this->quality_, callback_, this);
20
+ to_internal_(spec->format), this->quality_, callback, this);
19
21
 
20
22
  if (!success)
21
23
  return camera::ENCODER_ERROR_CONFIGURATION;
@@ -49,7 +51,7 @@ void ESP32CameraJPEGEncoder::dump_config() {
49
51
  this->output_->get_max_size(), this->quality_, this->buffer_expand_size_);
50
52
  }
51
53
 
52
- size_t ESP32CameraJPEGEncoder::callback_(void *arg, size_t index, const void *data, size_t len) {
54
+ size_t ESP32CameraJPEGEncoder::callback(void *arg, size_t index, const void *data, size_t len) {
53
55
  ESP32CameraJPEGEncoder *that = reinterpret_cast<ESP32CameraJPEGEncoder *>(arg);
54
56
  uint8_t *buffer = that->output_->get_data();
55
57
  size_t buffer_length = that->output_->get_max_size();
@@ -1,5 +1,7 @@
1
1
  #pragma once
2
2
 
3
+ #include "esphome/core/defines.h"
4
+
3
5
  #ifdef USE_ESP32_CAMERA_JPEG_ENCODER
4
6
 
5
7
  #include <esp_camera.h>
@@ -24,7 +26,7 @@ class ESP32CameraJPEGEncoder : public camera::Encoder {
24
26
  void dump_config() override;
25
27
  // -------------------------
26
28
  protected:
27
- static size_t callback_(void *arg, size_t index, const void *data, size_t len);
29
+ static size_t callback(void *arg, size_t index, const void *data, size_t len);
28
30
  pixformat_t to_internal_(camera::PixelFormat format);
29
31
 
30
32
  camera::EncoderBuffer *output_{};
@@ -21,8 +21,8 @@ void Canbus::dump_config() {
21
21
  }
22
22
  }
23
23
 
24
- void Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request,
25
- const std::vector<uint8_t> &data) {
24
+ canbus::Error Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request,
25
+ const std::vector<uint8_t> &data) {
26
26
  struct CanFrame can_message;
27
27
 
28
28
  uint8_t size = static_cast<uint8_t>(data.size());
@@ -45,13 +45,15 @@ void Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transm
45
45
  ESP_LOGVV(TAG, " data[%d]=%02x", i, can_message.data[i]);
46
46
  }
47
47
 
48
- if (this->send_message(&can_message) != canbus::ERROR_OK) {
48
+ canbus::Error error = this->send_message(&can_message);
49
+ if (error != canbus::ERROR_OK) {
49
50
  if (use_extended_id) {
50
- ESP_LOGW(TAG, "send to extended id=0x%08" PRIx32 " failed!", can_id);
51
+ ESP_LOGW(TAG, "send to extended id=0x%08" PRIx32 " failed with error %d!", can_id, error);
51
52
  } else {
52
- ESP_LOGW(TAG, "send to standard id=0x%03" PRIx32 " failed!", can_id);
53
+ ESP_LOGW(TAG, "send to standard id=0x%03" PRIx32 " failed with error %d!", can_id, error);
53
54
  }
54
55
  }
56
+ return error;
55
57
  }
56
58
 
57
59
  void Canbus::add_trigger(CanbusTrigger *trigger) {
@@ -70,11 +70,11 @@ class Canbus : public Component {
70
70
  float get_setup_priority() const override { return setup_priority::HARDWARE; }
71
71
  void loop() override;
72
72
 
73
- void send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request,
74
- const std::vector<uint8_t> &data);
75
- void send_data(uint32_t can_id, bool use_extended_id, const std::vector<uint8_t> &data) {
73
+ canbus::Error send_data(uint32_t can_id, bool use_extended_id, bool remote_transmission_request,
74
+ const std::vector<uint8_t> &data);
75
+ canbus::Error send_data(uint32_t can_id, bool use_extended_id, const std::vector<uint8_t> &data) {
76
76
  // for backwards compatibility only
77
- this->send_data(can_id, use_extended_id, false, data);
77
+ return this->send_data(can_id, use_extended_id, false, data);
78
78
  }
79
79
  void set_can_id(uint32_t can_id) { this->can_id_ = can_id; }
80
80
  void set_use_extended_id(bool use_extended_id) { this->use_extended_id_ = use_extended_id; }
@@ -1,6 +1,7 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import web_server_base
3
3
  from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
4
+ from esphome.config_helpers import filter_source_files_from_platform
4
5
  import esphome.config_validation as cv
5
6
  from esphome.const import (
6
7
  CONF_ID,
@@ -9,11 +10,19 @@ from esphome.const import (
9
10
  PLATFORM_ESP8266,
10
11
  PLATFORM_LN882X,
11
12
  PLATFORM_RTL87XX,
13
+ PlatformFramework,
12
14
  )
13
15
  from esphome.core import CORE, coroutine_with_priority
14
16
  from esphome.coroutine import CoroPriority
15
17
 
16
- AUTO_LOAD = ["web_server_base", "ota.web_server"]
18
+
19
+ def AUTO_LOAD() -> list[str]:
20
+ auto_load = ["web_server_base", "ota.web_server"]
21
+ if CORE.using_esp_idf:
22
+ auto_load.append("socket")
23
+ return auto_load
24
+
25
+
17
26
  DEPENDENCIES = ["wifi"]
18
27
  CODEOWNERS = ["@esphome/core"]
19
28
 
@@ -58,3 +67,11 @@ async def to_code(config):
58
67
  cg.add_library("DNSServer", None)
59
68
  if CORE.is_libretiny:
60
69
  cg.add_library("DNSServer", None)
70
+
71
+
72
+ # Only compile the ESP-IDF DNS server when using ESP-IDF framework
73
+ FILTER_SOURCE_FILES = filter_source_files_from_platform(
74
+ {
75
+ "dns_server_esp32_idf.cpp": {PlatformFramework.ESP32_IDF},
76
+ }
77
+ )
@@ -11,14 +11,14 @@ namespace captive_portal {
11
11
  static const char *const TAG = "captive_portal";
12
12
 
13
13
  void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
14
- AsyncResponseStream *stream = request->beginResponseStream(F("application/json"));
15
- stream->addHeader(F("cache-control"), F("public, max-age=0, must-revalidate"));
14
+ AsyncResponseStream *stream = request->beginResponseStream(ESPHOME_F("application/json"));
15
+ stream->addHeader(ESPHOME_F("cache-control"), ESPHOME_F("public, max-age=0, must-revalidate"));
16
16
  #ifdef USE_ESP8266
17
- stream->print(F("{\"mac\":\""));
17
+ stream->print(ESPHOME_F("{\"mac\":\""));
18
18
  stream->print(get_mac_address_pretty().c_str());
19
- stream->print(F("\",\"name\":\""));
19
+ stream->print(ESPHOME_F("\",\"name\":\""));
20
20
  stream->print(App.get_name().c_str());
21
- stream->print(F("\",\"aps\":[{}"));
21
+ stream->print(ESPHOME_F("\",\"aps\":[{}"));
22
22
  #else
23
23
  stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", get_mac_address_pretty().c_str(), App.get_name().c_str());
24
24
  #endif
@@ -29,37 +29,35 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
29
29
 
30
30
  // Assumes no " in ssid, possible unicode isses?
31
31
  #ifdef USE_ESP8266
32
- stream->print(F(",{\"ssid\":\""));
32
+ stream->print(ESPHOME_F(",{\"ssid\":\""));
33
33
  stream->print(scan.get_ssid().c_str());
34
- stream->print(F("\",\"rssi\":"));
34
+ stream->print(ESPHOME_F("\",\"rssi\":"));
35
35
  stream->print(scan.get_rssi());
36
- stream->print(F(",\"lock\":"));
36
+ stream->print(ESPHOME_F(",\"lock\":"));
37
37
  stream->print(scan.get_with_auth());
38
- stream->print(F("}"));
38
+ stream->print(ESPHOME_F("}"));
39
39
  #else
40
40
  stream->printf(R"(,{"ssid":"%s","rssi":%d,"lock":%d})", scan.get_ssid().c_str(), scan.get_rssi(),
41
41
  scan.get_with_auth());
42
42
  #endif
43
43
  }
44
- stream->print(F("]}"));
44
+ stream->print(ESPHOME_F("]}"));
45
45
  request->send(stream);
46
46
  }
47
47
  void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
48
- std::string ssid = request->arg("ssid").c_str();
49
- std::string psk = request->arg("psk").c_str();
48
+ std::string ssid = request->arg("ssid").c_str(); // NOLINT(readability-redundant-string-cstr)
49
+ std::string psk = request->arg("psk").c_str(); // NOLINT(readability-redundant-string-cstr)
50
50
  ESP_LOGI(TAG, "Requested WiFi Settings Change:");
51
51
  ESP_LOGI(TAG, " SSID='%s'", ssid.c_str());
52
52
  ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
53
53
  wifi::global_wifi_component->save_wifi_sta(ssid, psk);
54
54
  wifi::global_wifi_component->start_scanning();
55
- request->redirect(F("/?save"));
55
+ request->redirect(ESPHOME_F("/?save"));
56
56
  }
57
57
 
58
58
  void CaptivePortal::setup() {
59
- #ifndef USE_ARDUINO
60
- // No DNS server needed for non-Arduino frameworks
59
+ // Disable loop by default - will be enabled when captive portal starts
61
60
  this->disable_loop();
62
- #endif
63
61
  }
64
62
  void CaptivePortal::start() {
65
63
  this->base_->init();
@@ -67,51 +65,47 @@ void CaptivePortal::start() {
67
65
  this->base_->add_handler(this);
68
66
  }
69
67
 
68
+ network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
69
+
70
+ #ifdef USE_ESP_IDF
71
+ // Create DNS server instance for ESP-IDF
72
+ this->dns_server_ = make_unique<DNSServer>();
73
+ this->dns_server_->start(ip);
74
+ #endif
70
75
  #ifdef USE_ARDUINO
71
76
  this->dns_server_ = make_unique<DNSServer>();
72
77
  this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
73
- network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
74
- this->dns_server_->start(53, F("*"), ip);
75
- // Re-enable loop() when DNS server is started
76
- this->enable_loop();
77
- #endif
78
-
79
- this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
80
- if (!this->active_ || req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
81
- req->send(404, F("text/html"), F("File not found"));
82
- return;
83
- }
84
-
85
- #ifdef USE_ESP8266
86
- String url = F("http://");
87
- url += wifi::global_wifi_component->wifi_soft_ap_ip().str().c_str();
88
- #else
89
- auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().str();
78
+ this->dns_server_->start(53, ESPHOME_F("*"), ip);
90
79
  #endif
91
- req->redirect(url.c_str());
92
- });
93
80
 
94
81
  this->initialized_ = true;
95
82
  this->active_ = true;
83
+
84
+ // Enable loop() now that captive portal is active
85
+ this->enable_loop();
86
+
87
+ ESP_LOGV(TAG, "Captive portal started");
96
88
  }
97
89
 
98
90
  void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
99
- if (req->url() == F("/")) {
100
- #ifndef USE_ESP8266
101
- auto *response = req->beginResponse(200, F("text/html"), INDEX_GZ, sizeof(INDEX_GZ));
102
- #else
103
- auto *response = req->beginResponse_P(200, F("text/html"), INDEX_GZ, sizeof(INDEX_GZ));
104
- #endif
105
- response->addHeader(F("Content-Encoding"), F("gzip"));
106
- req->send(response);
107
- return;
108
- } else if (req->url() == F("/config.json")) {
91
+ if (req->url() == ESPHOME_F("/config.json")) {
109
92
  this->handle_config(req);
110
93
  return;
111
- } else if (req->url() == F("/wifisave")) {
94
+ } else if (req->url() == ESPHOME_F("/wifisave")) {
112
95
  this->handle_wifisave(req);
113
96
  return;
114
97
  }
98
+
99
+ // All other requests get the captive portal page
100
+ // This includes OS captive portal detection endpoints which will trigger
101
+ // the captive portal when they don't receive their expected responses
102
+ #ifndef USE_ESP8266
103
+ auto *response = req->beginResponse(200, ESPHOME_F("text/html"), INDEX_GZ, sizeof(INDEX_GZ));
104
+ #else
105
+ auto *response = req->beginResponse_P(200, ESPHOME_F("text/html"), INDEX_GZ, sizeof(INDEX_GZ));
106
+ #endif
107
+ response->addHeader(ESPHOME_F("Content-Encoding"), ESPHOME_F("gzip"));
108
+ req->send(response);
115
109
  }
116
110
 
117
111
  CaptivePortal::CaptivePortal(web_server_base::WebServerBase *base) : base_(base) { global_captive_portal = this; }
@@ -5,6 +5,9 @@
5
5
  #ifdef USE_ARDUINO
6
6
  #include <DNSServer.h>
7
7
  #endif
8
+ #ifdef USE_ESP_IDF
9
+ #include "dns_server_esp32_idf.h"
10
+ #endif
8
11
  #include "esphome/core/component.h"
9
12
  #include "esphome/core/helpers.h"
10
13
  #include "esphome/core/preferences.h"
@@ -19,41 +22,36 @@ class CaptivePortal : public AsyncWebHandler, public Component {
19
22
  CaptivePortal(web_server_base::WebServerBase *base);
20
23
  void setup() override;
21
24
  void dump_config() override;
22
- #ifdef USE_ARDUINO
23
25
  void loop() override {
26
+ #ifdef USE_ARDUINO
24
27
  if (this->dns_server_ != nullptr) {
25
28
  this->dns_server_->processNextRequest();
26
- } else {
27
- this->disable_loop();
28
29
  }
29
- }
30
30
  #endif
31
+ #ifdef USE_ESP_IDF
32
+ if (this->dns_server_ != nullptr) {
33
+ this->dns_server_->process_next_request();
34
+ }
35
+ #endif
36
+ }
31
37
  float get_setup_priority() const override;
32
38
  void start();
33
39
  bool is_active() const { return this->active_; }
34
40
  void end() {
35
41
  this->active_ = false;
42
+ this->disable_loop(); // Stop processing DNS requests
36
43
  this->base_->deinit();
37
- #ifdef USE_ARDUINO
38
- this->dns_server_->stop();
39
- this->dns_server_ = nullptr;
40
- #endif
44
+ if (this->dns_server_ != nullptr) {
45
+ this->dns_server_->stop();
46
+ this->dns_server_ = nullptr;
47
+ }
41
48
  }
42
49
 
43
50
  bool canHandle(AsyncWebServerRequest *request) const override {
44
- if (!this->active_)
45
- return false;
46
-
47
- if (request->method() == HTTP_GET) {
48
- if (request->url() == F("/"))
49
- return true;
50
- if (request->url() == F("/config.json"))
51
- return true;
52
- if (request->url() == F("/wifisave"))
53
- return true;
54
- }
55
-
56
- return false;
51
+ // Handle all GET requests when captive portal is active
52
+ // This allows us to respond with the portal page for any URL,
53
+ // triggering OS captive portal detection
54
+ return this->active_ && request->method() == HTTP_GET;
57
55
  }
58
56
 
59
57
  void handle_config(AsyncWebServerRequest *request);
@@ -66,7 +64,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
66
64
  web_server_base::WebServerBase *base_;
67
65
  bool initialized_{false};
68
66
  bool active_{false};
69
- #ifdef USE_ARDUINO
67
+ #if defined(USE_ARDUINO) || defined(USE_ESP_IDF)
70
68
  std::unique_ptr<DNSServer> dns_server_{nullptr};
71
69
  #endif
72
70
  };