From 9cdc17566a9d9b96ff37943cb815e4d66703e47e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 15:06:45 -1000 Subject: [PATCH 01/13] [combination] Use FixedVector and parent pointer to enable inline Callback storage (#14947) --- .../components/combination/combination.cpp | 34 +++++++++---------- esphome/components/combination/combination.h | 18 +++++++--- esphome/components/combination/sensor.py | 3 ++ 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/esphome/components/combination/combination.cpp b/esphome/components/combination/combination.cpp index 2f0bd26a02d..b858eee4ee4 100644 --- a/esphome/components/combination/combination.cpp +++ b/esphome/components/combination/combination.cpp @@ -4,8 +4,6 @@ #include "esphome/core/hal.h" #include -#include -#include namespace esphome { namespace combination { @@ -20,12 +18,12 @@ void CombinationComponent::log_config_(const LogString *combo_type) { void CombinationNoParameterComponent::add_source(Sensor *sensor) { this->sensors_.emplace_back(sensor); } -void CombinationOneParameterComponent::add_source(Sensor *sensor, std::function const &stddev) { - this->sensor_pairs_.emplace_back(sensor, stddev); +void CombinationOneParameterComponent::add_source(Sensor *sensor, std::function const &compute) { + this->sensor_sources_.push_back({sensor, compute, this}); } -void CombinationOneParameterComponent::add_source(Sensor *sensor, float stddev) { - this->add_source(sensor, std::function{[stddev](float x) -> float { return stddev; }}); +void CombinationOneParameterComponent::add_source(Sensor *sensor, float value) { + this->add_source(sensor, std::function{[value](float x) -> float { return value; }}); } void CombinationNoParameterComponent::log_source_sensors() { @@ -37,9 +35,8 @@ void CombinationNoParameterComponent::log_source_sensors() { void CombinationOneParameterComponent::log_source_sensors() { ESP_LOGCONFIG(TAG, " Source Sensors:"); - for (const auto &sensor : this->sensor_pairs_) { - auto &entity = *sensor.first; - ESP_LOGCONFIG(TAG, " - %s", entity.get_name().c_str()); + for (const auto &source : this->sensor_sources_) { + ESP_LOGCONFIG(TAG, " - %s", source.sensor->get_name().c_str()); } } @@ -62,9 +59,12 @@ void KalmanCombinationComponent::dump_config() { } void KalmanCombinationComponent::setup() { - for (const auto &sensor : this->sensor_pairs_) { - const auto stddev = sensor.second; - sensor.first->add_on_state_callback([this, stddev](float x) -> void { this->correct_(x, stddev(x)); }); + for (auto &source : this->sensor_sources_) { + // [&source] is safe: source refers to a FixedVector element that never reallocates, + // so the reference remains valid for the component's lifetime. + source.sensor->add_on_state_callback([&source](float x) -> void { + static_cast(source.parent)->correct_(x, source.compute(x)); + }); } } @@ -117,10 +117,10 @@ void KalmanCombinationComponent::correct_(float value, float stddev) { } void LinearCombinationComponent::setup() { - for (const auto &sensor : this->sensor_pairs_) { + for (auto &source : this->sensor_sources_) { // All sensor updates are deferred until the next loop. This avoids publishing the combined sensor's result // repeatedly in the same loop if multiple source senors update. - sensor.first->add_on_state_callback( + source.sensor->add_on_state_callback( [this](float value) -> void { this->defer("update", [this, value]() { this->handle_new_value(value); }); }); } } @@ -133,10 +133,10 @@ void LinearCombinationComponent::handle_new_value(float value) { float sum = 0.0; - for (const auto &sensor : this->sensor_pairs_) { - const float sensor_state = sensor.first->state; + for (const auto &source : this->sensor_sources_) { + const float sensor_state = source.sensor->state; if (std::isfinite(sensor_state)) { - sum += sensor_state * sensor.second(sensor_state); + sum += sensor_state * source.compute(sensor_state); } } diff --git a/esphome/components/combination/combination.h b/esphome/components/combination/combination.h index fb5e156da9a..463eedc5644 100644 --- a/esphome/components/combination/combination.h +++ b/esphome/components/combination/combination.h @@ -1,9 +1,10 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" -#include +#include namespace esphome { namespace combination { @@ -41,14 +42,21 @@ class CombinationNoParameterComponent : public CombinationComponent { // Base class for opertions that require one parameter to compute the combination class CombinationOneParameterComponent : public CombinationComponent { public: - void add_source(Sensor *sensor, std::function const &stddev); - void add_source(Sensor *sensor, float stddev); + void set_source_count(size_t count) { this->sensor_sources_.init(count); } + void add_source(Sensor *sensor, std::function const &compute); + void add_source(Sensor *sensor, float value); - /// @brief Logs all source sensor's names in sensor_pairs_ + /// @brief Logs all source sensors' names in sensor_sources_ void log_source_sensors() override; protected: - std::vector>> sensor_pairs_; + struct SensorSource { + sensor::Sensor *sensor; + std::function compute; + CombinationOneParameterComponent *parent; + }; + + FixedVector sensor_sources_; }; class KalmanCombinationComponent : public CombinationOneParameterComponent { diff --git a/esphome/components/combination/sensor.py b/esphome/components/combination/sensor.py index 0204162e8d9..327cedee1ef 100644 --- a/esphome/components/combination/sensor.py +++ b/esphome/components/combination/sensor.py @@ -180,6 +180,9 @@ async def to_code(config): if proces_std_dev := config.get(CONF_PROCESS_STD_DEV): cg.add(var.set_process_std_dev(proces_std_dev)) + if config[CONF_TYPE] in (CONF_KALMAN, CONF_LINEAR): + cg.add(var.set_source_count(len(config[CONF_SOURCES]))) + for source_conf in config[CONF_SOURCES]: source = await cg.get_variable(source_conf[CONF_SOURCE]) if config[CONF_TYPE] == CONF_KALMAN: From fbe3e7d99c59da1bdbef279cd99501707b2945eb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 15:28:46 -1000 Subject: [PATCH 02/13] [api] Emit raw tag+value writes for forced fixed32 key fields (#15051) --- esphome/components/api/api.proto | 142 ++++++++++---------- esphome/components/api/api_pb2.cpp | 200 ++++++++++++++-------------- esphome/components/api/proto.h | 18 ++- script/api_protobuf/api_protobuf.py | 17 ++- 4 files changed, 202 insertions(+), 175 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 28332d67a59..86daa9a2bf6 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -316,7 +316,7 @@ message ListEntitiesBinarySensorResponse { option (ifdef) = "USE_BINARY_SENSOR"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -334,7 +334,7 @@ message BinarySensorStateResponse { option (ifdef) = "USE_BINARY_SENSOR"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool state = 2; // If the binary sensor does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller @@ -350,7 +350,7 @@ message ListEntitiesCoverResponse { option (ifdef) = "USE_COVER"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -383,7 +383,7 @@ message CoverStateResponse { option (ifdef) = "USE_COVER"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; // legacy: state has been removed in 1.13 // clients/servers must still send/accept it until the next protocol change // Deprecated in API version 1.1 @@ -409,7 +409,7 @@ message CoverCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; // legacy: command has been removed in 1.13 // clients/servers must still send/accept it until the next protocol change @@ -434,7 +434,7 @@ message ListEntitiesFanResponse { option (ifdef) = "USE_FAN"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -466,7 +466,7 @@ message FanStateResponse { option (ifdef) = "USE_FAN"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool state = 2; bool oscillating = 3; // Deprecated in API version 1.6 @@ -483,7 +483,7 @@ message FanCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool has_state = 2; bool state = 3; // Deprecated in API version 1.6 @@ -522,7 +522,7 @@ message ListEntitiesLightResponse { option (ifdef) = "USE_LIGHT"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -551,7 +551,7 @@ message LightStateResponse { option (ifdef) = "USE_LIGHT"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool state = 2; float brightness = 3; ColorMode color_mode = 11; @@ -573,7 +573,7 @@ message LightCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool has_state = 2; bool state = 3; bool has_brightness = 4; @@ -627,7 +627,7 @@ message ListEntitiesSensorResponse { option (ifdef) = "USE_SENSOR"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -651,7 +651,7 @@ message SensorStateResponse { option (ifdef) = "USE_SENSOR"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; float state = 2; // If the sensor does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller @@ -667,7 +667,7 @@ message ListEntitiesSwitchResponse { option (ifdef) = "USE_SWITCH"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -685,7 +685,7 @@ message SwitchStateResponse { option (ifdef) = "USE_SWITCH"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -696,7 +696,7 @@ message SwitchCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -709,7 +709,7 @@ message ListEntitiesTextSensorResponse { option (ifdef) = "USE_TEXT_SENSOR"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -726,7 +726,7 @@ message TextSensorStateResponse { option (ifdef) = "USE_TEXT_SENSOR"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; string state = 2; // If the text sensor does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller @@ -922,7 +922,7 @@ message ListEntitiesServicesResponse { option (ifdef) = "USE_API_USER_DEFINED_ACTIONS"; string name = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; repeated ListEntitiesServicesArgument args = 3 [(fixed_vector) = true]; SupportsResponseType supports_response = 4; } @@ -945,7 +945,7 @@ message ExecuteServiceRequest { option (no_delay) = true; option (ifdef) = "USE_API_USER_DEFINED_ACTIONS"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; repeated ExecuteServiceArgument args = 2 [(fixed_vector) = true]; uint32 call_id = 3 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_RESPONSES"]; bool return_response = 4 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_RESPONSES"]; @@ -972,7 +972,7 @@ message ListEntitiesCameraResponse { option (ifdef) = "USE_CAMERA"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id bool disabled_by_default = 5; @@ -987,7 +987,7 @@ message CameraImageResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_CAMERA"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bytes data = 2; bool done = 3; uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"]; @@ -1057,7 +1057,7 @@ message ListEntitiesClimateResponse { option (ifdef) = "USE_CLIMATE"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -1095,7 +1095,7 @@ message ClimateStateResponse { option (ifdef) = "USE_CLIMATE"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; ClimateMode mode = 2; float current_temperature = 3; float target_temperature = 4; @@ -1121,7 +1121,7 @@ message ClimateCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool has_mode = 2; ClimateMode mode = 3; bool has_target_temperature = 4; @@ -1168,7 +1168,7 @@ message ListEntitiesWaterHeaterResponse { option (ifdef) = "USE_WATER_HEATER"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON"]; bool disabled_by_default = 5; @@ -1189,7 +1189,7 @@ message WaterHeaterStateResponse { option (ifdef) = "USE_WATER_HEATER"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; float current_temperature = 2; float target_temperature = 3; WaterHeaterMode mode = 4; @@ -1219,7 +1219,7 @@ message WaterHeaterCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; // Bitmask of which fields are set (see WaterHeaterCommandHasField) uint32 has_fields = 2; WaterHeaterMode mode = 3; @@ -1244,7 +1244,7 @@ message ListEntitiesNumberResponse { option (ifdef) = "USE_NUMBER"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -1266,7 +1266,7 @@ message NumberStateResponse { option (ifdef) = "USE_NUMBER"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; float state = 2; // If the number does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller @@ -1280,7 +1280,7 @@ message NumberCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; float state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -1293,7 +1293,7 @@ message ListEntitiesSelectResponse { option (ifdef) = "USE_SELECT"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -1310,7 +1310,7 @@ message SelectStateResponse { option (ifdef) = "USE_SELECT"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; string state = 2; // If the select does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller @@ -1324,7 +1324,7 @@ message SelectCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; string state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -1337,7 +1337,7 @@ message ListEntitiesSirenResponse { option (ifdef) = "USE_SIREN"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -1356,7 +1356,7 @@ message SirenStateResponse { option (ifdef) = "USE_SIREN"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -1367,7 +1367,7 @@ message SirenCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool has_state = 2; bool state = 3; bool has_tone = 4; @@ -1400,7 +1400,7 @@ message ListEntitiesLockResponse { option (ifdef) = "USE_LOCK"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -1422,7 +1422,7 @@ message LockStateResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_LOCK"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; LockState state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -1432,7 +1432,7 @@ message LockCommandRequest { option (ifdef) = "USE_LOCK"; option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; LockCommand command = 2; // Not yet implemented: @@ -1449,7 +1449,7 @@ message ListEntitiesButtonResponse { option (ifdef) = "USE_BUTTON"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -1466,7 +1466,7 @@ message ButtonCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; uint32 device_id = 2 [(field_ifdef) = "USE_DEVICES"]; } @@ -1516,7 +1516,7 @@ message ListEntitiesMediaPlayerResponse { option (ifdef) = "USE_MEDIA_PLAYER"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -1538,7 +1538,7 @@ message MediaPlayerStateResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_MEDIA_PLAYER"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; MediaPlayerState state = 2; float volume = 3; bool muted = 4; @@ -1551,7 +1551,7 @@ message MediaPlayerCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool has_command = 2; MediaPlayerCommand command = 3; @@ -2104,7 +2104,7 @@ message ListEntitiesAlarmControlPanelResponse { option (ifdef) = "USE_ALARM_CONTROL_PANEL"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2122,7 +2122,7 @@ message AlarmControlPanelStateResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_ALARM_CONTROL_PANEL"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; AlarmControlPanelState state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -2133,7 +2133,7 @@ message AlarmControlPanelCommandRequest { option (ifdef) = "USE_ALARM_CONTROL_PANEL"; option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; AlarmControlPanelStateCommand command = 2; string code = 3; uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"]; @@ -2151,7 +2151,7 @@ message ListEntitiesTextResponse { option (ifdef) = "USE_TEXT"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"]; @@ -2171,7 +2171,7 @@ message TextStateResponse { option (ifdef) = "USE_TEXT"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; string state = 2; // If the Text does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller @@ -2185,7 +2185,7 @@ message TextCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; string state = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -2199,7 +2199,7 @@ message ListEntitiesDateResponse { option (ifdef) = "USE_DATETIME_DATE"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -2215,7 +2215,7 @@ message DateStateResponse { option (ifdef) = "USE_DATETIME_DATE"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; // If the date does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller bool missing_state = 2; @@ -2231,7 +2231,7 @@ message DateCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; uint32 year = 2; uint32 month = 3; uint32 day = 4; @@ -2246,7 +2246,7 @@ message ListEntitiesTimeResponse { option (ifdef) = "USE_DATETIME_TIME"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -2262,7 +2262,7 @@ message TimeStateResponse { option (ifdef) = "USE_DATETIME_TIME"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; // If the time does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller bool missing_state = 2; @@ -2278,7 +2278,7 @@ message TimeCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; uint32 hour = 2; uint32 minute = 3; uint32 second = 4; @@ -2293,7 +2293,7 @@ message ListEntitiesEventResponse { option (ifdef) = "USE_EVENT"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -2311,7 +2311,7 @@ message EventResponse { option (source) = SOURCE_SERVER; option (ifdef) = "USE_EVENT"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; string event_type = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -2324,7 +2324,7 @@ message ListEntitiesValveResponse { option (ifdef) = "USE_VALVE"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -2351,7 +2351,7 @@ message ValveStateResponse { option (ifdef) = "USE_VALVE"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; float position = 2; ValveOperation current_operation = 3; uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"]; @@ -2364,7 +2364,7 @@ message ValveCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool has_position = 2; float position = 3; bool stop = 4; @@ -2379,7 +2379,7 @@ message ListEntitiesDateTimeResponse { option (ifdef) = "USE_DATETIME_DATETIME"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -2395,7 +2395,7 @@ message DateTimeStateResponse { option (ifdef) = "USE_DATETIME_DATETIME"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; // If the datetime does not have a valid state yet. // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller bool missing_state = 2; @@ -2409,7 +2409,7 @@ message DateTimeCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; fixed32 epoch_seconds = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -2422,7 +2422,7 @@ message ListEntitiesUpdateResponse { option (ifdef) = "USE_UPDATE"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; reserved 4; // Deprecated: was string unique_id @@ -2439,7 +2439,7 @@ message UpdateStateResponse { option (ifdef) = "USE_UPDATE"; option (no_delay) = true; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; bool missing_state = 2; bool in_progress = 3; bool has_progress = 4; @@ -2463,7 +2463,7 @@ message UpdateCommandRequest { option (no_delay) = true; option (base_class) = "CommandProtoMessage"; - fixed32 key = 1; + fixed32 key = 1 [(force) = true]; UpdateCommand command = 2; uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"]; } @@ -2505,7 +2505,7 @@ message ListEntitiesInfraredResponse { option (ifdef) = "USE_INFRARED"; string object_id = 1; - fixed32 key = 2; + fixed32 key = 2 [(force) = true]; string name = 3; string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON"]; bool disabled_by_default = 5; @@ -2521,7 +2521,7 @@ message InfraredRFTransmitRawTimingsRequest { option (ifdef) = "USE_IR_RF"; uint32 device_id = 1 [(field_ifdef) = "USE_DEVICES"]; - fixed32 key = 2; // Key identifying the transmitter instance + fixed32 key = 2 [(force) = true]; // Key identifying the transmitter instance uint32 carrier_frequency = 3; // Carrier frequency in Hz uint32 repeat_count = 4; // Number of times to transmit (1 = once, 2 = twice, etc.) repeated sint32 timings = 5 [packed = true, (packed_buffer) = true]; // Raw timings in microseconds (zigzag-encoded): positive = mark (LED/TX on), negative = space (LED/TX off) @@ -2535,7 +2535,7 @@ message InfraredRFReceiveEvent { option (no_delay) = true; uint32 device_id = 1 [(field_ifdef) = "USE_DEVICES"]; - fixed32 key = 2; // Key identifying the receiver instance + fixed32 key = 2 [(force) = true]; // Key identifying the receiver instance repeated sint32 timings = 3 [packed = true, (container_pointer_no_template) = "std::vector"]; // Raw timings in microseconds (zigzag-encoded): alternating mark/space periods } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 01993cc5e5f..61b034c7eae 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -208,7 +208,7 @@ uint32_t DeviceInfoResponse::calculate_size() const { #ifdef USE_BINARY_SENSOR void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); buffer.encode_string(5, this->device_class); buffer.encode_bool(6, this->is_status_binary_sensor); @@ -224,7 +224,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesBinarySensorResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); size += ProtoSize::calc_length(1, this->device_class.size()); size += ProtoSize::calc_bool(1, this->is_status_binary_sensor); @@ -239,7 +239,7 @@ uint32_t ListEntitiesBinarySensorResponse::calculate_size() const { return size; } void BinarySensorStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->state); buffer.encode_bool(3, this->missing_state); #ifdef USE_DEVICES @@ -248,7 +248,7 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t BinarySensorStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->state); size += ProtoSize::calc_bool(1, this->missing_state); #ifdef USE_DEVICES @@ -260,7 +260,7 @@ uint32_t BinarySensorStateResponse::calculate_size() const { #ifdef USE_COVER void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); buffer.encode_bool(5, this->assumed_state); buffer.encode_bool(6, this->supports_position); @@ -279,7 +279,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesCoverResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); size += ProtoSize::calc_bool(1, this->assumed_state); size += ProtoSize::calc_bool(1, this->supports_position); @@ -297,7 +297,7 @@ uint32_t ListEntitiesCoverResponse::calculate_size() const { return size; } void CoverStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_float(3, this->position); buffer.encode_float(4, this->tilt); buffer.encode_uint32(5, static_cast(this->current_operation)); @@ -307,7 +307,7 @@ void CoverStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t CoverStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_float(1, this->position); size += ProtoSize::calc_float(1, this->tilt); size += ProtoSize::calc_uint32(1, static_cast(this->current_operation)); @@ -357,7 +357,7 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_FAN void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); buffer.encode_bool(5, this->supports_oscillation); buffer.encode_bool(6, this->supports_speed); @@ -378,7 +378,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesFanResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); size += ProtoSize::calc_bool(1, this->supports_oscillation); size += ProtoSize::calc_bool(1, this->supports_speed); @@ -400,7 +400,7 @@ uint32_t ListEntitiesFanResponse::calculate_size() const { return size; } void FanStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->state); buffer.encode_bool(3, this->oscillating); buffer.encode_uint32(5, static_cast(this->direction)); @@ -412,7 +412,7 @@ void FanStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t FanStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->state); size += ProtoSize::calc_bool(1, this->oscillating); size += ProtoSize::calc_uint32(1, static_cast(this->direction)); @@ -487,7 +487,7 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_LIGHT void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); for (const auto &it : *this->supported_color_modes) { buffer.encode_uint32(12, static_cast(it), true); @@ -509,7 +509,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesLightResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); if (!this->supported_color_modes->empty()) { for (const auto &it : *this->supported_color_modes) { @@ -534,7 +534,7 @@ uint32_t ListEntitiesLightResponse::calculate_size() const { return size; } void LightStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->state); buffer.encode_float(3, this->brightness); buffer.encode_uint32(11, static_cast(this->color_mode)); @@ -553,7 +553,7 @@ void LightStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t LightStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->state); size += ProtoSize::calc_float(1, this->brightness); size += ProtoSize::calc_uint32(1, static_cast(this->color_mode)); @@ -683,7 +683,7 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_SENSOR void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -702,7 +702,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesSensorResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -720,7 +720,7 @@ uint32_t ListEntitiesSensorResponse::calculate_size() const { return size; } void SensorStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_float(2, this->state); buffer.encode_bool(3, this->missing_state); #ifdef USE_DEVICES @@ -729,7 +729,7 @@ void SensorStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t SensorStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_float(1, this->state); size += ProtoSize::calc_bool(1, this->missing_state); #ifdef USE_DEVICES @@ -741,7 +741,7 @@ uint32_t SensorStateResponse::calculate_size() const { #ifdef USE_SWITCH void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -757,7 +757,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesSwitchResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -772,7 +772,7 @@ uint32_t ListEntitiesSwitchResponse::calculate_size() const { return size; } void SwitchStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->state); #ifdef USE_DEVICES buffer.encode_uint32(3, this->device_id); @@ -780,7 +780,7 @@ void SwitchStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t SwitchStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->state); #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); @@ -816,7 +816,7 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_TEXT_SENSOR void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -831,7 +831,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesTextSensorResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -845,7 +845,7 @@ uint32_t ListEntitiesTextSensorResponse::calculate_size() const { return size; } void TextSensorStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_string(2, this->state); buffer.encode_bool(3, this->missing_state); #ifdef USE_DEVICES @@ -854,7 +854,7 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t TextSensorStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->state.size()); size += ProtoSize::calc_bool(1, this->missing_state); #ifdef USE_DEVICES @@ -1124,7 +1124,7 @@ uint32_t ListEntitiesServicesArgument::calculate_size() const { } void ListEntitiesServicesResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->name); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); for (auto &it : this->args) { buffer.encode_sub_message(3, it); } @@ -1133,7 +1133,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesServicesResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->name.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; if (!this->args.empty()) { for (const auto &it : this->args) { size += ProtoSize::calc_message_force(1, it.calculate_size()); @@ -1269,7 +1269,7 @@ uint32_t ExecuteServiceResponse::calculate_size() const { #ifdef USE_CAMERA void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); buffer.encode_bool(5, this->disabled_by_default); #ifdef USE_ENTITY_ICON @@ -1283,7 +1283,7 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesCameraResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); size += ProtoSize::calc_bool(1, this->disabled_by_default); #ifdef USE_ENTITY_ICON @@ -1296,7 +1296,7 @@ uint32_t ListEntitiesCameraResponse::calculate_size() const { return size; } void CameraImageResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bytes(2, this->data_ptr_, this->data_len_); buffer.encode_bool(3, this->done); #ifdef USE_DEVICES @@ -1305,7 +1305,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t CameraImageResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->data_len_); size += ProtoSize::calc_bool(1, this->done); #ifdef USE_DEVICES @@ -1330,7 +1330,7 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, proto_varint_value_t v #ifdef USE_CLIMATE void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); buffer.encode_bool(5, this->supports_current_temperature); buffer.encode_bool(6, this->supports_two_point_target_temperature); @@ -1374,7 +1374,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesClimateResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); size += ProtoSize::calc_bool(1, this->supports_current_temperature); size += ProtoSize::calc_bool(1, this->supports_two_point_target_temperature); @@ -1429,7 +1429,7 @@ uint32_t ListEntitiesClimateResponse::calculate_size() const { return size; } void ClimateStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_uint32(2, static_cast(this->mode)); buffer.encode_float(3, this->current_temperature); buffer.encode_float(4, this->target_temperature); @@ -1449,7 +1449,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ClimateStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_uint32(1, static_cast(this->mode)); size += ProtoSize::calc_float(1, this->current_temperature); size += ProtoSize::calc_float(1, this->target_temperature); @@ -1563,7 +1563,7 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_WATER_HEATER void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); @@ -1584,7 +1584,7 @@ void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesWaterHeaterResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -1606,7 +1606,7 @@ uint32_t ListEntitiesWaterHeaterResponse::calculate_size() const { return size; } void WaterHeaterStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_float(2, this->current_temperature); buffer.encode_float(3, this->target_temperature); buffer.encode_uint32(4, static_cast(this->mode)); @@ -1619,7 +1619,7 @@ void WaterHeaterStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t WaterHeaterStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_float(1, this->current_temperature); size += ProtoSize::calc_float(1, this->target_temperature); size += ProtoSize::calc_uint32(1, static_cast(this->mode)); @@ -1675,7 +1675,7 @@ bool WaterHeaterCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value #ifdef USE_NUMBER void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -1695,7 +1695,7 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesNumberResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -1714,7 +1714,7 @@ uint32_t ListEntitiesNumberResponse::calculate_size() const { return size; } void NumberStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_float(2, this->state); buffer.encode_bool(3, this->missing_state); #ifdef USE_DEVICES @@ -1723,7 +1723,7 @@ void NumberStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t NumberStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_float(1, this->state); size += ProtoSize::calc_bool(1, this->missing_state); #ifdef USE_DEVICES @@ -1760,7 +1760,7 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_SELECT void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -1777,7 +1777,7 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesSelectResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -1795,7 +1795,7 @@ uint32_t ListEntitiesSelectResponse::calculate_size() const { return size; } void SelectStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_string(2, this->state); buffer.encode_bool(3, this->missing_state); #ifdef USE_DEVICES @@ -1804,7 +1804,7 @@ void SelectStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t SelectStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->state.size()); size += ProtoSize::calc_bool(1, this->missing_state); #ifdef USE_DEVICES @@ -1849,7 +1849,7 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_SIREN void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -1868,7 +1868,7 @@ void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesSirenResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -1888,7 +1888,7 @@ uint32_t ListEntitiesSirenResponse::calculate_size() const { return size; } void SirenStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->state); #ifdef USE_DEVICES buffer.encode_uint32(3, this->device_id); @@ -1896,7 +1896,7 @@ void SirenStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t SirenStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->state); #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); @@ -1961,7 +1961,7 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_LOCK void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -1979,7 +1979,7 @@ void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesLockResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -1996,7 +1996,7 @@ uint32_t ListEntitiesLockResponse::calculate_size() const { return size; } void LockStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_uint32(2, static_cast(this->state)); #ifdef USE_DEVICES buffer.encode_uint32(3, this->device_id); @@ -2004,7 +2004,7 @@ void LockStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t LockStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_uint32(1, static_cast(this->state)); #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); @@ -2054,7 +2054,7 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_BUTTON void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -2069,7 +2069,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesButtonResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -2124,7 +2124,7 @@ uint32_t MediaPlayerSupportedFormat::calculate_size() const { } void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -2143,7 +2143,7 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesMediaPlayerResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -2163,7 +2163,7 @@ uint32_t ListEntitiesMediaPlayerResponse::calculate_size() const { return size; } void MediaPlayerStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_uint32(2, static_cast(this->state)); buffer.encode_float(3, this->volume); buffer.encode_bool(4, this->muted); @@ -2173,7 +2173,7 @@ void MediaPlayerStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t MediaPlayerStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_uint32(1, static_cast(this->state)); size += ProtoSize::calc_float(1, this->volume); size += ProtoSize::calc_bool(1, this->muted); @@ -2942,7 +2942,7 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt #ifdef USE_ALARM_CONTROL_PANEL void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -2959,7 +2959,7 @@ void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) con uint32_t ListEntitiesAlarmControlPanelResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -2975,7 +2975,7 @@ uint32_t ListEntitiesAlarmControlPanelResponse::calculate_size() const { return size; } void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_uint32(2, static_cast(this->state)); #ifdef USE_DEVICES buffer.encode_uint32(3, this->device_id); @@ -2983,7 +2983,7 @@ void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t AlarmControlPanelStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_uint32(1, static_cast(this->state)); #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); @@ -3030,7 +3030,7 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit #ifdef USE_TEXT void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -3048,7 +3048,7 @@ void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesTextResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3065,7 +3065,7 @@ uint32_t ListEntitiesTextResponse::calculate_size() const { return size; } void TextStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_string(2, this->state); buffer.encode_bool(3, this->missing_state); #ifdef USE_DEVICES @@ -3074,7 +3074,7 @@ void TextStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t TextStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->state.size()); size += ProtoSize::calc_bool(1, this->missing_state); #ifdef USE_DEVICES @@ -3119,7 +3119,7 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_DATETIME_DATE void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -3133,7 +3133,7 @@ void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesDateResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3146,7 +3146,7 @@ uint32_t ListEntitiesDateResponse::calculate_size() const { return size; } void DateStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->missing_state); buffer.encode_uint32(3, this->year); buffer.encode_uint32(4, this->month); @@ -3157,7 +3157,7 @@ void DateStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t DateStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->missing_state); size += ProtoSize::calc_uint32(1, this->year); size += ProtoSize::calc_uint32(1, this->month); @@ -3202,7 +3202,7 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_DATETIME_TIME void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -3216,7 +3216,7 @@ void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesTimeResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3229,7 +3229,7 @@ uint32_t ListEntitiesTimeResponse::calculate_size() const { return size; } void TimeStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->missing_state); buffer.encode_uint32(3, this->hour); buffer.encode_uint32(4, this->minute); @@ -3240,7 +3240,7 @@ void TimeStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t TimeStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->missing_state); size += ProtoSize::calc_uint32(1, this->hour); size += ProtoSize::calc_uint32(1, this->minute); @@ -3285,7 +3285,7 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_EVENT void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -3303,7 +3303,7 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesEventResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3322,7 +3322,7 @@ uint32_t ListEntitiesEventResponse::calculate_size() const { return size; } void EventResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_string(2, this->event_type); #ifdef USE_DEVICES buffer.encode_uint32(3, this->device_id); @@ -3330,7 +3330,7 @@ void EventResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t EventResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->event_type.size()); #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); @@ -3341,7 +3341,7 @@ uint32_t EventResponse::calculate_size() const { #ifdef USE_VALVE void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -3359,7 +3359,7 @@ void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesValveResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3376,7 +3376,7 @@ uint32_t ListEntitiesValveResponse::calculate_size() const { return size; } void ValveStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_float(2, this->position); buffer.encode_uint32(3, static_cast(this->current_operation)); #ifdef USE_DEVICES @@ -3385,7 +3385,7 @@ void ValveStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t ValveStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_float(1, this->position); size += ProtoSize::calc_uint32(1, static_cast(this->current_operation)); #ifdef USE_DEVICES @@ -3428,7 +3428,7 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_DATETIME_DATETIME void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -3442,7 +3442,7 @@ void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesDateTimeResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3455,7 +3455,7 @@ uint32_t ListEntitiesDateTimeResponse::calculate_size() const { return size; } void DateTimeStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->missing_state); buffer.encode_fixed32(3, this->epoch_seconds); #ifdef USE_DEVICES @@ -3464,7 +3464,7 @@ void DateTimeStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t DateTimeStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->missing_state); size += ProtoSize::calc_fixed32(1, this->epoch_seconds); #ifdef USE_DEVICES @@ -3501,7 +3501,7 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { #ifdef USE_UPDATE void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(5, this->icon); @@ -3516,7 +3516,7 @@ void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesUpdateResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3530,7 +3530,7 @@ uint32_t ListEntitiesUpdateResponse::calculate_size() const { return size; } void UpdateStateResponse::encode(ProtoWriteBuffer &buffer) const { - buffer.encode_fixed32(1, this->key); + buffer.write_tag_and_fixed32(13, this->key); buffer.encode_bool(2, this->missing_state); buffer.encode_bool(3, this->in_progress); buffer.encode_bool(4, this->has_progress); @@ -3546,7 +3546,7 @@ void UpdateStateResponse::encode(ProtoWriteBuffer &buffer) const { } uint32_t UpdateStateResponse::calculate_size() const { uint32_t size = 0; - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_bool(1, this->missing_state); size += ProtoSize::calc_bool(1, this->in_progress); size += ProtoSize::calc_bool(1, this->has_progress); @@ -3642,7 +3642,7 @@ uint32_t ZWaveProxyRequest::calculate_size() const { #ifdef USE_INFRARED void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_string(1, this->object_id); - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); buffer.encode_string(3, this->name); #ifdef USE_ENTITY_ICON buffer.encode_string(4, this->icon); @@ -3657,7 +3657,7 @@ void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const { uint32_t ListEntitiesInfraredResponse::calculate_size() const { uint32_t size = 0; size += ProtoSize::calc_length(1, this->object_id.size()); - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; size += ProtoSize::calc_length(1, this->name.size()); #ifdef USE_ENTITY_ICON size += ProtoSize::calc_length(1, this->icon.size()); @@ -3717,7 +3717,7 @@ void InfraredRFReceiveEvent::encode(ProtoWriteBuffer &buffer) const { #ifdef USE_DEVICES buffer.encode_uint32(1, this->device_id); #endif - buffer.encode_fixed32(2, this->key); + buffer.write_tag_and_fixed32(21, this->key); for (const auto &it : *this->timings) { buffer.encode_sint32(3, it, true); } @@ -3727,7 +3727,7 @@ uint32_t InfraredRFReceiveEvent::calculate_size() const { #ifdef USE_DEVICES size += ProtoSize::calc_uint32(1, this->device_id); #endif - size += ProtoSize::calc_fixed32(1, this->key); + size += 5; if (!this->timings->empty()) { for (const auto &it : *this->timings) { size += ProtoSize::calc_sint32_force(1, it); diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index d6e993d3a5a..cd229157030 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -236,6 +236,21 @@ class ProtoWriteBuffer { * Following https://protobuf.dev/programming-guides/encoding/#structure */ void encode_field_raw(uint32_t field_id, uint32_t type) { this->encode_varint_raw((field_id << 3) | type); } + /// Write a precomputed tag byte + 32-bit value in one operation. + /// Tag must be a single-byte varint (< 128). No zero check. + inline void write_tag_and_fixed32(uint8_t tag, uint32_t value) ESPHOME_ALWAYS_INLINE { + this->debug_check_bounds_(5); + this->pos_[0] = tag; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + std::memcpy(this->pos_ + 1, &value, 4); +#else + this->pos_[1] = static_cast(value & 0xFF); + this->pos_[2] = static_cast((value >> 8) & 0xFF); + this->pos_[3] = static_cast((value >> 16) & 0xFF); + this->pos_[4] = static_cast((value >> 24) & 0xFF); +#endif + this->pos_ += 5; + } void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) { if (len == 0 && !force) return; @@ -276,8 +291,7 @@ class ProtoWriteBuffer { this->debug_check_bounds_(1); *this->pos_++ = value ? 0x01 : 0x00; } - // noinline: 51 call sites; inlining causes net code growth vs a single out-of-line copy - __attribute__((noinline)) void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) { + void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) { if (value == 0 && !force) return; diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index dff6c7690a0..aca31e49c8e 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -254,14 +254,17 @@ class TypeInfo(ABC): def dump(self, name: str) -> str: """Dump the value to the output.""" + def calculate_tag(self) -> int: + """Calculate the protobuf tag (field_id << 3 | wire_type).""" + return (self.number << 3) | (self.wire_type & 0b111) + def calculate_field_id_size(self) -> int: """Calculates the size of a field ID in bytes. Returns: The number of bytes needed to encode the field ID """ - # Calculate the tag by combining field_id and wire_type - tag = (self.number << 3) | (self.wire_type & 0b111) + tag = self.calculate_tag() # Calculate the varint size if tag < 128: @@ -556,6 +559,16 @@ class Fixed32Type(TypeInfo): o += "out.append(buffer);" return o + @property + def encode_content(self) -> str: + tag = self.calculate_tag() + if self.force and tag < 128: + # Emit combined tag+value write: precomputed tag + direct memcpy + return f"buffer.write_tag_and_fixed32({tag}, this->{self.field_name});" + if self.force: + return f"buffer.{self.encode_func}({self.number}, this->{self.field_name}, true);" + return f"buffer.{self.encode_func}({self.number}, this->{self.field_name});" + def get_size_calculation(self, name: str, force: bool = False) -> str: field_id_size = self.calculate_field_id_size() if force: From 91944988ef55fdc574b7e0f978f65494180ea5a4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 15:58:31 -1000 Subject: [PATCH 03/13] [binary_sensor] Remove redundant optional state_ from StatefulEntityBase Replace optional state_ in StatefulEntityBase with pure virtual get_state()/set_state_value_() that subclasses implement. BinarySensor stores its value in the existing public bool state member and uses EntityBase::flags_.has_state instead of the optional's discriminant. Move trigger_on_initial_state_ into EntityBase::flags_ (1 bit from reserved), eliminating the standalone bool and its padding. Saves 8 bytes per BinarySensor instance: - optional (2B) + padding (2B) = 4B removed - bool trigger_on_initial_state_ (1B) + padding (3B) = 4B removed --- .../binary_sensor/binary_sensor.cpp | 4 +-- .../components/binary_sensor/binary_sensor.h | 10 ++++--- esphome/core/entity_base.h | 30 +++++++++++-------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index c4d3a29a1e5..35d7b3e430a 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -33,9 +33,7 @@ void BinarySensor::publish_initial_state(bool new_state) { this->publish_state(new_state); } void BinarySensor::send_state_internal(bool new_state) { - // copy the new state to the visible property for backwards compatibility, before any callbacks - this->state = new_state; - // Note that set_new_state_ de-dups and will only trigger callbacks if the state has actually changed + // set_new_state handles de-duplication, updating this->state via set_state_value_(), and triggering callbacks this->set_new_state(new_state); } diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 6ae5d04bcbf..8029ff7fba3 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -32,7 +32,9 @@ void log_binary_sensor(const char *tag, const char *prefix, const char *type, Bi */ class BinarySensor : public StatefulEntityBase { public: - explicit BinarySensor(){}; + explicit BinarySensor() { this->flags_.trigger_on_initial_state = true; } + + const bool &get_state() const override { return this->state; } /** Publish a new state to the front-end. * @@ -59,11 +61,11 @@ class BinarySensor : public StatefulEntityBase { /// Return whether this binary sensor has outputted a state. virtual bool is_status_binary_sensor() const; - // For backward compatibility, provide an accessible property - bool state{}; protected: + void set_state_value_(const bool &value) override { this->state = value; } + #ifdef USE_BINARY_SENSOR_FILTER Filter *filter_list_{nullptr}; #endif @@ -73,7 +75,7 @@ class BinarySensor : public StatefulEntityBase { class BinarySensorInitiallyOff : public BinarySensor { public: - bool has_state() const override { return true; } + BinarySensorInitiallyOff() { this->set_has_state(true); } }; } // namespace esphome::binary_sensor diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 8c1f1a213e5..f3ebeb202d2 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -266,8 +266,9 @@ class EntityBase { uint8_t internal : 1; uint8_t disabled_by_default : 1; uint8_t has_state : 1; - uint8_t entity_category : 2; // Supports up to 4 categories - uint8_t reserved : 2; // Reserved for future use + uint8_t entity_category : 2; // Supports up to 4 categories + uint8_t trigger_on_initial_state : 1; // Whether callbacks fire on first state (StatefulEntityBase) + uint8_t reserved : 1; // Reserved for future use } flags_{}; // String table indices — packed into the 3 padding bytes after flags_ #ifdef USE_ENTITY_DEVICE_CLASS @@ -302,9 +303,8 @@ void log_entity_unit_of_measurement(const char *tag, const char *prefix, const E */ template class StatefulEntityBase : public EntityBase { public: - virtual bool has_state() const { return this->state_.has_value(); } - virtual const T &get_state() const { return this->state_.value(); } // NOLINT(bugprone-unchecked-optional-access) - virtual T get_state_default(T default_value) const { return this->state_.value_or(default_value); } + virtual const T &get_state() const = 0; + T get_state_default(T default_value) const { return this->has_state() ? this->get_state() : default_value; } void invalidate_state() { this->set_new_state({}); } template void add_full_state_callback(F &&callback) { @@ -315,11 +315,10 @@ template class StatefulEntityBase : public EntityBase { } void set_trigger_on_initial_state(bool trigger_on_initial_state) { - this->trigger_on_initial_state_ = trigger_on_initial_state; + this->flags_.trigger_on_initial_state = trigger_on_initial_state; } protected: - optional state_{}; /** * Set a new state for this entity. This will trigger callbacks only if the new state is different from the previous. * @@ -327,20 +326,25 @@ template class StatefulEntityBase : public EntityBase { * @return True if the state was changed, false if it was the same as before. */ virtual bool set_new_state(const optional &new_state) { - if (this->state_ != new_state) { + optional old_state = this->has_state() ? optional(this->get_state()) : nullopt; + if (old_state != new_state) { // call the full state callbacks with the previous and new state - this->full_state_callbacks_.call(this->state_, new_state); + this->full_state_callbacks_.call(old_state, new_state); // trigger legacy callbacks only if the new state is valid and either the trigger on initial state is enabled or // the previous state was valid auto had_state = this->has_state(); - this->state_ = new_state; - if (new_state.has_value() && (this->trigger_on_initial_state_ || had_state)) - this->state_callbacks_.call(new_state.value()); + this->set_has_state(new_state.has_value()); + if (new_state.has_value()) { + this->set_state_value_(new_state.value()); + if (this->flags_.trigger_on_initial_state || had_state) + this->state_callbacks_.call(new_state.value()); + } return true; } return false; } - bool trigger_on_initial_state_{true}; + /// Subclasses implement this to store the actual value. + virtual void set_state_value_(const T &value) = 0; LazyCallbackManager previous, optional current)> full_state_callbacks_; LazyCallbackManager state_callbacks_; }; From 7754e517f20b6a3b094abf7325005726cdfe4f1c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 15:59:33 -1000 Subject: [PATCH 04/13] [binary_sensor] Add documentation comments to StatefulEntityBase --- esphome/core/entity_base.h | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index f3ebeb202d2..a3cbe84b13a 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -297,14 +297,29 @@ void log_entity_device_class(const char *tag, const char *prefix, const EntityBa #define LOG_ENTITY_UNIT_OF_MEASUREMENT(tag, prefix, obj) log_entity_unit_of_measurement(tag, prefix, obj) void log_entity_unit_of_measurement(const char *tag, const char *prefix, const EntityBase &obj); -/** - * An entity that has a state. - * @tparam T The type of the state +/** Base class for entities that track a typed state value with change-detection and callbacks. + * + * Subclasses must implement: + * - get_state(): return a const reference to the current value + * - set_state_value_(): store a new value (called only when the state actually changes) + * + * State tracking uses EntityBase::flags_.has_state (not optional) to avoid redundant storage. + * Subclasses own their storage — this class never allocates memory for the state value itself. + * + * Callback behavior: + * - full_state_callbacks_: fired on every change, receives optional previous and current + * - state_callbacks_: fired only when the new state has a value, and either this is not the + * first state (had_state) or trigger_on_initial_state is set + * + * @tparam T The type of the state value */ template class StatefulEntityBase : public EntityBase { public: + /// Return the current state value. Only valid when has_state() is true. virtual const T &get_state() const = 0; + /// Return the current state if available, otherwise return the provided default. T get_state_default(T default_value) const { return this->has_state() ? this->get_state() : default_value; } + /// Clear the state — sets has_state() to false and fires callbacks with nullopt. void invalidate_state() { this->set_new_state({}); } template void add_full_state_callback(F &&callback) { @@ -314,24 +329,21 @@ template class StatefulEntityBase : public EntityBase { this->state_callbacks_.add(std::forward(callback)); } + /// Control whether state_callbacks_ fire on the very first state (before any previous state exists). void set_trigger_on_initial_state(bool trigger_on_initial_state) { this->flags_.trigger_on_initial_state = trigger_on_initial_state; } protected: - /** - * Set a new state for this entity. This will trigger callbacks only if the new state is different from the previous. + /** Apply a new state, de-duplicating and firing callbacks as needed. * - * @param new_state The new state. - * @return True if the state was changed, false if it was the same as before. + * Pass nullopt to invalidate (clear) the state. Pass a value to set it. + * Returns true if the state actually changed, false if it was the same. */ virtual bool set_new_state(const optional &new_state) { optional old_state = this->has_state() ? optional(this->get_state()) : nullopt; if (old_state != new_state) { - // call the full state callbacks with the previous and new state this->full_state_callbacks_.call(old_state, new_state); - // trigger legacy callbacks only if the new state is valid and either the trigger on initial state is enabled or - // the previous state was valid auto had_state = this->has_state(); this->set_has_state(new_state.has_value()); if (new_state.has_value()) { @@ -343,7 +355,7 @@ template class StatefulEntityBase : public EntityBase { } return false; } - /// Subclasses implement this to store the actual value. + /// Subclasses implement this to store the actual value into their own storage. virtual void set_state_value_(const T &value) = 0; LazyCallbackManager previous, optional current)> full_state_callbacks_; LazyCallbackManager state_callbacks_; From 98b0aea6bf9376a203ecfc37263a9e1a23f30ad8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:00:07 -1000 Subject: [PATCH 05/13] [binary_sensor] Restore comment on state member --- esphome/components/binary_sensor/binary_sensor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 8029ff7fba3..e065088a371 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -61,6 +61,7 @@ class BinarySensor : public StatefulEntityBase { /// Return whether this binary sensor has outputted a state. virtual bool is_status_binary_sensor() const; + /// The current state of this binary sensor. Also used as the backing storage for StatefulEntityBase. bool state{}; protected: From e406fb25cbe2a2992dbd5ee63e4d5f93121a9ef6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:01:39 -1000 Subject: [PATCH 06/13] [binary_sensor] Move trigger_on_initial_state out of EntityBase flags into BinarySensor Use pure virtual get/set methods on StatefulEntityBase instead of storing the flag in EntityBase::flags_. This keeps EntityBase clean and avoids mixing binary_sensor-specific state into the base entity. --- esphome/components/binary_sensor/binary_sensor.h | 5 ++++- esphome/core/entity_base.h | 15 ++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index e065088a371..6263844f7fd 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -32,9 +32,10 @@ void log_binary_sensor(const char *tag, const char *prefix, const char *type, Bi */ class BinarySensor : public StatefulEntityBase { public: - explicit BinarySensor() { this->flags_.trigger_on_initial_state = true; } + explicit BinarySensor() = default; const bool &get_state() const override { return this->state; } + void set_trigger_on_initial_state(bool value) override { this->trigger_on_initial_state_ = value; } /** Publish a new state to the front-end. * @@ -65,8 +66,10 @@ class BinarySensor : public StatefulEntityBase { bool state{}; protected: + bool get_trigger_on_initial_state() const override { return this->trigger_on_initial_state_; } void set_state_value_(const bool &value) override { this->state = value; } + bool trigger_on_initial_state_{true}; #ifdef USE_BINARY_SENSOR_FILTER Filter *filter_list_{nullptr}; #endif diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index a3cbe84b13a..d638434d492 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -266,9 +266,8 @@ class EntityBase { uint8_t internal : 1; uint8_t disabled_by_default : 1; uint8_t has_state : 1; - uint8_t entity_category : 2; // Supports up to 4 categories - uint8_t trigger_on_initial_state : 1; // Whether callbacks fire on first state (StatefulEntityBase) - uint8_t reserved : 1; // Reserved for future use + uint8_t entity_category : 2; // Supports up to 4 categories + uint8_t reserved : 2; // Reserved for future use } flags_{}; // String table indices — packed into the 3 padding bytes after flags_ #ifdef USE_ENTITY_DEVICE_CLASS @@ -330,11 +329,13 @@ template class StatefulEntityBase : public EntityBase { } /// Control whether state_callbacks_ fire on the very first state (before any previous state exists). - void set_trigger_on_initial_state(bool trigger_on_initial_state) { - this->flags_.trigger_on_initial_state = trigger_on_initial_state; - } + /// Subclasses must implement set_trigger_on_initial_state() to store this value. + virtual void set_trigger_on_initial_state(bool value) = 0; protected: + /// Subclasses return whether callbacks should fire on the very first state. + virtual bool get_trigger_on_initial_state() const = 0; + /** Apply a new state, de-duplicating and firing callbacks as needed. * * Pass nullopt to invalidate (clear) the state. Pass a value to set it. @@ -348,7 +349,7 @@ template class StatefulEntityBase : public EntityBase { this->set_has_state(new_state.has_value()); if (new_state.has_value()) { this->set_state_value_(new_state.value()); - if (this->flags_.trigger_on_initial_state || had_state) + if (this->get_trigger_on_initial_state() || had_state) this->state_callbacks_.call(new_state.value()); } return true; From 047a744ed387a9e4d5f702fca83ae48e5d38b524 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:02:51 -1000 Subject: [PATCH 07/13] [binary_sensor] Inline send_state_internal into header --- esphome/components/binary_sensor/binary_sensor.cpp | 4 ---- esphome/components/binary_sensor/binary_sensor.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 35d7b3e430a..3ccaaf8f035 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -32,10 +32,6 @@ void BinarySensor::publish_initial_state(bool new_state) { this->invalidate_state(); this->publish_state(new_state); } -void BinarySensor::send_state_internal(bool new_state) { - // set_new_state handles de-duplication, updating this->state via set_state_value_(), and triggering callbacks - this->set_new_state(new_state); -} bool BinarySensor::set_new_state(const optional &new_state) { if (StatefulEntityBase::set_new_state(new_state)) { diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 6263844f7fd..6e6f77e010f 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -57,7 +57,7 @@ class BinarySensor : public StatefulEntityBase { // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - void send_state_internal(bool new_state); + void send_state_internal(bool new_state) { this->set_new_state(new_state); } /// Return whether this binary sensor has outputted a state. virtual bool is_status_binary_sensor() const; From fc1186895c23526f2df7d8113e2525ce2b2354b2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:03:44 -1000 Subject: [PATCH 08/13] [binary_sensor] Fix stale comment in StatefulEntityBase docs --- esphome/core/entity_base.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index d638434d492..e3a1891da6c 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -301,9 +301,10 @@ void log_entity_unit_of_measurement(const char *tag, const char *prefix, const E * Subclasses must implement: * - get_state(): return a const reference to the current value * - set_state_value_(): store a new value (called only when the state actually changes) + * - get_trigger_on_initial_state() / set_trigger_on_initial_state(): control initial callback behavior * - * State tracking uses EntityBase::flags_.has_state (not optional) to avoid redundant storage. - * Subclasses own their storage — this class never allocates memory for the state value itself. + * This class does not store the state value — subclasses own their storage. Whether a state + * has been set is tracked by EntityBase::has_state(). * * Callback behavior: * - full_state_callbacks_: fired on every change, receives optional previous and current From db3cfaa3fc6d2f48958ce6b639cbcce5e6ed5741 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:11:06 -1000 Subject: [PATCH 09/13] [binary_sensor] Reduce flash: un-inline send_state_internal, optimize set_new_state Move send_state_internal back to .cpp to avoid duplicating it at each call site (publish_state and Filter::output). Optimize set_new_state to compare has_state + value directly instead of constructing optional on stack for the comparison. Optionals are only constructed in the changed path for callbacks that need them. --- .../binary_sensor/binary_sensor.cpp | 1 + .../components/binary_sensor/binary_sensor.h | 2 +- esphome/core/entity_base.h | 30 +++++++++++-------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 3ccaaf8f035..7f46291e2d7 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -32,6 +32,7 @@ void BinarySensor::publish_initial_state(bool new_state) { this->invalidate_state(); this->publish_state(new_state); } +void BinarySensor::send_state_internal(bool new_state) { this->set_new_state(new_state); } bool BinarySensor::set_new_state(const optional &new_state) { if (StatefulEntityBase::set_new_state(new_state)) { diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 6e6f77e010f..6263844f7fd 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -57,7 +57,7 @@ class BinarySensor : public StatefulEntityBase { // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - void send_state_internal(bool new_state) { this->set_new_state(new_state); } + void send_state_internal(bool new_state); /// Return whether this binary sensor has outputted a state. virtual bool is_status_binary_sensor() const; diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index e3a1891da6c..c3ab0c55f52 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -343,19 +343,25 @@ template class StatefulEntityBase : public EntityBase { * Returns true if the state actually changed, false if it was the same. */ virtual bool set_new_state(const optional &new_state) { - optional old_state = this->has_state() ? optional(this->get_state()) : nullopt; - if (old_state != new_state) { - this->full_state_callbacks_.call(old_state, new_state); - auto had_state = this->has_state(); - this->set_has_state(new_state.has_value()); - if (new_state.has_value()) { - this->set_state_value_(new_state.value()); - if (this->get_trigger_on_initial_state() || had_state) - this->state_callbacks_.call(new_state.value()); - } - return true; + // Compare without constructing optional — avoid unnecessary codegen + bool had_state = this->has_state(); + if (new_state.has_value()) { + if (had_state && this->get_state() == new_state.value()) + return false; // same value, no change + } else { + if (!had_state) + return false; // already invalidated, no change } - return false; + // State changed — construct optionals only for callbacks that need them + optional old_state = had_state ? optional(this->get_state()) : nullopt; + this->full_state_callbacks_.call(old_state, new_state); + this->set_has_state(new_state.has_value()); + if (new_state.has_value()) { + this->set_state_value_(new_state.value()); + if (this->get_trigger_on_initial_state() || had_state) + this->state_callbacks_.call(new_state.value()); + } + return true; } /// Subclasses implement this to store the actual value into their own storage. virtual void set_state_value_(const T &value) = 0; From 1e2c5fbc4e2dec9d86912ee81353a52986c58375 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:13:35 -1000 Subject: [PATCH 10/13] [binary_sensor] Update state before firing callbacks in set_new_state Move set_has_state and set_state_value_ before callback invocations so callback code can inspect the entity's current state via get_state()/has_state(). The old state is passed as a parameter. Addresses Copilot review feedback. --- esphome/core/entity_base.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index c3ab0c55f52..63ebaca62f0 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -352,15 +352,16 @@ template class StatefulEntityBase : public EntityBase { if (!had_state) return false; // already invalidated, no change } - // State changed — construct optionals only for callbacks that need them + // State changed — capture old state, then update storage before firing callbacks + // so callback code can inspect the entity's current state via get_state()/has_state() optional old_state = had_state ? optional(this->get_state()) : nullopt; - this->full_state_callbacks_.call(old_state, new_state); this->set_has_state(new_state.has_value()); if (new_state.has_value()) { this->set_state_value_(new_state.value()); - if (this->get_trigger_on_initial_state() || had_state) - this->state_callbacks_.call(new_state.value()); } + this->full_state_callbacks_.call(old_state, new_state); + if (new_state.has_value() && (this->get_trigger_on_initial_state() || had_state)) + this->state_callbacks_.call(new_state.value()); return true; } /// Subclasses implement this to store the actual value into their own storage. From 2833401cc122929fe737e30341195a1da84641c1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:15:24 -1000 Subject: [PATCH 11/13] [binary_sensor] Simplify else-if in set_new_state --- esphome/core/entity_base.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 63ebaca62f0..e542d60e65e 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -348,9 +348,8 @@ template class StatefulEntityBase : public EntityBase { if (new_state.has_value()) { if (had_state && this->get_state() == new_state.value()) return false; // same value, no change - } else { - if (!had_state) - return false; // already invalidated, no change + } else if (!had_state) { + return false; // already invalidated, no change } // State changed — capture old state, then update storage before firing callbacks // so callback code can inspect the entity's current state via get_state()/has_state() From e28c9d191c01a468d8675ff728e6246d59b8eb19 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:16:59 -1000 Subject: [PATCH 12/13] [binary_sensor] Move send_state_internal to header for devirtualization --- esphome/components/binary_sensor/binary_sensor.cpp | 1 - esphome/components/binary_sensor/binary_sensor.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 7f46291e2d7..3ccaaf8f035 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -32,7 +32,6 @@ void BinarySensor::publish_initial_state(bool new_state) { this->invalidate_state(); this->publish_state(new_state); } -void BinarySensor::send_state_internal(bool new_state) { this->set_new_state(new_state); } bool BinarySensor::set_new_state(const optional &new_state) { if (StatefulEntityBase::set_new_state(new_state)) { diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 6263844f7fd..6e6f77e010f 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -57,7 +57,7 @@ class BinarySensor : public StatefulEntityBase { // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - void send_state_internal(bool new_state); + void send_state_internal(bool new_state) { this->set_new_state(new_state); } /// Return whether this binary sensor has outputted a state. virtual bool is_status_binary_sensor() const; From 5854fa1d8235349915b57d91fbdb3c008d5107e9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 16:17:35 -1000 Subject: [PATCH 13/13] [binary_sensor] Access flags_ directly in set_new_state to reduce flash --- esphome/core/entity_base.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index e542d60e65e..d19ef558ecb 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -343,8 +343,8 @@ template class StatefulEntityBase : public EntityBase { * Returns true if the state actually changed, false if it was the same. */ virtual bool set_new_state(const optional &new_state) { - // Compare without constructing optional — avoid unnecessary codegen - bool had_state = this->has_state(); + // Access flags_ directly to avoid virtual/function call overhead in this hot path + bool had_state = this->flags_.has_state; if (new_state.has_value()) { if (had_state && this->get_state() == new_state.value()) return false; // same value, no change @@ -354,7 +354,7 @@ template class StatefulEntityBase : public EntityBase { // State changed — capture old state, then update storage before firing callbacks // so callback code can inspect the entity's current state via get_state()/has_state() optional old_state = had_state ? optional(this->get_state()) : nullopt; - this->set_has_state(new_state.has_value()); + this->flags_.has_state = new_state.has_value(); if (new_state.has_value()) { this->set_state_value_(new_state.value()); }