From 2cfb1ab2e66a3c0bb84db56819eb51a8367c32b7 Mon Sep 17 00:00:00 2001 From: Roger Fachini Date: Sun, 1 Feb 2026 02:36:44 -0800 Subject: [PATCH] [ethernet] Add on_connect and on_disconnect automations --- esphome/components/ethernet/__init__.py | 18 +++++++++++++++++- .../components/ethernet/ethernet_component.cpp | 3 +++ .../components/ethernet/ethernet_component.h | 7 +++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 1f2fe61fe1..558e7e144b 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,6 +1,6 @@ import logging -from esphome import pins +from esphome import automation, pins import esphome.codegen as cg from esphome.components.esp32 import ( VARIANT_ESP32, @@ -34,6 +34,8 @@ from esphome.const import ( CONF_MODE, CONF_MOSI_PIN, CONF_NUMBER, + CONF_ON_CONNECT, + CONF_ON_DISCONNECT, CONF_PAGE_ID, CONF_PIN, CONF_POLLING_INTERVAL, @@ -236,6 +238,10 @@ BASE_SCHEMA = cv.Schema( cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True), + cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation( + single=True + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -426,6 +432,16 @@ async def to_code(config): if CORE.using_arduino: cg.add_library("WiFi", None) + if on_connect_config := config.get(CONF_ON_CONNECT): + await automation.build_automation( + var.get_connect_trigger(), [], on_connect_config + ) + + if on_disconnect_config := config.get(CONF_ON_DISCONNECT): + await automation.build_automation( + var.get_disconnect_trigger(), [], on_disconnect_config + ) + CORE.add_job(final_step) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 70f8ce1204..54b4567539 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -309,6 +309,7 @@ void EthernetComponent::loop() { this->dump_connect_params_(); this->status_clear_warning(); + this->connect_trigger_->trigger(); } else if (now - this->connect_begin_ > 15000) { ESP_LOGW(TAG, "Connecting failed; reconnecting"); this->start_connect_(); @@ -318,10 +319,12 @@ void EthernetComponent::loop() { if (!this->started_) { ESP_LOGI(TAG, "Stopped connection"); this->state_ = EthernetComponentState::STOPPED; + this->disconnect_trigger_->trigger(); } else if (!this->connected_) { ESP_LOGW(TAG, "Connection lost; reconnecting"); this->state_ = EthernetComponentState::CONNECTING; this->start_connect_(); + this->disconnect_trigger_->trigger(); } else { this->finish_connect_(); // When connected and stable, disable the loop to save CPU cycles diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 34380047d1..4b2e622225 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -4,6 +4,7 @@ #include "esphome/core/defines.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" +#include "esphome/core/automation.h" #include "esphome/components/network/ip_address.h" #ifdef USE_ESP32 @@ -119,6 +120,9 @@ class EthernetComponent : public Component { void add_ip_state_listener(EthernetIPStateListener *listener) { this->ip_state_listeners_.push_back(listener); } #endif + Trigger<> *get_connect_trigger() const { return this->connect_trigger_; } + Trigger<> *get_disconnect_trigger() const { return this->disconnect_trigger_; } + protected: static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); static void got_ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); @@ -190,6 +194,9 @@ class EthernetComponent : public Component { StaticVector ip_state_listeners_; #endif + Trigger<> *connect_trigger_{new Trigger<>()}; + Trigger<> *disconnect_trigger_{new Trigger<>()}; + private: // Stores a pointer to a string literal (static storage duration). // ONLY set from Python-generated code with string literals - never dynamic strings.