esphome 2025.6.3__py3-none-any.whl → 2025.7.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +1 -3
- esphome/codegen.py +2 -0
- esphome/components/ac_dimmer/ac_dimmer.cpp +6 -6
- esphome/components/adc/__init__.py +25 -1
- esphome/components/adc/adc_sensor.h +11 -11
- esphome/components/adc/adc_sensor_common.cpp +1 -1
- esphome/components/adc/adc_sensor_esp32.cpp +16 -8
- esphome/components/ade7880/ade7880.h +0 -2
- esphome/components/ads1115/ads1115.h +0 -1
- esphome/components/ads1118/ads1118.h +0 -1
- esphome/components/ags10/ags10.h +0 -2
- esphome/components/aic3204/aic3204.h +0 -1
- esphome/components/alarm_control_panel/__init__.py +5 -2
- esphome/components/alpha3/alpha3.h +0 -1
- esphome/components/am43/cover/am43_cover.h +0 -1
- esphome/components/am43/sensor/am43_sensor.h +0 -1
- esphome/components/analog_threshold/analog_threshold_binary_sensor.h +0 -2
- esphome/components/anova/anova.cpp +5 -1
- esphome/components/anova/anova.h +0 -1
- esphome/components/apds9960/apds9960.cpp +1 -1
- esphome/components/api/__init__.py +57 -21
- esphome/components/api/api_connection.cpp +344 -539
- esphome/components/api/api_connection.h +224 -141
- esphome/components/api/api_frame_helper.cpp +91 -127
- esphome/components/api/api_frame_helper.h +64 -54
- esphome/components/api/api_pb2.cpp +1837 -9044
- esphome/components/api/api_pb2.h +532 -685
- esphome/components/api/api_pb2_dump.cpp +4432 -0
- esphome/components/api/api_pb2_service.cpp +184 -425
- esphome/components/api/api_pb2_service.h +13 -6
- esphome/components/api/api_server.cpp +131 -167
- esphome/components/api/api_server.h +38 -10
- esphome/components/api/client.py +8 -2
- esphome/components/api/custom_api_device.h +8 -0
- esphome/components/api/list_entities.cpp +37 -104
- esphome/components/api/list_entities.h +33 -23
- esphome/components/api/proto.h +532 -26
- esphome/components/api/subscribe_state.cpp +23 -29
- esphome/components/api/subscribe_state.h +26 -19
- esphome/components/api/user_services.h +2 -0
- esphome/components/as5600/as5600.h +0 -1
- esphome/components/async_tcp/__init__.py +14 -5
- esphome/components/atc_mithermometer/atc_mithermometer.h +0 -1
- esphome/components/atm90e32/atm90e32.cpp +2 -1
- esphome/components/audio/audio_decoder.cpp +1 -1
- esphome/components/audio/audio_transfer_buffer.cpp +2 -2
- esphome/components/b_parasite/b_parasite.h +0 -1
- esphome/components/bedjet/bedjet_hub.cpp +5 -1
- esphome/components/bedjet/climate/bedjet_climate.cpp +5 -1
- esphome/components/beken_spi_led_strip/led_strip.cpp +4 -2
- esphome/components/bh1750/bh1750.cpp +5 -5
- esphome/components/binary_sensor/__init__.py +82 -5
- esphome/components/binary_sensor/automation.h +19 -1
- esphome/components/binary_sensor/binary_sensor.cpp +12 -30
- esphome/components/binary_sensor/binary_sensor.h +11 -25
- esphome/components/binary_sensor/filter.cpp +29 -24
- esphome/components/binary_sensor/filter.h +20 -10
- esphome/components/ble_client/output/ble_binary_output.h +0 -1
- esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +5 -1
- esphome/components/ble_client/sensor/ble_rssi_sensor.h +0 -1
- esphome/components/ble_client/sensor/ble_sensor.cpp +5 -1
- esphome/components/ble_client/sensor/ble_sensor.h +0 -1
- esphome/components/ble_client/switch/ble_switch.h +0 -1
- esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +5 -1
- esphome/components/ble_client/text_sensor/ble_text_sensor.h +0 -1
- esphome/components/ble_presence/ble_presence_device.h +0 -1
- esphome/components/ble_rssi/ble_rssi_sensor.h +0 -1
- esphome/components/ble_scanner/ble_scanner.h +0 -1
- esphome/components/bluetooth_proxy/bluetooth_connection.h +9 -2
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +16 -6
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -2
- esphome/components/bme680/sensor.py +1 -1
- esphome/components/bmp581/bmp581.h +0 -2
- esphome/components/button/__init__.py +5 -2
- esphome/components/camera/__init__.py +1 -0
- esphome/components/camera/camera.cpp +22 -0
- esphome/components/camera/camera.h +80 -0
- esphome/components/canbus/__init__.py +1 -0
- esphome/components/cap1188/cap1188.h +0 -1
- esphome/components/captive_portal/__init__.py +12 -2
- esphome/components/captive_portal/captive_portal.cpp +12 -2
- esphome/components/captive_portal/captive_portal.h +5 -2
- esphome/components/ccs811/ccs811.h +0 -2
- esphome/components/climate/__init__.py +5 -2
- esphome/components/cm1106/sensor.py +2 -2
- esphome/components/const/__init__.py +2 -0
- esphome/components/copy/binary_sensor/copy_binary_sensor.h +0 -1
- esphome/components/copy/button/copy_button.h +0 -1
- esphome/components/copy/cover/copy_cover.h +0 -1
- esphome/components/copy/fan/copy_fan.h +0 -1
- esphome/components/copy/lock/copy_lock.h +0 -1
- esphome/components/copy/number/copy_number.h +0 -1
- esphome/components/copy/select/copy_select.h +0 -1
- esphome/components/copy/sensor/copy_sensor.h +0 -1
- esphome/components/copy/switch/copy_switch.h +0 -1
- esphome/components/copy/text/copy_text.h +0 -1
- esphome/components/copy/text_sensor/copy_text_sensor.h +0 -1
- esphome/components/cover/__init__.py +5 -2
- esphome/components/cs5460a/cs5460a.h +0 -1
- esphome/components/datetime/__init__.py +4 -2
- esphome/components/debug/__init__.py +20 -0
- esphome/components/debug/debug_esp32.cpp +2 -0
- esphome/components/deep_sleep/__init__.py +43 -9
- esphome/components/demo/__init__.py +2 -2
- esphome/components/display/display.cpp +4 -3
- esphome/components/display/display.h +0 -2
- esphome/components/display/display_buffer.cpp +1 -1
- esphome/components/ds2484/__init__.py +1 -0
- esphome/components/ds2484/ds2484.cpp +209 -0
- esphome/components/ds2484/ds2484.h +43 -0
- esphome/components/ds2484/one_wire.py +37 -0
- esphome/components/duty_time/duty_time_sensor.h +0 -1
- esphome/components/ens160_base/ens160_base.h +0 -1
- esphome/components/es7210/es7210.h +0 -1
- esphome/components/es7243e/es7243e.h +0 -1
- esphome/components/es8156/es8156.h +0 -1
- esphome/components/es8311/es8311.h +0 -1
- esphome/components/es8388/es8388.h +0 -1
- esphome/components/esp32/__init__.py +103 -135
- esphome/components/esp32/core.cpp +0 -4
- esphome/components/esp32/gpio.h +1 -1
- esphome/components/esp32/helpers.cpp +69 -0
- esphome/components/esp32_ble/ble.cpp +5 -6
- esphome/components/esp32_ble/ble.h +29 -14
- esphome/components/esp32_ble/ble_event.h +6 -6
- esphome/components/esp32_ble_client/ble_client_base.cpp +21 -6
- esphome/components/esp32_ble_client/ble_client_base.h +24 -9
- esphome/components/esp32_ble_tracker/__init__.py +2 -8
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +5 -5
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +11 -7
- esphome/components/esp32_camera/__init__.py +112 -98
- esphome/components/esp32_camera/esp32_camera.cpp +41 -31
- esphome/components/esp32_camera/esp32_camera.h +35 -30
- esphome/components/esp32_camera_web_server/__init__.py +2 -1
- esphome/components/esp32_camera_web_server/camera_web_server.cpp +8 -8
- esphome/components/esp32_camera_web_server/camera_web_server.h +3 -3
- esphome/components/esp32_hall/sensor.py +2 -21
- esphome/components/esp32_hosted/__init__.py +101 -0
- esphome/components/esp32_hosted/esp32_hosted.py.script +12 -0
- esphome/components/esp32_improv/esp32_improv_component.cpp +3 -0
- esphome/components/esp32_rmt/__init__.py +0 -58
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +77 -63
- esphome/components/esp32_rmt_led_strip/led_strip.h +11 -17
- esphome/components/esp32_rmt_led_strip/light.py +14 -76
- esphome/components/esp32_touch/esp32_touch.h +174 -28
- esphome/components/esp32_touch/esp32_touch_common.cpp +162 -0
- esphome/components/esp32_touch/esp32_touch_v1.cpp +240 -0
- esphome/components/esp32_touch/esp32_touch_v2.cpp +397 -0
- esphome/components/esp8266/__init__.py +2 -0
- esphome/components/esp8266/gpio.cpp +10 -10
- esphome/components/esp8266/helpers.cpp +31 -0
- esphome/components/esphome/ota/__init__.py +1 -0
- esphome/components/esphome/ota/ota_esphome.cpp +24 -19
- esphome/components/ethernet/__init__.py +42 -23
- esphome/components/ethernet/esp_eth_phy_jl1101.c +0 -16
- esphome/components/ethernet/ethernet_component.cpp +69 -29
- esphome/components/ethernet/ethernet_component.h +18 -10
- esphome/components/event/__init__.py +5 -2
- esphome/components/ezo/ezo.h +0 -1
- esphome/components/ezo_pmp/ezo_pmp.h +0 -1
- esphome/components/fan/__init__.py +5 -2
- esphome/components/feedback/feedback_cover.h +0 -1
- esphome/components/font/__init__.py +92 -82
- esphome/components/font/font.cpp +9 -2
- esphome/components/font/font.h +20 -5
- esphome/components/fs3000/fs3000.h +0 -1
- esphome/components/gcja5/gcja5.h +0 -1
- esphome/components/gl_r01_i2c/__init__.py +0 -0
- esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +68 -0
- esphome/components/gl_r01_i2c/gl_r01_i2c.h +22 -0
- esphome/components/gl_r01_i2c/sensor.py +36 -0
- esphome/components/gp8403/gp8403.h +0 -1
- esphome/components/gpio/binary_sensor/__init__.py +17 -0
- esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +77 -3
- esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +40 -0
- esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +0 -2
- esphome/components/he60r/he60r.h +0 -1
- esphome/components/heatpumpir/climate.py +2 -1
- esphome/components/heatpumpir/heatpumpir.cpp +1 -0
- esphome/components/heatpumpir/heatpumpir.h +1 -0
- esphome/components/honeywellabp2_i2c/honeywellabp2.h +0 -1
- esphome/components/host/__init__.py +3 -1
- esphome/components/host/helpers.cpp +57 -0
- esphome/components/http_request/__init__.py +19 -1
- esphome/components/http_request/http_request.h +1 -1
- esphome/components/http_request/http_request_arduino.h +1 -0
- esphome/components/http_request/ota/ota_http_request.cpp +1 -1
- esphome/components/http_request/update/http_request_update.cpp +28 -9
- esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +3 -9
- esphome/components/hydreon_rgxx/sensor.py +1 -1
- esphome/components/i2c/__init__.py +23 -11
- esphome/components/i2c/i2c_bus.h +8 -1
- esphome/components/i2c/i2c_bus_arduino.cpp +4 -3
- esphome/components/i2c/i2c_bus_arduino.h +6 -3
- esphome/components/i2c/i2c_bus_esp_idf.h +5 -3
- esphome/components/i2c_device/i2c_device.h +0 -1
- esphome/components/i2s_audio/__init__.py +2 -10
- esphome/components/i2s_audio/i2s_audio.cpp +1 -5
- esphome/components/i2s_audio/media_player/__init__.py +2 -2
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +2 -2
- esphome/components/iaqcore/iaqcore.h +0 -2
- esphome/components/image/__init__.py +123 -24
- esphome/components/improv_serial/improv_serial_component.cpp +0 -4
- esphome/components/ina219/ina219.cpp +7 -0
- esphome/components/ina219/ina219.h +1 -0
- esphome/components/ina260/ina260.h +0 -2
- esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +0 -1
- esphome/components/inkplate6/display.py +15 -0
- esphome/components/inkplate6/inkplate.cpp +2 -2
- esphome/components/integration/integration_sensor.h +0 -1
- esphome/components/internal_temperature/internal_temperature.cpp +8 -27
- esphome/components/internal_temperature/sensor.py +0 -26
- esphome/components/interval/interval.h +0 -2
- esphome/components/ld2410/button/__init__.py +3 -3
- esphome/components/ld2410/button/factory_reset_button.cpp +9 -0
- esphome/components/ld2410/button/{reset_button.h → factory_reset_button.h} +2 -2
- esphome/components/ld2410/ld2410.cpp +421 -268
- esphome/components/ld2410/ld2410.h +44 -146
- esphome/components/ld2410/number/__init__.py +2 -2
- esphome/components/ld2410/sensor.py +1 -1
- esphome/components/ld2410/switch/__init__.py +1 -1
- esphome/components/ld2420/ld2420.cpp +196 -100
- esphome/components/ld2420/ld2420.h +46 -118
- esphome/components/ld2420/number/__init__.py +2 -2
- esphome/components/ld2420/sensor/__init__.py +6 -2
- esphome/components/ld2420/sensor/ld2420_sensor.h +1 -1
- esphome/components/ld2450/button/__init__.py +3 -3
- esphome/components/ld2450/button/factory_reset_button.cpp +9 -0
- esphome/components/ld2450/button/{reset_button.h → factory_reset_button.h} +2 -2
- esphome/components/ld2450/ld2450.cpp +384 -232
- esphome/components/ld2450/ld2450.h +60 -69
- esphome/components/ld2450/switch/__init__.py +1 -1
- esphome/components/ledc/ledc_output.cpp +1 -63
- esphome/components/libretiny/__init__.py +5 -3
- esphome/components/libretiny/const.py +5 -0
- esphome/components/libretiny/generate_components.py +1 -0
- esphome/components/libretiny/helpers.cpp +35 -0
- esphome/components/libretiny/lt_component.cpp +5 -3
- esphome/components/light/__init__.py +4 -2
- esphome/components/light/addressable_light.h +3 -3
- esphome/components/light/light_call.cpp +180 -243
- esphome/components/light/light_call.h +72 -20
- esphome/components/light/light_color_values.h +14 -14
- esphome/components/light/light_state.h +15 -13
- esphome/components/light/transformers.h +2 -2
- esphome/components/ln882x/__init__.py +52 -0
- esphome/components/ln882x/boards.py +285 -0
- esphome/components/lock/__init__.py +5 -2
- esphome/components/logger/__init__.py +40 -3
- esphome/components/logger/logger.cpp +47 -12
- esphome/components/logger/logger.h +80 -49
- esphome/components/logger/logger_esp32.cpp +3 -3
- esphome/components/lps22/__init__.py +0 -0
- esphome/components/lps22/lps22.cpp +75 -0
- esphome/components/lps22/lps22.h +27 -0
- esphome/components/lps22/sensor.py +58 -0
- esphome/components/ltr390/ltr390.h +0 -1
- esphome/components/ltr501/ltr501.h +0 -1
- esphome/components/ltr_als_ps/ltr_als_ps.h +0 -1
- esphome/components/lvgl/__init__.py +1 -1
- esphome/components/lvgl/schemas.py +66 -6
- esphome/components/lvgl/styles.py +24 -16
- esphome/components/lvgl/widgets/__init__.py +12 -2
- esphome/components/lvgl/widgets/lv_bar.py +40 -19
- esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +1 -1
- esphome/components/max9611/max9611.h +0 -1
- esphome/components/mcp23016/__init__.py +1 -1
- esphome/components/mcp23xxx_base/__init__.py +1 -1
- esphome/components/mcp4461/__init__.py +1 -1
- esphome/components/mcp4461/output/__init__.py +3 -2
- esphome/components/mcp9600/mcp9600.h +0 -2
- esphome/components/md5/md5.cpp +3 -3
- esphome/components/md5/md5.h +1 -6
- esphome/components/mdns/__init__.py +22 -11
- esphome/components/media_player/__init__.py +4 -3
- esphome/components/micro_wake_word/__init__.py +1 -5
- esphome/components/micro_wake_word/streaming_model.cpp +2 -2
- esphome/components/microphone/microphone.cpp +7 -9
- esphome/components/microphone/microphone.h +0 -2
- esphome/components/mipi_spi/display.py +1 -0
- esphome/components/mmc5603/mmc5603.cpp +1 -1
- esphome/components/modbus/modbus.cpp +33 -15
- esphome/components/modbus/modbus.h +9 -0
- esphome/components/modbus_controller/__init__.py +42 -10
- esphome/components/modbus_controller/modbus_controller.cpp +92 -11
- esphome/components/modbus_controller/modbus_controller.h +61 -7
- esphome/components/mopeka_pro_check/mopeka_pro_check.h +0 -1
- esphome/components/mopeka_std_check/mopeka_std_check.h +0 -1
- esphome/components/mpl3115a2/mpl3115a2.h +0 -2
- esphome/components/mqtt/__init__.py +16 -0
- esphome/components/mqtt/mqtt_backend.h +2 -1
- esphome/components/mqtt/mqtt_backend_esp32.cpp +132 -47
- esphome/components/mqtt/mqtt_backend_esp32.h +106 -4
- esphome/components/mqtt/mqtt_client.cpp +15 -9
- esphome/components/mqtt/mqtt_client.h +8 -3
- esphome/components/ms8607/ms8607.h +0 -1
- esphome/components/neopixelbus/light.py +4 -1
- esphome/components/neopixelbus/neopixelbus_light.h +1 -1
- esphome/components/network/__init__.py +4 -1
- esphome/components/network/ip_address.h +1 -0
- esphome/components/nextion/__init__.py +16 -0
- esphome/components/nextion/base_component.py +1 -0
- esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
- esphome/components/nextion/display.py +14 -4
- esphome/components/nextion/nextion.cpp +166 -101
- esphome/components/nextion/nextion.h +84 -53
- esphome/components/nextion/nextion_commands.cpp +11 -10
- esphome/components/nextion/nextion_component.cpp +28 -28
- esphome/components/nextion/nextion_component.h +53 -18
- esphome/components/nextion/nextion_component_base.h +3 -0
- esphome/components/nextion/nextion_upload.cpp +36 -0
- esphome/components/nextion/nextion_upload_arduino.cpp +10 -35
- esphome/components/nextion/nextion_upload_idf.cpp +9 -33
- esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
- esphome/components/nextion/switch/nextion_switch.cpp +1 -1
- esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
- esphome/components/nfc/nfc.cpp +3 -22
- esphome/components/nfc/nfc.h +3 -3
- esphome/components/number/__init__.py +5 -2
- esphome/components/online_image/__init__.py +5 -0
- esphome/components/online_image/online_image.cpp +6 -2
- esphome/components/online_image/online_image.h +4 -1
- esphome/components/opentherm/opentherm.cpp +7 -12
- esphome/components/openthread/__init__.py +47 -40
- esphome/components/openthread/const.py +1 -0
- esphome/components/openthread/openthread_esp.cpp +27 -5
- esphome/components/opt3001/__init__.py +0 -0
- esphome/components/opt3001/opt3001.cpp +122 -0
- esphome/components/opt3001/opt3001.h +27 -0
- esphome/components/opt3001/sensor.py +35 -0
- esphome/components/ota/__init__.py +17 -0
- esphome/components/ota/ota_backend.h +27 -1
- esphome/components/ota/ota_backend_arduino_esp32.cpp +12 -2
- esphome/components/ota/ota_backend_arduino_esp32.h +3 -0
- esphome/components/ota/ota_backend_arduino_esp8266.cpp +18 -4
- esphome/components/ota/ota_backend_arduino_esp8266.h +3 -0
- esphome/components/ota/ota_backend_arduino_libretiny.cpp +12 -2
- esphome/components/ota/ota_backend_arduino_libretiny.h +3 -0
- esphome/components/ota/ota_backend_arduino_rp2040.cpp +9 -2
- esphome/components/ota/ota_backend_arduino_rp2040.h +3 -0
- esphome/components/ota/ota_backend_esp_idf.cpp +10 -16
- esphome/components/ota/ota_backend_esp_idf.h +1 -0
- esphome/components/packages/__init__.py +5 -2
- esphome/components/packet_transport/binary_sensor.py +61 -4
- esphome/components/packet_transport/packet_transport.cpp +34 -1
- esphome/components/packet_transport/packet_transport.h +11 -5
- esphome/components/pcf8574/__init__.py +1 -1
- esphome/components/pi4ioe5v6408/__init__.py +84 -0
- esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +171 -0
- esphome/components/pi4ioe5v6408/pi4ioe5v6408.h +70 -0
- esphome/components/pmsa003i/pmsa003i.h +0 -1
- esphome/components/pmsx003/pmsx003.h +0 -1
- esphome/components/pn7150/pn7150.cpp +7 -7
- esphome/components/pn7150/pn7150.h +0 -1
- esphome/components/pn7160/pn7160.cpp +7 -7
- esphome/components/pn7160/pn7160.h +0 -1
- esphome/components/preferences/syncer.h +2 -0
- esphome/components/prometheus/prometheus_handler.h +1 -1
- esphome/components/psram/psram.cpp +0 -20
- esphome/components/pulse_counter/pulse_counter_sensor.h +0 -1
- esphome/components/pulse_meter/pulse_meter_sensor.cpp +8 -4
- esphome/components/pulse_width/pulse_width.h +0 -1
- esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +0 -4
- esphome/components/pvvx_mithermometer/display/pvvx_display.h +0 -2
- esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +0 -1
- esphome/components/qr_code/__init__.py +13 -10
- esphome/components/qwiic_pir/qwiic_pir.h +0 -1
- esphome/components/radon_eye_ble/radon_eye_listener.cpp +1 -1
- esphome/components/rc522/rc522.h +0 -1
- esphome/components/rdm6300/rdm6300.h +0 -2
- esphome/components/remote_base/__init__.py +7 -5
- esphome/components/remote_base/remote_base.cpp +24 -21
- esphome/components/remote_base/remote_base.h +3 -26
- esphome/components/remote_receiver/__init__.py +40 -46
- esphome/components/remote_receiver/remote_receiver.h +4 -18
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -87
- esphome/components/remote_receiver/remote_receiver_esp8266.cpp +1 -1
- esphome/components/remote_transmitter/__init__.py +42 -43
- esphome/components/remote_transmitter/remote_transmitter.h +2 -14
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -77
- esphome/components/resistance/resistance_sensor.h +0 -1
- esphome/components/rp2040/__init__.py +2 -0
- esphome/components/rp2040/helpers.cpp +55 -0
- esphome/components/rp2040_pio_led_strip/led_strip.cpp +2 -2
- esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -4
- esphome/components/rtttl/__init__.py +4 -4
- esphome/components/rtttl/rtttl.cpp +10 -1
- esphome/components/ruuvitag/ruuvitag.h +0 -1
- esphome/components/safe_mode/safe_mode.cpp +2 -0
- esphome/components/safe_mode/safe_mode.h +4 -1
- esphome/components/scd30/scd30.h +0 -1
- esphome/components/scd30/sensor.py +2 -2
- esphome/components/scd4x/scd4x.cpp +61 -54
- esphome/components/scd4x/scd4x.h +17 -15
- esphome/components/scd4x/sensor.py +4 -4
- esphome/components/script/script.h +0 -2
- esphome/components/sdp3x/sensor.py +1 -1
- esphome/components/select/__init__.py +5 -2
- esphome/components/sen5x/sen5x.h +0 -1
- esphome/components/senseair/senseair.h +0 -1
- esphome/components/sensor/__init__.py +4 -2
- esphome/components/sensor/filter.cpp +1 -1
- esphome/components/sensor/sensor.cpp +12 -6
- esphome/components/sensor/sensor.h +13 -5
- esphome/components/servo/servo.h +0 -1
- esphome/components/sfa30/sfa30.h +0 -1
- esphome/components/sgp30/sgp30.h +0 -1
- esphome/components/sgp4x/sgp4x.h +0 -1
- esphome/components/shelly_dimmer/stm32flash.cpp +1 -2
- esphome/components/sht4x/sht4x.h +0 -1
- esphome/components/sm300d2/sm300d2.h +0 -2
- esphome/components/smt100/sensor.py +8 -4
- esphome/components/smt100/smt100.cpp +5 -5
- esphome/components/smt100/smt100.h +3 -3
- esphome/components/sn74hc595/__init__.py +1 -1
- esphome/components/sn74hc595/sn74hc595.cpp +5 -4
- esphome/components/sntp/sntp_component.cpp +9 -3
- esphome/components/sntp/time.py +2 -0
- esphome/components/socket/__init__.py +17 -0
- esphome/components/spi/__init__.py +27 -6
- esphome/components/spi/spi.cpp +3 -2
- esphome/components/spi/spi.h +9 -3
- esphome/components/spi/spi_arduino.cpp +3 -5
- esphome/components/spi/spi_esp_idf.cpp +40 -21
- esphome/components/spi_led_strip/spi_led_strip.cpp +1 -1
- esphome/components/sps30/sps30.h +0 -1
- esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
- esphome/components/st7701s/st7701s.cpp +0 -4
- esphome/components/status/status_binary_sensor.h +0 -2
- esphome/components/substitutions/__init__.py +76 -19
- esphome/components/substitutions/jinja.py +99 -0
- esphome/components/sun/sun.cpp +3 -4
- esphome/components/switch/__init__.py +5 -2
- esphome/components/switch/binary_sensor/switch_binary_sensor.h +0 -1
- esphome/components/sx126x/__init__.py +317 -0
- esphome/components/sx126x/automation.h +62 -0
- esphome/components/sx126x/packet_transport/__init__.py +26 -0
- esphome/components/sx126x/packet_transport/sx126x_transport.cpp +26 -0
- esphome/components/sx126x/packet_transport/sx126x_transport.h +25 -0
- esphome/components/sx126x/sx126x.cpp +523 -0
- esphome/components/sx126x/sx126x.h +140 -0
- esphome/components/sx126x/sx126x_reg.h +163 -0
- esphome/components/sx127x/__init__.py +325 -0
- esphome/components/sx127x/automation.h +62 -0
- esphome/components/sx127x/packet_transport/__init__.py +26 -0
- esphome/components/sx127x/packet_transport/sx127x_transport.cpp +26 -0
- esphome/components/sx127x/packet_transport/sx127x_transport.h +25 -0
- esphome/components/sx127x/sx127x.cpp +498 -0
- esphome/components/sx127x/sx127x.h +128 -0
- esphome/components/sx127x/sx127x_reg.h +295 -0
- esphome/components/syslog/esphome_syslog.cpp +5 -3
- esphome/components/syslog/esphome_syslog.h +1 -1
- esphome/components/tca9555/__init__.py +1 -1
- esphome/components/template/binary_sensor/template_binary_sensor.cpp +1 -9
- esphome/components/text/__init__.py +5 -2
- esphome/components/text_sensor/__init__.py +5 -2
- esphome/components/thermostat/thermostat_climate.cpp +34 -31
- esphome/components/thermostat/thermostat_climate.h +43 -39
- esphome/components/time/__init__.py +16 -2
- esphome/components/time/real_time_clock.cpp +4 -0
- esphome/components/time/real_time_clock.h +5 -1
- esphome/components/tlc5971/tlc5971.cpp +4 -1
- esphome/components/tmp1075/tmp1075.h +0 -2
- esphome/components/tof10120/tof10120_sensor.h +0 -1
- esphome/components/tormatic/tormatic_cover.h +0 -1
- esphome/components/total_daily_energy/total_daily_energy.h +0 -1
- esphome/components/tsl2591/tsl2591.cpp +1 -1
- esphome/components/ttp229_bsf/ttp229_bsf.h +0 -1
- esphome/components/ttp229_lsf/ttp229_lsf.h +0 -1
- esphome/components/tx20/tx20.cpp +2 -2
- esphome/components/uart/__init__.py +18 -0
- esphome/components/uart/uart_component_esp_idf.cpp +0 -4
- esphome/components/update/__init__.py +5 -2
- esphome/components/update/update_entity.h +8 -0
- esphome/components/usb_host/__init__.py +5 -2
- esphome/components/usb_host/usb_host_client.cpp +10 -10
- esphome/components/usb_uart/cp210x.cpp +1 -1
- esphome/components/usb_uart/usb_uart.cpp +41 -44
- esphome/components/usb_uart/usb_uart.h +4 -3
- esphome/components/valve/__init__.py +5 -2
- esphome/components/vbus/vbus.h +0 -1
- esphome/components/veml3235/veml3235.h +0 -1
- esphome/components/veml7700/veml7700.h +0 -1
- esphome/components/vl53l0x/vl53l0x_sensor.h +0 -1
- esphome/components/voice_assistant/voice_assistant.cpp +4 -4
- esphome/components/watchdog/watchdog.cpp +0 -4
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +6 -6
- esphome/components/web_server/__init__.py +34 -19
- esphome/components/web_server/ota/__init__.py +32 -0
- esphome/components/web_server/ota/ota_web_server.cpp +210 -0
- esphome/components/web_server/ota/ota_web_server.h +26 -0
- esphome/components/web_server/web_server.cpp +305 -427
- esphome/components/web_server/web_server.h +33 -23
- esphome/components/web_server/web_server_v1.cpp +4 -5
- esphome/components/web_server_base/__init__.py +5 -2
- esphome/components/web_server_base/web_server_base.cpp +2 -94
- esphome/components/web_server_base/web_server_base.h +5 -25
- esphome/components/web_server_idf/multipart.cpp +254 -0
- esphome/components/web_server_idf/multipart.h +86 -0
- esphome/components/web_server_idf/utils.cpp +32 -0
- esphome/components/web_server_idf/utils.h +10 -0
- esphome/components/web_server_idf/web_server_idf.cpp +162 -16
- esphome/components/web_server_idf/web_server_idf.h +11 -10
- esphome/components/wiegand/wiegand.cpp +2 -2
- esphome/components/wifi/__init__.py +18 -0
- esphome/components/wifi/wifi_component.cpp +17 -22
- esphome/components/wifi/wifi_component.h +27 -23
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +52 -59
- esphome/components/wifi/wifi_component_esp8266.cpp +46 -46
- esphome/components/wifi/wifi_component_esp_idf.cpp +35 -36
- esphome/components/wifi/wifi_component_libretiny.cpp +26 -27
- esphome/components/wifi/wifi_component_pico_w.cpp +3 -3
- esphome/components/wifi_info/wifi_info_text_sensor.cpp +6 -6
- esphome/components/wireguard/__init__.py +2 -11
- esphome/components/xiaomi_ble/xiaomi_ble.cpp +13 -1
- esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
- esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +0 -1
- esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +0 -1
- esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +0 -1
- esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +0 -1
- esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +0 -1
- esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +0 -1
- esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h +0 -1
- esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +0 -1
- esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +0 -1
- esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +0 -1
- esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +0 -1
- esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +0 -1
- esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +0 -1
- esphome/components/xiaomi_mhoc303/xiaomi_mhoc303.h +0 -1
- esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +0 -1
- esphome/components/xiaomi_miscale/xiaomi_miscale.h +0 -1
- esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +0 -1
- esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +0 -1
- esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +0 -1
- esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +0 -1
- esphome/components/xiaomi_xmwsdj04mmc/__init__.py +0 -0
- esphome/components/xiaomi_xmwsdj04mmc/sensor.py +77 -0
- esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +77 -0
- esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +36 -0
- esphome/components/zio_ultrasonic/zio_ultrasonic.h +0 -2
- esphome/components/zyaura/zyaura.h +0 -1
- esphome/config.py +88 -22
- esphome/config_helpers.py +74 -1
- esphome/config_validation.py +12 -1
- esphome/const.py +65 -10
- esphome/core/__init__.py +18 -2
- esphome/core/application.cpp +163 -10
- esphome/core/application.h +145 -165
- esphome/core/area.h +19 -0
- esphome/core/automation.h +58 -9
- esphome/core/color.cpp +3 -5
- esphome/core/color.h +16 -16
- esphome/core/component.cpp +151 -18
- esphome/core/component.h +98 -4
- esphome/core/component_iterator.cpp +11 -9
- esphome/core/component_iterator.h +12 -10
- esphome/core/config.py +155 -6
- esphome/core/controller.cpp +4 -2
- esphome/core/controller.h +1 -1
- esphome/core/datatypes.h +2 -2
- esphome/core/defines.h +17 -2
- esphome/core/device.h +20 -0
- esphome/core/entity_base.cpp +20 -15
- esphome/core/entity_base.h +76 -0
- esphome/core/entity_helpers.py +168 -1
- esphome/core/event_pool.h +81 -0
- esphome/core/helpers.cpp +75 -230
- esphome/core/helpers.h +164 -104
- esphome/core/lock_free_queue.h +151 -0
- esphome/core/log.cpp +2 -2
- esphome/core/log.h +2 -0
- esphome/core/optional.h +5 -0
- esphome/core/ring_buffer.cpp +2 -2
- esphome/core/scheduler.cpp +275 -103
- esphome/core/scheduler.h +154 -17
- esphome/core/time.cpp +5 -5
- esphome/core/time.h +5 -5
- esphome/cpp_generator.py +17 -0
- esphome/cpp_helpers.py +0 -22
- esphome/cpp_types.py +3 -1
- esphome/dashboard/entries.py +1 -1
- esphome/dashboard/util/text.py +5 -21
- esphome/dashboard/web_server.py +9 -1
- esphome/helpers.py +47 -0
- esphome/loader.py +15 -1
- esphome/pins.py +14 -8
- esphome/wizard.py +17 -4
- esphome/writer.py +21 -3
- esphome/yaml_util.py +0 -2
- {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/METADATA +10 -9
- {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/RECORD +597 -538
- esphome/components/api/api_pb2_size.h +0 -361
- esphome/components/esp32_ble/ble_event_pool.h +0 -72
- esphome/components/esp32_ble/queue.h +0 -85
- esphome/components/esp32_hall/esp32_hall.cpp +0 -25
- esphome/components/esp32_hall/esp32_hall.h +0 -23
- esphome/components/esp32_touch/esp32_touch.cpp +0 -355
- esphome/components/ld2410/button/reset_button.cpp +0 -9
- esphome/components/ld2450/button/reset_button.cpp +0 -9
- esphome/components/openthread/tlv.py +0 -65
- /esphome/{dashboard/enum.py → enum.py} +0 -0
- {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/WHEEL +0 -0
- {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/entry_points.txt +0 -0
- {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/top_level.txt +0 -0
|
@@ -14,11 +14,6 @@
|
|
|
14
14
|
#include <string>
|
|
15
15
|
#include <utility>
|
|
16
16
|
#include <vector>
|
|
17
|
-
#ifdef USE_ESP32
|
|
18
|
-
#include <freertos/FreeRTOS.h>
|
|
19
|
-
#include <freertos/semphr.h>
|
|
20
|
-
#include <deque>
|
|
21
|
-
#endif
|
|
22
17
|
|
|
23
18
|
#if USE_WEBSERVER_VERSION >= 2
|
|
24
19
|
extern const uint8_t ESPHOME_WEBSERVER_INDEX_HTML[] PROGMEM;
|
|
@@ -40,12 +35,31 @@ namespace web_server {
|
|
|
40
35
|
|
|
41
36
|
/// Internal helper struct that is used to parse incoming URLs
|
|
42
37
|
struct UrlMatch {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
const char *domain; ///< Pointer to domain within URL, for example "sensor"
|
|
39
|
+
const char *id; ///< Pointer to id within URL, for example "living_room_fan"
|
|
40
|
+
const char *method; ///< Pointer to method within URL, for example "turn_on"
|
|
41
|
+
uint8_t domain_len; ///< Length of domain string
|
|
42
|
+
uint8_t id_len; ///< Length of id string
|
|
43
|
+
uint8_t method_len; ///< Length of method string
|
|
46
44
|
bool valid; ///< Whether this match is valid
|
|
45
|
+
|
|
46
|
+
// Helper methods for string comparisons
|
|
47
|
+
bool domain_equals(const char *str) const {
|
|
48
|
+
return domain && domain_len == strlen(str) && memcmp(domain, str, domain_len) == 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
bool id_equals(const std::string &str) const {
|
|
52
|
+
return id && id_len == str.length() && memcmp(id, str.c_str(), id_len) == 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
bool method_equals(const char *str) const {
|
|
56
|
+
return method && method_len == strlen(str) && memcmp(method, str, method_len) == 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
bool method_empty() const { return method_len == 0; }
|
|
47
60
|
};
|
|
48
61
|
|
|
62
|
+
#ifdef USE_WEBSERVER_SORTING
|
|
49
63
|
struct SortingComponents {
|
|
50
64
|
float weight;
|
|
51
65
|
uint64_t group_id;
|
|
@@ -55,6 +69,7 @@ struct SortingGroup {
|
|
|
55
69
|
std::string name;
|
|
56
70
|
float weight;
|
|
57
71
|
};
|
|
72
|
+
#endif
|
|
58
73
|
|
|
59
74
|
enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
|
|
60
75
|
|
|
@@ -99,13 +114,15 @@ class DeferredUpdateEventSource : public AsyncEventSource {
|
|
|
99
114
|
protected:
|
|
100
115
|
// surface a couple methods from the base class
|
|
101
116
|
using AsyncEventSource::handleRequest;
|
|
102
|
-
using AsyncEventSource::
|
|
117
|
+
using AsyncEventSource::send;
|
|
103
118
|
|
|
104
119
|
ListEntitiesIterator entities_iterator_;
|
|
105
120
|
// vector is used very specifically for its zero memory overhead even though items are popped from the front (memory
|
|
106
121
|
// footprint is more important than speed here)
|
|
107
122
|
std::vector<DeferredEvent> deferred_queue_;
|
|
108
123
|
WebServer *web_server_;
|
|
124
|
+
uint16_t consecutive_send_failures_{0};
|
|
125
|
+
static constexpr uint16_t MAX_CONSECUTIVE_SEND_FAILURES = 2500; // ~20 seconds at 125Hz loop rate
|
|
109
126
|
|
|
110
127
|
// helper for allowing only unique entries in the queue
|
|
111
128
|
void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator);
|
|
@@ -192,11 +209,6 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
|
192
209
|
* @param include_internal Whether internal components should be displayed.
|
|
193
210
|
*/
|
|
194
211
|
void set_include_internal(bool include_internal) { include_internal_ = include_internal; }
|
|
195
|
-
/** Set whether or not the webserver should expose the OTA form and handler.
|
|
196
|
-
*
|
|
197
|
-
* @param allow_ota.
|
|
198
|
-
*/
|
|
199
|
-
void set_allow_ota(bool allow_ota) { this->allow_ota_ = allow_ota; }
|
|
200
212
|
/** Set whether or not the webserver should expose the Log.
|
|
201
213
|
*
|
|
202
214
|
* @param expose_log.
|
|
@@ -269,7 +281,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
|
269
281
|
#endif
|
|
270
282
|
|
|
271
283
|
#ifdef USE_BINARY_SENSOR
|
|
272
|
-
void on_binary_sensor_update(binary_sensor::BinarySensor *obj
|
|
284
|
+
void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override;
|
|
273
285
|
|
|
274
286
|
/// Handle a binary sensor request under '/binary_sensor/<id>'.
|
|
275
287
|
void handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
|
@@ -468,21 +480,24 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
|
468
480
|
#endif
|
|
469
481
|
|
|
470
482
|
/// Override the web handler's canHandle method.
|
|
471
|
-
bool canHandle(AsyncWebServerRequest *request) override;
|
|
483
|
+
bool canHandle(AsyncWebServerRequest *request) const override;
|
|
472
484
|
/// Override the web handler's handleRequest method.
|
|
473
485
|
void handleRequest(AsyncWebServerRequest *request) override;
|
|
474
486
|
/// This web handle is not trivial.
|
|
475
|
-
bool isRequestHandlerTrivial() override; // NOLINT(readability-identifier-naming)
|
|
487
|
+
bool isRequestHandlerTrivial() const override; // NOLINT(readability-identifier-naming)
|
|
476
488
|
|
|
489
|
+
#ifdef USE_WEBSERVER_SORTING
|
|
477
490
|
void add_entity_config(EntityBase *entity, float weight, uint64_t group);
|
|
478
491
|
void add_sorting_group(uint64_t group_id, const std::string &group_name, float weight);
|
|
479
492
|
|
|
480
493
|
std::map<EntityBase *, SortingComponents> sorting_entitys_;
|
|
481
494
|
std::map<uint64_t, SortingGroup> sorting_groups_;
|
|
495
|
+
#endif
|
|
496
|
+
|
|
482
497
|
bool include_internal_{false};
|
|
483
498
|
|
|
484
499
|
protected:
|
|
485
|
-
void
|
|
500
|
+
void add_sorting_info_(JsonObject &root, EntityBase *entity);
|
|
486
501
|
web_server_base::WebServerBase *base_;
|
|
487
502
|
#ifdef USE_ARDUINO
|
|
488
503
|
DeferredUpdateEventSourceList events_;
|
|
@@ -501,12 +516,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
|
|
501
516
|
#ifdef USE_WEBSERVER_JS_INCLUDE
|
|
502
517
|
const char *js_include_{nullptr};
|
|
503
518
|
#endif
|
|
504
|
-
bool allow_ota_{true};
|
|
505
519
|
bool expose_log_{true};
|
|
506
|
-
#ifdef USE_ESP32
|
|
507
|
-
std::deque<std::function<void()>> to_schedule_;
|
|
508
|
-
SemaphoreHandle_t to_schedule_lock_;
|
|
509
|
-
#endif
|
|
510
520
|
};
|
|
511
521
|
|
|
512
522
|
} // namespace web_server
|
|
@@ -192,11 +192,10 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
|
|
|
192
192
|
|
|
193
193
|
stream->print(F("</tbody></table><p>See <a href=\"https://esphome.io/web-api/index.html\">ESPHome Web API</a> for "
|
|
194
194
|
"REST API documentation.</p>"));
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
195
|
+
#ifdef USE_WEBSERVER_OTA
|
|
196
|
+
stream->print(F("<h2>OTA Update</h2><form method=\"POST\" action=\"/update\" enctype=\"multipart/form-data\"><input "
|
|
197
|
+
"type=\"file\" name=\"update\"><input type=\"submit\" value=\"Update\"></form>"));
|
|
198
|
+
#endif
|
|
200
199
|
stream->print(F("<h2>Debug Log</h2><pre id=\"log\"></pre>"));
|
|
201
200
|
#ifdef USE_WEBSERVER_JS_INCLUDE
|
|
202
201
|
if (this->js_include_ != nullptr) {
|
|
@@ -30,11 +30,14 @@ CONFIG_SCHEMA = cv.Schema(
|
|
|
30
30
|
async def to_code(config):
|
|
31
31
|
var = cg.new_Pvariable(config[CONF_ID])
|
|
32
32
|
await cg.register_component(var, config)
|
|
33
|
+
cg.add(cg.RawExpression(f"{web_server_base_ns}::global_web_server_base = {var}"))
|
|
33
34
|
|
|
34
35
|
if CORE.using_arduino:
|
|
35
36
|
if CORE.is_esp32:
|
|
36
37
|
cg.add_library("WiFi", None)
|
|
37
38
|
cg.add_library("FS", None)
|
|
38
39
|
cg.add_library("Update", None)
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
if CORE.is_esp8266:
|
|
41
|
+
cg.add_library("ESP8266WiFi", None)
|
|
42
|
+
# https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json
|
|
43
|
+
cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.8")
|
|
@@ -4,21 +4,13 @@
|
|
|
4
4
|
#include "esphome/core/helpers.h"
|
|
5
5
|
#include "esphome/core/log.h"
|
|
6
6
|
|
|
7
|
-
#ifdef USE_ARDUINO
|
|
8
|
-
#include <StreamString.h>
|
|
9
|
-
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
|
10
|
-
#include <Update.h>
|
|
11
|
-
#endif
|
|
12
|
-
#ifdef USE_ESP8266
|
|
13
|
-
#include <Updater.h>
|
|
14
|
-
#endif
|
|
15
|
-
#endif
|
|
16
|
-
|
|
17
7
|
namespace esphome {
|
|
18
8
|
namespace web_server_base {
|
|
19
9
|
|
|
20
10
|
static const char *const TAG = "web_server_base";
|
|
21
11
|
|
|
12
|
+
WebServerBase *global_web_server_base = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
13
|
+
|
|
22
14
|
void WebServerBase::add_handler(AsyncWebHandler *handler) {
|
|
23
15
|
// remove all handlers
|
|
24
16
|
|
|
@@ -31,90 +23,6 @@ void WebServerBase::add_handler(AsyncWebHandler *handler) {
|
|
|
31
23
|
}
|
|
32
24
|
}
|
|
33
25
|
|
|
34
|
-
void report_ota_error() {
|
|
35
|
-
#ifdef USE_ARDUINO
|
|
36
|
-
StreamString ss;
|
|
37
|
-
Update.printError(ss);
|
|
38
|
-
ESP_LOGW(TAG, "OTA Update failed! Error: %s", ss.c_str());
|
|
39
|
-
#endif
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index,
|
|
43
|
-
uint8_t *data, size_t len, bool final) {
|
|
44
|
-
#ifdef USE_ARDUINO
|
|
45
|
-
bool success;
|
|
46
|
-
if (index == 0) {
|
|
47
|
-
ESP_LOGI(TAG, "OTA Update Start: %s", filename.c_str());
|
|
48
|
-
this->ota_read_length_ = 0;
|
|
49
|
-
#ifdef USE_ESP8266
|
|
50
|
-
Update.runAsync(true);
|
|
51
|
-
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
|
52
|
-
success = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
|
|
53
|
-
#endif
|
|
54
|
-
#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_LIBRETINY)
|
|
55
|
-
if (Update.isRunning()) {
|
|
56
|
-
Update.abort();
|
|
57
|
-
}
|
|
58
|
-
success = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH);
|
|
59
|
-
#endif
|
|
60
|
-
if (!success) {
|
|
61
|
-
report_ota_error();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
} else if (Update.hasError()) {
|
|
65
|
-
// don't spam logs with errors if something failed at start
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
success = Update.write(data, len) == len;
|
|
70
|
-
if (!success) {
|
|
71
|
-
report_ota_error();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
this->ota_read_length_ += len;
|
|
75
|
-
|
|
76
|
-
const uint32_t now = millis();
|
|
77
|
-
if (now - this->last_ota_progress_ > 1000) {
|
|
78
|
-
if (request->contentLength() != 0) {
|
|
79
|
-
float percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
|
|
80
|
-
ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
|
|
81
|
-
} else {
|
|
82
|
-
ESP_LOGD(TAG, "OTA in progress: %u bytes read", this->ota_read_length_);
|
|
83
|
-
}
|
|
84
|
-
this->last_ota_progress_ = now;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (final) {
|
|
88
|
-
if (Update.end(true)) {
|
|
89
|
-
ESP_LOGI(TAG, "OTA update successful!");
|
|
90
|
-
this->parent_->set_timeout(100, []() { App.safe_reboot(); });
|
|
91
|
-
} else {
|
|
92
|
-
report_ota_error();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
#endif
|
|
96
|
-
}
|
|
97
|
-
void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) {
|
|
98
|
-
#ifdef USE_ARDUINO
|
|
99
|
-
AsyncWebServerResponse *response;
|
|
100
|
-
if (!Update.hasError()) {
|
|
101
|
-
response = request->beginResponse(200, "text/plain", "Update Successful!");
|
|
102
|
-
} else {
|
|
103
|
-
StreamString ss;
|
|
104
|
-
ss.print("Update Failed: ");
|
|
105
|
-
Update.printError(ss);
|
|
106
|
-
response = request->beginResponse(200, "text/plain", ss);
|
|
107
|
-
}
|
|
108
|
-
response->addHeader("Connection", "close");
|
|
109
|
-
request->send(response);
|
|
110
|
-
#endif
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
void WebServerBase::add_ota_handler() {
|
|
114
|
-
#ifdef USE_ARDUINO
|
|
115
|
-
this->add_handler(new OTARequestHandler(this)); // NOLINT
|
|
116
|
-
#endif
|
|
117
|
-
}
|
|
118
26
|
float WebServerBase::get_setup_priority() const {
|
|
119
27
|
// Before WiFi (captive portal)
|
|
120
28
|
return setup_priority::WIFI + 2.0f;
|
|
@@ -17,13 +17,16 @@
|
|
|
17
17
|
namespace esphome {
|
|
18
18
|
namespace web_server_base {
|
|
19
19
|
|
|
20
|
+
class WebServerBase;
|
|
21
|
+
extern WebServerBase *global_web_server_base; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
22
|
+
|
|
20
23
|
namespace internal {
|
|
21
24
|
|
|
22
25
|
class MiddlewareHandler : public AsyncWebHandler {
|
|
23
26
|
public:
|
|
24
27
|
MiddlewareHandler(AsyncWebHandler *next) : next_(next) {}
|
|
25
28
|
|
|
26
|
-
bool canHandle(AsyncWebServerRequest *request) override { return next_->canHandle(request); }
|
|
29
|
+
bool canHandle(AsyncWebServerRequest *request) const override { return next_->canHandle(request); }
|
|
27
30
|
void handleRequest(AsyncWebServerRequest *request) override { next_->handleRequest(request); }
|
|
28
31
|
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
|
|
29
32
|
bool final) override {
|
|
@@ -32,7 +35,7 @@ class MiddlewareHandler : public AsyncWebHandler {
|
|
|
32
35
|
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override {
|
|
33
36
|
next_->handleBody(request, data, len, index, total);
|
|
34
37
|
}
|
|
35
|
-
bool isRequestHandlerTrivial() override { return next_->isRequestHandlerTrivial(); }
|
|
38
|
+
bool isRequestHandlerTrivial() const override { return next_->isRequestHandlerTrivial(); }
|
|
36
39
|
|
|
37
40
|
protected:
|
|
38
41
|
AsyncWebHandler *next_;
|
|
@@ -110,14 +113,10 @@ class WebServerBase : public Component {
|
|
|
110
113
|
|
|
111
114
|
void add_handler(AsyncWebHandler *handler);
|
|
112
115
|
|
|
113
|
-
void add_ota_handler();
|
|
114
|
-
|
|
115
116
|
void set_port(uint16_t port) { port_ = port; }
|
|
116
117
|
uint16_t get_port() const { return port_; }
|
|
117
118
|
|
|
118
119
|
protected:
|
|
119
|
-
friend class OTARequestHandler;
|
|
120
|
-
|
|
121
120
|
int initialized_{0};
|
|
122
121
|
uint16_t port_{80};
|
|
123
122
|
std::shared_ptr<AsyncWebServer> server_{nullptr};
|
|
@@ -125,25 +124,6 @@ class WebServerBase : public Component {
|
|
|
125
124
|
internal::Credentials credentials_;
|
|
126
125
|
};
|
|
127
126
|
|
|
128
|
-
class OTARequestHandler : public AsyncWebHandler {
|
|
129
|
-
public:
|
|
130
|
-
OTARequestHandler(WebServerBase *parent) : parent_(parent) {}
|
|
131
|
-
void handleRequest(AsyncWebServerRequest *request) override;
|
|
132
|
-
void handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len,
|
|
133
|
-
bool final) override;
|
|
134
|
-
bool canHandle(AsyncWebServerRequest *request) override {
|
|
135
|
-
return request->url() == "/update" && request->method() == HTTP_POST;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// NOLINTNEXTLINE(readability-identifier-naming)
|
|
139
|
-
bool isRequestHandlerTrivial() override { return false; }
|
|
140
|
-
|
|
141
|
-
protected:
|
|
142
|
-
uint32_t last_ota_progress_{0};
|
|
143
|
-
uint32_t ota_read_length_{0};
|
|
144
|
-
WebServerBase *parent_;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
127
|
} // namespace web_server_base
|
|
148
128
|
} // namespace esphome
|
|
149
129
|
#endif
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#include "esphome/core/defines.h"
|
|
2
|
+
#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
|
|
3
|
+
#include "multipart.h"
|
|
4
|
+
#include "utils.h"
|
|
5
|
+
#include "esphome/core/log.h"
|
|
6
|
+
#include <cstring>
|
|
7
|
+
#include "multipart_parser.h"
|
|
8
|
+
|
|
9
|
+
namespace esphome {
|
|
10
|
+
namespace web_server_idf {
|
|
11
|
+
|
|
12
|
+
static const char *const TAG = "multipart";
|
|
13
|
+
|
|
14
|
+
// ========== MultipartReader Implementation ==========
|
|
15
|
+
|
|
16
|
+
MultipartReader::MultipartReader(const std::string &boundary) {
|
|
17
|
+
// Initialize settings with callbacks
|
|
18
|
+
memset(&settings_, 0, sizeof(settings_));
|
|
19
|
+
settings_.on_header_field = on_header_field;
|
|
20
|
+
settings_.on_header_value = on_header_value;
|
|
21
|
+
settings_.on_part_data = on_part_data;
|
|
22
|
+
settings_.on_part_data_end = on_part_data_end;
|
|
23
|
+
|
|
24
|
+
ESP_LOGV(TAG, "Initializing multipart parser with boundary: '%s' (len: %zu)", boundary.c_str(), boundary.length());
|
|
25
|
+
|
|
26
|
+
// Create parser with boundary
|
|
27
|
+
parser_ = multipart_parser_init(boundary.c_str(), &settings_);
|
|
28
|
+
if (parser_) {
|
|
29
|
+
multipart_parser_set_data(parser_, this);
|
|
30
|
+
} else {
|
|
31
|
+
ESP_LOGE(TAG, "Failed to initialize multipart parser");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
MultipartReader::~MultipartReader() {
|
|
36
|
+
if (parser_) {
|
|
37
|
+
multipart_parser_free(parser_);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
size_t MultipartReader::parse(const char *data, size_t len) {
|
|
42
|
+
if (!parser_) {
|
|
43
|
+
ESP_LOGE(TAG, "Parser not initialized");
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
size_t parsed = multipart_parser_execute(parser_, data, len);
|
|
48
|
+
|
|
49
|
+
if (parsed != len) {
|
|
50
|
+
ESP_LOGW(TAG, "Parser consumed %zu of %zu bytes - possible error", parsed, len);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void MultipartReader::process_header_(const char *value, size_t length) {
|
|
57
|
+
// Process the completed header (field + value pair)
|
|
58
|
+
std::string value_str(value, length);
|
|
59
|
+
|
|
60
|
+
if (str_startswith_case_insensitive(current_header_field_, "content-disposition")) {
|
|
61
|
+
// Parse name and filename from Content-Disposition
|
|
62
|
+
current_part_.name = extract_header_param(value_str, "name");
|
|
63
|
+
current_part_.filename = extract_header_param(value_str, "filename");
|
|
64
|
+
} else if (str_startswith_case_insensitive(current_header_field_, "content-type")) {
|
|
65
|
+
current_part_.content_type = str_trim(value_str);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Clear field for next header
|
|
69
|
+
current_header_field_.clear();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
int MultipartReader::on_header_field(multipart_parser *parser, const char *at, size_t length) {
|
|
73
|
+
MultipartReader *reader = static_cast<MultipartReader *>(multipart_parser_get_data(parser));
|
|
74
|
+
reader->current_header_field_.assign(at, length);
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
int MultipartReader::on_header_value(multipart_parser *parser, const char *at, size_t length) {
|
|
79
|
+
MultipartReader *reader = static_cast<MultipartReader *>(multipart_parser_get_data(parser));
|
|
80
|
+
reader->process_header_(at, length);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
int MultipartReader::on_part_data(multipart_parser *parser, const char *at, size_t length) {
|
|
85
|
+
MultipartReader *reader = static_cast<MultipartReader *>(multipart_parser_get_data(parser));
|
|
86
|
+
// Only process file uploads
|
|
87
|
+
if (reader->has_file() && reader->data_callback_) {
|
|
88
|
+
// IMPORTANT: The 'at' pointer points to data within the parser's input buffer.
|
|
89
|
+
// This data is only valid during this callback. The callback handler MUST
|
|
90
|
+
// process or copy the data immediately - it cannot store the pointer for
|
|
91
|
+
// later use as the buffer will be overwritten.
|
|
92
|
+
reader->data_callback_(reinterpret_cast<const uint8_t *>(at), length);
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
int MultipartReader::on_part_data_end(multipart_parser *parser) {
|
|
98
|
+
MultipartReader *reader = static_cast<MultipartReader *>(multipart_parser_get_data(parser));
|
|
99
|
+
ESP_LOGV(TAG, "Part data end");
|
|
100
|
+
if (reader->part_complete_callback_) {
|
|
101
|
+
reader->part_complete_callback_();
|
|
102
|
+
}
|
|
103
|
+
// Clear part info for next part
|
|
104
|
+
reader->current_part_ = Part{};
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ========== Utility Functions ==========
|
|
109
|
+
|
|
110
|
+
// Case-insensitive string prefix check
|
|
111
|
+
bool str_startswith_case_insensitive(const std::string &str, const std::string &prefix) {
|
|
112
|
+
if (str.length() < prefix.length()) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return str_ncmp_ci(str.c_str(), prefix.c_str(), prefix.length());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Extract a parameter value from a header line
|
|
119
|
+
// Handles both quoted and unquoted values
|
|
120
|
+
std::string extract_header_param(const std::string &header, const std::string ¶m) {
|
|
121
|
+
size_t search_pos = 0;
|
|
122
|
+
|
|
123
|
+
while (search_pos < header.length()) {
|
|
124
|
+
// Look for param name
|
|
125
|
+
const char *found = stristr(header.c_str() + search_pos, param.c_str());
|
|
126
|
+
if (!found) {
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
size_t pos = found - header.c_str();
|
|
130
|
+
|
|
131
|
+
// Check if this is a word boundary (not part of another parameter)
|
|
132
|
+
if (pos > 0 && header[pos - 1] != ' ' && header[pos - 1] != ';' && header[pos - 1] != '\t') {
|
|
133
|
+
search_pos = pos + 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Move past param name
|
|
138
|
+
pos += param.length();
|
|
139
|
+
|
|
140
|
+
// Skip whitespace and find '='
|
|
141
|
+
while (pos < header.length() && (header[pos] == ' ' || header[pos] == '\t')) {
|
|
142
|
+
pos++;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (pos >= header.length() || header[pos] != '=') {
|
|
146
|
+
search_pos = pos;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pos++; // Skip '='
|
|
151
|
+
|
|
152
|
+
// Skip whitespace after '='
|
|
153
|
+
while (pos < header.length() && (header[pos] == ' ' || header[pos] == '\t')) {
|
|
154
|
+
pos++;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (pos >= header.length()) {
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Check if value is quoted
|
|
162
|
+
if (header[pos] == '"') {
|
|
163
|
+
pos++;
|
|
164
|
+
size_t end = header.find('"', pos);
|
|
165
|
+
if (end != std::string::npos) {
|
|
166
|
+
return header.substr(pos, end - pos);
|
|
167
|
+
}
|
|
168
|
+
// Malformed - no closing quote
|
|
169
|
+
return "";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Unquoted value - find the end (semicolon, comma, or end of string)
|
|
173
|
+
size_t end = pos;
|
|
174
|
+
while (end < header.length() && header[end] != ';' && header[end] != ',' && header[end] != ' ' &&
|
|
175
|
+
header[end] != '\t') {
|
|
176
|
+
end++;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return header.substr(pos, end - pos);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return "";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Parse boundary from Content-Type header
|
|
186
|
+
// Returns true if boundary found, false otherwise
|
|
187
|
+
// boundary_start and boundary_len will point to the boundary value
|
|
188
|
+
bool parse_multipart_boundary(const char *content_type, const char **boundary_start, size_t *boundary_len) {
|
|
189
|
+
if (!content_type) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check for multipart/form-data (case-insensitive)
|
|
194
|
+
if (!stristr(content_type, "multipart/form-data")) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Look for boundary parameter
|
|
199
|
+
const char *b = stristr(content_type, "boundary=");
|
|
200
|
+
if (!b) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const char *start = b + 9; // Skip "boundary="
|
|
205
|
+
|
|
206
|
+
// Skip whitespace
|
|
207
|
+
while (*start == ' ' || *start == '\t') {
|
|
208
|
+
start++;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!*start) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Find end of boundary
|
|
216
|
+
const char *end = start;
|
|
217
|
+
if (*end == '"') {
|
|
218
|
+
// Quoted boundary
|
|
219
|
+
start++;
|
|
220
|
+
end++;
|
|
221
|
+
while (*end && *end != '"') {
|
|
222
|
+
end++;
|
|
223
|
+
}
|
|
224
|
+
*boundary_len = end - start;
|
|
225
|
+
} else {
|
|
226
|
+
// Unquoted boundary
|
|
227
|
+
while (*end && *end != ' ' && *end != ';' && *end != '\r' && *end != '\n' && *end != '\t') {
|
|
228
|
+
end++;
|
|
229
|
+
}
|
|
230
|
+
*boundary_len = end - start;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (*boundary_len == 0) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
*boundary_start = start;
|
|
238
|
+
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Trim whitespace from both ends of a string
|
|
243
|
+
std::string str_trim(const std::string &str) {
|
|
244
|
+
size_t start = str.find_first_not_of(" \t\r\n");
|
|
245
|
+
if (start == std::string::npos) {
|
|
246
|
+
return "";
|
|
247
|
+
}
|
|
248
|
+
size_t end = str.find_last_not_of(" \t\r\n");
|
|
249
|
+
return str.substr(start, end - start + 1);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
} // namespace web_server_idf
|
|
253
|
+
} // namespace esphome
|
|
254
|
+
#endif // defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "esphome/core/defines.h"
|
|
3
|
+
#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
|
|
4
|
+
|
|
5
|
+
#include <cctype>
|
|
6
|
+
#include <cstring>
|
|
7
|
+
#include <esp_http_server.h>
|
|
8
|
+
#include <functional>
|
|
9
|
+
#include <multipart_parser.h>
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <utility>
|
|
12
|
+
|
|
13
|
+
namespace esphome {
|
|
14
|
+
namespace web_server_idf {
|
|
15
|
+
|
|
16
|
+
// Wrapper around zorxx/multipart-parser for ESP-IDF OTA uploads
|
|
17
|
+
class MultipartReader {
|
|
18
|
+
public:
|
|
19
|
+
struct Part {
|
|
20
|
+
std::string name;
|
|
21
|
+
std::string filename;
|
|
22
|
+
std::string content_type;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// IMPORTANT: The data pointer in DataCallback is only valid during the callback!
|
|
26
|
+
// The multipart parser passes pointers to its internal buffer which will be
|
|
27
|
+
// overwritten after the callback returns. Callbacks MUST process or copy the
|
|
28
|
+
// data immediately - storing the pointer for deferred processing will result
|
|
29
|
+
// in use-after-free bugs.
|
|
30
|
+
using DataCallback = std::function<void(const uint8_t *data, size_t len)>;
|
|
31
|
+
using PartCompleteCallback = std::function<void()>;
|
|
32
|
+
|
|
33
|
+
explicit MultipartReader(const std::string &boundary);
|
|
34
|
+
~MultipartReader();
|
|
35
|
+
|
|
36
|
+
// Set callbacks for handling data
|
|
37
|
+
void set_data_callback(DataCallback callback) { data_callback_ = std::move(callback); }
|
|
38
|
+
void set_part_complete_callback(PartCompleteCallback callback) { part_complete_callback_ = std::move(callback); }
|
|
39
|
+
|
|
40
|
+
// Parse incoming data
|
|
41
|
+
size_t parse(const char *data, size_t len);
|
|
42
|
+
|
|
43
|
+
// Get current part info
|
|
44
|
+
const Part &get_current_part() const { return current_part_; }
|
|
45
|
+
|
|
46
|
+
// Check if we found a file upload
|
|
47
|
+
bool has_file() const { return !current_part_.filename.empty(); }
|
|
48
|
+
|
|
49
|
+
private:
|
|
50
|
+
static int on_header_field(multipart_parser *parser, const char *at, size_t length);
|
|
51
|
+
static int on_header_value(multipart_parser *parser, const char *at, size_t length);
|
|
52
|
+
static int on_part_data(multipart_parser *parser, const char *at, size_t length);
|
|
53
|
+
static int on_part_data_end(multipart_parser *parser);
|
|
54
|
+
|
|
55
|
+
multipart_parser *parser_{nullptr};
|
|
56
|
+
multipart_parser_settings settings_{};
|
|
57
|
+
|
|
58
|
+
Part current_part_;
|
|
59
|
+
std::string current_header_field_;
|
|
60
|
+
|
|
61
|
+
DataCallback data_callback_;
|
|
62
|
+
PartCompleteCallback part_complete_callback_;
|
|
63
|
+
|
|
64
|
+
void process_header_(const char *value, size_t length);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ========== Utility Functions ==========
|
|
68
|
+
|
|
69
|
+
// Case-insensitive string prefix check
|
|
70
|
+
bool str_startswith_case_insensitive(const std::string &str, const std::string &prefix);
|
|
71
|
+
|
|
72
|
+
// Extract a parameter value from a header line
|
|
73
|
+
// Handles both quoted and unquoted values
|
|
74
|
+
std::string extract_header_param(const std::string &header, const std::string ¶m);
|
|
75
|
+
|
|
76
|
+
// Parse boundary from Content-Type header
|
|
77
|
+
// Returns true if boundary found, false otherwise
|
|
78
|
+
// boundary_start and boundary_len will point to the boundary value
|
|
79
|
+
bool parse_multipart_boundary(const char *content_type, const char **boundary_start, size_t *boundary_len);
|
|
80
|
+
|
|
81
|
+
// Trim whitespace from both ends of a string
|
|
82
|
+
std::string str_trim(const std::string &str);
|
|
83
|
+
|
|
84
|
+
} // namespace web_server_idf
|
|
85
|
+
} // namespace esphome
|
|
86
|
+
#endif // defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
|