Version: Zone Configurator 2.2.5, EP Pro firmware 1.3.0
Steps
- Rename the device's Occupancy entity in HA (e.g. to
binary_sensor.living_room_presence), so it no longer matches binary_sensor.${name}_occupancy.
- Settings → Re-sync entities → map "Presence" manually → Continue. Save succeeds.
- Open Re-sync entities again.
Expected
"Presence" is pre-selected with the saved entity.
Actual
"Presence" shows "Select entity…" and "Unmatched required: 1" again, as if nothing had been saved. Pressing Continue at this point saves the incomplete auto-match and overwrites the earlier manual mapping.
Cause
In EntityDiscovery.tsx, existingMapping is loaded asynchronously in its own effect, but runDiscovery reads it inside a useCallback whose dependencies are only [deviceId, profileId]:
|
// Load existing device mapping to preserve user overrides during re-sync |
|
useEffect(() => { |
|
const loadExistingMapping = async () => { |
|
try { |
|
const mapping = await getMapping(deviceId); |
|
setExistingMapping(mapping); |
|
} catch { |
|
setExistingMapping(null); |
|
} |
|
}; |
|
loadExistingMapping(); |
|
}, [deviceId, getMapping]); |
|
|
|
const runDiscovery = useCallback(async () => { |
|
setStatus('loading'); |
|
setError(null); |
|
setValidationErrors(null); |
|
setManualOverrides({}); |
|
setDiscoveredServices({}); |
|
|
|
try { |
|
const result = await discoverEntities(deviceId, profileId); |
|
setDiscoveryResult(result); |
|
setAllEntities(result.deviceEntities); |
|
setDiscoveredServices(result.serviceMappings ?? {}); |
|
|
|
const conflictCount = result.results.filter((r) => r.matchConfidence === 'conflict').length; |
|
|
|
if (result.allMatched) { |
|
setStatus(conflictCount > 0 ? 'partial' : 'success'); |
|
} else { |
|
setStatus('partial'); |
|
// Auto-expand groups with unmatched entities |
|
const groups = groupMatchResultsByCategory(result.results); |
|
const groupsWithUnmatched = new Set<string>(); |
|
for (const [groupName, results] of Object.entries(groups)) { |
|
if (results.some((r) => !r.matchedEntityId && !r.isOptional)) { |
|
groupsWithUnmatched.add(groupName); |
|
} |
|
} |
|
setExpandedGroups(groupsWithUnmatched); |
|
} |
|
|
|
// Seed manual overrides from existing mapping so user-set values persist on re-sync |
|
if (existingMapping) { |
|
const overrides: Record<string, string> = {}; |
|
for (const match of result.results) { |
|
const key = match.templateKey; |
|
const m = existingMapping.mappings; |
|
const direct = m[key]; |
|
const withEntitySuffix = m[`${key}Entity`]; |
|
const withoutEntitySuffix = key.endsWith('Entity') ? m[key.replace(/Entity$/, '')] : undefined; |
|
const chosen = direct || withEntitySuffix || withoutEntitySuffix; |
|
if (chosen) { |
|
overrides[key] = chosen; |
|
} |
|
} |
|
setManualOverrides(overrides); |
|
} |
|
} catch (err) { |
|
setError((err as Error).message); |
|
setStatus('error'); |
|
|
|
// Try to at least get the device entities for manual mapping |
|
try { |
|
const { entities } = await getDeviceEntities(deviceId); |
|
setAllEntities(entities); |
|
} catch { |
|
// Ignore - we'll just have no entities |
|
} |
|
} |
|
}, [deviceId, profileId]); |
|
|
|
useEffect(() => { |
|
runDiscovery(); |
|
}, [runDiscovery]); |
runDiscovery runs once with the first-render closure, where existingMapping is still null, so the "Seed manual overrides from existing mapping" block never runs.
A possible fix is to load the mapping inside runDiscovery (const existing = await getMapping(deviceId)) and seed the overrides from that.
Version: Zone Configurator 2.2.5, EP Pro firmware 1.3.0
Steps
binary_sensor.living_room_presence), so it no longer matchesbinary_sensor.${name}_occupancy.Expected
"Presence" is pre-selected with the saved entity.
Actual
"Presence" shows "Select entity…" and "Unmatched required: 1" again, as if nothing had been saved. Pressing Continue at this point saves the incomplete auto-match and overwrites the earlier manual mapping.
Cause
In
EntityDiscovery.tsx,existingMappingis loaded asynchronously in its own effect, butrunDiscoveryreads it inside auseCallbackwhose dependencies are only[deviceId, profileId]:everything-presence-addons/everything-presence-mmwave-configurator/frontend/src/components/EntityDiscovery.tsx
Lines 51 to 126 in 2d439c3
runDiscoveryruns once with the first-render closure, whereexistingMappingis stillnull, so the "Seed manual overrides from existing mapping" block never runs.A possible fix is to load the mapping inside
runDiscovery(const existing = await getMapping(deviceId)) and seed the overrides from that.