perf: keep Zigbee route overlay within bundle guard

Issue: #457
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-09-05 10:50:19 +03:00
parent 58b1651570
commit 41b8e71248
35 changed files with 362 additions and 384 deletions
+1 -1
View File
@@ -11779,7 +11779,7 @@ export class HouseplanCard extends LitElement {
<svg class="iso-walls-svg" data-hp-live-viewbox="camera" viewBox="${view.x} ${view.y} ${view.w} ${view.h}"
preserveAspectRatio="xMidYMid meet" aria-hidden="true" pointer-events="none">
${this._renderIsoWalls(isoLayers, isoPanels)}
</svg>` : nothing}${renderZigbeeTopologyOverlay({ hass: this.hass, settings: this._settings, devices: this._renderDevices, registry: this._haRegistry, currentSpace: space.id, spaces: this._serverCfg?.spaces || [], viewKey: view, view: this._mode === 'view', kiosk: this._kiosk })}
</svg>` : nothing}${renderZigbeeTopologyOverlay({ hass: this.hass, settings: this._settings, devices: this._renderDevices, registry: this._haRegistry, currentSpace: space.id, spaces: this._serverCfg?.spaces, viewKey: view, view: this._mode === 'view', kiosk: this._kiosk })}
${''/* docs/CANVAS.md §6: an icon is a percentage of the PLAN and
scales with it when you zoom — the behaviour the card always
had, restored by the owner. `iconCqw` is `iconPct * iconUnit
+4 -3
View File
@@ -23,7 +23,7 @@ export class HpZigbeeTopologyOverlay extends LitElement {
devices: { attribute: false },
registry: { attribute: false },
currentSpace: { type: String, attribute: 'current-space' },
spaceTitles: { attribute: false },
spaces: { attribute: false },
viewKey: { attribute: false },
};
@@ -31,7 +31,7 @@ export class HpZigbeeTopologyOverlay extends LitElement {
devices: readonly DevItem[] = [];
registry!: HaRegistrySnapshot;
currentSpace = '';
spaceTitles: Readonly<Record<string, string>> = {};
spaces?: readonly { id?: unknown; title?: unknown }[];
viewKey: unknown;
private _runtime = EMPTY_RUNTIME;
private _hovered = '';
@@ -178,7 +178,8 @@ export class HpZigbeeTopologyOverlay extends LitElement {
private _targetText(target: ZigbeeParentTarget): string {
if (target.kind === 'remote-space') {
return this.spaceTitles[target.spaceId]?.trim()
const title = this.spaces?.find((space) => space.id === target.spaceId)?.title;
return (typeof title === 'string' && title.trim())
|| topologyT(langOf(this.hass), 'route_other_space');
}
return topologyT(langOf(this.hass), target.kind === 'unplaced-coordinator'
+2 -17
View File
@@ -4,36 +4,21 @@ import type { HaRegistrySnapshot } from './ha-binding-status';
import type { DevItem } from './types';
import type { ZigbeeTopologyHass } from './zigbee-topology-runtime';
const spaceTitleMemo = new WeakMap<object, { signature: string; titles: Readonly<Record<string, string>> }>();
function spaceTitlesOf(spaces: readonly { id?: unknown; title?: unknown }[]): Readonly<Record<string, string>> {
const signature = spaces.map((space) => `${String(space.id)}\u0000${String(space.title)}`).join('\u0001');
const cached = spaceTitleMemo.get(spaces);
if (cached?.signature === signature) return cached.titles;
const projected = Object.fromEntries(spaces.flatMap((space) => (
typeof space.id === 'string' && typeof space.title === 'string'
? [[space.id, space.title] as const] : []
)));
spaceTitleMemo.set(spaces, { signature, titles: projected });
return projected;
}
export function renderZigbeeTopologyOverlay(input: {
hass: ZigbeeTopologyHass;
settings: unknown;
devices: readonly DevItem[];
registry: HaRegistrySnapshot;
currentSpace: string;
spaces: readonly { id?: unknown; title?: unknown }[];
spaces?: readonly { id?: unknown; title?: unknown }[];
viewKey: unknown;
view: boolean;
kiosk: boolean;
}): TemplateResult | typeof nothing {
if (!input.view || input.kiosk || input.hass?.user?.is_admin !== true
|| !zigbeeTopologySettingsOf(input.settings).enabled) return nothing;
const spaceTitles = spaceTitlesOf(input.spaces);
void import('./hp-zigbee-topology-overlay');
return html`<hp-zigbee-topology-overlay data-hp-live-layer="camera" .hass=${input.hass} .devices=${input.devices}
.registry=${input.registry} .currentSpace=${input.currentSpace} .spaceTitles=${spaceTitles}
.registry=${input.registry} .currentSpace=${input.currentSpace} .spaces=${input.spaces}
.viewKey=${input.viewKey}></hp-zigbee-topology-overlay>`;
}
+4 -12
View File
@@ -107,20 +107,12 @@ function lqiOf(value: unknown): number | undefined {
return Number.isFinite(n) && n >= 0 && n <= 255 ? Math.round(n) : undefined;
}
const Z2M_RELATIONSHIPS: Readonly<Record<number, string>> = {
0: 'parent', 1: 'child', 2: 'sibling', 3: 'none', 4: 'previous_child',
};
const Z2M_RELATIONSHIPS = ['parent', 'child', 'sibling', 'none', 'previous_child'] as const;
function relationshipOf(value: unknown): string | undefined {
if (typeof value === 'number' && Number.isInteger(value)) return Z2M_RELATIONSHIPS[value];
if (typeof value !== 'string') return undefined;
const compact = value.trim().toLowerCase().replace(/[\s_-]+/g, '');
if (!compact) return undefined;
if (compact === 'previouschild') return 'previous_child';
if (compact === 'parent' || compact === 'child' || compact === 'sibling' || compact === 'none') {
return compact;
}
return value.trim().toLowerCase().slice(0, 40) || undefined;
if (typeof value === 'number') return Z2M_RELATIONSHIPS[value];
return typeof value === 'string'
? value.trim().toLowerCase().slice(0, 40) || undefined : undefined;
}
function observation(value: unknown): ZigbeeDirectionalObservation {