esphome 2025.7.4__py3-none-any.whl → 2025.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +190 -83
- esphome/automation.py +2 -4
- esphome/build_gen/__init__.py +0 -0
- esphome/build_gen/platformio.py +102 -0
- esphome/components/a4988/a4988.cpp +0 -1
- esphome/components/absolute_humidity/absolute_humidity.cpp +0 -2
- esphome/components/absolute_humidity/sensor.py +2 -2
- esphome/components/adc/__init__.py +123 -85
- esphome/components/adc/adc_sensor.h +98 -35
- esphome/components/adc/adc_sensor_common.cpp +10 -4
- esphome/components/adc/adc_sensor_esp32.cpp +291 -123
- esphome/components/adc/adc_sensor_esp8266.cpp +1 -4
- esphome/components/adc/adc_sensor_libretiny.cpp +1 -2
- esphome/components/adc/adc_sensor_rp2040.cpp +1 -2
- esphome/components/adc/adc_sensor_zephyr.cpp +207 -0
- esphome/components/adc/sensor.py +61 -27
- esphome/components/adc128s102/adc128s102.cpp +1 -4
- esphome/components/ade7880/sensor.py +75 -49
- esphome/components/ads1115/ads1115.cpp +0 -1
- esphome/components/ads1118/ads1118.cpp +0 -1
- esphome/components/ags10/ags10.cpp +0 -4
- esphome/components/aht10/aht10.cpp +0 -4
- esphome/components/aic3204/aic3204.cpp +0 -2
- esphome/components/airthings_wave_plus/__init__.py +1 -1
- esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +22 -4
- esphome/components/airthings_wave_plus/airthings_wave_plus.h +10 -1
- esphome/components/airthings_wave_plus/sensor.py +55 -28
- esphome/components/alarm_control_panel/__init__.py +4 -9
- esphome/components/am2315c/am2315c.cpp +0 -2
- esphome/components/am2320/am2320.cpp +0 -1
- esphome/components/animation/__init__.py +14 -11
- esphome/components/apds9306/apds9306.cpp +0 -4
- esphome/components/apds9960/apds9960.cpp +0 -1
- esphome/components/api/__init__.py +29 -4
- esphome/components/api/api_connection.cpp +378 -401
- esphome/components/api/api_connection.h +112 -56
- esphome/components/api/api_frame_helper.cpp +69 -896
- esphome/components/api/api_frame_helper.h +31 -126
- esphome/components/api/api_frame_helper_noise.cpp +583 -0
- esphome/components/api/api_frame_helper_noise.h +68 -0
- esphome/components/api/api_frame_helper_plaintext.cpp +290 -0
- esphome/components/api/api_frame_helper_plaintext.h +53 -0
- esphome/components/api/api_noise_context.h +2 -4
- esphome/components/api/api_pb2.cpp +1601 -1808
- esphome/components/api/api_pb2.h +367 -323
- esphome/components/api/api_pb2_dump.cpp +1137 -3466
- esphome/components/api/api_pb2_includes.h +34 -0
- esphome/components/api/api_pb2_service.cpp +94 -105
- esphome/components/api/api_pb2_service.h +27 -16
- esphome/components/api/api_server.cpp +18 -17
- esphome/components/api/api_server.h +8 -5
- esphome/components/api/client.py +16 -8
- esphome/components/api/custom_api_device.h +68 -14
- esphome/components/api/homeassistant_service.h +24 -19
- esphome/components/api/list_entities.cpp +3 -5
- esphome/components/api/list_entities.h +2 -4
- esphome/components/api/proto.cpp +3 -5
- esphome/components/api/proto.h +239 -274
- esphome/components/api/subscribe_state.cpp +2 -4
- esphome/components/api/subscribe_state.h +2 -4
- esphome/components/api/user_services.cpp +2 -4
- esphome/components/api/user_services.h +8 -8
- esphome/components/as3935/as3935.cpp +0 -2
- esphome/components/as3935_spi/as3935_spi.cpp +0 -2
- esphome/components/as5600/__init__.py +1 -1
- esphome/components/as5600/as5600.cpp +0 -2
- esphome/components/as5600/sensor/__init__.py +0 -1
- esphome/components/as7341/as7341.cpp +0 -1
- esphome/components/async_tcp/__init__.py +1 -1
- esphome/components/at581x/at581x.cpp +1 -1
- esphome/components/atm90e26/atm90e26.cpp +0 -1
- esphome/components/atm90e32/atm90e32.cpp +475 -116
- esphome/components/atm90e32/atm90e32.h +43 -5
- esphome/components/audio/audio.h +2 -2
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +0 -2
- esphome/components/beken_spi_led_strip/led_strip.cpp +0 -2
- esphome/components/bh1750/bh1750.cpp +0 -1
- esphome/components/binary_sensor/__init__.py +14 -12
- esphome/components/ble_client/__init__.py +4 -7
- esphome/components/bluetooth_proxy/__init__.py +40 -3
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +392 -81
- esphome/components/bluetooth_proxy/bluetooth_connection.h +16 -5
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +102 -311
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +30 -14
- esphome/components/bme280_base/bme280_base.cpp +15 -16
- esphome/components/bme680/bme680.cpp +2 -3
- esphome/components/bme680_bsec/bme680_bsec.cpp +0 -2
- esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +0 -2
- esphome/components/bmi160/bmi160.cpp +0 -1
- esphome/components/bmp085/bmp085.cpp +0 -1
- esphome/components/bmp280_base/bmp280_base.cpp +13 -14
- esphome/components/bmp3xx_base/bmp3xx_base.cpp +0 -1
- esphome/components/bmp581/bmp581.cpp +0 -2
- esphome/components/bp1658cj/bp1658cj.cpp +0 -1
- esphome/components/bp5758d/bp5758d.cpp +0 -1
- esphome/components/button/__init__.py +0 -1
- esphome/components/canbus/__init__.py +2 -3
- esphome/components/canbus/canbus.cpp +0 -1
- esphome/components/cap1188/cap1188.cpp +0 -2
- esphome/components/captive_portal/__init__.py +1 -1
- esphome/components/cd74hc4067/cd74hc4067.cpp +0 -2
- esphome/components/ch422g/ch422g.cpp +0 -1
- esphome/components/chsc6x/chsc6x_touchscreen.cpp +0 -3
- esphome/components/climate/__init__.py +0 -1
- esphome/components/climate/climate_traits.h +24 -0
- esphome/components/cm1106/cm1106.cpp +0 -1
- esphome/components/const/__init__.py +6 -0
- esphome/components/cover/__init__.py +0 -1
- esphome/components/cover/cover.cpp +9 -13
- esphome/components/cs5460a/cs5460a.cpp +0 -2
- esphome/components/cse7761/cse7761.cpp +0 -1
- esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +0 -2
- esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +0 -2
- esphome/components/dac7678/dac7678_output.cpp +0 -2
- esphome/components/dallas_temp/dallas_temp.cpp +0 -1
- esphome/components/datetime/__init__.py +0 -2
- esphome/components/debug/__init__.py +15 -1
- esphome/components/debug/debug_zephyr.cpp +281 -0
- esphome/components/debug/sensor.py +2 -1
- esphome/components/deep_sleep/deep_sleep_component.cpp +0 -1
- esphome/components/deep_sleep/deep_sleep_esp32.cpp +20 -1
- esphome/components/dfrobot_sen0395/__init__.py +1 -2
- esphome/components/dht/dht.cpp +0 -1
- esphome/components/dht12/dht12.cpp +0 -1
- esphome/components/display/__init__.py +16 -3
- esphome/components/display_menu_base/__init__.py +1 -1
- esphome/components/dps310/dps310.cpp +0 -2
- esphome/components/ds1307/ds1307.cpp +0 -1
- esphome/components/ds2484/ds2484.cpp +0 -1
- esphome/components/duty_cycle/duty_cycle_sensor.cpp +0 -1
- esphome/components/ee895/ee895.cpp +0 -1
- esphome/components/ektf2232/touchscreen/ektf2232.cpp +0 -1
- esphome/components/emc2101/emc2101.cpp +0 -2
- esphome/components/ens160_base/ens160_base.cpp +0 -2
- esphome/components/ens210/ens210.cpp +0 -1
- esphome/components/es7210/es7210.cpp +0 -2
- esphome/components/es7243e/es7243e.cpp +0 -2
- esphome/components/es8156/es8156.cpp +0 -2
- esphome/components/es8311/es8311.cpp +0 -2
- esphome/components/es8388/es8388.cpp +0 -2
- esphome/components/esp32/__init__.py +203 -60
- esphome/components/esp32/boards.py +17 -0
- esphome/components/esp32/gpio.cpp +0 -1
- esphome/components/esp32/gpio.py +1 -2
- esphome/components/esp32/gpio_esp32_h2.py +2 -7
- esphome/components/esp32/gpio_esp32_p4.py +2 -7
- esphome/components/esp32/post_build.py.script +112 -61
- esphome/components/esp32_ble/__init__.py +41 -2
- esphome/components/esp32_ble/ble.cpp +14 -10
- esphome/components/esp32_ble/ble.h +18 -18
- esphome/components/esp32_ble/ble_advertising.cpp +5 -5
- esphome/components/esp32_ble/ble_advertising.h +7 -5
- esphome/components/esp32_ble/ble_event.h +139 -73
- esphome/components/esp32_ble/ble_scan_result.h +2 -4
- esphome/components/esp32_ble/ble_uuid.cpp +5 -5
- esphome/components/esp32_ble/ble_uuid.h +6 -5
- esphome/components/esp32_ble_beacon/__init__.py +4 -0
- esphome/components/esp32_ble_client/__init__.py +1 -1
- esphome/components/esp32_ble_client/ble_characteristic.cpp +4 -4
- esphome/components/esp32_ble_client/ble_characteristic.h +6 -4
- esphome/components/esp32_ble_client/ble_client_base.cpp +155 -104
- esphome/components/esp32_ble_client/ble_client_base.h +17 -6
- esphome/components/esp32_ble_client/ble_descriptor.h +6 -4
- esphome/components/esp32_ble_client/ble_service.cpp +4 -4
- esphome/components/esp32_ble_client/ble_service.h +6 -4
- esphome/components/esp32_ble_server/__init__.py +15 -12
- esphome/components/esp32_ble_tracker/__init__.py +79 -11
- esphome/components/esp32_ble_tracker/automation.h +4 -4
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +264 -261
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +103 -37
- esphome/components/esp32_camera/__init__.py +13 -1
- esphome/components/esp32_camera/esp32_camera.cpp +7 -2
- esphome/components/esp32_camera/esp32_camera.h +1 -0
- esphome/components/esp32_dac/esp32_dac.cpp +3 -19
- esphome/components/esp32_dac/esp32_dac.h +4 -8
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -6
- esphome/components/esp32_rmt_led_strip/light.py +1 -1
- esphome/components/esp32_touch/__init__.py +2 -3
- esphome/components/esp32_touch/esp32_touch.h +9 -6
- esphome/components/esp32_touch/esp32_touch_common.cpp +2 -0
- esphome/components/esp32_touch/esp32_touch_v1.cpp +7 -9
- esphome/components/esp32_touch/esp32_touch_v2.cpp +10 -6
- esphome/components/esp8266/__init__.py +3 -1
- esphome/components/esp8266_pwm/esp8266_pwm.cpp +0 -1
- esphome/components/esphome/ota/__init__.py +1 -2
- esphome/components/esphome/ota/ota_esphome.cpp +150 -77
- esphome/components/esphome/ota/ota_esphome.h +8 -1
- esphome/components/espnow/__init__.py +309 -0
- esphome/components/espnow/automation.h +175 -0
- esphome/components/espnow/espnow_component.cpp +468 -0
- esphome/components/espnow/espnow_component.h +183 -0
- esphome/components/espnow/espnow_err.h +19 -0
- esphome/components/espnow/espnow_packet.h +166 -0
- esphome/components/ethernet/__init__.py +7 -1
- esphome/components/ethernet/esp_eth_phy_jl1101.c +5 -0
- esphome/components/ethernet/ethernet_component.cpp +0 -1
- esphome/components/ethernet/ethernet_component.h +4 -0
- esphome/components/ethernet_info/ethernet_info_text_sensor.h +0 -3
- esphome/components/event/__init__.py +0 -1
- esphome/components/factory_reset/__init__.py +92 -0
- esphome/components/factory_reset/factory_reset.cpp +76 -0
- esphome/components/factory_reset/factory_reset.h +43 -0
- esphome/components/fan/__init__.py +0 -1
- esphome/components/fan/fan_traits.h +16 -0
- esphome/components/fastled_base/fastled_light.cpp +0 -1
- esphome/components/fastled_spi/light.py +1 -3
- esphome/components/fingerprint_grow/fingerprint_grow.cpp +0 -2
- esphome/components/font/__init__.py +9 -1
- esphome/components/fs3000/fs3000.cpp +0 -2
- esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +0 -2
- esphome/components/ft63x6/ft63x6.cpp +0 -1
- esphome/components/gdk101/gdk101.cpp +0 -1
- esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +0 -1
- esphome/components/gl_r01_i2c/sensor.py +1 -1
- esphome/components/gpio/one_wire/gpio_one_wire.cpp +0 -1
- esphome/components/gpio/switch/gpio_switch.cpp +0 -2
- esphome/components/gpio_expander/cached_gpio.h +24 -15
- esphome/components/gps/__init__.py +6 -2
- esphome/components/gps/gps.cpp +50 -49
- esphome/components/gps/gps.h +4 -8
- esphome/components/gps/time/gps_time.cpp +3 -9
- esphome/components/gps/time/gps_time.h +4 -7
- esphome/components/graph/__init__.py +1 -1
- esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +0 -1
- esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +0 -1
- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +21 -12
- esphome/components/gt911/touchscreen/gt911_touchscreen.h +26 -2
- esphome/components/haier/climate.py +5 -10
- esphome/components/haier/haier_base.cpp +0 -1
- esphome/components/hbridge/switch/hbridge_switch.cpp +0 -2
- esphome/components/hdc1080/hdc1080.cpp +0 -2
- esphome/components/heatpumpir/climate.py +2 -2
- esphome/components/hlw8012/hlw8012.cpp +0 -1
- esphome/components/hm3301/hm3301.cpp +0 -1
- esphome/components/hmc5883l/hmc5883l.cpp +0 -1
- esphome/components/homeassistant/__init__.py +1 -0
- esphome/components/homeassistant/number/__init__.py +1 -0
- esphome/components/homeassistant/number/homeassistant_number.cpp +11 -7
- esphome/components/homeassistant/switch/__init__.py +1 -0
- esphome/components/homeassistant/switch/homeassistant_switch.cpp +9 -5
- esphome/components/honeywellabp/honeywellabp.cpp +1 -4
- esphome/components/host/__init__.py +2 -0
- esphome/components/hte501/hte501.cpp +0 -1
- esphome/components/http_request/__init__.py +2 -3
- esphome/components/http_request/http_request_idf.cpp +2 -2
- esphome/components/htu21d/htu21d.cpp +0 -2
- esphome/components/htu31d/htu31d.cpp +0 -2
- esphome/components/hx711/hx711.cpp +0 -1
- esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +0 -1
- esphome/components/hydreon_rgxx/sensor.py +4 -5
- esphome/components/i2c/i2c_bus.h +1 -1
- esphome/components/i2c/i2c_bus_arduino.cpp +1 -2
- esphome/components/i2c/i2c_bus_esp_idf.cpp +192 -17
- esphome/components/i2c/i2c_bus_esp_idf.h +11 -1
- esphome/components/i2s_audio/__init__.py +6 -5
- esphome/components/i2s_audio/i2s_audio.cpp +0 -2
- esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +1 -4
- esphome/components/i2s_audio/microphone/__init__.py +4 -6
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +46 -19
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +4 -3
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +273 -269
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +19 -34
- esphome/components/ili9xxx/display.py +4 -3
- esphome/components/ili9xxx/ili9xxx_display.cpp +0 -2
- esphome/components/image/__init__.py +123 -92
- esphome/components/improv_serial/__init__.py +7 -8
- esphome/components/ina219/ina219.cpp +0 -1
- esphome/components/ina226/ina226.cpp +0 -2
- esphome/components/ina260/ina260.cpp +0 -2
- esphome/components/ina2xx_base/__init__.py +2 -5
- esphome/components/ina2xx_base/ina2xx_base.cpp +0 -2
- esphome/components/ina3221/ina3221.cpp +0 -1
- esphome/components/internal_temperature/internal_temperature.cpp +0 -2
- esphome/components/interval/interval.h +5 -9
- esphome/components/json/__init__.py +1 -1
- esphome/components/kmeteriso/kmeteriso.cpp +0 -2
- esphome/components/lc709203f/lc709203f.cpp +0 -2
- esphome/components/lcd_gpio/display.py +1 -3
- esphome/components/lcd_gpio/gpio_lcd_display.cpp +0 -1
- esphome/components/lcd_pcf8574/pcf8574_display.cpp +0 -1
- esphome/components/ld2410/__init__.py +4 -6
- esphome/components/ld2410/binary_sensor.py +4 -0
- esphome/components/ld2410/ld2410.cpp +56 -100
- esphome/components/ld2410/ld2410.h +17 -15
- esphome/components/ld2410/sensor.py +24 -10
- esphome/components/ld2412/__init__.py +46 -0
- esphome/components/ld2412/binary_sensor.py +70 -0
- esphome/components/ld2412/button/__init__.py +74 -0
- esphome/components/ld2412/button/factory_reset_button.cpp +9 -0
- esphome/components/ld2412/button/factory_reset_button.h +18 -0
- esphome/components/ld2412/button/query_button.cpp +9 -0
- esphome/components/ld2412/button/query_button.h +18 -0
- esphome/components/ld2412/button/restart_button.cpp +9 -0
- esphome/components/ld2412/button/restart_button.h +18 -0
- esphome/components/ld2412/button/start_dynamic_background_correction_button.cpp +11 -0
- esphome/components/ld2412/button/start_dynamic_background_correction_button.h +18 -0
- esphome/components/ld2412/ld2412.cpp +861 -0
- esphome/components/ld2412/ld2412.h +141 -0
- esphome/components/ld2412/number/__init__.py +126 -0
- esphome/components/ld2412/number/gate_threshold_number.cpp +14 -0
- esphome/components/ld2412/number/gate_threshold_number.h +19 -0
- esphome/components/ld2412/number/light_threshold_number.cpp +12 -0
- esphome/components/ld2412/number/light_threshold_number.h +18 -0
- esphome/components/ld2412/number/max_distance_timeout_number.cpp +12 -0
- esphome/components/ld2412/number/max_distance_timeout_number.h +18 -0
- esphome/components/ld2412/select/__init__.py +82 -0
- esphome/components/ld2412/select/baud_rate_select.cpp +12 -0
- esphome/components/ld2412/select/baud_rate_select.h +18 -0
- esphome/components/ld2412/select/distance_resolution_select.cpp +12 -0
- esphome/components/ld2412/select/distance_resolution_select.h +18 -0
- esphome/components/ld2412/select/light_out_control_select.cpp +12 -0
- esphome/components/ld2412/select/light_out_control_select.h +18 -0
- esphome/components/ld2412/sensor.py +124 -0
- esphome/components/ld2412/switch/__init__.py +45 -0
- esphome/components/ld2412/switch/bluetooth_switch.cpp +12 -0
- esphome/components/ld2412/switch/bluetooth_switch.h +18 -0
- esphome/components/ld2412/switch/engineering_mode_switch.cpp +12 -0
- esphome/components/ld2412/switch/engineering_mode_switch.h +18 -0
- esphome/components/ld2412/text_sensor.py +34 -0
- esphome/components/ld2420/ld2420.cpp +0 -1
- esphome/components/ld2450/__init__.py +3 -4
- esphome/components/ld2450/binary_sensor.py +3 -0
- esphome/components/ld2450/ld2450.cpp +77 -165
- esphome/components/ld2450/ld2450.h +26 -54
- esphome/components/ld2450/sensor.py +120 -6
- esphome/components/ld2450/text_sensor.py +5 -4
- esphome/components/ld24xx/__init__.py +1 -0
- esphome/components/ld24xx/ld24xx.h +65 -0
- esphome/components/ledc/ledc_output.cpp +0 -1
- esphome/components/libretiny/__init__.py +2 -0
- esphome/components/light/__init__.py +0 -1
- esphome/components/light/effects.py +70 -45
- esphome/components/light/light_call.cpp +101 -66
- esphome/components/light/light_color_values.h +16 -11
- esphome/components/light/light_json_schema.cpp +46 -44
- esphome/components/light/light_state.cpp +8 -11
- esphome/components/light/light_traits.h +17 -0
- esphome/components/lightwaverf/lightwaverf.cpp +0 -2
- esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +0 -1
- esphome/components/lock/__init__.py +0 -1
- esphome/components/logger/__init__.py +31 -9
- esphome/components/logger/logger.cpp +12 -7
- esphome/components/logger/logger.h +25 -14
- esphome/components/logger/logger_esp32.cpp +2 -7
- esphome/components/logger/logger_esp8266.cpp +2 -4
- esphome/components/logger/logger_host.cpp +2 -4
- esphome/components/logger/logger_libretiny.cpp +2 -4
- esphome/components/logger/logger_rp2040.cpp +2 -4
- esphome/components/logger/logger_zephyr.cpp +86 -0
- esphome/components/logger/select/logger_level_select.cpp +2 -4
- esphome/components/logger/select/logger_level_select.h +2 -4
- esphome/components/logger/task_log_buffer.cpp +2 -4
- esphome/components/logger/task_log_buffer.h +2 -4
- esphome/components/lps22/sensor.py +5 -5
- esphome/components/ltr390/ltr390.cpp +0 -2
- esphome/components/ltr501/ltr501.cpp +0 -1
- esphome/components/ltr_als_ps/ltr_als_ps.cpp +0 -1
- esphome/components/lvgl/__init__.py +14 -13
- esphome/components/lvgl/automation.py +2 -4
- esphome/components/lvgl/defines.py +0 -2
- esphome/components/lvgl/helpers.py +1 -1
- esphome/components/lvgl/lv_validation.py +7 -4
- esphome/components/lvgl/lvgl_esphome.cpp +2 -3
- esphome/components/lvgl/styles.py +2 -2
- esphome/components/lvgl/types.py +1 -1
- esphome/components/lvgl/widgets/__init__.py +2 -2
- esphome/components/lvgl/widgets/arc.py +14 -11
- esphome/components/lvgl/widgets/buttonmatrix.py +1 -1
- esphome/components/lvgl/widgets/qrcode.py +7 -7
- esphome/components/lvgl/widgets/spinner.py +6 -6
- esphome/components/lvgl/widgets/switch.py +2 -2
- esphome/components/lvgl/widgets/tabview.py +3 -3
- esphome/components/lvgl/widgets/tileview.py +15 -7
- esphome/components/m5stack_8angle/m5stack_8angle.cpp +0 -1
- esphome/components/matrix_keypad/__init__.py +4 -3
- esphome/components/max17043/max17043.cpp +0 -2
- esphome/components/max31855/max31855.cpp +1 -4
- esphome/components/max31856/max31856.cpp +0 -4
- esphome/components/max31865/max31865.cpp +0 -1
- esphome/components/max44009/max44009.cpp +0 -1
- esphome/components/max6675/max6675.cpp +1 -4
- esphome/components/max6956/max6956.cpp +0 -1
- esphome/components/max7219/max7219.cpp +0 -1
- esphome/components/max7219digit/display.py +1 -1
- esphome/components/max7219digit/max7219digit.cpp +0 -1
- esphome/components/max9611/max9611.cpp +0 -1
- esphome/components/mcp23008/__init__.py +1 -1
- esphome/components/mcp23008/mcp23008.cpp +0 -1
- esphome/components/mcp23016/mcp23016.cpp +0 -1
- esphome/components/mcp23017/__init__.py +1 -1
- esphome/components/mcp23017/mcp23017.cpp +0 -1
- esphome/components/mcp23s08/__init__.py +1 -1
- esphome/components/mcp23s08/mcp23s08.cpp +0 -1
- esphome/components/mcp23s17/__init__.py +1 -1
- esphome/components/mcp23s17/mcp23s17.cpp +0 -1
- esphome/components/mcp23x08_base/__init__.py +2 -0
- esphome/components/mcp23x08_base/mcp23x08_base.cpp +9 -7
- esphome/components/mcp23x08_base/mcp23x08_base.h +9 -4
- esphome/components/mcp23x17_base/__init__.py +2 -0
- esphome/components/mcp23x17_base/mcp23x17_base.cpp +20 -7
- esphome/components/mcp23x17_base/mcp23x17_base.h +9 -4
- esphome/components/mcp23xxx_base/__init__.py +11 -5
- esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +15 -12
- esphome/components/mcp23xxx_base/mcp23xxx_base.h +8 -7
- esphome/components/mcp3008/mcp3008.cpp +1 -4
- esphome/components/mcp3204/mcp3204.cpp +1 -4
- esphome/components/mcp4461/mcp4461.cpp +0 -1
- esphome/components/mcp4725/mcp4725.cpp +0 -1
- esphome/components/mcp4728/mcp4728.cpp +0 -1
- esphome/components/mcp9600/mcp9600.cpp +0 -2
- esphome/components/mcp9808/mcp9808.cpp +0 -2
- esphome/components/mdns/__init__.py +3 -0
- esphome/components/mdns/mdns_component.cpp +2 -0
- esphome/components/mdns/mdns_component.h +4 -0
- esphome/components/media_player/__init__.py +40 -0
- esphome/components/media_player/automation.h +16 -0
- esphome/components/media_player/media_player.cpp +13 -0
- esphome/components/media_player/media_player.h +50 -3
- esphome/components/micro_wake_word/micro_wake_word.cpp +0 -3
- esphome/components/mics_4514/mics_4514.cpp +1 -6
- esphome/components/midea/ir_transmitter.h +4 -4
- esphome/components/mipi/__init__.py +416 -0
- esphome/components/mipi_dsi/__init__.py +5 -0
- esphome/components/mipi_dsi/display.py +233 -0
- esphome/components/mipi_dsi/mipi_dsi.cpp +379 -0
- esphome/components/mipi_dsi/mipi_dsi.h +123 -0
- esphome/components/mipi_dsi/models/__init__.py +0 -0
- esphome/components/mipi_dsi/models/guition.py +38 -0
- esphome/components/mipi_dsi/models/m5stack.py +57 -0
- esphome/components/mipi_dsi/models/waveshare.py +105 -0
- esphome/components/mipi_rgb/models/lilygo.py +0 -0
- esphome/components/mipi_spi/__init__.py +0 -9
- esphome/components/mipi_spi/display.py +220 -256
- esphome/components/mipi_spi/mipi_spi.cpp +1 -485
- esphome/components/mipi_spi/mipi_spi.h +556 -108
- esphome/components/mipi_spi/models/__init__.py +0 -65
- esphome/components/mipi_spi/models/adafruit.py +30 -0
- esphome/components/mipi_spi/models/amoled.py +41 -5
- esphome/components/mipi_spi/models/ili.py +5 -5
- esphome/components/mipi_spi/models/jc.py +1 -3
- esphome/components/mipi_spi/models/lilygo.py +1 -1
- esphome/components/mipi_spi/models/waveshare.py +16 -1
- esphome/components/mixer/speaker/__init__.py +4 -5
- esphome/components/mlx90393/sensor.py +7 -5
- esphome/components/mlx90393/sensor_mlx90393.cpp +0 -1
- esphome/components/mlx90614/mlx90614.cpp +0 -1
- esphome/components/mmc5603/mmc5603.cpp +0 -1
- esphome/components/mmc5983/mmc5983.cpp +0 -2
- esphome/components/mpl3115a2/mpl3115a2.cpp +0 -2
- esphome/components/mpr121/__init__.py +7 -6
- esphome/components/mpr121/mpr121.cpp +0 -1
- esphome/components/mpu6050/mpu6050.cpp +0 -1
- esphome/components/mpu6886/mpu6886.cpp +0 -1
- esphome/components/mqtt/__init__.py +1 -2
- esphome/components/mqtt/mqtt_button.cpp +1 -1
- esphome/components/mqtt/mqtt_client.cpp +0 -1
- esphome/components/mqtt/mqtt_component.cpp +8 -14
- esphome/components/mqtt/mqtt_component.h +0 -7
- esphome/components/mqtt/mqtt_sensor.cpp +0 -1
- esphome/components/mqtt/mqtt_sensor.h +0 -1
- esphome/components/mqtt/mqtt_text_sensor.cpp +0 -1
- esphome/components/mqtt/mqtt_text_sensor.h +0 -1
- esphome/components/ms5611/ms5611.cpp +0 -1
- esphome/components/ms8607/ms8607.cpp +0 -1
- esphome/components/msa3xx/msa3xx.cpp +0 -2
- esphome/components/my9231/my9231.cpp +0 -2
- esphome/components/nau7802/nau7802.cpp +0 -1
- esphome/components/neopixelbus/light.py +3 -0
- esphome/components/network/util.cpp +29 -0
- esphome/components/nextion/nextion.cpp +2 -2
- esphome/components/nfc/binary_sensor/{binary_sensor.cpp → nfc_binary_sensor.cpp} +1 -1
- esphome/components/npi19/npi19.cpp +0 -2
- esphome/components/nrf52/__init__.py +223 -0
- esphome/components/nrf52/boards.py +34 -0
- esphome/components/nrf52/const.py +18 -0
- esphome/components/nrf52/gpio.py +79 -0
- esphome/components/number/__init__.py +2 -1
- esphome/components/one_wire/__init__.py +1 -2
- esphome/components/one_wire/one_wire.cpp +0 -2
- esphome/components/one_wire/one_wire.h +0 -2
- esphome/components/opentherm/hub.cpp +0 -1
- esphome/components/opentherm/number/__init__.py +2 -2
- esphome/components/openthread/__init__.py +2 -3
- esphome/components/openthread/openthread.cpp +30 -13
- esphome/components/openthread/openthread.h +3 -0
- esphome/components/openthread/openthread_esp.cpp +3 -1
- esphome/components/opt3001/sensor.py +2 -6
- esphome/components/output/__init__.py +38 -0
- esphome/components/output/automation.h +24 -0
- esphome/components/output/switch/output_switch.cpp +0 -2
- esphome/components/packages/__init__.py +1 -2
- esphome/components/packet_transport/__init__.py +4 -3
- esphome/components/pca6416a/pca6416a.cpp +0 -1
- esphome/components/pca9554/pca9554.cpp +0 -1
- esphome/components/pca9685/pca9685_output.cpp +0 -2
- esphome/components/pcf85063/pcf85063.cpp +0 -1
- esphome/components/pcf8563/pcf8563.cpp +0 -1
- esphome/components/pcf8574/pcf8574.cpp +0 -1
- esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +0 -1
- esphome/components/pipsolar/pipsolar.cpp +54 -42
- esphome/components/pipsolar/pipsolar.h +5 -4
- esphome/components/pipsolar/sensor/__init__.py +1 -1
- esphome/components/pm2005/pm2005.cpp +0 -1
- esphome/components/pmsa003i/pmsa003i.cpp +0 -2
- esphome/components/pmwcs3/sensor.py +1 -2
- esphome/components/pn532/pn532.cpp +0 -2
- esphome/components/pn532_spi/pn532_spi.cpp +0 -2
- esphome/components/power_supply/power_supply.cpp +7 -10
- esphome/components/power_supply/power_supply.h +1 -1
- esphome/components/psram/__init__.py +6 -1
- esphome/components/pulse_counter/pulse_counter_sensor.cpp +0 -1
- esphome/components/pulse_counter/sensor.py +9 -6
- esphome/components/pylontech/pylontech.cpp +0 -1
- esphome/components/qmc5883l/qmc5883l.cpp +0 -1
- esphome/components/qmp6988/qmp6988.cpp +0 -2
- esphome/components/qspi_dbi/display.py +2 -3
- esphome/components/qspi_dbi/qspi_dbi.cpp +0 -2
- esphome/components/qwiic_pir/binary_sensor.py +2 -3
- esphome/components/qwiic_pir/qwiic_pir.cpp +0 -2
- esphome/components/rc522/rc522.cpp +9 -31
- esphome/components/rc522_spi/rc522_spi.cpp +0 -1
- esphome/components/remote_base/__init__.py +5 -6
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -1
- esphome/components/remote_receiver/remote_receiver_esp8266.cpp +0 -1
- esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -1
- esphome/components/remote_transmitter/__init__.py +26 -0
- esphome/components/remote_transmitter/automation.h +18 -0
- esphome/components/remote_transmitter/remote_transmitter.h +2 -1
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -1
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
- esphome/components/resampler/speaker/__init__.py +4 -5
- esphome/components/rf_bridge/__init__.py +4 -8
- esphome/components/rotary_encoder/rotary_encoder.cpp +0 -2
- esphome/components/rp2040/__init__.py +3 -1
- esphome/components/rp2040_pio_led_strip/led_strip.cpp +0 -2
- esphome/components/rp2040_pio_led_strip/light.py +1 -2
- esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -5
- esphome/components/rpi_dpi_rgb/display.py +13 -15
- esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -3
- esphome/components/runtime_stats/__init__.py +34 -0
- esphome/components/runtime_stats/runtime_stats.cpp +102 -0
- esphome/components/runtime_stats/runtime_stats.h +132 -0
- esphome/components/scd30/scd30.cpp +0 -2
- esphome/components/scd30/sensor.py +1 -2
- esphome/components/scd4x/scd4x.cpp +0 -1
- esphome/components/scd4x/sensor.py +1 -3
- esphome/components/sdl/display.py +3 -1
- esphome/components/sdl/sdl_esphome.cpp +0 -2
- esphome/components/sdp3x/sdp3x.cpp +0 -2
- esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +0 -2
- esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +0 -3
- esphome/components/select/__init__.py +2 -3
- esphome/components/select/select_traits.cpp +1 -1
- esphome/components/select/select_traits.h +1 -1
- esphome/components/sen0321/sen0321.cpp +0 -1
- esphome/components/sen5x/sen5x.cpp +0 -2
- esphome/components/senseair/senseair.cpp +7 -3
- esphome/components/senseair/senseair.h +11 -0
- esphome/components/sensor/__init__.py +36 -4
- esphome/components/sensor/filter.cpp +49 -10
- esphome/components/sensor/filter.h +22 -7
- esphome/components/sensor/sensor.cpp +0 -1
- esphome/components/sensor/sensor.h +0 -9
- esphome/components/sfa30/sfa30.cpp +0 -4
- esphome/components/sgp30/sgp30.cpp +0 -2
- esphome/components/sgp4x/sensor.py +1 -1
- esphome/components/sgp4x/sgp4x.cpp +0 -2
- esphome/components/shelly_dimmer/shelly_dimmer.cpp +0 -2
- esphome/components/sht3xd/sht3xd.cpp +0 -2
- esphome/components/sht4x/sht4x.cpp +0 -2
- esphome/components/shtcx/shtcx.cpp +0 -1
- esphome/components/sim800l/__init__.py +2 -4
- esphome/components/sm16716/sm16716.cpp +0 -1
- esphome/components/sm2135/sm2135.cpp +0 -1
- esphome/components/sm2235/sm2235.cpp +0 -1
- esphome/components/sm2335/sm2335.cpp +0 -1
- esphome/components/sn74hc165/sn74hc165.cpp +0 -1
- esphome/components/sn74hc595/sn74hc595.cpp +0 -1
- esphome/components/sntp/sntp_component.cpp +0 -1
- esphome/components/sound_level/sensor.py +1 -1
- esphome/components/speaker/media_player/__init__.py +21 -33
- esphome/components/speaker/media_player/audio_pipeline.cpp +4 -7
- esphome/components/spi/__init__.py +29 -13
- esphome/components/spi/spi.cpp +0 -2
- esphome/components/spi_device/spi_device.cpp +1 -4
- esphome/components/sprinkler/__init__.py +4 -4
- esphome/components/sps30/sps30.cpp +0 -1
- esphome/components/ssd1306_base/__init__.py +11 -11
- esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
- esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +0 -1
- esphome/components/ssd1306_spi/ssd1306_spi.cpp +0 -1
- esphome/components/ssd1322_spi/ssd1322_spi.cpp +0 -1
- esphome/components/ssd1325_spi/ssd1325_spi.cpp +0 -1
- esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +0 -1
- esphome/components/ssd1327_spi/ssd1327_spi.cpp +0 -1
- esphome/components/ssd1331_spi/ssd1331_spi.cpp +0 -1
- esphome/components/ssd1351_spi/ssd1351_spi.cpp +0 -1
- esphome/components/st7567_i2c/st7567_i2c.cpp +0 -1
- esphome/components/st7567_spi/st7567_spi.cpp +0 -1
- esphome/components/st7701s/display.py +10 -14
- esphome/components/st7701s/st7701s.cpp +0 -3
- esphome/components/st7735/st7735.cpp +0 -1
- esphome/components/st7789v/st7789v.cpp +0 -1
- esphome/components/st7920/st7920.cpp +0 -1
- esphome/components/status_led/light/status_led_light.cpp +0 -2
- esphome/components/status_led/status_led.cpp +0 -1
- esphome/components/stepper/__init__.py +2 -4
- esphome/components/sts3x/sts3x.cpp +0 -1
- esphome/components/substitutions/__init__.py +10 -16
- esphome/components/substitutions/jinja.py +24 -1
- esphome/components/sun/__init__.py +2 -3
- esphome/components/switch/__init__.py +31 -1
- esphome/components/switch/automation.h +24 -0
- esphome/components/switch/switch.cpp +8 -0
- esphome/components/switch/switch.h +8 -0
- esphome/components/sx126x/sx126x.cpp +0 -2
- esphome/components/sx127x/sx127x.cpp +0 -2
- esphome/components/sx1509/__init__.py +7 -5
- esphome/components/sx1509/output/sx1509_float_output.cpp +1 -1
- esphome/components/sx1509/sx1509.cpp +0 -2
- esphome/components/syslog/esphome_syslog.cpp +1 -1
- esphome/components/tc74/tc74.cpp +0 -1
- esphome/components/tca9548a/tca9548a.cpp +0 -1
- esphome/components/tca9555/tca9555.cpp +0 -1
- esphome/components/tcs34725/tcs34725.cpp +0 -1
- esphome/components/tee501/tee501.cpp +0 -1
- esphome/components/tem3200/tem3200.cpp +0 -2
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +0 -1
- esphome/components/template/cover/template_cover.cpp +0 -1
- esphome/components/template/select/template_select.cpp +0 -1
- esphome/components/template/text/template_text.cpp +0 -2
- esphome/components/template/valve/template_valve.cpp +0 -1
- esphome/components/text/__init__.py +0 -1
- esphome/components/text/text_traits.h +2 -0
- esphome/components/text_sensor/__init__.py +2 -1
- esphome/components/text_sensor/text_sensor.cpp +0 -2
- esphome/components/text_sensor/text_sensor.h +0 -8
- esphome/components/thermostat/climate.py +4 -4
- esphome/components/time/__init__.py +7 -4
- esphome/components/time/real_time_clock.cpp +16 -3
- esphome/components/tlc59208f/tlc59208f_output.cpp +0 -2
- esphome/components/tlc5947/tlc5947.cpp +0 -2
- esphome/components/tlc5971/tlc5971.cpp +0 -2
- esphome/components/tm1621/tm1621.cpp +0 -2
- esphome/components/tm1637/tm1637.cpp +0 -2
- esphome/components/tm1638/tm1638.cpp +0 -2
- esphome/components/tm1651/__init__.py +45 -48
- esphome/components/tm1651/tm1651.cpp +213 -47
- esphome/components/tm1651/tm1651.h +37 -32
- esphome/components/tmp117/tmp117.cpp +0 -2
- esphome/components/tsl2561/tsl2561.cpp +0 -1
- esphome/components/tsl2591/tsl2591.cpp +0 -1
- esphome/components/tt21100/touchscreen/tt21100.cpp +0 -2
- esphome/components/ttp229_bsf/ttp229_bsf.cpp +0 -1
- esphome/components/ttp229_lsf/ttp229_lsf.cpp +0 -1
- esphome/components/tuya/climate/__init__.py +9 -10
- esphome/components/tuya/number/__init__.py +8 -6
- esphome/components/tx20/tx20.cpp +0 -1
- esphome/components/uart/uart_component_esp32_arduino.cpp +0 -1
- esphome/components/uart/uart_component_esp8266.cpp +0 -1
- esphome/components/uart/uart_component_esp_idf.cpp +0 -2
- esphome/components/uart/uart_component_libretiny.cpp +0 -2
- esphome/components/uart/uart_component_rp2040.cpp +0 -2
- esphome/components/udp/__init__.py +1 -1
- esphome/components/ufire_ec/sensor.py +1 -2
- esphome/components/ufire_ec/ufire_ec.cpp +0 -2
- esphome/components/ufire_ise/sensor.py +1 -2
- esphome/components/ufire_ise/ufire_ise.cpp +0 -2
- esphome/components/ultrasonic/ultrasonic_sensor.cpp +0 -1
- esphome/components/update/__init__.py +0 -1
- esphome/components/uptime/sensor/uptime_seconds_sensor.cpp +0 -1
- esphome/components/uptime/sensor/uptime_seconds_sensor.h +0 -2
- esphome/components/usb_host/usb_host_client.cpp +0 -1
- esphome/components/usb_host/usb_host_component.cpp +0 -1
- esphome/components/valve/__init__.py +0 -1
- esphome/components/veml3235/veml3235.cpp +0 -3
- esphome/components/veml7700/veml7700.cpp +0 -2
- esphome/components/version/version_text_sensor.cpp +0 -1
- esphome/components/version/version_text_sensor.h +0 -1
- esphome/components/vl53l0x/vl53l0x_sensor.cpp +0 -4
- esphome/components/voice_assistant/voice_assistant.cpp +9 -8
- esphome/components/web_server/__init__.py +13 -0
- esphome/components/web_server/web_server.cpp +188 -353
- esphome/components/web_server/web_server.h +61 -1
- esphome/components/web_server_base/__init__.py +1 -1
- esphome/components/web_server_base/web_server_base.cpp +2 -0
- esphome/components/web_server_base/web_server_base.h +6 -0
- esphome/components/web_server_idf/web_server_idf.cpp +10 -8
- esphome/components/web_server_idf/web_server_idf.h +2 -0
- esphome/components/weikai_i2c/weikai_i2c.cpp +1 -2
- esphome/components/weikai_spi/weikai_spi.cpp +1 -1
- esphome/components/wifi/__init__.py +17 -43
- esphome/components/wifi/wifi_component.cpp +100 -36
- esphome/components/wifi/wifi_component.h +5 -1
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -0
- esphome/components/wifi/wifi_component_esp_idf.cpp +30 -0
- esphome/components/wifi_info/wifi_info_text_sensor.h +0 -6
- esphome/components/wifi_signal/wifi_signal_sensor.h +0 -1
- esphome/components/wireguard/wireguard.cpp +0 -2
- esphome/components/x9c/x9c.cpp +0 -2
- esphome/components/xgzp68xx/xgzp68xx.cpp +0 -1
- esphome/components/xl9535/xl9535.cpp +0 -2
- esphome/components/zephyr/__init__.py +252 -0
- esphome/components/zephyr/const.py +16 -0
- esphome/components/zephyr/core.cpp +90 -0
- esphome/components/zephyr/gpio.cpp +120 -0
- esphome/components/zephyr/gpio.h +38 -0
- esphome/components/zephyr/pre_build.py.script +4 -0
- esphome/components/zephyr/preferences.cpp +156 -0
- esphome/components/zephyr/preferences.h +13 -0
- esphome/config.py +38 -16
- esphome/config_helpers.py +1 -2
- esphome/config_validation.py +12 -16
- esphome/const.py +26 -1
- esphome/core/__init__.py +92 -51
- esphome/core/application.cpp +75 -21
- esphome/core/application.h +106 -171
- esphome/core/color.h +10 -0
- esphome/core/component.cpp +41 -25
- esphome/core/component.h +9 -6
- esphome/core/component_iterator.cpp +61 -261
- esphome/core/component_iterator.h +15 -0
- esphome/core/config.py +26 -11
- esphome/core/defines.h +40 -2
- esphome/core/entity_base.h +18 -0
- esphome/core/entity_helpers.py +45 -10
- esphome/core/helpers.cpp +8 -15
- esphome/core/helpers.h +60 -6
- esphome/core/lock_free_queue.h +1 -1
- esphome/core/scheduler.cpp +311 -77
- esphome/core/scheduler.h +141 -28
- esphome/cpp_generator.py +2 -6
- esphome/cpp_helpers.py +1 -1
- esphome/dashboard/dashboard.py +2 -3
- esphome/dashboard/dns.py +2 -8
- esphome/dashboard/web_server.py +34 -19
- esphome/espota2.py +1 -4
- esphome/git.py +3 -1
- esphome/helpers.py +23 -4
- esphome/log.py +3 -1
- esphome/mqtt.py +3 -5
- esphome/platformio_api.py +7 -4
- esphome/types.py +12 -0
- esphome/util.py +20 -8
- esphome/voluptuous_schema.py +4 -3
- esphome/vscode.py +1 -2
- esphome/wizard.py +1 -4
- esphome/writer.py +16 -108
- esphome/yaml_util.py +7 -5
- {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/METADATA +13 -14
- {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/RECORD +757 -677
- esphome/components/mipi_spi/models/commands.py +0 -82
- /esphome/components/nfc/binary_sensor/{binary_sensor.h → nfc_binary_sensor.h} +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/WHEEL +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/entry_points.txt +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0.dist-info}/top_level.txt +0 -0
esphome/core/scheduler.cpp
CHANGED
|
@@ -8,12 +8,17 @@
|
|
|
8
8
|
#include <algorithm>
|
|
9
9
|
#include <cinttypes>
|
|
10
10
|
#include <cstring>
|
|
11
|
+
#include <limits>
|
|
11
12
|
|
|
12
13
|
namespace esphome {
|
|
13
14
|
|
|
14
15
|
static const char *const TAG = "scheduler";
|
|
15
16
|
|
|
16
17
|
static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10;
|
|
18
|
+
// Half the 32-bit range - used to detect rollovers vs normal time progression
|
|
19
|
+
static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max() / 2;
|
|
20
|
+
// max delay to start an interval sequence
|
|
21
|
+
static constexpr uint32_t MAX_INTERVAL_DELAY = 5000;
|
|
17
22
|
|
|
18
23
|
// Uncomment to debug scheduler
|
|
19
24
|
// #define ESPHOME_DEBUG_SCHEDULER
|
|
@@ -51,7 +56,7 @@ static void validate_static_string(const char *name) {
|
|
|
51
56
|
ESP_LOGW(TAG, "WARNING: Scheduler name '%s' at %p might be on heap (static ref at %p)", name, name, static_str);
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
|
-
#endif
|
|
59
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
55
60
|
|
|
56
61
|
// A note on locking: the `lock_` lock protects the `items_` and `to_add_` containers. It must be taken when writing to
|
|
57
62
|
// them (i.e. when adding/removing items, but not when changing items). As items are only deleted from the loop task,
|
|
@@ -60,7 +65,7 @@ static void validate_static_string(const char *name) {
|
|
|
60
65
|
|
|
61
66
|
// Common implementation for both timeout and interval
|
|
62
67
|
void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string,
|
|
63
|
-
const void *name_ptr, uint32_t delay, std::function<void()> func) {
|
|
68
|
+
const void *name_ptr, uint32_t delay, std::function<void()> func, bool is_retry) {
|
|
64
69
|
// Get the name as const char*
|
|
65
70
|
const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
|
|
66
71
|
|
|
@@ -77,11 +82,18 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
77
82
|
item->set_name(name_cstr, !is_static_string);
|
|
78
83
|
item->type = type;
|
|
79
84
|
item->callback = std::move(func);
|
|
85
|
+
// Initialize remove to false (though it should already be from constructor)
|
|
86
|
+
// Not using mark_item_removed_ helper since we're setting to false, not true
|
|
87
|
+
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
|
88
|
+
item->remove.store(false, std::memory_order_relaxed);
|
|
89
|
+
#else
|
|
80
90
|
item->remove = false;
|
|
91
|
+
#endif
|
|
92
|
+
item->is_retry = is_retry;
|
|
81
93
|
|
|
82
|
-
#
|
|
94
|
+
#ifndef ESPHOME_THREAD_SINGLE
|
|
83
95
|
// Special handling for defer() (delay = 0, type = TIMEOUT)
|
|
84
|
-
//
|
|
96
|
+
// Single-core platforms don't need thread-safe defer handling
|
|
85
97
|
if (delay == 0 && type == SchedulerItem::TIMEOUT) {
|
|
86
98
|
// Put in defer queue for guaranteed FIFO execution
|
|
87
99
|
LockGuard guard{this->lock_};
|
|
@@ -89,16 +101,20 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
89
101
|
this->defer_queue_.push_back(std::move(item));
|
|
90
102
|
return;
|
|
91
103
|
}
|
|
92
|
-
#endif
|
|
104
|
+
#endif /* not ESPHOME_THREAD_SINGLE */
|
|
93
105
|
|
|
94
|
-
|
|
106
|
+
// Get fresh timestamp for new timer/interval - ensures accurate scheduling
|
|
107
|
+
const auto now = this->millis_64_(millis()); // Fresh millis() call
|
|
95
108
|
|
|
96
109
|
// Type-specific setup
|
|
97
110
|
if (type == SchedulerItem::INTERVAL) {
|
|
98
111
|
item->interval = delay;
|
|
99
|
-
//
|
|
100
|
-
|
|
112
|
+
// first execution happens immediately after a random smallish offset
|
|
113
|
+
// Calculate random offset (0 to min(interval/2, 5s))
|
|
114
|
+
uint32_t offset = (uint32_t) (std::min(delay / 2, MAX_INTERVAL_DELAY) * random_float());
|
|
101
115
|
item->next_execution_ = now + offset;
|
|
116
|
+
ESP_LOGV(TAG, "Scheduler interval for %s is %" PRIu32 "ms, offset %" PRIu32 "ms", name_cstr ? name_cstr : "", delay,
|
|
117
|
+
offset);
|
|
102
118
|
} else {
|
|
103
119
|
item->interval = 0;
|
|
104
120
|
item->next_execution_ = now + delay;
|
|
@@ -119,9 +135,21 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
119
135
|
ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, item->get_source(),
|
|
120
136
|
name_cstr ? name_cstr : "(null)", type_str, delay, static_cast<uint32_t>(item->next_execution_ - now));
|
|
121
137
|
}
|
|
122
|
-
#endif
|
|
138
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
123
139
|
|
|
124
140
|
LockGuard guard{this->lock_};
|
|
141
|
+
|
|
142
|
+
// For retries, check if there's a cancelled timeout first
|
|
143
|
+
if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT &&
|
|
144
|
+
(has_cancelled_timeout_in_container_(this->items_, component, name_cstr, /* match_retry= */ true) ||
|
|
145
|
+
has_cancelled_timeout_in_container_(this->to_add_, component, name_cstr, /* match_retry= */ true))) {
|
|
146
|
+
// Skip scheduling - the retry was cancelled
|
|
147
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
148
|
+
ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr);
|
|
149
|
+
#endif
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
125
153
|
// If name is provided, do atomic cancel-and-add
|
|
126
154
|
// Cancel existing items
|
|
127
155
|
this->cancel_item_locked_(component, name_cstr, type);
|
|
@@ -170,32 +198,34 @@ struct RetryArgs {
|
|
|
170
198
|
Scheduler *scheduler;
|
|
171
199
|
};
|
|
172
200
|
|
|
173
|
-
|
|
201
|
+
void retry_handler(const std::shared_ptr<RetryArgs> &args) {
|
|
174
202
|
RetryResult const retry_result = args->func(--args->retry_countdown);
|
|
175
203
|
if (retry_result == RetryResult::DONE || args->retry_countdown <= 0)
|
|
176
204
|
return;
|
|
177
205
|
// second execution of `func` happens after `initial_wait_time`
|
|
178
|
-
args->scheduler->
|
|
206
|
+
args->scheduler->set_timer_common_(
|
|
207
|
+
args->component, Scheduler::SchedulerItem::TIMEOUT, false, &args->name, args->current_interval,
|
|
208
|
+
[args]() { retry_handler(args); }, /* is_retry= */ true);
|
|
179
209
|
// backoff_increase_factor applied to third & later executions
|
|
180
210
|
args->current_interval *= args->backoff_increase_factor;
|
|
181
211
|
}
|
|
182
212
|
|
|
183
|
-
void HOT Scheduler::
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
213
|
+
void HOT Scheduler::set_retry_common_(Component *component, bool is_static_string, const void *name_ptr,
|
|
214
|
+
uint32_t initial_wait_time, uint8_t max_attempts,
|
|
215
|
+
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
|
|
216
|
+
const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
|
|
217
|
+
|
|
218
|
+
if (name_cstr != nullptr)
|
|
219
|
+
this->cancel_retry(component, name_cstr);
|
|
188
220
|
|
|
189
221
|
if (initial_wait_time == SCHEDULER_DONT_RUN)
|
|
190
222
|
return;
|
|
191
223
|
|
|
192
224
|
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)",
|
|
193
|
-
|
|
225
|
+
name_cstr ? name_cstr : "", initial_wait_time, max_attempts, backoff_increase_factor);
|
|
194
226
|
|
|
195
227
|
if (backoff_increase_factor < 0.0001) {
|
|
196
|
-
ESP_LOGE(TAG,
|
|
197
|
-
"set_retry(name='%s'): backoff_factor cannot be close to zero nor negative (%0.1f). Using 1.0 instead",
|
|
198
|
-
name.c_str(), backoff_increase_factor);
|
|
228
|
+
ESP_LOGE(TAG, "backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor, name_cstr ? name_cstr : "");
|
|
199
229
|
backoff_increase_factor = 1;
|
|
200
230
|
}
|
|
201
231
|
|
|
@@ -204,31 +234,56 @@ void HOT Scheduler::set_retry(Component *component, const std::string &name, uin
|
|
|
204
234
|
args->retry_countdown = max_attempts;
|
|
205
235
|
args->current_interval = initial_wait_time;
|
|
206
236
|
args->component = component;
|
|
207
|
-
args->name = "
|
|
237
|
+
args->name = name_cstr ? name_cstr : ""; // Convert to std::string for RetryArgs
|
|
208
238
|
args->backoff_increase_factor = backoff_increase_factor;
|
|
209
239
|
args->scheduler = this;
|
|
210
240
|
|
|
211
|
-
// First execution of `func` immediately
|
|
212
|
-
this->
|
|
241
|
+
// First execution of `func` immediately - use set_timer_common_ with is_retry=true
|
|
242
|
+
this->set_timer_common_(
|
|
243
|
+
component, SchedulerItem::TIMEOUT, false, &args->name, 0, [args]() { retry_handler(args); },
|
|
244
|
+
/* is_retry= */ true);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time,
|
|
248
|
+
uint8_t max_attempts, std::function<RetryResult(uint8_t)> func,
|
|
249
|
+
float backoff_increase_factor) {
|
|
250
|
+
this->set_retry_common_(component, false, &name, initial_wait_time, max_attempts, std::move(func),
|
|
251
|
+
backoff_increase_factor);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
void HOT Scheduler::set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts,
|
|
255
|
+
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
|
|
256
|
+
this->set_retry_common_(component, true, name, initial_wait_time, max_attempts, std::move(func),
|
|
257
|
+
backoff_increase_factor);
|
|
213
258
|
}
|
|
214
259
|
bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) {
|
|
215
|
-
return this->
|
|
260
|
+
return this->cancel_retry(component, name.c_str());
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
bool HOT Scheduler::cancel_retry(Component *component, const char *name) {
|
|
264
|
+
// Cancel timeouts that have is_retry flag set
|
|
265
|
+
LockGuard guard{this->lock_};
|
|
266
|
+
return this->cancel_item_locked_(component, name, SchedulerItem::TIMEOUT, /* match_retry= */ true);
|
|
216
267
|
}
|
|
217
268
|
|
|
218
|
-
optional<uint32_t> HOT Scheduler::next_schedule_in() {
|
|
269
|
+
optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
|
|
219
270
|
// IMPORTANT: This method should only be called from the main thread (loop task).
|
|
220
|
-
// It
|
|
271
|
+
// It performs cleanup and accesses items_[0] without holding a lock, which is only
|
|
221
272
|
// safe when called from the main thread. Other threads must not call this method.
|
|
222
|
-
|
|
273
|
+
|
|
274
|
+
// If no items, return empty optional
|
|
275
|
+
if (this->cleanup_() == 0)
|
|
223
276
|
return {};
|
|
277
|
+
|
|
224
278
|
auto &item = this->items_[0];
|
|
225
|
-
|
|
226
|
-
|
|
279
|
+
// Convert the fresh timestamp from caller (usually Application::loop()) to 64-bit
|
|
280
|
+
const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from caller
|
|
281
|
+
if (item->next_execution_ < now_64)
|
|
227
282
|
return 0;
|
|
228
|
-
return item->next_execution_ -
|
|
283
|
+
return item->next_execution_ - now_64;
|
|
229
284
|
}
|
|
230
|
-
void HOT Scheduler::call() {
|
|
231
|
-
#
|
|
285
|
+
void HOT Scheduler::call(uint32_t now) {
|
|
286
|
+
#ifndef ESPHOME_THREAD_SINGLE
|
|
232
287
|
// Process defer queue first to guarantee FIFO execution order for deferred items.
|
|
233
288
|
// Previously, defer() used the heap which gave undefined order for equal timestamps,
|
|
234
289
|
// causing race conditions on multi-core systems (ESP32, BK7200).
|
|
@@ -236,8 +291,7 @@ void HOT Scheduler::call() {
|
|
|
236
291
|
// - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_
|
|
237
292
|
// - Items execute in exact order they were deferred (FIFO guarantee)
|
|
238
293
|
// - No deferred items exist in to_add_, so processing order doesn't affect correctness
|
|
239
|
-
//
|
|
240
|
-
// (ESP8266: single-core, RP2040: empty mutex implementation).
|
|
294
|
+
// Single-core platforms don't use this queue and fall back to the heap-based approach.
|
|
241
295
|
//
|
|
242
296
|
// Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still
|
|
243
297
|
// processed here. They are removed from the queue normally via pop_front() but skipped
|
|
@@ -256,23 +310,33 @@ void HOT Scheduler::call() {
|
|
|
256
310
|
// Execute callback without holding lock to prevent deadlocks
|
|
257
311
|
// if the callback tries to call defer() again
|
|
258
312
|
if (!this->should_skip_item_(item.get())) {
|
|
259
|
-
this->execute_item_(item.get());
|
|
313
|
+
this->execute_item_(item.get(), now);
|
|
260
314
|
}
|
|
261
315
|
}
|
|
262
|
-
#endif
|
|
316
|
+
#endif /* not ESPHOME_THREAD_SINGLE */
|
|
263
317
|
|
|
264
|
-
|
|
318
|
+
// Convert the fresh timestamp from main loop to 64-bit for scheduler operations
|
|
319
|
+
const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from Application::loop()
|
|
265
320
|
this->process_to_add();
|
|
266
321
|
|
|
267
322
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
268
323
|
static uint64_t last_print = 0;
|
|
269
324
|
|
|
270
|
-
if (
|
|
271
|
-
last_print =
|
|
325
|
+
if (now_64 - last_print > 2000) {
|
|
326
|
+
last_print = now_64;
|
|
272
327
|
std::vector<std::unique_ptr<SchedulerItem>> old_items;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
328
|
+
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
|
329
|
+
const auto last_dbg = this->last_millis_.load(std::memory_order_relaxed);
|
|
330
|
+
const auto major_dbg = this->millis_major_.load(std::memory_order_relaxed);
|
|
331
|
+
ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64,
|
|
332
|
+
major_dbg, last_dbg);
|
|
333
|
+
#else /* not ESPHOME_THREAD_MULTI_ATOMICS */
|
|
334
|
+
ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64,
|
|
335
|
+
this->millis_major_, this->last_millis_);
|
|
336
|
+
#endif /* else ESPHOME_THREAD_MULTI_ATOMICS */
|
|
337
|
+
// Cleanup before debug output
|
|
338
|
+
this->cleanup_();
|
|
339
|
+
while (!this->items_.empty()) {
|
|
276
340
|
std::unique_ptr<SchedulerItem> item;
|
|
277
341
|
{
|
|
278
342
|
LockGuard guard{this->lock_};
|
|
@@ -283,7 +347,7 @@ void HOT Scheduler::call() {
|
|
|
283
347
|
const char *name = item->get_name();
|
|
284
348
|
ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64,
|
|
285
349
|
item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
|
|
286
|
-
item->next_execution_ -
|
|
350
|
+
item->next_execution_ - now_64, item->next_execution_);
|
|
287
351
|
|
|
288
352
|
old_items.push_back(std::move(item));
|
|
289
353
|
}
|
|
@@ -296,7 +360,7 @@ void HOT Scheduler::call() {
|
|
|
296
360
|
std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
|
|
297
361
|
}
|
|
298
362
|
}
|
|
299
|
-
#endif
|
|
363
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
300
364
|
|
|
301
365
|
// If we have too many items to remove
|
|
302
366
|
if (this->to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
|
|
@@ -323,12 +387,14 @@ void HOT Scheduler::call() {
|
|
|
323
387
|
this->to_remove_ = 0;
|
|
324
388
|
}
|
|
325
389
|
|
|
326
|
-
|
|
390
|
+
// Cleanup removed items before processing
|
|
391
|
+
this->cleanup_();
|
|
392
|
+
while (!this->items_.empty()) {
|
|
327
393
|
// use scoping to indicate visibility of `item` variable
|
|
328
394
|
{
|
|
329
395
|
// Don't copy-by value yet
|
|
330
396
|
auto &item = this->items_[0];
|
|
331
|
-
if (item->next_execution_ >
|
|
397
|
+
if (item->next_execution_ > now_64) {
|
|
332
398
|
// Not reached timeout yet, done for this call
|
|
333
399
|
break;
|
|
334
400
|
}
|
|
@@ -338,17 +404,42 @@ void HOT Scheduler::call() {
|
|
|
338
404
|
this->pop_raw_();
|
|
339
405
|
continue;
|
|
340
406
|
}
|
|
407
|
+
|
|
408
|
+
// Check if item is marked for removal
|
|
409
|
+
// This handles two cases:
|
|
410
|
+
// 1. Item was marked for removal after cleanup_() but before we got here
|
|
411
|
+
// 2. Item is marked for removal but wasn't at the front of the heap during cleanup_()
|
|
412
|
+
#ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS
|
|
413
|
+
// Multi-threaded platforms without atomics: must take lock to safely read remove flag
|
|
414
|
+
{
|
|
415
|
+
LockGuard guard{this->lock_};
|
|
416
|
+
if (is_item_removed_(item.get())) {
|
|
417
|
+
this->pop_raw_();
|
|
418
|
+
this->to_remove_--;
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
#else
|
|
423
|
+
// Single-threaded or multi-threaded with atomics: can check without lock
|
|
424
|
+
if (is_item_removed_(item.get())) {
|
|
425
|
+
LockGuard guard{this->lock_};
|
|
426
|
+
this->pop_raw_();
|
|
427
|
+
this->to_remove_--;
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
#endif
|
|
431
|
+
|
|
341
432
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
342
433
|
const char *item_name = item->get_name();
|
|
343
434
|
ESP_LOGV(TAG, "Running %s '%s/%s' with interval=%" PRIu32 " next_execution=%" PRIu64 " (now=%" PRIu64 ")",
|
|
344
435
|
item->get_type_str(), item->get_source(), item_name ? item_name : "(null)", item->interval,
|
|
345
|
-
item->next_execution_,
|
|
346
|
-
#endif
|
|
436
|
+
item->next_execution_, now_64);
|
|
437
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
347
438
|
|
|
348
439
|
// Warning: During callback(), a lot of stuff can happen, including:
|
|
349
440
|
// - timeouts/intervals get added, potentially invalidating vector pointers
|
|
350
441
|
// - timeouts/intervals get cancelled
|
|
351
|
-
this->execute_item_(item.get());
|
|
442
|
+
this->execute_item_(item.get(), now);
|
|
352
443
|
}
|
|
353
444
|
|
|
354
445
|
{
|
|
@@ -367,7 +458,7 @@ void HOT Scheduler::call() {
|
|
|
367
458
|
}
|
|
368
459
|
|
|
369
460
|
if (item->type == SchedulerItem::INTERVAL) {
|
|
370
|
-
item->next_execution_ =
|
|
461
|
+
item->next_execution_ = now_64 + item->interval;
|
|
371
462
|
// Add new item directly to to_add_
|
|
372
463
|
// since we have the lock held
|
|
373
464
|
this->to_add_.push_back(std::move(item));
|
|
@@ -389,8 +480,8 @@ void HOT Scheduler::process_to_add() {
|
|
|
389
480
|
}
|
|
390
481
|
this->to_add_.clear();
|
|
391
482
|
}
|
|
392
|
-
|
|
393
|
-
// Fast path: if nothing to remove, just return
|
|
483
|
+
size_t HOT Scheduler::cleanup_() {
|
|
484
|
+
// Fast path: if nothing to remove, just return the current size
|
|
394
485
|
// Reading to_remove_ without lock is safe because:
|
|
395
486
|
// 1. We only call this from the main thread during call()
|
|
396
487
|
// 2. If it's 0, there's definitely nothing to cleanup
|
|
@@ -398,7 +489,7 @@ void HOT Scheduler::cleanup_() {
|
|
|
398
489
|
// 4. Not all platforms support atomics, so we accept this race in favor of performance
|
|
399
490
|
// 5. The worst case is a one-loop-iteration delay in cleanup, which is harmless
|
|
400
491
|
if (this->to_remove_ == 0)
|
|
401
|
-
return;
|
|
492
|
+
return this->items_.size();
|
|
402
493
|
|
|
403
494
|
// We must hold the lock for the entire cleanup operation because:
|
|
404
495
|
// 1. We're modifying items_ (via pop_raw_) which requires exclusive access
|
|
@@ -412,10 +503,11 @@ void HOT Scheduler::cleanup_() {
|
|
|
412
503
|
while (!this->items_.empty()) {
|
|
413
504
|
auto &item = this->items_[0];
|
|
414
505
|
if (!item->remove)
|
|
415
|
-
|
|
506
|
+
break;
|
|
416
507
|
this->to_remove_--;
|
|
417
508
|
this->pop_raw_();
|
|
418
509
|
}
|
|
510
|
+
return this->items_.size();
|
|
419
511
|
}
|
|
420
512
|
void HOT Scheduler::pop_raw_() {
|
|
421
513
|
std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
|
|
@@ -423,11 +515,9 @@ void HOT Scheduler::pop_raw_() {
|
|
|
423
515
|
}
|
|
424
516
|
|
|
425
517
|
// Helper to execute a scheduler item
|
|
426
|
-
void HOT Scheduler::execute_item_(SchedulerItem *item) {
|
|
518
|
+
void HOT Scheduler::execute_item_(SchedulerItem *item, uint32_t now) {
|
|
427
519
|
App.set_current_component(item->component);
|
|
428
|
-
|
|
429
|
-
uint32_t now_ms = millis();
|
|
430
|
-
WarnIfComponentBlockingGuard guard{item->component, now_ms};
|
|
520
|
+
WarnIfComponentBlockingGuard guard{item->component, now};
|
|
431
521
|
item->callback();
|
|
432
522
|
guard.finish();
|
|
433
523
|
}
|
|
@@ -444,7 +534,8 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co
|
|
|
444
534
|
}
|
|
445
535
|
|
|
446
536
|
// Helper to cancel items by name - must be called with lock held
|
|
447
|
-
bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type
|
|
537
|
+
bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type,
|
|
538
|
+
bool match_retry) {
|
|
448
539
|
// Early return if name is invalid - no items to cancel
|
|
449
540
|
if (name_cstr == nullptr) {
|
|
450
541
|
return false;
|
|
@@ -453,22 +544,22 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
|
453
544
|
size_t total_cancelled = 0;
|
|
454
545
|
|
|
455
546
|
// Check all containers for matching items
|
|
456
|
-
#
|
|
547
|
+
#ifndef ESPHOME_THREAD_SINGLE
|
|
457
548
|
// Only check defer queue for timeouts (intervals never go there)
|
|
458
549
|
if (type == SchedulerItem::TIMEOUT) {
|
|
459
550
|
for (auto &item : this->defer_queue_) {
|
|
460
|
-
if (this->matches_item_(item, component, name_cstr, type)) {
|
|
461
|
-
item
|
|
551
|
+
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
|
552
|
+
this->mark_item_removed_(item.get());
|
|
462
553
|
total_cancelled++;
|
|
463
554
|
}
|
|
464
555
|
}
|
|
465
556
|
}
|
|
466
|
-
#endif
|
|
557
|
+
#endif /* not ESPHOME_THREAD_SINGLE */
|
|
467
558
|
|
|
468
559
|
// Cancel items in the main heap
|
|
469
560
|
for (auto &item : this->items_) {
|
|
470
|
-
if (this->matches_item_(item, component, name_cstr, type)) {
|
|
471
|
-
item
|
|
561
|
+
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
|
562
|
+
this->mark_item_removed_(item.get());
|
|
472
563
|
total_cancelled++;
|
|
473
564
|
this->to_remove_++; // Track removals for heap items
|
|
474
565
|
}
|
|
@@ -476,8 +567,8 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
|
476
567
|
|
|
477
568
|
// Cancel items in to_add_
|
|
478
569
|
for (auto &item : this->to_add_) {
|
|
479
|
-
if (this->matches_item_(item, component, name_cstr, type)) {
|
|
480
|
-
item
|
|
570
|
+
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
|
571
|
+
this->mark_item_removed_(item.get());
|
|
481
572
|
total_cancelled++;
|
|
482
573
|
// Don't track removals for to_add_ items
|
|
483
574
|
}
|
|
@@ -486,19 +577,162 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
|
486
577
|
return total_cancelled > 0;
|
|
487
578
|
}
|
|
488
579
|
|
|
489
|
-
uint64_t Scheduler::
|
|
490
|
-
//
|
|
491
|
-
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
580
|
+
uint64_t Scheduler::millis_64_(uint32_t now) {
|
|
581
|
+
// THREAD SAFETY NOTE:
|
|
582
|
+
// This function has three implementations, based on the precompiler flags
|
|
583
|
+
// - ESPHOME_THREAD_SINGLE - Runs on single-threaded platforms (ESP8266, RP2040, etc.)
|
|
584
|
+
// - ESPHOME_THREAD_MULTI_NO_ATOMICS - Runs on multi-threaded platforms without atomics (LibreTiny)
|
|
585
|
+
// - ESPHOME_THREAD_MULTI_ATOMICS - Runs on multi-threaded platforms with atomics (ESP32, HOST, etc.)
|
|
586
|
+
//
|
|
587
|
+
// Make sure all changes are synchronized if you edit this function.
|
|
588
|
+
//
|
|
589
|
+
// IMPORTANT: Always pass fresh millis() values to this function. The implementation
|
|
590
|
+
// handles out-of-order timestamps between threads, but minimizing time differences
|
|
591
|
+
// helps maintain accuracy.
|
|
592
|
+
//
|
|
593
|
+
|
|
594
|
+
#ifdef ESPHOME_THREAD_SINGLE
|
|
595
|
+
// This is the single core implementation.
|
|
596
|
+
//
|
|
597
|
+
// Single-core platforms have no concurrency, so this is a simple implementation
|
|
598
|
+
// that just tracks 32-bit rollover (every 49.7 days) without any locking or atomics.
|
|
599
|
+
|
|
600
|
+
uint16_t major = this->millis_major_;
|
|
601
|
+
uint32_t last = this->last_millis_;
|
|
602
|
+
|
|
603
|
+
// Check for rollover
|
|
604
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
495
605
|
this->millis_major_++;
|
|
496
|
-
|
|
497
|
-
|
|
606
|
+
major++;
|
|
607
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
608
|
+
ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
|
|
609
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
498
610
|
}
|
|
499
|
-
|
|
611
|
+
|
|
612
|
+
// Only update if time moved forward
|
|
613
|
+
if (now > last) {
|
|
614
|
+
this->last_millis_ = now;
|
|
615
|
+
}
|
|
616
|
+
|
|
500
617
|
// Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
|
|
501
|
-
return now + (static_cast<uint64_t>(
|
|
618
|
+
return now + (static_cast<uint64_t>(major) << 32);
|
|
619
|
+
|
|
620
|
+
#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
|
|
621
|
+
// This is the multi core no atomics implementation.
|
|
622
|
+
//
|
|
623
|
+
// Without atomics, this implementation uses locks more aggressively:
|
|
624
|
+
// 1. Always locks when near the rollover boundary (within 10 seconds)
|
|
625
|
+
// 2. Always locks when detecting a large backwards jump
|
|
626
|
+
// 3. Updates without lock in normal forward progression (accepting minor races)
|
|
627
|
+
// This is less efficient but necessary without atomic operations.
|
|
628
|
+
uint16_t major = this->millis_major_;
|
|
629
|
+
uint32_t last = this->last_millis_;
|
|
630
|
+
|
|
631
|
+
// Define a safe window around the rollover point (10 seconds)
|
|
632
|
+
// This covers any reasonable scheduler delays or thread preemption
|
|
633
|
+
static const uint32_t ROLLOVER_WINDOW = 10000; // 10 seconds in milliseconds
|
|
634
|
+
|
|
635
|
+
// Check if we're near the rollover boundary (close to std::numeric_limits<uint32_t>::max() or just past 0)
|
|
636
|
+
bool near_rollover = (last > (std::numeric_limits<uint32_t>::max() - ROLLOVER_WINDOW)) || (now < ROLLOVER_WINDOW);
|
|
637
|
+
|
|
638
|
+
if (near_rollover || (now < last && (last - now) > HALF_MAX_UINT32)) {
|
|
639
|
+
// Near rollover or detected a rollover - need lock for safety
|
|
640
|
+
LockGuard guard{this->lock_};
|
|
641
|
+
// Re-read with lock held
|
|
642
|
+
last = this->last_millis_;
|
|
643
|
+
|
|
644
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
645
|
+
// True rollover detected (happens every ~49.7 days)
|
|
646
|
+
this->millis_major_++;
|
|
647
|
+
major++;
|
|
648
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
649
|
+
ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
|
|
650
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
651
|
+
}
|
|
652
|
+
// Update last_millis_ while holding lock
|
|
653
|
+
this->last_millis_ = now;
|
|
654
|
+
} else if (now > last) {
|
|
655
|
+
// Normal case: Not near rollover and time moved forward
|
|
656
|
+
// Update without lock. While this may cause minor races (microseconds of
|
|
657
|
+
// backwards time movement), they're acceptable because:
|
|
658
|
+
// 1. The scheduler operates at millisecond resolution, not microsecond
|
|
659
|
+
// 2. We've already prevented the critical rollover race condition
|
|
660
|
+
// 3. Any backwards movement is orders of magnitude smaller than scheduler delays
|
|
661
|
+
this->last_millis_ = now;
|
|
662
|
+
}
|
|
663
|
+
// If now <= last and we're not near rollover, don't update
|
|
664
|
+
// This minimizes backwards time movement
|
|
665
|
+
|
|
666
|
+
// Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
|
|
667
|
+
return now + (static_cast<uint64_t>(major) << 32);
|
|
668
|
+
|
|
669
|
+
#elif defined(ESPHOME_THREAD_MULTI_ATOMICS)
|
|
670
|
+
// This is the multi core with atomics implementation.
|
|
671
|
+
//
|
|
672
|
+
// Uses atomic operations with acquire/release semantics to ensure coherent
|
|
673
|
+
// reads of millis_major_ and last_millis_ across cores. Features:
|
|
674
|
+
// 1. Epoch-coherency retry loop to handle concurrent updates
|
|
675
|
+
// 2. Lock only taken for actual rollover detection and update
|
|
676
|
+
// 3. Lock-free CAS updates for normal forward time progression
|
|
677
|
+
// 4. Memory ordering ensures cores see consistent time values
|
|
678
|
+
|
|
679
|
+
for (;;) {
|
|
680
|
+
uint16_t major = this->millis_major_.load(std::memory_order_acquire);
|
|
681
|
+
|
|
682
|
+
/*
|
|
683
|
+
* Acquire so that if we later decide **not** to take the lock we still
|
|
684
|
+
* observe a `millis_major_` value coherent with the loaded `last_millis_`.
|
|
685
|
+
* The acquire load ensures any later read of `millis_major_` sees its
|
|
686
|
+
* corresponding increment.
|
|
687
|
+
*/
|
|
688
|
+
uint32_t last = this->last_millis_.load(std::memory_order_acquire);
|
|
689
|
+
|
|
690
|
+
// If we might be near a rollover (large backwards jump), take the lock for the entire operation
|
|
691
|
+
// This ensures rollover detection and last_millis_ update are atomic together
|
|
692
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
693
|
+
// Potential rollover - need lock for atomic rollover detection + update
|
|
694
|
+
LockGuard guard{this->lock_};
|
|
695
|
+
// Re-read with lock held; mutex already provides ordering
|
|
696
|
+
last = this->last_millis_.load(std::memory_order_relaxed);
|
|
697
|
+
|
|
698
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
699
|
+
// True rollover detected (happens every ~49.7 days)
|
|
700
|
+
this->millis_major_.fetch_add(1, std::memory_order_relaxed);
|
|
701
|
+
major++;
|
|
702
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
703
|
+
ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
|
|
704
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
705
|
+
}
|
|
706
|
+
/*
|
|
707
|
+
* Update last_millis_ while holding the lock to prevent races
|
|
708
|
+
* Publish the new low-word *after* bumping `millis_major_` (done above)
|
|
709
|
+
* so readers never see a mismatched pair.
|
|
710
|
+
*/
|
|
711
|
+
this->last_millis_.store(now, std::memory_order_release);
|
|
712
|
+
} else {
|
|
713
|
+
// Normal case: Try lock-free update, but only allow forward movement within same epoch
|
|
714
|
+
// This prevents accidentally moving backwards across a rollover boundary
|
|
715
|
+
while (now > last && (now - last) < HALF_MAX_UINT32) {
|
|
716
|
+
if (this->last_millis_.compare_exchange_weak(last, now,
|
|
717
|
+
std::memory_order_release, // success
|
|
718
|
+
std::memory_order_relaxed)) { // failure
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
// CAS failure means no data was published; relaxed is fine
|
|
722
|
+
// last is automatically updated by compare_exchange_weak if it fails
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
uint16_t major_end = this->millis_major_.load(std::memory_order_relaxed);
|
|
726
|
+
if (major_end == major)
|
|
727
|
+
return now + (static_cast<uint64_t>(major) << 32);
|
|
728
|
+
}
|
|
729
|
+
// Unreachable - the loop always returns when major_end == major
|
|
730
|
+
__builtin_unreachable();
|
|
731
|
+
|
|
732
|
+
#else
|
|
733
|
+
#error \
|
|
734
|
+
"No platform threading model defined. One of ESPHOME_THREAD_SINGLE, ESPHOME_THREAD_MULTI_NO_ATOMICS, or ESPHOME_THREAD_MULTI_ATOMICS must be defined."
|
|
735
|
+
#endif
|
|
502
736
|
}
|
|
503
737
|
|
|
504
738
|
bool HOT Scheduler::SchedulerItem::cmp(const std::unique_ptr<SchedulerItem> &a,
|