[mdns] Simplify listener logic: always re-arm on IP notify, drop transition tracking

ESPHome's WiFiIPStateListener only notifies on IP acquisition (GOT_IP events), not
on IP loss — on disconnect, only the WiFiConnectStateListener's disconnect path
fires (see wifi_component_esp8266.cpp:952-962 and wifi_component_pico_w.cpp:340).

The previous commit's `ip_was_up_` transition tracking was broken: after the first
IP-up event, `ip_was_up_` latched to true and never reset, so subsequent
disconnect+reconnect cycles would see has_ip=true && ip_was_up_=true and skip
re-arming the polling window.

Fix: always re-arm on any IP notification. The scheduler's set_interval/set_timeout
with a uint32_t ID already performs atomic cancel-and-add for matching IDs
(Scheduler::set_timer_common_ line 232-234), so start_polling_window_ is idempotent
and needs no explicit cancel. Drop the ip_was_up_ field and cancel_polling_window_
helper entirely.

The !has_ip branch (cancel on disconnect) was dead code: it would never fire because
the listener doesn't receive disconnect events. Removing it; the polling window will
naturally expire on its own (at most 12s of harmless MDNS.update() calls during a
disconnect that isn't followed by reconnect within the window).
This commit is contained in:
J. Nick Koston
2026-04-23 19:26:01 -05:00
parent bf7083c501
commit 25601434d9
4 changed files with 23 additions and 30 deletions
+3 -7
View File
@@ -192,16 +192,12 @@ void MDNSComponent::compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUN
#ifdef USE_MDNS_EVENT_DRIVEN_POLLING
void MDNSComponent::start_polling_window_() {
// Re-arming replaces the previous window; cancel any active schedulers first.
this->cancel_polling_window_();
// Re-arming replaces the previous window. The scheduler's set_interval/set_timeout
// with a uint32_t ID already does atomic cancel-and-add for items sharing that ID
// (see Scheduler::set_timer_common_), so no explicit cancel is needed.
this->set_interval(MDNS_POLL_ID, MDNS_UPDATE_INTERVAL_MS, mdns_pump_update);
this->set_timeout(MDNS_POLL_STOP_ID, MDNS_POLL_WINDOW_MS, [this]() { this->cancel_interval(MDNS_POLL_ID); });
}
void MDNSComponent::cancel_polling_window_() {
this->cancel_interval(MDNS_POLL_ID);
this->cancel_timeout(MDNS_POLL_STOP_ID);
}
#endif
void MDNSComponent::dump_config() {
-3
View File
@@ -122,9 +122,6 @@ class MDNSComponent final : public Component
/// Arm a bounded polling window so MDNS.update() runs at MDNS_UPDATE_INTERVAL_MS
/// for MDNS_POLL_WINDOW_MS. A subsequent call replaces the previous window.
void start_polling_window_();
/// Cancel any active polling window.
void cancel_polling_window_();
bool ip_was_up_{false};
#endif
/// Helper to set up services and MAC buffers, then call platform-specific registration
using PlatformRegisterFn = void (*)(MDNSComponent *, StaticVector<MDNSService, MDNS_SERVICE_COUNT> &);
+6 -8
View File
@@ -51,16 +51,14 @@ void MDNSComponent::setup() {
void MDNSComponent::on_ip_state(const network::IPAddresses &ips, const network::IPAddress &,
const network::IPAddress &) {
const bool has_ip = ips[0].is_set();
if (has_ip && !this->ip_was_up_) {
// IP came up. LEAmDNS's internal lwIP callback will call _restart() shortly after
// (if it hasn't already) — arm the polling window so the probe/announce phase is
// serviced regardless of our relative timing vs the library's callback.
// ESPHome's WiFiIPStateListener only notifies on IP acquisition (GOT_IP events on
// ESP8266 — see wifi_component_esp8266.cpp), not on IP loss, so every notification
// represents a fresh IP that the LEAmDNS library's lwIP callback will trigger a
// _restart() for. Always re-arm the polling window — start_polling_window_() is
// idempotent (scheduler does atomic cancel-and-add on matching IDs).
if (ips[0].is_set()) {
this->start_polling_window_();
} else if (!has_ip && this->ip_was_up_) {
this->cancel_polling_window_();
}
this->ip_was_up_ = has_ip;
}
void MDNSComponent::on_shutdown() {
+14 -12
View File
@@ -91,19 +91,21 @@ void MDNSComponent::setup() {
#ifdef USE_MDNS_EVENT_DRIVEN_POLLING
void MDNSComponent::on_ip_state(const network::IPAddresses &ips, const network::IPAddress &,
const network::IPAddress &) {
const bool has_ip = ips[0].is_set();
if (has_ip && !this->ip_was_up_) {
if (!this->initialized_) {
this->setup_buffers_and_register_(register_rp2040);
this->initialized_ = true;
} else {
MDNS.notifyAPChange();
}
this->start_polling_window_();
} else if (!has_ip && this->ip_was_up_) {
this->cancel_polling_window_();
// ESPHome's WiFiIPStateListener only notifies on IP acquisition (see
// wifi_component_pico_w.cpp), not on IP loss, so every notification represents a
// fresh IP that needs a probe/announce cycle. The library's internal
// LwipIntf::stateUpCB is stubbed out on arduino-pico (see setup()), so we drive
// begin/restart ourselves from this callback.
if (!ips[0].is_set()) {
return;
}
this->ip_was_up_ = has_ip;
if (!this->initialized_) {
this->setup_buffers_and_register_(register_rp2040);
this->initialized_ = true;
} else {
MDNS.notifyAPChange();
}
this->start_polling_window_();
}
#endif