diff --git a/esphome/analyze_memory/__init__.py b/esphome/analyze_memory/__init__.py index 48ecf2c1dc3..f56d720ec2a 100644 --- a/esphome/analyze_memory/__init__.py +++ b/esphome/analyze_memory/__init__.py @@ -56,6 +56,10 @@ _COMPONENT_PREFIX_LIB = "[lib]" _COMPONENT_CORE = f"{_COMPONENT_PREFIX_ESPHOME}core" _COMPONENT_API = f"{_COMPONENT_PREFIX_ESPHOME}api" +# Placement new storage suffix (generated by codegen Pvariable) +_PSTORAGE_SUFFIX = "__pstorage" + + # C++ namespace prefixes _NAMESPACE_ESPHOME = "esphome::" _NAMESPACE_STD = "std::" @@ -332,6 +336,13 @@ class MemoryAnalyzer: # Demangle C++ names if needed demangled = self._demangle_symbol(symbol_name) + # Check for placement new storage symbols (generated by codegen) + # Format: {component}__{id}__pstorage + if demangled.endswith(_PSTORAGE_SUFFIX) and ( + component := self._match_pstorage_component(demangled) + ): + return component + # Check for special component classes first (before namespace pattern) # This handles cases like esphome::ESPHomeOTAComponent which should map to ota if _NAMESPACE_ESPHOME in demangled: @@ -399,6 +410,24 @@ class MemoryAnalyzer: # Track uncategorized symbols for analysis return "other" + def _match_pstorage_component(self, symbol_name: str) -> str | None: + """Match a __pstorage symbol to its ESPHome component. + + Symbol format: {component}__{id}__pstorage + The component namespace is embedded by codegen before the double underscore. + """ + prefix = symbol_name[: -len(_PSTORAGE_SUFFIX)] + # Extract component namespace before the first double underscore + dunder_pos = prefix.find("__") + if dunder_pos == -1: + return None + component_name = prefix[:dunder_pos] + if component_name in get_esphome_components(): + return f"{_COMPONENT_PREFIX_ESPHOME}{component_name}" + if component_name in self.external_components: + return f"{_COMPONENT_PREFIX_EXTERNAL}{component_name}" + return None + def _batch_demangle_symbols(self, symbols: list[str]) -> None: """Batch demangle C++ symbol names for efficiency.""" if not symbols: diff --git a/esphome/analyze_memory/cli.py b/esphome/analyze_memory/cli.py index acaf5f45621..b7561e8ffc0 100644 --- a/esphome/analyze_memory/cli.py +++ b/esphome/analyze_memory/cli.py @@ -15,6 +15,7 @@ from . import ( _COMPONENT_PREFIX_ESPHOME, _COMPONENT_PREFIX_EXTERNAL, _COMPONENT_PREFIX_LIB, + _PSTORAGE_SUFFIX, RAM_SECTIONS, MemoryAnalyzer, ) @@ -23,6 +24,17 @@ if TYPE_CHECKING: from . import ComponentMemory +def _format_pstorage_name(name: str) -> str: + """Format a __pstorage symbol as 'storage for {id}'.""" + if not name.endswith(_PSTORAGE_SUFFIX): + return name + prefix = name[: -len(_PSTORAGE_SUFFIX)] + # Strip component namespace prefix: {component}__{id} -> {id} + dunder_pos = prefix.find("__") + var_id = prefix[dunder_pos + 2 :] if dunder_pos != -1 else prefix + return f"storage for {var_id}" + + class MemoryAnalyzerCLI(MemoryAnalyzer): """Memory analyzer with CLI-specific report generation.""" @@ -148,11 +160,14 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): If section is one of the RAM sections (.data or .bss), a label like " [data]" or " [bss]" is appended. For non-RAM sections or when section is None, no section label is added. + + Placement new storage symbols are formatted as "storage for {id}". """ + display_name = _format_pstorage_name(demangled) section_label = "" if section in RAM_SECTIONS: section_label = f" [{section[1:]}]" # .data -> [data], .bss -> [bss] - return f"{demangled} ({size:,} B){section_label}" + return f"{display_name} ({size:,} B){section_label}" def _add_top_symbols(self, lines: list[str]) -> None: """Add a section showing the top largest symbols in the binary.""" @@ -175,11 +190,13 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): for i, (_, demangled, size, section, component) in enumerate(top_symbols): # Format section label section_label = f"[{section[1:]}]" if section else "" - # Truncate demangled name if too long + # Format storage symbols readably + display_name = _format_pstorage_name(demangled) + # Truncate if too long demangled_display = ( - f"{demangled[:truncate_limit]}..." - if len(demangled) > self.COL_TOP_SYMBOL_NAME - else demangled + f"{display_name[:truncate_limit]}..." + if len(display_name) > self.COL_TOP_SYMBOL_NAME + else display_name ) lines.append( f"{i + 1:>2}. {size:>7,} B {section_label:<8} {demangled_display:<{self.COL_TOP_SYMBOL_NAME}} {component}" @@ -573,15 +590,16 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): lines.append(f"Total size: {comp_mem.flash_total:,} B") lines.append("") - # Show all symbols above threshold for better visibility + # Show symbols above threshold, always include storage symbols large_symbols = [ (sym, dem, size, sec) for sym, dem, size, sec in sorted_symbols if size > self.SYMBOL_SIZE_THRESHOLD + or dem.endswith(_PSTORAGE_SUFFIX) ] lines.append( - f"{comp_name} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_symbols)} symbols):" + f"{comp_name} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B & storage ({len(large_symbols)} symbols):" ) for i, (symbol, demangled, size, section) in enumerate(large_symbols): lines.append( @@ -604,7 +622,10 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): # Sort by size descending sorted_ram_syms = sorted(ram_syms, key=lambda x: x[2], reverse=True) large_ram_syms = [ - s for s in sorted_ram_syms if s[2] > self.RAM_SYMBOL_SIZE_THRESHOLD + s + for s in sorted_ram_syms + if s[2] > self.RAM_SYMBOL_SIZE_THRESHOLD + or s[1].endswith(_PSTORAGE_SUFFIX) ] lines.append(f"{name} ({mem.ram_total:,} B total RAM):") @@ -622,13 +643,14 @@ class MemoryAnalyzerCLI(MemoryAnalyzer): for symbol, demangled, size, section in large_ram_syms[:10]: # Format section label consistently by stripping leading dot section_label = section.lstrip(".") if section else "" + display_name = _format_pstorage_name(demangled) # Add ellipsis if name is truncated - demangled_display = ( - f"{demangled[:70]}..." if len(demangled) > 70 else demangled - ) - lines.append( - f" {size:>6,} B [{section_label}] {demangled_display}" + display_name = ( + f"{display_name[:70]}..." + if len(display_name) > 70 + else display_name ) + lines.append(f" {size:>6,} B [{section_label}] {display_name}") if len(large_ram_syms) > 10: lines.append(f" ... and {len(large_ram_syms) - 10} more") lines.append("") 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_connection.cpp b/esphome/components/api/api_connection.cpp index 7b90d883cc6..d023cd21a8d 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1519,16 +1519,16 @@ void APIConnection::on_serial_proxy_request(const SerialProxyRequest &msg) { resp.instance = msg.instance; resp.type = enums::SERIAL_PROXY_REQUEST_TYPE_FLUSH; switch (proxies[msg.instance]->flush_port()) { - case uart::FlushResult::SUCCESS: + case uart::UARTFlushResult::UART_FLUSH_RESULT_SUCCESS: resp.status = enums::SERIAL_PROXY_STATUS_OK; break; - case uart::FlushResult::ASSUMED_SUCCESS: + case uart::UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS: resp.status = enums::SERIAL_PROXY_STATUS_ASSUMED_SUCCESS; break; - case uart::FlushResult::TIMEOUT: + case uart::UARTFlushResult::UART_FLUSH_RESULT_TIMEOUT: resp.status = enums::SERIAL_PROXY_STATUS_TIMEOUT; break; - case uart::FlushResult::FAILED: + case uart::UARTFlushResult::UART_FLUSH_RESULT_FAILED: resp.status = enums::SERIAL_PROXY_STATUS_ERROR; break; } 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 a63f53adb42..d6f9c947d71 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -228,6 +228,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; @@ -268,8 +283,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/esphome/components/ble_nus/ble_nus.cpp b/esphome/components/ble_nus/ble_nus.cpp index 2f60f814718..71d98332e09 100644 --- a/esphome/components/ble_nus/ble_nus.cpp +++ b/esphome/components/ble_nus/ble_nus.cpp @@ -103,17 +103,17 @@ size_t BLENUS::available() { #endif } -uart::FlushResult BLENUS::flush() { +uart::UARTFlushResult BLENUS::flush() { constexpr uint32_t timeout_500ms = 500; uint32_t start = millis(); while (atomic_get(&this->tx_status_) != TX_DISABLED && !ring_buf_is_empty(&global_ble_tx_ring_buf)) { if (millis() - start > timeout_500ms) { ESP_LOGW(TAG, "Flush timeout"); - return uart::FlushResult::TIMEOUT; + return uart::UARTFlushResult::UART_FLUSH_RESULT_TIMEOUT; } delay(1); } - return uart::FlushResult::SUCCESS; + return uart::UARTFlushResult::UART_FLUSH_RESULT_SUCCESS; } void BLENUS::connected(bt_conn *conn, uint8_t err) { diff --git a/esphome/components/ble_nus/ble_nus.h b/esphome/components/ble_nus/ble_nus.h index b482c240e53..f1afd54af9e 100644 --- a/esphome/components/ble_nus/ble_nus.h +++ b/esphome/components/ble_nus/ble_nus.h @@ -26,7 +26,7 @@ class BLENUS : public uart::UARTComponent, public Component { bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; size_t available() override; - uart::FlushResult flush() override; + uart::UARTFlushResult flush() override; void check_logger_conflict() override {} void set_expose_log(bool expose_log) { this->expose_log_ = expose_log; } #ifdef USE_LOGGER 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: diff --git a/esphome/components/esp32/iram_fix.py.script b/esphome/components/esp32/iram_fix.py.script index 0d23f9a81ba..b656d7d1a6f 100644 --- a/esphome/components/esp32/iram_fix.py.script +++ b/esphome/components/esp32/iram_fix.py.script @@ -4,12 +4,40 @@ import re # pylint: disable=E0602 Import("env") # noqa -# IRAM size for testing mode (2MB - large enough to accommodate grouped tests) -TESTING_IRAM_SIZE = 0x200000 +# Memory sizes for testing mode (large enough to accommodate grouped tests) +TESTING_IRAM_SIZE = 0x200000 # 2MB +TESTING_DRAM_SIZE = 0x200000 # 2MB + + +def patch_segment(content, segment_name, new_size): + """Patch a memory segment's length in linker script content. + + Handles both single-line and multi-line segment definitions, e.g.: + iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 + 0x0 + or split across lines: + dram0_0_seg (RW) : org = 0x3FFB0000 + 0xdb5c, + len = 0x2c200 - 0xdb5c + + Args: + content: Full linker script content as string + segment_name: Name of the segment (e.g., 'iram0_0_seg') + new_size: New size as integer + + Returns: + Tuple of (new_content, was_patched) + """ + # Match segment name through to "len = " allowing newlines between org and len + pattern = rf'({re.escape(segment_name)}\s*\([^)]*\)\s*:\s*org\s*=\s*.+?,\s*len\s*=\s*)(\S+[^\n]*)' + if match := re.search(pattern, content, re.DOTALL): + replacement = f"{match.group(1)}{new_size:#x}" + new_content = content[:match.start()] + replacement + content[match.end():] + if new_content != content: + return new_content, True + return content, False def patch_idf_linker_script(source, target, env): - """Patch ESP-IDF linker script to increase IRAM size for testing mode.""" + """Patch ESP-IDF linker script to increase IRAM and DRAM size for testing mode.""" # Check if we're in testing mode by looking for the define build_flags = env.get("BUILD_FLAGS", []) testing_mode = any("-DESPHOME_TESTING_MODE" in flag for flag in build_flags) @@ -34,36 +62,22 @@ def patch_idf_linker_script(source, target, env): print(f"ESPHome: Error reading linker script: {e}") return - # Check if this file contains iram0_0_seg - if 'iram0_0_seg' not in content: - print(f"ESPHome: Warning - iram0_0_seg not found in {memory_ld}") - return + patches = [] - # Look for iram0_0_seg definition and increase its length - # ESP-IDF format can be: - # iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 + 0x0 - # or more complex with nested parentheses: - # iram0_0_seg (RX) : org = (0x40370000 + 0x4000), len = (((0x403CB700 - (0x40378000 - 0x3FC88000)) - 0x3FC88000) + 0x8000 - 0x4000) - # We want to change len to TESTING_IRAM_SIZE for testing + content, patched = patch_segment(content, 'iram0_0_seg', TESTING_IRAM_SIZE) + if patched: + patches.append(f"IRAM={TESTING_IRAM_SIZE:#x}") - # Use a more robust approach: find the line and manually parse it - lines = content.split('\n') - for i, line in enumerate(lines): - if 'iram0_0_seg' in line and 'len' in line: - # Find the position of "len = " and replace everything after it until the end of the statement - match = re.search(r'(iram0_0_seg\s*\([^)]*\)\s*:\s*org\s*=\s*(?:\([^)]+\)|0x[0-9a-fA-F]+)\s*,\s*len\s*=\s*)(.+?)(\s*)$', line) - if match: - lines[i] = f"{match.group(1)}{TESTING_IRAM_SIZE:#x}{match.group(3)}" - break + content, patched = patch_segment(content, 'dram0_0_seg', TESTING_DRAM_SIZE) + if patched: + patches.append(f"DRAM={TESTING_DRAM_SIZE:#x}") - updated = '\n'.join(lines) - - if updated != content: + if patches: with open(memory_ld, "w") as f: - f.write(updated) - print(f"ESPHome: Patched IRAM size to {TESTING_IRAM_SIZE:#x} in {memory_ld} for testing mode") + f.write(content) + print(f"ESPHome: Patched {', '.join(patches)} in {memory_ld} for testing mode") else: - print(f"ESPHome: Warning - could not patch iram0_0_seg in {memory_ld}") + print(f"ESPHome: Warning - could not patch memory segments in {memory_ld}") # Hook into the build process before linking diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 4421a1c7aaf..42cb0b3cfc0 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -18,12 +18,6 @@ void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } #endif -// set_use_address() is guaranteed to be called during component setup by Python code generation, -// so use_address_ will always be valid when get_use_address() is called - no fallback needed. -const char *EthernetComponent::get_use_address() const { return this->use_address_; } - -void EthernetComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; } - #ifdef USE_ETHERNET_IP_STATE_LISTENERS void EthernetComponent::notify_ip_state_listeners_() { auto ips = this->get_ip_addresses(); diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 88a86bc0432..b6699e80204 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -103,8 +103,8 @@ class EthernetComponent final : public Component { network::IPAddresses get_ip_addresses(); network::IPAddress get_dns_address(uint8_t num); - const char *get_use_address() const; - void set_use_address(const char *use_address); + const char *get_use_address() const { return this->use_address_; } + void set_use_address(const char *use_address) { this->use_address_ = use_address; } void get_eth_mac_address_raw(uint8_t *mac); // Remove before 2026.9.0 ESPDEPRECATED("Use get_eth_mac_address_pretty_into_buffer() instead. Removed in 2026.9.0", "2026.3.0") diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 0d52d98d2fa..14473f737a0 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -366,14 +366,14 @@ class MQTTJsonMessageTrigger : public Trigger { class MQTTConnectTrigger : public Trigger { public: - explicit MQTTConnectTrigger(MQTTClientComponent *&client) { + explicit MQTTConnectTrigger(MQTTClientComponent *client) { client->set_on_connect([this](bool session_present) { this->trigger(session_present); }); } }; class MQTTDisconnectTrigger : public Trigger { public: - explicit MQTTDisconnectTrigger(MQTTClientComponent *&client) { + explicit MQTTDisconnectTrigger(MQTTClientComponent *client) { client->set_on_disconnect([this](MQTTClientDisconnectReason reason) { this->trigger(reason); }); } }; diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index 226b11b8cd3..79ddd3844c5 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -42,29 +42,5 @@ network::IPAddresses get_ip_addresses() { return {}; } -const char *get_use_address() { - // Global component pointers are guaranteed to be set by component constructors when USE_* is defined -#ifdef USE_ETHERNET - return ethernet::global_eth_component->get_use_address(); -#endif - -#ifdef USE_MODEM - return modem::global_modem_component->get_use_address(); -#endif - -#ifdef USE_WIFI - return wifi::global_wifi_component->get_use_address(); -#endif - -#ifdef USE_OPENTHREAD - return openthread::global_openthread_component->get_use_address(); -#endif - -#if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) && !defined(USE_OPENTHREAD) - // Fallback when no network component is defined (e.g., host platform) - return ""; -#endif -} - } // namespace esphome::network #endif diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h index 4b700fe74c0..e4e8a01f8cb 100644 --- a/esphome/components/network/util.h +++ b/esphome/components/network/util.h @@ -54,7 +54,29 @@ ESPHOME_ALWAYS_INLINE inline bool is_connected() { /// Return whether the network is disabled (only wifi for now) bool is_disabled(); /// Get the active network hostname -const char *get_use_address(); +ESPHOME_ALWAYS_INLINE inline const char *get_use_address() { + // Global component pointers are guaranteed to be set by component constructors when USE_* is defined +#ifdef USE_ETHERNET + return ethernet::global_eth_component->get_use_address(); +#endif + +#ifdef USE_MODEM + return modem::global_modem_component->get_use_address(); +#endif + +#ifdef USE_WIFI + return wifi::global_wifi_component->get_use_address(); +#endif + +#ifdef USE_OPENTHREAD + return openthread::global_openthread_component->get_use_address(); +#endif + +#if !defined(USE_ETHERNET) && !defined(USE_MODEM) && !defined(USE_WIFI) && !defined(USE_OPENTHREAD) + // Fallback when no network component is defined (e.g., host platform) + return ""; +#endif +} IPAddresses get_ip_addresses(); } // namespace esphome::network diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp index 1596b6e9908..7c9a3083034 100644 --- a/esphome/components/openthread/openthread.cpp +++ b/esphome/components/openthread/openthread.cpp @@ -257,11 +257,5 @@ void OpenThreadComponent::on_factory_reset(std::function callback) { ESP_LOGD(TAG, "Waiting on Confirmation Removal SRP Host and Services"); } -// set_use_address() is guaranteed to be called during component setup by Python code generation, -// so use_address_ will always be valid when get_use_address() is called - no fallback needed. -const char *OpenThreadComponent::get_use_address() const { return this->use_address_; } - -void OpenThreadComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; } - } // namespace esphome::openthread #endif diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h index bd10774fcf5..b42fdd2d307 100644 --- a/esphome/components/openthread/openthread.h +++ b/esphome/components/openthread/openthread.h @@ -37,8 +37,8 @@ class OpenThreadComponent : public Component { void on_factory_reset(std::function callback); void defer_factory_reset_external_callback(); - const char *get_use_address() const; - void set_use_address(const char *use_address); + const char *get_use_address() const { return this->use_address_; } + void set_use_address(const char *use_address) { this->use_address_ = use_address; } #if CONFIG_OPENTHREAD_MTD void set_poll_period(uint32_t poll_period) { this->poll_period_ = poll_period; } #endif diff --git a/esphome/components/serial_proxy/serial_proxy.cpp b/esphome/components/serial_proxy/serial_proxy.cpp index 00d822b75cb..f3c256c62a7 100644 --- a/esphome/components/serial_proxy/serial_proxy.cpp +++ b/esphome/components/serial_proxy/serial_proxy.cpp @@ -165,7 +165,7 @@ uint32_t SerialProxy::get_modem_pins() const { (this->dtr_state_ ? SERIAL_PROXY_LINE_STATE_FLAG_DTR : 0u); } -uart::FlushResult SerialProxy::flush_port() { +uart::UARTFlushResult SerialProxy::flush_port() { ESP_LOGV(TAG, "Flushing serial proxy [%u]", this->instance_index_); return this->flush(); } diff --git a/esphome/components/serial_proxy/serial_proxy.h b/esphome/components/serial_proxy/serial_proxy.h index 5adfa4fe53b..c435787a61e 100644 --- a/esphome/components/serial_proxy/serial_proxy.h +++ b/esphome/components/serial_proxy/serial_proxy.h @@ -92,7 +92,7 @@ class SerialProxy : public uart::UARTDevice, public Component { uint32_t get_modem_pins() const; /// Flush the serial port (block until all TX data is sent) - uart::FlushResult flush_port(); + uart::UARTFlushResult flush_port(); /// Set the RTS GPIO pin (from YAML configuration) void set_rts_pin(GPIOPin *pin) { this->rts_pin_ = pin; } diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 2c4fb34c9a8..899d349e212 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -45,7 +45,7 @@ class UARTDevice { size_t available() { return this->parent_->available(); } - FlushResult flush() { return this->parent_->flush(); } + UARTFlushResult flush() { return this->parent_->flush(); } // Compat APIs int read() { diff --git a/esphome/components/uart/uart_component.h b/esphome/components/uart/uart_component.h index 00a4c788784..abc77fbae82 100644 --- a/esphome/components/uart/uart_component.h +++ b/esphome/components/uart/uart_component.h @@ -30,17 +30,12 @@ enum UARTDirection { const LogString *parity_to_str(UARTParityOptions parity); /// Result of a flush() call. -// Some vendor SDKs (e.g., Realtek) define SUCCESS as a macro. -// Save and restore around the enum to avoid collisions with our scoped enum value. -#pragma push_macro("SUCCESS") -#undef SUCCESS -enum class FlushResult { - SUCCESS, ///< Confirmed: all bytes left the TX FIFO. - TIMEOUT, ///< Confirmed: timed out before TX completed. - FAILED, ///< Confirmed: driver or hardware error. - ASSUMED_SUCCESS, ///< Platform cannot report result; success is assumed. +enum class UARTFlushResult { + UART_FLUSH_RESULT_SUCCESS, ///< Confirmed: all bytes left the TX FIFO. + UART_FLUSH_RESULT_TIMEOUT, ///< Confirmed: timed out before TX completed. + UART_FLUSH_RESULT_FAILED, ///< Confirmed: driver or hardware error. + UART_FLUSH_RESULT_ASSUMED_SUCCESS, ///< Platform cannot report result; success is assumed. }; -#pragma pop_macro("SUCCESS") class UARTComponent { public: @@ -87,8 +82,8 @@ class UARTComponent { virtual size_t available() = 0; // Pure virtual method to block until all bytes have been written to the UART bus. - // @return FlushResult indicating whether the flush was confirmed, timed out, failed, or assumed successful. - virtual FlushResult flush() = 0; + // @return UARTFlushResult indicating whether the flush was confirmed, timed out, failed, or assumed successful. + virtual UARTFlushResult flush() = 0; // Sets the maximum time to wait for TX to drain during flush(). // Only meaningful on ESP32 (IDF). Other platforms ignore this value. diff --git a/esphome/components/uart/uart_component_esp8266.cpp b/esphome/components/uart/uart_component_esp8266.cpp index 91218c43006..0ea79307605 100644 --- a/esphome/components/uart/uart_component_esp8266.cpp +++ b/esphome/components/uart/uart_component_esp8266.cpp @@ -213,14 +213,14 @@ size_t ESP8266UartComponent::available() { return this->sw_serial_->available(); } } -FlushResult ESP8266UartComponent::flush() { +UARTFlushResult ESP8266UartComponent::flush() { ESP_LOGVV(TAG, " Flushing"); if (this->hw_serial_ != nullptr) { this->hw_serial_->flush(); } else { this->sw_serial_->flush(); } - return FlushResult::ASSUMED_SUCCESS; + return UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity, diff --git a/esphome/components/uart/uart_component_esp8266.h b/esphome/components/uart/uart_component_esp8266.h index ca90dc59646..7f844d9b651 100644 --- a/esphome/components/uart/uart_component_esp8266.h +++ b/esphome/components/uart/uart_component_esp8266.h @@ -58,7 +58,7 @@ class ESP8266UartComponent : public UARTComponent, public Component { bool read_array(uint8_t *data, size_t len) override; size_t available() override; - FlushResult flush() override; + UARTFlushResult flush() override; uint32_t get_config(); diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 8168e49805a..bd2f915d3a8 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -360,15 +360,15 @@ size_t IDFUARTComponent::available() { return available; } -FlushResult IDFUARTComponent::flush() { +UARTFlushResult IDFUARTComponent::flush() { ESP_LOGVV(TAG, " Flushing"); TickType_t ticks = this->flush_timeout_ms_ == 0 ? portMAX_DELAY : pdMS_TO_TICKS(this->flush_timeout_ms_); esp_err_t err = uart_wait_tx_done(this->uart_num_, ticks); if (err == ESP_OK) - return FlushResult::SUCCESS; + return UARTFlushResult::UART_FLUSH_RESULT_SUCCESS; if (err == ESP_ERR_TIMEOUT) - return FlushResult::TIMEOUT; - return FlushResult::FAILED; + return UARTFlushResult::UART_FLUSH_RESULT_TIMEOUT; + return UARTFlushResult::UART_FLUSH_RESULT_FAILED; } void IDFUARTComponent::check_logger_conflict() {} diff --git a/esphome/components/uart/uart_component_esp_idf.h b/esphome/components/uart/uart_component_esp_idf.h index 9fa2013cfd1..ec4f2884b29 100644 --- a/esphome/components/uart/uart_component_esp_idf.h +++ b/esphome/components/uart/uart_component_esp_idf.h @@ -31,7 +31,7 @@ class IDFUARTComponent : public UARTComponent, public Component { bool read_array(uint8_t *data, size_t len) override; size_t available() override; - FlushResult flush() override; + UARTFlushResult flush() override; void set_flush_timeout(uint32_t flush_timeout_ms) override { this->flush_timeout_ms_ = flush_timeout_ms; } diff --git a/esphome/components/uart/uart_component_host.cpp b/esphome/components/uart/uart_component_host.cpp index 66026f3ccdd..0042ffae23f 100644 --- a/esphome/components/uart/uart_component_host.cpp +++ b/esphome/components/uart/uart_component_host.cpp @@ -274,13 +274,13 @@ size_t HostUartComponent::available() { return result; }; -FlushResult HostUartComponent::flush() { +UARTFlushResult HostUartComponent::flush() { if (this->file_descriptor_ == -1) { - return FlushResult::ASSUMED_SUCCESS; + return UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } tcflush(this->file_descriptor_, TCIOFLUSH); ESP_LOGV(TAG, " Flushing"); - return FlushResult::ASSUMED_SUCCESS; + return UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } void HostUartComponent::update_error_(const std::string &error) { diff --git a/esphome/components/uart/uart_component_host.h b/esphome/components/uart/uart_component_host.h index c22efdcb92d..56ff525bc3d 100644 --- a/esphome/components/uart/uart_component_host.h +++ b/esphome/components/uart/uart_component_host.h @@ -18,7 +18,7 @@ class HostUartComponent : public UARTComponent, public Component { bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; size_t available() override; - FlushResult flush() override; + UARTFlushResult flush() override; void set_name(std::string port_name) { port_name_ = port_name; }; protected: diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index 6a550f296a1..4172e7c1649 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -170,10 +170,10 @@ bool LibreTinyUARTComponent::read_array(uint8_t *data, size_t len) { } size_t LibreTinyUARTComponent::available() { return this->serial_->available(); } -FlushResult LibreTinyUARTComponent::flush() { +UARTFlushResult LibreTinyUARTComponent::flush() { ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); - return FlushResult::ASSUMED_SUCCESS; + return UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } void LibreTinyUARTComponent::check_logger_conflict() { diff --git a/esphome/components/uart/uart_component_libretiny.h b/esphome/components/uart/uart_component_libretiny.h index 77df8080671..872ea866014 100644 --- a/esphome/components/uart/uart_component_libretiny.h +++ b/esphome/components/uart/uart_component_libretiny.h @@ -22,7 +22,7 @@ class LibreTinyUARTComponent : public UARTComponent, public Component { bool read_array(uint8_t *data, size_t len) override; size_t available() override; - FlushResult flush() override; + UARTFlushResult flush() override; uint16_t get_config(); diff --git a/esphome/components/uart/uart_component_rp2040.cpp b/esphome/components/uart/uart_component_rp2040.cpp index 6f6f1fb96b7..1aaf98dc84b 100644 --- a/esphome/components/uart/uart_component_rp2040.cpp +++ b/esphome/components/uart/uart_component_rp2040.cpp @@ -208,10 +208,10 @@ bool RP2040UartComponent::read_array(uint8_t *data, size_t len) { return true; } size_t RP2040UartComponent::available() { return this->serial_->available(); } -FlushResult RP2040UartComponent::flush() { +UARTFlushResult RP2040UartComponent::flush() { ESP_LOGVV(TAG, " Flushing"); this->serial_->flush(); - return FlushResult::ASSUMED_SUCCESS; + return UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } } // namespace esphome::uart diff --git a/esphome/components/uart/uart_component_rp2040.h b/esphome/components/uart/uart_component_rp2040.h index 891212ca74c..198c698af9c 100644 --- a/esphome/components/uart/uart_component_rp2040.h +++ b/esphome/components/uart/uart_component_rp2040.h @@ -25,7 +25,7 @@ class RP2040UartComponent : public UARTComponent, public Component { bool read_array(uint8_t *data, size_t len) override; size_t available() override; - FlushResult flush() override; + UARTFlushResult flush() override; uint16_t get_config(); diff --git a/esphome/components/usb_cdc_acm/usb_cdc_acm.h b/esphome/components/usb_cdc_acm/usb_cdc_acm.h index a56abc9ee8e..89405ab8939 100644 --- a/esphome/components/usb_cdc_acm/usb_cdc_acm.h +++ b/esphome/components/usb_cdc_acm/usb_cdc_acm.h @@ -82,7 +82,7 @@ class USBCDCACMInstance : public uart::UARTComponent, public Parentedhas_peek_ ? 1 : 0); } -uart::FlushResult USBCDCACMInstance::flush() { +uart::UARTFlushResult USBCDCACMInstance::flush() { // Wait for TX ring buffer to be empty if (this->usb_tx_ringbuf_ == nullptr) { - return uart::FlushResult::ASSUMED_SUCCESS; + return uart::UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } UBaseType_t waiting = 1; @@ -342,10 +342,10 @@ uart::FlushResult USBCDCACMInstance::flush() { // Also wait for USB to finish transmitting esp_err_t err = tinyusb_cdcacm_write_flush(static_cast(this->itf_), pdMS_TO_TICKS(100)); if (err == ESP_OK) - return uart::FlushResult::SUCCESS; + return uart::UARTFlushResult::UART_FLUSH_RESULT_SUCCESS; if (err == ESP_ERR_TIMEOUT) - return uart::FlushResult::TIMEOUT; - return uart::FlushResult::FAILED; + return uart::UARTFlushResult::UART_FLUSH_RESULT_TIMEOUT; + return uart::UARTFlushResult::UART_FLUSH_RESULT_FAILED; } void USBCDCACMInstance::check_logger_conflict() {} diff --git a/esphome/components/usb_uart/usb_uart.cpp b/esphome/components/usb_uart/usb_uart.cpp index a5d312f1912..0b8589f6713 100644 --- a/esphome/components/usb_uart/usb_uart.cpp +++ b/esphome/components/usb_uart/usb_uart.cpp @@ -169,7 +169,7 @@ void USBUartChannel::write_array(const uint8_t *data, size_t len) { this->parent_->start_output(this); } -uart::FlushResult USBUartChannel::flush() { +uart::UARTFlushResult USBUartChannel::flush() { // Spin until the output queue is drained and the last USB transfer completes. // Safe to call from the main loop only. // The flush_timeout_ms_ timeout guards against a device that stops responding mid-flush; @@ -181,8 +181,8 @@ uart::FlushResult USBUartChannel::flush() { yield(); } if (!this->output_queue_.empty() || this->output_started_.load()) - return uart::FlushResult::TIMEOUT; - return uart::FlushResult::SUCCESS; + return uart::UARTFlushResult::UART_FLUSH_RESULT_TIMEOUT; + return uart::UARTFlushResult::UART_FLUSH_RESULT_SUCCESS; } bool USBUartChannel::peek_byte(uint8_t *data) { diff --git a/esphome/components/usb_uart/usb_uart.h b/esphome/components/usb_uart/usb_uart.h index 7a06b04f114..8a47f0cf4be 100644 --- a/esphome/components/usb_uart/usb_uart.h +++ b/esphome/components/usb_uart/usb_uart.h @@ -140,7 +140,7 @@ class USBUartChannel : public uart::UARTComponent, public Parentedinput_buffer_.get_available(); } - uart::FlushResult flush() override; + uart::UARTFlushResult flush() override; void check_logger_conflict() override {} void set_parity(UARTParityOptions parity) { this->parity_ = parity; } void set_debug(bool debug) { this->debug_ = debug; } diff --git a/esphome/components/weikai/weikai.cpp b/esphome/components/weikai/weikai.cpp index f01d164e9fd..2ec56326912 100644 --- a/esphome/components/weikai/weikai.cpp +++ b/esphome/components/weikai/weikai.cpp @@ -433,16 +433,16 @@ void WeikaiChannel::write_array(const uint8_t *buffer, size_t length) { this->reg(0).write_fifo(const_cast(buffer), length); } -uart::FlushResult WeikaiChannel::flush() { +uart::UARTFlushResult WeikaiChannel::flush() { uint32_t const start_time = millis(); while (this->tx_fifo_is_not_empty_()) { // wait until buffer empty if (millis() - start_time > 200) { ESP_LOGW(TAG, "WARNING flush timeout - still %d bytes not sent after 200 ms", this->tx_in_fifo_()); - return uart::FlushResult::TIMEOUT; + return uart::UARTFlushResult::UART_FLUSH_RESULT_TIMEOUT; } yield(); // reschedule our thread to avoid blocking } - return uart::FlushResult::SUCCESS; + return uart::UARTFlushResult::UART_FLUSH_RESULT_SUCCESS; } size_t WeikaiChannel::xfer_fifo_to_buffer_() { diff --git a/esphome/components/weikai/weikai.h b/esphome/components/weikai/weikai.h index 715b82bfc71..36d8f66265e 100644 --- a/esphome/components/weikai/weikai.h +++ b/esphome/components/weikai/weikai.h @@ -380,7 +380,7 @@ class WeikaiChannel : public uart::UARTComponent { /// @details If we refer to Serial.flush() in Arduino it says: ** Waits for the transmission of outgoing serial data /// to complete. (Prior to Arduino 1.0, this the method was removing any buffered incoming serial data.). ** Therefore /// we wait until all bytes are gone with a timeout of 100 ms - uart::FlushResult flush() override; + uart::UARTFlushResult flush() override; protected: friend class WeikaiComponent; diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 08065a7544e..0fd13852581 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -891,10 +891,6 @@ network::IPAddress WiFiComponent::get_dns_address(int num) { return this->wifi_dns_ip_(num); return {}; } -// set_use_address() is guaranteed to be called during component setup by Python code generation, -// so use_address_ will always be valid when get_use_address() is called - no fallback needed. -const char *WiFiComponent::get_use_address() const { return this->use_address_; } -void WiFiComponent::set_use_address(const char *use_address) { this->use_address_ = use_address; } #ifdef USE_WIFI_AP void WiFiComponent::setup_ap_config_() { diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index bd202604d6f..718f4a6e12c 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -481,8 +481,8 @@ class WiFiComponent final : public Component { network::IPAddress get_dns_address(int num); network::IPAddresses get_ip_addresses(); - const char *get_use_address() const; - void set_use_address(const char *use_address); + const char *get_use_address() const { return this->use_address_; } + void set_use_address(const char *use_address) { this->use_address_ = use_address; } const wifi_scan_vector_t &get_scan_result() const { return scan_result_; } diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 51cbfb208ea..9ee3b2fdd2a 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -166,7 +166,13 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type item->component = component; item->set_name(name_type, static_name, hash_or_id); item->type = type; - item->callback = std::move(func); + // Use destroy + placement-new instead of move-assignment. + // GCC's std::function::operator=(function&&) does a full swap dance even when the + // target is empty. Since recycled/new items always have an empty callback, we can + // destroy the empty one (no-op) and move-construct directly, saving ~40 bytes of + // swap/destructor code on Xtensa. + item->callback.~function(); + new (&item->callback) std::function(std::move(func)); // Reset remove flag - recycled items may have been cancelled (remove=true) in previous use this->set_item_removed_(item, false); item->is_retry = is_retry; diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 3ed5d0ba37d..a8efe96cce3 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -565,6 +565,29 @@ def new_variable( return obj +def _extract_component_ns(type_str: str) -> str: + """Extract the component namespace from a fully-qualified C++ type string. + + Strips leading ``esphome::`` and template arguments, then returns + the first namespace segment. Falls back to ``"esphome"`` when the + type has no namespace qualifier (after stripping templates). + + Examples:: + + esphome::dsmr::Dsmr -> dsmr + esphome::logger::Logger -> logger + esphome::Automation, std::optional> -> esphome + Logger -> esphome + """ + bare = type_str.removeprefix("esphome::") + # Strip template arguments before namespace extraction to avoid + # matching :: inside template params (e.g. Automation>) + bare_no_template = bare.split("<", maxsplit=1)[0] + if "::" in bare_no_template: + return bare_no_template.split("::", maxsplit=1)[0].rstrip("_") + return "esphome" + + def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": """Declare a new pointer variable in the code generation. @@ -584,7 +607,9 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": # For 'new' allocations, use placement new into static storage # to avoid heap fragmentation on embedded devices. the_type = id_.type - storage_name = f"{id_.id}__pstorage" + # Extract component namespace from type for memory analysis attribution + component_ns = _extract_component_ns(str(the_type)) + storage_name = f"{component_ns}__{id_.id}__pstorage" # Declare aligned byte array for the object storage CORE.add_global( diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 5f791c9c6b8..0bb569fdb54 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: diff --git a/tests/benchmarks/components/binary_sensor/__init__.py b/tests/benchmarks/components/binary_sensor/__init__.py new file mode 100644 index 00000000000..b08f67a0956 --- /dev/null +++ b/tests/benchmarks/components/binary_sensor/__init__.py @@ -0,0 +1,5 @@ +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + manifest.enable_codegen() diff --git a/tests/benchmarks/components/binary_sensor/bench_binary_sensor_publish.cpp b/tests/benchmarks/components/binary_sensor/bench_binary_sensor_publish.cpp new file mode 100644 index 00000000000..8bae943e2ea --- /dev/null +++ b/tests/benchmarks/components/binary_sensor/bench_binary_sensor_publish.cpp @@ -0,0 +1,61 @@ +#include + +#include "esphome/components/binary_sensor/binary_sensor.h" + +namespace esphome::binary_sensor::benchmarks { + +static constexpr int kInnerIterations = 2000; + +// Benchmark: publish_state with alternating values (forces state change every time) +static void BinarySensorPublish_Alternating(benchmark::State &state) { + BinarySensor sensor; + + // First publish to establish initial state + sensor.publish_initial_state(false); + + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(i % 2 == 0); + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(BinarySensorPublish_Alternating); + +// Benchmark: publish_state with same value (tests dedup fast path) +static void BinarySensorPublish_NoChange(benchmark::State &state) { + BinarySensor sensor; + + sensor.publish_initial_state(true); + + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(true); + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(BinarySensorPublish_NoChange); + +// Benchmark: publish_state with a callback registered +static void BinarySensorPublish_WithCallback(benchmark::State &state) { + BinarySensor sensor; + + int callback_count = 0; + sensor.add_on_state_callback([&callback_count](bool) { callback_count++; }); + + sensor.publish_initial_state(false); + + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(i % 2 == 0); + } + benchmark::DoNotOptimize(callback_count); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(BinarySensorPublish_WithCallback); + +} // namespace esphome::binary_sensor::benchmarks diff --git a/tests/benchmarks/components/binary_sensor/benchmark.yaml b/tests/benchmarks/components/binary_sensor/benchmark.yaml new file mode 100644 index 00000000000..fc0db6c52c9 --- /dev/null +++ b/tests/benchmarks/components/binary_sensor/benchmark.yaml @@ -0,0 +1 @@ +binary_sensor: diff --git a/tests/benchmarks/components/sensor/__init__.py b/tests/benchmarks/components/sensor/__init__.py new file mode 100644 index 00000000000..5a593aa8c27 --- /dev/null +++ b/tests/benchmarks/components/sensor/__init__.py @@ -0,0 +1,12 @@ +import esphome.codegen as cg +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + # Sensor filter benchmarks need USE_SENSOR_FILTER defined. + # We use a custom to_code instead of enable_codegen() to avoid + # pulling in the full sensor component setup. + async def to_code(config): + cg.add_define("USE_SENSOR_FILTER") + + manifest.to_code = to_code diff --git a/tests/benchmarks/components/sensor/bench_sensor_filter.cpp b/tests/benchmarks/components/sensor/bench_sensor_filter.cpp new file mode 100644 index 00000000000..e4aa3976901 --- /dev/null +++ b/tests/benchmarks/components/sensor/bench_sensor_filter.cpp @@ -0,0 +1,78 @@ +#include + +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/sensor/filter.h" + +namespace esphome::sensor::benchmarks { + +static constexpr int kInnerIterations = 2000; + +// Benchmark: sensor publish through a SlidingWindowMovingAverageFilter (window=5, send_every=1) +static void SensorFilter_SlidingWindowAvg(benchmark::State &state) { + Sensor sensor; + + // Create filter: window_size=5, send_every=1, send_first_at=1 + auto *filter = new SlidingWindowMovingAverageFilter(5, 1, 1); + sensor.add_filter(filter); + + float value = 0.0f; + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(value); + value += 0.1f; + if (value > 1000.0f) + value = 0.0f; + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(SensorFilter_SlidingWindowAvg); + +// Benchmark: sensor publish through ExponentialMovingAverageFilter +static void SensorFilter_ExponentialMovingAvg(benchmark::State &state) { + Sensor sensor; + + // alpha=0.1, send_every=1, send_first_at=1 + auto *filter = new ExponentialMovingAverageFilter(0.1f, 1, 1); + sensor.add_filter(filter); + + float value = 0.0f; + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(value); + value += 0.1f; + if (value > 1000.0f) + value = 0.0f; + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(SensorFilter_ExponentialMovingAvg); + +// Benchmark: sensor publish through a chain of 3 filters (offset + multiply + sliding window) +static void SensorFilter_Chain3(benchmark::State &state) { + Sensor sensor; + + sensor.add_filters({ + new OffsetFilter(1.0f), + new MultiplyFilter(2.0f), + new SlidingWindowMovingAverageFilter(5, 1, 1), + }); + + float value = 0.0f; + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(value); + value += 0.1f; + if (value > 1000.0f) + value = 0.0f; + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(SensorFilter_Chain3); + +} // namespace esphome::sensor::benchmarks diff --git a/tests/benchmarks/components/sensor/benchmark.yaml b/tests/benchmarks/components/sensor/benchmark.yaml new file mode 100644 index 00000000000..e1fb52cdd6a --- /dev/null +++ b/tests/benchmarks/components/sensor/benchmark.yaml @@ -0,0 +1 @@ +sensor: diff --git a/tests/component_tests/deep_sleep/test_deep_sleep.py b/tests/component_tests/deep_sleep/test_deep_sleep.py index 212f61e44b9..8c1278a3323 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep.py +++ b/tests/component_tests/deep_sleep/test_deep_sleep.py @@ -8,7 +8,7 @@ def test_deep_sleep_setup(generate_main): main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml") assert ( - "static deep_sleep::DeepSleepComponent *const deepsleep = reinterpret_cast(deepsleep__pstorage);" + "static deep_sleep::DeepSleepComponent *const deepsleep = reinterpret_cast(deep_sleep__deepsleep__pstorage);" in main_cpp ) assert "new(deepsleep) deep_sleep::DeepSleepComponent();" in main_cpp diff --git a/tests/component_tests/image/test_init.py b/tests/component_tests/image/test_init.py index 9003a4ee5d9..6f73888c7d1 100644 --- a/tests/component_tests/image/test_init.py +++ b/tests/component_tests/image/test_init.py @@ -242,11 +242,11 @@ def test_image_generation( main_cpp = generate_main(component_config_path("image_test.yaml")) assert "uint8_t_id[] PROGMEM = {0x24, 0x21, 0x24, 0x21" in main_cpp assert ( - "alignas(image::Image) static unsigned char cat_img__pstorage[sizeof(image::Image)];" + "alignas(image::Image) static unsigned char image__cat_img__pstorage[sizeof(image::Image)];" in main_cpp ) assert ( - "static image::Image *const cat_img = reinterpret_cast(cat_img__pstorage);" + "static image::Image *const cat_img = reinterpret_cast(image__cat_img__pstorage);" in main_cpp ) assert ( diff --git a/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py b/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py index e6f344b086c..1ae8cc644e0 100644 --- a/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py +++ b/tests/component_tests/mipi_dsi/test_mipi_dsi_config.py @@ -119,11 +119,11 @@ def test_code_generation( main_cpp = generate_main(component_fixture_path("mipi_dsi.yaml")) assert ( - "alignas(mipi_dsi::MIPI_DSI) static unsigned char p4_nano__pstorage[sizeof(mipi_dsi::MIPI_DSI)];" + "alignas(mipi_dsi::MIPI_DSI) static unsigned char mipi_dsi__p4_nano__pstorage[sizeof(mipi_dsi::MIPI_DSI)];" in main_cpp ) assert ( - "static mipi_dsi::MIPI_DSI *const p4_nano = reinterpret_cast(p4_nano__pstorage);" + "static mipi_dsi::MIPI_DSI *const p4_nano = reinterpret_cast(mipi_dsi__p4_nano__pstorage);" in main_cpp ) assert ( diff --git a/tests/component_tests/status_led/test_status_led.py b/tests/component_tests/status_led/test_status_led.py index 0e96e631f5b..f7e0a9de86d 100644 --- a/tests/component_tests/status_led/test_status_led.py +++ b/tests/component_tests/status_led/test_status_led.py @@ -13,11 +13,11 @@ def test_status_led_generation( """Test status_led generation.""" main_cpp = generate_main(component_config_path("status_led_test.yaml")) assert ( - "alignas(status_led::StatusLED) static unsigned char status_led_statusled_id__pstorage[sizeof(status_led::StatusLED)];" + "alignas(status_led::StatusLED) static unsigned char status_led__status_led_statusled_id__pstorage[sizeof(status_led::StatusLED)];" in main_cpp ) assert ( - "static status_led::StatusLED *const status_led_statusled_id = reinterpret_cast(status_led_statusled_id__pstorage);" + "static status_led::StatusLED *const status_led_statusled_id = reinterpret_cast(status_led__status_led_statusled_id__pstorage);" in main_cpp ) assert "new(status_led_statusled_id) status_led::StatusLED(" in main_cpp diff --git a/tests/components/ld2450/common.h b/tests/components/ld2450/common.h index 9f9e7b3e9f6..304634edcaa 100644 --- a/tests/components/ld2450/common.h +++ b/tests/components/ld2450/common.h @@ -16,7 +16,7 @@ class MockUARTComponent : public uart::UARTComponent { MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override)); MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override)); MOCK_METHOD(size_t, available, (), (override)); - MOCK_METHOD(uart::FlushResult, flush, (), (override)); + MOCK_METHOD(uart::UARTFlushResult, flush, (), (override)); MOCK_METHOD(void, check_logger_conflict, (), (override)); }; diff --git a/tests/components/uart/common.h b/tests/components/uart/common.h index f7e2d8a3f76..de3ea3029ef 100644 --- a/tests/components/uart/common.h +++ b/tests/components/uart/common.h @@ -30,7 +30,7 @@ class MockUARTComponent : public UARTComponent { MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override)); MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override)); MOCK_METHOD(size_t, available, (), (override)); - MOCK_METHOD(FlushResult, flush, (), (override)); + MOCK_METHOD(UARTFlushResult, flush, (), (override)); MOCK_METHOD(void, check_logger_conflict, (), (override)); }; diff --git a/tests/integration/fixtures/external_components/uart_mock/uart_mock.cpp b/tests/integration/fixtures/external_components/uart_mock/uart_mock.cpp index 1a15da76d13..d0690e75154 100644 --- a/tests/integration/fixtures/external_components/uart_mock/uart_mock.cpp +++ b/tests/integration/fixtures/external_components/uart_mock/uart_mock.cpp @@ -153,9 +153,9 @@ bool MockUartComponent::read_array(uint8_t *data, size_t len) { size_t MockUartComponent::available() { return this->rx_buffer_.size(); } -uart::FlushResult MockUartComponent::flush() { +uart::UARTFlushResult MockUartComponent::flush() { // Nothing to flush in mock - return uart::FlushResult::ASSUMED_SUCCESS; + return uart::UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } void MockUartComponent::set_rx_full_threshold(size_t rx_full_threshold) { diff --git a/tests/integration/fixtures/external_components/uart_mock/uart_mock.h b/tests/integration/fixtures/external_components/uart_mock/uart_mock.h index 82e3b3d5632..0b3b49893df 100644 --- a/tests/integration/fixtures/external_components/uart_mock/uart_mock.h +++ b/tests/integration/fixtures/external_components/uart_mock/uart_mock.h @@ -28,7 +28,7 @@ class MockUartComponent : public uart::UARTComponent, public Component { bool peek_byte(uint8_t *data) override; bool read_array(uint8_t *data, size_t len) override; size_t available() override; - uart::FlushResult flush() override; + uart::UARTFlushResult flush() override; void set_rx_full_threshold(size_t rx_full_threshold) override; void set_rx_timeout(size_t rx_timeout) override; diff --git a/tests/integration/test_uart_mock_ld2412.py b/tests/integration/test_uart_mock_ld2412.py index 9b928ef14f2..12aa3f8397c 100644 --- a/tests/integration/test_uart_mock_ld2412.py +++ b/tests/integration/test_uart_mock_ld2412.py @@ -79,9 +79,15 @@ async def test_uart_mock_ld2412( ], ) - # Signal when we see recovery frame values + # Signal when we see all recovery frame values recovery_received = collector.add_waiter( - lambda: pytest.approx(50.0) in collector.sensor_states["moving_distance"] + lambda: ( + pytest.approx(50.0) in collector.sensor_states["moving_distance"] + and pytest.approx(75.0) in collector.sensor_states["still_distance"] + and pytest.approx(100.0) in collector.sensor_states["moving_energy"] + and pytest.approx(80.0) in collector.sensor_states["still_energy"] + and pytest.approx(50.0) in collector.sensor_states["detection_distance"] + ) ) async with ( @@ -150,23 +156,12 @@ async def test_uart_mock_ld2412( ) # Recovery frame: moving=50, still=75, energy=100/80, detect=50 - recovery_idx = next( - i - for i, v in enumerate(collector.sensor_states["moving_distance"]) - if v == pytest.approx(50.0) - ) - assert collector.sensor_states["still_distance"][recovery_idx] == pytest.approx( - 75.0 - ) - assert collector.sensor_states["moving_energy"][recovery_idx] == pytest.approx( - 100.0 - ) - assert collector.sensor_states["still_energy"][recovery_idx] == pytest.approx( - 80.0 - ) - assert collector.sensor_states["detection_distance"][ - recovery_idx - ] == pytest.approx(50.0) + # Check values exist (waiter already ensured all are present) + assert pytest.approx(50.0) in collector.sensor_states["moving_distance"] + assert pytest.approx(75.0) in collector.sensor_states["still_distance"] + assert pytest.approx(100.0) in collector.sensor_states["moving_energy"] + assert pytest.approx(80.0) in collector.sensor_states["still_energy"] + assert pytest.approx(50.0) in collector.sensor_states["detection_distance"] # Verify binary sensors detected targets (from Phase 1 frame) assert collector.binary_states["has_target"][0] is True diff --git a/tests/unit_tests/analyze_memory/test_pstorage_attribution.py b/tests/unit_tests/analyze_memory/test_pstorage_attribution.py new file mode 100644 index 00000000000..a57b283f448 --- /dev/null +++ b/tests/unit_tests/analyze_memory/test_pstorage_attribution.py @@ -0,0 +1,90 @@ +"""Tests for __pstorage symbol attribution in memory analyzer.""" + +from unittest.mock import patch + +from esphome.analyze_memory import _PSTORAGE_SUFFIX, MemoryAnalyzer + + +def _make_analyzer(external_components: set[str] | None = None) -> MemoryAnalyzer: + """Create a MemoryAnalyzer with mocked dependencies.""" + with patch.object(MemoryAnalyzer, "__init__", lambda self, *a, **kw: None): + analyzer = MemoryAnalyzer.__new__(MemoryAnalyzer) + analyzer.external_components = external_components or set() + return analyzer + + +def test_pstorage_suffix_constant() -> None: + """Verify the suffix constant matches what codegen produces.""" + assert _PSTORAGE_SUFFIX == "__pstorage" + + +def test_match_pstorage_simple_component() -> None: + """Simple component name like 'logger'.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("logger__logger_id__pstorage") + assert result == "[esphome]logger" + + +def test_match_pstorage_underscore_component() -> None: + """Component with underscore like 'web_server'.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("web_server__webserver_id__pstorage") + assert result == "[esphome]web_server" + + +def test_match_pstorage_api() -> None: + """API component.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("api__apiserver_id__pstorage") + assert result == "[esphome]api" + + +def test_match_pstorage_deep_sleep() -> None: + """Component with underscore: deep_sleep.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("deep_sleep__deepsleep__pstorage") + assert result == "[esphome]deep_sleep" + + +def test_match_pstorage_status_led() -> None: + """Component with underscore: status_led.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("status_led__statusled_id__pstorage") + assert result == "[esphome]status_led" + + +def test_match_pstorage_external_component() -> None: + """External component should be attributed correctly.""" + analyzer = _make_analyzer(external_components={"my_custom"}) + result = analyzer._match_pstorage_component("my_custom__thing_id__pstorage") + assert result == "[external]my_custom" + + +def test_match_pstorage_no_dunder_returns_none() -> None: + """Symbol without double underscore separator returns None.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("something__pstorage") + assert result is None + + +def test_match_pstorage_unknown_component_returns_none() -> None: + """Unknown component namespace returns None.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("nonexistent__thing_id__pstorage") + assert result is None + + +def test_match_pstorage_esphome_component() -> None: + """esphome:: namespace types map to the esphome component.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component( + "esphome__esphomeotacomponent_id__pstorage" + ) + assert result == "[esphome]esphome" + + +def test_match_pstorage_user_id_with_component_prefix() -> None: + """User-chosen ID that happens to contain a component name.""" + analyzer = _make_analyzer() + result = analyzer._match_pstorage_component("logger__relay1__pstorage") + assert result == "[esphome]logger" diff --git a/tests/unit_tests/test_codegen.py b/tests/unit_tests/test_codegen.py index 3f32a117ff9..8d01fef7c20 100644 --- a/tests/unit_tests/test_codegen.py +++ b/tests/unit_tests/test_codegen.py @@ -1,6 +1,7 @@ import pytest from esphome import codegen as cg +from esphome.cpp_generator import _extract_component_ns # Test interface remains the same. @@ -75,3 +76,29 @@ from esphome import codegen as cg ) def test_exists(attr): assert hasattr(cg, attr) + + +@pytest.mark.parametrize( + ("type_str", "expected"), + ( + ("esphome::dsmr::Dsmr", "dsmr"), + ("esphome::logger::Logger", "logger"), + ("esphome::web_server::WebServer", "web_server"), + ("esphome::deep_sleep::DeepSleep", "deep_sleep"), + ("esphome::Component", "esphome"), + ("Logger", "esphome"), + # Template types with :: in template args must not confuse extraction + ( + "esphome::Automation, std::optional>", + "esphome", + ), + ( + "esphome::StatelessLambdaAction, std::optional>", + "esphome", + ), + # Namespaced template type + ("esphome::sensor::Sensor", "sensor"), + ), +) +def test_extract_component_ns(type_str, expected): + assert _extract_component_ns(type_str) == expected