2323import contextlib
2424import logging
2525
26- from zeroconf import get_all_addresses , get_all_addresses_v6
26+ import ifaddr
27+
2728from zeroconf .asyncio import AsyncServiceInfo , AsyncZeroconf
2829
30+ _LOGGER = logging .getLogger ("interface_monitor" )
31+
2932
30- def address_snapshot () -> set [object ]:
31- """A hashable snapshot of the host's current IPv4 and IPv6 addresses ."""
32- return {* get_all_addresses ( ), * get_all_addresses_v6 () }
33+ def address_snapshot () -> set [tuple [ str , int ] ]:
34+ """A snapshot of the host's current addresses; a change triggers a reconcile ."""
35+ return {( str ( ip . ip ), ip . network_prefix ) for adapter in ifaddr . get_adapters () for ip in adapter . ips }
3336
3437
3538async def monitor_interfaces (aiozc : AsyncZeroconf , interval : float ) -> None :
@@ -38,10 +41,17 @@ async def monitor_interfaces(aiozc: AsyncZeroconf, interval: float) -> None:
3841 while True :
3942 await asyncio .sleep (interval )
4043 current = address_snapshot ()
41- if current != previous :
42- previous = current
44+ if current == previous :
45+ continue
46+ try :
4347 print ("Interfaces changed, reconciling sockets..." )
4448 await aiozc .async_update_interfaces ()
49+ except Exception :
50+ # Log and retry on the next tick rather than letting the monitor
51+ # die; leave ``previous`` unchanged so the change is re-attempted.
52+ _LOGGER .exception ("Interface reconcile failed; will retry" )
53+ else :
54+ previous = current
4555
4656
4757class AsyncRunner :
@@ -63,7 +73,8 @@ async def close(self, info: AsyncServiceInfo) -> None:
6373 self .monitor .cancel ()
6474 with contextlib .suppress (asyncio .CancelledError ):
6575 await self .monitor
66- await self .aiozc .async_unregister_service (info )
76+ # Await the goodbye broadcast so the TTL-0 records are actually sent.
77+ await (await self .aiozc .async_unregister_service (info ))
6778 await self .aiozc .async_close ()
6879
6980
0 commit comments