mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-01 04:09:17 +00:00
feat: add contextual Zigbee topology hover
Issue: #54 User-Visible: yes
This commit is contained in:
@@ -100,6 +100,22 @@ export const CONFIG_FIELD_REGISTRY = Object.freeze([
|
||||
migration: 'safe point-in-room backfill; cross-source full import drops it',
|
||||
compatibility: 'older frontends ignore it; same-source backup preserves it',
|
||||
},
|
||||
{
|
||||
id: 'settings.zigbee_topology',
|
||||
schema: 'allow-extra',
|
||||
enforcedBy: 'zigbee-topology-settings.ts + explicit admin provider controls; #54',
|
||||
selector: { path: ['settings', 'zigbee_topology'] },
|
||||
storage: 'House Plan server config',
|
||||
type: '{ enabled?: boolean, z2m_base_topics?: string[] }',
|
||||
default: 'unset (= disabled; default Zigbee2MQTT base topic is presented in UI)',
|
||||
level: 'global',
|
||||
ui: 'General settings → Zigbee links',
|
||||
runtime: 'allows a mouse-only incident-neighbour overlay after explicit provider refresh',
|
||||
introduced: '#54',
|
||||
status: 'current',
|
||||
migration: 'none — topology snapshots remain memory-only',
|
||||
compatibility: 'older frontends ignore it; backend preserves it through allow-extra',
|
||||
},
|
||||
{
|
||||
id: 'markers[].display=ripple',
|
||||
enforcedBy: 'logic.ts normalizeDeviceDisplay (read) + editor-runtime _dropLegacySegments rewrites on every write',
|
||||
|
||||
@@ -5607,6 +5607,50 @@ const MUTANT_DEFINITIONS = [
|
||||
+ ' && fingerprint(current) !== attempt.attemptedFingerprint)) return false;',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'zigbee-topology-zha-read-starts-scan',
|
||||
guard: 'node --test test/zigbee-topology.test.mjs',
|
||||
because: 'hover diagnostics may read the existing ZHA snapshot but must never turn a read '
|
||||
+ 'into an implicit radio scan (#54 AC4)',
|
||||
patches: [{
|
||||
file: 'src/zigbee-topology-runtime.ts',
|
||||
find: " return normalizeZhaTopology(await hass.callWS({ type: 'zha/devices' }));",
|
||||
replace: " return normalizeZhaTopology(await hass.callWS({ type: 'zha/topology/update' }));",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'zigbee-topology-ambiguous-marker-selected',
|
||||
guard: 'node --test test/zigbee-topology.test.mjs',
|
||||
because: 'a Zigbee node with multiple drawable placements must fail closed instead of '
|
||||
+ 'drawing a plausible but false neighbour line (#54 AC8)',
|
||||
patches: [{
|
||||
file: 'src/zigbee-topology.ts',
|
||||
find: ' if (candidates.length === 1) placements.set(node.key, {',
|
||||
replace: ' if (candidates.length >= 1) placements.set(node.key, {',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'zigbee-topology-z2m-foreign-response-accepted',
|
||||
guard: 'node --test test/zigbee-topology.test.mjs',
|
||||
because: 'parallel Zigbee2MQTT requests share one response topic; only the matching '
|
||||
+ 'transaction may complete this snapshot (#54 AC6)',
|
||||
patches: [{
|
||||
file: 'src/zigbee-topology-runtime.ts',
|
||||
find: ' if (value && transactionOf(value) === transaction) responseResolve?.(value);',
|
||||
replace: ' if (value) responseResolve?.(value);',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'zigbee-topology-z2m-subscriptions-leak',
|
||||
guard: 'node --test test/zigbee-topology.test.mjs',
|
||||
because: 'both MQTT subscriptions must be released after success or failure so one manual '
|
||||
+ 'refresh cannot leave listeners processing later payloads (#54 AC6)',
|
||||
patches: [{
|
||||
file: 'src/zigbee-topology-runtime.ts',
|
||||
find: ' try { unsubscribe(); } catch { /* cleanup is best effort */ }',
|
||||
replace: ' try { void unsubscribe; } catch { /* cleanup is best effort */ }',
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
const mutationCardSource = readFileSync(join(repoRoot, 'src/houseplan-card.ts'), 'utf8');
|
||||
|
||||
@@ -27,6 +27,16 @@
|
||||
|
||||
/** @type {SmokeLink[]} */
|
||||
export const SMOKE_LINKS = [
|
||||
{
|
||||
symbols: [
|
||||
'renderZigbeeTopologyOverlay', 'resolveTopologyHover', 'mapTopologyNodes',
|
||||
'readZhaTopology', 'refreshZ2mTopology', 'zigbeeTopologySettingsOf',
|
||||
],
|
||||
smokes: ['smoke_zigbee_topology_hover.mjs'],
|
||||
because: 'the #54 production-bundle scenario proves default-off lazy loading, explicit provider '
|
||||
+ 'refresh, incident-only mouse hover, cross-space counting, pointer transparency and immediate '
|
||||
+ 'cleanup across leave, touch, editor and non-admin boundaries',
|
||||
},
|
||||
{
|
||||
symbols: [
|
||||
'beginVacuumRouteDraft', 'chooseVacuumRouteSpace', 'commitVacuumRouteDraft',
|
||||
|
||||
Reference in New Issue
Block a user