esphome 2025.7.4__py3-none-any.whl → 2025.8.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +189 -82
- 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 +488 -118
- esphome/components/atm90e32/atm90e32.h +44 -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 +387 -82
- 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 +199 -58
- 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 +40 -2
- esphome/components/esp32_ble/ble.cpp +12 -8
- 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 +2 -4
- 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 +0 -1
- 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/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 +2 -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/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 +187 -352
- 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 +8 -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 +8 -15
- esphome/const.py +26 -1
- esphome/core/__init__.py +88 -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 +41 -6
- esphome/core/helpers.cpp +8 -15
- esphome/core/helpers.h +60 -6
- esphome/core/lock_free_queue.h +1 -1
- esphome/core/scheduler.cpp +277 -74
- esphome/core/scheduler.h +89 -27
- 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 +5 -107
- esphome/yaml_util.py +7 -5
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/METADATA +12 -13
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/RECORD +753 -673
- 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.0b1.dist-info}/WHEEL +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/entry_points.txt +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.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
|
|
|
@@ -78,10 +83,11 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
78
83
|
item->type = type;
|
|
79
84
|
item->callback = std::move(func);
|
|
80
85
|
item->remove = false;
|
|
86
|
+
item->is_retry = is_retry;
|
|
81
87
|
|
|
82
|
-
#
|
|
88
|
+
#ifndef ESPHOME_THREAD_SINGLE
|
|
83
89
|
// Special handling for defer() (delay = 0, type = TIMEOUT)
|
|
84
|
-
//
|
|
90
|
+
// Single-core platforms don't need thread-safe defer handling
|
|
85
91
|
if (delay == 0 && type == SchedulerItem::TIMEOUT) {
|
|
86
92
|
// Put in defer queue for guaranteed FIFO execution
|
|
87
93
|
LockGuard guard{this->lock_};
|
|
@@ -89,16 +95,20 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
89
95
|
this->defer_queue_.push_back(std::move(item));
|
|
90
96
|
return;
|
|
91
97
|
}
|
|
92
|
-
#endif
|
|
98
|
+
#endif /* not ESPHOME_THREAD_SINGLE */
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
// Get fresh timestamp for new timer/interval - ensures accurate scheduling
|
|
101
|
+
const auto now = this->millis_64_(millis()); // Fresh millis() call
|
|
95
102
|
|
|
96
103
|
// Type-specific setup
|
|
97
104
|
if (type == SchedulerItem::INTERVAL) {
|
|
98
105
|
item->interval = delay;
|
|
99
|
-
//
|
|
100
|
-
|
|
106
|
+
// first execution happens immediately after a random smallish offset
|
|
107
|
+
// Calculate random offset (0 to min(interval/2, 5s))
|
|
108
|
+
uint32_t offset = (uint32_t) (std::min(delay / 2, MAX_INTERVAL_DELAY) * random_float());
|
|
101
109
|
item->next_execution_ = now + offset;
|
|
110
|
+
ESP_LOGV(TAG, "Scheduler interval for %s is %" PRIu32 "ms, offset %" PRIu32 "ms", name_cstr ? name_cstr : "", delay,
|
|
111
|
+
offset);
|
|
102
112
|
} else {
|
|
103
113
|
item->interval = 0;
|
|
104
114
|
item->next_execution_ = now + delay;
|
|
@@ -119,9 +129,21 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
119
129
|
ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, item->get_source(),
|
|
120
130
|
name_cstr ? name_cstr : "(null)", type_str, delay, static_cast<uint32_t>(item->next_execution_ - now));
|
|
121
131
|
}
|
|
122
|
-
#endif
|
|
132
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
123
133
|
|
|
124
134
|
LockGuard guard{this->lock_};
|
|
135
|
+
|
|
136
|
+
// For retries, check if there's a cancelled timeout first
|
|
137
|
+
if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT &&
|
|
138
|
+
(has_cancelled_timeout_in_container_(this->items_, component, name_cstr, /* match_retry= */ true) ||
|
|
139
|
+
has_cancelled_timeout_in_container_(this->to_add_, component, name_cstr, /* match_retry= */ true))) {
|
|
140
|
+
// Skip scheduling - the retry was cancelled
|
|
141
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
142
|
+
ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr);
|
|
143
|
+
#endif
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
125
147
|
// If name is provided, do atomic cancel-and-add
|
|
126
148
|
// Cancel existing items
|
|
127
149
|
this->cancel_item_locked_(component, name_cstr, type);
|
|
@@ -170,32 +192,34 @@ struct RetryArgs {
|
|
|
170
192
|
Scheduler *scheduler;
|
|
171
193
|
};
|
|
172
194
|
|
|
173
|
-
|
|
195
|
+
void retry_handler(const std::shared_ptr<RetryArgs> &args) {
|
|
174
196
|
RetryResult const retry_result = args->func(--args->retry_countdown);
|
|
175
197
|
if (retry_result == RetryResult::DONE || args->retry_countdown <= 0)
|
|
176
198
|
return;
|
|
177
199
|
// second execution of `func` happens after `initial_wait_time`
|
|
178
|
-
args->scheduler->
|
|
200
|
+
args->scheduler->set_timer_common_(
|
|
201
|
+
args->component, Scheduler::SchedulerItem::TIMEOUT, false, &args->name, args->current_interval,
|
|
202
|
+
[args]() { retry_handler(args); }, /* is_retry= */ true);
|
|
179
203
|
// backoff_increase_factor applied to third & later executions
|
|
180
204
|
args->current_interval *= args->backoff_increase_factor;
|
|
181
205
|
}
|
|
182
206
|
|
|
183
|
-
void HOT Scheduler::
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
207
|
+
void HOT Scheduler::set_retry_common_(Component *component, bool is_static_string, const void *name_ptr,
|
|
208
|
+
uint32_t initial_wait_time, uint8_t max_attempts,
|
|
209
|
+
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
|
|
210
|
+
const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
|
|
211
|
+
|
|
212
|
+
if (name_cstr != nullptr)
|
|
213
|
+
this->cancel_retry(component, name_cstr);
|
|
188
214
|
|
|
189
215
|
if (initial_wait_time == SCHEDULER_DONT_RUN)
|
|
190
216
|
return;
|
|
191
217
|
|
|
192
218
|
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)",
|
|
193
|
-
|
|
219
|
+
name_cstr ? name_cstr : "", initial_wait_time, max_attempts, backoff_increase_factor);
|
|
194
220
|
|
|
195
221
|
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);
|
|
222
|
+
ESP_LOGE(TAG, "backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor, name_cstr ? name_cstr : "");
|
|
199
223
|
backoff_increase_factor = 1;
|
|
200
224
|
}
|
|
201
225
|
|
|
@@ -204,31 +228,56 @@ void HOT Scheduler::set_retry(Component *component, const std::string &name, uin
|
|
|
204
228
|
args->retry_countdown = max_attempts;
|
|
205
229
|
args->current_interval = initial_wait_time;
|
|
206
230
|
args->component = component;
|
|
207
|
-
args->name = "
|
|
231
|
+
args->name = name_cstr ? name_cstr : ""; // Convert to std::string for RetryArgs
|
|
208
232
|
args->backoff_increase_factor = backoff_increase_factor;
|
|
209
233
|
args->scheduler = this;
|
|
210
234
|
|
|
211
|
-
// First execution of `func` immediately
|
|
212
|
-
this->
|
|
235
|
+
// First execution of `func` immediately - use set_timer_common_ with is_retry=true
|
|
236
|
+
this->set_timer_common_(
|
|
237
|
+
component, SchedulerItem::TIMEOUT, false, &args->name, 0, [args]() { retry_handler(args); },
|
|
238
|
+
/* is_retry= */ true);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time,
|
|
242
|
+
uint8_t max_attempts, std::function<RetryResult(uint8_t)> func,
|
|
243
|
+
float backoff_increase_factor) {
|
|
244
|
+
this->set_retry_common_(component, false, &name, initial_wait_time, max_attempts, std::move(func),
|
|
245
|
+
backoff_increase_factor);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
void HOT Scheduler::set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts,
|
|
249
|
+
std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
|
|
250
|
+
this->set_retry_common_(component, true, name, initial_wait_time, max_attempts, std::move(func),
|
|
251
|
+
backoff_increase_factor);
|
|
213
252
|
}
|
|
214
253
|
bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) {
|
|
215
|
-
return this->
|
|
254
|
+
return this->cancel_retry(component, name.c_str());
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
bool HOT Scheduler::cancel_retry(Component *component, const char *name) {
|
|
258
|
+
// Cancel timeouts that have is_retry flag set
|
|
259
|
+
LockGuard guard{this->lock_};
|
|
260
|
+
return this->cancel_item_locked_(component, name, SchedulerItem::TIMEOUT, /* match_retry= */ true);
|
|
216
261
|
}
|
|
217
262
|
|
|
218
|
-
optional<uint32_t> HOT Scheduler::next_schedule_in() {
|
|
263
|
+
optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
|
|
219
264
|
// IMPORTANT: This method should only be called from the main thread (loop task).
|
|
220
|
-
// It
|
|
265
|
+
// It performs cleanup and accesses items_[0] without holding a lock, which is only
|
|
221
266
|
// safe when called from the main thread. Other threads must not call this method.
|
|
222
|
-
|
|
267
|
+
|
|
268
|
+
// If no items, return empty optional
|
|
269
|
+
if (this->cleanup_() == 0)
|
|
223
270
|
return {};
|
|
271
|
+
|
|
224
272
|
auto &item = this->items_[0];
|
|
225
|
-
|
|
226
|
-
|
|
273
|
+
// Convert the fresh timestamp from caller (usually Application::loop()) to 64-bit
|
|
274
|
+
const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from caller
|
|
275
|
+
if (item->next_execution_ < now_64)
|
|
227
276
|
return 0;
|
|
228
|
-
return item->next_execution_ -
|
|
277
|
+
return item->next_execution_ - now_64;
|
|
229
278
|
}
|
|
230
|
-
void HOT Scheduler::call() {
|
|
231
|
-
#
|
|
279
|
+
void HOT Scheduler::call(uint32_t now) {
|
|
280
|
+
#ifndef ESPHOME_THREAD_SINGLE
|
|
232
281
|
// Process defer queue first to guarantee FIFO execution order for deferred items.
|
|
233
282
|
// Previously, defer() used the heap which gave undefined order for equal timestamps,
|
|
234
283
|
// causing race conditions on multi-core systems (ESP32, BK7200).
|
|
@@ -236,8 +285,7 @@ void HOT Scheduler::call() {
|
|
|
236
285
|
// - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_
|
|
237
286
|
// - Items execute in exact order they were deferred (FIFO guarantee)
|
|
238
287
|
// - No deferred items exist in to_add_, so processing order doesn't affect correctness
|
|
239
|
-
//
|
|
240
|
-
// (ESP8266: single-core, RP2040: empty mutex implementation).
|
|
288
|
+
// Single-core platforms don't use this queue and fall back to the heap-based approach.
|
|
241
289
|
//
|
|
242
290
|
// Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still
|
|
243
291
|
// processed here. They are removed from the queue normally via pop_front() but skipped
|
|
@@ -256,23 +304,33 @@ void HOT Scheduler::call() {
|
|
|
256
304
|
// Execute callback without holding lock to prevent deadlocks
|
|
257
305
|
// if the callback tries to call defer() again
|
|
258
306
|
if (!this->should_skip_item_(item.get())) {
|
|
259
|
-
this->execute_item_(item.get());
|
|
307
|
+
this->execute_item_(item.get(), now);
|
|
260
308
|
}
|
|
261
309
|
}
|
|
262
|
-
#endif
|
|
310
|
+
#endif /* not ESPHOME_THREAD_SINGLE */
|
|
263
311
|
|
|
264
|
-
|
|
312
|
+
// Convert the fresh timestamp from main loop to 64-bit for scheduler operations
|
|
313
|
+
const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from Application::loop()
|
|
265
314
|
this->process_to_add();
|
|
266
315
|
|
|
267
316
|
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
268
317
|
static uint64_t last_print = 0;
|
|
269
318
|
|
|
270
|
-
if (
|
|
271
|
-
last_print =
|
|
319
|
+
if (now_64 - last_print > 2000) {
|
|
320
|
+
last_print = now_64;
|
|
272
321
|
std::vector<std::unique_ptr<SchedulerItem>> old_items;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
322
|
+
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
|
323
|
+
const auto last_dbg = this->last_millis_.load(std::memory_order_relaxed);
|
|
324
|
+
const auto major_dbg = this->millis_major_.load(std::memory_order_relaxed);
|
|
325
|
+
ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64,
|
|
326
|
+
major_dbg, last_dbg);
|
|
327
|
+
#else /* not ESPHOME_THREAD_MULTI_ATOMICS */
|
|
328
|
+
ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64,
|
|
329
|
+
this->millis_major_, this->last_millis_);
|
|
330
|
+
#endif /* else ESPHOME_THREAD_MULTI_ATOMICS */
|
|
331
|
+
// Cleanup before debug output
|
|
332
|
+
this->cleanup_();
|
|
333
|
+
while (!this->items_.empty()) {
|
|
276
334
|
std::unique_ptr<SchedulerItem> item;
|
|
277
335
|
{
|
|
278
336
|
LockGuard guard{this->lock_};
|
|
@@ -283,7 +341,7 @@ void HOT Scheduler::call() {
|
|
|
283
341
|
const char *name = item->get_name();
|
|
284
342
|
ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64,
|
|
285
343
|
item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
|
|
286
|
-
item->next_execution_ -
|
|
344
|
+
item->next_execution_ - now_64, item->next_execution_);
|
|
287
345
|
|
|
288
346
|
old_items.push_back(std::move(item));
|
|
289
347
|
}
|
|
@@ -296,7 +354,7 @@ void HOT Scheduler::call() {
|
|
|
296
354
|
std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
|
|
297
355
|
}
|
|
298
356
|
}
|
|
299
|
-
#endif
|
|
357
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
300
358
|
|
|
301
359
|
// If we have too many items to remove
|
|
302
360
|
if (this->to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
|
|
@@ -323,12 +381,14 @@ void HOT Scheduler::call() {
|
|
|
323
381
|
this->to_remove_ = 0;
|
|
324
382
|
}
|
|
325
383
|
|
|
326
|
-
|
|
384
|
+
// Cleanup removed items before processing
|
|
385
|
+
this->cleanup_();
|
|
386
|
+
while (!this->items_.empty()) {
|
|
327
387
|
// use scoping to indicate visibility of `item` variable
|
|
328
388
|
{
|
|
329
389
|
// Don't copy-by value yet
|
|
330
390
|
auto &item = this->items_[0];
|
|
331
|
-
if (item->next_execution_ >
|
|
391
|
+
if (item->next_execution_ > now_64) {
|
|
332
392
|
// Not reached timeout yet, done for this call
|
|
333
393
|
break;
|
|
334
394
|
}
|
|
@@ -342,13 +402,13 @@ void HOT Scheduler::call() {
|
|
|
342
402
|
const char *item_name = item->get_name();
|
|
343
403
|
ESP_LOGV(TAG, "Running %s '%s/%s' with interval=%" PRIu32 " next_execution=%" PRIu64 " (now=%" PRIu64 ")",
|
|
344
404
|
item->get_type_str(), item->get_source(), item_name ? item_name : "(null)", item->interval,
|
|
345
|
-
item->next_execution_,
|
|
346
|
-
#endif
|
|
405
|
+
item->next_execution_, now_64);
|
|
406
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
347
407
|
|
|
348
408
|
// Warning: During callback(), a lot of stuff can happen, including:
|
|
349
409
|
// - timeouts/intervals get added, potentially invalidating vector pointers
|
|
350
410
|
// - timeouts/intervals get cancelled
|
|
351
|
-
this->execute_item_(item.get());
|
|
411
|
+
this->execute_item_(item.get(), now);
|
|
352
412
|
}
|
|
353
413
|
|
|
354
414
|
{
|
|
@@ -367,7 +427,7 @@ void HOT Scheduler::call() {
|
|
|
367
427
|
}
|
|
368
428
|
|
|
369
429
|
if (item->type == SchedulerItem::INTERVAL) {
|
|
370
|
-
item->next_execution_ =
|
|
430
|
+
item->next_execution_ = now_64 + item->interval;
|
|
371
431
|
// Add new item directly to to_add_
|
|
372
432
|
// since we have the lock held
|
|
373
433
|
this->to_add_.push_back(std::move(item));
|
|
@@ -389,8 +449,8 @@ void HOT Scheduler::process_to_add() {
|
|
|
389
449
|
}
|
|
390
450
|
this->to_add_.clear();
|
|
391
451
|
}
|
|
392
|
-
|
|
393
|
-
// Fast path: if nothing to remove, just return
|
|
452
|
+
size_t HOT Scheduler::cleanup_() {
|
|
453
|
+
// Fast path: if nothing to remove, just return the current size
|
|
394
454
|
// Reading to_remove_ without lock is safe because:
|
|
395
455
|
// 1. We only call this from the main thread during call()
|
|
396
456
|
// 2. If it's 0, there's definitely nothing to cleanup
|
|
@@ -398,7 +458,7 @@ void HOT Scheduler::cleanup_() {
|
|
|
398
458
|
// 4. Not all platforms support atomics, so we accept this race in favor of performance
|
|
399
459
|
// 5. The worst case is a one-loop-iteration delay in cleanup, which is harmless
|
|
400
460
|
if (this->to_remove_ == 0)
|
|
401
|
-
return;
|
|
461
|
+
return this->items_.size();
|
|
402
462
|
|
|
403
463
|
// We must hold the lock for the entire cleanup operation because:
|
|
404
464
|
// 1. We're modifying items_ (via pop_raw_) which requires exclusive access
|
|
@@ -412,10 +472,11 @@ void HOT Scheduler::cleanup_() {
|
|
|
412
472
|
while (!this->items_.empty()) {
|
|
413
473
|
auto &item = this->items_[0];
|
|
414
474
|
if (!item->remove)
|
|
415
|
-
|
|
475
|
+
break;
|
|
416
476
|
this->to_remove_--;
|
|
417
477
|
this->pop_raw_();
|
|
418
478
|
}
|
|
479
|
+
return this->items_.size();
|
|
419
480
|
}
|
|
420
481
|
void HOT Scheduler::pop_raw_() {
|
|
421
482
|
std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
|
|
@@ -423,11 +484,9 @@ void HOT Scheduler::pop_raw_() {
|
|
|
423
484
|
}
|
|
424
485
|
|
|
425
486
|
// Helper to execute a scheduler item
|
|
426
|
-
void HOT Scheduler::execute_item_(SchedulerItem *item) {
|
|
487
|
+
void HOT Scheduler::execute_item_(SchedulerItem *item, uint32_t now) {
|
|
427
488
|
App.set_current_component(item->component);
|
|
428
|
-
|
|
429
|
-
uint32_t now_ms = millis();
|
|
430
|
-
WarnIfComponentBlockingGuard guard{item->component, now_ms};
|
|
489
|
+
WarnIfComponentBlockingGuard guard{item->component, now};
|
|
431
490
|
item->callback();
|
|
432
491
|
guard.finish();
|
|
433
492
|
}
|
|
@@ -444,7 +503,8 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co
|
|
|
444
503
|
}
|
|
445
504
|
|
|
446
505
|
// 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
|
|
506
|
+
bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type,
|
|
507
|
+
bool match_retry) {
|
|
448
508
|
// Early return if name is invalid - no items to cancel
|
|
449
509
|
if (name_cstr == nullptr) {
|
|
450
510
|
return false;
|
|
@@ -453,21 +513,21 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
|
453
513
|
size_t total_cancelled = 0;
|
|
454
514
|
|
|
455
515
|
// Check all containers for matching items
|
|
456
|
-
#
|
|
516
|
+
#ifndef ESPHOME_THREAD_SINGLE
|
|
457
517
|
// Only check defer queue for timeouts (intervals never go there)
|
|
458
518
|
if (type == SchedulerItem::TIMEOUT) {
|
|
459
519
|
for (auto &item : this->defer_queue_) {
|
|
460
|
-
if (this->matches_item_(item, component, name_cstr, type)) {
|
|
520
|
+
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
|
461
521
|
item->remove = true;
|
|
462
522
|
total_cancelled++;
|
|
463
523
|
}
|
|
464
524
|
}
|
|
465
525
|
}
|
|
466
|
-
#endif
|
|
526
|
+
#endif /* not ESPHOME_THREAD_SINGLE */
|
|
467
527
|
|
|
468
528
|
// Cancel items in the main heap
|
|
469
529
|
for (auto &item : this->items_) {
|
|
470
|
-
if (this->matches_item_(item, component, name_cstr, type)) {
|
|
530
|
+
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
|
471
531
|
item->remove = true;
|
|
472
532
|
total_cancelled++;
|
|
473
533
|
this->to_remove_++; // Track removals for heap items
|
|
@@ -476,7 +536,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
|
476
536
|
|
|
477
537
|
// Cancel items in to_add_
|
|
478
538
|
for (auto &item : this->to_add_) {
|
|
479
|
-
if (this->matches_item_(item, component, name_cstr, type)) {
|
|
539
|
+
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
|
480
540
|
item->remove = true;
|
|
481
541
|
total_cancelled++;
|
|
482
542
|
// Don't track removals for to_add_ items
|
|
@@ -486,19 +546,162 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
|
486
546
|
return total_cancelled > 0;
|
|
487
547
|
}
|
|
488
548
|
|
|
489
|
-
uint64_t Scheduler::
|
|
490
|
-
//
|
|
491
|
-
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
549
|
+
uint64_t Scheduler::millis_64_(uint32_t now) {
|
|
550
|
+
// THREAD SAFETY NOTE:
|
|
551
|
+
// This function has three implementations, based on the precompiler flags
|
|
552
|
+
// - ESPHOME_THREAD_SINGLE - Runs on single-threaded platforms (ESP8266, RP2040, etc.)
|
|
553
|
+
// - ESPHOME_THREAD_MULTI_NO_ATOMICS - Runs on multi-threaded platforms without atomics (LibreTiny)
|
|
554
|
+
// - ESPHOME_THREAD_MULTI_ATOMICS - Runs on multi-threaded platforms with atomics (ESP32, HOST, etc.)
|
|
555
|
+
//
|
|
556
|
+
// Make sure all changes are synchronized if you edit this function.
|
|
557
|
+
//
|
|
558
|
+
// IMPORTANT: Always pass fresh millis() values to this function. The implementation
|
|
559
|
+
// handles out-of-order timestamps between threads, but minimizing time differences
|
|
560
|
+
// helps maintain accuracy.
|
|
561
|
+
//
|
|
562
|
+
|
|
563
|
+
#ifdef ESPHOME_THREAD_SINGLE
|
|
564
|
+
// This is the single core implementation.
|
|
565
|
+
//
|
|
566
|
+
// Single-core platforms have no concurrency, so this is a simple implementation
|
|
567
|
+
// that just tracks 32-bit rollover (every 49.7 days) without any locking or atomics.
|
|
568
|
+
|
|
569
|
+
uint16_t major = this->millis_major_;
|
|
570
|
+
uint32_t last = this->last_millis_;
|
|
571
|
+
|
|
572
|
+
// Check for rollover
|
|
573
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
495
574
|
this->millis_major_++;
|
|
496
|
-
|
|
497
|
-
|
|
575
|
+
major++;
|
|
576
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
577
|
+
ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
|
|
578
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
498
579
|
}
|
|
499
|
-
|
|
580
|
+
|
|
581
|
+
// Only update if time moved forward
|
|
582
|
+
if (now > last) {
|
|
583
|
+
this->last_millis_ = now;
|
|
584
|
+
}
|
|
585
|
+
|
|
500
586
|
// Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
|
|
501
|
-
return now + (static_cast<uint64_t>(
|
|
587
|
+
return now + (static_cast<uint64_t>(major) << 32);
|
|
588
|
+
|
|
589
|
+
#elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
|
|
590
|
+
// This is the multi core no atomics implementation.
|
|
591
|
+
//
|
|
592
|
+
// Without atomics, this implementation uses locks more aggressively:
|
|
593
|
+
// 1. Always locks when near the rollover boundary (within 10 seconds)
|
|
594
|
+
// 2. Always locks when detecting a large backwards jump
|
|
595
|
+
// 3. Updates without lock in normal forward progression (accepting minor races)
|
|
596
|
+
// This is less efficient but necessary without atomic operations.
|
|
597
|
+
uint16_t major = this->millis_major_;
|
|
598
|
+
uint32_t last = this->last_millis_;
|
|
599
|
+
|
|
600
|
+
// Define a safe window around the rollover point (10 seconds)
|
|
601
|
+
// This covers any reasonable scheduler delays or thread preemption
|
|
602
|
+
static const uint32_t ROLLOVER_WINDOW = 10000; // 10 seconds in milliseconds
|
|
603
|
+
|
|
604
|
+
// Check if we're near the rollover boundary (close to std::numeric_limits<uint32_t>::max() or just past 0)
|
|
605
|
+
bool near_rollover = (last > (std::numeric_limits<uint32_t>::max() - ROLLOVER_WINDOW)) || (now < ROLLOVER_WINDOW);
|
|
606
|
+
|
|
607
|
+
if (near_rollover || (now < last && (last - now) > HALF_MAX_UINT32)) {
|
|
608
|
+
// Near rollover or detected a rollover - need lock for safety
|
|
609
|
+
LockGuard guard{this->lock_};
|
|
610
|
+
// Re-read with lock held
|
|
611
|
+
last = this->last_millis_;
|
|
612
|
+
|
|
613
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
614
|
+
// True rollover detected (happens every ~49.7 days)
|
|
615
|
+
this->millis_major_++;
|
|
616
|
+
major++;
|
|
617
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
618
|
+
ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
|
|
619
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
620
|
+
}
|
|
621
|
+
// Update last_millis_ while holding lock
|
|
622
|
+
this->last_millis_ = now;
|
|
623
|
+
} else if (now > last) {
|
|
624
|
+
// Normal case: Not near rollover and time moved forward
|
|
625
|
+
// Update without lock. While this may cause minor races (microseconds of
|
|
626
|
+
// backwards time movement), they're acceptable because:
|
|
627
|
+
// 1. The scheduler operates at millisecond resolution, not microsecond
|
|
628
|
+
// 2. We've already prevented the critical rollover race condition
|
|
629
|
+
// 3. Any backwards movement is orders of magnitude smaller than scheduler delays
|
|
630
|
+
this->last_millis_ = now;
|
|
631
|
+
}
|
|
632
|
+
// If now <= last and we're not near rollover, don't update
|
|
633
|
+
// This minimizes backwards time movement
|
|
634
|
+
|
|
635
|
+
// Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
|
|
636
|
+
return now + (static_cast<uint64_t>(major) << 32);
|
|
637
|
+
|
|
638
|
+
#elif defined(ESPHOME_THREAD_MULTI_ATOMICS)
|
|
639
|
+
// This is the multi core with atomics implementation.
|
|
640
|
+
//
|
|
641
|
+
// Uses atomic operations with acquire/release semantics to ensure coherent
|
|
642
|
+
// reads of millis_major_ and last_millis_ across cores. Features:
|
|
643
|
+
// 1. Epoch-coherency retry loop to handle concurrent updates
|
|
644
|
+
// 2. Lock only taken for actual rollover detection and update
|
|
645
|
+
// 3. Lock-free CAS updates for normal forward time progression
|
|
646
|
+
// 4. Memory ordering ensures cores see consistent time values
|
|
647
|
+
|
|
648
|
+
for (;;) {
|
|
649
|
+
uint16_t major = this->millis_major_.load(std::memory_order_acquire);
|
|
650
|
+
|
|
651
|
+
/*
|
|
652
|
+
* Acquire so that if we later decide **not** to take the lock we still
|
|
653
|
+
* observe a `millis_major_` value coherent with the loaded `last_millis_`.
|
|
654
|
+
* The acquire load ensures any later read of `millis_major_` sees its
|
|
655
|
+
* corresponding increment.
|
|
656
|
+
*/
|
|
657
|
+
uint32_t last = this->last_millis_.load(std::memory_order_acquire);
|
|
658
|
+
|
|
659
|
+
// If we might be near a rollover (large backwards jump), take the lock for the entire operation
|
|
660
|
+
// This ensures rollover detection and last_millis_ update are atomic together
|
|
661
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
662
|
+
// Potential rollover - need lock for atomic rollover detection + update
|
|
663
|
+
LockGuard guard{this->lock_};
|
|
664
|
+
// Re-read with lock held; mutex already provides ordering
|
|
665
|
+
last = this->last_millis_.load(std::memory_order_relaxed);
|
|
666
|
+
|
|
667
|
+
if (now < last && (last - now) > HALF_MAX_UINT32) {
|
|
668
|
+
// True rollover detected (happens every ~49.7 days)
|
|
669
|
+
this->millis_major_.fetch_add(1, std::memory_order_relaxed);
|
|
670
|
+
major++;
|
|
671
|
+
#ifdef ESPHOME_DEBUG_SCHEDULER
|
|
672
|
+
ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
|
|
673
|
+
#endif /* ESPHOME_DEBUG_SCHEDULER */
|
|
674
|
+
}
|
|
675
|
+
/*
|
|
676
|
+
* Update last_millis_ while holding the lock to prevent races
|
|
677
|
+
* Publish the new low-word *after* bumping `millis_major_` (done above)
|
|
678
|
+
* so readers never see a mismatched pair.
|
|
679
|
+
*/
|
|
680
|
+
this->last_millis_.store(now, std::memory_order_release);
|
|
681
|
+
} else {
|
|
682
|
+
// Normal case: Try lock-free update, but only allow forward movement within same epoch
|
|
683
|
+
// This prevents accidentally moving backwards across a rollover boundary
|
|
684
|
+
while (now > last && (now - last) < HALF_MAX_UINT32) {
|
|
685
|
+
if (this->last_millis_.compare_exchange_weak(last, now,
|
|
686
|
+
std::memory_order_release, // success
|
|
687
|
+
std::memory_order_relaxed)) { // failure
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
// CAS failure means no data was published; relaxed is fine
|
|
691
|
+
// last is automatically updated by compare_exchange_weak if it fails
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
uint16_t major_end = this->millis_major_.load(std::memory_order_relaxed);
|
|
695
|
+
if (major_end == major)
|
|
696
|
+
return now + (static_cast<uint64_t>(major) << 32);
|
|
697
|
+
}
|
|
698
|
+
// Unreachable - the loop always returns when major_end == major
|
|
699
|
+
__builtin_unreachable();
|
|
700
|
+
|
|
701
|
+
#else
|
|
702
|
+
#error \
|
|
703
|
+
"No platform threading model defined. One of ESPHOME_THREAD_SINGLE, ESPHOME_THREAD_MULTI_NO_ATOMICS, or ESPHOME_THREAD_MULTI_ATOMICS must be defined."
|
|
704
|
+
#endif
|
|
502
705
|
}
|
|
503
706
|
|
|
504
707
|
bool HOT Scheduler::SchedulerItem::cmp(const std::unique_ptr<SchedulerItem> &a,
|