Skip to content

Commit da4c3a7

Browse files
committed
v0.4.24: ZimaOS support, Docker bridge IP fix, hostname-based deduplication
1 parent 46b10d6 commit da4c3a7

2 files changed

Lines changed: 47 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ All notable changes to SMART Sniffer are documented here.
66

77
### Fixed
88
- Zeroconf discovery now correctly selects real LAN IPs (10.x, 192.168.x) over Docker bridge IPs (172.17.x) on container-based systems like ZimaOS/CasaOS
9+
- Switched config entry unique IDs from IP-based to hostname-based to prevent duplicate discoveries when mDNS reflectors surface the same agent on multiple IPs/VLANs; existing entries are migrated automatically
910

1011
### Added
1112
- Installer now probes for writable paths on immutable-rootfs platforms (ZimaOS, CasaOS); falls back to `/DATA/smartha-agent/` or `/opt/smartha-agent/`

custom_components/smart_sniffer/config_flow.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,64 @@ def _score(ip_str: str) -> int:
130130
candidates.sort(key=_score)
131131
return candidates[0]
132132

133+
def _migrate_legacy_unique_ids(self, hostname: str, host: str, port: int) -> None:
134+
"""Migrate existing config entries from IP-based to hostname-based unique IDs.
135+
136+
Before v0.4.24, unique IDs were "{ip}:{port}". This caused duplicates
137+
when mDNS reflectors or multi-homed hosts advertised multiple IPs.
138+
Now we use "smartha-{hostname}" for stable deduplication.
139+
140+
This scans existing entries and updates any that match by IP or hostname
141+
so the new discovery is properly deduplicated.
142+
"""
143+
for entry in self._async_current_entries():
144+
if entry.domain != DOMAIN:
145+
continue
146+
old_uid = entry.unique_id or ""
147+
# Already migrated.
148+
if old_uid.startswith("smartha-"):
149+
continue
150+
# Match by IP:port (old format) or by hostname in entry title/data.
151+
entry_host = entry.data.get(CONF_HOST, "")
152+
entry_port = entry.data.get(CONF_PORT, 0)
153+
entry_title = entry.title or ""
154+
if (
155+
old_uid == f"{host}:{port}"
156+
or (entry_host == host and entry_port == port)
157+
or hostname.lower() in entry_title.lower()
158+
):
159+
_LOGGER.info(
160+
"Migrating SMART Sniffer unique_id: %s → smartha-%s",
161+
old_uid,
162+
hostname,
163+
)
164+
self.hass.config_entries.async_update_entry(
165+
entry,
166+
unique_id=f"smartha-{hostname}",
167+
)
168+
133169
async def async_step_zeroconf(
134170
self, discovery_info: ZeroconfServiceInfo
135171
) -> ConfigFlowResult:
136172
"""Handle discovery via mDNS/Zeroconf."""
137173
host = self._pick_best_ip(discovery_info)
138174
port = discovery_info.port
139175
properties = discovery_info.properties
176+
hostname = properties.get("hostname", host)
140177

141-
# Deduplicate — don't prompt for agents already configured.
142-
await self.async_set_unique_id(f"{host}:{port}")
143-
self._abort_if_unique_id_configured()
178+
# Migrate any existing IP-based unique IDs to hostname-based.
179+
self._migrate_legacy_unique_ids(hostname, host, port)
180+
181+
# Deduplicate — hostname-based ID is stable across interfaces/VLANs.
182+
await self.async_set_unique_id(f"smartha-{hostname}")
183+
self._abort_if_unique_id_configured(
184+
updates={CONF_HOST: host} # Update IP if it changed (e.g. DHCP)
185+
)
144186

145187
# Stash discovery data for the confirmation step.
146188
self._discovery_host = host
147189
self._discovery_port = port
148-
self._discovery_hostname = properties.get("hostname", host)
190+
self._discovery_hostname = hostname
149191
self._discovery_drives = properties.get("drives", "?")
150192
self._discovery_auth = properties.get("auth", "0") == "1"
151193

0 commit comments

Comments
 (0)