From 128bd76f204bfa906a4b4d5c02b4d19b4af77e6f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jun 2025 22:45:00 -0500 Subject: [PATCH 1/2] reduce --- .../components/api/entity_iterator_macros.h | 27 +++ esphome/components/api/list_entities.cpp | 175 ++++++------------ esphome/components/api/subscribe_state.cpp | 55 +++--- 3 files changed, 105 insertions(+), 152 deletions(-) create mode 100644 esphome/components/api/entity_iterator_macros.h diff --git a/esphome/components/api/entity_iterator_macros.h b/esphome/components/api/entity_iterator_macros.h new file mode 100644 index 0000000000..a3dac32e09 --- /dev/null +++ b/esphome/components/api/entity_iterator_macros.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esphome/core/defines.h" +#ifdef USE_API + +// Macro-based approach to eliminate duplication without runtime overhead +// This generates the entity handler methods at compile time + +// For ListEntitiesIterator - calls schedule_message_ with try_send_*_info +#define LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \ + bool ListEntitiesIterator::on_##entity_type(EntityClass *entity) { \ + return this->client_->schedule_message_(entity, &APIConnection::try_send_##entity_type##_info, \ + ResponseType::MESSAGE_TYPE); \ + } + +// For InitialStateIterator - calls send_*_state +#define INITIAL_STATE_HANDLER(entity_type, EntityClass) \ + bool InitialStateIterator::on_##entity_type(EntityClass *entity) { \ + return this->client_->send_##entity_type##_state(entity); \ + } + +// Combined macro that generates both handlers +#define ENTITY_HANDLERS(entity_type, EntityClass, ResponseType) \ + LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \ + INITIAL_STATE_HANDLER(entity_type, EntityClass) + +#endif // USE_API \ No newline at end of file diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index 1087270a9d..a9ce3524a4 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -2,6 +2,7 @@ #ifdef USE_API #include "api_connection.h" #include "api_pb2.h" +#include "entity_iterator_macros.h" #include "esphome/core/application.h" #include "esphome/core/log.h" #include "esphome/core/util.h" @@ -9,155 +10,85 @@ namespace esphome { namespace api { +// Generate entity handler implementations using macros #ifdef USE_BINARY_SENSOR -bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { - return this->client_->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info, - ListEntitiesBinarySensorResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(binary_sensor, binary_sensor::BinarySensor, ListEntitiesBinarySensorResponse) #endif #ifdef USE_COVER -bool ListEntitiesIterator::on_cover(cover::Cover *cover) { - return this->client_->schedule_message_(cover, &APIConnection::try_send_cover_info, - ListEntitiesCoverResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(cover, cover::Cover, ListEntitiesCoverResponse) #endif #ifdef USE_FAN -bool ListEntitiesIterator::on_fan(fan::Fan *fan) { - return this->client_->schedule_message_(fan, &APIConnection::try_send_fan_info, - ListEntitiesFanResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(fan, fan::Fan, ListEntitiesFanResponse) #endif #ifdef USE_LIGHT -bool ListEntitiesIterator::on_light(light::LightState *light) { - return this->client_->schedule_message_(light, &APIConnection::try_send_light_info, - ListEntitiesLightResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(light, light::LightState, ListEntitiesLightResponse) #endif #ifdef USE_SENSOR -bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { - return this->client_->schedule_message_(sensor, &APIConnection::try_send_sensor_info, - ListEntitiesSensorResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(sensor, sensor::Sensor, ListEntitiesSensorResponse) #endif #ifdef USE_SWITCH -bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { - return this->client_->schedule_message_(a_switch, &APIConnection::try_send_switch_info, - ListEntitiesSwitchResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(switch, switch_::Switch, ListEntitiesSwitchResponse) #endif #ifdef USE_BUTTON -bool ListEntitiesIterator::on_button(button::Button *button) { - return this->client_->schedule_message_(button, &APIConnection::try_send_button_info, - ListEntitiesButtonResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(button, button::Button, ListEntitiesButtonResponse) #endif #ifdef USE_TEXT_SENSOR -bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { - return this->client_->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info, - ListEntitiesTextSensorResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(text_sensor, text_sensor::TextSensor, ListEntitiesTextSensorResponse) #endif #ifdef USE_LOCK -bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { - return this->client_->schedule_message_(a_lock, &APIConnection::try_send_lock_info, - ListEntitiesLockResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(lock, lock::Lock, ListEntitiesLockResponse) #endif #ifdef USE_VALVE -bool ListEntitiesIterator::on_valve(valve::Valve *valve) { - return this->client_->schedule_message_(valve, &APIConnection::try_send_valve_info, - ListEntitiesValveResponse::MESSAGE_TYPE); -} +LIST_ENTITIES_HANDLER(valve, valve::Valve, ListEntitiesValveResponse) +#endif +#ifdef USE_ESP32_CAMERA +LIST_ENTITIES_HANDLER(camera, esp32_camera::ESP32Camera, ListEntitiesCameraResponse) +#endif +#ifdef USE_CLIMATE +LIST_ENTITIES_HANDLER(climate, climate::Climate, ListEntitiesClimateResponse) +#endif +#ifdef USE_NUMBER +LIST_ENTITIES_HANDLER(number, number::Number, ListEntitiesNumberResponse) +#endif +#ifdef USE_DATETIME_DATE +LIST_ENTITIES_HANDLER(date, datetime::DateEntity, ListEntitiesDateResponse) +#endif +#ifdef USE_DATETIME_TIME +LIST_ENTITIES_HANDLER(time, datetime::TimeEntity, ListEntitiesTimeResponse) +#endif +#ifdef USE_DATETIME_DATETIME +LIST_ENTITIES_HANDLER(datetime, datetime::DateTimeEntity, ListEntitiesDateTimeResponse) +#endif +#ifdef USE_TEXT +LIST_ENTITIES_HANDLER(text, text::Text, ListEntitiesTextResponse) +#endif +#ifdef USE_SELECT +LIST_ENTITIES_HANDLER(select, select::Select, ListEntitiesSelectResponse) +#endif +#ifdef USE_MEDIA_PLAYER +LIST_ENTITIES_HANDLER(media_player, media_player::MediaPlayer, ListEntitiesMediaPlayerResponse) +#endif +#ifdef USE_ALARM_CONTROL_PANEL +LIST_ENTITIES_HANDLER(alarm_control_panel, alarm_control_panel::AlarmControlPanel, + ListEntitiesAlarmControlPanelResponse) +#endif +#ifdef USE_EVENT +LIST_ENTITIES_HANDLER(event, event::Event, ListEntitiesEventResponse) +#endif +#ifdef USE_UPDATE +LIST_ENTITIES_HANDLER(update, update::UpdateEntity, ListEntitiesUpdateResponse) #endif +// Special cases that don't follow the pattern bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); } + ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} + bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { auto resp = service->encode_list_service_response(); return this->client_->send_message(resp); } -#ifdef USE_ESP32_CAMERA -bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) { - return this->client_->schedule_message_(camera, &APIConnection::try_send_camera_info, - ListEntitiesCameraResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_CLIMATE -bool ListEntitiesIterator::on_climate(climate::Climate *climate) { - return this->client_->schedule_message_(climate, &APIConnection::try_send_climate_info, - ListEntitiesClimateResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_NUMBER -bool ListEntitiesIterator::on_number(number::Number *number) { - return this->client_->schedule_message_(number, &APIConnection::try_send_number_info, - ListEntitiesNumberResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_DATETIME_DATE -bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { - return this->client_->schedule_message_(date, &APIConnection::try_send_date_info, - ListEntitiesDateResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_DATETIME_TIME -bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { - return this->client_->schedule_message_(time, &APIConnection::try_send_time_info, - ListEntitiesTimeResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_DATETIME_DATETIME -bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) { - return this->client_->schedule_message_(datetime, &APIConnection::try_send_datetime_info, - ListEntitiesDateTimeResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_TEXT -bool ListEntitiesIterator::on_text(text::Text *text) { - return this->client_->schedule_message_(text, &APIConnection::try_send_text_info, - ListEntitiesTextResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_SELECT -bool ListEntitiesIterator::on_select(select::Select *select) { - return this->client_->schedule_message_(select, &APIConnection::try_send_select_info, - ListEntitiesSelectResponse::MESSAGE_TYPE); -} -#endif - -#ifdef USE_MEDIA_PLAYER -bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) { - return this->client_->schedule_message_(media_player, &APIConnection::try_send_media_player_info, - ListEntitiesMediaPlayerResponse::MESSAGE_TYPE); -} -#endif -#ifdef USE_ALARM_CONTROL_PANEL -bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { - return this->client_->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info, - ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE); -} -#endif -#ifdef USE_EVENT -bool ListEntitiesIterator::on_event(event::Event *event) { - return this->client_->schedule_message_(event, &APIConnection::try_send_event_info, - ListEntitiesEventResponse::MESSAGE_TYPE); -} -#endif -#ifdef USE_UPDATE -bool ListEntitiesIterator::on_update(update::UpdateEntity *update) { - return this->client_->schedule_message_(update, &APIConnection::try_send_update_info, - ListEntitiesUpdateResponse::MESSAGE_TYPE); -} -#endif - } // namespace api } // namespace esphome -#endif +#endif \ No newline at end of file diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 4180435fcc..3dbe0eb811 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -1,80 +1,75 @@ #include "subscribe_state.h" #ifdef USE_API #include "api_connection.h" +#include "entity_iterator_macros.h" #include "esphome/core/log.h" namespace esphome { namespace api { +// Generate entity handler implementations using macros #ifdef USE_BINARY_SENSOR -bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { - return this->client_->send_binary_sensor_state(binary_sensor); -} +INITIAL_STATE_HANDLER(binary_sensor, binary_sensor::BinarySensor) #endif #ifdef USE_COVER -bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); } +INITIAL_STATE_HANDLER(cover, cover::Cover) #endif #ifdef USE_FAN -bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_state(fan); } +INITIAL_STATE_HANDLER(fan, fan::Fan) #endif #ifdef USE_LIGHT -bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); } +INITIAL_STATE_HANDLER(light, light::LightState) #endif #ifdef USE_SENSOR -bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); } +INITIAL_STATE_HANDLER(sensor, sensor::Sensor) #endif #ifdef USE_SWITCH -bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); } +INITIAL_STATE_HANDLER(switch, switch_::Switch) #endif #ifdef USE_TEXT_SENSOR -bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { - return this->client_->send_text_sensor_state(text_sensor); -} +INITIAL_STATE_HANDLER(text_sensor, text_sensor::TextSensor) #endif #ifdef USE_CLIMATE -bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } +INITIAL_STATE_HANDLER(climate, climate::Climate) #endif #ifdef USE_NUMBER -bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); } +INITIAL_STATE_HANDLER(number, number::Number) #endif #ifdef USE_DATETIME_DATE -bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); } +INITIAL_STATE_HANDLER(date, datetime::DateEntity) #endif #ifdef USE_DATETIME_TIME -bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); } +INITIAL_STATE_HANDLER(time, datetime::TimeEntity) #endif #ifdef USE_DATETIME_DATETIME -bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) { - return this->client_->send_datetime_state(datetime); -} +INITIAL_STATE_HANDLER(datetime, datetime::DateTimeEntity) #endif #ifdef USE_TEXT -bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); } +INITIAL_STATE_HANDLER(text, text::Text) #endif #ifdef USE_SELECT -bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); } +INITIAL_STATE_HANDLER(select, select::Select) #endif #ifdef USE_LOCK -bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); } +INITIAL_STATE_HANDLER(lock, lock::Lock) #endif #ifdef USE_VALVE -bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); } +INITIAL_STATE_HANDLER(valve, valve::Valve) #endif #ifdef USE_MEDIA_PLAYER -bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) { - return this->client_->send_media_player_state(media_player); -} +INITIAL_STATE_HANDLER(media_player, media_player::MediaPlayer) #endif #ifdef USE_ALARM_CONTROL_PANEL -bool InitialStateIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { - return this->client_->send_alarm_control_panel_state(a_alarm_control_panel); -} +INITIAL_STATE_HANDLER(alarm_control_panel, alarm_control_panel::AlarmControlPanel) #endif #ifdef USE_UPDATE -bool InitialStateIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_state(update); } +INITIAL_STATE_HANDLER(update, update::UpdateEntity) #endif + +// Special cases (button and event) are already defined inline in subscribe_state.h + InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {} } // namespace api } // namespace esphome -#endif +#endif \ No newline at end of file From a3eeb46961a642d42f40950d95a75d4041f92a22 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 28 Jun 2025 23:01:48 -0500 Subject: [PATCH 2/2] reduce --- esphome/components/api/entity_iterator_macros.h | 6 ++++++ esphome/components/api/list_entities.cpp | 2 +- esphome/components/api/subscribe_state.cpp | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/entity_iterator_macros.h b/esphome/components/api/entity_iterator_macros.h index a3dac32e09..5bb5069a99 100644 --- a/esphome/components/api/entity_iterator_macros.h +++ b/esphome/components/api/entity_iterator_macros.h @@ -3,6 +3,9 @@ #include "esphome/core/defines.h" #ifdef USE_API +namespace esphome { +namespace api { + // Macro-based approach to eliminate duplication without runtime overhead // This generates the entity handler methods at compile time @@ -24,4 +27,7 @@ LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \ INITIAL_STATE_HANDLER(entity_type, EntityClass) +} // namespace api +} // namespace esphome + #endif // USE_API \ No newline at end of file diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index a9ce3524a4..d88d552691 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -91,4 +91,4 @@ bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { } // namespace api } // namespace esphome -#endif \ No newline at end of file +#endif diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 3dbe0eb811..4516b551a1 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -72,4 +72,4 @@ InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(clie } // namespace api } // namespace esphome -#endif \ No newline at end of file +#endif