fix(unifi-protect): stop keying device identity on DHCP host/IP (#2082)

getNativeId matched cameras on host (IP) as a fallback identity key. IPs
are not stable: when DHCP recycles an address, a newly provisioned camera
resolves to an unrelated camera's nativeId. cleanDict() then overwrites the
original camera's mac/anonymousDeviceId entries, collapsing two cameras onto
one Scrypted device and dropping the other from the device list.

Match only on stable hardware identifiers (mac, anonymousDeviceId, id) and
stop maintaining the host map.
This commit is contained in:
Nick Berardi
2026-08-29 18:06:49 -04:00
committed by GitHub
parent b63b7df286
commit abd37e416d

View File

@@ -859,14 +859,18 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device
// for now fall back to old behavior which will be removed at a later date.
}
const { id, mac, anonymousDeviceId, host } = device;
const { id, mac, anonymousDeviceId } = device;
const idMaps = this.storageSettings.values.idMaps;
// try to find an existing nativeId given the mac and anonymous device id
// Try to find an existing nativeId using stable hardware identifiers.
// Do NOT match on host/IP: DHCP reassigns addresses, so a recycled IP
// would bind a newly provisioned camera to an unrelated camera's
// nativeId. The subsequent cleanDict() then clobbers the original
// camera's mac/anonymousDeviceId entries, collapsing two cameras onto
// a single device (and losing the other from the device list).
const found = (mac && idMaps.mac?.[mac])
|| (anonymousDeviceId && idMaps.anonymousDeviceId?.[anonymousDeviceId])
|| (id && idMaps.id?.[id])
|| (host && idMaps.host?.[host])
;
// use the found id if one exists (device got provisioned a new id), otherwise use the id provided by the device.
@@ -889,25 +893,20 @@ export class UnifiProtect extends ScryptedDeviceBase implements Settings, Device
// Clean existing mappings before adding new ones
idMaps.mac ||= {};
idMaps.anonymousDeviceId ||= {};
idMaps.host ||= {};
idMaps.id ||= {};
idMaps.nativeId ||= {};
cleanDict(idMaps.mac);
cleanDict(idMaps.anonymousDeviceId);
cleanDict(idMaps.host);
cleanDict(idMaps.id);
// map the mac, host, and anonymous device id to the native id.
// map the mac and anonymous device id to the native id.
if (mac) {
idMaps.mac[mac] = nativeId;
}
if (anonymousDeviceId) {
idMaps.anonymousDeviceId[anonymousDeviceId] = nativeId;
}
if (host) {
idMaps.host[host] = nativeId;
}
// map the id and native id to each other.
idMaps.id[id] = nativeId;