mirror of
https://github.com/esphome/esphome.git
synced 2025-11-03 08:31:47 +00:00
Merge upstream/dev into integration
Resolved conflicts in: - esphome/components/fan/fan.cpp: Preserved pointer-based preset mode optimization - esphome/components/fan/fan_traits.h: Kept cstring include for strcmp - esphome/components/web_server_idf/web_server_idf.cpp: Kept float_buf_size constant 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ from esphome.const import (
|
||||
CONF_TYPE_ID,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.core import ID, Lambda
|
||||
from esphome.cpp_generator import (
|
||||
LambdaExpression,
|
||||
MockObj,
|
||||
@@ -310,6 +310,30 @@ async def for_condition_to_code(
|
||||
return var
|
||||
|
||||
|
||||
@register_condition(
|
||||
"component.is_idle",
|
||||
LambdaCondition,
|
||||
maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(cg.Component),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def component_is_idle_condition_to_code(
|
||||
config: ConfigType,
|
||||
condition_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
comp = await cg.get_variable(config[CONF_ID])
|
||||
lambda_ = await cg.process_lambda(
|
||||
Lambda(f"return {comp}->is_idle();"), args, return_type=bool
|
||||
)
|
||||
return new_lambda_pvariable(
|
||||
condition_id, lambda_, StatelessLambdaCondition, template_arg
|
||||
)
|
||||
|
||||
|
||||
@register_action(
|
||||
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
|
||||
)
|
||||
|
||||
@@ -77,6 +77,9 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
||||
}
|
||||
} else {
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
// For non-notify characteristics, trigger an immediate read after service discovery
|
||||
// to avoid peripherals disconnecting due to inactivity
|
||||
this->update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,9 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
}
|
||||
} else {
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
// For non-notify characteristics, trigger an immediate read after service discovery
|
||||
// to avoid peripherals disconnecting due to inactivity
|
||||
this->update();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -121,10 +121,13 @@ void FanRestoreState::apply(Fan &fan) {
|
||||
fan.speed = this->speed;
|
||||
fan.direction = this->direction;
|
||||
|
||||
// Use stored preset index to get preset name from traits
|
||||
const auto &preset_modes = fan.get_traits().supported_preset_modes();
|
||||
if (this->preset_mode < preset_modes.size()) {
|
||||
fan.set_preset_mode_(preset_modes[this->preset_mode]);
|
||||
auto traits = fan.get_traits();
|
||||
if (traits.supports_preset_modes()) {
|
||||
// Use stored preset index to get preset name from traits
|
||||
const auto &preset_modes = traits.supported_preset_modes();
|
||||
if (this->preset_mode < preset_modes.size()) {
|
||||
fan.set_preset_mode_(preset_modes[this->preset_mode]);
|
||||
}
|
||||
}
|
||||
|
||||
fan.publish_state();
|
||||
@@ -228,7 +231,7 @@ void Fan::save_state_() {
|
||||
state.direction = this->direction;
|
||||
|
||||
const char *preset = this->get_preset_mode();
|
||||
if (preset != nullptr) {
|
||||
if (traits.supports_preset_modes() && preset != nullptr) {
|
||||
const auto &preset_modes = traits.supported_preset_modes();
|
||||
// Find index of current preset mode (pointer comparison is safe since preset is from traits)
|
||||
for (size_t i = 0; i < preset_modes.size(); i++) {
|
||||
|
||||
@@ -137,7 +137,11 @@ async def lvgl_is_idle(config, condition_id, template_arg, args):
|
||||
lvgl = config[CONF_LVGL_ID]
|
||||
timeout = await lv_milliseconds.process(config[CONF_TIMEOUT])
|
||||
async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context:
|
||||
lv_add(ReturnStatement(lvgl_comp.is_idle(timeout)))
|
||||
lv_add(
|
||||
ReturnStatement(
|
||||
lv_expr.disp_get_inactive_time(lvgl_comp.get_disp()) > timeout
|
||||
)
|
||||
)
|
||||
var = cg.new_Pvariable(
|
||||
condition_id,
|
||||
TemplateArguments(LvglComponent, *template_arg),
|
||||
|
||||
@@ -175,7 +175,6 @@ class LvglComponent : public PollingComponent {
|
||||
static void monitor_cb(lv_disp_drv_t *disp_drv, uint32_t time, uint32_t px);
|
||||
static void render_start_cb(lv_disp_drv_t *disp_drv);
|
||||
void dump_config() override;
|
||||
bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; }
|
||||
lv_disp_t *get_disp() { return this->disp_; }
|
||||
lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); }
|
||||
// Pause or resume the display.
|
||||
|
||||
@@ -103,7 +103,7 @@ template<typename... Ts> class ForCondition : public Condition<Ts...>, public Co
|
||||
bool check_internal() {
|
||||
bool cond = this->condition_->check();
|
||||
if (!cond)
|
||||
this->last_inactive_ = millis();
|
||||
this->last_inactive_ = App.get_loop_component_start_time();
|
||||
return cond;
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ template<typename... Ts> class WaitUntilAction : public Action<Ts...>, public Co
|
||||
if (this->num_running_ == 0)
|
||||
return;
|
||||
|
||||
auto now = millis();
|
||||
auto now = App.get_loop_component_start_time();
|
||||
|
||||
this->var_queue_.remove_if([&](auto &queued) {
|
||||
auto start = std::get<uint32_t>(queued);
|
||||
|
||||
@@ -284,6 +284,7 @@ bool Component::is_ready() const {
|
||||
(this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE ||
|
||||
(this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP;
|
||||
}
|
||||
bool Component::is_idle() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE; }
|
||||
bool Component::can_proceed() { return true; }
|
||||
bool Component::status_has_warning() const { return this->component_state_ & STATUS_LED_WARNING; }
|
||||
bool Component::status_has_error() const { return this->component_state_ & STATUS_LED_ERROR; }
|
||||
|
||||
@@ -141,6 +141,14 @@ class Component {
|
||||
*/
|
||||
bool is_in_loop_state() const;
|
||||
|
||||
/** Check if this component is idle.
|
||||
* Being idle means being in LOOP_DONE state.
|
||||
* This means the component has completed setup, is not failed, but its loop is currently disabled.
|
||||
*
|
||||
* @return True if the component is idle
|
||||
*/
|
||||
bool is_idle() const;
|
||||
|
||||
/** Mark this component as failed. Any future timeouts/intervals/setup/loop will no longer be called.
|
||||
*
|
||||
* This might be useful if a component wants to indicate that a connection to its peripheral failed.
|
||||
|
||||
@@ -10,7 +10,11 @@ esphome:
|
||||
on_shutdown:
|
||||
logger.log: on_shutdown
|
||||
on_loop:
|
||||
logger.log: on_loop
|
||||
if:
|
||||
condition:
|
||||
component.is_idle: binary_sensor_id
|
||||
then:
|
||||
logger.log: on_loop - sensor idle
|
||||
compile_process_limit: 1
|
||||
min_version: "2025.1"
|
||||
name_add_mac_suffix: true
|
||||
@@ -34,5 +38,6 @@ esphome:
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
id: binary_sensor_id
|
||||
name: Other device sensor
|
||||
device_id: other_device
|
||||
|
||||
@@ -21,12 +21,12 @@ font:
|
||||
id: roboto_greek
|
||||
size: 20
|
||||
glyphs: ["\u0300", "\u00C5", "\U000000C7"]
|
||||
- file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf"
|
||||
- file: "https://media.esphome.io/tests/fonts/Monocraft.ttf"
|
||||
id: monocraft
|
||||
size: 20
|
||||
- file:
|
||||
type: web
|
||||
url: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf"
|
||||
url: "https://media.esphome.io/tests/fonts/Monocraft.ttf"
|
||||
id: monocraft2
|
||||
size: 24
|
||||
- file: $component_dir/Monocraft.ttf
|
||||
|
||||
@@ -21,12 +21,12 @@ font:
|
||||
id: roboto_greek
|
||||
size: 20
|
||||
glyphs: ["\u0300", "\u00C5", "\U000000C7"]
|
||||
- file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf"
|
||||
- file: "https://media.esphome.io/tests/fonts/Monocraft.ttf"
|
||||
id: monocraft
|
||||
size: 20
|
||||
- file:
|
||||
type: web
|
||||
url: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf"
|
||||
url: "https://media.esphome.io/tests/fonts/Monocraft.ttf"
|
||||
id: monocraft2
|
||||
size: 24
|
||||
- file: $component_dir/Monocraft.ttf
|
||||
|
||||
@@ -50,16 +50,16 @@ image:
|
||||
transparency: opaque
|
||||
|
||||
- id: web_svg_image
|
||||
file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg
|
||||
file: https://media.esphome.io/logo/logo.svg
|
||||
resize: 256x48
|
||||
type: BINARY
|
||||
transparency: chroma_key
|
||||
- id: web_tiff_image
|
||||
file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff
|
||||
file: https://media.esphome.io/tests/images/SIPI_Jelly_Beans_4.1.07.tiff
|
||||
type: RGB
|
||||
resize: 48x48
|
||||
- id: web_redirect_image
|
||||
file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4
|
||||
file: https://media.esphome.io/logo/logo.png
|
||||
type: RGB
|
||||
resize: 48x48
|
||||
- id: mdi_alert
|
||||
|
||||
Reference in New Issue
Block a user