mirror of
https://github.com/esphome/esphome.git
synced 2025-11-08 19:11:49 +00:00
Compare commits
1 Commits
memory_api
...
bl0940_pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e3882a992 |
@@ -741,13 +741,6 @@ def command_vscode(args: ArgsProtocol) -> int | None:
|
||||
|
||||
|
||||
def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
# Set memory analysis options in config
|
||||
if args.analyze_memory:
|
||||
config.setdefault(CONF_ESPHOME, {})["analyze_memory"] = True
|
||||
|
||||
if args.memory_report:
|
||||
config.setdefault(CONF_ESPHOME, {})["memory_report_file"] = args.memory_report
|
||||
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
@@ -1209,17 +1202,6 @@ def parse_args(argv):
|
||||
help="Only generate source code, do not compile.",
|
||||
action="store_true",
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--analyze-memory",
|
||||
help="Analyze and display memory usage by component after compilation.",
|
||||
action="store_true",
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--memory-report",
|
||||
help="Save memory analysis report to a file (supports .json or .txt).",
|
||||
type=str,
|
||||
metavar="FILE",
|
||||
)
|
||||
|
||||
parser_upload = subparsers.add_parser(
|
||||
"upload",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""CLI interface for memory analysis with report generation."""
|
||||
|
||||
from collections import defaultdict
|
||||
import json
|
||||
import sys
|
||||
|
||||
from . import (
|
||||
@@ -284,28 +283,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Export analysis results as JSON."""
|
||||
data = {
|
||||
"components": {
|
||||
name: {
|
||||
"text": mem.text_size,
|
||||
"rodata": mem.rodata_size,
|
||||
"data": mem.data_size,
|
||||
"bss": mem.bss_size,
|
||||
"flash_total": mem.flash_total,
|
||||
"ram_total": mem.ram_total,
|
||||
"symbol_count": mem.symbol_count,
|
||||
}
|
||||
for name, mem in self.components.items()
|
||||
},
|
||||
"totals": {
|
||||
"flash": sum(c.flash_total for c in self.components.values()),
|
||||
"ram": sum(c.ram_total for c in self.components.values()),
|
||||
},
|
||||
}
|
||||
return json.dumps(data, indent=2)
|
||||
|
||||
def dump_uncategorized_symbols(self, output_file: str | None = None) -> None:
|
||||
"""Dump uncategorized symbols for analysis."""
|
||||
# Sort by size descending
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <utility>
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#include "alarm_control_panel.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -35,9 +34,6 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
||||
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
||||
this->current_state_ = state;
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_ALARM_CONTROL_PANEL) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_alarm_control_panel_update(this);
|
||||
#endif
|
||||
if (state == ACP_STATE_TRIGGERED) {
|
||||
this->triggered_callback_.call();
|
||||
} else if (state == ACP_STATE_ARMING) {
|
||||
|
||||
@@ -244,9 +244,6 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# Track controller registration for StaticVector sizing
|
||||
CORE.register_controller()
|
||||
|
||||
cg.add(var.set_port(config[CONF_PORT]))
|
||||
if config[CONF_PASSWORD]:
|
||||
cg.add_define("USE_API_PASSWORD")
|
||||
|
||||
@@ -2147,7 +2147,7 @@ message ListEntitiesEventResponse {
|
||||
EntityCategory entity_category = 7;
|
||||
string device_class = 8;
|
||||
|
||||
repeated string event_types = 9 [(container_pointer_no_template) = "FixedVector<const char *>"];
|
||||
repeated string event_types = 9;
|
||||
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
|
||||
}
|
||||
message EventResponse {
|
||||
|
||||
@@ -1310,7 +1310,8 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c
|
||||
auto *event = static_cast<event::Event *>(entity);
|
||||
ListEntitiesEventResponse msg;
|
||||
msg.set_device_class(event->get_device_class_ref());
|
||||
msg.event_types = &event->get_event_types();
|
||||
for (const auto &event_type : event->get_event_types())
|
||||
msg.event_types.push_back(event_type);
|
||||
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||
is_single);
|
||||
}
|
||||
|
||||
@@ -2877,8 +2877,8 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
buffer.encode_uint32(7, static_cast<uint32_t>(this->entity_category));
|
||||
buffer.encode_string(8, this->device_class_ref_);
|
||||
for (const char *it : *this->event_types) {
|
||||
buffer.encode_string(9, it, strlen(it), true);
|
||||
for (auto &it : this->event_types) {
|
||||
buffer.encode_string(9, it, true);
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
buffer.encode_uint32(10, this->device_id);
|
||||
@@ -2894,9 +2894,9 @@ void ListEntitiesEventResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_bool(1, this->disabled_by_default);
|
||||
size.add_uint32(1, static_cast<uint32_t>(this->entity_category));
|
||||
size.add_length(1, this->device_class_ref_.size());
|
||||
if (!this->event_types->empty()) {
|
||||
for (const char *it : *this->event_types) {
|
||||
size.add_length_force(1, strlen(it));
|
||||
if (!this->event_types.empty()) {
|
||||
for (const auto &it : this->event_types) {
|
||||
size.add_length_force(1, it.size());
|
||||
}
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
|
||||
@@ -2788,7 +2788,7 @@ class ListEntitiesEventResponse final : public InfoResponseProtoMessage {
|
||||
#endif
|
||||
StringRef device_class_ref_{};
|
||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||
const FixedVector<const char *> *event_types{};
|
||||
std::vector<std::string> event_types{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
|
||||
@@ -2053,7 +2053,7 @@ void ListEntitiesEventResponse::dump_to(std::string &out) const {
|
||||
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
||||
dump_field(out, "entity_category", static_cast<enums::EntityCategory>(this->entity_category));
|
||||
dump_field(out, "device_class", this->device_class_ref_);
|
||||
for (const auto &it : *this->event_types) {
|
||||
for (const auto &it : this->event_types) {
|
||||
dump_field(out, "event_types", it, 4);
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
@@ -35,7 +34,7 @@ APIServer::APIServer() {
|
||||
}
|
||||
|
||||
void APIServer::setup() {
|
||||
ControllerRegistry::register_controller(this);
|
||||
this->setup_controller();
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
uint32_t hash = 88491486UL;
|
||||
@@ -270,7 +269,7 @@ bool APIServer::check_password(const uint8_t *password_data, size_t password_len
|
||||
|
||||
void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||
|
||||
// Macro for controller update dispatch
|
||||
// Macro for entities without extra parameters
|
||||
#define API_DISPATCH_UPDATE(entity_type, entity_name) \
|
||||
void APIServer::on_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
if (obj->is_internal()) \
|
||||
@@ -279,6 +278,15 @@ void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||
c->send_##entity_name##_state(obj); \
|
||||
}
|
||||
|
||||
// Macro for entities with extra parameters (but parameters not used in send)
|
||||
#define API_DISPATCH_UPDATE_IGNORE_PARAMS(entity_type, entity_name, ...) \
|
||||
void APIServer::on_##entity_name##_update(entity_type *obj, __VA_ARGS__) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
if (obj->is_internal()) \
|
||||
return; \
|
||||
for (auto &c : this->clients_) \
|
||||
c->send_##entity_name##_state(obj); \
|
||||
}
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
API_DISPATCH_UPDATE(binary_sensor::BinarySensor, binary_sensor)
|
||||
#endif
|
||||
@@ -296,15 +304,15 @@ API_DISPATCH_UPDATE(light::LightState, light)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
API_DISPATCH_UPDATE(sensor::Sensor, sensor)
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(sensor::Sensor, sensor, float state)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
API_DISPATCH_UPDATE(switch_::Switch, switch)
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(switch_::Switch, switch, bool state)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
API_DISPATCH_UPDATE(text_sensor::TextSensor, text_sensor)
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(text_sensor::TextSensor, text_sensor, const std::string &state)
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
@@ -312,7 +320,7 @@ API_DISPATCH_UPDATE(climate::Climate, climate)
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
API_DISPATCH_UPDATE(number::Number, number)
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(number::Number, number, float state)
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
@@ -328,11 +336,11 @@ API_DISPATCH_UPDATE(datetime::DateTimeEntity, datetime)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
API_DISPATCH_UPDATE(text::Text, text)
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(text::Text, text, const std::string &state)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
API_DISPATCH_UPDATE(select::Select, select)
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(select::Select, select, const std::string &state, size_t index)
|
||||
#endif
|
||||
|
||||
#ifdef USE_LOCK
|
||||
@@ -348,13 +356,12 @@ API_DISPATCH_UPDATE(media_player::MediaPlayer, media_player)
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
// Event is a special case - unlike other entities with simple state fields,
|
||||
// events store their state in a member accessed via obj->get_last_event_type()
|
||||
void APIServer::on_event(event::Event *obj) {
|
||||
// Event is a special case - it's the only entity that passes extra parameters to the send method
|
||||
void APIServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto &c : this->clients_)
|
||||
c->send_event(obj, obj->get_last_event_type());
|
||||
c->send_event(obj, event_type);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -72,19 +72,19 @@ class APIServer : public Component, public Controller {
|
||||
void on_light_update(light::LightState *obj) override;
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
void on_sensor_update(sensor::Sensor *obj) override;
|
||||
void on_sensor_update(sensor::Sensor *obj, float state) override;
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
void on_switch_update(switch_::Switch *obj) override;
|
||||
void on_switch_update(switch_::Switch *obj, bool state) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj) override;
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
void on_climate_update(climate::Climate *obj) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void on_number_update(number::Number *obj) override;
|
||||
void on_number_update(number::Number *obj, float state) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void on_date_update(datetime::DateEntity *obj) override;
|
||||
@@ -96,10 +96,10 @@ class APIServer : public Component, public Controller {
|
||||
void on_datetime_update(datetime::DateTimeEntity *obj) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
void on_text_update(text::Text *obj) override;
|
||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void on_select_update(select::Select *obj) override;
|
||||
void on_select_update(select::Select *obj, const std::string &state, size_t index) override;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
void on_lock_update(lock::Lock *obj) override;
|
||||
@@ -141,7 +141,7 @@ class APIServer : public Component, public Controller {
|
||||
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
void on_event(event::Event *obj) override;
|
||||
void on_event(event::Event *obj, const std::string &event_type) override;
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
void on_update(update::UpdateEntity *obj) override;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "binary_sensor.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -39,9 +37,6 @@ void BinarySensor::send_state_internal(bool new_state) {
|
||||
// Note that set_state_ de-dups and will only trigger callbacks if the state has actually changed
|
||||
if (this->set_state_(new_state)) {
|
||||
ESP_LOGD(TAG, "'%s': New state is %s", this->get_name().c_str(), ONOFF(new_state));
|
||||
#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_binary_sensor_update(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ static const char *const TAG = "bl0940.number";
|
||||
void CalibrationNumber::setup() {
|
||||
float value = 0.0f;
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
if (!this->pref_.load(&value)) {
|
||||
value = 0.0f;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "climate.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/macros.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -465,9 +463,6 @@ void Climate::publish_state() {
|
||||
|
||||
// Send state to frontend
|
||||
this->state_callback_.call(*this);
|
||||
#if defined(USE_CLIMATE) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_climate_update(this);
|
||||
#endif
|
||||
// Save state
|
||||
this->save_state_();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "cover.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include <strings.h>
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -171,9 +169,6 @@ void Cover::publish_state(bool save) {
|
||||
ESP_LOGD(TAG, " Current Operation: %s", cover_operation_to_str(this->current_operation));
|
||||
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_COVER) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_cover_update(this);
|
||||
#endif
|
||||
|
||||
if (save) {
|
||||
CoverRestoreState restore{};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "date_entity.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
@@ -33,9 +32,6 @@ void DateEntity::publish_state() {
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_DATE) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_date_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
DateCall DateEntity::make_call() { return DateCall(this); }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "datetime_entity.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
@@ -49,9 +48,6 @@ void DateTimeEntity::publish_state() {
|
||||
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
||||
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_datetime_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
DateTimeCall DateTimeEntity::make_call() { return DateTimeCall(this); }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "time_entity.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
@@ -30,9 +29,6 @@ void TimeEntity::publish_state() {
|
||||
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
||||
this->second_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_time_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
TimeCall TimeEntity::make_call() { return TimeCall(this); }
|
||||
|
||||
@@ -96,10 +96,6 @@ void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->advertising_set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
this->advertising_init_();
|
||||
this->advertising_->set_manufacturer_data(data);
|
||||
this->advertising_start();
|
||||
|
||||
@@ -118,7 +118,6 @@ class ESP32BLE : public Component {
|
||||
void advertising_start();
|
||||
void advertising_set_service_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void advertising_set_appearance(uint16_t appearance) { this->appearance_ = appearance; }
|
||||
void advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name);
|
||||
void advertising_add_service_uuid(ESPBTUUID uuid);
|
||||
|
||||
@@ -59,10 +59,6 @@ void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
delete[] this->advertising_data_.p_manufacturer_data;
|
||||
this->advertising_data_.p_manufacturer_data = nullptr;
|
||||
this->advertising_data_.manufacturer_len = data.size();
|
||||
|
||||
@@ -37,7 +37,6 @@ class BLEAdvertising {
|
||||
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
|
||||
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
|
||||
void set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void set_appearance(uint16_t appearance) { this->advertising_data_.appearance = appearance; }
|
||||
void set_service_data(const std::vector<uint8_t> &data);
|
||||
void set_service_data(std::span<const uint8_t> data);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "esp32_ble_beacon.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
|
||||
@@ -15,10 +15,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_w
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
characteristic->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
@@ -30,10 +27,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
descriptor->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "event.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -9,11 +8,11 @@ namespace event {
|
||||
static const char *const TAG = "event";
|
||||
|
||||
void Event::trigger(const std::string &event_type) {
|
||||
// Linear search with strcmp - faster than std::set for small datasets (1-5 items typical)
|
||||
const char *found = nullptr;
|
||||
for (const char *type : this->types_) {
|
||||
if (strcmp(type, event_type.c_str()) == 0) {
|
||||
found = type;
|
||||
// Linear search - faster than std::set for small datasets (1-5 items typical)
|
||||
const std::string *found = nullptr;
|
||||
for (const auto &type : this->types_) {
|
||||
if (type == event_type) {
|
||||
found = &type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -21,44 +20,9 @@ void Event::trigger(const std::string &event_type) {
|
||||
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str());
|
||||
return;
|
||||
}
|
||||
this->last_event_type_ = found;
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), this->last_event_type_);
|
||||
last_event_type = found;
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str());
|
||||
this->event_callback_.call(event_type);
|
||||
#if defined(USE_EVENT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_event(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Event::set_event_types(const FixedVector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::set_event_types(const std::vector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::set_event_types(const FixedVector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::set_event_types(const std::vector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
@@ -24,39 +22,16 @@ namespace event {
|
||||
|
||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
const std::string *last_event_type;
|
||||
|
||||
void trigger(const std::string &event_type);
|
||||
|
||||
/// Set the event types supported by this event (from initializer list).
|
||||
void set_event_types(std::initializer_list<const char *> event_types) {
|
||||
this->types_ = event_types;
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
/// Set the event types supported by this event (from FixedVector).
|
||||
void set_event_types(const FixedVector<const char *> &event_types);
|
||||
/// Set the event types supported by this event (from vector).
|
||||
void set_event_types(const std::vector<const char *> &event_types);
|
||||
|
||||
// Deleted overloads to catch incorrect std::string usage at compile time with clear error messages
|
||||
void set_event_types(std::initializer_list<std::string> event_types) = delete;
|
||||
void set_event_types(const FixedVector<std::string> &event_types) = delete;
|
||||
void set_event_types(const std::vector<std::string> &event_types) = delete;
|
||||
|
||||
/// Return the event types supported by this event.
|
||||
const FixedVector<const char *> &get_event_types() const { return this->types_; }
|
||||
|
||||
/// Return the last triggered event type (pointer to string in types_), or nullptr if no event triggered yet.
|
||||
const char *get_last_event_type() const { return this->last_event_type_; }
|
||||
|
||||
void set_event_types(const std::initializer_list<std::string> &event_types) { this->types_ = event_types; }
|
||||
const FixedVector<std::string> &get_event_types() const { return this->types_; }
|
||||
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
||||
|
||||
protected:
|
||||
CallbackManager<void(const std::string &event_type)> event_callback_;
|
||||
FixedVector<const char *> types_;
|
||||
|
||||
private:
|
||||
/// Last triggered event type - must point to entry in types_ to ensure valid lifetime.
|
||||
/// Set by trigger() after validation, reset to nullptr when types_ changes.
|
||||
const char *last_event_type_{nullptr};
|
||||
FixedVector<std::string> types_;
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "fan.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -183,9 +181,6 @@ void Fan::publish_state() {
|
||||
ESP_LOGD(TAG, " Preset Mode: %s", preset);
|
||||
}
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_FAN) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_fan_update(this);
|
||||
#endif
|
||||
this->save_state_();
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ void IDFI2CBus::dump_config() {
|
||||
if (s.second) {
|
||||
ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", s.first);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include "light_output.h"
|
||||
@@ -139,12 +137,7 @@ void LightState::loop() {
|
||||
|
||||
float LightState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; }
|
||||
|
||||
void LightState::publish_state() {
|
||||
this->remote_values_callback_.call();
|
||||
#if defined(USE_LIGHT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_light_update(this);
|
||||
#endif
|
||||
}
|
||||
void LightState::publish_state() { this->remote_values_callback_.call(); }
|
||||
|
||||
LightOutput *LightState::get_output() const { return this->output_; }
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "lock.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -55,9 +53,6 @@ void Lock::publish_state(LockState state) {
|
||||
this->rtc_.save(&this->state);
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), lock_state_to_string(state));
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_LOCK) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_lock_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Lock::add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
||||
|
||||
@@ -56,7 +56,7 @@ void MCP23016::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
this->update_reg_(pin, false, iodir);
|
||||
}
|
||||
}
|
||||
float MCP23016::get_setup_priority() const { return setup_priority::IO; }
|
||||
float MCP23016::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
bool MCP23016::read_reg_(uint8_t reg, uint8_t *value) {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "media_player.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -149,12 +148,7 @@ void MediaPlayer::add_on_state_callback(std::function<void()> &&callback) {
|
||||
this->state_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void MediaPlayer::publish_state() {
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_MEDIA_PLAYER) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_media_player_update(this);
|
||||
#endif
|
||||
}
|
||||
void MediaPlayer::publish_state() { this->state_callback_.call(); }
|
||||
|
||||
} // namespace media_player
|
||||
} // namespace esphome
|
||||
|
||||
@@ -38,8 +38,8 @@ void MQTTEventComponent::setup() {
|
||||
void MQTTEventComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MQTT Event '%s': ", this->event_->get_name().c_str());
|
||||
ESP_LOGCONFIG(TAG, "Event Types: ");
|
||||
for (const char *event_type : this->event_->get_event_types()) {
|
||||
ESP_LOGCONFIG(TAG, "- %s", event_type);
|
||||
for (const auto &event_type : this->event_->get_event_types()) {
|
||||
ESP_LOGCONFIG(TAG, "- %s", event_type.c_str());
|
||||
}
|
||||
LOG_MQTT_COMPONENT(true, true);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "number.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -34,9 +32,6 @@ void Number::publish_state(float state) {
|
||||
this->state = state;
|
||||
ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state);
|
||||
this->state_callback_.call(state);
|
||||
#if defined(USE_NUMBER) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_number_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Number::add_on_state_callback(std::function<void(float)> &&callback) {
|
||||
|
||||
@@ -71,7 +71,6 @@ static const uint16_t FALLBACK_FREQUENCY = 64767U; // To use with frequency = 0
|
||||
static const uint32_t MICROSECONDS_IN_SECONDS = 1000000UL;
|
||||
static const uint16_t PRONTO_DEFAULT_GAP = 45000;
|
||||
static const uint16_t MARK_EXCESS_MICROS = 20;
|
||||
static constexpr size_t PRONTO_LOG_CHUNK_SIZE = 230;
|
||||
|
||||
static uint16_t to_frequency_k_hz(uint16_t code) {
|
||||
if (code == 0)
|
||||
@@ -226,17 +225,18 @@ optional<ProntoData> ProntoProtocol::decode(RemoteReceiveData src) {
|
||||
}
|
||||
|
||||
void ProntoProtocol::dump(const ProntoData &data) {
|
||||
std::string rest = data.data;
|
||||
std::string rest;
|
||||
|
||||
rest = data.data;
|
||||
ESP_LOGI(TAG, "Received Pronto: data=");
|
||||
do {
|
||||
size_t chunk_size = rest.size() > PRONTO_LOG_CHUNK_SIZE ? PRONTO_LOG_CHUNK_SIZE : rest.size();
|
||||
ESP_LOGI(TAG, "%.*s", (int) chunk_size, rest.c_str());
|
||||
if (rest.size() > PRONTO_LOG_CHUNK_SIZE) {
|
||||
rest.erase(0, PRONTO_LOG_CHUNK_SIZE);
|
||||
while (true) {
|
||||
ESP_LOGI(TAG, "%s", rest.substr(0, 230).c_str());
|
||||
if (rest.size() > 230) {
|
||||
rest = rest.substr(230);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "select.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cstring>
|
||||
|
||||
@@ -35,9 +33,6 @@ void Select::publish_state(size_t index) {
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s (index %zu)", this->get_name().c_str(), option, index);
|
||||
// Callback signature requires std::string, create temporary for compatibility
|
||||
this->state_callback_.call(std::string(option), index);
|
||||
#if defined(USE_SELECT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_select_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *Select::current_option() const { return this->has_state() ? this->option_at(this->active_index_) : ""; }
|
||||
|
||||
@@ -77,21 +77,23 @@ class Select : public EntityBase {
|
||||
|
||||
void add_on_state_callback(std::function<void(std::string, size_t)> &&callback);
|
||||
|
||||
/** Set the value of the select by index, this is an optional virtual method.
|
||||
*
|
||||
* This method is called by the SelectCall when the index is already known.
|
||||
* Default implementation converts to string and calls control().
|
||||
* Override this to work directly with indices and avoid string conversions.
|
||||
*
|
||||
* @param index The index as validated by the SelectCall.
|
||||
*/
|
||||
virtual void control(size_t index) { this->control(this->option_at(index)); }
|
||||
|
||||
protected:
|
||||
friend class SelectCall;
|
||||
|
||||
size_t active_index_{0};
|
||||
|
||||
/** Set the value of the select by index, this is an optional virtual method.
|
||||
*
|
||||
* IMPORTANT: At least ONE of the two control() methods must be overridden by derived classes.
|
||||
* Overriding this index-based version is PREFERRED as it avoids string conversions.
|
||||
*
|
||||
* This method is called by the SelectCall when the index is already known.
|
||||
* Default implementation converts to string and calls control(const std::string&).
|
||||
*
|
||||
* @param index The index as validated by the SelectCall.
|
||||
*/
|
||||
virtual void control(size_t index) { this->control(this->option_at(index)); }
|
||||
|
||||
/** Set the value of the select, this is a virtual method that each select integration can implement.
|
||||
*
|
||||
* IMPORTANT: At least ONE of the two control() methods must be overridden by derived classes.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "sensor.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -133,9 +131,6 @@ void Sensor::internal_send_state_to_frontend(float state) {
|
||||
ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state,
|
||||
this->get_unit_of_measurement_ref().c_str(), this->get_accuracy_decimals());
|
||||
this->callback_.call(state);
|
||||
#if defined(USE_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_sensor_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace sensor
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "switch.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -64,9 +62,6 @@ void Switch::publish_state(bool state) {
|
||||
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s", this->name_.c_str(), ONOFF(this->state));
|
||||
this->state_callback_.call(this->state);
|
||||
#if defined(USE_SWITCH) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_switch_update(this);
|
||||
#endif
|
||||
}
|
||||
bool Switch::assumed_state() { return false; }
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "text.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -18,9 +16,6 @@ void Text::publish_state(const std::string &state) {
|
||||
ESP_LOGD(TAG, "'%s': Sending state %s", this->get_name().c_str(), state.c_str());
|
||||
}
|
||||
this->state_callback_.call(state);
|
||||
#if defined(USE_TEXT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_text_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Text::add_on_state_callback(std::function<void(std::string)> &&callback) {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "text_sensor.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -86,9 +84,6 @@ void TextSensor::internal_send_state_to_frontend(const std::string &state) {
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str());
|
||||
this->callback_.call(state);
|
||||
#if defined(USE_TEXT_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_text_sensor_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace text_sensor
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "update_entity.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -33,9 +32,6 @@ void UpdateEntity::publish_state() {
|
||||
|
||||
this->set_has_state(true);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_UPDATE) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace update
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "valve.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <strings.h>
|
||||
|
||||
@@ -149,9 +147,6 @@ void Valve::publish_state(bool save) {
|
||||
ESP_LOGD(TAG, " Current Operation: %s", valve_operation_to_str(this->current_operation));
|
||||
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_VALVE) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_valve_update(this);
|
||||
#endif
|
||||
|
||||
if (save) {
|
||||
ValveRestoreState restore{};
|
||||
|
||||
@@ -289,9 +289,6 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# Track controller registration for StaticVector sizing
|
||||
CORE.register_controller()
|
||||
|
||||
version = config[CONF_VERSION]
|
||||
|
||||
cg.add(paren.set_port(config[CONF_PORT]))
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
#include "esphome/components/json/json_util.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -296,7 +294,7 @@ std::string WebServer::get_config_json() {
|
||||
}
|
||||
|
||||
void WebServer::setup() {
|
||||
ControllerRegistry::register_controller(this);
|
||||
this->setup_controller(this->include_internal_);
|
||||
this->base_->init();
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
@@ -432,9 +430,7 @@ static JsonDetail get_request_detail(AsyncWebServerRequest *request) {
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void WebServer::on_sensor_update(sensor::Sensor *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
void WebServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||
this->events_.deferrable_send_state(obj, "state", sensor_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -477,9 +473,7 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
||||
this->events_.deferrable_send_state(obj, "state", text_sensor_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -519,9 +513,7 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std:
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
void WebServer::on_switch_update(switch_::Switch *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
void WebServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||
this->events_.deferrable_send_state(obj, "state", switch_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -633,8 +625,6 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config)
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void WebServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", binary_sensor_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -674,8 +664,6 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool
|
||||
|
||||
#ifdef USE_FAN
|
||||
void WebServer::on_fan_update(fan::Fan *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", fan_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -750,8 +738,6 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
void WebServer::on_light_update(light::LightState *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", light_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -825,8 +811,6 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
|
||||
|
||||
#ifdef USE_COVER
|
||||
void WebServer::on_cover_update(cover::Cover *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", cover_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -911,9 +895,7 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
void WebServer::on_number_update(number::Number *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
void WebServer::on_number_update(number::Number *obj, float state) {
|
||||
this->events_.deferrable_send_state(obj, "state", number_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -979,8 +961,6 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void WebServer::on_date_update(datetime::DateEntity *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", date_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1036,8 +1016,6 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void WebServer::on_time_update(datetime::TimeEntity *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", time_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1092,8 +1070,6 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void WebServer::on_datetime_update(datetime::DateTimeEntity *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", datetime_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1148,9 +1124,7 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s
|
||||
#endif // USE_DATETIME_DATETIME
|
||||
|
||||
#ifdef USE_TEXT
|
||||
void WebServer::on_text_update(text::Text *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
void WebServer::on_text_update(text::Text *obj, const std::string &state) {
|
||||
this->events_.deferrable_send_state(obj, "state", text_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1204,9 +1178,7 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
void WebServer::on_select_update(select::Select *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
void WebServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
|
||||
this->events_.deferrable_send_state(obj, "state", select_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1265,8 +1237,6 @@ std::string WebServer::select_json(select::Select *obj, const char *value, JsonD
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
void WebServer::on_climate_update(climate::Climate *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", climate_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1408,8 +1378,6 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
||||
|
||||
#ifdef USE_LOCK
|
||||
void WebServer::on_lock_update(lock::Lock *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", lock_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1481,8 +1449,6 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet
|
||||
|
||||
#ifdef USE_VALVE
|
||||
void WebServer::on_valve_update(valve::Valve *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", valve_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1564,8 +1530,6 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void WebServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
this->events_.deferrable_send_state(obj, "state", alarm_control_panel_state_json_generator);
|
||||
}
|
||||
void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match) {
|
||||
@@ -1643,9 +1607,7 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void WebServer::on_event(event::Event *obj) {
|
||||
if (!this->include_internal_ && obj->is_internal())
|
||||
return;
|
||||
void WebServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
this->events_.deferrable_send_state(obj, "state", event_state_json_generator);
|
||||
}
|
||||
|
||||
@@ -1666,8 +1628,7 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa
|
||||
}
|
||||
|
||||
static std::string get_event_type(event::Event *event) {
|
||||
const char *last_type = event ? event->get_last_event_type() : nullptr;
|
||||
return last_type ? last_type : "";
|
||||
return (event && event->last_event_type) ? *event->last_event_type : "";
|
||||
}
|
||||
|
||||
std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) {
|
||||
@@ -1688,7 +1649,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
|
||||
}
|
||||
if (start_config == DETAIL_ALL) {
|
||||
JsonArray event_types = root["event_types"].to<JsonArray>();
|
||||
for (const char *event_type : obj->get_event_types()) {
|
||||
for (auto const &event_type : obj->get_event_types()) {
|
||||
event_types.add(event_type);
|
||||
}
|
||||
root["device_class"] = obj->get_device_class_ref();
|
||||
|
||||
@@ -255,7 +255,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void on_sensor_update(sensor::Sensor *obj) override;
|
||||
void on_sensor_update(sensor::Sensor *obj, float state) override;
|
||||
/// Handle a sensor request under '/sensor/<id>'.
|
||||
void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
|
||||
@@ -266,7 +266,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
void on_switch_update(switch_::Switch *obj) override;
|
||||
void on_switch_update(switch_::Switch *obj, bool state) override;
|
||||
|
||||
/// Handle a switch request under '/switch/<id>/</turn_on/turn_off/toggle>'.
|
||||
void handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
@@ -324,7 +324,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj) override;
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
|
||||
|
||||
/// Handle a text sensor request under '/text_sensor/<id>'.
|
||||
void handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
@@ -348,7 +348,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
void on_number_update(number::Number *obj) override;
|
||||
void on_number_update(number::Number *obj, float state) override;
|
||||
/// Handle a number request under '/number/<id>'.
|
||||
void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
|
||||
@@ -392,7 +392,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
void on_text_update(text::Text *obj) override;
|
||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||
/// Handle a text input request under '/text/<id>'.
|
||||
void handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
|
||||
@@ -403,7 +403,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
void on_select_update(select::Select *obj) override;
|
||||
void on_select_update(select::Select *obj, const std::string &state, size_t index) override;
|
||||
/// Handle a select request under '/select/<id>'.
|
||||
void handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match);
|
||||
|
||||
@@ -462,7 +462,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void on_event(event::Event *obj) override;
|
||||
void on_event(event::Event *obj, const std::string &event_type) override;
|
||||
|
||||
static std::string event_state_json_generator(WebServer *web_server, void *source);
|
||||
static std::string event_all_json_generator(WebServer *web_server, void *source);
|
||||
|
||||
@@ -353,9 +353,8 @@ void AsyncWebServerResponse::addHeader(const char *name, const char *value) {
|
||||
void AsyncResponseStream::print(float value) {
|
||||
// Use stack buffer to avoid temporary string allocation
|
||||
// Size: sign (1) + digits (10) + decimal (1) + precision (6) + exponent (5) + null (1) = 24, use 32 for safety
|
||||
constexpr size_t float_buf_size = 32;
|
||||
char buf[float_buf_size];
|
||||
int len = snprintf(buf, float_buf_size, "%f", value);
|
||||
char buf[32];
|
||||
int len = snprintf(buf, sizeof(buf), "%f", value);
|
||||
this->content_.append(buf, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +48,6 @@ if TYPE_CHECKING:
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Key for tracking controller count in CORE.data for ControllerRegistry StaticVector sizing
|
||||
KEY_CONTROLLER_REGISTRY_COUNT = "controller_registry_count"
|
||||
|
||||
|
||||
class EsphomeError(Exception):
|
||||
"""General ESPHome exception occurred."""
|
||||
@@ -710,15 +707,6 @@ class EsphomeCore:
|
||||
def relative_piolibdeps_path(self, *path: str | Path) -> Path:
|
||||
return self.relative_build_path(".piolibdeps", *path)
|
||||
|
||||
@property
|
||||
def platformio_cache_dir(self) -> str:
|
||||
"""Get the PlatformIO cache directory path."""
|
||||
# Check if running in Docker/HA addon with custom cache dir
|
||||
if (cache_dir := os.environ.get("PLATFORMIO_CACHE_DIR")) and cache_dir.strip():
|
||||
return cache_dir
|
||||
# Default PlatformIO cache location
|
||||
return os.path.expanduser("~/.platformio/.cache")
|
||||
|
||||
@property
|
||||
def firmware_bin(self) -> Path:
|
||||
if self.is_libretiny:
|
||||
@@ -922,11 +910,6 @@ class EsphomeCore:
|
||||
"""
|
||||
self.platform_counts[platform_name] += 1
|
||||
|
||||
def register_controller(self) -> None:
|
||||
"""Track registration of a Controller for ControllerRegistry StaticVector sizing."""
|
||||
controller_count = self.data.setdefault(KEY_CONTROLLER_REGISTRY_COUNT, 0)
|
||||
self.data[KEY_CONTROLLER_REGISTRY_COUNT] = controller_count + 1
|
||||
|
||||
@property
|
||||
def cpp_main_section(self):
|
||||
from esphome.cpp_generator import statement
|
||||
|
||||
@@ -40,12 +40,7 @@ from esphome.const import (
|
||||
PlatformFramework,
|
||||
__version__ as ESPHOME_VERSION,
|
||||
)
|
||||
from esphome.core import (
|
||||
CORE,
|
||||
KEY_CONTROLLER_REGISTRY_COUNT,
|
||||
CoroPriority,
|
||||
coroutine_with_priority,
|
||||
)
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
from esphome.helpers import (
|
||||
copy_file_if_changed,
|
||||
fnv1a_32bit_hash,
|
||||
@@ -467,15 +462,6 @@ async def _add_platform_defines() -> None:
|
||||
cg.add_define(f"USE_{platform_name.upper()}")
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.FINAL)
|
||||
async def _add_controller_registry_define() -> None:
|
||||
# Generate StaticVector size for ControllerRegistry
|
||||
controller_count = CORE.data.get(KEY_CONTROLLER_REGISTRY_COUNT, 0)
|
||||
if controller_count > 0:
|
||||
cg.add_define("USE_CONTROLLER_REGISTRY")
|
||||
cg.add_define("CONTROLLER_REGISTRY_MAX", controller_count)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.CORE)
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
cg.add_global(cg.global_ns.namespace("esphome").using)
|
||||
@@ -497,7 +483,6 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add_define("ESPHOME_COMPONENT_COUNT", len(CORE.component_ids))
|
||||
|
||||
CORE.add_job(_add_platform_defines)
|
||||
CORE.add_job(_add_controller_registry_define)
|
||||
|
||||
CORE.add_job(_add_automations, config)
|
||||
|
||||
|
||||
134
esphome/core/controller.cpp
Normal file
134
esphome/core/controller.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "controller.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
void Controller::setup_controller(bool include_internal) {
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
for (auto *obj : App.get_binary_sensors()) {
|
||||
if (include_internal || !obj->is_internal()) {
|
||||
obj->add_full_state_callback(
|
||||
[this, obj](optional<bool> previous, optional<bool> state) { this->on_binary_sensor_update(obj); });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
for (auto *obj : App.get_fans()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_fan_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
for (auto *obj : App.get_lights()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_new_remote_values_callback([this, obj]() { this->on_light_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
for (auto *obj : App.get_sensors()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](float state) { this->on_sensor_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
for (auto *obj : App.get_switches()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](bool state) { this->on_switch_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
for (auto *obj : App.get_covers()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_cover_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
for (auto *obj : App.get_text_sensors()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](const std::string &state) { this->on_text_sensor_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
for (auto *obj : App.get_climates()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](climate::Climate & /*unused*/) { this->on_climate_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
for (auto *obj : App.get_numbers()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](float state) { this->on_number_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
for (auto *obj : App.get_dates()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_date_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
for (auto *obj : App.get_times()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_time_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
for (auto *obj : App.get_datetimes()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_datetime_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
for (auto *obj : App.get_texts()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj](const std::string &state) { this->on_text_update(obj, state); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
for (auto *obj : App.get_selects()) {
|
||||
if (include_internal || !obj->is_internal()) {
|
||||
obj->add_on_state_callback(
|
||||
[this, obj](const std::string &state, size_t index) { this->on_select_update(obj, state, index); });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
for (auto *obj : App.get_locks()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_lock_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_VALVE
|
||||
for (auto *obj : App.get_valves()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_valve_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
for (auto *obj : App.get_media_players()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_media_player_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
for (auto *obj : App.get_alarm_control_panels()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_alarm_control_panel_update(obj); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
for (auto *obj : App.get_events()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_event_callback([this, obj](const std::string &event_type) { this->on_event(obj, event_type); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
for (auto *obj : App.get_updates()) {
|
||||
if (include_internal || !obj->is_internal())
|
||||
obj->add_on_state_callback([this, obj]() { this->on_update(obj); });
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
@@ -69,6 +69,7 @@ namespace esphome {
|
||||
|
||||
class Controller {
|
||||
public:
|
||||
void setup_controller(bool include_internal = false);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
virtual void on_binary_sensor_update(binary_sensor::BinarySensor *obj){};
|
||||
#endif
|
||||
@@ -79,22 +80,22 @@ class Controller {
|
||||
virtual void on_light_update(light::LightState *obj){};
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
virtual void on_sensor_update(sensor::Sensor *obj){};
|
||||
virtual void on_sensor_update(sensor::Sensor *obj, float state){};
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
virtual void on_switch_update(switch_::Switch *obj){};
|
||||
virtual void on_switch_update(switch_::Switch *obj, bool state){};
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
virtual void on_cover_update(cover::Cover *obj){};
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
virtual void on_text_sensor_update(text_sensor::TextSensor *obj){};
|
||||
virtual void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state){};
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual void on_climate_update(climate::Climate *obj){};
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
virtual void on_number_update(number::Number *obj){};
|
||||
virtual void on_number_update(number::Number *obj, float state){};
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
virtual void on_date_update(datetime::DateEntity *obj){};
|
||||
@@ -106,10 +107,10 @@ class Controller {
|
||||
virtual void on_datetime_update(datetime::DateTimeEntity *obj){};
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
virtual void on_text_update(text::Text *obj){};
|
||||
virtual void on_text_update(text::Text *obj, const std::string &state){};
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
virtual void on_select_update(select::Select *obj){};
|
||||
virtual void on_select_update(select::Select *obj, const std::string &state, size_t index){};
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
virtual void on_lock_update(lock::Lock *obj){};
|
||||
@@ -124,7 +125,7 @@ class Controller {
|
||||
virtual void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj){};
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
virtual void on_event(event::Event *obj){};
|
||||
virtual void on_event(event::Event *obj, const std::string &event_type){};
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
virtual void on_update(update::UpdateEntity *obj){};
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#ifdef USE_CONTROLLER_REGISTRY
|
||||
|
||||
#include "esphome/core/controller.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
StaticVector<Controller *, CONTROLLER_REGISTRY_MAX> ControllerRegistry::controllers;
|
||||
|
||||
void ControllerRegistry::register_controller(Controller *controller) { controllers.push_back(controller); }
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void ControllerRegistry::notify_binary_sensor_update(binary_sensor::BinarySensor *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_binary_sensor_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_FAN
|
||||
void ControllerRegistry::notify_fan_update(fan::Fan *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_fan_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
void ControllerRegistry::notify_light_update(light::LightState *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_light_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void ControllerRegistry::notify_sensor_update(sensor::Sensor *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_sensor_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
void ControllerRegistry::notify_switch_update(switch_::Switch *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_switch_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_COVER
|
||||
void ControllerRegistry::notify_cover_update(cover::Cover *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_cover_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void ControllerRegistry::notify_text_sensor_update(text_sensor::TextSensor *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_text_sensor_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
void ControllerRegistry::notify_climate_update(climate::Climate *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_climate_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
void ControllerRegistry::notify_number_update(number::Number *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_number_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void ControllerRegistry::notify_date_update(datetime::DateEntity *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_date_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
void ControllerRegistry::notify_time_update(datetime::TimeEntity *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_time_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
void ControllerRegistry::notify_datetime_update(datetime::DateTimeEntity *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_datetime_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
void ControllerRegistry::notify_text_update(text::Text *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_text_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
void ControllerRegistry::notify_select_update(select::Select *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_select_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_LOCK
|
||||
void ControllerRegistry::notify_lock_update(lock::Lock *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_lock_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_VALVE
|
||||
void ControllerRegistry::notify_valve_update(valve::Valve *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_valve_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
void ControllerRegistry::notify_media_player_update(media_player::MediaPlayer *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_media_player_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
void ControllerRegistry::notify_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_alarm_control_panel_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void ControllerRegistry::notify_event(event::Event *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_event(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
void ControllerRegistry::notify_update(update::UpdateEntity *obj) {
|
||||
for (auto *controller : controllers) {
|
||||
controller->on_update(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_CONTROLLER_REGISTRY
|
||||
@@ -1,245 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_CONTROLLER_REGISTRY
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
// Forward declarations
|
||||
namespace esphome {
|
||||
|
||||
class Controller;
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
namespace binary_sensor {
|
||||
class BinarySensor;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_FAN
|
||||
namespace fan {
|
||||
class Fan;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
namespace light {
|
||||
class LightState;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
namespace sensor {
|
||||
class Sensor;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
namespace switch_ {
|
||||
class Switch;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_COVER
|
||||
namespace cover {
|
||||
class Cover;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
namespace text_sensor {
|
||||
class TextSensor;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
namespace climate {
|
||||
class Climate;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
namespace number {
|
||||
class Number;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
namespace datetime {
|
||||
class DateEntity;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
namespace datetime {
|
||||
class TimeEntity;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
namespace datetime {
|
||||
class DateTimeEntity;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
namespace text {
|
||||
class Text;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
namespace select {
|
||||
class Select;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_LOCK
|
||||
namespace lock {
|
||||
class Lock;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_VALVE
|
||||
namespace valve {
|
||||
class Valve;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
namespace media_player {
|
||||
class MediaPlayer;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
namespace alarm_control_panel {
|
||||
class AlarmControlPanel;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
namespace event {
|
||||
class Event;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
namespace update {
|
||||
class UpdateEntity;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Global registry for Controllers to receive entity state updates.
|
||||
*
|
||||
* This singleton registry allows Controllers (APIServer, WebServer) to receive
|
||||
* entity state change notifications without storing per-entity callbacks.
|
||||
*
|
||||
* Instead of each entity maintaining controller callbacks (32 bytes overhead per entity),
|
||||
* entities call ControllerRegistry::notify_*_update() which iterates the small list
|
||||
* of registered controllers (typically 2: API and WebServer).
|
||||
*
|
||||
* Controllers read state directly from entities using existing accessors (obj->state, etc.)
|
||||
* rather than receiving it as callback parameters that were being ignored anyway.
|
||||
*
|
||||
* Memory savings: 32 bytes per entity (2 controllers × 16 bytes std::function overhead)
|
||||
* Typical config (25 entities): ~780 bytes saved
|
||||
* Large config (80 entities): ~2,540 bytes saved
|
||||
*/
|
||||
class ControllerRegistry {
|
||||
public:
|
||||
/** Register a controller to receive entity state updates.
|
||||
*
|
||||
* Controllers should call this in their setup() method.
|
||||
* Typically only APIServer and WebServer register.
|
||||
*/
|
||||
static void register_controller(Controller *controller);
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
static void notify_binary_sensor_update(binary_sensor::BinarySensor *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_FAN
|
||||
static void notify_fan_update(fan::Fan *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
static void notify_light_update(light::LightState *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
static void notify_sensor_update(sensor::Sensor *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
static void notify_switch_update(switch_::Switch *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_COVER
|
||||
static void notify_cover_update(cover::Cover *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
static void notify_text_sensor_update(text_sensor::TextSensor *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
static void notify_climate_update(climate::Climate *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
static void notify_number_update(number::Number *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
static void notify_date_update(datetime::DateEntity *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_TIME
|
||||
static void notify_time_update(datetime::TimeEntity *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
static void notify_datetime_update(datetime::DateTimeEntity *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
static void notify_text_update(text::Text *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
static void notify_select_update(select::Select *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_LOCK
|
||||
static void notify_lock_update(lock::Lock *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_VALVE
|
||||
static void notify_valve_update(valve::Valve *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
static void notify_media_player_update(media_player::MediaPlayer *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
static void notify_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
static void notify_event(event::Event *obj);
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
static void notify_update(update::UpdateEntity *obj);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static StaticVector<Controller *, CONTROLLER_REGISTRY_MAX> controllers;
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_CONTROLLER_REGISTRY
|
||||
@@ -28,7 +28,6 @@
|
||||
#define USE_BUTTON
|
||||
#define USE_CAMERA
|
||||
#define USE_CLIMATE
|
||||
#define USE_CONTROLLER_REGISTRY
|
||||
#define USE_COVER
|
||||
#define USE_DATETIME
|
||||
#define USE_DATETIME_DATE
|
||||
@@ -297,7 +296,6 @@
|
||||
#define USE_DASHBOARD_IMPORT
|
||||
|
||||
// Default counts for static analysis
|
||||
#define CONTROLLER_REGISTRY_MAX 2
|
||||
#define ESPHOME_COMPONENT_COUNT 50
|
||||
#define ESPHOME_DEVICE_COUNT 10
|
||||
#define ESPHOME_AREA_COUNT 10
|
||||
|
||||
@@ -414,8 +414,10 @@ int8_t step_to_accuracy_decimals(float step) {
|
||||
return str.length() - dot_pos - 1;
|
||||
}
|
||||
|
||||
// Store BASE64 characters as array - automatically placed in flash/ROM on embedded platforms
|
||||
static const char BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
// Use C-style string constant to store in ROM instead of RAM (saves 24 bytes)
|
||||
static constexpr const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
// Helper function to find the index of a base64 character in the lookup table.
|
||||
// Returns the character's position (0-63) if found, or 0 if not found.
|
||||
@@ -425,8 +427,8 @@ static const char BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr
|
||||
// stops processing at the first invalid character due to the is_base64() check in its
|
||||
// while loop condition, making this edge case harmless in practice.
|
||||
static inline uint8_t base64_find_char(char c) {
|
||||
const void *ptr = memchr(BASE64_CHARS, c, sizeof(BASE64_CHARS));
|
||||
return ptr ? (static_cast<const char *>(ptr) - BASE64_CHARS) : 0;
|
||||
const char *pos = strchr(BASE64_CHARS, c);
|
||||
return pos ? (pos - BASE64_CHARS) : 0;
|
||||
}
|
||||
|
||||
static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); }
|
||||
|
||||
@@ -145,9 +145,6 @@ template<typename T, size_t N> class StaticVector {
|
||||
size_t size() const { return count_; }
|
||||
bool empty() const { return count_ == 0; }
|
||||
|
||||
// Direct access to size counter for efficient in-place construction
|
||||
size_t &count() { return count_; }
|
||||
|
||||
T &operator[](size_t i) { return data_[i]; }
|
||||
const T &operator[](size_t i) const { return data_[i]; }
|
||||
|
||||
|
||||
@@ -94,9 +94,10 @@ class Scheduler {
|
||||
} name_;
|
||||
uint32_t interval;
|
||||
// Split time to handle millis() rollover. The scheduler combines the 32-bit millis()
|
||||
// with a 16-bit rollover counter to create a 48-bit time space (stored as 64-bit
|
||||
// for compatibility). With 49.7 days per 32-bit rollover, the 16-bit counter
|
||||
// supports 49.7 days × 65536 = ~8900 years. This ensures correct scheduling
|
||||
// with a 16-bit rollover counter to create a 48-bit time space (using 32+16 bits).
|
||||
// This is intentionally limited to 48 bits, not stored as a full 64-bit value.
|
||||
// With 49.7 days per 32-bit rollover, the 16-bit counter supports
|
||||
// 49.7 days × 65536 = ~8900 years. This ensures correct scheduling
|
||||
// even when devices run for months. Split into two fields for better memory
|
||||
// alignment on 32-bit systems.
|
||||
uint32_t next_execution_low_; // Lower 32 bits of execution time (millis value)
|
||||
|
||||
@@ -145,16 +145,7 @@ def run_compile(config, verbose):
|
||||
args = []
|
||||
if CONF_COMPILE_PROCESS_LIMIT in config[CONF_ESPHOME]:
|
||||
args += [f"-j{config[CONF_ESPHOME][CONF_COMPILE_PROCESS_LIMIT]}"]
|
||||
result = run_platformio_cli_run(config, verbose, *args)
|
||||
|
||||
# Run memory analysis if enabled
|
||||
if config.get(CONF_ESPHOME, {}).get("analyze_memory", False):
|
||||
try:
|
||||
analyze_memory_usage(config)
|
||||
except Exception as e:
|
||||
_LOGGER.warning("Failed to analyze memory usage: %s", e)
|
||||
|
||||
return result
|
||||
return run_platformio_cli_run(config, verbose, *args)
|
||||
|
||||
|
||||
def _run_idedata(config):
|
||||
@@ -403,74 +394,3 @@ class IDEData:
|
||||
if path.endswith(".exe")
|
||||
else f"{path[:-3]}readelf"
|
||||
)
|
||||
|
||||
|
||||
def analyze_memory_usage(config: dict[str, Any]) -> None:
|
||||
"""Analyze memory usage by component after compilation."""
|
||||
# Lazy import to avoid overhead when not needed
|
||||
from esphome.analyze_memory.cli import MemoryAnalyzerCLI
|
||||
from esphome.analyze_memory.helpers import get_esphome_components
|
||||
|
||||
idedata = get_idedata(config)
|
||||
|
||||
# Get paths to tools
|
||||
elf_path = idedata.firmware_elf_path
|
||||
objdump_path = idedata.objdump_path
|
||||
readelf_path = idedata.readelf_path
|
||||
|
||||
# Debug logging
|
||||
_LOGGER.debug("ELF path from idedata: %s", elf_path)
|
||||
|
||||
# Check if file exists
|
||||
if not Path(elf_path).exists():
|
||||
# Try alternate path
|
||||
alt_path = Path(CORE.relative_build_path(".pioenvs", CORE.name, "firmware.elf"))
|
||||
if alt_path.exists():
|
||||
elf_path = str(alt_path)
|
||||
_LOGGER.debug("Using alternate ELF path: %s", elf_path)
|
||||
else:
|
||||
_LOGGER.warning("ELF file not found at %s or %s", elf_path, alt_path)
|
||||
return
|
||||
|
||||
# Extract external components from config
|
||||
external_components = set()
|
||||
|
||||
# Get the list of built-in ESPHome components
|
||||
builtin_components = get_esphome_components()
|
||||
|
||||
# Special non-component keys that appear in configs
|
||||
NON_COMPONENT_KEYS = {
|
||||
CONF_ESPHOME,
|
||||
"substitutions",
|
||||
"packages",
|
||||
"globals",
|
||||
"<<",
|
||||
}
|
||||
|
||||
# Check all top-level keys in config
|
||||
for key in config:
|
||||
if key not in builtin_components and key not in NON_COMPONENT_KEYS:
|
||||
# This is an external component
|
||||
external_components.add(key)
|
||||
|
||||
_LOGGER.debug("Detected external components: %s", external_components)
|
||||
|
||||
# Create analyzer and run analysis
|
||||
analyzer = MemoryAnalyzerCLI(
|
||||
elf_path, objdump_path, readelf_path, external_components
|
||||
)
|
||||
analyzer.analyze()
|
||||
|
||||
# Generate and print report
|
||||
report = analyzer.generate_report()
|
||||
_LOGGER.info("\n%s", report)
|
||||
|
||||
# Optionally save to file
|
||||
if config.get(CONF_ESPHOME, {}).get("memory_report_file"):
|
||||
report_file = Path(config[CONF_ESPHOME]["memory_report_file"])
|
||||
if report_file.suffix == ".json":
|
||||
report_file.write_text(analyzer.to_json())
|
||||
_LOGGER.info("Memory report saved to %s", report_file)
|
||||
else:
|
||||
report_file.write_text(report)
|
||||
_LOGGER.info("Memory report saved to %s", report_file)
|
||||
|
||||
@@ -66,6 +66,5 @@ def test_text_config_lamda_is_set(generate_main):
|
||||
main_cpp = generate_main("tests/component_tests/text/test_text.yaml")
|
||||
|
||||
# Then
|
||||
# Stateless lambda optimization: empty capture list allows function pointer conversion
|
||||
assert "it_4->set_template([]() -> esphome::optional<std::string> {" in main_cpp
|
||||
assert 'return std::string{"Hello"};' in main_cpp
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
esphome:
|
||||
name: test-user-services-union
|
||||
friendly_name: Test User Services Union Storage
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
wifi:
|
||||
ssid: "test"
|
||||
password: "password"
|
||||
|
||||
api:
|
||||
actions:
|
||||
# Test service with no arguments
|
||||
- action: test_no_args
|
||||
then:
|
||||
- logger.log: "No args service called"
|
||||
|
||||
# Test service with one argument
|
||||
- action: test_one_arg
|
||||
variables:
|
||||
value: int
|
||||
then:
|
||||
- logger.log:
|
||||
format: "One arg service: %d"
|
||||
args: [value]
|
||||
|
||||
# Test service with multiple arguments of different types
|
||||
- action: test_multi_args
|
||||
variables:
|
||||
int_val: int
|
||||
float_val: float
|
||||
str_val: string
|
||||
bool_val: bool
|
||||
then:
|
||||
- logger.log:
|
||||
format: "Multi args: %d, %.2f, %s, %d"
|
||||
args: [int_val, float_val, str_val.c_str(), bool_val]
|
||||
|
||||
# Test service with max typical arguments
|
||||
- action: test_many_args
|
||||
variables:
|
||||
arg1: int
|
||||
arg2: int
|
||||
arg3: int
|
||||
arg4: string
|
||||
arg5: float
|
||||
then:
|
||||
- logger.log: "Many args service called"
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
name: "Test Binary Sensor"
|
||||
id: test_sensor
|
||||
@@ -670,45 +670,3 @@ class TestEsphomeCore:
|
||||
os.environ.pop("ESPHOME_IS_HA_ADDON", None)
|
||||
os.environ.pop("ESPHOME_DATA_DIR", None)
|
||||
assert target.data_dir == Path(expected_default)
|
||||
|
||||
def test_platformio_cache_dir_with_env_var(self):
|
||||
"""Test platformio_cache_dir when PLATFORMIO_CACHE_DIR env var is set."""
|
||||
target = core.EsphomeCore()
|
||||
test_cache_dir = "/custom/cache/dir"
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": test_cache_dir}):
|
||||
assert target.platformio_cache_dir == test_cache_dir
|
||||
|
||||
def test_platformio_cache_dir_without_env_var(self):
|
||||
"""Test platformio_cache_dir defaults to ~/.platformio/.cache."""
|
||||
target = core.EsphomeCore()
|
||||
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
# Ensure env var is not set
|
||||
os.environ.pop("PLATFORMIO_CACHE_DIR", None)
|
||||
expected = os.path.expanduser("~/.platformio/.cache")
|
||||
assert target.platformio_cache_dir == expected
|
||||
|
||||
def test_platformio_cache_dir_empty_env_var(self):
|
||||
"""Test platformio_cache_dir with empty env var falls back to default."""
|
||||
target = core.EsphomeCore()
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": ""}):
|
||||
expected = os.path.expanduser("~/.platformio/.cache")
|
||||
assert target.platformio_cache_dir == expected
|
||||
|
||||
def test_platformio_cache_dir_whitespace_env_var(self):
|
||||
"""Test platformio_cache_dir with whitespace-only env var falls back to default."""
|
||||
target = core.EsphomeCore()
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": " "}):
|
||||
expected = os.path.expanduser("~/.platformio/.cache")
|
||||
assert target.platformio_cache_dir == expected
|
||||
|
||||
def test_platformio_cache_dir_docker_addon_path(self):
|
||||
"""Test platformio_cache_dir in Docker/HA addon environment."""
|
||||
target = core.EsphomeCore()
|
||||
addon_cache = "/data/cache/platformio"
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": addon_cache}):
|
||||
assert target.platformio_cache_dir == addon_cache
|
||||
|
||||
@@ -355,7 +355,6 @@ def test_clean_build(
|
||||
mock_core.relative_pioenvs_path.return_value = pioenvs_dir
|
||||
mock_core.relative_piolibdeps_path.return_value = piolibdeps_dir
|
||||
mock_core.relative_build_path.return_value = dependencies_lock
|
||||
mock_core.platformio_cache_dir = str(platformio_cache_dir)
|
||||
|
||||
# Verify all exist before
|
||||
assert pioenvs_dir.exists()
|
||||
|
||||
Reference in New Issue
Block a user