1
0
mirror of https://github.com/esphome/esphome.git synced 2025-06-15 14:56:59 +02:00

Avoid protobuf message construction when tx buffer is full (#8787)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
J. Nick Koston 2025-05-14 21:25:44 -05:00 committed by Jesse Hills
parent aaaf9b2b62
commit cab6edd800
No known key found for this signature in database
GPG Key ID: BEAAE804EFD8E83A
2 changed files with 581 additions and 714 deletions

View File

@ -30,7 +30,7 @@ static const char *const TAG = "api.connection";
static const int ESP32_CAMERA_STOP_STREAM = 5000;
// helper for allowing only unique entries in the queue
void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t *send_message) {
void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t send_message) {
DeferredMessage item(source, send_message);
auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(),
@ -46,7 +46,7 @@ void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_
void DeferredMessageQueue::process_queue() {
while (!deferred_queue_.empty()) {
DeferredMessage &de = deferred_queue_.front();
if (de.send_message_(this->api_connection_, de.source_)) {
if ((this->api_connection_->*(de.send_message_))(de.source_)) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin());
} else {
@ -55,7 +55,7 @@ void DeferredMessageQueue::process_queue() {
}
}
void DeferredMessageQueue::defer(void *source, send_message_t *send_message) {
void DeferredMessageQueue::defer(void *source, send_message_t send_message) {
this->dmq_push_back_with_dedup_(source, send_message);
}
@ -154,7 +154,9 @@ void APIConnection::loop() {
return;
}
this->deferred_message_queue_.process_queue();
if (!this->deferred_message_queue_.empty() && this->helper_->can_write_without_blocking()) {
this->deferred_message_queue_.process_queue();
}
if (!this->list_entities_iterator_.completed())
this->list_entities_iterator_.advance();
@ -268,96 +270,65 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
#ifdef USE_BINARY_SENSOR
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_binary_sensor_state(this, binary_sensor, state)) {
this->deferred_message_queue_.defer(binary_sensor, try_send_binary_sensor_state);
}
return true;
return this->send_state_with_value_(binary_sensor, &APIConnection::try_send_binary_sensor_state_,
&APIConnection::try_send_binary_sensor_state_, state);
}
void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
if (!APIConnection::try_send_binary_sensor_info(this, binary_sensor)) {
this->deferred_message_queue_.defer(binary_sensor, try_send_binary_sensor_info);
}
this->send_info_(static_cast<EntityBase *>(binary_sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_binary_sensor_info_));
}
bool APIConnection::try_send_binary_sensor_state(APIConnection *api, void *v_binary_sensor) {
binary_sensor::BinarySensor *binary_sensor = reinterpret_cast<binary_sensor::BinarySensor *>(v_binary_sensor);
return APIConnection::try_send_binary_sensor_state(api, binary_sensor, binary_sensor->state);
bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor) {
return this->try_send_binary_sensor_state_(binary_sensor, binary_sensor->state);
}
bool APIConnection::try_send_binary_sensor_state(APIConnection *api, binary_sensor::BinarySensor *binary_sensor,
bool state) {
BinarySensorStateResponse resp;
resp.key = binary_sensor->get_object_id_hash();
resp.state = state;
resp.missing_state = !binary_sensor->has_state();
return api->send_binary_sensor_state_response(resp);
}
bool APIConnection::try_send_binary_sensor_info(APIConnection *api, void *v_binary_sensor) {
binary_sensor::BinarySensor *binary_sensor = reinterpret_cast<binary_sensor::BinarySensor *>(v_binary_sensor);
ListEntitiesBinarySensorResponse msg;
msg.object_id = binary_sensor->get_object_id();
bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state) {
BinarySensorStateResponse msg;
msg.state = state;
msg.missing_state = !binary_sensor->has_state();
msg.key = binary_sensor->get_object_id_hash();
if (binary_sensor->has_own_name())
msg.name = binary_sensor->get_name();
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
return this->send_binary_sensor_state_response(msg);
}
bool APIConnection::try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor) {
ListEntitiesBinarySensorResponse msg;
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
msg.icon = binary_sensor->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(binary_sensor->get_entity_category());
return api->send_list_entities_binary_sensor_response(msg);
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(binary_sensor), msg,
&APIConnection::send_list_entities_binary_sensor_response);
}
#endif
#ifdef USE_COVER
bool APIConnection::send_cover_state(cover::Cover *cover) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_cover_state(this, cover)) {
this->deferred_message_queue_.defer(cover, try_send_cover_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(cover),
reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_state_));
}
void APIConnection::send_cover_info(cover::Cover *cover) {
if (!APIConnection::try_send_cover_info(this, cover)) {
this->deferred_message_queue_.defer(cover, try_send_cover_info);
}
this->send_info_(static_cast<EntityBase *>(cover),
reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_info_));
}
bool APIConnection::try_send_cover_state(APIConnection *api, void *v_cover) {
cover::Cover *cover = reinterpret_cast<cover::Cover *>(v_cover);
bool APIConnection::try_send_cover_state_(cover::Cover *cover) {
CoverStateResponse msg;
auto traits = cover->get_traits();
CoverStateResponse resp{};
resp.key = cover->get_object_id_hash();
resp.legacy_state =
msg.legacy_state =
(cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
resp.position = cover->position;
msg.position = cover->position;
if (traits.get_supports_tilt())
resp.tilt = cover->tilt;
resp.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
return api->send_cover_state_response(resp);
}
bool APIConnection::try_send_cover_info(APIConnection *api, void *v_cover) {
cover::Cover *cover = reinterpret_cast<cover::Cover *>(v_cover);
auto traits = cover->get_traits();
ListEntitiesCoverResponse msg;
msg.tilt = cover->tilt;
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
msg.key = cover->get_object_id_hash();
msg.object_id = cover->get_object_id();
if (cover->has_own_name())
msg.name = cover->get_name();
msg.unique_id = get_default_unique_id("cover", cover);
return this->send_cover_state_response(msg);
}
bool APIConnection::try_send_cover_info_(cover::Cover *cover) {
ListEntitiesCoverResponse msg;
auto traits = cover->get_traits();
msg.assumed_state = traits.get_is_assumed_state();
msg.supports_position = traits.get_supports_position();
msg.supports_tilt = traits.get_supports_tilt();
msg.supports_stop = traits.get_supports_stop();
msg.device_class = cover->get_device_class();
msg.disabled_by_default = cover->is_disabled_by_default();
msg.icon = cover->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(cover->get_entity_category());
return api->send_list_entities_cover_response(msg);
msg.unique_id = get_default_unique_id("cover", cover);
return this->try_send_entity_info_(static_cast<EntityBase *>(cover), msg,
&APIConnection::send_list_entities_cover_response);
}
void APIConnection::cover_command(const CoverCommandRequest &msg) {
cover::Cover *cover = App.get_cover_by_key(msg.key);
@ -390,56 +361,41 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
#ifdef USE_FAN
bool APIConnection::send_fan_state(fan::Fan *fan) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_fan_state(this, fan)) {
this->deferred_message_queue_.defer(fan, try_send_fan_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(fan),
reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_state_));
}
void APIConnection::send_fan_info(fan::Fan *fan) {
if (!APIConnection::try_send_fan_info(this, fan)) {
this->deferred_message_queue_.defer(fan, try_send_fan_info);
}
this->send_info_(static_cast<EntityBase *>(fan),
reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_info_));
}
bool APIConnection::try_send_fan_state(APIConnection *api, void *v_fan) {
fan::Fan *fan = reinterpret_cast<fan::Fan *>(v_fan);
bool APIConnection::try_send_fan_state_(fan::Fan *fan) {
FanStateResponse msg;
auto traits = fan->get_traits();
FanStateResponse resp{};
resp.key = fan->get_object_id_hash();
resp.state = fan->state;
msg.state = fan->state;
if (traits.supports_oscillation())
resp.oscillating = fan->oscillating;
msg.oscillating = fan->oscillating;
if (traits.supports_speed()) {
resp.speed_level = fan->speed;
msg.speed_level = fan->speed;
}
if (traits.supports_direction())
resp.direction = static_cast<enums::FanDirection>(fan->direction);
msg.direction = static_cast<enums::FanDirection>(fan->direction);
if (traits.supports_preset_modes())
resp.preset_mode = fan->preset_mode;
return api->send_fan_state_response(resp);
}
bool APIConnection::try_send_fan_info(APIConnection *api, void *v_fan) {
fan::Fan *fan = reinterpret_cast<fan::Fan *>(v_fan);
auto traits = fan->get_traits();
ListEntitiesFanResponse msg;
msg.preset_mode = fan->preset_mode;
msg.key = fan->get_object_id_hash();
msg.object_id = fan->get_object_id();
if (fan->has_own_name())
msg.name = fan->get_name();
msg.unique_id = get_default_unique_id("fan", fan);
return this->send_fan_state_response(msg);
}
bool APIConnection::try_send_fan_info_(fan::Fan *fan) {
ListEntitiesFanResponse msg;
auto traits = fan->get_traits();
msg.supports_oscillation = traits.supports_oscillation();
msg.supports_speed = traits.supports_speed();
msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count();
for (auto const &preset : traits.supported_preset_modes())
msg.supported_preset_modes.push_back(preset);
msg.disabled_by_default = fan->is_disabled_by_default();
msg.icon = fan->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(fan->get_entity_category());
return api->send_list_entities_fan_response(msg);
msg.unique_id = get_default_unique_id("fan", fan);
return this->try_send_entity_info_(static_cast<EntityBase *>(fan), msg,
&APIConnection::send_list_entities_fan_response);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
fan::Fan *fan = App.get_fan_by_key(msg.key);
@ -465,28 +421,18 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
#ifdef USE_LIGHT
bool APIConnection::send_light_state(light::LightState *light) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_light_state(this, light)) {
this->deferred_message_queue_.defer(light, try_send_light_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(light),
reinterpret_cast<send_message_t>(&APIConnection::try_send_light_state_));
}
void APIConnection::send_light_info(light::LightState *light) {
if (!APIConnection::try_send_light_info(this, light)) {
this->deferred_message_queue_.defer(light, try_send_light_info);
}
this->send_info_(static_cast<EntityBase *>(light),
reinterpret_cast<send_message_t>(&APIConnection::try_send_light_info_));
}
bool APIConnection::try_send_light_state(APIConnection *api, void *v_light) {
light::LightState *light = reinterpret_cast<light::LightState *>(v_light);
bool APIConnection::try_send_light_state_(light::LightState *light) {
LightStateResponse resp;
auto traits = light->get_traits();
auto values = light->remote_values;
auto color_mode = values.get_color_mode();
LightStateResponse resp{};
resp.key = light->get_object_id_hash();
resp.state = values.is_on();
resp.color_mode = static_cast<enums::ColorMode>(color_mode);
resp.brightness = values.get_brightness();
@ -500,25 +446,14 @@ bool APIConnection::try_send_light_state(APIConnection *api, void *v_light) {
resp.warm_white = values.get_warm_white();
if (light->supports_effects())
resp.effect = light->get_effect_name();
return api->send_light_state_response(resp);
resp.key = light->get_object_id_hash();
return this->send_light_state_response(resp);
}
bool APIConnection::try_send_light_info(APIConnection *api, void *v_light) {
light::LightState *light = reinterpret_cast<light::LightState *>(v_light);
auto traits = light->get_traits();
bool APIConnection::try_send_light_info_(light::LightState *light) {
ListEntitiesLightResponse msg;
msg.key = light->get_object_id_hash();
msg.object_id = light->get_object_id();
if (light->has_own_name())
msg.name = light->get_name();
msg.unique_id = get_default_unique_id("light", light);
msg.disabled_by_default = light->is_disabled_by_default();
msg.icon = light->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(light->get_entity_category());
auto traits = light->get_traits();
for (auto mode : traits.get_supported_color_modes())
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
msg.legacy_supports_white_value =
@ -526,17 +461,19 @@ bool APIConnection::try_send_light_info(APIConnection *api, void *v_light) {
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
if (msg.legacy_supports_color_temperature) {
msg.min_mireds = traits.get_min_mireds();
msg.max_mireds = traits.get_max_mireds();
}
if (light->supports_effects()) {
msg.effects.emplace_back("None");
for (auto *effect : light->get_effects())
for (auto *effect : light->get_effects()) {
msg.effects.push_back(effect->get_name());
}
}
return api->send_list_entities_light_response(msg);
msg.unique_id = get_default_unique_id("light", light);
return this->try_send_entity_info_(static_cast<EntityBase *>(light), msg,
&APIConnection::send_list_entities_light_response);
}
void APIConnection::light_command(const LightCommandRequest &msg) {
light::LightState *light = App.get_light_by_key(msg.key);
@ -577,93 +514,65 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
#ifdef USE_SENSOR
bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_sensor_state(this, sensor, state)) {
this->deferred_message_queue_.defer(sensor, try_send_sensor_state);
}
return true;
return this->send_state_with_value_(sensor, &APIConnection::try_send_sensor_state_,
&APIConnection::try_send_sensor_state_, state);
}
void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
if (!APIConnection::try_send_sensor_info(this, sensor)) {
this->deferred_message_queue_.defer(sensor, try_send_sensor_info);
}
this->send_info_(static_cast<EntityBase *>(sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_sensor_info_));
}
bool APIConnection::try_send_sensor_state(APIConnection *api, void *v_sensor) {
sensor::Sensor *sensor = reinterpret_cast<sensor::Sensor *>(v_sensor);
return APIConnection::try_send_sensor_state(api, sensor, sensor->state);
bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor) {
return this->try_send_sensor_state_(sensor, sensor->state);
}
bool APIConnection::try_send_sensor_state(APIConnection *api, sensor::Sensor *sensor, float state) {
SensorStateResponse resp{};
resp.key = sensor->get_object_id_hash();
bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor, float state) {
SensorStateResponse resp;
resp.state = state;
resp.missing_state = !sensor->has_state();
return api->send_sensor_state_response(resp);
resp.key = sensor->get_object_id_hash();
return this->send_sensor_state_response(resp);
}
bool APIConnection::try_send_sensor_info(APIConnection *api, void *v_sensor) {
sensor::Sensor *sensor = reinterpret_cast<sensor::Sensor *>(v_sensor);
bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) {
ListEntitiesSensorResponse msg;
msg.key = sensor->get_object_id_hash();
msg.object_id = sensor->get_object_id();
if (sensor->has_own_name())
msg.name = sensor->get_name();
msg.unique_id = sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("sensor", sensor);
msg.icon = sensor->get_icon();
msg.unit_of_measurement = sensor->get_unit_of_measurement();
msg.accuracy_decimals = sensor->get_accuracy_decimals();
msg.force_update = sensor->get_force_update();
msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
msg.disabled_by_default = sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(sensor->get_entity_category());
return api->send_list_entities_sensor_response(msg);
msg.unique_id = sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("sensor", sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(sensor), msg,
&APIConnection::send_list_entities_sensor_response);
}
#endif
#ifdef USE_SWITCH
bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_switch_state(this, a_switch, state)) {
this->deferred_message_queue_.defer(a_switch, try_send_switch_state);
}
return true;
return this->send_state_with_value_(a_switch, &APIConnection::try_send_switch_state_,
&APIConnection::try_send_switch_state_, state);
}
void APIConnection::send_switch_info(switch_::Switch *a_switch) {
if (!APIConnection::try_send_switch_info(this, a_switch)) {
this->deferred_message_queue_.defer(a_switch, try_send_switch_info);
}
this->send_info_(static_cast<EntityBase *>(a_switch),
reinterpret_cast<send_message_t>(&APIConnection::try_send_switch_info_));
}
bool APIConnection::try_send_switch_state(APIConnection *api, void *v_a_switch) {
switch_::Switch *a_switch = reinterpret_cast<switch_::Switch *>(v_a_switch);
return APIConnection::try_send_switch_state(api, a_switch, a_switch->state);
bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch) {
return this->try_send_switch_state_(a_switch, a_switch->state);
}
bool APIConnection::try_send_switch_state(APIConnection *api, switch_::Switch *a_switch, bool state) {
SwitchStateResponse resp{};
resp.key = a_switch->get_object_id_hash();
bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch, bool state) {
SwitchStateResponse resp;
resp.state = state;
return api->send_switch_state_response(resp);
resp.key = a_switch->get_object_id_hash();
return this->send_switch_state_response(resp);
}
bool APIConnection::try_send_switch_info(APIConnection *api, void *v_a_switch) {
switch_::Switch *a_switch = reinterpret_cast<switch_::Switch *>(v_a_switch);
bool APIConnection::try_send_switch_info_(switch_::Switch *a_switch) {
ListEntitiesSwitchResponse msg;
msg.key = a_switch->get_object_id_hash();
msg.object_id = a_switch->get_object_id();
if (a_switch->has_own_name())
msg.name = a_switch->get_name();
msg.unique_id = get_default_unique_id("switch", a_switch);
msg.icon = a_switch->get_icon();
msg.assumed_state = a_switch->assumed_state();
msg.disabled_by_default = a_switch->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
msg.device_class = a_switch->get_device_class();
return api->send_list_entities_switch_response(msg);
msg.unique_id = get_default_unique_id("switch", a_switch);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_switch), msg,
&APIConnection::send_list_entities_switch_response);
}
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
switch_::Switch *a_switch = App.get_switch_by_key(msg.key);
@ -680,70 +589,48 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
#ifdef USE_TEXT_SENSOR
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_text_sensor_state(this, text_sensor, std::move(state))) {
this->deferred_message_queue_.defer(text_sensor, try_send_text_sensor_state);
}
return true;
return this->send_state_with_value_(text_sensor, &APIConnection::try_send_text_sensor_state_,
&APIConnection::try_send_text_sensor_state_, std::move(state));
}
void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
if (!APIConnection::try_send_text_sensor_info(this, text_sensor)) {
this->deferred_message_queue_.defer(text_sensor, try_send_text_sensor_info);
}
this->send_info_(static_cast<EntityBase *>(text_sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_text_sensor_info_));
}
bool APIConnection::try_send_text_sensor_state(APIConnection *api, void *v_text_sensor) {
text_sensor::TextSensor *text_sensor = reinterpret_cast<text_sensor::TextSensor *>(v_text_sensor);
return APIConnection::try_send_text_sensor_state(api, text_sensor, text_sensor->state);
bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor) {
return this->try_send_text_sensor_state_(text_sensor, text_sensor->state);
}
bool APIConnection::try_send_text_sensor_state(APIConnection *api, text_sensor::TextSensor *text_sensor,
std::string state) {
TextSensorStateResponse resp{};
resp.key = text_sensor->get_object_id_hash();
bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state) {
TextSensorStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !text_sensor->has_state();
return api->send_text_sensor_state_response(resp);
resp.key = text_sensor->get_object_id_hash();
return this->send_text_sensor_state_response(resp);
}
bool APIConnection::try_send_text_sensor_info(APIConnection *api, void *v_text_sensor) {
text_sensor::TextSensor *text_sensor = reinterpret_cast<text_sensor::TextSensor *>(v_text_sensor);
bool APIConnection::try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor) {
ListEntitiesTextSensorResponse msg;
msg.key = text_sensor->get_object_id_hash();
msg.object_id = text_sensor->get_object_id();
msg.name = text_sensor->get_name();
msg.device_class = text_sensor->get_device_class();
msg.unique_id = text_sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
msg.icon = text_sensor->get_icon();
msg.disabled_by_default = text_sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
msg.device_class = text_sensor->get_device_class();
return api->send_list_entities_text_sensor_response(msg);
return this->try_send_entity_info_(static_cast<EntityBase *>(text_sensor), msg,
&APIConnection::send_list_entities_text_sensor_response);
}
#endif
#ifdef USE_CLIMATE
bool APIConnection::send_climate_state(climate::Climate *climate) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_climate_state(this, climate)) {
this->deferred_message_queue_.defer(climate, try_send_climate_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(climate),
reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_state_));
}
void APIConnection::send_climate_info(climate::Climate *climate) {
if (!APIConnection::try_send_climate_info(this, climate)) {
this->deferred_message_queue_.defer(climate, try_send_climate_info);
}
this->send_info_(static_cast<EntityBase *>(climate),
reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_info_));
}
bool APIConnection::try_send_climate_state(APIConnection *api, void *v_climate) {
climate::Climate *climate = reinterpret_cast<climate::Climate *>(v_climate);
auto traits = climate->get_traits();
ClimateStateResponse resp{};
bool APIConnection::try_send_climate_state_(climate::Climate *climate) {
ClimateStateResponse resp;
resp.key = climate->get_object_id_hash();
auto traits = climate->get_traits();
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
resp.action = static_cast<enums::ClimateAction>(climate->action);
if (traits.get_supports_current_temperature())
@ -769,40 +656,25 @@ bool APIConnection::try_send_climate_state(APIConnection *api, void *v_climate)
resp.current_humidity = climate->current_humidity;
if (traits.get_supports_target_humidity())
resp.target_humidity = climate->target_humidity;
return api->send_climate_state_response(resp);
return this->send_climate_state_response(resp);
}
bool APIConnection::try_send_climate_info(APIConnection *api, void *v_climate) {
climate::Climate *climate = reinterpret_cast<climate::Climate *>(v_climate);
auto traits = climate->get_traits();
bool APIConnection::try_send_climate_info_(climate::Climate *climate) {
ListEntitiesClimateResponse msg;
msg.key = climate->get_object_id_hash();
msg.object_id = climate->get_object_id();
if (climate->has_own_name())
msg.name = climate->get_name();
msg.unique_id = get_default_unique_id("climate", climate);
msg.disabled_by_default = climate->is_disabled_by_default();
msg.icon = climate->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(climate->get_entity_category());
auto traits = climate->get_traits();
msg.supports_current_temperature = traits.get_supports_current_temperature();
msg.supports_current_humidity = traits.get_supports_current_humidity();
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
msg.supports_target_humidity = traits.get_supports_target_humidity();
for (auto mode : traits.get_supported_modes())
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
msg.visual_min_temperature = traits.get_visual_min_temperature();
msg.visual_max_temperature = traits.get_visual_max_temperature();
msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
msg.visual_min_humidity = traits.get_visual_min_humidity();
msg.visual_max_humidity = traits.get_visual_max_humidity();
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
msg.supports_action = traits.get_supports_action();
for (auto fan_mode : traits.get_supported_fan_modes())
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
@ -813,7 +685,9 @@ bool APIConnection::try_send_climate_info(APIConnection *api, void *v_climate) {
msg.supported_custom_presets.push_back(custom_preset);
for (auto swing_mode : traits.get_supported_swing_modes())
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
return api->send_list_entities_climate_response(msg);
msg.unique_id = get_default_unique_id("climate", climate);
return this->try_send_entity_info_(static_cast<EntityBase *>(climate), msg,
&APIConnection::send_list_entities_climate_response);
}
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
climate::Climate *climate = App.get_climate_by_key(msg.key);
@ -847,51 +721,35 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
#ifdef USE_NUMBER
bool APIConnection::send_number_state(number::Number *number, float state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_number_state(this, number, state)) {
this->deferred_message_queue_.defer(number, try_send_number_state);
}
return true;
return this->send_state_with_value_(number, &APIConnection::try_send_number_state_,
&APIConnection::try_send_number_state_, state);
}
void APIConnection::send_number_info(number::Number *number) {
if (!APIConnection::try_send_number_info(this, number)) {
this->deferred_message_queue_.defer(number, try_send_number_info);
}
this->send_info_(static_cast<EntityBase *>(number),
reinterpret_cast<send_message_t>(&APIConnection::try_send_number_info_));
}
bool APIConnection::try_send_number_state(APIConnection *api, void *v_number) {
number::Number *number = reinterpret_cast<number::Number *>(v_number);
return APIConnection::try_send_number_state(api, number, number->state);
bool APIConnection::try_send_number_state_(number::Number *number) {
return this->try_send_number_state_(number, number->state);
}
bool APIConnection::try_send_number_state(APIConnection *api, number::Number *number, float state) {
NumberStateResponse resp{};
resp.key = number->get_object_id_hash();
bool APIConnection::try_send_number_state_(number::Number *number, float state) {
NumberStateResponse resp;
resp.state = state;
resp.missing_state = !number->has_state();
return api->send_number_state_response(resp);
resp.key = number->get_object_id_hash();
return this->send_number_state_response(resp);
}
bool APIConnection::try_send_number_info(APIConnection *api, void *v_number) {
number::Number *number = reinterpret_cast<number::Number *>(v_number);
bool APIConnection::try_send_number_info_(number::Number *number) {
ListEntitiesNumberResponse msg;
msg.key = number->get_object_id_hash();
msg.object_id = number->get_object_id();
if (number->has_own_name())
msg.name = number->get_name();
msg.unique_id = get_default_unique_id("number", number);
msg.icon = number->get_icon();
msg.disabled_by_default = number->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
msg.device_class = number->traits.get_device_class();
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
msg.step = number->traits.get_step();
return api->send_list_entities_number_response(msg);
msg.unique_id = get_default_unique_id("number", number);
return this->try_send_entity_info_(static_cast<EntityBase *>(number), msg,
&APIConnection::send_list_entities_number_response);
}
void APIConnection::number_command(const NumberCommandRequest &msg) {
number::Number *number = App.get_number_by_key(msg.key);
@ -906,43 +764,28 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
#ifdef USE_DATETIME_DATE
bool APIConnection::send_date_state(datetime::DateEntity *date) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_date_state(this, date)) {
this->deferred_message_queue_.defer(date, try_send_date_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(date),
reinterpret_cast<send_message_t>(&APIConnection::try_send_date_state_));
}
void APIConnection::send_date_info(datetime::DateEntity *date) {
if (!APIConnection::try_send_date_info(this, date)) {
this->deferred_message_queue_.defer(date, try_send_date_info);
}
this->send_info_(static_cast<EntityBase *>(date),
reinterpret_cast<send_message_t>(&APIConnection::try_send_date_info_));
}
bool APIConnection::try_send_date_state(APIConnection *api, void *v_date) {
datetime::DateEntity *date = reinterpret_cast<datetime::DateEntity *>(v_date);
DateStateResponse resp{};
resp.key = date->get_object_id_hash();
bool APIConnection::try_send_date_state_(datetime::DateEntity *date) {
DateStateResponse resp;
resp.missing_state = !date->has_state();
resp.year = date->year;
resp.month = date->month;
resp.day = date->day;
return api->send_date_state_response(resp);
}
bool APIConnection::try_send_date_info(APIConnection *api, void *v_date) {
datetime::DateEntity *date = reinterpret_cast<datetime::DateEntity *>(v_date);
ListEntitiesDateResponse msg;
msg.key = date->get_object_id_hash();
msg.object_id = date->get_object_id();
if (date->has_own_name())
msg.name = date->get_name();
msg.unique_id = get_default_unique_id("date", date);
msg.icon = date->get_icon();
msg.disabled_by_default = date->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(date->get_entity_category());
return api->send_list_entities_date_response(msg);
resp.key = date->get_object_id_hash();
return this->send_date_state_response(resp);
}
bool APIConnection::try_send_date_info_(datetime::DateEntity *date) {
ListEntitiesDateResponse msg;
msg.unique_id = get_default_unique_id("date", date);
return this->try_send_entity_info_(static_cast<EntityBase *>(date), msg,
&APIConnection::send_list_entities_date_response);
}
void APIConnection::date_command(const DateCommandRequest &msg) {
datetime::DateEntity *date = App.get_date_by_key(msg.key);
@ -957,43 +800,28 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
#ifdef USE_DATETIME_TIME
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_time_state(this, time)) {
this->deferred_message_queue_.defer(time, try_send_time_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(time),
reinterpret_cast<send_message_t>(&APIConnection::try_send_time_state_));
}
void APIConnection::send_time_info(datetime::TimeEntity *time) {
if (!APIConnection::try_send_time_info(this, time)) {
this->deferred_message_queue_.defer(time, try_send_time_info);
}
this->send_info_(static_cast<EntityBase *>(time),
reinterpret_cast<send_message_t>(&APIConnection::try_send_time_info_));
}
bool APIConnection::try_send_time_state(APIConnection *api, void *v_time) {
datetime::TimeEntity *time = reinterpret_cast<datetime::TimeEntity *>(v_time);
TimeStateResponse resp{};
resp.key = time->get_object_id_hash();
bool APIConnection::try_send_time_state_(datetime::TimeEntity *time) {
TimeStateResponse resp;
resp.missing_state = !time->has_state();
resp.hour = time->hour;
resp.minute = time->minute;
resp.second = time->second;
return api->send_time_state_response(resp);
}
bool APIConnection::try_send_time_info(APIConnection *api, void *v_time) {
datetime::TimeEntity *time = reinterpret_cast<datetime::TimeEntity *>(v_time);
ListEntitiesTimeResponse msg;
msg.key = time->get_object_id_hash();
msg.object_id = time->get_object_id();
if (time->has_own_name())
msg.name = time->get_name();
msg.unique_id = get_default_unique_id("time", time);
msg.icon = time->get_icon();
msg.disabled_by_default = time->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(time->get_entity_category());
return api->send_list_entities_time_response(msg);
resp.key = time->get_object_id_hash();
return this->send_time_state_response(resp);
}
bool APIConnection::try_send_time_info_(datetime::TimeEntity *time) {
ListEntitiesTimeResponse msg;
msg.unique_id = get_default_unique_id("time", time);
return this->try_send_entity_info_(static_cast<EntityBase *>(time), msg,
&APIConnection::send_list_entities_time_response);
}
void APIConnection::time_command(const TimeCommandRequest &msg) {
datetime::TimeEntity *time = App.get_time_by_key(msg.key);
@ -1008,44 +836,29 @@ void APIConnection::time_command(const TimeCommandRequest &msg) {
#ifdef USE_DATETIME_DATETIME
bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_datetime_state(this, datetime)) {
this->deferred_message_queue_.defer(datetime, try_send_datetime_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(datetime),
reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_state_));
}
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
if (!APIConnection::try_send_datetime_info(this, datetime)) {
this->deferred_message_queue_.defer(datetime, try_send_datetime_info);
}
this->send_info_(static_cast<EntityBase *>(datetime),
reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_info_));
}
bool APIConnection::try_send_datetime_state(APIConnection *api, void *v_datetime) {
datetime::DateTimeEntity *datetime = reinterpret_cast<datetime::DateTimeEntity *>(v_datetime);
DateTimeStateResponse resp{};
resp.key = datetime->get_object_id_hash();
bool APIConnection::try_send_datetime_state_(datetime::DateTimeEntity *datetime) {
DateTimeStateResponse resp;
resp.missing_state = !datetime->has_state();
if (datetime->has_state()) {
ESPTime state = datetime->state_as_esptime();
resp.epoch_seconds = state.timestamp;
}
return api->send_date_time_state_response(resp);
}
bool APIConnection::try_send_datetime_info(APIConnection *api, void *v_datetime) {
datetime::DateTimeEntity *datetime = reinterpret_cast<datetime::DateTimeEntity *>(v_datetime);
ListEntitiesDateTimeResponse msg;
msg.key = datetime->get_object_id_hash();
msg.object_id = datetime->get_object_id();
if (datetime->has_own_name())
msg.name = datetime->get_name();
msg.unique_id = get_default_unique_id("datetime", datetime);
msg.icon = datetime->get_icon();
msg.disabled_by_default = datetime->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(datetime->get_entity_category());
return api->send_list_entities_date_time_response(msg);
resp.key = datetime->get_object_id_hash();
return this->send_date_time_state_response(resp);
}
bool APIConnection::try_send_datetime_info_(datetime::DateTimeEntity *datetime) {
ListEntitiesDateTimeResponse msg;
msg.unique_id = get_default_unique_id("datetime", datetime);
return this->try_send_entity_info_(static_cast<EntityBase *>(datetime), msg,
&APIConnection::send_list_entities_date_time_response);
}
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key);
@ -1060,47 +873,31 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
#ifdef USE_TEXT
bool APIConnection::send_text_state(text::Text *text, std::string state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_text_state(this, text, std::move(state))) {
this->deferred_message_queue_.defer(text, try_send_text_state);
}
return true;
return this->send_state_with_value_(text, &APIConnection::try_send_text_state_, &APIConnection::try_send_text_state_,
std::move(state));
}
void APIConnection::send_text_info(text::Text *text) {
if (!APIConnection::try_send_text_info(this, text)) {
this->deferred_message_queue_.defer(text, try_send_text_info);
}
this->send_info_(static_cast<EntityBase *>(text),
reinterpret_cast<send_message_t>(&APIConnection::try_send_text_info_));
}
bool APIConnection::try_send_text_state(APIConnection *api, void *v_text) {
text::Text *text = reinterpret_cast<text::Text *>(v_text);
return APIConnection::try_send_text_state(api, text, text->state);
}
bool APIConnection::try_send_text_state(APIConnection *api, text::Text *text, std::string state) {
TextStateResponse resp{};
resp.key = text->get_object_id_hash();
bool APIConnection::try_send_text_state_(text::Text *text) { return this->try_send_text_state_(text, text->state); }
bool APIConnection::try_send_text_state_(text::Text *text, std::string state) {
TextStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !text->has_state();
return api->send_text_state_response(resp);
}
bool APIConnection::try_send_text_info(APIConnection *api, void *v_text) {
text::Text *text = reinterpret_cast<text::Text *>(v_text);
ListEntitiesTextResponse msg;
msg.key = text->get_object_id_hash();
msg.object_id = text->get_object_id();
msg.name = text->get_name();
msg.icon = text->get_icon();
msg.disabled_by_default = text->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(text->get_entity_category());
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
resp.key = text->get_object_id_hash();
return this->send_text_state_response(resp);
}
bool APIConnection::try_send_text_info_(text::Text *text) {
ListEntitiesTextResponse msg;
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
msg.min_length = text->traits.get_min_length();
msg.max_length = text->traits.get_max_length();
msg.pattern = text->traits.get_pattern();
return api->send_list_entities_text_response(msg);
msg.unique_id = get_default_unique_id("text", text);
return this->try_send_entity_info_(static_cast<EntityBase *>(text), msg,
&APIConnection::send_list_entities_text_response);
}
void APIConnection::text_command(const TextCommandRequest &msg) {
text::Text *text = App.get_text_by_key(msg.key);
@ -1115,47 +912,31 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
#ifdef USE_SELECT
bool APIConnection::send_select_state(select::Select *select, std::string state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_select_state(this, select, std::move(state))) {
this->deferred_message_queue_.defer(select, try_send_select_state);
}
return true;
return this->send_state_with_value_(select, &APIConnection::try_send_select_state_,
&APIConnection::try_send_select_state_, std::move(state));
}
void APIConnection::send_select_info(select::Select *select) {
if (!APIConnection::try_send_select_info(this, select)) {
this->deferred_message_queue_.defer(select, try_send_select_info);
}
this->send_info_(static_cast<EntityBase *>(select),
reinterpret_cast<send_message_t>(&APIConnection::try_send_select_info_));
}
bool APIConnection::try_send_select_state(APIConnection *api, void *v_select) {
select::Select *select = reinterpret_cast<select::Select *>(v_select);
return APIConnection::try_send_select_state(api, select, select->state);
bool APIConnection::try_send_select_state_(select::Select *select) {
return this->try_send_select_state_(select, select->state);
}
bool APIConnection::try_send_select_state(APIConnection *api, select::Select *select, std::string state) {
SelectStateResponse resp{};
resp.key = select->get_object_id_hash();
bool APIConnection::try_send_select_state_(select::Select *select, std::string state) {
SelectStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !select->has_state();
return api->send_select_state_response(resp);
}
bool APIConnection::try_send_select_info(APIConnection *api, void *v_select) {
select::Select *select = reinterpret_cast<select::Select *>(v_select);
ListEntitiesSelectResponse msg;
msg.key = select->get_object_id_hash();
msg.object_id = select->get_object_id();
if (select->has_own_name())
msg.name = select->get_name();
msg.unique_id = get_default_unique_id("select", select);
msg.icon = select->get_icon();
msg.disabled_by_default = select->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(select->get_entity_category());
resp.key = select->get_object_id_hash();
return this->send_select_state_response(resp);
}
bool APIConnection::try_send_select_info_(select::Select *select) {
ListEntitiesSelectResponse msg;
for (const auto &option : select->traits.get_options())
msg.options.push_back(option);
return api->send_list_entities_select_response(msg);
msg.unique_id = get_default_unique_id("select", select);
return this->try_send_entity_info_(static_cast<EntityBase *>(select), msg,
&APIConnection::send_list_entities_select_response);
}
void APIConnection::select_command(const SelectCommandRequest &msg) {
select::Select *select = App.get_select_by_key(msg.key);
@ -1169,26 +950,18 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
#endif
#ifdef USE_BUTTON
void APIConnection::send_button_info(button::Button *button) {
if (!APIConnection::try_send_button_info(this, button)) {
this->deferred_message_queue_.defer(button, try_send_button_info);
}
void esphome::api::APIConnection::send_button_info(button::Button *button) {
this->send_info_(static_cast<EntityBase *>(button),
reinterpret_cast<send_message_t>(&APIConnection::try_send_button_info_));
}
bool APIConnection::try_send_button_info(APIConnection *api, void *v_button) {
button::Button *button = reinterpret_cast<button::Button *>(v_button);
bool esphome::api::APIConnection::try_send_button_info_(button::Button *button) {
ListEntitiesButtonResponse msg;
msg.key = button->get_object_id_hash();
msg.object_id = button->get_object_id();
if (button->has_own_name())
msg.name = button->get_name();
msg.unique_id = get_default_unique_id("button", button);
msg.icon = button->get_icon();
msg.disabled_by_default = button->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
msg.device_class = button->get_device_class();
return api->send_list_entities_button_response(msg);
msg.unique_id = get_default_unique_id("button", button);
return this->try_send_entity_info_(static_cast<EntityBase *>(button), msg,
&APIConnection::send_list_entities_button_response);
}
void APIConnection::button_command(const ButtonCommandRequest &msg) {
void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
button::Button *button = App.get_button_by_key(msg.key);
if (button == nullptr)
return;
@ -1199,45 +972,31 @@ void APIConnection::button_command(const ButtonCommandRequest &msg) {
#ifdef USE_LOCK
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_lock_state(this, a_lock, state)) {
this->deferred_message_queue_.defer(a_lock, try_send_lock_state);
}
return true;
return this->send_state_with_value_(a_lock, &APIConnection::try_send_lock_state_,
&APIConnection::try_send_lock_state_, state);
}
void APIConnection::send_lock_info(lock::Lock *a_lock) {
if (!APIConnection::try_send_lock_info(this, a_lock)) {
this->deferred_message_queue_.defer(a_lock, try_send_lock_info);
}
this->send_info_(static_cast<EntityBase *>(a_lock),
reinterpret_cast<send_message_t>(&APIConnection::try_send_lock_info_));
}
bool APIConnection::try_send_lock_state(APIConnection *api, void *v_a_lock) {
lock::Lock *a_lock = reinterpret_cast<lock::Lock *>(v_a_lock);
return APIConnection::try_send_lock_state(api, a_lock, a_lock->state);
bool APIConnection::try_send_lock_state_(lock::Lock *a_lock) {
return this->try_send_lock_state_(a_lock, a_lock->state);
}
bool APIConnection::try_send_lock_state(APIConnection *api, lock::Lock *a_lock, lock::LockState state) {
LockStateResponse resp{};
resp.key = a_lock->get_object_id_hash();
bool APIConnection::try_send_lock_state_(lock::Lock *a_lock, lock::LockState state) {
LockStateResponse resp;
resp.state = static_cast<enums::LockState>(state);
return api->send_lock_state_response(resp);
resp.key = a_lock->get_object_id_hash();
return this->send_lock_state_response(resp);
}
bool APIConnection::try_send_lock_info(APIConnection *api, void *v_a_lock) {
lock::Lock *a_lock = reinterpret_cast<lock::Lock *>(v_a_lock);
bool APIConnection::try_send_lock_info_(lock::Lock *a_lock) {
ListEntitiesLockResponse msg;
msg.key = a_lock->get_object_id_hash();
msg.object_id = a_lock->get_object_id();
if (a_lock->has_own_name())
msg.name = a_lock->get_name();
msg.unique_id = get_default_unique_id("lock", a_lock);
msg.icon = a_lock->get_icon();
msg.assumed_state = a_lock->traits.get_assumed_state();
msg.disabled_by_default = a_lock->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_lock->get_entity_category());
msg.supports_open = a_lock->traits.get_supports_open();
msg.requires_code = a_lock->traits.get_requires_code();
return api->send_list_entities_lock_response(msg);
msg.unique_id = get_default_unique_id("lock", a_lock);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_lock), msg,
&APIConnection::send_list_entities_lock_response);
}
void APIConnection::lock_command(const LockCommandRequest &msg) {
lock::Lock *a_lock = App.get_lock_by_key(msg.key);
@ -1260,45 +1019,31 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
#ifdef USE_VALVE
bool APIConnection::send_valve_state(valve::Valve *valve) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_valve_state(this, valve)) {
this->deferred_message_queue_.defer(valve, try_send_valve_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(valve),
reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_state_));
}
void APIConnection::send_valve_info(valve::Valve *valve) {
if (!APIConnection::try_send_valve_info(this, valve)) {
this->deferred_message_queue_.defer(valve, try_send_valve_info);
}
this->send_info_(static_cast<EntityBase *>(valve),
reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_info_));
}
bool APIConnection::try_send_valve_state(APIConnection *api, void *v_valve) {
valve::Valve *valve = reinterpret_cast<valve::Valve *>(v_valve);
ValveStateResponse resp{};
resp.key = valve->get_object_id_hash();
bool APIConnection::try_send_valve_state_(valve::Valve *valve) {
ValveStateResponse resp;
resp.position = valve->position;
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
return api->send_valve_state_response(resp);
resp.key = valve->get_object_id_hash();
return this->send_valve_state_response(resp);
}
bool APIConnection::try_send_valve_info(APIConnection *api, void *v_valve) {
valve::Valve *valve = reinterpret_cast<valve::Valve *>(v_valve);
auto traits = valve->get_traits();
bool APIConnection::try_send_valve_info_(valve::Valve *valve) {
ListEntitiesValveResponse msg;
msg.key = valve->get_object_id_hash();
msg.object_id = valve->get_object_id();
if (valve->has_own_name())
msg.name = valve->get_name();
msg.unique_id = get_default_unique_id("valve", valve);
msg.icon = valve->get_icon();
msg.disabled_by_default = valve->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(valve->get_entity_category());
auto traits = valve->get_traits();
msg.device_class = valve->get_device_class();
msg.assumed_state = traits.get_is_assumed_state();
msg.supports_position = traits.get_supports_position();
msg.supports_stop = traits.get_supports_stop();
return api->send_list_entities_valve_response(msg);
msg.unique_id = get_default_unique_id("valve", valve);
return this->try_send_entity_info_(static_cast<EntityBase *>(valve), msg,
&APIConnection::send_list_entities_valve_response);
}
void APIConnection::valve_command(const ValveCommandRequest &msg) {
valve::Valve *valve = App.get_valve_by_key(msg.key);
@ -1316,48 +1061,29 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) {
#ifdef USE_MEDIA_PLAYER
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_media_player_state(this, media_player)) {
this->deferred_message_queue_.defer(media_player, try_send_media_player_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(media_player),
reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_state_));
}
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
if (!APIConnection::try_send_media_player_info(this, media_player)) {
this->deferred_message_queue_.defer(media_player, try_send_media_player_info);
}
this->send_info_(static_cast<EntityBase *>(media_player),
reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_info_));
}
bool APIConnection::try_send_media_player_state(APIConnection *api, void *v_media_player) {
media_player::MediaPlayer *media_player = reinterpret_cast<media_player::MediaPlayer *>(v_media_player);
MediaPlayerStateResponse resp{};
resp.key = media_player->get_object_id_hash();
bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *media_player) {
MediaPlayerStateResponse resp;
media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
? media_player::MEDIA_PLAYER_STATE_PLAYING
: media_player->state;
resp.state = static_cast<enums::MediaPlayerState>(report_state);
resp.volume = media_player->volume;
resp.muted = media_player->is_muted();
return api->send_media_player_state_response(resp);
}
bool APIConnection::try_send_media_player_info(APIConnection *api, void *v_media_player) {
media_player::MediaPlayer *media_player = reinterpret_cast<media_player::MediaPlayer *>(v_media_player);
ListEntitiesMediaPlayerResponse msg;
msg.key = media_player->get_object_id_hash();
msg.object_id = media_player->get_object_id();
if (media_player->has_own_name())
msg.name = media_player->get_name();
msg.unique_id = get_default_unique_id("media_player", media_player);
msg.icon = media_player->get_icon();
msg.disabled_by_default = media_player->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(media_player->get_entity_category());
resp.key = media_player->get_object_id_hash();
return this->send_media_player_state_response(resp);
}
bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media_player) {
ListEntitiesMediaPlayerResponse msg;
auto traits = media_player->get_traits();
msg.supports_pause = traits.get_supports_pause();
for (auto &supported_format : traits.get_supported_formats()) {
MediaPlayerSupportedFormat media_format;
media_format.format = supported_format.format;
@ -1367,8 +1093,9 @@ bool APIConnection::try_send_media_player_info(APIConnection *api, void *v_media
media_format.sample_bytes = supported_format.sample_bytes;
msg.supported_formats.push_back(media_format);
}
return api->send_list_entities_media_player_response(msg);
msg.unique_id = get_default_unique_id("media_player", media_player);
return this->try_send_entity_info_(static_cast<EntityBase *>(media_player), msg,
&APIConnection::send_list_entities_media_player_response);
}
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
@ -1403,22 +1130,14 @@ void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage>
this->image_reader_.set_image(std::move(image));
}
void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
if (!APIConnection::try_send_camera_info(this, camera)) {
this->deferred_message_queue_.defer(camera, try_send_camera_info);
}
this->send_info_(static_cast<EntityBase *>(camera),
reinterpret_cast<send_message_t>(&APIConnection::try_send_camera_info_));
}
bool APIConnection::try_send_camera_info(APIConnection *api, void *v_camera) {
esp32_camera::ESP32Camera *camera = reinterpret_cast<esp32_camera::ESP32Camera *>(v_camera);
bool APIConnection::try_send_camera_info_(esp32_camera::ESP32Camera *camera) {
ListEntitiesCameraResponse msg;
msg.key = camera->get_object_id_hash();
msg.object_id = camera->get_object_id();
if (camera->has_own_name())
msg.name = camera->get_name();
msg.unique_id = get_default_unique_id("camera", camera);
msg.disabled_by_default = camera->is_disabled_by_default();
msg.icon = camera->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(camera->get_entity_category());
return api->send_list_entities_camera_response(msg);
return this->try_send_entity_info_(static_cast<EntityBase *>(camera), msg,
&APIConnection::send_list_entities_camera_response);
}
void APIConnection::camera_image(const CameraImageRequest &msg) {
if (esp32_camera::global_esp32_camera == nullptr)
@ -1607,43 +1326,28 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
#ifdef USE_ALARM_CONTROL_PANEL
bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_alarm_control_panel_state(this, a_alarm_control_panel)) {
this->deferred_message_queue_.defer(a_alarm_control_panel, try_send_alarm_control_panel_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(a_alarm_control_panel),
reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_state_));
}
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
if (!APIConnection::try_send_alarm_control_panel_info(this, a_alarm_control_panel)) {
this->deferred_message_queue_.defer(a_alarm_control_panel, try_send_alarm_control_panel_info);
}
this->send_info_(static_cast<EntityBase *>(a_alarm_control_panel),
reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_info_));
}
bool APIConnection::try_send_alarm_control_panel_state(APIConnection *api, void *v_a_alarm_control_panel) {
alarm_control_panel::AlarmControlPanel *a_alarm_control_panel =
reinterpret_cast<alarm_control_panel::AlarmControlPanel *>(v_a_alarm_control_panel);
AlarmControlPanelStateResponse resp{};
resp.key = a_alarm_control_panel->get_object_id_hash();
bool APIConnection::try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
AlarmControlPanelStateResponse resp;
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
return api->send_alarm_control_panel_state_response(resp);
resp.key = a_alarm_control_panel->get_object_id_hash();
return this->send_alarm_control_panel_state_response(resp);
}
bool APIConnection::try_send_alarm_control_panel_info(APIConnection *api, void *v_a_alarm_control_panel) {
alarm_control_panel::AlarmControlPanel *a_alarm_control_panel =
reinterpret_cast<alarm_control_panel::AlarmControlPanel *>(v_a_alarm_control_panel);
bool APIConnection::try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
ListEntitiesAlarmControlPanelResponse msg;
msg.key = a_alarm_control_panel->get_object_id_hash();
msg.object_id = a_alarm_control_panel->get_object_id();
msg.name = a_alarm_control_panel->get_name();
msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
msg.icon = a_alarm_control_panel->get_icon();
msg.disabled_by_default = a_alarm_control_panel->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_alarm_control_panel->get_entity_category());
msg.supported_features = a_alarm_control_panel->get_supported_features();
msg.requires_code = a_alarm_control_panel->get_requires_code();
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
return api->send_list_entities_alarm_control_panel_response(msg);
msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_alarm_control_panel), msg,
&APIConnection::send_list_entities_alarm_control_panel_response);
}
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key);
@ -1681,63 +1385,45 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
#ifdef USE_EVENT
void APIConnection::send_event(event::Event *event, std::string event_type) {
if (!APIConnection::try_send_event(this, event, std::move(event_type))) {
this->deferred_message_queue_.defer(event, try_send_event);
}
this->send_state_with_value_(event, &APIConnection::try_send_event_, &APIConnection::try_send_event_,
std::move(event_type));
}
void APIConnection::send_event_info(event::Event *event) {
if (!APIConnection::try_send_event_info(this, event)) {
this->deferred_message_queue_.defer(event, try_send_event_info);
}
this->send_info_(static_cast<EntityBase *>(event),
reinterpret_cast<send_message_t>(&APIConnection::try_send_event_info_));
}
bool APIConnection::try_send_event(APIConnection *api, void *v_event) {
event::Event *event = reinterpret_cast<event::Event *>(v_event);
return APIConnection::try_send_event(api, event, *(event->last_event_type));
bool APIConnection::try_send_event_(event::Event *event) {
return this->try_send_event_(event, *(event->last_event_type));
}
bool APIConnection::try_send_event(APIConnection *api, event::Event *event, std::string event_type) {
EventResponse resp{};
resp.key = event->get_object_id_hash();
bool APIConnection::try_send_event_(event::Event *event, std::string event_type) {
EventResponse resp;
resp.event_type = std::move(event_type);
return api->send_event_response(resp);
resp.key = event->get_object_id_hash();
return this->send_event_response(resp);
}
bool APIConnection::try_send_event_info(APIConnection *api, void *v_event) {
event::Event *event = reinterpret_cast<event::Event *>(v_event);
bool APIConnection::try_send_event_info_(event::Event *event) {
ListEntitiesEventResponse msg;
msg.key = event->get_object_id_hash();
msg.object_id = event->get_object_id();
if (event->has_own_name())
msg.name = event->get_name();
msg.unique_id = get_default_unique_id("event", event);
msg.icon = event->get_icon();
msg.disabled_by_default = event->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(event->get_entity_category());
msg.device_class = event->get_device_class();
for (const auto &event_type : event->get_event_types())
msg.event_types.push_back(event_type);
return api->send_list_entities_event_response(msg);
msg.unique_id = get_default_unique_id("event", event);
return this->try_send_entity_info_(static_cast<EntityBase *>(event), msg,
&APIConnection::send_list_entities_event_response);
}
#endif
#ifdef USE_UPDATE
bool APIConnection::send_update_state(update::UpdateEntity *update) {
if (!this->state_subscription_)
return false;
if (!APIConnection::try_send_update_state(this, update)) {
this->deferred_message_queue_.defer(update, try_send_update_state);
}
return true;
return this->send_state_(static_cast<EntityBase *>(update),
reinterpret_cast<send_message_t>(&APIConnection::try_send_update_state_));
}
void APIConnection::send_update_info(update::UpdateEntity *update) {
if (!APIConnection::try_send_update_info(this, update)) {
this->deferred_message_queue_.defer(update, try_send_update_info);
}
this->send_info_(static_cast<EntityBase *>(update),
reinterpret_cast<send_message_t>(&APIConnection::try_send_update_info_));
}
bool APIConnection::try_send_update_state(APIConnection *api, void *v_update) {
update::UpdateEntity *update = reinterpret_cast<update::UpdateEntity *>(v_update);
UpdateStateResponse resp{};
resp.key = update->get_object_id_hash();
bool APIConnection::try_send_update_state_(update::UpdateEntity *update) {
UpdateStateResponse resp;
resp.missing_state = !update->has_state();
if (update->has_state()) {
resp.in_progress = update->state == update::UpdateState::UPDATE_STATE_INSTALLING;
@ -1752,21 +1438,15 @@ bool APIConnection::try_send_update_state(APIConnection *api, void *v_update) {
resp.release_url = update->update_info.release_url;
}
return api->send_update_state_response(resp);
resp.key = update->get_object_id_hash();
return this->send_update_state_response(resp);
}
bool APIConnection::try_send_update_info(APIConnection *api, void *v_update) {
update::UpdateEntity *update = reinterpret_cast<update::UpdateEntity *>(v_update);
bool APIConnection::try_send_update_info_(update::UpdateEntity *update) {
ListEntitiesUpdateResponse msg;
msg.key = update->get_object_id_hash();
msg.object_id = update->get_object_id();
if (update->has_own_name())
msg.name = update->get_name();
msg.unique_id = get_default_unique_id("update", update);
msg.icon = update->get_icon();
msg.disabled_by_default = update->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(update->get_entity_category());
msg.device_class = update->get_device_class();
return api->send_list_entities_update_response(msg);
msg.unique_id = get_default_unique_id("update", update);
return this->try_send_entity_info_(static_cast<EntityBase *>(update), msg,
&APIConnection::send_list_entities_update_response);
}
void APIConnection::update_command(const UpdateCommandRequest &msg) {
update::UpdateEntity *update = App.get_update_by_key(msg.key);
@ -1940,26 +1620,29 @@ NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const Nois
void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
state_subs_at_ = 0;
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
if (this->remove_)
return false;
if (!this->helper_->can_write_without_blocking()) {
delay(0);
APIError err = this->helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
api_error_to_str(err), errno);
return false;
}
if (!this->helper_->can_write_without_blocking()) {
// SubscribeLogsResponse
if (message_type != 29) {
ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
}
delay(0);
return false;
}
if (this->helper_->can_write_without_blocking())
return true;
delay(0);
APIError err = this->helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
api_error_to_str(err), errno);
return false;
}
if (this->helper_->can_write_without_blocking())
return true;
if (log_out_of_space) {
ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
}
return false;
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse
return false;
}
APIError err = this->helper_->write_protobuf_packet(message_type, buffer);

View File

@ -8,13 +8,14 @@
#include "api_server.h"
#include "esphome/core/application.h"
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include <vector>
namespace esphome {
namespace api {
using send_message_t = bool(APIConnection *, void *);
using send_message_t = bool (APIConnection::*)(void *);
/*
This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that
@ -30,10 +31,10 @@ class DeferredMessageQueue {
protected:
void *source_;
send_message_t *send_message_;
send_message_t send_message_;
public:
DeferredMessage(void *source, send_message_t *send_message) : source_(source), send_message_(send_message) {}
DeferredMessage(void *source, send_message_t send_message) : source_(source), send_message_(send_message) {}
bool operator==(const DeferredMessage &test) const {
return (source_ == test.source_ && send_message_ == test.send_message_);
}
@ -46,12 +47,13 @@ class DeferredMessageQueue {
APIConnection *api_connection_;
// helper for allowing only unique entries in the queue
void dmq_push_back_with_dedup_(void *source, send_message_t *send_message);
void dmq_push_back_with_dedup_(void *source, send_message_t send_message);
public:
DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {}
void process_queue();
void defer(void *source, send_message_t *send_message);
void defer(void *source, send_message_t send_message);
bool empty() const { return deferred_queue_.empty(); }
};
class APIConnection : public APIServerConnection {
@ -69,137 +71,213 @@ class APIConnection : public APIServerConnection {
#ifdef USE_BINARY_SENSOR
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
static bool try_send_binary_sensor_state(APIConnection *api, void *v_binary_sensor);
static bool try_send_binary_sensor_state(APIConnection *api, binary_sensor::BinarySensor *binary_sensor, bool state);
static bool try_send_binary_sensor_info(APIConnection *api, void *v_binary_sensor);
protected:
bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor);
bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state);
bool try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor);
public:
#endif
#ifdef USE_COVER
bool send_cover_state(cover::Cover *cover);
void send_cover_info(cover::Cover *cover);
static bool try_send_cover_state(APIConnection *api, void *v_cover);
static bool try_send_cover_info(APIConnection *api, void *v_cover);
void cover_command(const CoverCommandRequest &msg) override;
protected:
bool try_send_cover_state_(cover::Cover *cover);
bool try_send_cover_info_(cover::Cover *cover);
public:
#endif
#ifdef USE_FAN
bool send_fan_state(fan::Fan *fan);
void send_fan_info(fan::Fan *fan);
static bool try_send_fan_state(APIConnection *api, void *v_fan);
static bool try_send_fan_info(APIConnection *api, void *v_fan);
void fan_command(const FanCommandRequest &msg) override;
protected:
bool try_send_fan_state_(fan::Fan *fan);
bool try_send_fan_info_(fan::Fan *fan);
public:
#endif
#ifdef USE_LIGHT
bool send_light_state(light::LightState *light);
void send_light_info(light::LightState *light);
static bool try_send_light_state(APIConnection *api, void *v_light);
static bool try_send_light_info(APIConnection *api, void *v_light);
void light_command(const LightCommandRequest &msg) override;
protected:
bool try_send_light_state_(light::LightState *light);
bool try_send_light_info_(light::LightState *light);
public:
#endif
#ifdef USE_SENSOR
bool send_sensor_state(sensor::Sensor *sensor, float state);
void send_sensor_info(sensor::Sensor *sensor);
static bool try_send_sensor_state(APIConnection *api, void *v_sensor);
static bool try_send_sensor_state(APIConnection *api, sensor::Sensor *sensor, float state);
static bool try_send_sensor_info(APIConnection *api, void *v_sensor);
protected:
bool try_send_sensor_state_(sensor::Sensor *sensor);
bool try_send_sensor_state_(sensor::Sensor *sensor, float state);
bool try_send_sensor_info_(sensor::Sensor *sensor);
public:
#endif
#ifdef USE_SWITCH
bool send_switch_state(switch_::Switch *a_switch, bool state);
void send_switch_info(switch_::Switch *a_switch);
static bool try_send_switch_state(APIConnection *api, void *v_a_switch);
static bool try_send_switch_state(APIConnection *api, switch_::Switch *a_switch, bool state);
static bool try_send_switch_info(APIConnection *api, void *v_a_switch);
void switch_command(const SwitchCommandRequest &msg) override;
protected:
bool try_send_switch_state_(switch_::Switch *a_switch);
bool try_send_switch_state_(switch_::Switch *a_switch, bool state);
bool try_send_switch_info_(switch_::Switch *a_switch);
public:
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
static bool try_send_text_sensor_state(APIConnection *api, void *v_text_sensor);
static bool try_send_text_sensor_state(APIConnection *api, text_sensor::TextSensor *text_sensor, std::string state);
static bool try_send_text_sensor_info(APIConnection *api, void *v_text_sensor);
protected:
bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor);
bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state);
bool try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor);
public:
#endif
#ifdef USE_ESP32_CAMERA
void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
void send_camera_info(esp32_camera::ESP32Camera *camera);
static bool try_send_camera_info(APIConnection *api, void *v_camera);
void camera_image(const CameraImageRequest &msg) override;
protected:
bool try_send_camera_info_(esp32_camera::ESP32Camera *camera);
public:
#endif
#ifdef USE_CLIMATE
bool send_climate_state(climate::Climate *climate);
void send_climate_info(climate::Climate *climate);
static bool try_send_climate_state(APIConnection *api, void *v_climate);
static bool try_send_climate_info(APIConnection *api, void *v_climate);
void climate_command(const ClimateCommandRequest &msg) override;
protected:
bool try_send_climate_state_(climate::Climate *climate);
bool try_send_climate_info_(climate::Climate *climate);
public:
#endif
#ifdef USE_NUMBER
bool send_number_state(number::Number *number, float state);
void send_number_info(number::Number *number);
static bool try_send_number_state(APIConnection *api, void *v_number);
static bool try_send_number_state(APIConnection *api, number::Number *number, float state);
static bool try_send_number_info(APIConnection *api, void *v_number);
void number_command(const NumberCommandRequest &msg) override;
protected:
bool try_send_number_state_(number::Number *number);
bool try_send_number_state_(number::Number *number, float state);
bool try_send_number_info_(number::Number *number);
public:
#endif
#ifdef USE_DATETIME_DATE
bool send_date_state(datetime::DateEntity *date);
void send_date_info(datetime::DateEntity *date);
static bool try_send_date_state(APIConnection *api, void *v_date);
static bool try_send_date_info(APIConnection *api, void *v_date);
void date_command(const DateCommandRequest &msg) override;
protected:
bool try_send_date_state_(datetime::DateEntity *date);
bool try_send_date_info_(datetime::DateEntity *date);
public:
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state(datetime::TimeEntity *time);
void send_time_info(datetime::TimeEntity *time);
static bool try_send_time_state(APIConnection *api, void *v_time);
static bool try_send_time_info(APIConnection *api, void *v_time);
void time_command(const TimeCommandRequest &msg) override;
protected:
bool try_send_time_state_(datetime::TimeEntity *time);
bool try_send_time_info_(datetime::TimeEntity *time);
public:
#endif
#ifdef USE_DATETIME_DATETIME
bool send_datetime_state(datetime::DateTimeEntity *datetime);
void send_datetime_info(datetime::DateTimeEntity *datetime);
static bool try_send_datetime_state(APIConnection *api, void *v_datetime);
static bool try_send_datetime_info(APIConnection *api, void *v_datetime);
void datetime_command(const DateTimeCommandRequest &msg) override;
protected:
bool try_send_datetime_state_(datetime::DateTimeEntity *datetime);
bool try_send_datetime_info_(datetime::DateTimeEntity *datetime);
public:
#endif
#ifdef USE_TEXT
bool send_text_state(text::Text *text, std::string state);
void send_text_info(text::Text *text);
static bool try_send_text_state(APIConnection *api, void *v_text);
static bool try_send_text_state(APIConnection *api, text::Text *text, std::string state);
static bool try_send_text_info(APIConnection *api, void *v_text);
void text_command(const TextCommandRequest &msg) override;
protected:
bool try_send_text_state_(text::Text *text);
bool try_send_text_state_(text::Text *text, std::string state);
bool try_send_text_info_(text::Text *text);
public:
#endif
#ifdef USE_SELECT
bool send_select_state(select::Select *select, std::string state);
void send_select_info(select::Select *select);
static bool try_send_select_state(APIConnection *api, void *v_select);
static bool try_send_select_state(APIConnection *api, select::Select *select, std::string state);
static bool try_send_select_info(APIConnection *api, void *v_select);
void select_command(const SelectCommandRequest &msg) override;
protected:
bool try_send_select_state_(select::Select *select);
bool try_send_select_state_(select::Select *select, std::string state);
bool try_send_select_info_(select::Select *select);
public:
#endif
#ifdef USE_BUTTON
void send_button_info(button::Button *button);
static bool try_send_button_info(APIConnection *api, void *v_button);
void button_command(const ButtonCommandRequest &msg) override;
protected:
bool try_send_button_info_(button::Button *button);
public:
#endif
#ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
void send_lock_info(lock::Lock *a_lock);
static bool try_send_lock_state(APIConnection *api, void *v_a_lock);
static bool try_send_lock_state(APIConnection *api, lock::Lock *a_lock, lock::LockState state);
static bool try_send_lock_info(APIConnection *api, void *v_a_lock);
void lock_command(const LockCommandRequest &msg) override;
protected:
bool try_send_lock_state_(lock::Lock *a_lock);
bool try_send_lock_state_(lock::Lock *a_lock, lock::LockState state);
bool try_send_lock_info_(lock::Lock *a_lock);
public:
#endif
#ifdef USE_VALVE
bool send_valve_state(valve::Valve *valve);
void send_valve_info(valve::Valve *valve);
static bool try_send_valve_state(APIConnection *api, void *v_valve);
static bool try_send_valve_info(APIConnection *api, void *v_valve);
void valve_command(const ValveCommandRequest &msg) override;
protected:
bool try_send_valve_state_(valve::Valve *valve);
bool try_send_valve_info_(valve::Valve *valve);
public:
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state(media_player::MediaPlayer *media_player);
void send_media_player_info(media_player::MediaPlayer *media_player);
static bool try_send_media_player_state(APIConnection *api, void *v_media_player);
static bool try_send_media_player_info(APIConnection *api, void *v_media_player);
void media_player_command(const MediaPlayerCommandRequest &msg) override;
protected:
bool try_send_media_player_state_(media_player::MediaPlayer *media_player);
bool try_send_media_player_info_(media_player::MediaPlayer *media_player);
public:
#endif
bool try_send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
@ -246,25 +324,37 @@ class APIConnection : public APIServerConnection {
#ifdef USE_ALARM_CONTROL_PANEL
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
static bool try_send_alarm_control_panel_state(APIConnection *api, void *v_a_alarm_control_panel);
static bool try_send_alarm_control_panel_info(APIConnection *api, void *v_a_alarm_control_panel);
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
protected:
bool try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
bool try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
public:
#endif
#ifdef USE_EVENT
void send_event(event::Event *event, std::string event_type);
void send_event_info(event::Event *event);
static bool try_send_event(APIConnection *api, void *v_event);
static bool try_send_event(APIConnection *api, event::Event *event, std::string event_type);
static bool try_send_event_info(APIConnection *api, void *v_event);
protected:
bool try_send_event_(event::Event *event);
bool try_send_event_(event::Event *event, std::string event_type);
bool try_send_event_info_(event::Event *event);
public:
#endif
#ifdef USE_UPDATE
bool send_update_state(update::UpdateEntity *update);
void send_update_info(update::UpdateEntity *update);
static bool try_send_update_state(APIConnection *api, void *v_update);
static bool try_send_update_info(APIConnection *api, void *v_update);
void update_command(const UpdateCommandRequest &msg) override;
protected:
bool try_send_update_state_(update::UpdateEntity *update);
bool try_send_update_info_(update::UpdateEntity *update);
public:
#endif
void on_disconnect_response(const DisconnectResponse &value) override;
@ -325,6 +415,7 @@ class APIConnection : public APIServerConnection {
this->proto_write_buffer_.insert(this->proto_write_buffer_.begin(), header_padding, 0);
return {&this->proto_write_buffer_};
}
bool try_to_clear_buffer(bool log_out_of_space);
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
std::string get_client_combined_info() const { return this->client_combined_info_; }
@ -332,6 +423,99 @@ class APIConnection : public APIServerConnection {
protected:
friend APIServer;
/**
* Generic send entity state method to reduce code duplication.
* Only attempts to build and send the message if the transmit buffer is available.
*
* This is the base version for entities that use their current state.
*
* @param entity The entity to send state for
* @param try_send_func The function that tries to send the state
* @return True on success or message deferred, false if subscription check failed
*/
bool send_state_(esphome::EntityBase *entity, send_message_t try_send_func) {
if (!this->state_subscription_)
return false;
if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) {
return true;
}
this->deferred_message_queue_.defer(entity, try_send_func);
return true;
}
/**
* Send entity state method that handles explicit state values.
* Only attempts to build and send the message if the transmit buffer is available.
*
* This method accepts a state parameter to be used instead of the entity's current state.
* It attempts to send the state with the provided value first, and if that fails due to buffer constraints,
* it defers the entity for later processing using the entity-only function.
*
* @tparam EntityT The entity type
* @tparam StateT Type of the state parameter
* @tparam Args Additional argument types (if any)
* @param entity The entity to send state for
* @param try_send_entity_func The function that tries to send the state with entity pointer only
* @param try_send_state_func The function that tries to send the state with entity and state parameters
* @param state The state value to send
* @param args Additional arguments to pass to the try_send_state_func
* @return True on success or message deferred, false if subscription check failed
*/
template<typename EntityT, typename StateT, typename... Args>
bool send_state_with_value_(EntityT *entity, bool (APIConnection::*try_send_entity_func)(EntityT *),
bool (APIConnection::*try_send_state_func)(EntityT *, StateT, Args...), StateT state,
Args... args) {
if (!this->state_subscription_)
return false;
if (this->try_to_clear_buffer(true) && (this->*try_send_state_func)(entity, state, args...)) {
return true;
}
this->deferred_message_queue_.defer(entity, reinterpret_cast<send_message_t>(try_send_entity_func));
return true;
}
/**
* Generic send entity info method to reduce code duplication.
* Only attempts to build and send the message if the transmit buffer is available.
*
* @param entity The entity to send info for
* @param try_send_func The function that tries to send the info
*/
void send_info_(esphome::EntityBase *entity, send_message_t try_send_func) {
if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) {
return;
}
this->deferred_message_queue_.defer(entity, try_send_func);
}
/**
* Generic function for generating entity info response messages.
* This is used to reduce duplication in the try_send_*_info functions.
*
* @param entity The entity to generate info for
* @param response The response object
* @param send_response_func Function pointer to send the response
* @return True if the message was sent successfully
*/
template<typename ResponseT>
bool try_send_entity_info_(esphome::EntityBase *entity, ResponseT &response,
bool (APIServerConnectionBase::*send_response_func)(const ResponseT &)) {
// Set common fields that are shared by all entity types
response.key = entity->get_object_id_hash();
response.object_id = entity->get_object_id();
if (entity->has_own_name())
response.name = entity->get_name();
// Set common EntityBase properties
response.icon = entity->get_icon();
response.disabled_by_default = entity->is_disabled_by_default();
response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
// Send the response using the provided send method
return (this->*send_response_func)(response);
}
bool send_(const void *buf, size_t len, bool force);
enum class ConnectionState {