diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index f15acfe4a9..4c211b2f2a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -1265,15 +1265,8 @@ def _configure_lwip_max_sockets(conf: dict) -> None: # CONFIG_LWIP_MAX_SOCKETS is a single VFS socket pool shared by all socket # types (TCP clients, TCP listeners, and UDP). Include all three counts. - ( - tcp_sockets, - udp_sockets, - tcp_listen, - tcp_details, - udp_details, - tcp_listen_details, - ) = get_socket_counts() - total_sockets = tcp_sockets + udp_sockets + tcp_listen + sc = get_socket_counts() + total_sockets = sc.tcp + sc.udp + sc.tcp_listen # User specified their own value - respect it but warn if insufficient if user_max_sockets is not None: @@ -1294,9 +1287,9 @@ def _configure_lwip_max_sockets(conf: dict) -> None: "at least %d.", user_sockets_int, total_sockets, - tcp_sockets, - udp_sockets, - tcp_listen, + sc.tcp, + sc.udp, + sc.tcp_listen, total_sockets, ) # User's value already added via sdkconfig_options processing @@ -1314,12 +1307,12 @@ def _configure_lwip_max_sockets(conf: dict) -> None: "(TCP=%d [%s], UDP=%d [%s], TCP_LISTEN=%d [%s])", max_sockets, sock_min, - tcp_sockets, - tcp_details, - udp_sockets, - udp_details, - tcp_listen, - tcp_listen_details, + sc.tcp, + sc.tcp_details, + sc.udp, + sc.udp_details, + sc.tcp_listen, + sc.tcp_listen_details, ) add_idf_sdkconfig_option("CONFIG_LWIP_MAX_SOCKETS", max_sockets) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 1a424b653f..2291114d9a 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -322,15 +322,13 @@ def _configure_lwip(config: dict) -> None: get_socket_counts, ) - raw_tcp, raw_udp, raw_tcp_listen, tcp_details, udp_details, tcp_listen_details = ( - get_socket_counts() - ) + sc = get_socket_counts() # Apply platform minimums — ensure headroom for ESPHome's needs - tcp_sockets = max(MIN_TCP_SOCKETS, raw_tcp) - udp_sockets = max(MIN_UDP_SOCKETS, raw_udp) + tcp_sockets = max(MIN_TCP_SOCKETS, sc.tcp) + udp_sockets = max(MIN_UDP_SOCKETS, sc.udp) # Listening sockets — registered by components (api, ota, web_server_base, etc.) # Not all components register yet, so ensure a minimum for baseline operation. - listening_tcp = max(MIN_TCP_LISTEN_SOCKETS, raw_tcp_listen) + listening_tcp = max(MIN_TCP_LISTEN_SOCKETS, sc.tcp_listen) # TCP_SND_BUF: ESPAsyncWebServer allocates malloc(tcp_sndbuf()) per # response chunk. At 10×MSS=14.6KB (BK default) this causes OOM (#14095). @@ -399,20 +397,20 @@ def _configure_lwip(config: dict) -> None: if CORE.is_bk72xx: lwip_opts.append("PBUF_POOL_SIZE=10") - tcp_min = " (min)" if tcp_sockets > raw_tcp else "" - udp_min = " (min)" if udp_sockets > raw_udp else "" - listen_min = " (min)" if listening_tcp > raw_tcp_listen else "" + tcp_min = " (min)" if tcp_sockets > sc.tcp else "" + udp_min = " (min)" if udp_sockets > sc.udp else "" + listen_min = " (min)" if listening_tcp > sc.tcp_listen else "" _LOGGER.info( "Configuring lwIP: TCP=%d%s [%s], UDP=%d%s [%s], TCP_LISTEN=%d%s [%s]", tcp_sockets, tcp_min, - tcp_details, + sc.tcp_details, udp_sockets, udp_min, - udp_details, + sc.udp_details, listening_tcp, listen_min, - tcp_listen_details, + sc.tcp_listen_details, ) cg.add_platformio_option("custom_options.lwip", lwip_opts) diff --git a/esphome/components/socket/__init__.py b/esphome/components/socket/__init__.py index 572e7993b9..d82f0c7aba 100644 --- a/esphome/components/socket/__init__.py +++ b/esphome/components/socket/__init__.py @@ -1,4 +1,5 @@ from collections.abc import Callable, MutableMapping +from dataclasses import dataclass from enum import StrEnum import logging @@ -77,10 +78,20 @@ def _format_consumers(consumers: dict[str, int]) -> str: return ", ".join(f"{name}={count}" for name, count in sorted(consumers.items())) -def get_socket_counts() -> tuple[int, int, int, str, str, str]: - """Return socket counts and component details for platform configuration. +@dataclass(frozen=True) +class SocketCounts: + """Socket counts and component details for platform configuration.""" - Returns (tcp, udp, tcp_listen, tcp_details, udp_details, tcp_listen_details). + tcp: int + udp: int + tcp_listen: int + tcp_details: str + udp_details: str + tcp_listen_details: str + + +def get_socket_counts() -> SocketCounts: + """Return socket counts and component details for platform configuration. Platforms call this during code generation to configure lwIP socket limits. All components will have registered their needs by then. @@ -106,7 +117,9 @@ def get_socket_counts() -> tuple[int, int, int, str, str, str]: tcp_listen, tcp_listen_details, ) - return tcp, udp, tcp_listen, tcp_details, udp_details, tcp_listen_details + return SocketCounts( + tcp, udp, tcp_listen, tcp_details, udp_details, tcp_listen_details + ) def require_wake_loop_threadsafe() -> None: