From c50ee215f225ced93a3074b5b42cd18ea4855ac5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 17 Oct 2026 15:07:42 -1000 Subject: [PATCH] [tcp_uart] Expose the connection entities as platforms (#21009) --- esphome/components/tcp_uart/__init__.py | 30 +++--------------- esphome/components/tcp_uart/binary_sensor.py | 25 +++++++++++++++ esphome/components/tcp_uart/sensor.py | 31 +++++++++++++++++++ esphome/components/tcp_uart/tcp_uart.cpp | 6 ++++ esphome/components/tcp_uart/tcp_uart.h | 7 +++++ tests/components/tcp_uart/benchmark.yaml | 5 +-- tests/components/tcp_uart/common.yaml | 8 +++++ .../tcp_uart/test-client.esp32-idf.yaml | 8 +++++ .../integration/fixtures/tcp_uart_bridge.yaml | 4 +++ 9 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 esphome/components/tcp_uart/binary_sensor.py create mode 100644 esphome/components/tcp_uart/sensor.py diff --git a/esphome/components/tcp_uart/__init__.py b/esphome/components/tcp_uart/__init__.py index 796a9fc48db..c05b9d17a32 100644 --- a/esphome/components/tcp_uart/__init__.py +++ b/esphome/components/tcp_uart/__init__.py @@ -1,8 +1,7 @@ import esphome.codegen as cg -from esphome.components import binary_sensor, sensor, socket, uart +from esphome.components import socket, uart from esphome.components.const import ( CONF_ALLOWED_IPS, - CONF_CONNECTED, CONF_DATA_BITS, CONF_HOST, CONF_PARITY, @@ -11,22 +10,16 @@ from esphome.components.const import ( CONF_STOP_BITS, ) import esphome.config_validation as cv -from esphome.const import ( - CONF_BAUD_RATE, - CONF_ID, - CONF_PORT, - DEVICE_CLASS_CONNECTIVITY, - ENTITY_CATEGORY_DIAGNOSTIC, - STATE_CLASS_TOTAL_INCREASING, -) +from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_PORT from esphome.types import ConfigType CODEOWNERS = ["@Bascht74"] DEPENDENCIES = ["network"] -AUTO_LOAD = ["uart", "binary_sensor", "sensor", "socket"] +AUTO_LOAD = ["uart", "socket"] +DOMAIN = "tcp_uart" MULTI_CONF = True -CONF_DISCONNECTS = "disconnects" +CONF_TCP_UART_ID = "tcp_uart_id" tcp_uart_ns = cg.esphome_ns.namespace("tcp_uart") TcpUart = tcp_uart_ns.class_("TcpUart", uart.UARTComponent, cg.Component) @@ -45,15 +38,6 @@ BASE_SCHEMA = cv.Schema( cv.Optional( CONF_RECONNECT_INTERVAL, default="5s" ): cv.positive_time_period_milliseconds, - cv.Optional(CONF_CONNECTED): binary_sensor.binary_sensor_schema( - device_class=DEVICE_CLASS_CONNECTIVITY, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), - cv.Optional(CONF_DISCONNECTS): sensor.sensor_schema( - accuracy_decimals=0, - state_class=STATE_CLASS_TOTAL_INCREASING, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, - ), } ).extend(cv.COMPONENT_SCHEMA) @@ -99,7 +83,3 @@ async def to_code(config: ConfigType) -> None: cg.add(var.set_parity(config[CONF_PARITY])) if (host := config.get(CONF_HOST)) is not None: cg.add(var.set_host(host)) - binary_sensors = binary_sensor.sub_binary_sensors(config) - await binary_sensors(CONF_CONNECTED, var.set_connected_sensor) - sensors = sensor.sub_sensors(config) - await sensors(CONF_DISCONNECTS, var.set_disconnects_sensor) diff --git a/esphome/components/tcp_uart/binary_sensor.py b/esphome/components/tcp_uart/binary_sensor.py new file mode 100644 index 00000000000..8337973c837 --- /dev/null +++ b/esphome/components/tcp_uart/binary_sensor.py @@ -0,0 +1,25 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +from esphome.components.const import CONF_CONNECTED +import esphome.config_validation as cv +from esphome.const import CONF_ID, DEVICE_CLASS_CONNECTIVITY, ENTITY_CATEGORY_DIAGNOSTIC +from esphome.types import ConfigType + +from . import CONF_TCP_UART_ID, TcpUart + +DEPENDENCIES = ["tcp_uart"] + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase), + cv.GenerateID(CONF_TCP_UART_ID): cv.use_id(TcpUart), + cv.Optional(CONF_CONNECTED): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_CONNECTIVITY, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), +} + + +async def to_code(config: ConfigType) -> None: + hub = await cg.get_variable(config[CONF_TCP_UART_ID]) + binary_sensors = binary_sensor.sub_binary_sensors(config) + await binary_sensors(CONF_CONNECTED, hub.set_connected_sensor) diff --git a/esphome/components/tcp_uart/sensor.py b/esphome/components/tcp_uart/sensor.py new file mode 100644 index 00000000000..099ca8734e1 --- /dev/null +++ b/esphome/components/tcp_uart/sensor.py @@ -0,0 +1,31 @@ +import esphome.codegen as cg +from esphome.components import sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_CLASS_TOTAL_INCREASING, +) +from esphome.types import ConfigType + +from . import CONF_TCP_UART_ID, TcpUart + +DEPENDENCIES = ["tcp_uart"] + +CONF_DISCONNECTS = "disconnects" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase), + cv.GenerateID(CONF_TCP_UART_ID): cv.use_id(TcpUart), + cv.Optional(CONF_DISCONNECTS): sensor.sensor_schema( + accuracy_decimals=0, + state_class=STATE_CLASS_TOTAL_INCREASING, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), +} + + +async def to_code(config: ConfigType) -> None: + hub = await cg.get_variable(config[CONF_TCP_UART_ID]) + sensors = sensor.sub_sensors(config) + await sensors(CONF_DISCONNECTS, hub.set_disconnects_sensor) diff --git a/esphome/components/tcp_uart/tcp_uart.cpp b/esphome/components/tcp_uart/tcp_uart.cpp index efc85804e99..139b31a2203 100644 --- a/esphome/components/tcp_uart/tcp_uart.cpp +++ b/esphome/components/tcp_uart/tcp_uart.cpp @@ -17,9 +17,11 @@ void TcpUart::setup() { #ifdef USE_SOCKET_TCP_LISTENER this->listener_.begin(TAG); #endif +#ifdef USE_BINARY_SENSOR if (this->connected_sensor_ != nullptr) { this->connected_sensor_->publish_state(false); } +#endif #ifdef USE_SENSOR if (this->disconnects_sensor_ != nullptr) { this->disconnects_sensor_->publish_state(0); @@ -38,7 +40,9 @@ void TcpUart::dump_config() { #ifdef USE_SOCKET_TCP_LISTENER this->listener_.dump_config(); #endif +#ifdef USE_BINARY_SENSOR LOG_BINARY_SENSOR(" ", "Connected", this->connected_sensor_); +#endif #ifdef USE_SENSOR LOG_SENSOR(" ", "Disconnects", this->disconnects_sensor_); #endif @@ -58,9 +62,11 @@ void TcpUart::sync_link_() { // Unread bytes of the last session stay readable while down, never into the next one. this->rx_start_ = this->rx_end_ = 0; } +#ifdef USE_BINARY_SENSOR if (this->connected_sensor_ != nullptr) { this->connected_sensor_->publish_state(up); } +#endif #ifdef USE_SENSOR // Only edges get here, so down is the falling edge. if (!up && this->disconnects_sensor_ != nullptr) { diff --git a/esphome/components/tcp_uart/tcp_uart.h b/esphome/components/tcp_uart/tcp_uart.h index 1e1d48e4c20..6ecfd1a0862 100644 --- a/esphome/components/tcp_uart/tcp_uart.h +++ b/esphome/components/tcp_uart/tcp_uart.h @@ -1,6 +1,9 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" +#endif #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" #endif @@ -23,7 +26,9 @@ class TcpUart : public uart::UARTComponent, public Component { void set_host(const char *host) { this->link_.set_host(host); } void set_port(uint16_t port) { this->link_.set_port(port); } void set_reconnect_interval(uint32_t ms) { this->link_.set_reconnect_interval(ms); } +#ifdef USE_BINARY_SENSOR void set_connected_sensor(binary_sensor::BinarySensor *sensor) { this->connected_sensor_ = sensor; } +#endif #ifdef USE_SENSOR void set_disconnects_sensor(sensor::Sensor *sensor) { this->disconnects_sensor_ = sensor; } #endif @@ -64,7 +69,9 @@ class TcpUart : public uart::UARTComponent, public Component { #ifdef USE_SOCKET_TCP_LISTENER socket::TcpListener listener_; #endif +#ifdef USE_BINARY_SENSOR binary_sensor::BinarySensor *connected_sensor_{nullptr}; +#endif #ifdef USE_SENSOR sensor::Sensor *disconnects_sensor_{nullptr}; uint32_t disconnects_{0}; diff --git a/tests/components/tcp_uart/benchmark.yaml b/tests/components/tcp_uart/benchmark.yaml index 781dc15b681..ed771f20534 100644 --- a/tests/components/tcp_uart/benchmark.yaml +++ b/tests/components/tcp_uart/benchmark.yaml @@ -1,3 +1,4 @@ -# The disconnects counter is compiled only with USE_SENSOR; declaring the -# sensor domain makes the C++ unit test build define it. +# The connection entities are compiled only with USE_BINARY_SENSOR and +# USE_SENSOR; declaring the domains makes the C++ unit test build define them. +binary_sensor: sensor: diff --git a/tests/components/tcp_uart/common.yaml b/tests/components/tcp_uart/common.yaml index 97ac0247da3..97bcd24f6a7 100644 --- a/tests/components/tcp_uart/common.yaml +++ b/tests/components/tcp_uart/common.yaml @@ -10,8 +10,16 @@ tcp_uart: allowed_ips: - 192.0.2.20 - 192.0.2.0/24 + +binary_sensor: + - platform: tcp_uart + tcp_uart_id: tcp_uart_server connected: name: TCP UART Server Connected + +sensor: + - platform: tcp_uart + tcp_uart_id: tcp_uart_server disconnects: name: TCP UART Server Disconnects diff --git a/tests/components/tcp_uart/test-client.esp32-idf.yaml b/tests/components/tcp_uart/test-client.esp32-idf.yaml index 74bd26a98bc..921ce0cbc1a 100644 --- a/tests/components/tcp_uart/test-client.esp32-idf.yaml +++ b/tests/components/tcp_uart/test-client.esp32-idf.yaml @@ -7,7 +7,15 @@ tcp_uart: host: 192.0.2.10 port: 502 reconnect_interval: 10s + +binary_sensor: + - platform: tcp_uart + tcp_uart_id: tcp_uart_1 connected: name: TCP UART Connected + +sensor: + - platform: tcp_uart + tcp_uart_id: tcp_uart_1 disconnects: name: TCP UART Disconnects diff --git a/tests/integration/fixtures/tcp_uart_bridge.yaml b/tests/integration/fixtures/tcp_uart_bridge.yaml index 12791e7dd46..74a8710b19e 100644 --- a/tests/integration/fixtures/tcp_uart_bridge.yaml +++ b/tests/integration/fixtures/tcp_uart_bridge.yaml @@ -20,6 +20,10 @@ tcp_uart: host: 127.0.0.1 port: 18125 reconnect_interval: 3s + +binary_sensor: + - platform: tcp_uart + tcp_uart_id: tcp_uart_1 connected: name: Bridge Connected