diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 86ce53b804..c42835f169 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -32,6 +32,8 @@ from esphome.const import ( CONF_KEY, CONF_USERNAME, CONF_EAP, + CONF_ON_CONNECT, + CONF_ON_DISCONNECT, ) from esphome.core import CORE, HexInt, coroutine_with_priority from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const @@ -306,6 +308,10 @@ CONFIG_SCHEMA = cv.All( "new mdns component instead." ), cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean, + cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True), + cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation( + single=True + ), } ), _validate, @@ -425,9 +431,21 @@ async def to_code(config): cg.add_define("USE_WIFI") - # Register at end for OTA safe mode + # must register before OTA safe mode check await cg.register_component(var, config) + await cg.past_safe_mode() + + 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 + ) + @automation.register_condition("wifi.connected", WiFiConnectedCondition, cv.Schema({})) async def wifi_connected_to_code(config, condition_id, template_arg, args): diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index b08f20de21..95d430de4f 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -138,9 +138,19 @@ void WiFiComponent::loop() { ESP_LOGW(TAG, "WiFi Connection lost... Reconnecting..."); this->state_ = WIFI_COMPONENT_STATE_STA_CONNECTING; this->retry_connect(); + + if (this->handled_connected_state_) { + this->disconnect_trigger_->trigger(); + this->handled_connected_state_ = false; + } } else { this->status_clear_warning(); this->last_connected_ = now; + + if (!this->handled_connected_state_) { + this->connect_trigger_->trigger(); + this->handled_connected_state_ = true; + } } break; } diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index b418a5b353..3ee69bb5de 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -1,18 +1,18 @@ #pragma once +#include "esphome/components/network/ip_address.h" +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" -#include "esphome/core/automation.h" #include "esphome/core/helpers.h" -#include "esphome/components/network/ip_address.h" #include #include #ifdef USE_ESP32_FRAMEWORK_ARDUINO -#include -#include #include +#include +#include #endif #ifdef USE_LIBRETINY @@ -294,6 +294,9 @@ class WiFiComponent : public Component { void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; } + Trigger<> *get_connect_trigger() const { return this->connect_trigger_; }; + Trigger<> *get_disconnect_trigger() const { return this->disconnect_trigger_; }; + protected: static std::string format_mac_addr(const uint8_t mac[6]); void setup_ap_config_(); @@ -354,6 +357,7 @@ class WiFiComponent : public Component { bool has_ap_{false}; WiFiAP ap_; WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; + bool handled_connected_state_{false}; uint32_t action_started_; uint8_t num_retried_{0}; uint32_t last_connected_{0}; @@ -373,6 +377,9 @@ class WiFiComponent : public Component { bool rrm_{false}; #endif bool enable_on_boot_; + + Trigger<> *connect_trigger_{new Trigger<>()}; + Trigger<> *disconnect_trigger_{new Trigger<>()}; }; extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/tests/test1.yaml b/tests/test1.yaml index f653960c40..6d1136eb1b 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -73,6 +73,10 @@ wifi: domain: .local reboot_timeout: 120s power_save_mode: light + on_connect: + - light.turn_on: ${roomname}_lights + on_disconnect: + - light.turn_off: ${roomname}_lights network: enable_ipv6: true