1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-30 14:43:51 +00:00

[component] Fix `is_ready` flag when loop disabled (#9501)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Jesse Hills
2025-07-15 19:20:18 +12:00
parent 9bc3ff5f53
commit 02b7db7311
6 changed files with 159 additions and 1 deletions

View File

@@ -1,7 +1,7 @@
from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_NAME
from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL
CODEOWNERS = ["@esphome/tests"]
@@ -10,10 +10,15 @@ LoopTestComponent = loop_test_component_ns.class_("LoopTestComponent", cg.Compon
LoopTestISRComponent = loop_test_component_ns.class_(
"LoopTestISRComponent", cg.Component
)
LoopTestUpdateComponent = loop_test_component_ns.class_(
"LoopTestUpdateComponent", cg.PollingComponent
)
CONF_DISABLE_AFTER = "disable_after"
CONF_TEST_REDUNDANT_OPERATIONS = "test_redundant_operations"
CONF_ISR_COMPONENTS = "isr_components"
CONF_UPDATE_COMPONENTS = "update_components"
CONF_DISABLE_LOOP_AFTER = "disable_loop_after"
COMPONENT_CONFIG_SCHEMA = cv.Schema(
{
@@ -31,11 +36,23 @@ ISR_COMPONENT_CONFIG_SCHEMA = cv.Schema(
}
)
UPDATE_COMPONENT_CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(LoopTestUpdateComponent),
cv.Required(CONF_NAME): cv.string,
cv.Optional(CONF_DISABLE_LOOP_AFTER, default=0): cv.int_,
cv.Optional(CONF_UPDATE_INTERVAL, default="1s"): cv.update_interval,
}
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(LoopTestComponent),
cv.Required(CONF_COMPONENTS): cv.ensure_list(COMPONENT_CONFIG_SCHEMA),
cv.Optional(CONF_ISR_COMPONENTS): cv.ensure_list(ISR_COMPONENT_CONFIG_SCHEMA),
cv.Optional(CONF_UPDATE_COMPONENTS): cv.ensure_list(
UPDATE_COMPONENT_CONFIG_SCHEMA
),
}
).extend(cv.COMPONENT_SCHEMA)
@@ -94,3 +111,12 @@ async def to_code(config):
var = cg.new_Pvariable(isr_config[CONF_ID])
await cg.register_component(var, isr_config)
cg.add(var.set_name(isr_config[CONF_NAME]))
# Create update test components
for update_config in config.get(CONF_UPDATE_COMPONENTS, []):
var = cg.new_Pvariable(update_config[CONF_ID])
await cg.register_component(var, update_config)
cg.add(var.set_name(update_config[CONF_NAME]))
cg.add(var.set_disable_loop_after(update_config[CONF_DISABLE_LOOP_AFTER]))
cg.add(var.set_update_interval(update_config[CONF_UPDATE_INTERVAL]))

View File

@@ -39,5 +39,29 @@ void LoopTestComponent::service_disable() {
this->disable_loop();
}
// LoopTestUpdateComponent implementation
void LoopTestUpdateComponent::setup() {
ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent setup called", this->name_.c_str());
}
void LoopTestUpdateComponent::loop() {
this->loop_count_++;
ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent loop count: %d", this->name_.c_str(), this->loop_count_);
// Disable loop after specified count to test component.update when loop is disabled
if (this->disable_loop_after_ > 0 && this->loop_count_ == this->disable_loop_after_) {
ESP_LOGI(TAG, "[%s] Disabling loop after %d iterations", this->name_.c_str(), this->disable_loop_after_);
this->disable_loop();
}
}
void LoopTestUpdateComponent::update() {
this->update_count_++;
// Check if loop is disabled by testing component state
bool loop_disabled = this->component_state_ == COMPONENT_STATE_LOOP_DONE;
ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent update() called, count: %d, loop_disabled: %s", this->name_.c_str(),
this->update_count_, loop_disabled ? "YES" : "NO");
}
} // namespace loop_test_component
} // namespace esphome

View File

@@ -4,6 +4,7 @@
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/automation.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace loop_test_component {
@@ -54,5 +55,29 @@ template<typename... Ts> class DisableAction : public Action<Ts...> {
LoopTestComponent *parent_;
};
// Component with update() method to test component.update action
class LoopTestUpdateComponent : public PollingComponent {
public:
LoopTestUpdateComponent() : PollingComponent(1000) {} // Default 1s update interval
void set_name(const std::string &name) { this->name_ = name; }
void set_disable_loop_after(int count) { this->disable_loop_after_ = count; }
void setup() override;
void loop() override;
void update() override;
int get_update_count() const { return this->update_count_; }
int get_loop_count() const { return this->loop_count_; }
float get_setup_priority() const override { return setup_priority::DATA; }
protected:
std::string name_;
int loop_count_{0};
int update_count_{0};
int disable_loop_after_{0};
};
} // namespace loop_test_component
} // namespace esphome

View File

@@ -40,6 +40,13 @@ loop_test_component:
- id: isr_test
name: "isr_test"
# Update test component to test component.update when loop is disabled
update_components:
- id: update_test_component
name: "update_test"
disable_loop_after: 3 # Disable loop after 3 iterations
update_interval: 0.1s # Fast update interval for testing
# Interval to re-enable the self_disable_10 component after some time
interval:
- interval: 0.5s
@@ -51,3 +58,28 @@ interval:
- logger.log: "Re-enabling self_disable_10 via service"
- loop_test_component.enable:
id: self_disable_10
# Test component.update on a component with disabled loop
- interval: 0.1s
then:
- lambda: |-
static bool manual_update_done = false;
if (!manual_update_done &&
id(update_test_component).get_loop_count() == 3 &&
id(update_test_component).get_update_count() >= 3) {
ESP_LOGI("main", "Manually calling component.update on update_test_component with disabled loop");
manual_update_done = true;
}
- if:
condition:
lambda: |-
static bool manual_update_triggered = false;
if (!manual_update_triggered &&
id(update_test_component).get_loop_count() == 3 &&
id(update_test_component).get_update_count() >= 3) {
manual_update_triggered = true;
return true;
}
return false;
then:
- component.update: update_test_component