From 295fe8da040bd822475e5b047f38154391a2954f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Nov 2025 15:32:46 -0600 Subject: [PATCH] controller registry phase1/2 --- esphome/components/api/api_server.cpp | 26 +- esphome/components/api/api_server.h | 12 +- esphome/components/web_server/web_server.cpp | 49 +++- esphome/components/web_server/web_server.h | 12 +- esphome/core/controller.h | 12 +- esphome/core/controller_registry.cpp | 177 +++++++++++++ esphome/core/controller_registry.h | 263 +++++++++++++++++++ 7 files changed, 509 insertions(+), 42 deletions(-) create mode 100644 esphome/core/controller_registry.cpp create mode 100644 esphome/core/controller_registry.h diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index e5f0d9795e..a08554acd8 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -4,6 +4,7 @@ #include "api_connection.h" #include "esphome/components/network/util.h" #include "esphome/core/application.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" @@ -34,7 +35,7 @@ APIServer::APIServer() { } void APIServer::setup() { - this->setup_controller(); + ControllerRegistry::register_controller(this); #ifdef USE_API_NOISE uint32_t hash = 88491486UL; @@ -269,7 +270,7 @@ bool APIServer::check_password(const uint8_t *password_data, size_t password_len void APIServer::handle_disconnect(APIConnection *conn) {} -// Macro for entities without extra parameters +// Macro for controller update dispatch #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()) \ @@ -278,15 +279,6 @@ 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 @@ -304,15 +296,15 @@ API_DISPATCH_UPDATE(light::LightState, light) #endif #ifdef USE_SENSOR -API_DISPATCH_UPDATE_IGNORE_PARAMS(sensor::Sensor, sensor, float state) +API_DISPATCH_UPDATE(sensor::Sensor, sensor) #endif #ifdef USE_SWITCH -API_DISPATCH_UPDATE_IGNORE_PARAMS(switch_::Switch, switch, bool state) +API_DISPATCH_UPDATE(switch_::Switch, switch) #endif #ifdef USE_TEXT_SENSOR -API_DISPATCH_UPDATE_IGNORE_PARAMS(text_sensor::TextSensor, text_sensor, const std::string &state) +API_DISPATCH_UPDATE(text_sensor::TextSensor, text_sensor) #endif #ifdef USE_CLIMATE @@ -320,7 +312,7 @@ API_DISPATCH_UPDATE(climate::Climate, climate) #endif #ifdef USE_NUMBER -API_DISPATCH_UPDATE_IGNORE_PARAMS(number::Number, number, float state) +API_DISPATCH_UPDATE(number::Number, number) #endif #ifdef USE_DATETIME_DATE @@ -336,11 +328,11 @@ API_DISPATCH_UPDATE(datetime::DateTimeEntity, datetime) #endif #ifdef USE_TEXT -API_DISPATCH_UPDATE_IGNORE_PARAMS(text::Text, text, const std::string &state) +API_DISPATCH_UPDATE(text::Text, text) #endif #ifdef USE_SELECT -API_DISPATCH_UPDATE_IGNORE_PARAMS(select::Select, select, const std::string &state, size_t index) +API_DISPATCH_UPDATE(select::Select, select) #endif #ifdef USE_LOCK diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index f1f44a266d..4b03023957 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -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, float state) override; + void on_sensor_update(sensor::Sensor *obj) override; #endif #ifdef USE_SWITCH - void on_switch_update(switch_::Switch *obj, bool state) override; + void on_switch_update(switch_::Switch *obj) override; #endif #ifdef USE_TEXT_SENSOR - void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override; + void on_text_sensor_update(text_sensor::TextSensor *obj) override; #endif #ifdef USE_CLIMATE void on_climate_update(climate::Climate *obj) override; #endif #ifdef USE_NUMBER - void on_number_update(number::Number *obj, float state) override; + void on_number_update(number::Number *obj) 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, const std::string &state) override; + void on_text_update(text::Text *obj) override; #endif #ifdef USE_SELECT - void on_select_update(select::Select *obj, const std::string &state, size_t index) override; + void on_select_update(select::Select *obj) override; #endif #ifdef USE_LOCK void on_lock_update(lock::Lock *obj) override; diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index f1d1a75875..5c2596d731 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -3,6 +3,7 @@ #include "esphome/components/json/json_util.h" #include "esphome/components/network/util.h" #include "esphome/core/application.h" +#include "esphome/core/controller_registry.h" #include "esphome/core/entity_base.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" @@ -294,7 +295,7 @@ std::string WebServer::get_config_json() { } void WebServer::setup() { - this->setup_controller(this->include_internal_); + ControllerRegistry::register_controller(this); this->base_->init(); #ifdef USE_LOGGER @@ -430,7 +431,9 @@ static JsonDetail get_request_detail(AsyncWebServerRequest *request) { } #ifdef USE_SENSOR -void WebServer::on_sensor_update(sensor::Sensor *obj, float state) { +void WebServer::on_sensor_update(sensor::Sensor *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", sensor_state_json_generator); } void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -473,7 +476,9 @@ 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, const std::string &state) { +void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", text_sensor_state_json_generator); } void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -513,7 +518,9 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std: #endif #ifdef USE_SWITCH -void WebServer::on_switch_update(switch_::Switch *obj, bool state) { +void WebServer::on_switch_update(switch_::Switch *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", switch_state_json_generator); } void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -625,6 +632,8 @@ 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) { @@ -664,6 +673,8 @@ 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) { @@ -738,6 +749,8 @@ 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) { @@ -811,6 +824,8 @@ 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) { @@ -895,7 +910,9 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { #endif #ifdef USE_NUMBER -void WebServer::on_number_update(number::Number *obj, float state) { +void WebServer::on_number_update(number::Number *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", number_state_json_generator); } void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -961,6 +978,8 @@ 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) { @@ -1016,6 +1035,8 @@ 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) { @@ -1070,6 +1091,8 @@ 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) { @@ -1124,7 +1147,9 @@ 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, const std::string &state) { +void WebServer::on_text_update(text::Text *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", text_state_json_generator); } void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1178,7 +1203,9 @@ 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, const std::string &state, size_t index) { +void WebServer::on_select_update(select::Select *obj) { + if (!this->include_internal_ && obj->is_internal()) + return; this->events_.deferrable_send_state(obj, "state", select_state_json_generator); } void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match) { @@ -1237,6 +1264,8 @@ 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) { @@ -1378,6 +1407,8 @@ 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) { @@ -1449,6 +1480,8 @@ 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) { @@ -1530,6 +1563,8 @@ 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) { diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 328140cfae..8e74c42bff 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -255,7 +255,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #endif #ifdef USE_SENSOR - void on_sensor_update(sensor::Sensor *obj, float state) override; + void on_sensor_update(sensor::Sensor *obj) override; /// Handle a sensor request under '/sensor/'. 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, bool state) override; + void on_switch_update(switch_::Switch *obj) override; /// Handle a switch request under '/switch//'. 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, const std::string &state) override; + void on_text_sensor_update(text_sensor::TextSensor *obj) override; /// Handle a text sensor request under '/text_sensor/'. 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, float state) override; + void on_number_update(number::Number *obj) override; /// Handle a number request under '/number/'. 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, const std::string &state) override; + void on_text_update(text::Text *obj) override; /// Handle a text input request under '/text/'. 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, const std::string &state, size_t index) override; + void on_select_update(select::Select *obj) override; /// Handle a select request under '/select/'. void handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match); diff --git a/esphome/core/controller.h b/esphome/core/controller.h index b475e326ee..a62e53d9fc 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -80,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, float state){}; + virtual void on_sensor_update(sensor::Sensor *obj){}; #endif #ifdef USE_SWITCH - virtual void on_switch_update(switch_::Switch *obj, bool state){}; + virtual void on_switch_update(switch_::Switch *obj){}; #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, const std::string &state){}; + virtual void on_text_sensor_update(text_sensor::TextSensor *obj){}; #endif #ifdef USE_CLIMATE virtual void on_climate_update(climate::Climate *obj){}; #endif #ifdef USE_NUMBER - virtual void on_number_update(number::Number *obj, float state){}; + virtual void on_number_update(number::Number *obj){}; #endif #ifdef USE_DATETIME_DATE virtual void on_date_update(datetime::DateEntity *obj){}; @@ -107,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, const std::string &state){}; + virtual void on_text_update(text::Text *obj){}; #endif #ifdef USE_SELECT - virtual void on_select_update(select::Select *obj, const std::string &state, size_t index){}; + virtual void on_select_update(select::Select *obj){}; #endif #ifdef USE_LOCK virtual void on_lock_update(lock::Lock *obj){}; diff --git a/esphome/core/controller_registry.cpp b/esphome/core/controller_registry.cpp new file mode 100644 index 0000000000..3223d92170 --- /dev/null +++ b/esphome/core/controller_registry.cpp @@ -0,0 +1,177 @@ +#include "esphome/core/controller_registry.h" +#include "esphome/core/controller.h" + +namespace esphome { + +std::vector ControllerRegistry::controllers_; + +void ControllerRegistry::register_controller(Controller *controller) { controllers_.push_back(controller); } + +void ControllerRegistry::unregister_controller(Controller *controller) { + auto it = std::find(controllers_.begin(), controllers_.end(), controller); + if (it != controllers_.end()) { + controllers_.erase(it); + } +} + +#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, const std::string &event_type) { + for (auto *controller : controllers_) { + controller->on_event(obj, event_type); + } +} +#endif + +#ifdef USE_UPDATE +void ControllerRegistry::notify_update(update::UpdateEntity *obj) { + for (auto *controller : controllers_) { + controller->on_update(obj); + } +} +#endif + +} // namespace esphome diff --git a/esphome/core/controller_registry.h b/esphome/core/controller_registry.h new file mode 100644 index 0000000000..33cd2ccebc --- /dev/null +++ b/esphome/core/controller_registry.h @@ -0,0 +1,263 @@ +#pragma once + +#include "esphome/core/defines.h" +#include + +// 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 a list of controller callbacks (32 bytes overhead), + * entities call ControllerRegistry::notify_*_update() which iterates the small list + * of registered controllers (typically 2: API and WebServer). + * + * Memory savings: 32 bytes per entity (2 controllers × 16 bytes std::function overhead) + * For 80 entities: 2,560 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); + + /** Unregister a controller (rarely used). + * + * Controllers are typically never unregistered in ESPHome's lifecycle, + * but this is provided for completeness and testing. + */ + static void unregister_controller(Controller *controller); + +#ifdef USE_BINARY_SENSOR + /** Notify all controllers of a binary sensor state update. */ + static void notify_binary_sensor_update(binary_sensor::BinarySensor *obj); +#endif + +#ifdef USE_FAN + /** Notify all controllers of a fan state update. */ + static void notify_fan_update(fan::Fan *obj); +#endif + +#ifdef USE_LIGHT + /** Notify all controllers of a light state update. */ + static void notify_light_update(light::LightState *obj); +#endif + +#ifdef USE_SENSOR + /** Notify all controllers of a sensor state update. */ + static void notify_sensor_update(sensor::Sensor *obj); +#endif + +#ifdef USE_SWITCH + /** Notify all controllers of a switch state update. */ + static void notify_switch_update(switch_::Switch *obj); +#endif + +#ifdef USE_COVER + /** Notify all controllers of a cover state update. */ + static void notify_cover_update(cover::Cover *obj); +#endif + +#ifdef USE_TEXT_SENSOR + /** Notify all controllers of a text sensor state update. */ + static void notify_text_sensor_update(text_sensor::TextSensor *obj); +#endif + +#ifdef USE_CLIMATE + /** Notify all controllers of a climate state update. */ + static void notify_climate_update(climate::Climate *obj); +#endif + +#ifdef USE_NUMBER + /** Notify all controllers of a number state update. */ + static void notify_number_update(number::Number *obj); +#endif + +#ifdef USE_DATETIME_DATE + /** Notify all controllers of a date entity state update. */ + static void notify_date_update(datetime::DateEntity *obj); +#endif + +#ifdef USE_DATETIME_TIME + /** Notify all controllers of a time entity state update. */ + static void notify_time_update(datetime::TimeEntity *obj); +#endif + +#ifdef USE_DATETIME_DATETIME + /** Notify all controllers of a datetime entity state update. */ + static void notify_datetime_update(datetime::DateTimeEntity *obj); +#endif + +#ifdef USE_TEXT + /** Notify all controllers of a text entity state update. */ + static void notify_text_update(text::Text *obj); +#endif + +#ifdef USE_SELECT + /** Notify all controllers of a select entity state update. */ + static void notify_select_update(select::Select *obj); +#endif + +#ifdef USE_LOCK + /** Notify all controllers of a lock state update. */ + static void notify_lock_update(lock::Lock *obj); +#endif + +#ifdef USE_VALVE + /** Notify all controllers of a valve state update. */ + static void notify_valve_update(valve::Valve *obj); +#endif + +#ifdef USE_MEDIA_PLAYER + /** Notify all controllers of a media player state update. */ + static void notify_media_player_update(media_player::MediaPlayer *obj); +#endif + +#ifdef USE_ALARM_CONTROL_PANEL + /** Notify all controllers of an alarm control panel state update. */ + static void notify_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj); +#endif + +#ifdef USE_EVENT + /** Notify all controllers of an event trigger. */ + static void notify_event(event::Event *obj, const std::string &event_type); +#endif + +#ifdef USE_UPDATE + /** Notify all controllers of an update entity state update. */ + static void notify_update(update::UpdateEntity *obj); +#endif + + protected: + static std::vector controllers_; +}; + +} // namespace esphome