fix: align device catalog with plan rooms

Issue: #29
User-Visible: yes
This commit is contained in:
Matysh
2026-08-27 23:17:53 +00:00
committed by claude[bot]
parent 361ec47856
commit aa9444491d
10 changed files with 55 additions and 24 deletions
File diff suppressed because one or more lines are too long
+25
View File
@@ -39,6 +39,31 @@ const out = await page.evaluate(async () => {
result.onPlanHasRows = root().querySelectorAll('.device-inbox-row[data-category="on_plan"]').length > 0;
result.noHorizontalOverflow = !!dialog && dialog.scrollWidth <= dialog.clientWidth + 1;
// The catalog speaks in the plan's room names, not a stale HA Area name.
const initialRoomRow = c._deviceInboxRows()
.find((item) => item.areaId && c._areaToSpace[item.areaId]);
const roomTarget = initialRoomRow ? c._areaToSpace[initialRoomRow.areaId] : null;
const sourceRoom = roomTarget ? c._serverCfg.spaces.find((space) => space.id === roomTarget.space)
?.rooms?.find((room) => room.id === roomTarget.room.id) : null;
result.hasCatalogRowInPlanRoom = !!roomTarget && !!initialRoomRow && !!sourceRoom;
if (roomTarget && initialRoomRow && sourceRoom) {
const originalRoomName = sourceRoom?.name;
const planRoomName = 'Renamed room on plan';
sourceRoom.name = planRoomName;
c._cfgEpoch++;
c._deviceInboxMemo = null;
await c.updateComplete;
const roomRow = c._deviceInboxRows().find((item) => item.binding === initialRoomRow.binding);
result.planRoomNameWins = roomRow?.areaName === planRoomName;
result.planRoomNameIsSearchable = !!roomRow?.searchText.includes(planRoomName.toLowerCase());
sourceRoom.name = originalRoomName;
c._cfgEpoch++;
c._deviceInboxMemo = null;
} else {
result.planRoomNameWins = false;
result.planRoomNameIsSearchable = false;
}
const search = root().querySelector('.device-inbox-search');
search.value = 'Ceiling light';
search.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
+1 -1
View File
@@ -156,7 +156,7 @@ const res = await page.evaluate(async () => {
tabs()[1].click(); await c.updateComplete;
out.directSwitch = c._mode === 'devices';
out.devBar = !!sr().querySelector('.editbar.devbar');
out.devBarBtns = sr().querySelectorAll('.editbar.devbar .btn:not(.barclose)').length === 3; // add/show-all/rules (v1.33.2: Reset removed)
out.devBarBtns = sr().querySelectorAll('.editbar.devbar .btn:not(.barclose)').length === 2; // devices catalog + icon rules (#29)
const swapChrome = sr().querySelector('.editorchrome');
const swapInner = swapChrome.querySelector('.editorchrome-inner');
out.editorSwapAnimatesHeight = c._modeTransitionBusy
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -12,7 +12,7 @@
- The Device editor now has one searchable lifecycle catalog for devices on
the plan, available bindings, hidden markers and bindings available to add
again. It explains Home Assistant status separately, can find markers on the
plan, and keeps hide/show/add/re-add flows in one place
plan using the room names visible there, and keeps hide/show/add/re-add flows in one place
([#29](https://github.com/Matysh/houseplan-card/issues/29)).
## v1.68.1 — 2026-08-27
+1 -1
View File
@@ -18,7 +18,7 @@
- В редакторе устройств появился единый каталог с поиском: устройства
на плане, доступные привязки, скрытые маркеры и привязки для
повторного добавления. Статус Home Assistant объясняется отдельно, а
поиск на плане, скрытие, показ и повторное добавление собраны в одном
поиск использует видимые на плане имена комнат; скрытие, показ и повторное добавление собраны в одном
месте ([#29](https://github.com/Matysh/houseplan-card/issues/29)).
## v1.68.1 — 2026-08-27
Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 291 KiB

+12 -12
View File
@@ -2,7 +2,7 @@
"version": 1,
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
"sourceFingerprint": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceFingerprint": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"captureScriptSha256": "ce2e9542fed9dade3085be87d16f69adb2ac8262893ad78ad966b1b9673f2983",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -14,7 +14,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "36223106c073f07d8cc3ecf8eaab37192ebb2687daba65c5c21047d0b7890de0"
},
"view-touch": {
@@ -25,7 +25,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "41e3ba67f8db0e98f26f484293af83ef937c369ca5ca6a59a3350d8954c906f4"
},
"space-create": {
@@ -36,7 +36,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "0dc024726327f60f4a9ceaf3044381691f81f1090af81d1812e870f22d9343ba"
},
"room-contour-close": {
@@ -47,7 +47,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "44cfaa95bd51e2cd628400e28db0ad8b2f0cd904385845bd402494f3f5c0d93c"
},
"plan-context-tray": {
@@ -58,7 +58,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "e0662429b423fb74151b583dcc2c8635b001b637d03bbf7a6b16aec46399c3f8"
},
"device-editor": {
@@ -69,8 +69,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"imageSha256": "d0ffd31ce80bfde21ab75da356a5fc1af38246f2b301030880320620c228d89d"
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "827a5f0cd6f1c122a63a51d83569a2dbd87b70b3753266a3eb0a9f2986b1b911"
},
"device-display-preview": {
"file": "06-device-display-preview.png",
@@ -80,7 +80,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "2cdabae1f89c3286e4fac0ce30f757ee1690b707ab8a5488748b7cd420626160"
},
"background-editor": {
@@ -91,7 +91,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "30147bb00a90eea7136b4cee30995f6e6a9217b5132f3e8d3ad7471413b1af8a"
},
"room-card": {
@@ -102,7 +102,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
},
"device-info": {
@@ -113,7 +113,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "78c24f3a8aef0c2bad01bebc701a8127c43fb40f1a298df7b769375bc5b081fa",
"sourceSha256": "6d094396ebd90fad934bace295f8eee0e6dafda27d8f38dd31beac954ff93880",
"imageSha256": "dd492f53150b7149085daada5cce9eeae9bde9e7ea1d86679a54b3041f72f517"
}
}
-3
View File
@@ -152,7 +152,6 @@ export interface DeviceInboxRow {
canHide: boolean;
canShow: boolean;
canAdd: boolean;
canOpenHa: boolean;
}
export interface DeviceInboxInput {
@@ -242,7 +241,6 @@ export function buildDeviceInbox(input: DeviceInboxInput): DeviceInboxRow[] {
const active = status.kind === 'active';
const canFind = isRendered && (active
|| (status.kind === 'ha_disabled' && showHiddenOnPlan));
const canOpenHa = kind === 'device' || kind === 'entity';
const searchText = [
name, model, integrationByBinding[binding], areaNames[areaId], spaceNames[spaceId],
binding, candidate?.sub,
@@ -261,7 +259,6 @@ export function buildDeviceInbox(input: DeviceInboxInput): DeviceInboxRow[] {
canHide: category === 'on_plan' && active,
canShow: category === 'hidden' && active,
canAdd: (category === 'available' || category === 'readd') && active,
canOpenHa,
});
}
const statusRank = (row: DeviceInboxRow): number => row.status.kind === 'active' ? 1 : 0;
+11 -2
View File
@@ -14097,11 +14097,20 @@ class HouseplanCard extends LitElement {
const statuses = new Map<string, HaBindingStatus>();
for (const binding of bindings) statuses.set(binding, this._bindingStatus(binding));
const areaMap = this._areaToSpace;
const areaNames: Record<string, string> = {};
for (const [id, area] of Object.entries<any>(this.hass?.areas || {})) areaNames[id] = area?.name || id;
for (const [id, area] of Object.entries<any>(this.hass?.areas || {})) {
areaNames[id] = areaMap[id]?.room?.name || area?.name || id;
}
// A room may keep a valid HA area reference even when the current user has
// only a limited area-registry snapshot. Its plan-visible name is still
// authoritative for the catalog and search.
for (const [id, target] of Object.entries(areaMap)) {
areaNames[id] = target.room.name || areaNames[id] || id;
}
const spaceNames = Object.fromEntries(this._model.map((space) => [space.id, space.title]));
const spaceByArea = Object.fromEntries(
Object.entries(this._areaToSpace).map(([area, value]) => [area, value.space]),
Object.entries(areaMap).map(([area, value]) => [area, value.space]),
);
const integrationByBinding: Record<string, string> = {};
const devicePlatforms = new Map<string, Set<string>>();