fix: улучшить доступность View (#565)

Issue: #565
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-09-14 07:06:16 +00:00
committed by claude[bot]
parent 6103e7de68
commit 6aca447fa0
73 changed files with 927 additions and 530 deletions
@@ -1,311 +1,311 @@
{
"schema": 1,
"fingerprint": "b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d",
"fingerprint": "668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991",
"entry": "houseplan-card.js",
"panelEntry": "houseplan-panel.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 290173,
"initialViewGzipBytes": 290252,
"initialPanelFiles": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-panel.js"
],
"initialPanelGzipBytes": 291697,
"initialPanelGzipBytes": 291775,
"initialPanelOnlyFiles": [
"houseplan-panel.js"
],
"initialPanelOnlyGzipBytes": 2326,
"initialPanelOnlyGzipBytes": 2329,
"lazyFiles": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/de-C6F-eg43.js",
"houseplan-assets/editor-Ce0BQpJ-.js",
"houseplan-assets/fr-DjiokT_b.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js",
"houseplan-assets/hp-zigbee-topology-overlay-BAqKJEV5.js",
"houseplan-assets/iso-scene-render-BAa5l9tq.js",
"houseplan-assets/live-interaction-runtime-DKhsH9o9.js",
"houseplan-assets/pdf-export-CTnhlVcd.js",
"houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js",
"houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/de-C6YxCDZc.js",
"houseplan-assets/editor-EFCjKUy0.js",
"houseplan-assets/fr-BKJEc804.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js",
"houseplan-assets/hp-zigbee-topology-overlay-DknAaLQb.js",
"houseplan-assets/iso-scene-render-BfcWAjpL.js",
"houseplan-assets/live-interaction-runtime-C65e5HA-.js",
"houseplan-assets/pdf-export-DOIJatzy.js",
"houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js",
"houseplan-assets/zigbee-topology-runtime-CgRErW8m.js"
],
"lazyGzipBytes": 417876,
"lazyGzipBytes": 418572,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/editor-Ce0BQpJ-.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/editor-EFCjKUy0.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js"
],
"lazyEditorGzipBytes": 205041,
"lazyEditorGzipBytes": 205056,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js"
],
"lazyOnboardingGzipBytes": 13914,
"lazyOnboardingGzipBytes": 13919,
"lazyLocaleFiles": [
"houseplan-assets/de-C6F-eg43.js",
"houseplan-assets/fr-DjiokT_b.js"
"houseplan-assets/de-C6YxCDZc.js",
"houseplan-assets/fr-BKJEc804.js"
],
"lazyLocaleGzipBytes": 54818,
"lazyLocaleGzipBytes": 54867,
"lazyIsometricFiles": [
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/iso-scene-render-BAa5l9tq.js"
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/iso-scene-render-BfcWAjpL.js"
],
"lazyIsometricGzipBytes": 13453,
"lazyIsometricGzipBytes": 13459,
"lazyFurnitureArtFiles": [
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js"
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js"
],
"lazyFurnitureArtGzipBytes": 10254,
"lazyFurnitureArtGzipBytes": 10249,
"lazyPdfFiles": [
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/pdf-export-CTnhlVcd.js"
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/pdf-export-DOIJatzy.js"
],
"lazyPdfGzipBytes": 120126,
"lazyPdfGzipBytes": 120121,
"files": [
{
"path": "houseplan-assets/backdrop-pick-DN-2tcOT.js",
"sha256": "a27dc1661c6203ce7d114a5985791f2d27d5dccd171f1c5965cc53455ee3e9eb",
"path": "houseplan-assets/backdrop-pick-CvrPdUHE.js",
"sha256": "f18c266ff57ede82c32fb37d78aa58ae990db5d0e0220d1f4331c1e21c674b1d",
"rawBytes": 19956,
"gzipBytes": 6879,
"gzipBytes": 6880,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-C6F-eg43.js",
"sha256": "a496b5f2164c8ece60bfd7819a992c8821cdc3a250da94ade633853f96ddd8d5",
"rawBytes": 91402,
"gzipBytes": 27693,
"path": "houseplan-assets/de-C6YxCDZc.js",
"sha256": "b3dde2c9526d2f3e9ca2583dfc90dba106c4493b7d24574b826031dd8f04c63e",
"rawBytes": 91437,
"gzipBytes": 27718,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-Ce0BQpJ-.js",
"sha256": "5b15efb75f8fcd388761011369a8733c97a7fd9c018398536e0d22a7eb7bc0a3",
"path": "houseplan-assets/editor-EFCjKUy0.js",
"sha256": "851fa5b9cd7084225fa06cf167d3329e802dca2ba32e773486aeb9e16c73629a",
"rawBytes": 3832,
"gzipBytes": 1583,
"gzipBytes": 1586,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-DjiokT_b.js",
"sha256": "9358eace06b200d704c840dab68669aead728ce5536c90862d5305ac81639a7f",
"rawBytes": 93667,
"gzipBytes": 27125,
"path": "houseplan-assets/fr-BKJEc804.js",
"sha256": "ad81af3ad6d911bf4d8a2ea7a739a6fd89427d7e496e36e0c24bf979ff42ba19",
"rawBytes": 93701,
"gzipBytes": 27149,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"sha256": "e38cd9cb67be92d2d9fd2e3ae0fa69523d214e448bef309897ebdeb0843245b3",
"path": "houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"sha256": "c24257131a7fadad206c7acbdeae3d39bf8f57efc107b409848d40eed36fb128",
"rawBytes": 33655,
"gzipBytes": 10254,
"gzipBytes": 10249,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/guard-BuxOGplD.js",
"sha256": "6f982643134192c4816627ae9c0c873c09fd480d2c3794662a96f8d4a653e062",
"path": "houseplan-assets/guard-BMBlK50U.js",
"sha256": "0e82964c9e805ff5dd1ae2000363aaab638b03da9561ea9d7fcb22e0b070b924",
"rawBytes": 518,
"gzipBytes": 366,
"gzipBytes": 370,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-sltdEKFS.js",
"sha256": "6979d29cb9740e8e37d4c3717932b63068be43ecf57c55e3d8a7cc45df7fdabc",
"rawBytes": 1030749,
"gzipBytes": 289371,
"path": "houseplan-assets/houseplan-card-chP6NBhK.js",
"sha256": "8a88d86c85a97ad97d0abd3858211b5f6d5d168ec6eff566036b96197e2d42dc",
"rawBytes": 1031082,
"gzipBytes": 289446,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-C6F-eg43.js",
"houseplan-assets/editor-Ce0BQpJ-.js",
"houseplan-assets/fr-DjiokT_b.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js",
"houseplan-assets/hp-zigbee-topology-overlay-BAqKJEV5.js",
"houseplan-assets/iso-scene-render-BAa5l9tq.js",
"houseplan-assets/live-interaction-runtime-DKhsH9o9.js",
"houseplan-assets/pdf-export-CTnhlVcd.js",
"houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js"
"houseplan-assets/de-C6YxCDZc.js",
"houseplan-assets/editor-EFCjKUy0.js",
"houseplan-assets/fr-BKJEc804.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js",
"houseplan-assets/hp-zigbee-topology-overlay-DknAaLQb.js",
"houseplan-assets/iso-scene-render-BfcWAjpL.js",
"houseplan-assets/live-interaction-runtime-C65e5HA-.js",
"houseplan-assets/pdf-export-DOIJatzy.js",
"houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"sha256": "8fc4aa226dc304ab75914a05c61e61dd1f578f11152358f762fbf450fb5c6d03",
"path": "houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"sha256": "20f076f7ec4c6c7bfb1e71c50a0db309ab3f368274fc4e0e716d67082da8ef50",
"rawBytes": 668940,
"gzipBytes": 178532,
"gzipBytes": 178538,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js"
"houseplan-assets/zigbee-topology-runtime-CgRErW8m.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js",
"sha256": "d43cc9955fd13308508529cff764d5607899e3424116f60a3b69c169e009229b",
"path": "houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js",
"sha256": "a8a4541a25b9600440dbff0cd88f7946b1e1a9b69757a5d7326a6007da76d84f",
"rawBytes": 28287,
"gzipBytes": 7035,
"gzipBytes": 7039,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-BAqKJEV5.js",
"sha256": "933f069fd8e49ba2cb84e44435291259710c2dee792293ea4deda05bab85192f",
"path": "houseplan-assets/hp-zigbee-topology-overlay-DknAaLQb.js",
"sha256": "77885083e418a824e4329f888ca7a9cc6f987f78188c72969578a510ecec7363",
"rawBytes": 10756,
"gzipBytes": 3679,
"gzipBytes": 3683,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js",
"houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js"
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js",
"houseplan-assets/zigbee-topology-runtime-CgRErW8m.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/iso-scene-render-BAa5l9tq.js",
"sha256": "b4acedbe48cb242fa676dd896bf10e01e152de12b51e25bca9f484fe5c560fc7",
"path": "houseplan-assets/iso-scene-render-BfcWAjpL.js",
"sha256": "7191548d53d1b65cb9cd63b2972c04f5b612e7bed59ccd8fe8d5b759c39376bf",
"rawBytes": 38562,
"gzipBytes": 13087,
"gzipBytes": 13089,
"isEntry": false,
"imports": [
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/live-interaction-runtime-DKhsH9o9.js",
"sha256": "943a780fe9b53872a85ccf3e772f73735489a7e625f6a13bfd91039ecd7ed524",
"rawBytes": 6233,
"gzipBytes": 2497,
"path": "houseplan-assets/live-interaction-runtime-C65e5HA-.js",
"sha256": "fd554312270581fb71bf0d854bb28b0e3c09248cf90330fc3506f4f076d050a2",
"rawBytes": 7884,
"gzipBytes": 3112,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/pdf-export-CTnhlVcd.js",
"sha256": "fa7107ab2bd2f10f011959429d097cf439326680e179ae082143a71b50c06c2b",
"path": "houseplan-assets/pdf-export-DOIJatzy.js",
"sha256": "0946991958e9bcadbed2df522562f1fb1128d1ba6512c56188bdf1266dda7163",
"rawBytes": 236244,
"gzipBytes": 109872,
"isEntry": false,
"imports": [
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-editor-C0mf6c_y.js",
"sha256": "69e89730f4ed2023504d4ee14a76ef6c750f06b61610623b767be3d4f5227b9b",
"path": "houseplan-assets/summary-panel-editor-B0Pr-g7s.js",
"sha256": "7c7ebb5493f4a0c39be7ecb29fd1377fb1fa74ec376701fc434f65d2d30e26f3",
"rawBytes": 14824,
"gzipBytes": 3397,
"gzipBytes": 3404,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js"
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-metrics-_3hH5UlC.js",
"sha256": "a151ee21a940503df39ee5cc8fdd044091486c1028ba54149f75f466bf20b3a7",
"path": "houseplan-assets/summary-panel-metrics-DT6qsOpv.js",
"sha256": "461a5d15b503e863b8385cf8279bf0f0e16cc704335c440a984468c91f3ac54e",
"rawBytes": 2610,
"gzipBytes": 1306,
"gzipBytes": 1309,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js",
"sha256": "925c98e3a20e79190d1dca7baf60a020086c65d6f38b76030916a77989ed0adb",
"path": "houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js",
"sha256": "91fb3818a781de5d386bb652592b927a8f5b3c4e11362c8555d00db7f99f2c04",
"rawBytes": 73366,
"gzipBytes": 20085,
"gzipBytes": 20088,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": [
"houseplan-assets/summary-panel-editor-C0mf6c_y.js",
"houseplan-assets/summary-panel-metrics-_3hH5UlC.js"
"houseplan-assets/summary-panel-editor-B0Pr-g7s.js",
"houseplan-assets/summary-panel-metrics-DT6qsOpv.js"
]
},
{
"path": "houseplan-assets/zigbee-topology-Nr9i4fmi.js",
"sha256": "b8bc9ef2f7ae94d1cb6fe8bbc61f405ca1d776748952f7e45c6269060b7db4f3",
"path": "houseplan-assets/zigbee-topology-E9um8AjR.js",
"sha256": "332d01ce8e41dbac9af4fa798bf3a806ea2de52412ae26c58f47eb3054701f26",
"rawBytes": 18961,
"gzipBytes": 7427,
"gzipBytes": 7433,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js",
"sha256": "de20368d5b24934a550a70dd51f247b615acb32cd717630a07509438fa0948e3",
"path": "houseplan-assets/zigbee-topology-runtime-CgRErW8m.js",
"sha256": "da42a4df320003f9ca4dc732069d7e1ca47402e0cc222d8227b786a52285f6e8",
"rawBytes": 3941,
"gzipBytes": 1762,
"gzipBytes": 1766,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js"
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "ba3f05f030017e34a51d285b74776e2e0199ec3da30ac394114510c90be0cfbd",
"sha256": "08dec1f8e4ef7c1624f10d49df909dce2c6c3037bbf462f233e627bc7ba2ff95",
"rawBytes": 1205,
"gzipBytes": 802,
"gzipBytes": 806,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-panel.js",
"sha256": "1fecde5b6428af6d91db6f774152c1338e8522a68383c4be499d9ff25888b68a",
"sha256": "6531186ab5b9e0b0833275a9887db0f4574aedd2fe466a0af87c90c4f63e94f0",
"rawBytes": 6165,
"gzipBytes": 2326,
"gzipBytes": 2329,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{m as e,l as o,t,e3 as s,A as a,e4 as i,e5 as l,e6 as n,dU as r,b as c}from"./houseplan-card-sltdEKFS.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{m as e,l as o,t,e3 as s,A as a,e4 as i,e5 as l,e6 as n,dU as r,b as c}from"./houseplan-card-chP6NBhK.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{dS as r,dT as t,dU as s}from"./houseplan-card-chP6NBhK.js";const e={},a=r(class extends t{constructor(){super(...arguments),this.ot=e}render(r,t){return t()}update(r,[t,e]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((r,t)=>r===this.ot[t]))return s}else if(this.ot===t)return s;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,e)}});export{a as i};
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{dS as r,dT as e,dU as t}from"./houseplan-card-sltdEKFS.js";const s={},d=r(class extends e{constructor(){super(...arguments),this.ot=s}render(r,e){return e()}update(r,[e,s]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((r,e)=>r===this.ot[e]))return t}else if(this.ot===e)return t;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,s)}});export{d as i};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{b as a,A as e}from"./houseplan-card-sltdEKFS.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded--HpxRg3N.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{b as a,A as e}from"./houseplan-card-chP6NBhK.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded-D2ugQG3G.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
<span class="summary-switch-caption"><strong>${b("summary.show_local")}</strong>
<small id="summary-local-show-hint">${b("summary.show_local_hint")}</small></span>
<input id="summary-local-show" data-summary-local-show type="checkbox"
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{e9 as e,L as t,a2 as n,J as i,an as o,N as r,bo as s,bn as a,a1 as l,a0 as c,ax as u,ea as d,eb as f,bq as m,ec as g}from"./houseplan-card-chP6NBhK.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=u(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const o=String(i.binding||"").indexOf(":");if(o<1)continue;const r=i.binding.slice(0,o),s=i.binding.slice(o+1),a="device"===r?e.registry.devices?.[s]:null,l="entity"===r?e.registry.entities?.[s]:null,c="device"===r?s:l?.device_id,u="device"===r?a?.area_id:l?.area_id||c&&e.registry.devices?.[c]?.area_id,d="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||u||"",f=d&&e.areaToSpace[d]||i.space||e.firstSpaceId;c&&e.registry.devices?.[c]&&e.spaceIds.has(f)&&(!n.devices.has(c)||"entity"===r&&n.liveEntities.has(s))&&t.add(c)}return t}function y(e,t){return e?.length?t?.length?g(e,t):e:t}function _(e,t){const n=l(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,o,r);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:c(e.rooms,t.walls,t.openCuts,i,t.cellCm,o,r)}}function*h(l,c,u=_){try{let d=0;for(const f of c){const c=l.spaces.find(e=>String(e?.id)===f.id);if(!c)continue;const m=e(c,f),g=u(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const l=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,o,r,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(l,m.physicalBodies)),yield}const _=m.cellCm/o;d+=a(v)*_*_/1e4}return d}catch{return null}}function p(e,t,n=_){const i=h(e,t,n);let o=i.next();for(;!o.done;)o=i.next();return o.value}function b(e,t,n,i){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:m(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(i||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function S(e,t){const n=e?.states?.[t];if(!n)return null;const i=d(e,t);return i?f(i,String(n.attributes?.unit_of_measurement||"")):null}export{h as cleanFloorAreaSteps,v as representedHaDeviceIds,_ as spaceWallGeometry,S as summaryEntityValue,b as summarySystemValue,p as totalCleanFloorAreaM2};
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{e9 as e,L as t,a2 as n,J as i,an as o,N as r,bo as s,bn as a,a1 as c,a0 as l,ax as d,ea as u,eb as f,bq as m,ec as g}from"./houseplan-card-sltdEKFS.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=d(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const o=String(i.binding||"").indexOf(":");if(o<1)continue;const r=i.binding.slice(0,o),s=i.binding.slice(o+1),a="device"===r?e.registry.devices?.[s]:null,c="entity"===r?e.registry.entities?.[s]:null,l="device"===r?s:c?.device_id,d="device"===r?a?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.area_id,u="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||d||"",f=u&&e.areaToSpace[u]||i.space||e.firstSpaceId;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===r&&n.liveEntities.has(s))&&t.add(l)}return t}function y(e,t){return e?.length?t?.length?g(e,t):e:t}function _(e,t){const n=c(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,o,r);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:l(e.rooms,t.walls,t.openCuts,i,t.cellCm,o,r)}}function*b(c,l,d=_){try{let u=0;for(const f of l){const l=c.spaces.find(e=>String(e?.id)===f.id);if(!l)continue;const m=e(l,f),g=d(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const c=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,o,r,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(c,m.physicalBodies)),yield}const _=m.cellCm/o;u+=a(v)*_*_/1e4}return u}catch{return null}}function h(e,t,n=_){const i=b(e,t,n);let o=i.next();for(;!o.done;)o=i.next();return o.value}function p(e,t,n,i){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:m(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(i||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function S(e,t){const n=e?.states?.[t];if(!n)return null;const i=u(e,t);return i?f(i,String(n.attributes?.unit_of_measurement||"")):null}export{b as cleanFloorAreaSteps,v as representedHaDeviceIds,_ as spaceWallGeometry,S as summaryEntityValue,p as summarySystemValue,h as totalCleanFloorAreaM2};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{n as t,b as n}from"./zigbee-topology-Nr9i4fmi.js";import{B as e}from"./houseplan-card-sltdEKFS.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{n as t,b as n}from"./zigbee-topology-E9um8AjR.js";import{B as e}from"./houseplan-card-chP6NBhK.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";try{await import("./houseplan-assets/houseplan-card-sltdEKFS.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";try{await import("./houseplan-assets/houseplan-card-chP6NBhK.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
File diff suppressed because one or more lines are too long
+108 -1
View File
@@ -88,11 +88,15 @@ const j2 = await page.evaluate(async () => {
state: node?.dataset.state || null,
alarmClass: node?.classList.contains('alarm') || false,
role: node?.getAttribute('role') || null,
segments: (node?.getAttribute('aria-label') || '').split(', ')
.map((value) => value.trim().toLowerCase()).filter(Boolean),
};
});
out.j2 = j2;
check('j2.alarm_has_accessible_state', /\S/.test(j2.label) && j2.state !== null, true);
check('j2.alarm_not_colour_only', j2.label.split(',').length >= 2, true);
check('j2.alarm_fact_is_not_repeated', new Set(j2.segments).size, j2.segments.length);
check('j2.alarm_is_spoken_once', j2.segments.filter((part) => part === 'alarm').length, 1);
// --- J3. «Сколько в спальне» -------------------------------------------------
// Оракул: числовое значение попадает в ДОСТУПНОЕ ИМЯ, а не только в рисунок.
@@ -143,15 +147,89 @@ const j4 = await page.evaluate(async () => {
const ring = leaf ? getComputedStyle(leaf).getPropertyValue('--device-ring-color').trim() : null;
const idle = card.renderRoot.querySelector('[data-hp="device"]:not(:focus-visible)');
const idleRing = idle ? getComputedStyle(idle).getPropertyValue('--device-ring-color').trim() : null;
const tip = card.renderRoot.querySelector('[data-hp-live-tip]');
const tipBox = tip?.getBoundingClientRect();
const focusedDevice = card._devices.find((device) => device.id === leaf?.dataset?.id);
const focusTooltip = {
visible: !!tip && !tip.hidden,
title: tip?.querySelector('b')?.textContent?.trim() || '',
text: tip?.textContent?.trim() || '',
expectedTitle: focusedDevice?.name || '',
inViewport: !!tipBox && tipBox.left >= 0 && tipBox.top >= 0
&& tipBox.right <= innerWidth && tipBox.bottom <= innerHeight,
source: card._tip?.source || null,
};
leaf?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true }));
await new Promise((resolve) => setTimeout(resolve, 150));
return { calls, focusVisible, ring, idleRing };
return { calls, focusVisible, ring, idleRing, focusTooltip };
});
out.j4 = { ...j4, ...tabStops };
check('j4.marker_reachable_by_tab', tabStops.id !== null, true);
check('j4.focus_is_visible', j4.focusVisible && j4.ring !== j4.idleRing, true);
check('j4.focus_shows_same_device_tooltip',
j4.focusTooltip.visible && j4.focusTooltip.title === j4.focusTooltip.expectedTitle, true);
check('j4.focus_tooltip_is_anchored_in_viewport',
j4.focusTooltip.source === 'focus' && j4.focusTooltip.inViewport, true);
check('j4.enter_calls_service', j4.calls.length > 0, true);
await page.keyboard.press('Tab');
const j4Next = await page.evaluate(() => {
const card = window.__card;
const tip = card.renderRoot.querySelector('[data-hp-live-tip]');
const leaf = (() => {
let node = document.activeElement;
while (node?.shadowRoot?.activeElement) node = node.shadowRoot.activeElement;
return node;
})();
return {
visible: !!tip && !tip.hidden,
activeTag: leaf?.tagName || null,
activeHp: leaf?.dataset?.hp || null,
activeId: leaf?.dataset?.id || null,
tipSource: card._tip?.source || null,
tipDevice: card._tip?.deviceId || null,
};
});
out.j4_next = j4Next;
check('j4.tab_moves_focus_tooltip_to_next_device',
j4Next.visible && j4Next.activeHp === 'device' && j4Next.activeId === j4Next.tipDevice
&& j4Next.tipSource === 'focus', true);
const j4Blur = await page.evaluate(() => {
const card = window.__card;
let leaf = document.activeElement;
while (leaf?.shadowRoot?.activeElement) leaf = leaf.shadowRoot.activeElement;
leaf?.blur?.();
const tip = card.renderRoot.querySelector('[data-hp-live-tip]');
return { hidden: !tip || tip.hidden, stateCleared: card._tip === null };
});
out.j4_blur = j4Blur;
check('j4.blur_hides_focus_tooltip', j4Blur.hidden && j4Blur.stateCleared, true);
const j4HoverParity = await page.evaluate((deviceId) => {
const card = window.__card;
const marker = card.renderRoot.querySelector(`[data-hp="device"][data-id="${deviceId}"]`);
const rect = marker?.getBoundingClientRect();
marker?.dispatchEvent(new PointerEvent('pointerover', {
bubbles: true, composed: true, pointerType: 'mouse',
clientX: rect?.left || 0, clientY: rect?.top || 0,
}));
const tip = card.renderRoot.querySelector('[data-hp-live-tip]');
const hoverText = tip?.textContent?.trim() || '';
card._showTip(new PointerEvent('pointermove', {
pointerType: 'mouse', clientX: innerWidth, clientY: innerHeight,
}), 'Viewport edge', 'clamp probe');
const edgeBox = tip?.getBoundingClientRect();
const edgeInViewport = !!edgeBox && edgeBox.left >= 0 && edgeBox.top >= 0
&& edgeBox.right <= innerWidth && edgeBox.bottom <= innerHeight;
card._clearTransientHover();
return { hoverText, edgeInViewport };
}, tabStops.id);
out.j4_hover_parity = j4HoverParity;
check('j4.focus_and_mouse_tooltip_content_match',
j4HoverParity.hoverText, j4.focusTooltip.text);
check('j4.tooltip_clamps_at_viewport_edge', j4HoverParity.edgeInViewport, true);
// --- J5. «Переключить этаж» --------------------------------------------------
const j5 = await page.evaluate(async () => {
const card = window.__card;
@@ -165,6 +243,9 @@ const j5 = await page.evaluate(async () => {
await new Promise((resolve) => setTimeout(resolve, 250));
const afterRooms = [...root.querySelectorAll('[data-hp="room"]')].map((node) => node.dataset.id).join(',');
const activeTab = root.querySelector('[data-hp="space-tab"].active');
const navigation = root.querySelector('nav.tabs');
const currentTabs = [...root.querySelectorAll('[data-hp="space-tab"][aria-current]')];
const add = root.querySelector('[data-hp="space-add"]');
return {
tabs: tabs.length, before, after: card._space,
roomsChanged: beforeRooms !== afterRooms,
@@ -173,12 +254,38 @@ const j5 = await page.evaluate(async () => {
ariaCurrent: activeTab?.getAttribute('aria-current'),
ariaPressed: activeTab?.getAttribute('aria-pressed'),
ariaSelected: activeTab?.getAttribute('aria-selected'),
navigationName: navigation?.getAttribute('aria-label') || '',
currentCount: currentTabs.length,
inactiveHaveNoCurrent: [...root.querySelectorAll('[data-hp="space-tab"]')]
.filter((tab) => tab !== activeTab).every((tab) => !tab.hasAttribute('aria-current')),
addHasNoCurrent: !add?.hasAttribute('aria-current'),
hasTablistRoles: !!root.querySelector('[role="tablist"], [role="tab"]'),
};
});
out.j5 = j5;
check('j5.floor_switched', j5.after !== j5.before, true);
check('j5.plan_rerendered', j5.roomsChanged, true);
check('j5.active_tab_matches_space', j5.activeTabIsCurrent, true);
check('j5.spaces_are_named_navigation', /\S/.test(j5.navigationName), true);
check('j5.only_current_space_is_programmatic',
j5.ariaCurrent === 'page' && j5.currentCount === 1 && j5.inactiveHaveNoCurrent
&& j5.addHasNoCurrent, true);
check('j5.native_button_navigation_is_kept', j5.hasTablistRoles, false);
const j5Keyboard = {};
for (const [key, expected] of [['Enter', j5.before], [' ', j5.after]]) {
const before = await page.evaluate(() => window.__card._space);
await page.evaluate((id) => {
window.__card.renderRoot.querySelector(`[data-hp="space-tab"][data-id="${id}"]`)?.focus();
}, expected);
await page.keyboard.press(key === ' ' ? 'Space' : key);
await settle();
const after = await page.evaluate(() => window.__card._space);
j5Keyboard[key === ' ' ? 'space' : 'enter'] = { before, after, expected };
}
out.j5_keyboard = j5Keyboard;
check('j5.enter_switches_space', j5Keyboard.enter.after, j5Keyboard.enter.expected);
check('j5.space_switches_space', j5Keyboard.space.after, j5Keyboard.space.expected);
// --- J6. «Вернулся после обновления» ----------------------------------------
const j6 = await page.evaluate(async () => {
+16
View File
@@ -4,6 +4,13 @@ const { page, browser } = await launch({ width: 900, height: 900 }, 1);
const res = await page.evaluate(async () => {
await customElements.whenDefined('houseplan-space-card');
const hass = window.__card.hass;
const alarmDevice = window.__card._devices.find((device) => device.id === 'd_leak');
if (alarmDevice?.primary && hass.states[alarmDevice.primary]) {
hass.states = {
...hass.states,
[alarmDevice.primary]: { ...hass.states[alarmDevice.primary], state: 'on' },
};
}
const spaceId = window.__card._model[0].id;
// Give the real static renderer structural voters that room-only fixtures
// cannot prove: an independent wall, saved draft, column and hosted door.
@@ -124,6 +131,11 @@ const res = await page.evaluate(async () => {
const pe = stage ? getComputedStyle(stage).pointerEvents : null;
const markers = card.renderRoot.querySelectorAll('.hp-static-stage .devlayer .dev').length;
const litMarker = card.renderRoot.querySelector('.hp-static-stage .dev[data-id="d_light1"]');
const alarmMarker = alarmDevice
? card.renderRoot.querySelector(`.hp-static-stage .dev[data-id="${alarmDevice.id}"]`)
: null;
const alarmSegments = (alarmMarker?.getAttribute('aria-label') || '').split(', ')
.map((value) => value.trim().toLowerCase()).filter(Boolean);
const btn = card.renderRoot.querySelector('.hp-static-btn');
// deep-link: clicking the button pushes #space=<id>
@@ -143,6 +155,9 @@ const res = await page.evaluate(async () => {
markers,
litMarkerOn: !!litMarker?.classList.contains('on'),
sharedFacePresent: !!litMarker?.querySelector('ha-icon'),
alarmLabelHasNoRepeats: alarmSegments.length >= 2
&& new Set(alarmSegments).size === alarmSegments.length
&& alarmSegments.filter((part) => part === 'alarm').length === 1,
omittedTitle: card.renderRoot.querySelector('.hp-static-title')?.textContent?.trim() || null,
namedTitle: named.renderRoot.querySelector('.hp-static-title')?.textContent?.trim() || null,
compactHasTitle: !!compact.renderRoot.querySelector('.hp-static-title'),
@@ -209,6 +224,7 @@ const ok =
res.markers > 0 &&
res.litMarkerOn &&
res.sharedFacePresent &&
res.alarmLabelHasNoRepeats &&
!!res.omittedTitle &&
res.namedTitle === 'Named floor' &&
!res.compactHasTitle &&
+145 -145
View File
@@ -1,311 +1,311 @@
{
"schema": 1,
"fingerprint": "b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d",
"fingerprint": "668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991",
"entry": "houseplan-card.js",
"panelEntry": "houseplan-panel.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 290173,
"initialViewGzipBytes": 290252,
"initialPanelFiles": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-panel.js"
],
"initialPanelGzipBytes": 291697,
"initialPanelGzipBytes": 291775,
"initialPanelOnlyFiles": [
"houseplan-panel.js"
],
"initialPanelOnlyGzipBytes": 2326,
"initialPanelOnlyGzipBytes": 2329,
"lazyFiles": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/de-C6F-eg43.js",
"houseplan-assets/editor-Ce0BQpJ-.js",
"houseplan-assets/fr-DjiokT_b.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js",
"houseplan-assets/hp-zigbee-topology-overlay-BAqKJEV5.js",
"houseplan-assets/iso-scene-render-BAa5l9tq.js",
"houseplan-assets/live-interaction-runtime-DKhsH9o9.js",
"houseplan-assets/pdf-export-CTnhlVcd.js",
"houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js",
"houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/de-C6YxCDZc.js",
"houseplan-assets/editor-EFCjKUy0.js",
"houseplan-assets/fr-BKJEc804.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js",
"houseplan-assets/hp-zigbee-topology-overlay-DknAaLQb.js",
"houseplan-assets/iso-scene-render-BfcWAjpL.js",
"houseplan-assets/live-interaction-runtime-C65e5HA-.js",
"houseplan-assets/pdf-export-DOIJatzy.js",
"houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js",
"houseplan-assets/zigbee-topology-runtime-CgRErW8m.js"
],
"lazyGzipBytes": 417876,
"lazyGzipBytes": 418572,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/editor-Ce0BQpJ-.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/editor-EFCjKUy0.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js"
],
"lazyEditorGzipBytes": 205041,
"lazyEditorGzipBytes": 205056,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js"
],
"lazyOnboardingGzipBytes": 13914,
"lazyOnboardingGzipBytes": 13919,
"lazyLocaleFiles": [
"houseplan-assets/de-C6F-eg43.js",
"houseplan-assets/fr-DjiokT_b.js"
"houseplan-assets/de-C6YxCDZc.js",
"houseplan-assets/fr-BKJEc804.js"
],
"lazyLocaleGzipBytes": 54818,
"lazyLocaleGzipBytes": 54867,
"lazyIsometricFiles": [
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/iso-scene-render-BAa5l9tq.js"
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/iso-scene-render-BfcWAjpL.js"
],
"lazyIsometricGzipBytes": 13453,
"lazyIsometricGzipBytes": 13459,
"lazyFurnitureArtFiles": [
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js"
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js"
],
"lazyFurnitureArtGzipBytes": 10254,
"lazyFurnitureArtGzipBytes": 10249,
"lazyPdfFiles": [
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/pdf-export-CTnhlVcd.js"
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/pdf-export-DOIJatzy.js"
],
"lazyPdfGzipBytes": 120126,
"lazyPdfGzipBytes": 120121,
"files": [
{
"path": "houseplan-assets/backdrop-pick-DN-2tcOT.js",
"sha256": "a27dc1661c6203ce7d114a5985791f2d27d5dccd171f1c5965cc53455ee3e9eb",
"path": "houseplan-assets/backdrop-pick-CvrPdUHE.js",
"sha256": "f18c266ff57ede82c32fb37d78aa58ae990db5d0e0220d1f4331c1e21c674b1d",
"rawBytes": 19956,
"gzipBytes": 6879,
"gzipBytes": 6880,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-C6F-eg43.js",
"sha256": "a496b5f2164c8ece60bfd7819a992c8821cdc3a250da94ade633853f96ddd8d5",
"rawBytes": 91402,
"gzipBytes": 27693,
"path": "houseplan-assets/de-C6YxCDZc.js",
"sha256": "b3dde2c9526d2f3e9ca2583dfc90dba106c4493b7d24574b826031dd8f04c63e",
"rawBytes": 91437,
"gzipBytes": 27718,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-Ce0BQpJ-.js",
"sha256": "5b15efb75f8fcd388761011369a8733c97a7fd9c018398536e0d22a7eb7bc0a3",
"path": "houseplan-assets/editor-EFCjKUy0.js",
"sha256": "851fa5b9cd7084225fa06cf167d3329e802dca2ba32e773486aeb9e16c73629a",
"rawBytes": 3832,
"gzipBytes": 1583,
"gzipBytes": 1586,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-DjiokT_b.js",
"sha256": "9358eace06b200d704c840dab68669aead728ce5536c90862d5305ac81639a7f",
"rawBytes": 93667,
"gzipBytes": 27125,
"path": "houseplan-assets/fr-BKJEc804.js",
"sha256": "ad81af3ad6d911bf4d8a2ea7a739a6fd89427d7e496e36e0c24bf979ff42ba19",
"rawBytes": 93701,
"gzipBytes": 27149,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"sha256": "e38cd9cb67be92d2d9fd2e3ae0fa69523d214e448bef309897ebdeb0843245b3",
"path": "houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"sha256": "c24257131a7fadad206c7acbdeae3d39bf8f57efc107b409848d40eed36fb128",
"rawBytes": 33655,
"gzipBytes": 10254,
"gzipBytes": 10249,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/guard-BuxOGplD.js",
"sha256": "6f982643134192c4816627ae9c0c873c09fd480d2c3794662a96f8d4a653e062",
"path": "houseplan-assets/guard-BMBlK50U.js",
"sha256": "0e82964c9e805ff5dd1ae2000363aaab638b03da9561ea9d7fcb22e0b070b924",
"rawBytes": 518,
"gzipBytes": 366,
"gzipBytes": 370,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-sltdEKFS.js",
"sha256": "6979d29cb9740e8e37d4c3717932b63068be43ecf57c55e3d8a7cc45df7fdabc",
"rawBytes": 1030749,
"gzipBytes": 289371,
"path": "houseplan-assets/houseplan-card-chP6NBhK.js",
"sha256": "8a88d86c85a97ad97d0abd3858211b5f6d5d168ec6eff566036b96197e2d42dc",
"rawBytes": 1031082,
"gzipBytes": 289446,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-C6F-eg43.js",
"houseplan-assets/editor-Ce0BQpJ-.js",
"houseplan-assets/fr-DjiokT_b.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js",
"houseplan-assets/hp-zigbee-topology-overlay-BAqKJEV5.js",
"houseplan-assets/iso-scene-render-BAa5l9tq.js",
"houseplan-assets/live-interaction-runtime-DKhsH9o9.js",
"houseplan-assets/pdf-export-CTnhlVcd.js",
"houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js"
"houseplan-assets/de-C6YxCDZc.js",
"houseplan-assets/editor-EFCjKUy0.js",
"houseplan-assets/fr-BKJEc804.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js",
"houseplan-assets/hp-zigbee-topology-overlay-DknAaLQb.js",
"houseplan-assets/iso-scene-render-BfcWAjpL.js",
"houseplan-assets/live-interaction-runtime-C65e5HA-.js",
"houseplan-assets/pdf-export-DOIJatzy.js",
"houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-C9YWmQNQ.js",
"sha256": "8fc4aa226dc304ab75914a05c61e61dd1f578f11152358f762fbf450fb5c6d03",
"path": "houseplan-assets/houseplan-editor-runtime-FHl9qBUM.js",
"sha256": "20f076f7ec4c6c7bfb1e71c50a0db309ab3f368274fc4e0e716d67082da8ef50",
"rawBytes": 668940,
"gzipBytes": 178532,
"gzipBytes": 178538,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js"
"houseplan-assets/zigbee-topology-runtime-CgRErW8m.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BuOtxAIl.js",
"sha256": "d43cc9955fd13308508529cff764d5607899e3424116f60a3b69c169e009229b",
"path": "houseplan-assets/houseplan-onboarding-runtime-DqKk60fa.js",
"sha256": "a8a4541a25b9600440dbff0cd88f7946b1e1a9b69757a5d7326a6007da76d84f",
"rawBytes": 28287,
"gzipBytes": 7035,
"gzipBytes": 7039,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DN-2tcOT.js",
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/backdrop-pick-CvrPdUHE.js",
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-BAqKJEV5.js",
"sha256": "933f069fd8e49ba2cb84e44435291259710c2dee792293ea4deda05bab85192f",
"path": "houseplan-assets/hp-zigbee-topology-overlay-DknAaLQb.js",
"sha256": "77885083e418a824e4329f888ca7a9cc6f987f78188c72969578a510ecec7363",
"rawBytes": 10756,
"gzipBytes": 3679,
"gzipBytes": 3683,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js",
"houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js"
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js",
"houseplan-assets/zigbee-topology-runtime-CgRErW8m.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/iso-scene-render-BAa5l9tq.js",
"sha256": "b4acedbe48cb242fa676dd896bf10e01e152de12b51e25bca9f484fe5c560fc7",
"path": "houseplan-assets/iso-scene-render-BfcWAjpL.js",
"sha256": "7191548d53d1b65cb9cd63b2972c04f5b612e7bed59ccd8fe8d5b759c39376bf",
"rawBytes": 38562,
"gzipBytes": 13087,
"gzipBytes": 13089,
"isEntry": false,
"imports": [
"houseplan-assets/guard-BuxOGplD.js",
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/guard-BMBlK50U.js",
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/live-interaction-runtime-DKhsH9o9.js",
"sha256": "943a780fe9b53872a85ccf3e772f73735489a7e625f6a13bfd91039ecd7ed524",
"rawBytes": 6233,
"gzipBytes": 2497,
"path": "houseplan-assets/live-interaction-runtime-C65e5HA-.js",
"sha256": "fd554312270581fb71bf0d854bb28b0e3c09248cf90330fc3506f4f076d050a2",
"rawBytes": 7884,
"gzipBytes": 3112,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/pdf-export-CTnhlVcd.js",
"sha256": "fa7107ab2bd2f10f011959429d097cf439326680e179ae082143a71b50c06c2b",
"path": "houseplan-assets/pdf-export-DOIJatzy.js",
"sha256": "0946991958e9bcadbed2df522562f1fb1128d1ba6512c56188bdf1266dda7163",
"rawBytes": 236244,
"gzipBytes": 109872,
"isEntry": false,
"imports": [
"houseplan-assets/furniture-plan-art.generated-B24_xJbE.js",
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/furniture-plan-art.generated-BLtR621S.js",
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-editor-C0mf6c_y.js",
"sha256": "69e89730f4ed2023504d4ee14a76ef6c750f06b61610623b767be3d4f5227b9b",
"path": "houseplan-assets/summary-panel-editor-B0Pr-g7s.js",
"sha256": "7c7ebb5493f4a0c39be7ecb29fd1377fb1fa74ec376701fc434f65d2d30e26f3",
"rawBytes": 14824,
"gzipBytes": 3397,
"gzipBytes": 3404,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js"
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-metrics-_3hH5UlC.js",
"sha256": "a151ee21a940503df39ee5cc8fdd044091486c1028ba54149f75f466bf20b3a7",
"path": "houseplan-assets/summary-panel-metrics-DT6qsOpv.js",
"sha256": "461a5d15b503e863b8385cf8279bf0f0e16cc704335c440a984468c91f3ac54e",
"rawBytes": 2610,
"gzipBytes": 1306,
"gzipBytes": 1309,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-runtime-loaded--HpxRg3N.js",
"sha256": "925c98e3a20e79190d1dca7baf60a020086c65d6f38b76030916a77989ed0adb",
"path": "houseplan-assets/summary-panel-runtime-loaded-D2ugQG3G.js",
"sha256": "91fb3818a781de5d386bb652592b927a8f5b3c4e11362c8555d00db7f99f2c04",
"rawBytes": 73366,
"gzipBytes": 20085,
"gzipBytes": 20088,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": [
"houseplan-assets/summary-panel-editor-C0mf6c_y.js",
"houseplan-assets/summary-panel-metrics-_3hH5UlC.js"
"houseplan-assets/summary-panel-editor-B0Pr-g7s.js",
"houseplan-assets/summary-panel-metrics-DT6qsOpv.js"
]
},
{
"path": "houseplan-assets/zigbee-topology-Nr9i4fmi.js",
"sha256": "b8bc9ef2f7ae94d1cb6fe8bbc61f405ca1d776748952f7e45c6269060b7db4f3",
"path": "houseplan-assets/zigbee-topology-E9um8AjR.js",
"sha256": "332d01ce8e41dbac9af4fa798bf3a806ea2de52412ae26c58f47eb3054701f26",
"rawBytes": 18961,
"gzipBytes": 7427,
"gzipBytes": 7433,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-BWmqNI5x.js",
"sha256": "de20368d5b24934a550a70dd51f247b615acb32cd717630a07509438fa0948e3",
"path": "houseplan-assets/zigbee-topology-runtime-CgRErW8m.js",
"sha256": "da42a4df320003f9ca4dc732069d7e1ca47402e0cc222d8227b786a52285f6e8",
"rawBytes": 3941,
"gzipBytes": 1762,
"gzipBytes": 1766,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js",
"houseplan-assets/zigbee-topology-Nr9i4fmi.js"
"houseplan-assets/houseplan-card-chP6NBhK.js",
"houseplan-assets/zigbee-topology-E9um8AjR.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "ba3f05f030017e34a51d285b74776e2e0199ec3da30ac394114510c90be0cfbd",
"sha256": "08dec1f8e4ef7c1624f10d49df909dce2c6c3037bbf462f233e627bc7ba2ff95",
"rawBytes": 1205,
"gzipBytes": 802,
"gzipBytes": 806,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
},
{
"path": "houseplan-panel.js",
"sha256": "1fecde5b6428af6d91db6f774152c1338e8522a68383c4be499d9ff25888b68a",
"sha256": "6531186ab5b9e0b0833275a9887db0f4574aedd2fe466a0af87c90c4f63e94f0",
"rawBytes": 6165,
"gzipBytes": 2326,
"gzipBytes": 2329,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-sltdEKFS.js"
"houseplan-assets/houseplan-card-chP6NBhK.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{m as e,l as o,t,e3 as s,A as a,e4 as i,e5 as l,e6 as n,dU as r,b as c}from"./houseplan-card-sltdEKFS.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{m as e,l as o,t,e3 as s,A as a,e4 as i,e5 as l,e6 as n,dU as r,b as c}from"./houseplan-card-chP6NBhK.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{dS as r,dT as t,dU as s}from"./houseplan-card-chP6NBhK.js";const e={},a=r(class extends t{constructor(){super(...arguments),this.ot=e}render(r,t){return t()}update(r,[t,e]){if(Array.isArray(t)){if(Array.isArray(this.ot)&&this.ot.length===t.length&&t.every((r,t)=>r===this.ot[t]))return s}else if(this.ot===t)return s;return this.ot=Array.isArray(t)?Array.from(t):t,this.render(t,e)}});export{a as i};
-1
View File
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{dS as r,dT as e,dU as t}from"./houseplan-card-sltdEKFS.js";const s={},d=r(class extends e{constructor(){super(...arguments),this.ot=s}render(r,e){return e()}update(r,[e,s]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((r,e)=>r===this.ot[e]))return t}else if(this.ot===e)return t;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,s)}});export{d as i};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{b as a,A as e}from"./houseplan-card-sltdEKFS.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded--HpxRg3N.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{b as a,A as e}from"./houseplan-card-chP6NBhK.js";import{s,n as t,a as l,m as r,b as o,c as i,d as u}from"./summary-panel-runtime-loaded-D2ugQG3G.js";const m=m=>{const{host:c,dialog:d,problems:n,t:b}=m,y=n.find(a=>"error"===a.kind),$=d.localShow!==d.baseLocalShow||!d.localOnly&&!s(t(d.draft),d.base),p=$&&!d.busy&&!y,v=a=>n.find(e=>e.path===a),h=a=>a?b(`summary.problem.${a.code}`):"",k=()=>m.close(),g=a`<label class="summary-switch" for="summary-local-show">
<span class="summary-switch-caption"><strong>${b("summary.show_local")}</strong>
<small id="summary-local-show-hint">${b("summary.show_local_hint")}</small></span>
<input id="summary-local-show" data-summary-local-show type="checkbox"
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{e9 as e,L as t,a2 as n,J as i,an as o,N as r,bo as s,bn as a,a1 as l,a0 as c,ax as u,ea as d,eb as f,bq as m,ec as g}from"./houseplan-card-chP6NBhK.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=u(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const o=String(i.binding||"").indexOf(":");if(o<1)continue;const r=i.binding.slice(0,o),s=i.binding.slice(o+1),a="device"===r?e.registry.devices?.[s]:null,l="entity"===r?e.registry.entities?.[s]:null,c="device"===r?s:l?.device_id,u="device"===r?a?.area_id:l?.area_id||c&&e.registry.devices?.[c]?.area_id,d="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||u||"",f=d&&e.areaToSpace[d]||i.space||e.firstSpaceId;c&&e.registry.devices?.[c]&&e.spaceIds.has(f)&&(!n.devices.has(c)||"entity"===r&&n.liveEntities.has(s))&&t.add(c)}return t}function y(e,t){return e?.length?t?.length?g(e,t):e:t}function _(e,t){const n=l(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,o,r);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:c(e.rooms,t.walls,t.openCuts,i,t.cellCm,o,r)}}function*h(l,c,u=_){try{let d=0;for(const f of c){const c=l.spaces.find(e=>String(e?.id)===f.id);if(!c)continue;const m=e(c,f),g=u(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const l=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,o,r,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(l,m.physicalBodies)),yield}const _=m.cellCm/o;d+=a(v)*_*_/1e4}return d}catch{return null}}function p(e,t,n=_){const i=h(e,t,n);let o=i.next();for(;!o.done;)o=i.next();return o.value}function b(e,t,n,i){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:m(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(i||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function S(e,t){const n=e?.states?.[t];if(!n)return null;const i=d(e,t);return i?f(i,String(n.attributes?.unit_of_measurement||"")):null}export{h as cleanFloorAreaSteps,v as representedHaDeviceIds,_ as spaceWallGeometry,S as summaryEntityValue,b as summarySystemValue,p as totalCleanFloorAreaM2};
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{e9 as e,L as t,a2 as n,J as i,an as o,N as r,bo as s,bn as a,a1 as c,a0 as l,ax as d,ea as u,eb as f,bq as m,ec as g}from"./houseplan-card-sltdEKFS.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=d(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const o=String(i.binding||"").indexOf(":");if(o<1)continue;const r=i.binding.slice(0,o),s=i.binding.slice(o+1),a="device"===r?e.registry.devices?.[s]:null,c="entity"===r?e.registry.entities?.[s]:null,l="device"===r?s:c?.device_id,d="device"===r?a?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.area_id,u="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||d||"",f=u&&e.areaToSpace[u]||i.space||e.firstSpaceId;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===r&&n.liveEntities.has(s))&&t.add(l)}return t}function y(e,t){return e?.length?t?.length?g(e,t):e:t}function _(e,t){const n=c(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,o,r);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:l(e.rooms,t.walls,t.openCuts,i,t.cellCm,o,r)}}function*b(c,l,d=_){try{let u=0;for(const f of l){const l=c.spaces.find(e=>String(e?.id)===f.id);if(!l)continue;const m=e(l,f),g=d(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const c=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,o,r,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(c,m.physicalBodies)),yield}const _=m.cellCm/o;u+=a(v)*_*_/1e4}return u}catch{return null}}function h(e,t,n=_){const i=b(e,t,n);let o=i.next();for(;!o.done;)o=i.next();return o.value}function p(e,t,n,i){if("system"!==e.type)return null;if("device_count"===e.key)return null===t.deviceCount?null:String(t.deviceCount);if("total_area"===e.key)return null===t.areaM2?null:m(t.areaM2,"mi"===n?.config?.unit_system?.length);try{return new Intl.DateTimeFormat(i||void 0,{dateStyle:"short",timeStyle:"short",timeZone:n?.config?.time_zone||void 0}).format(t.now)}catch{return t.now.toLocaleString()}}function S(e,t){const n=e?.states?.[t];if(!n)return null;const i=u(e,t);return i?f(i,String(n.attributes?.unit_of_measurement||"")):null}export{b as cleanFloorAreaSteps,v as representedHaDeviceIds,_ as spaceWallGeometry,S as summaryEntityValue,p as summarySystemValue,h as totalCleanFloorAreaM2};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";import{n as t,b as n}from"./zigbee-topology-Nr9i4fmi.js";import{B as e}from"./houseplan-card-sltdEKFS.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";import{n as t,b as n}from"./zigbee-topology-E9um8AjR.js";import{B as e}from"./houseplan-card-chP6NBhK.js";const o=new WeakMap;function i(t){const n=function(t){const n=t?.connection||t;return!n||"object"!=typeof n&&"function"!=typeof n?null:n}(t);if(!n)return null;let e=o.get(n);return e||(e={revision:0,topologies:[],states:{},listeners:new Set,inflight:new Map},o.set(n,e)),e}function r(t,n,e){t.states={...t.states,[n]:e},function(t){t.revision++;for(const n of t.listeners)n()}(t)}function s(t){const n=t?.code;if("permission"===n||"unsupported"===n||"timeout"===n||"invalid_topic"===n||"invalid_payload"===n)return n;const e=String(t?.message||"").toLowerCase();return e.includes("unauthor")||e.includes("permission")?"permission":e.includes("unknown_command")||e.includes("not found")?"unsupported":"provider"}function a(t){return Object.assign(new Error(t),{code:t})}function c(t,n,e){const o=t.inflight.get(n);if(o)return o;r(t,n,{phase:"loading"});const i=e().then(n=>{if(!n.nodes.length&&n.warnings.some(t=>"invalid_payload"===t.code))throw a("invalid_payload");!function(t,n){t.topologies=[...t.topologies.filter(t=>!(t.provider===n.provider&&t.instanceId===n.instanceId)),n],r(t,"zha"===n.provider?"zha":`z2m:${n.instanceId}`,{phase:"ready",obtainedAt:n.obtainedAt,partial:n.warnings.length>0})}(t,n)}).catch(e=>{r(t,n,{phase:"error",error:s(e)})}).finally(()=>t.inflight.delete(n));return t.inflight.set(n,i),i}function l(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function u(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function g(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const l=e(o);return c(s,`z2m:${l||String(o)}`,async()=>{if(p(t),!l)throw a("invalid_topic");const e=t.connection,o=e?.subscribeMessage;if("function"!=typeof o||"function"!=typeof t?.callService)throw a("unsupported");const i=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),s=Date.now()+Math.max(1,r);let c=!1,u=null,f=null,m=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,m=n}),b=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&u?.()},{type:"mqtt/subscribe",topic:`${l}/bridge/info`});"function"==typeof r&&b.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);null!==n?n&&function(t){const n=d(t),e=n?.transaction??d(n?.data)?.transaction;return"string"==typeof e||"number"==typeof e?String(e):null}(n)===i&&f?.(n):c&&m?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await g(y,Math.min(4e3,Math.max(1,s-Date.now()))),c=!0,await t.callService("mqtt","publish",{topic:`${l}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const v=await g(w,s-Date.now()),_=d(v)?.status;if(_&&"ok"!==_)throw a("provider");return n(v,l)}finally{for(const t of b)try{t()}catch{}}})}export{f as readZhaTopology,m as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="b9fc9e3485e171367db91b8386e18e79c8d2f4b0e379d14db6dfc2dd93e8423d";try{await import("./houseplan-assets/houseplan-card-sltdEKFS.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="668466a273bc161fb0232215df40fd095aa162047e9b574518867c8554a75991";try{await import("./houseplan-assets/houseplan-card-chP6NBhK.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale houseplan-card.js: the implementation chunk is unavailable",e)}
+1 -1
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -2,6 +2,12 @@
## Unreleased
- Space navigation now exposes the current space to assistive technology, an
alarm is no longer repeated in a device's accessible name, and keyboard
focus opens the same device tooltip as mouse hover. Room-label colours and
the touch/pen behaviour are unchanged
([#565](https://github.com/Matysh/houseplan-card/issues/565)).
- Closely placed device markers no longer steal one another's hover, click,
long-press or drag target. The painted marker under the pointer now wins;
overlapping invisible touch areas fall back to the nearest marker while the
+6
View File
@@ -8,6 +8,12 @@
## Не выпущено
- Навигация по пространствам теперь сообщает вспомогательным технологиям
текущее пространство, тревога больше не повторяется в доступном имени
устройства, а клавиатурный фокус открывает ту же подсказку устройства, что и
наведение мыши. Цвета подписей комнат и поведение touch/pen не изменились
([#565](https://github.com/Matysh/houseplan-card/issues/565)).
- Близко размещённые значки устройств больше не перехватывают друг у друга
наведение, нажатие, долгое нажатие или перетаскивание. В нарисованной области
выбирается видимый значок; пересечение невидимых зон нажатия разрешается в
+16
View File
@@ -4171,3 +4171,19 @@ require hands on real hardware — they remain for the human pass.
`range_v1`/`zones_v1`.
- [ ] Мутант `radar-profile-source-entity-id-omitted` удаляет обе роли
`entity_id`; точный backend guard обязан покраснеть.
## Доступность основного View (#565)
- [ ] `test/device-presentation.test.mjs` доказывает whole-segment
дедупликацию доступного имени во всех четырёх локалях, сохранение порядка
и отсутствие ложного удаления при частичном совпадении.
- [ ] `demo/smoke_household_journeys.mjs` проверяет именованную навигацию и
единственный `aria-current="page"`, реальный Tab-focus tooltip, переход к
следующему устройству, blur-cleanup и попадание подсказки в viewport.
- [ ] `demo/smoke_space_card.mjs` считает сегменты тревоги в доступном имени
статической карточки и не добавляет ей интерактивность.
- [ ] Мутанты `view-current-space-aria-removed`,
`device-accessible-label-dedup-removed`,
`device-focus-tooltip-handler-removed` и
`device-focus-tooltip-blur-cleanup-removed` обязаны покраснеть на своих
заявленных гардах.
+5
View File
@@ -73,6 +73,11 @@ desktop hover without a reload. Space/mode changes, page hiding and remounts
also discard transient hover. Keyboard focus and explicit click/tap surfaces
remain independent of this visual hover gate.
In View, a device reached with visible keyboard focus opens the same anchored
device tooltip as mouse hover. It follows focus from marker to marker and closes
on blur, space/mode changes, page hiding or remount. Touch and pen still never
open it and immediately clear it on hybrid hardware.
When device markers are packed more tightly than their 44 × 44 px minimum
targets, the marker visibly painted under the pointer owns that point. Only an
otherwise empty overlap of invisible target floors is resolved by the nearest
+4
View File
@@ -886,6 +886,10 @@ room fill gradient and the displayed number are unchanged.
Interactive View/kiosk and Device-editor markers have at least a 44×44 CSS px
target. Enter and Space reuse the exact current click and confirmation path;
Plan, Background, preview and the read-only static card add no tab stop.
In the full View, Tab focus also opens the same device tooltip as mouse hover;
moving to the next control closes or moves it. The active space is exposed as
the current item of the named space navigation. The static card remains
non-interactive.
## 13. Room fills and light
+6
View File
@@ -1237,6 +1237,12 @@ Hover включается только после события от наст
подсветку и не оставляет их «прилипшими»; последующее движение реальной мыши
возвращает desktop-hover без перезагрузки страницы.
В полном режиме просмотра клавиатурный Tab также открывает у сфокусированного
устройства ту же подсказку, что наведение мыши; переход к следующему элементу
переносит или закрывает её. Текущее пространство программно отмечено в
именованной навигации по пространствам. Статическая карточка остаётся
неинтерактивной.
### Подложка и постоянный статус
| Вид | Что означает | Примеры |
+4 -1
View File
@@ -83,7 +83,10 @@ Room, device, opening and shared-control hover is a mouse-only transient layer:
the card enables it per instance after real mouse input on fine/hover hardware,
clears it on touch/pen and on mode, space or lifecycle boundaries, and restores
it when a real mouse is used again. Keyboard focus, selection and semantic
device state do not depend on that gate. A device action that actually dispatches
device state do not depend on that gate. Visible keyboard focus on a View device
opens the same marker-anchored tooltip as mouse hover; it moves with Tab focus
and closes at the usual focus, mode, space and lifecycle boundaries. Touch and
pen do not open it. A device action that actually dispatches
briefly scales its painted shell to 95% and back over 200 ms; informational,
editor and no-op paths do not imitate a successful action.
+44
View File
@@ -106,6 +106,50 @@ const MUTANT_DEFINITIONS = [
replace: ' return caught === toRun.length ? 0 : 1;',
}],
},
{
id: 'view-current-space-aria-removed',
guard: 'node demo/smoke_household_journeys.mjs',
because: '#565 AC1: the active class is visual only; removing aria-current makes the '
+ 'open space unknowable to assistive technology even though the tab still looks selected',
patches: [{
file: 'src/houseplan-card.ts',
find: " aria-current=${this._space === s.id ? 'page' : nothing}",
replace: ' aria-current=${nothing}',
}],
},
{
id: 'device-accessible-label-dedup-removed',
guard: 'node --test --test-name-pattern="#565 accessible" test/device-presentation.test.mjs',
because: '#565 AC3/AC4: state and pulse may localise to the same word; without whole-segment '
+ 'deduplication a screen reader announces Alarm twice in both plan cards',
patches: [{
file: 'src/device-presentation.ts',
find: ' if (seen.has(key)) continue;',
replace: ' void seen; // mutant: repeated accessible facts are retained',
}],
},
{
id: 'device-focus-tooltip-handler-removed',
guard: 'node demo/smoke_household_journeys.mjs',
because: '#565 AC5: the visible focus ring alone does not give a sighted keyboard user the '
+ 'device name and metrics shown to a mouse user',
patches: [{
file: 'src/houseplan-card.ts',
find: ' @focus=${(e: FocusEvent) => this._showDeviceFocusTip(e, d)}',
replace: ' @focus=${nothing}',
}],
},
{
id: 'device-focus-tooltip-blur-cleanup-removed',
guard: 'node demo/smoke_household_journeys.mjs',
because: '#565 AC6: a focus-owned tooltip must not remain after keyboard focus leaves the '
+ 'marker; stale content can otherwise describe the wrong device',
patches: [{
file: 'src/houseplan-card.ts',
find: ' private _hideDeviceFocusTip(deviceId: string): void { this._liveRt?.deviceBlur(deviceId); }',
replace: ' private _hideDeviceFocusTip(deviceId: string): void { void deviceId; }',
}],
},
{
id: 'invariants-blame-every-stale-position',
guard: 'node --test --test-name-pattern="#566" test/model-invariants.test.mjs',
+23
View File
@@ -60,6 +60,29 @@ export function markerLqiColor(lqi: number): string {
return lqiColor(lqi);
}
/**
* Join short, already-localised accessible-name segments without making a
* screen reader repeat the same fact. Only whole segments are deduplicated:
* state, value and signal details remain independent even when they share a
* word.
*/
export function deviceAccessibleLabel(
segments: readonly (string | null | undefined | false)[],
): string {
const seen = new Set<string>();
const unique: string[] = [];
for (const segment of segments) {
if (typeof segment !== 'string') continue;
const value = segment.trim().replace(/\s+/g, ' ');
if (!value) continue;
const key = value.toLowerCase();
if (seen.has(key)) continue;
seen.add(key);
unique.push(value);
}
return unique.join(', ');
}
export function deviceA11yState(presentation: Pick<
ResolvedDevicePresentation, 'visual' | 'lockState' | 'display'
>): DeviceA11yState {
+30 -30
View File
@@ -310,7 +310,8 @@ import {
type DeviceActivity, type DeviceVisualState, type EntityVisualSample,
} from './device-visual';
import {
activitySourceSignature, deviceA11yState, presentationClasses, resolveDevicePresentation,
activitySourceSignature, deviceAccessibleLabel, deviceA11yState, presentationClasses,
resolveDevicePresentation,
resolvePresentationSources, type ResolvedDevicePresentation,
} from './device-presentation';
import {
@@ -980,6 +981,7 @@ export class HouseplanCard extends LitElement {
temp?: number | null;
hum?: number | null;
room?: boolean;
source?: 'pointer' | 'focus'; deviceId?: string;
} | null = null;
/** Room whose physical perimeter is highlighted in View. The explicit
* overlay is needed because thick wall bodies paint above room shapes. */
@@ -5733,26 +5735,20 @@ export class HouseplanCard extends LitElement {
}
private _showDeviceTip(ev: PointerEvent, d: DevItem): void {
this._notePointer(ev);
if (!this._pointerModality.hoverEnabled || this._drag || this._deviceDrag) {
this._deviceHits.hover(this.renderRoot, null);
return;
}
const showLqi = this._spaceDisplayForRender().showLqi ?? this._config?.show_signal ?? true;
const presentation = this._devicePresentation(d, showLqi);
const disabledReason = presentation.disabledReason;
const ghostLabel = presentation.haDisabled
? this._t((`marker.ha_disabled_${disabledReason}`) as I18nKey)
: d.userHidden ? this._t('marker.hidden_ghost') : d.name;
const metrics = [
d.model,
presentation.valueBadge?.fullText || '',
presentation.lqiText != null ? 'LQI ' + presentation.lqiText : '',
].filter(Boolean).join(' · ');
this._deviceHits.hover(this.renderRoot, d.id);
this._showTip(ev, d.name, presentation.haDisabled ? ghostLabel : metrics);
if (this._liveRt) this._liveRt.devicePointerTip(ev, d);
else void this._ensureLiveRuntime().then(() => this._liveRt?.devicePointerTip(ev, d));
}
private _showDeviceFocusTip(ev: FocusEvent, d: DevItem): void {
const target = ev.currentTarget as HTMLElement | null;
if (this._liveRt) this._liveRt.deviceFocusTip(target, d);
else void this._ensureLiveRuntime().then(() => this._liveRt?.deviceFocusTip(target, d));
}
private _hideDeviceFocusTip(deviceId: string): void { this._liveRt?.deviceBlur(deviceId); }
private _clearPointerHover(): void { this._liveRt?.pointerLeave() ?? this._clearTransientHover(); }
/** Right click in VIEW mode always opens HA's more-info (owner's decision). */
private _ctxDevice(ev: MouseEvent, d: DevItem): void {
if (this._mode !== 'view') return; // editors keep the native context menu
@@ -7436,7 +7432,7 @@ export class HouseplanCard extends LitElement {
this._notePointer(ev);
if (!this._pointerModality.hoverEnabled) return;
if (this._drag || this._deviceDrag) return;
this._tip = { x: ev.clientX, y: ev.clientY, title, meta, lqi, temp, hum, room };
this._tip = { x: ev.clientX, y: ev.clientY, title, meta, lqi, temp, hum, room, source: 'pointer' };
this._syncLiveHover();
}
@@ -7602,6 +7598,7 @@ export class HouseplanCard extends LitElement {
this._clearRoomFocus(true);
this._cancelDangerConfirm();
this._resetDeviceHitState();
this._clearTransientHover(true);
}
this._warmModeRequest = 0;
if (!this._editorRuntime) {
@@ -11466,7 +11463,7 @@ export class HouseplanCard extends LitElement {
<ha-icon icon="mdi:home-city"></ha-icon>
${this._config.title || this._t('card.title')}
</div>`}
<div class="tabs" @pointermove=${(e: PointerEvent) => this._tabPointerMove(e)}>
<nav class="tabs" aria-label=${this._t('nav.spaces')} @pointermove=${(e: PointerEvent) => this._tabPointerMove(e)}>
${navigationSpaces.map(
(s) => html`<button
data-hp="space-tab" data-id="${s.id}"
@@ -11475,6 +11472,7 @@ export class HouseplanCard extends LitElement {
this._tabDrag?.moved && this._tabDrag.targetId === s.id
? ` drop-${this._tabDrag.placement}` : ''}"
?data-reorderable=${this._canReorderTabs}
aria-current=${this._space === s.id ? 'page' : nothing}
@pointerdown=${(e: PointerEvent) => this._tabPointerDown(e, s.id)}
@pointerup=${(e: PointerEvent) => this._tabPointerUp(e)}
@pointercancel=${() => this._endTabDrag()}
@@ -11503,7 +11501,7 @@ export class HouseplanCard extends LitElement {
<ha-icon icon="mdi:plus"></ha-icon>
</button>`
: nothing}
</div>
</nav>
${this._canEdit
? html`<div class="modes">
${([['plan', 'mdi:floor-plan'], ['devices', 'mdi:tune-variant'], ['decor', 'mdi:draw']] as const).map(
@@ -11734,32 +11732,32 @@ export class HouseplanCard extends LitElement {
d="${[obstaclePath, ...holes.map(pathD)].join(' ')}"
@pointerenter=${enterRoom}
@pointermove=${tip}
@pointerleave=${() => this._clearTransientHover()}></path>`
@pointerleave=${() => this._clearPointerHover()}></path>`
: holes.length && fillPoly
? svg`<path class="${cls}" style="${style}" fill-rule="evenodd"
data-hp="room" data-id=${hpId} data-area=${hpArea}
d="${[fillPoly, ...holes].map(pathD).join(' ')}"
@pointerenter=${enterRoom}
@pointermove=${tip}
@pointerleave=${() => this._clearTransientHover()}></path>`
@pointerleave=${() => this._clearPointerHover()}></path>`
: fillPoly && fillPoly !== myPoly
? svg`<polygon class="${cls}" style="${style}" points="${fillPoly.map((p) => p.join(',')).join(' ')}"
data-hp="room" data-id=${hpId} data-area=${hpArea}
@pointerenter=${enterRoom}
@pointermove=${tip}
@pointerleave=${() => this._clearTransientHover()}></polygon>`
@pointerleave=${() => this._clearPointerHover()}></polygon>`
: r.poly
? svg`<polygon class="${cls}" style="${style}" points="${r.poly.map((p) => p.join(',')).join(' ')}"
data-hp="room" data-id=${hpId} data-area=${hpArea}
@pointerenter=${enterRoom}
@pointermove=${tip}
@pointerleave=${() => this._clearTransientHover()}></polygon>`
@pointerleave=${() => this._clearPointerHover()}></polygon>`
: svg`<rect class="${cls}" style="${style}"
data-hp="room" data-id=${hpId} data-area=${hpArea}
x="${r.x}" y="${r.y}" width="${r.w}" height="${r.h}" rx="${Math.min(r.w!, r.h!) * 0.03}"
@pointerenter=${enterRoom}
@pointermove=${tip}
@pointerleave=${() => this._clearTransientHover()}></rect>`;
@pointerleave=${() => this._clearPointerHover()}></rect>`;
const trimmed = edgeCuts.length && myPoly
? outlineWithout(myPoly, edgeCuts, this._gridPitch * 0.02)
: null;
@@ -12518,7 +12516,7 @@ export class HouseplanCard extends LitElement {
: d.userHidden ? this._t('marker.hidden_ghost') : d.name;
const a11yState = deviceA11yState(presentation);
const interactive = this._mode === 'view' || this._mode === 'devices';
const deviceAriaLabel = [
const deviceAriaLabel = deviceAccessibleLabel([
ghostLabel,
!presentation.haDisabled
? this._t((`marker.state_a11y_${a11yState}`) as I18nKey) : '',
@@ -12530,7 +12528,7 @@ export class HouseplanCard extends LitElement {
? this._t((`marker.lqi_a11y_${presentation.lqiBand}`) as I18nKey, {
value: presentation.lqiText,
}) : '',
].filter(Boolean).join(', ');
]);
return html`<div
${''/* docs/STYLING-HOOKS.md §3: the styling contract. `nothing` on an
attribute binding REMOVES the attribute, so a virtual marker has
@@ -12555,12 +12553,14 @@ export class HouseplanCard extends LitElement {
style="${st.join(';')}"
@click=${(e: MouseEvent) => this._clickDevice(e, d)}
@keydown=${(e: KeyboardEvent) => this._keyDevice(e, d)}
@focus=${(e: FocusEvent) => this._showDeviceFocusTip(e, d)}
@focusout=${() => this._hideDeviceFocusTip(d.id)}
@contextmenu=${(e: MouseEvent) => this._ctxDevice(e, d)}
@pointerover=${(e: PointerEvent) => {
if (this._mode !== 'view' && this._mode !== 'devices') return;
this._showDeviceTip(e, this._deviceForPointerEvent(e, d));
}}
@pointerleave=${() => this._clearTransientHover()}
@pointerleave=${() => this._clearPointerHover()}
@pointerdown=${(e: PointerEvent) => this._pointerDown(e, d)}
@pointermove=${(e: PointerEvent) => {
if (this._mode !== 'view' && this._mode !== 'devices') return;
+1
View File
@@ -52,6 +52,7 @@
"card.title": "Hausplan",
"count.devices": "{n} Entwicklung",
"empty.no_spaces": "Noch keine Bereiche.",
"nav.spaces": "House-Plan-Bereiche",
"empty.add_first": "Ersten Bereich hinzufügen und einen Grundriss hochladen.",
"empty.read_only": "Es sind keine Bereiche eingerichtet. Bitten Sie einen Administrator, einen hinzuzufügen.",
"empty.install": "Installieren Sie die House Plan-Integration und fügen Sie sie unter „Geräte & Dienste“ hinzu.",
+1
View File
@@ -52,6 +52,7 @@
"card.title": "House plan",
"count.devices": "{n} dev.",
"empty.no_spaces": "No spaces yet.",
"nav.spaces": "House Plan spaces",
"empty.add_first": "Add the first space and upload a floor plan.",
"empty.read_only": "No spaces are configured. Ask an administrator to add one.",
"empty.install": "Install the House Plan integration and add it in \"Devices & services\".",
+1
View File
@@ -52,6 +52,7 @@
"card.title": "Plan de la maison",
"count.devices": "{n} app.",
"empty.no_spaces": "Aucun espace pour le moment.",
"nav.spaces": "Espaces House Plan",
"empty.add_first": "Ajoutez le premier espace et importez un plan.",
"empty.read_only": "Aucun espace n’est configuré. Demandez à un administrateur d’en ajouter un.",
"empty.install": "Installez l’intégration House Plan et ajoutez-la dans « Appareils et services ».",
+1
View File
@@ -52,6 +52,7 @@
"card.title": "План дома",
"count.devices": "{n} устр.",
"empty.no_spaces": "Пространств пока нет.",
"nav.spaces": "Пространства House Plan",
"empty.add_first": "Добавьте первое пространство и загрузите план этажа.",
"empty.read_only": "Пространства не настроены. Попросите администратора добавить первое.",
"empty.install": "Установите интеграцию House Plan и добавьте запись в «Устройства и службы».",
+91 -2
View File
@@ -1,4 +1,7 @@
import { lqiColor } from './logic';
import type { I18nKey } from './i18n';
import type { ResolvedDevicePresentation } from './device-presentation';
import type { DevItem } from './types';
export interface LiveTip {
x: number;
@@ -9,6 +12,8 @@ export interface LiveTip {
temp?: number | null;
hum?: number | null;
room?: boolean;
source?: 'pointer' | 'focus';
deviceId?: string;
}
interface HoverState {
@@ -25,8 +30,85 @@ interface LiveHoverHost {
_roomHoverPaths: (model: unknown) => { fillD: string; outlineD: string } | null;
}
interface DeviceTipHost extends LiveHoverHost {
_mode: string;
_drag: unknown;
_deviceDrag: unknown;
_config: { show_signal?: boolean } | null;
_pointerModality: { hoverEnabled: boolean };
_deviceHits: { hover: (root: ParentNode, id: string | null) => void };
_spaceDisplayForRender: () => { showLqi?: boolean };
_devicePresentation: (device: DevItem, showLqi: boolean) => ResolvedDevicePresentation;
_notePointer: (event: PointerEvent) => void;
_showTip: (event: PointerEvent, title: string, meta: string) => void;
_t: LiveHoverHost['_t'] & ((key: I18nKey) => string);
}
const states = new WeakMap<object, HoverState>();
const deviceTipContent = (host: DeviceTipHost, device: DevItem): { title: string; meta: string } => {
const showLqi = host._spaceDisplayForRender().showLqi ?? host._config?.show_signal ?? true;
const presentation = host._devicePresentation(device, showLqi);
const ghostLabel = presentation.haDisabled
? host._t((`marker.ha_disabled_${presentation.disabledReason}`) as I18nKey)
: device.userHidden ? host._t('marker.hidden_ghost') : device.name;
const metrics = [
device.model,
presentation.valueBadge?.fullText || '',
presentation.lqiText != null ? `LQI ${presentation.lqiText}` : '',
].filter(Boolean).join(' · ');
return { title: device.name, meta: presentation.haDisabled ? ghostLabel : metrics };
};
export function showDevicePointerTip(value: object, event: PointerEvent, device: DevItem): void {
const host = value as DeviceTipHost;
host._notePointer(event);
if (!host._pointerModality.hoverEnabled || host._drag || host._deviceDrag) {
host._deviceHits.hover(host.renderRoot, null);
return;
}
if (host._tip?.source === 'focus') return;
const tip = deviceTipContent(host, device);
host._deviceHits.hover(host.renderRoot, device.id);
host._showTip(event, tip.title, tip.meta);
}
export function showDeviceFocusTip(value: object, target: HTMLElement | null, device: DevItem): void {
const host = value as DeviceTipHost;
if (host._mode !== 'view' || host._drag || host._deviceDrag
|| !target?.matches(':focus-visible')) return;
const rect = target.getBoundingClientRect();
host._tip = {
x: rect.right, y: rect.top, ...deviceTipContent(host, device),
source: 'focus', deviceId: device.id,
};
syncHouseplanHover(host);
}
export function hideDeviceFocusTip(value: object, deviceId: string): void {
const host = value as DeviceTipHost;
if (host._tip?.source !== 'focus' || host._tip.deviceId !== deviceId) return;
host._tip = null;
syncHouseplanHover(host);
}
export function clearPointerHover(value: object): void {
const host = value as DeviceTipHost;
host._deviceHits.hover(host.renderRoot, null);
if (host._tip?.source !== 'focus') host._tip = null;
host._hoverRoom = null;
syncHouseplanHover(host);
}
export function reconcileDeviceFocusTip(value: object): void {
const host = value as DeviceTipHost;
if (host._tip?.source !== 'focus') return;
const marker = [...host.renderRoot.querySelectorAll<HTMLElement>('[data-hp="device"]')]
.find((node) => node.dataset.id === host._tip?.deviceId);
if (marker?.matches(':focus-visible')) return;
host._tip = null;
}
const appendMeta = (tip: HTMLElement, label: string, value?: string, color?: string): void => {
if (!value) return;
const row = document.createElement('span');
@@ -51,8 +133,10 @@ const syncTip = (host: LiveHoverHost, root: ParentNode): void => {
element.replaceChildren();
return;
}
element.style.left = `${tip.x + 12}px`;
element.style.top = `${tip.y + 12}px`;
const gap = 12;
const margin = 8;
element.style.left = `${tip.x + gap}px`;
element.style.top = `${tip.y + gap}px`;
element.replaceChildren();
const title = document.createElement('b');
title.textContent = tip.title;
@@ -63,6 +147,11 @@ const syncTip = (host: LiveHoverHost, root: ParentNode): void => {
appendMeta(element, host._t('tip.lqi'), tip.lqi == null ? '' : String(tip.lqi),
tip.lqi == null ? undefined : lqiColor(tip.lqi));
element.hidden = false;
const box = element.getBoundingClientRect();
const maxLeft = Math.max(margin, window.innerWidth - box.width - margin);
const maxTop = Math.max(margin, window.innerHeight - box.height - margin);
element.style.left = `${Math.min(maxLeft, Math.max(margin, tip.x + gap))}px`;
element.style.top = `${Math.min(maxTop, Math.max(margin, tip.y + gap))}px`;
};
const setRoomPath = (root: ParentNode, selector: string, d: string): void => {
+14 -1
View File
@@ -1,11 +1,15 @@
import {
commitHouseplanViewport, disposeHouseplanViewport, scheduleHouseplanViewport,
} from './live-viewport';
import { resetHouseplanHover, syncHouseplanHover } from './live-hover';
import {
clearPointerHover, hideDeviceFocusTip, reconcileDeviceFocusTip, resetHouseplanHover,
showDeviceFocusTip, showDevicePointerTip, syncHouseplanHover,
} from './live-hover';
import {
classifyHassRenderChange, type HassRenderDependencies, type HassRenderSnapshot,
} from './render-invalidation';
import type { RenderLifecycle } from './houseplan-render-lifecycle';
import type { DevItem } from './types';
interface LiveRuntimeHost {
_renderLife: RenderLifecycle;
@@ -53,6 +57,14 @@ export class LiveRuntime {
}
public viewport(now = false): void { scheduleHouseplanViewport(this.host, now); }
public hover(): void { syncHouseplanHover(this.host); }
public devicePointerTip(event: PointerEvent, device: DevItem): void {
showDevicePointerTip(this.host, event, device);
}
public deviceFocusTip(target: HTMLElement | null, device: DevItem): void {
showDeviceFocusTip(this.host, target, device);
}
public deviceBlur(deviceId: string): void { hideDeviceFocusTip(this.host, deviceId); }
public pointerLeave(): void { clearPointerHover(this.host); }
public active(): boolean {
const host = this.host as LiveRuntimeHost;
return host._pointers.size > 0 || host._cameraTransition.active || !!host._deviceDrag
@@ -62,6 +74,7 @@ export class LiveRuntime {
}
public commit(): void {
commitHouseplanViewport(this.host);
reconcileDeviceFocusTip(this.host);
resetHouseplanHover(this.host);
syncHouseplanHover(this.host);
}
+3 -3
View File
@@ -37,7 +37,7 @@ import {
} from './render/opening-symbol';
import { activeRegistryHass, fullRegistryHass, type HaRegistrySnapshot } from './ha-binding-status';
import {
deviceA11yState, resolveDevicePresentation,
deviceAccessibleLabel, deviceA11yState, resolveDevicePresentation,
type PresentationActivityRuntime, type ResolvedDevicePresentation,
} from './device-presentation';
import { presentationSnapshotKey } from './render-device-snapshot';
@@ -591,7 +591,7 @@ export function renderSpaceStatic(o: StaticRenderOpts): TemplateResult | null {
});
const st = [`left:${left}%`, `top:${top}%`, ...deviceFaceStyle(presentation)];
const a11yState = deviceA11yState(presentation);
const deviceAriaLabel = [
const deviceAriaLabel = deviceAccessibleLabel([
d.name,
t(o.lang, (`marker.state_a11y_${a11yState}`) as any),
presentation.pulse.kind !== 'none'
@@ -602,7 +602,7 @@ export function renderSpaceStatic(o: StaticRenderOpts): TemplateResult | null {
? t(o.lang, (`marker.lqi_a11y_${presentation.lqiBand}`) as any, {
value: presentation.lqiText,
}) : '',
].filter(Boolean).join(', ');
]);
return html`<div class="dev ${deviceThemeClass(planHass)} ${presentation.classes.join(' ')} ${d.virtual ? 'virtual' : ''} ${presentation.valueText != null ? 'valonly' : ''}"
data-hp="device" data-id="${d.id}" data-entity=${d.primary || nothing} data-area=${d.area || nothing}
role="img" aria-label=${deviceAriaLabel}
+24
View File
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import {
activitySourceSignature,
deviceAccessibleLabel,
deviceA11yState,
markerLqiBand,
markerLqiColor,
@@ -55,6 +56,29 @@ const options = {
showSignal: true,
};
test('#565 accessible device label removes only whole repeated segments', () => {
assert.equal(deviceAccessibleLabel([
'Sink leak sensor', ' Alarm ', 'alarm', '', null, false,
'LQI 117', 'medium signal',
]), 'Sink leak sensor, Alarm, LQI 117, medium signal');
assert.equal(deviceAccessibleLabel([
'Alarm sensor', 'Alarm', 'Alarm active', 'LQI 117', 'LQI 117, medium signal',
]), 'Alarm sensor, Alarm, Alarm active, LQI 117, LQI 117, medium signal');
assert.equal(deviceAccessibleLabel([
' Door sensor ', 'Open', 'Recent opening', 'OPEN',
]), 'Door sensor, Open, Recent opening');
for (const language of ['en', 'ru', 'de', 'fr']) {
const dictionary = JSON.parse(readFileSync(
new URL(`../src/i18n/${language}.json`, import.meta.url), 'utf8',
));
const alarm = dictionary['marker.state_a11y_alarm'];
assert.equal(alarm, dictionary['marker.pulse_a11y_alarm']);
assert.equal(deviceAccessibleLabel(['Leak sensor', alarm, alarm]).split(', ').length, 2,
`${language}: alarm is spoken once`);
}
});
test('marker LQI keeps semantic bands while its colour uses the continuous gradient', () => {
assert.deepEqual([0, 40, 41, 179, 180].map(markerLqiBand), [
'low', 'low', 'mid', 'mid', 'high',
+25
View File
@@ -0,0 +1,25 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
const source = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8');
test('#565 View keeps native space buttons and exposes one current page', () => {
const card = source('src/houseplan-card.ts');
assert.match(card, /<nav class="tabs" aria-label=\$\{this\._t\('nav\.spaces'\)\}/);
assert.match(card, /aria-current=\$\{this\._space === s\.id \? 'page' : nothing\}/);
assert.doesNotMatch(card, /role="tablist"|role="tab"/);
});
test('#565 keyboard tooltip has explicit focus ownership and room labels stay untouched', () => {
const card = source('src/houseplan-card.ts');
const hover = source('src/live-hover.ts');
const staticCard = source('src/space-render.ts');
assert.match(card, /@focus=\$\{\(e: FocusEvent\) => this\._showDeviceFocusTip\(e, d\)\}/);
assert.match(card, /@focusout=\$\{\(\) => this\._hideDeviceFocusTip\(d\.id\)\}/);
assert.match(hover, /!target\?\.matches\(':focus-visible'\)/);
assert.match(hover, /marker\?\.matches\(':focus-visible'\)/);
assert.match(card, /style="left:\$\{left\}%;top:\$\{top\}%;color:\$\{disp\.color\};opacity:\$\{op\};--rl-scale:/);
assert.match(staticCard, /style="left:\$\{left\}%;top:\$\{top\}%;color:\$\{disp\.color\};opacity:\$\{op\}"/);
assert.doesNotMatch(staticCard, /_showDeviceFocusTip|@focus=/);
});