From 625f108183fc58575315789033c5ed67fa2f0f8c Mon Sep 17 00:00:00 2001 From: rwrozelle Date: Thu, 11 Sep 2025 01:23:58 -0400 Subject: [PATCH] Openthread Fix Factory Reset (#9281) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../button/factory_reset_button.cpp | 19 +++++++- .../button/factory_reset_button.h | 7 ++- .../switch/factory_reset_switch.cpp | 19 +++++++- .../switch/factory_reset_switch.h | 6 ++- esphome/components/openthread/openthread.cpp | 48 ++++++++++++++++--- esphome/components/openthread/openthread.h | 11 +++++ 6 files changed, 99 insertions(+), 11 deletions(-) diff --git a/esphome/components/factory_reset/button/factory_reset_button.cpp b/esphome/components/factory_reset/button/factory_reset_button.cpp index 585975c043..d582317767 100644 --- a/esphome/components/factory_reset/button/factory_reset_button.cpp +++ b/esphome/components/factory_reset/button/factory_reset_button.cpp @@ -1,7 +1,13 @@ #include "factory_reset_button.h" + +#include "esphome/core/defines.h" + +#ifdef USE_OPENTHREAD +#include "esphome/components/openthread/openthread.h" +#endif +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" namespace esphome { namespace factory_reset { @@ -13,9 +19,20 @@ void FactoryResetButton::press_action() { ESP_LOGI(TAG, "Resetting"); // Let MQTT settle a bit delay(100); // NOLINT +#ifdef USE_OPENTHREAD + openthread::global_openthread_component->on_factory_reset(FactoryResetButton::factory_reset_callback); +#else + global_preferences->reset(); + App.safe_reboot(); +#endif +} + +#ifdef USE_OPENTHREAD +void FactoryResetButton::factory_reset_callback() { global_preferences->reset(); App.safe_reboot(); } +#endif } // namespace factory_reset } // namespace esphome diff --git a/esphome/components/factory_reset/button/factory_reset_button.h b/esphome/components/factory_reset/button/factory_reset_button.h index 9996a860d9..c68da2ca74 100644 --- a/esphome/components/factory_reset/button/factory_reset_button.h +++ b/esphome/components/factory_reset/button/factory_reset_button.h @@ -1,7 +1,9 @@ #pragma once -#include "esphome/core/component.h" +#include "esphome/core/defines.h" + #include "esphome/components/button/button.h" +#include "esphome/core/component.h" namespace esphome { namespace factory_reset { @@ -9,6 +11,9 @@ namespace factory_reset { class FactoryResetButton : public button::Button, public Component { public: void dump_config() override; +#ifdef USE_OPENTHREAD + static void factory_reset_callback(); +#endif protected: void press_action() override; diff --git a/esphome/components/factory_reset/switch/factory_reset_switch.cpp b/esphome/components/factory_reset/switch/factory_reset_switch.cpp index 1282c73f4e..75449aa526 100644 --- a/esphome/components/factory_reset/switch/factory_reset_switch.cpp +++ b/esphome/components/factory_reset/switch/factory_reset_switch.cpp @@ -1,7 +1,13 @@ #include "factory_reset_switch.h" + +#include "esphome/core/defines.h" + +#ifdef USE_OPENTHREAD +#include "esphome/components/openthread/openthread.h" +#endif +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" namespace esphome { namespace factory_reset { @@ -17,10 +23,21 @@ void FactoryResetSwitch::write_state(bool state) { ESP_LOGI(TAG, "Resetting"); // Let MQTT settle a bit delay(100); // NOLINT +#ifdef USE_OPENTHREAD + openthread::global_openthread_component->on_factory_reset(FactoryResetSwitch::factory_reset_callback); +#else global_preferences->reset(); App.safe_reboot(); +#endif } } +#ifdef USE_OPENTHREAD +void FactoryResetSwitch::factory_reset_callback() { + global_preferences->reset(); + App.safe_reboot(); +} +#endif + } // namespace factory_reset } // namespace esphome diff --git a/esphome/components/factory_reset/switch/factory_reset_switch.h b/esphome/components/factory_reset/switch/factory_reset_switch.h index 2c914ea76d..8ea0c79108 100644 --- a/esphome/components/factory_reset/switch/factory_reset_switch.h +++ b/esphome/components/factory_reset/switch/factory_reset_switch.h @@ -1,7 +1,8 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/components/switch/switch.h" +#include "esphome/core/component.h" +#include "esphome/core/defines.h" namespace esphome { namespace factory_reset { @@ -9,6 +10,9 @@ namespace factory_reset { class FactoryResetSwitch : public switch_::Switch, public Component { public: void dump_config() override; +#ifdef USE_OPENTHREAD + static void factory_reset_callback(); +#endif protected: void write_state(bool state) override; diff --git a/esphome/components/openthread/openthread.cpp b/esphome/components/openthread/openthread.cpp index 322ff43238..5b5c113f83 100644 --- a/esphome/components/openthread/openthread.cpp +++ b/esphome/components/openthread/openthread.cpp @@ -11,8 +11,6 @@ #include #include #include -#include -#include #include #include @@ -77,8 +75,14 @@ std::optional OpenThreadComponent::get_omr_address_(InstanceLock & return {}; } -void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services, - const otSrpClientService *removed_services, void *context) { +void OpenThreadComponent::defer_factory_reset_external_callback() { + ESP_LOGD(TAG, "Defer factory_reset_external_callback_"); + this->defer([this]() { this->factory_reset_external_callback_(); }); +} + +void OpenThreadSrpComponent::srp_callback(otError err, const otSrpClientHostInfo *host_info, + const otSrpClientService *services, + const otSrpClientService *removed_services, void *context) { if (err != 0) { ESP_LOGW(TAG, "SRP client reported an error: %s", otThreadErrorToString(err)); for (const otSrpClientHostInfo *host = host_info; host; host = nullptr) { @@ -90,16 +94,30 @@ void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrp } } -void srp_start_callback(const otSockAddr *server_socket_address, void *context) { +void OpenThreadSrpComponent::srp_start_callback(const otSockAddr *server_socket_address, void *context) { ESP_LOGI(TAG, "SRP client has started"); } +void OpenThreadSrpComponent::srp_factory_reset_callback(otError err, const otSrpClientHostInfo *host_info, + const otSrpClientService *services, + const otSrpClientService *removed_services, void *context) { + OpenThreadComponent *obj = (OpenThreadComponent *) context; + if (err == OT_ERROR_NONE && removed_services != NULL && host_info != NULL && + host_info->mState == OT_SRP_CLIENT_ITEM_STATE_REMOVED) { + ESP_LOGD(TAG, "Successful Removal SRP Host and Services"); + } else if (err != OT_ERROR_NONE) { + // Handle other SRP client events or errors + ESP_LOGW(TAG, "SRP client event/error: %s", otThreadErrorToString(err)); + } + obj->defer_factory_reset_external_callback(); +} + void OpenThreadSrpComponent::setup() { otError error; InstanceLock lock = InstanceLock::acquire(); otInstance *instance = lock.get_instance(); - otSrpClientSetCallback(instance, srp_callback, nullptr); + otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_callback, nullptr); // set the host name uint16_t size; @@ -179,7 +197,8 @@ void OpenThreadSrpComponent::setup() { ESP_LOGD(TAG, "Added service: %s", full_service.c_str()); } - otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr); + otSrpClientEnableAutoStartMode(instance, OpenThreadSrpComponent::srp_start_callback, nullptr); + ESP_LOGD(TAG, "Finished SRP setup"); } void *OpenThreadSrpComponent::pool_alloc_(size_t size) { @@ -217,6 +236,21 @@ bool OpenThreadComponent::teardown() { return this->teardown_complete_; } +void OpenThreadComponent::on_factory_reset(std::function callback) { + factory_reset_external_callback_ = callback; + ESP_LOGD(TAG, "Start Removal SRP Host and Services"); + otError error; + InstanceLock lock = InstanceLock::acquire(); + otInstance *instance = lock.get_instance(); + otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_factory_reset_callback, this); + error = otSrpClientRemoveHostAndServices(instance, true, true); + if (error != OT_ERROR_NONE) { + ESP_LOGW(TAG, "Failed to Remove SRP Host and Services"); + return; + } + ESP_LOGD(TAG, "Waiting on Confirmation Removal SRP Host and Services"); +} + } // namespace openthread } // namespace esphome diff --git a/esphome/components/openthread/openthread.h b/esphome/components/openthread/openthread.h index a0ea1b3f3a..a9aff78e56 100644 --- a/esphome/components/openthread/openthread.h +++ b/esphome/components/openthread/openthread.h @@ -6,6 +6,8 @@ #include "esphome/components/network/ip_address.h" #include "esphome/core/component.h" +#include +#include #include #include @@ -28,11 +30,14 @@ class OpenThreadComponent : public Component { network::IPAddresses get_ip_addresses(); std::optional get_omr_address(); void ot_main(); + void on_factory_reset(std::function callback); + void defer_factory_reset_external_callback(); protected: std::optional get_omr_address_(InstanceLock &lock); bool teardown_started_{false}; bool teardown_complete_{false}; + std::function factory_reset_external_callback_; }; extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -43,6 +48,12 @@ class OpenThreadSrpComponent : public Component { // This has to run after the mdns component or else no services are available to advertise float get_setup_priority() const override { return this->mdns_->get_setup_priority() - 1.0; } void setup() override; + static void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services, + const otSrpClientService *removed_services, void *context); + static void srp_start_callback(const otSockAddr *server_socket_address, void *context); + static void srp_factory_reset_callback(otError err, const otSrpClientHostInfo *host_info, + const otSrpClientService *services, const otSrpClientService *removed_services, + void *context); protected: esphome::mdns::MDNSComponent *mdns_{nullptr};