fix: «Как у пространства» забывает свой цвет комнаты (#581)

Режим заливки комнаты и её цвет — два поля конфига под одним переключателем.
«Как у пространства» снимало только режим; цвет оставался и продолжал
применяться, потому что `roomCustomFillOf` отдавал цвет комнаты независимо от
того, чей режим `custom` действует. Так возникало безымянное состояние «режим
наследую, цвет свой» — Cabinet на даче.

Теперь цвет комнаты участвует в раскраске только вместе с её собственным
`fill_mode: 'custom'` (одна функция — все поверхности: карточка, space-card,
PDF, черновик диалога). Диалог загружает цвет в черновик только при своём
режиме, обнуляет его при уходе с «Свой цвет» и показывает строку цвета только
под этим радио; сохранение пишет `custom_fill` только с `fill_mode: 'custom'`,
иначе удаляет — включая сироту от прежнего редактора. Чтение конфиг не
переписывает: застрявшие комнаты выздоравливают обновлением.

- `test/logic.test.mjs`: AC1 — сирота и любой чужой режим → цвет пространства
- `demo/smoke_room_settings.mjs` шаг 7: свой цвет → «Как у пространства» →
  ни режима, ни цвета, во View цвет пространства; сирота открывается как
  наследование, сохранение её удаляет (проверено красным на базе: 9 фактов)
- `demo/smoke_space_settings.mjs`: override с собственным режимом + сирота
- `demo/golden/harness.mjs`: `roomCustomFill` ставит комнате её режим —
  кадры `lighting-custom-glow-*` не меняются
- мутант `room-orphan-colour-wins-again`
- docs: ARCHITECTURE (#56), CONFIG-COMPATIBILITY, USER-GUIDE ru/en, TESTING;
  отпечаток скриншотов принят попиксельно (11 кадров)

Issue: #581
User-Visible: yes
This commit is contained in:
Claude
2026-09-15 09:48:37 +03:00
parent bae6afaae7
commit 2bec03e6fa
58 changed files with 616 additions and 447 deletions
@@ -1,295 +1,295 @@
{
"schema": 1,
"fingerprint": "ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621",
"fingerprint": "241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef",
"entry": "houseplan-card.js",
"panelEntry": "houseplan-panel.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 290789,
"initialViewGzipBytes": 290805,
"initialPanelFiles": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-panel.js"
],
"initialPanelGzipBytes": 292313,
"initialPanelGzipBytes": 292327,
"initialPanelOnlyFiles": [
"houseplan-panel.js"
],
"initialPanelOnlyGzipBytes": 2328,
"initialPanelOnlyGzipBytes": 2326,
"lazyFiles": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/de-CVHXDkji.js",
"houseplan-assets/editor-wZz2IlGD.js",
"houseplan-assets/fr-DR1YSIn4.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js",
"houseplan-assets/hp-zigbee-topology-overlay-B2VZUom6.js",
"houseplan-assets/iso-scene-render-BgPGm0jS.js",
"houseplan-assets/live-interaction-runtime-B3A1vG3V.js",
"houseplan-assets/pdf-export-BjqKp8FJ.js",
"houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js",
"houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/de-BrzcYO-1.js",
"houseplan-assets/editor-Cfrg3iMq.js",
"houseplan-assets/fr-CLHfUBDP.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js",
"houseplan-assets/hp-zigbee-topology-overlay-jvjkBbI9.js",
"houseplan-assets/iso-scene-render-D2f0EUgN.js",
"houseplan-assets/live-interaction-runtime-BA69oN1Z.js",
"houseplan-assets/pdf-export-DeQrvN1z.js",
"houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js",
"houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js"
],
"lazyGzipBytes": 418794,
"lazyGzipBytes": 418772,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/editor-wZz2IlGD.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/editor-Cfrg3iMq.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js"
],
"lazyEditorGzipBytes": 204946,
"lazyEditorGzipBytes": 204963,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js"
],
"lazyOnboardingGzipBytes": 13922,
"lazyOnboardingGzipBytes": 13916,
"lazyLocaleFiles": [
"houseplan-assets/de-CVHXDkji.js",
"houseplan-assets/fr-DR1YSIn4.js"
"houseplan-assets/de-BrzcYO-1.js",
"houseplan-assets/fr-CLHfUBDP.js"
],
"lazyLocaleGzipBytes": 54936,
"lazyLocaleGzipBytes": 54921,
"lazyIsometricFiles": [
"houseplan-assets/iso-scene-render-BgPGm0jS.js"
"houseplan-assets/iso-scene-render-D2f0EUgN.js"
],
"lazyIsometricGzipBytes": 13237,
"lazyIsometricGzipBytes": 13234,
"lazyFurnitureArtFiles": [
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js"
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js"
],
"lazyFurnitureArtGzipBytes": 10249,
"lazyFurnitureArtGzipBytes": 10255,
"lazyPdfFiles": [
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/pdf-export-BjqKp8FJ.js"
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/pdf-export-DeQrvN1z.js"
],
"lazyPdfGzipBytes": 120122,
"lazyPdfGzipBytes": 120128,
"files": [
{
"path": "houseplan-assets/backdrop-pick-BS6cZmWE.js",
"sha256": "66693f70e3a9779dc690c07b21c915800b675918c891c20d52538e5ee82e6eac",
"path": "houseplan-assets/backdrop-pick-ClLjW_UW.js",
"sha256": "5c8e9728df6ebdaad6346a26686f058ba4cce8bebd68c01d6cf18423ac67b792",
"rawBytes": 19956,
"gzipBytes": 6882,
"gzipBytes": 6880,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-CVHXDkji.js",
"sha256": "07785e7cc056eff8b274b48b692d8874d20f958c5c82afc31321df4f82c60121",
"path": "houseplan-assets/de-BrzcYO-1.js",
"sha256": "b7488c7f186f740f02cdfde3c093aed8754e91157c0903d196a36fee4fbfef60",
"rawBytes": 91590,
"gzipBytes": 27750,
"gzipBytes": 27743,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-wZz2IlGD.js",
"sha256": "09464bc1bc28ae0eefccff27e63d05093a1b7960e4961ff3202050c335af19ff",
"path": "houseplan-assets/editor-Cfrg3iMq.js",
"sha256": "83f9d762289e0f83a6e78730d3eb54862dbfa78408c93d410126f15988ca758c",
"rawBytes": 3831,
"gzipBytes": 1586,
"gzipBytes": 1584,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-DR1YSIn4.js",
"sha256": "2ec37225ffc437fd742ff9f525ebf086f1acb916bddc214058a40a2544a8f645",
"path": "houseplan-assets/fr-CLHfUBDP.js",
"sha256": "3524222b350ecbe2187351bf1730cc1d6393497104b10478cc3b7b0e5f14a9ec",
"rawBytes": 93884,
"gzipBytes": 27186,
"gzipBytes": 27178,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"sha256": "f0610686040a739a88f1b74dbcebbd0b2197bc4af40c0eae6876411d118d7140",
"path": "houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"sha256": "8303521f70c98617ece5cea54517760fdb808bec3fc7b68add0434ea56980dd3",
"rawBytes": 33655,
"gzipBytes": 10249,
"gzipBytes": 10255,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-DFzGk9yU.js",
"sha256": "768748c29e9e6fa154dc69cd8e81d6cd1ff54b8eca2874a2a1f0bf05404cbb55",
"rawBytes": 1032664,
"gzipBytes": 289985,
"path": "houseplan-assets/houseplan-card-BvxqYw8A.js",
"sha256": "da77e0c3d6445d114ff496613fc8aebfb7e7ed132bdfbc5c2bc8c5a79fd8afc2",
"rawBytes": 1032746,
"gzipBytes": 290001,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-CVHXDkji.js",
"houseplan-assets/editor-wZz2IlGD.js",
"houseplan-assets/fr-DR1YSIn4.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js",
"houseplan-assets/hp-zigbee-topology-overlay-B2VZUom6.js",
"houseplan-assets/iso-scene-render-BgPGm0jS.js",
"houseplan-assets/live-interaction-runtime-B3A1vG3V.js",
"houseplan-assets/pdf-export-BjqKp8FJ.js",
"houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js"
"houseplan-assets/de-BrzcYO-1.js",
"houseplan-assets/editor-Cfrg3iMq.js",
"houseplan-assets/fr-CLHfUBDP.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js",
"houseplan-assets/hp-zigbee-topology-overlay-jvjkBbI9.js",
"houseplan-assets/iso-scene-render-D2f0EUgN.js",
"houseplan-assets/live-interaction-runtime-BA69oN1Z.js",
"houseplan-assets/pdf-export-DeQrvN1z.js",
"houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"sha256": "127b16a25364b9c345048b0644af66507c965c8860238f1df44fd5e57cf45673",
"rawBytes": 669985,
"gzipBytes": 178796,
"path": "houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"sha256": "0d095de08b11966dd3cb784099bba2f92d8c294f0817a49f3e3d4813ca9baa0b",
"rawBytes": 670150,
"gzipBytes": 178818,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js"
"houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js",
"sha256": "78b8014a00d4e3d8514673b19408b26b7ff7d7318f4ebfaf92862cba9af29e17",
"path": "houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js",
"sha256": "66ec3c0650ce721de901ec5d8d175c3b76e4878da909dada0d92589b3c8da4b5",
"rawBytes": 28287,
"gzipBytes": 7040,
"gzipBytes": 7036,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-B2VZUom6.js",
"sha256": "8de5b4d99de13b1535ba2315eb8513cd244dab6697f5254434becd36655d2cad",
"path": "houseplan-assets/hp-zigbee-topology-overlay-jvjkBbI9.js",
"sha256": "d4b6c957d4a82b979f7a71832e583fbd93852f7f8c340ce7a32fb8f773a59bc5",
"rawBytes": 10756,
"gzipBytes": 3690,
"gzipBytes": 3685,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js",
"houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js",
"houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/iso-scene-render-BgPGm0jS.js",
"sha256": "05edb6cfa80969f2ad13bf9e27841ca4848585b027b81231b975b0dedbb5ea6a",
"path": "houseplan-assets/iso-scene-render-D2f0EUgN.js",
"sha256": "92b77a114af62667adceacab9ecb76b631afb2ac83d9e255af206678f79cac47",
"rawBytes": 39206,
"gzipBytes": 13237,
"gzipBytes": 13234,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/live-interaction-runtime-B3A1vG3V.js",
"sha256": "fbb0229d581500c183cca3a874429bcdb796500b2c93dfeb45b11e6cf220bc3b",
"path": "houseplan-assets/live-interaction-runtime-BA69oN1Z.js",
"sha256": "1e3130110cdb4af9dfe938444d9b8a88ef4f6c30502841f5785231894e1ed7a6",
"rawBytes": 8216,
"gzipBytes": 3212,
"gzipBytes": 3207,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/pdf-export-BjqKp8FJ.js",
"sha256": "d82304f619cf6c96010be3ca3ed5485c7202b906a8bbdc81c728f1f892c0e519",
"path": "houseplan-assets/pdf-export-DeQrvN1z.js",
"sha256": "11a305cbbb3772c55968cbcb502c1878e4e74522c5179e1ee281e80fb85d6f93",
"rawBytes": 236244,
"gzipBytes": 109873,
"isEntry": false,
"imports": [
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-editor-BYyTIolZ.js",
"sha256": "28a5039bf5a396afd340b8451a26bc5b4eb039891bcca619920b26442c1cacdd",
"path": "houseplan-assets/summary-panel-editor-o2bn2wLY.js",
"sha256": "c253cfec20c5cef0a472b4dd42caca4276d147e48ea795f9d1b562c4a39da46e",
"rawBytes": 14824,
"gzipBytes": 3403,
"gzipBytes": 3401,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-metrics-DI6NSgQ6.js",
"sha256": "8d6b62e1d95fa3c0bc67895de8be13dafead66504a4ddfcd672557c4ed43a10b",
"path": "houseplan-assets/summary-panel-metrics-CzgAvzH6.js",
"sha256": "51530c69ada90dc42a92f5e297366734969c1a91c58ae30b9ecd528978a5441e",
"rawBytes": 2610,
"gzipBytes": 1309,
"gzipBytes": 1305,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js",
"sha256": "e0fab1aed737361ca8ccda596e4fc9449ee180e87b3cfab36fd3df6cf029f821",
"path": "houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js",
"sha256": "fa463fbec81b0635b56be87906d24442eba48ed1047445e192b5eb3ec519b7c1",
"rawBytes": 73366,
"gzipBytes": 20092,
"gzipBytes": 20089,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": [
"houseplan-assets/summary-panel-editor-BYyTIolZ.js",
"houseplan-assets/summary-panel-metrics-DI6NSgQ6.js"
"houseplan-assets/summary-panel-editor-o2bn2wLY.js",
"houseplan-assets/summary-panel-metrics-CzgAvzH6.js"
]
},
{
"path": "houseplan-assets/zigbee-topology-BQmHNkVD.js",
"sha256": "9b3a88e02b6e05722f67545e1d954b43d10fd2ac543632cc9a6902ca96190b4e",
"path": "houseplan-assets/zigbee-topology-B8St6NV9.js",
"sha256": "d35d5ebeedb562a4c29709da95964080f053e4e1eb0d3f2fb7eb957b71444e9c",
"rawBytes": 18961,
"gzipBytes": 7433,
"gzipBytes": 7426,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js",
"sha256": "2ae2e58c3e26a1e7744be195e2eec0b48b44056ed994c586f1d1c492a657afa7",
"path": "houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js",
"sha256": "a7774967077014e62b395dacb43dc7eb10022b03b6ac24a358ce53eee4469786",
"rawBytes": 3941,
"gzipBytes": 1768,
"gzipBytes": 1764,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "a4a059b33f1c6638c3470a30ae7db42d841d650fdf67e11db64c6934642de56c",
"sha256": "79b31c6683afff9ca22fb99a7a855f81957a199352f0f212807444b2998ee7d1",
"rawBytes": 1205,
"gzipBytes": 804,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-panel.js",
"sha256": "3371c2efe7f5f523ac78c4e06f073b0a6c2527bb69008dfb08d8c4b34313fb21",
"sha256": "4240e1335652c727ecf9a8868b515945f8c97b50cff332708ac302b9c0ccf6b4",
"rawBytes": 6165,
"gzipBytes": 2328,
"gzipBytes": 2326,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
}
@@ -1,14 +1,14 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{q as e,l as o,v as t,e4 as s,A as a,e5 as i,e6 as l,e7 as n,E as r,c}from"./houseplan-card-DFzGk9yU.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")},_=this._schema,f=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__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{q as e,l as o,v as t,e4 as s,A as a,e5 as i,e6 as l,e7 as n,E as r,c}from"./houseplan-card-BvxqYw8A.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}
.computeLabel=${e=>h[e.name]||e.name}
@value-changed=${this._valueChanged}
></ha-form>`;return c`
${d(_.slice(0,3))}
${f?c`<div class="default-floor-error" role="alert"
${d(f.slice(0,3))}
${_?c`<div class="default-floor-error" role="alert"
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
${t(s,"editor.default_floor_missing",{id:f})}
${t(s,"editor.default_floor_missing",{id:_})}
</div>`:a}
${d(_.slice(3))}
${d(f.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{c as a,A as e}from"./houseplan-card-DFzGk9yU.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-C6bLorXN.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__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{c as a,A as e}from"./houseplan-card-BvxqYw8A.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-B6w0YS6P.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"
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{ea as e,P as t,a5 as n,M as i,aq as r,N as o,bs as a,br as s,a4 as c,a3 as l,aA as u,eb as d,ec as f,bu as m,ed as g}from"./houseplan-card-DFzGk9yU.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 r=String(i.binding||"").indexOf(":");if(r<1)continue;const o=i.binding.slice(0,r),a=i.binding.slice(r+1),s="device"===o?e.registry.devices?.[a]:null,c="entity"===o?e.registry.entities?.[a]:null,l="device"===o?a:c?.device_id,u="device"===o?s?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.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;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===o&&n.liveEntities.has(a))&&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,r,o);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:l(e.rooms,t.walls,t.openCuts,i,t.cellCm,r,o)}}function*h(c,l,u=_){try{let d=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=u(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const s=t(e);if(!s)continue;const c=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,r,o,g.roomGeom,g.multiWallNodes)||s;v=y(v,a(c,m.physicalBodies)),yield}const _=m.cellCm/r;d+=s(v)*_*_/1e4}return d}catch{return null}}function p(e,t,n=_){const i=h(e,t,n);let r=i.next();for(;!r.done;)r=i.next();return r.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};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{ea as e,P as t,a5 as n,M as i,aq as r,N as o,bs as s,br as a,a4 as c,a3 as l,aA as u,eb as d,ec as f,bu as m,ed as g}from"./houseplan-card-BvxqYw8A.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 r=String(i.binding||"").indexOf(":");if(r<1)continue;const o=i.binding.slice(0,r),s=i.binding.slice(r+1),a="device"===o?e.registry.devices?.[s]:null,c="entity"===o?e.registry.entities?.[s]:null,l="device"===o?s:c?.device_id,u="device"===o?a?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.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;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===o&&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,r,o);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:l(e.rooms,t.walls,t.openCuts,i,t.cellCm,r,o)}}function*b(c,l,u=_){try{let d=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=u(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,r,o,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(c,m.physicalBodies)),yield}const _=m.cellCm/r;d+=a(v)*_*_/1e4}return d}catch{return null}}function h(e,t,n=_){const i=b(e,t,n);let r=i.next();for(;!r.done;)r=i.next();return r.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=d(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__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{n as t,b as n}from"./zigbee-topology-BQmHNkVD.js";import{F as e}from"./houseplan-card-DFzGk9yU.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 m(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 g(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,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,g=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&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await m(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 m(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,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{n as t,b as n}from"./zigbee-topology-B8St6NV9.js";import{F as e}from"./houseplan-card-BvxqYw8A.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 f(t){if(!0!==t?.user?.is_admin)throw a("permission")}function p(n){const e=i(n);return e?c(e,"zha",async()=>{if(f(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 m(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 g(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(f(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,p=null,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{p=t,g=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 f=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&&p?.(n):c&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof f&&b.push(f),await m(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 m(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{p as readZhaTopology,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";try{await import("./houseplan-assets/houseplan-card-DFzGk9yU.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__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";try{await import("./houseplan-assets/houseplan-card-BvxqYw8A.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
+2 -1
View File
@@ -768,7 +768,8 @@ export function prepareGoldenFixture(scenario) {
for (const room of space.rooms) {
if (!(room.id in scenario.roomCustomFill)) continue;
unknown.delete(room.id);
room.settings = { ...(room.settings || {}), custom_fill: scenario.roomCustomFill[room.id] };
// #581: a room colour is carried only by the room's own `custom` mode
room.settings = { ...(room.settings || {}), fill_mode: 'custom', custom_fill: scenario.roomCustomFill[room.id] };
}
if (unknown.size)
throw new Error(`golden roomCustomFill references missing room(s): ${[...unknown].join(', ')}`);
+59
View File
@@ -79,6 +79,65 @@ const res = await page.evaluate(async () => {
await new Promise((r) => setTimeout(r, 250));
out.fillDoesNotOptOutGlow = !sr().querySelector('clipPath#hp-glow-enabled')
&& sr().querySelectorAll('.glow-base-layer .glow-base').length === c._spaceModel().rooms.length;
// 7) #581: «Как у пространства» забывает свой цвет комнаты; сироты лечатся при чтении.
// Пространство — «Свой цвет» A; комната получает свой цвет B, потом возвращается
// к пространству: в конфиге не остаётся ни режима, ни цвета, план красит A.
// Диалог живёт в редакторе плана, где комнаты рисуются синей размывкой без заливок,
// поэтому цвет проверяется во View, а черновик — через кадровый резолвер заливок,
// единый для всех поверхностей (`_resolvedRoomFills`).
const A = { c: '#123456', a: 0.37 };
const B = { c: '#abcdef', a: 0.61 };
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== spId ? s : ({
...s, settings: { ...(s.settings || {}), fill_mode: 'custom', custom_fill: A, glow_enabled: false } })) };
c._setMode('plan'); c.requestUpdate(); await c.updateComplete;
const frameFill = (id) => c._resolvedRoomFills(c._spaceModel(), c._spaceDisplayForRender()).byId.get(id);
const resolves = (id, fill) => { const f = frameFill(id); return !!f && f.mode === 'custom' && f.color === fill.c && f.opacity === fill.a; };
const paintsInView = async (id, fill) => {
c._setMode('view'); c.requestUpdate(); await c.updateComplete;
const style = sr().querySelector(`.room[data-id="${id}"]`)?.getAttribute('style') || '';
c._setMode('plan'); c.requestUpdate(); await c.updateComplete;
return style.includes(`--room-fill:${fill.c}`) && style.includes(`--room-fill-op:${fill.a}`);
};
const colorRow = () => !!sr().querySelector('hp-dialog hp-color-opacity');
const cfgRoom = () => c._curSpaceCfg.rooms.find((r) => r.id === editedId);
// own colour: mode + colour stored, plan paints B
c._openRoomEdit(cfgRoom()); await c.updateComplete;
out.ownColourRowHiddenWhileInheriting = !colorRow();
c._roomFill = 'custom'; c._roomCustomFill = { ...B }; c.requestUpdate(); await c.updateComplete;
out.ownColourRowShownForCustom = colorRow();
out.ownColourDraftResolves = resolves(editedId, B);
c._saveRoomEdit(); await c.updateComplete;
out.ownColourStored = cfgRoom().settings?.fill_mode === 'custom' && cfgRoom().settings?.custom_fill?.c === B.c;
out.ownColourPaintsInView = await paintsInView(editedId, B);
// AC5: back to "as the space" in the open dialog — the frame resolver answers A at once
c._openRoomEdit(cfgRoom()); await c.updateComplete;
out.reopenedWithOwnColour = c._roomFill === 'custom' && c._roomCustomFill?.c === B.c && colorRow();
const inheritRadio = [...sr().querySelectorAll('hp-dialog .srcrow')]
.find((l) => l.textContent.trim() === c._t('fill.inherit'))?.querySelector('input[type="radio"]');
inheritRadio.checked = true; inheritRadio.dispatchEvent(new Event('change', { bubbles: true }));
await c.updateComplete;
out.inheritClearsDraft = c._roomFill === '' && c._roomCustomFill === null && !colorRow();
out.inheritDraftResolvesSpace = resolves(editedId, A);
// AC3: saved — neither the mode nor the colour survives, the plan paints A
c._saveRoomEdit(); await c.updateComplete;
out.inheritForgetsColour = !('fill_mode' in (cfgRoom().settings || {})) && !('custom_fill' in (cfgRoom().settings || {}));
out.inheritPaintsSpaceInView = await paintsInView(editedId, A);
c._openRoomEdit(cfgRoom()); await c.updateComplete;
out.inheritReopensWithoutColourRow = c._roomFill === '' && c._roomCustomFill === null && !colorRow();
c._roomDialogCancel(); await c.updateComplete;
// AC4: an orphan saved by an older editor (Cabinet on the dacha) — paints A,
// opens as "as the space" without a colour row, and a plain save drops it
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== spId ? s : ({
...s, rooms: s.rooms.map((r) => r.id !== editedId ? r : ({ ...r,
settings: { ...(r.settings || {}), custom_fill: { c: '#182a32', a: 0.59 }, name_scale: 1.35, label_scale: 1.2 } })) })) };
c.requestUpdate(); await c.updateComplete;
out.orphanPaintsSpaceInView = await paintsInView(editedId, A) && !(await paintsInView(editedId, { c: '#182a32', a: 0.59 }));
c._openRoomEdit(cfgRoom()); await c.updateComplete;
out.orphanOpensAsInherit = c._roomFill === '' && c._roomCustomFill === null && !colorRow();
c._saveRoomEdit(); await c.updateComplete;
out.orphanDroppedOnSave = !('custom_fill' in (cfgRoom().settings || {}))
&& cfgRoom().settings?.name_scale === 1.35 && cfgRoom().settings?.label_scale === 1.2;
out.orphanStillPaintsSpaceAfterSave = await paintsInView(editedId, A);
return out;
});
checkAll(res);
+12 -3
View File
@@ -32,18 +32,27 @@ const res = await page.evaluate(async () => {
})};
c.requestUpdate(); await c.updateComplete;
out.lqiFills = [...sr().querySelectorAll('.room.styled')].filter((r) => (r.getAttribute('style') || '').includes('hsl(')).length;
// 4) custom fill is static, room-overridable, and Glow pools do not tint it
// 4) custom fill is static, room-overridable, and Glow pools do not tint it.
// #581: the room colour counts only with the room's OWN `custom` mode; a
// colour stored without it (an orphan) paints the space colour.
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== 'f1' ? s : ({
...s,
settings: { ...s.settings, fill_mode: 'custom', glow_enabled: true,
custom_fill: { c: '#123456', a: 0.37 } },
rooms: s.rooms.map((room, index) => index ? room : ({ ...room,
settings: { ...(room.settings || {}), custom_fill: { c: '#abcdef', a: 0.61 } } })),
rooms: s.rooms.map((room, index) => index === 0
? ({ ...room, settings: { ...(room.settings || {}), fill_mode: 'custom', custom_fill: { c: '#abcdef', a: 0.61 } } })
: index === 1
? ({ ...room, settings: { ...(room.settings || {}), custom_fill: { c: '#ff0000', a: 0.9 } } })
: room),
})) };
c.requestUpdate(); await c.updateComplete;
const customStyles = [...sr().querySelectorAll('.room.styled')].map((r) => r.getAttribute('style') || '');
out.customSpaceFill = customStyles.some((s) => s.includes('--room-fill:#123456') && s.includes('--room-fill-op:0.37'));
out.customRoomOverride = customStyles.some((s) => s.includes('--room-fill:#abcdef') && s.includes('--room-fill-op:0.61'));
const orphanRoomId = c._serverCfg.spaces.find((s) => s.id === 'f1').rooms[1].id;
const orphanStyle = sr().querySelector(`.room[data-id="${orphanRoomId}"]`)?.getAttribute('style') || '';
out.customRoomOrphanInheritsSpace = orphanStyle.includes('--room-fill:#123456') && orphanStyle.includes('--room-fill-op:0.37')
&& !customStyles.some((s) => s.includes('#ff0000'));
out.customNotTintedByBase = !sr().querySelector('.glow-base-layer, .glow-base-tunnels')
&& sr().querySelectorAll('.glow-pool').length > 0;
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== 'f1' ? s : ({
+133 -133
View File
@@ -1,295 +1,295 @@
{
"schema": 1,
"fingerprint": "ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621",
"fingerprint": "241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef",
"entry": "houseplan-card.js",
"panelEntry": "houseplan-panel.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 290789,
"initialViewGzipBytes": 290805,
"initialPanelFiles": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-panel.js"
],
"initialPanelGzipBytes": 292313,
"initialPanelGzipBytes": 292327,
"initialPanelOnlyFiles": [
"houseplan-panel.js"
],
"initialPanelOnlyGzipBytes": 2328,
"initialPanelOnlyGzipBytes": 2326,
"lazyFiles": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/de-CVHXDkji.js",
"houseplan-assets/editor-wZz2IlGD.js",
"houseplan-assets/fr-DR1YSIn4.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js",
"houseplan-assets/hp-zigbee-topology-overlay-B2VZUom6.js",
"houseplan-assets/iso-scene-render-BgPGm0jS.js",
"houseplan-assets/live-interaction-runtime-B3A1vG3V.js",
"houseplan-assets/pdf-export-BjqKp8FJ.js",
"houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js",
"houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/de-BrzcYO-1.js",
"houseplan-assets/editor-Cfrg3iMq.js",
"houseplan-assets/fr-CLHfUBDP.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js",
"houseplan-assets/hp-zigbee-topology-overlay-jvjkBbI9.js",
"houseplan-assets/iso-scene-render-D2f0EUgN.js",
"houseplan-assets/live-interaction-runtime-BA69oN1Z.js",
"houseplan-assets/pdf-export-DeQrvN1z.js",
"houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js",
"houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js"
],
"lazyGzipBytes": 418794,
"lazyGzipBytes": 418772,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/editor-wZz2IlGD.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/editor-Cfrg3iMq.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js"
],
"lazyEditorGzipBytes": 204946,
"lazyEditorGzipBytes": 204963,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js"
],
"lazyOnboardingGzipBytes": 13922,
"lazyOnboardingGzipBytes": 13916,
"lazyLocaleFiles": [
"houseplan-assets/de-CVHXDkji.js",
"houseplan-assets/fr-DR1YSIn4.js"
"houseplan-assets/de-BrzcYO-1.js",
"houseplan-assets/fr-CLHfUBDP.js"
],
"lazyLocaleGzipBytes": 54936,
"lazyLocaleGzipBytes": 54921,
"lazyIsometricFiles": [
"houseplan-assets/iso-scene-render-BgPGm0jS.js"
"houseplan-assets/iso-scene-render-D2f0EUgN.js"
],
"lazyIsometricGzipBytes": 13237,
"lazyIsometricGzipBytes": 13234,
"lazyFurnitureArtFiles": [
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js"
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js"
],
"lazyFurnitureArtGzipBytes": 10249,
"lazyFurnitureArtGzipBytes": 10255,
"lazyPdfFiles": [
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/pdf-export-BjqKp8FJ.js"
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/pdf-export-DeQrvN1z.js"
],
"lazyPdfGzipBytes": 120122,
"lazyPdfGzipBytes": 120128,
"files": [
{
"path": "houseplan-assets/backdrop-pick-BS6cZmWE.js",
"sha256": "66693f70e3a9779dc690c07b21c915800b675918c891c20d52538e5ee82e6eac",
"path": "houseplan-assets/backdrop-pick-ClLjW_UW.js",
"sha256": "5c8e9728df6ebdaad6346a26686f058ba4cce8bebd68c01d6cf18423ac67b792",
"rawBytes": 19956,
"gzipBytes": 6882,
"gzipBytes": 6880,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-CVHXDkji.js",
"sha256": "07785e7cc056eff8b274b48b692d8874d20f958c5c82afc31321df4f82c60121",
"path": "houseplan-assets/de-BrzcYO-1.js",
"sha256": "b7488c7f186f740f02cdfde3c093aed8754e91157c0903d196a36fee4fbfef60",
"rawBytes": 91590,
"gzipBytes": 27750,
"gzipBytes": 27743,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-wZz2IlGD.js",
"sha256": "09464bc1bc28ae0eefccff27e63d05093a1b7960e4961ff3202050c335af19ff",
"path": "houseplan-assets/editor-Cfrg3iMq.js",
"sha256": "83f9d762289e0f83a6e78730d3eb54862dbfa78408c93d410126f15988ca758c",
"rawBytes": 3831,
"gzipBytes": 1586,
"gzipBytes": 1584,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-DR1YSIn4.js",
"sha256": "2ec37225ffc437fd742ff9f525ebf086f1acb916bddc214058a40a2544a8f645",
"path": "houseplan-assets/fr-CLHfUBDP.js",
"sha256": "3524222b350ecbe2187351bf1730cc1d6393497104b10478cc3b7b0e5f14a9ec",
"rawBytes": 93884,
"gzipBytes": 27186,
"gzipBytes": 27178,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"sha256": "f0610686040a739a88f1b74dbcebbd0b2197bc4af40c0eae6876411d118d7140",
"path": "houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"sha256": "8303521f70c98617ece5cea54517760fdb808bec3fc7b68add0434ea56980dd3",
"rawBytes": 33655,
"gzipBytes": 10249,
"gzipBytes": 10255,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-DFzGk9yU.js",
"sha256": "768748c29e9e6fa154dc69cd8e81d6cd1ff54b8eca2874a2a1f0bf05404cbb55",
"rawBytes": 1032664,
"gzipBytes": 289985,
"path": "houseplan-assets/houseplan-card-BvxqYw8A.js",
"sha256": "da77e0c3d6445d114ff496613fc8aebfb7e7ed132bdfbc5c2bc8c5a79fd8afc2",
"rawBytes": 1032746,
"gzipBytes": 290001,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-CVHXDkji.js",
"houseplan-assets/editor-wZz2IlGD.js",
"houseplan-assets/fr-DR1YSIn4.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js",
"houseplan-assets/hp-zigbee-topology-overlay-B2VZUom6.js",
"houseplan-assets/iso-scene-render-BgPGm0jS.js",
"houseplan-assets/live-interaction-runtime-B3A1vG3V.js",
"houseplan-assets/pdf-export-BjqKp8FJ.js",
"houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js"
"houseplan-assets/de-BrzcYO-1.js",
"houseplan-assets/editor-Cfrg3iMq.js",
"houseplan-assets/fr-CLHfUBDP.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js",
"houseplan-assets/hp-zigbee-topology-overlay-jvjkBbI9.js",
"houseplan-assets/iso-scene-render-D2f0EUgN.js",
"houseplan-assets/live-interaction-runtime-BA69oN1Z.js",
"houseplan-assets/pdf-export-DeQrvN1z.js",
"houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-BUCY_q3j.js",
"sha256": "127b16a25364b9c345048b0644af66507c965c8860238f1df44fd5e57cf45673",
"rawBytes": 669985,
"gzipBytes": 178796,
"path": "houseplan-assets/houseplan-editor-runtime-Pkk2XGqR.js",
"sha256": "0d095de08b11966dd3cb784099bba2f92d8c294f0817a49f3e3d4813ca9baa0b",
"rawBytes": 670150,
"gzipBytes": 178818,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js"
"houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BMDNtsdn.js",
"sha256": "78b8014a00d4e3d8514673b19408b26b7ff7d7318f4ebfaf92862cba9af29e17",
"path": "houseplan-assets/houseplan-onboarding-runtime-BhJG-ch3.js",
"sha256": "66ec3c0650ce721de901ec5d8d175c3b76e4878da909dada0d92589b3c8da4b5",
"rawBytes": 28287,
"gzipBytes": 7040,
"gzipBytes": 7036,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-BS6cZmWE.js",
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/backdrop-pick-ClLjW_UW.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-B2VZUom6.js",
"sha256": "8de5b4d99de13b1535ba2315eb8513cd244dab6697f5254434becd36655d2cad",
"path": "houseplan-assets/hp-zigbee-topology-overlay-jvjkBbI9.js",
"sha256": "d4b6c957d4a82b979f7a71832e583fbd93852f7f8c340ce7a32fb8f773a59bc5",
"rawBytes": 10756,
"gzipBytes": 3690,
"gzipBytes": 3685,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js",
"houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js",
"houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/iso-scene-render-BgPGm0jS.js",
"sha256": "05edb6cfa80969f2ad13bf9e27841ca4848585b027b81231b975b0dedbb5ea6a",
"path": "houseplan-assets/iso-scene-render-D2f0EUgN.js",
"sha256": "92b77a114af62667adceacab9ecb76b631afb2ac83d9e255af206678f79cac47",
"rawBytes": 39206,
"gzipBytes": 13237,
"gzipBytes": 13234,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/live-interaction-runtime-B3A1vG3V.js",
"sha256": "fbb0229d581500c183cca3a874429bcdb796500b2c93dfeb45b11e6cf220bc3b",
"path": "houseplan-assets/live-interaction-runtime-BA69oN1Z.js",
"sha256": "1e3130110cdb4af9dfe938444d9b8a88ef4f6c30502841f5785231894e1ed7a6",
"rawBytes": 8216,
"gzipBytes": 3212,
"gzipBytes": 3207,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/pdf-export-BjqKp8FJ.js",
"sha256": "d82304f619cf6c96010be3ca3ed5485c7202b906a8bbdc81c728f1f892c0e519",
"path": "houseplan-assets/pdf-export-DeQrvN1z.js",
"sha256": "11a305cbbb3772c55968cbcb502c1878e4e74522c5179e1ee281e80fb85d6f93",
"rawBytes": 236244,
"gzipBytes": 109873,
"isEntry": false,
"imports": [
"houseplan-assets/furniture-plan-art.generated-C1gnUJZQ.js",
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/furniture-plan-art.generated-7nJsC48X.js",
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-editor-BYyTIolZ.js",
"sha256": "28a5039bf5a396afd340b8451a26bc5b4eb039891bcca619920b26442c1cacdd",
"path": "houseplan-assets/summary-panel-editor-o2bn2wLY.js",
"sha256": "c253cfec20c5cef0a472b4dd42caca4276d147e48ea795f9d1b562c4a39da46e",
"rawBytes": 14824,
"gzipBytes": 3403,
"gzipBytes": 3401,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-metrics-DI6NSgQ6.js",
"sha256": "8d6b62e1d95fa3c0bc67895de8be13dafead66504a4ddfcd672557c4ed43a10b",
"path": "houseplan-assets/summary-panel-metrics-CzgAvzH6.js",
"sha256": "51530c69ada90dc42a92f5e297366734969c1a91c58ae30b9ecd528978a5441e",
"rawBytes": 2610,
"gzipBytes": 1309,
"gzipBytes": 1305,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/summary-panel-runtime-loaded-C6bLorXN.js",
"sha256": "e0fab1aed737361ca8ccda596e4fc9449ee180e87b3cfab36fd3df6cf029f821",
"path": "houseplan-assets/summary-panel-runtime-loaded-B6w0YS6P.js",
"sha256": "fa463fbec81b0635b56be87906d24442eba48ed1047445e192b5eb3ec519b7c1",
"rawBytes": 73366,
"gzipBytes": 20092,
"gzipBytes": 20089,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": [
"houseplan-assets/summary-panel-editor-BYyTIolZ.js",
"houseplan-assets/summary-panel-metrics-DI6NSgQ6.js"
"houseplan-assets/summary-panel-editor-o2bn2wLY.js",
"houseplan-assets/summary-panel-metrics-CzgAvzH6.js"
]
},
{
"path": "houseplan-assets/zigbee-topology-BQmHNkVD.js",
"sha256": "9b3a88e02b6e05722f67545e1d954b43d10fd2ac543632cc9a6902ca96190b4e",
"path": "houseplan-assets/zigbee-topology-B8St6NV9.js",
"sha256": "d35d5ebeedb562a4c29709da95964080f053e4e1eb0d3f2fb7eb957b71444e9c",
"rawBytes": 18961,
"gzipBytes": 7433,
"gzipBytes": 7426,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-Cy0BWLHN.js",
"sha256": "2ae2e58c3e26a1e7744be195e2eec0b48b44056ed994c586f1d1c492a657afa7",
"path": "houseplan-assets/zigbee-topology-runtime-BkrLGjiL.js",
"sha256": "a7774967077014e62b395dacb43dc7eb10022b03b6ac24a358ce53eee4469786",
"rawBytes": 3941,
"gzipBytes": 1768,
"gzipBytes": 1764,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js",
"houseplan-assets/zigbee-topology-BQmHNkVD.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js",
"houseplan-assets/zigbee-topology-B8St6NV9.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "a4a059b33f1c6638c3470a30ae7db42d841d650fdf67e11db64c6934642de56c",
"sha256": "79b31c6683afff9ca22fb99a7a855f81957a199352f0f212807444b2998ee7d1",
"rawBytes": 1205,
"gzipBytes": 804,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.js"
],
"dynamicImports": []
},
{
"path": "houseplan-panel.js",
"sha256": "3371c2efe7f5f523ac78c4e06f073b0a6c2527bb69008dfb08d8c4b34313fb21",
"sha256": "4240e1335652c727ecf9a8868b515945f8c97b50cff332708ac302b9c0ccf6b4",
"rawBytes": 6165,
"gzipBytes": 2328,
"gzipBytes": 2326,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-DFzGk9yU.js"
"houseplan-assets/houseplan-card-BvxqYw8A.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,14 +1,14 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{q as e,l as o,v as t,e4 as s,A as a,e5 as i,e6 as l,e7 as n,E as r,c}from"./houseplan-card-DFzGk9yU.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")},_=this._schema,f=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__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{q as e,l as o,v as t,e4 as s,A as a,e5 as i,e6 as l,e7 as n,E as r,c}from"./houseplan-card-BvxqYw8A.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}
.computeLabel=${e=>h[e.name]||e.name}
@value-changed=${this._valueChanged}
></ha-form>`;return c`
${d(_.slice(0,3))}
${f?c`<div class="default-floor-error" role="alert"
${d(f.slice(0,3))}
${_?c`<div class="default-floor-error" role="alert"
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
${t(s,"editor.default_floor_missing",{id:f})}
${t(s,"editor.default_floor_missing",{id:_})}
</div>`:a}
${d(_.slice(3))}
${d(f.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
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__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{c as a,A as e}from"./houseplan-card-DFzGk9yU.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-C6bLorXN.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__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{c as a,A as e}from"./houseplan-card-BvxqYw8A.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-B6w0YS6P.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"
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{ea as e,P as t,a5 as n,M as i,aq as r,N as o,bs as a,br as s,a4 as c,a3 as l,aA as u,eb as d,ec as f,bu as m,ed as g}from"./houseplan-card-DFzGk9yU.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 r=String(i.binding||"").indexOf(":");if(r<1)continue;const o=i.binding.slice(0,r),a=i.binding.slice(r+1),s="device"===o?e.registry.devices?.[a]:null,c="entity"===o?e.registry.entities?.[a]:null,l="device"===o?a:c?.device_id,u="device"===o?s?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.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;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===o&&n.liveEntities.has(a))&&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,r,o);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:l(e.rooms,t.walls,t.openCuts,i,t.cellCm,r,o)}}function*h(c,l,u=_){try{let d=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=u(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const s=t(e);if(!s)continue;const c=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,r,o,g.roomGeom,g.multiWallNodes)||s;v=y(v,a(c,m.physicalBodies)),yield}const _=m.cellCm/r;d+=s(v)*_*_/1e4}return d}catch{return null}}function p(e,t,n=_){const i=h(e,t,n);let r=i.next();for(;!r.done;)r=i.next();return r.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};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{ea as e,P as t,a5 as n,M as i,aq as r,N as o,bs as s,br as a,a4 as c,a3 as l,aA as u,eb as d,ec as f,bu as m,ed as g}from"./houseplan-card-BvxqYw8A.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 r=String(i.binding||"").indexOf(":");if(r<1)continue;const o=i.binding.slice(0,r),s=i.binding.slice(r+1),a="device"===o?e.registry.devices?.[s]:null,c="entity"===o?e.registry.entities?.[s]:null,l="device"===o?s:c?.device_id,u="device"===o?a?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.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;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===o&&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,r,o);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:l(e.rooms,t.walls,t.openCuts,i,t.cellCm,r,o)}}function*b(c,l,u=_){try{let d=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=u(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,r,o,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(c,m.physicalBodies)),yield}const _=m.cellCm/r;d+=a(v)*_*_/1e4}return d}catch{return null}}function h(e,t,n=_){const i=b(e,t,n);let r=i.next();for(;!r.done;)r=i.next();return r.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=d(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__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";import{n as t,b as n}from"./zigbee-topology-BQmHNkVD.js";import{F as e}from"./houseplan-card-DFzGk9yU.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 m(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 g(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,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{f=t,g=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&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof p&&b.push(p),await m(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 m(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,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";import{n as t,b as n}from"./zigbee-topology-B8St6NV9.js";import{F as e}from"./houseplan-card-BvxqYw8A.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 f(t){if(!0!==t?.user?.is_admin)throw a("permission")}function p(n){const e=i(n);return e?c(e,"zha",async()=>{if(f(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 m(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 g(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(f(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,p=null,g=null;const y=new Promise(t=>{u=t}),w=new Promise((t,n)=>{p=t,g=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 f=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&&p?.(n):c&&g?.(a("invalid_payload"))},{type:"mqtt/subscribe",topic:`${l}/bridge/response/networkmap`});"function"==typeof f&&b.push(f),await m(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 m(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{p as readZhaTopology,g as refreshZ2mTopology,u as subscribeZigbeeTopology,l as zigbeeTopologyRuntimeSnapshot};
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="ab886881caa24d91f2954677ab9477c66a5158d85e9808105269a78eb868c621";try{await import("./houseplan-assets/houseplan-card-DFzGk9yU.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__="241ab786b7cce5c1f837e73704240f138bd3e097bfecdbb0e93046bf4a19d3ef";try{await import("./houseplan-assets/houseplan-card-BvxqYw8A.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
+10 -4
View File
@@ -1654,10 +1654,16 @@ its configured space.
stay independent and continue to render. The static room card uses the same
data/base projection, omits empty base groups, and renders the same live pools
only when its default-off `light_pools` option is enabled.
- **Custom room fill** (#56): `space.settings.custom_fill` is the space color
and `room.settings.custom_fill` is an optional explicit override. The pure
projection is room → space → `{c:'#607d8b',a:.18}` and every read crosses
`safeStoredColor` plus finite alpha clamping. `resolveEffectiveRoomFill`
- **Custom room fill** (#56, #581): `space.settings.custom_fill` is the space
color and `room.settings.custom_fill` is an optional explicit override that
counts **only together with the room's own `fill_mode: 'custom'`**. The pure
projection is room (own mode) → space → `{c:'#607d8b',a:.18}` and every read
crosses `safeStoredColor` plus finite alpha clamping. A room colour stored
without the own mode — the state "as the space" used to leave behind — is an
orphan: `roomCustomFillOf` paints the space colour, never rewrites the config,
and the next save of that room drops the field. The room dialog keeps the
colour draft only under its own "custom" radio and clears it when the radio
leaves that mode, so the dialog can no longer create the orphan. `resolveEffectiveRoomFill`
remains the single source for room floor, clean-floor holes and thick-wall
tunnel colors; stored `room_color` continues to control borders/names only.
- **Glow pools and additive composition** (#19, #71): every source retains its
+7
View File
@@ -7,6 +7,13 @@
opening reaches the room, instead of shining through solid wall
([#580](https://github.com/Matysh/houseplan-card/issues/580)).
- A room switched back to “As the space” no longer keeps its own colour: that
choice now forgets the room colour, and the colour row in room settings is
shown only under “Custom color”. Rooms already stuck with a foreign colour
(a room colour stored without the room's own mode) are painted in the space
colour after the update, with nothing to do
([#581](https://github.com/Matysh/houseplan-card/issues/581)).
## v1.76.0-beta.4 — 2026-09-15
- General settings now let window sunlight start at either the inner window
+7
View File
@@ -13,6 +13,13 @@
оконный проём часть луча, а не свет сквозь сплошную стену
([#580](https://github.com/Matysh/houseplan-card/issues/580)).
- Комната, которой вернули «Как у пространства», больше не остаётся своего
цвета: этот выбор теперь забывает цвет комнаты, а строка цвета в настройках
комнаты показывается только при «Свой цвет». Комнаты, уже застрявшие с чужим
цветом (свой цвет без своего режима), после обновления красятся цветом
пространства без каких-либо действий
([#581](https://github.com/Matysh/houseplan-card/issues/581)).
## v1.76.0-beta.4 — 2026-09-15
- В общих настройках теперь можно выбрать, от каких углов окна начинаются
+11 -1
View File
@@ -590,7 +590,17 @@ Current `fill_mode` additionally accepts `custom`. Its optional color is stored
as `{c:'#RRGGBB',a:0..1}` in `space.settings.custom_fill` and, for an explicit
room override, `room.settings.custom_fill`. Missing or invalid historical data
is projected at render time through room → space → `#607d8b`/`0.18`; merely
reading it never rewrites the config. Backend writes keep the strict shared
reading it never rewrites the config.
Since #581 the room override is read only together with the room's **own**
`fill_mode: 'custom'`. A `room.settings.custom_fill` stored without that mode —
older editors wrote it when a room was switched back to "as the space" — is
projected as the space colour on every surface (card, space-card, PDF) without
being rewritten; the next save of that room through its dialog removes the
field. New saves never write `custom_fill` without `fill_mode: 'custom'`. The
field shapes and the backend schema are unchanged, so older clients read
configs saved by newer ones as "a room without its own colour" — the same
picture the newer client shows. Backend writes keep the strict shared
hex/finite-alpha contract. An explicit `null` is accepted at either level and
has the same projection semantics as a missing override.
+9
View File
@@ -1388,6 +1388,15 @@ separately promised workflows:
temp cold/comfy/hot, LQI weak/strong), each with its own opacity slider [manual];
Reset restores defaults; saving defaults stores nothing [manual]
- [ ] Custom fill colors apply to the full card AND the static space-card
- [ ] **Room colour follows the room's own mode (#581):** a room colour counts
only with the room's own «Свой цвет»; «Как у пространства» clears the
colour draft at once, the saved room keeps neither `fill_mode` nor
`custom_fill`, and the plan paints the space colour. A colour stored
without the mode (an orphan from an older editor) paints the space colour,
opens as «Как у пространства» without a colour row and is dropped by a
plain save; `name_scale`/`label_scale` survive [unit: `logic.test.mjs`
`#581 AC1`; auto: `smoke_room_settings` step 7, `smoke_space_settings`
`customRoomOrphanInheritsSpace`; mutation: `room-orphan-colour-wins-again`]
- [ ] LQI gradient interpolates between the configured weak/strong colors [manual]
- [ ] Per-space "Show zigbee signal (LQI)" toggle hides/shows the badges next to
devices and the signal line in room tooltips for that space only [manual]
+4 -1
View File
@@ -894,7 +894,10 @@ non-interactive.
## 13. Room fills and light
Space fill modes include user colour, temperature comfort range and LQI. Room
settings may override the space. Glow is independent from the base fill.
settings may override the space. A room has its own colour only while its fill
is set to its own **Custom color**; choosing **As the space** forgets that
colour and the room is painted like the rest of the space. Glow is independent
from the base fill.
When a room effectively uses the temperature fill, its settings show optional
lower and upper comfort bounds. Each blank field independently inherits the
+1 -1
View File
@@ -1421,7 +1421,7 @@ Power=`off`/`unavailable` подавляет даже устаревший ак
|---|---|
| Общие настройки | Цвет и прозрачность состояний данных, настройки Glow вместе с его радиусом, цвет стен |
| Пространство | Режим заливки, свой цвет и прозрачность, независимый переключатель Glow, температурные границы, показ LQI, фон |
| Комната | Наследовать или явно выбрать Нет/LQI/Свет/Температуру/свой цвет; задать собственные нижнюю и верхнюю границы температуры; свой цвет и каждая пустая температурная граница наследуются от пространства; пока наследует Glow пространства |
| Комната | Наследовать или явно выбрать Нет/LQI/Свет/Температуру/свой цвет; задать собственные нижнюю и верхнюю границы температуры; собственный цвет есть только в режиме «Свой цвет» комнаты — «Как у пространства» забывает его, и комната красится цветом пространства; каждая пустая температурная граница наследуется от пространства; пока наследует Glow пространства |
| Устройство | Выбрать Авто/Всегда/Никогда для собственной световой роли, цвет и яркость Glow, радиус свечения |
При температурной заливке в настройках комнаты появляется диапазон комфорта.
+12 -12
View File
@@ -3,7 +3,7 @@
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
"oxipng": "oxipng 10.2.0",
"sourceFingerprint": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceFingerprint": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"captureScriptSha256": "9e4b0ae533407fe003a129078aec020f2c83d67cdb6fc6da696ce3d829beaeb5",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -15,7 +15,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "76c35a87810e3dc6890bda7b43da133f57d8860031204a62cda7115c15950090"
},
"view-touch": {
@@ -26,7 +26,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "795585acb09488236ebb8028a7fcbd918404c5fe01560bb9b4559de13d441625"
},
"space-create": {
@@ -37,7 +37,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "9f0970de09772674c3156b9fc5b2d49de0ceb22be11ecf8b136047d37d100a84"
},
"room-contour-close": {
@@ -48,7 +48,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "0c354a79f9c3660788b6c5e889a27b6bd78b1f209dc0eef6bb43e2c1344028c4"
},
"plan-context-tray": {
@@ -59,7 +59,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "64169c543291bc4df806dcf8ff484b42c8ea55eb343323d945344e0e28f2ce97"
},
"device-editor": {
@@ -70,7 +70,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "3c8703175605fcf0387d654cd30783ecfd33e21743e70b45190a5ae1a8d355c2"
},
"device-display-preview": {
@@ -81,7 +81,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "5d6193202102f5e6d85567f1111fa105895b933979fbf369c6ba0b1029deb823"
},
"background-editor": {
@@ -92,7 +92,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "375900a29aa62632883ede6727b7dbb8b48cabe9d7d0e16425f9779c837f3dd9"
},
"room-card": {
@@ -103,7 +103,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "3d85424676c823ddca3fa7dab3cce089b6030e48c05d16d2d48d922c66c0cbd2"
},
"device-info": {
@@ -114,7 +114,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "ac962820f8e1c5cb5b042d39b18dc95905fa840cb2d4749060d991be4f6977c2"
},
"pdf-export": {
@@ -125,7 +125,7 @@
},
"theme": "light",
"language": "en",
"sourceSha256": "1d093022e8e71a794c3996bb5ad4e23d473cdf79b6d9fa3e188661603ef1170a",
"sourceSha256": "46939ba13473911af6bb51df07e30c93e3f1af9908678c530c49608fca443207",
"imageSha256": "4537692fd57a3ed95528eddd748eee5cb6e456ffab7cc49169853d62459252cd"
}
},
+9 -1
View File
@@ -268,8 +268,16 @@ export const LOW_HEADROOM_WARNING_BYTES = 15_000;
* общего бюджета. Выбор внутренней или внешней грани окна относится к
* синхронной геометрии лучей в initial View. Измеренный факт 290 629 Б
* оставляет 671 Б сверху и 1 329 Б до нижней границы полосы.
*
* 2026-09-15, #581: потолок перецентрирован 291 300 → 291 700 без изменения
* общего бюджета. Цвет комнаты участвует в раскраске только вместе с её
* собственным режимом «Свой цвет» — одна проверка режима в `roomCustomFillOf`
* и та же проверка на черновике диалога (+16 Б gzip). Факт на `dev` после #580
* был 290 789 Б — 511 Б сверху, у самого края шумовой полосы; измеренный факт
* 290 805 Б под новым центром оставляет 895 Б сверху и 1 105 Б до нижней
* границы полосы.
*/
export const INITIAL_VIEW_GZIP_CEILING = 291_300;
export const INITIAL_VIEW_GZIP_CEILING = 291_700;
export const INITIAL_VIEW_CEILING_BAND = 2_000;
/**
+13
View File
@@ -9863,6 +9863,19 @@ const MUTANT_DEFINITIONS = [
replace: ' "zones_v1": ("zones", ()),',
}],
},
{
id: 'room-orphan-colour-wins-again',
guard: 'node --test test/logic.test.mjs',
because: '#581: a room colour stored without the room\'s own `custom` mode is an orphan — the '
+ 'state "as the space" used to leave behind (Cabinet on the dacha). Without the mode gate the '
+ 'orphan paints again on every surface, and the radio "as the space" lies once more',
patches: [{
file: 'src/logic.ts',
find: " if (settings?.fill_mode !== 'custom') return spaceFill;",
// runtime-false, not statically dead (#568): tsc must still compile the mutant
replace: " if (settings?.fill_mode === 'never-a-fill-mode') return spaceFill;",
}],
},
];
const mutationCardSource = readFileSync(join(repoRoot, 'src/houseplan-card.ts'), 'utf8');
+7
View File
@@ -27,6 +27,13 @@
/** @type {SmokeLink[]} */
export const SMOKE_LINKS = [
{
symbols: ['roomCustomFillOf'],
smokes: ['smoke_space_settings.mjs', 'smoke_room_settings.mjs'],
because: '#581: the room-colour projection (own custom mode carries the colour, an orphan paints '
+ 'the space) is observed through `.room` style variables in View and through the room-dialog '
+ 'draft; neither smoke calls the pure function by name',
},
{
symbols: [
'commitHouseplanViewport', 'isIdentityLiveLayerProjection', 'paintLiveViewport',
+3 -3
View File
@@ -9330,9 +9330,9 @@ export class HouseplanCard extends LitElement {
const mode = this._roomDialog && room.id === this._roomEditId
? (this._roomFill || disp.fill)
: roomFillModeOf(disp.fill, room);
const customFill = this._roomDialog && room.id === this._roomEditId
? (this._roomCustomFill || disp.customFill)
: roomCustomFillOf(disp.customFill, room);
// #581: the draft obeys the stored-settings rule — only the room's own `custom` mode carries its colour
const customFill = roomCustomFillOf(disp.customFill, this._roomDialog && room.id === this._roomEditId
? { settings: { fill_mode: this._roomFill, custom_fill: this._roomCustomFill } } : room);
const tempRange = roomTempRangeFromDraft(disp.tempMin, disp.tempMax, room,
this._roomTempMin, this._roomTempMax, this._roomDialog && room.id === this._roomEditId);
const resolved = resolveEffectiveRoomFill(
+10 -10
View File
@@ -11001,10 +11001,10 @@ public _openRoomEdit(r: RoomCfg): void {
this.host._roomFill = r.settings?.fill_mode === 'glow'
? ''
: ((r.settings?.fill_mode as any) || '');
const rawCustom = r.settings?.custom_fill;
// #581: a colour stored without the room's own custom mode is an orphan and stays out of the draft
const rawCustom = this.host._roomFill === 'custom' ? r.settings?.custom_fill : null;
this.host._roomCustomFill = rawCustom && typeof rawCustom === 'object'
? customFillOf(rawCustom, spaceDisplayOf(this.host._curSpaceCfg).customFill)
: null;
? customFillOf(rawCustom, spaceDisplayOf(this.host._curSpaceCfg).customFill) : null;
[this.host._roomTempMin, this.host._roomTempMax] = roomTempThresholdInputValues(r.settings);
this.host._roomTempSrc = r.settings?.temp_source || '';
this.host._roomHumSrc = r.settings?.hum_source || '';
@@ -11018,7 +11018,7 @@ public _openRoomEdit(r: RoomCfg): void {
public _roomSettingsFromDialog(): RoomCfg['settings'] {
const st: any = {};
if (this.host._roomFill) st.fill_mode = this.host._roomFill;
if (this.host._roomCustomFill) st.custom_fill = this.host._roomCustomFill;
if (this.host._roomFill === 'custom' && this.host._roomCustomFill) st.custom_fill = this.host._roomCustomFill; // #581
if (this.host._roomTempSrc) st.temp_source = this.host._roomTempSrc;
if (this.host._roomHumSrc) st.hum_source = this.host._roomHumSrc;
applyRoomTempThresholdDraft(st, this.host._roomTempMin, this.host._roomTempMax);
@@ -11037,15 +11037,15 @@ public _saveRoomEdit(): void {
}
room.name = this.host._nameSel.trim() || room.name;
room.area = this.host._areaSel || null;
// Preserve unknown/future room settings. If this dialog replaces a legacy
// fill_mode:'glow', materialize its effective Glow in the same write so a
// seemingly unrelated room edit cannot switch the light overlay off.
// Preserve unknown/future room settings. A legacy fill_mode:'glow' materializes its effective
// Glow in the same write (an unrelated room edit must not switch the overlay off). #581: the room
// colour is kept only with the room's own custom mode — "as the space" forgets it, orphans included.
const previous = room.settings || {};
const next: any = { ...previous };
if (!applyRoomTempThresholdDraft(next, this.host._roomTempMin, this.host._roomTempMax)) return;
if (this.host._roomFill) next.fill_mode = this.host._roomFill;
else delete next.fill_mode;
if (this.host._roomCustomFill) next.custom_fill = this.host._roomCustomFill;
if (this.host._roomFill === 'custom' && this.host._roomCustomFill) next.custom_fill = this.host._roomCustomFill;
else delete next.custom_fill;
if (previous.fill_mode === 'glow' && typeof previous.glow !== 'boolean') next.glow = true;
if (this.host._roomTempSrc) next.temp_source = this.host._roomTempSrc;
@@ -14034,11 +14034,11 @@ public _renderRoomDialog(): TemplateResult {
${([['', 'fill.inherit'], ...ROOM_FILL_MODES.map((v) => [v, 'fill.' + v])] as const).map(
([v, k]) => html`<label class="srcrow inline">
<input type="radio" name="rfill" .checked=${this.host._roomFill === v}
@change=${() => { this.host._roomFill = v as any; this.host.requestUpdate(); }} />
@change=${() => { this.host._roomFill = v as typeof this.host._roomFill; if (v !== 'custom') this.host._roomCustomFill = null; this.host.requestUpdate(); }} />
<span>${this.host._t(k as any)}</span>
</label>`,
)}
${effectiveFill === 'custom'
${this.host._roomFill === 'custom'
? html`<div class="colorrow gsrow">
<span class="gsl">${this.host._roomCustomFill
? this.host._t('room.custom_fill_own') : this.host._t('room.custom_fill_space')}</span>
+13 -3
View File
@@ -1299,13 +1299,23 @@ export function customFillOf(value: unknown, fallback: FillColorEntry = DEFAULT_
};
}
/** Effective custom fill: explicit room value -> space value -> product default. */
/**
* Effective custom fill: the room's own colour -> space colour -> product default.
*
* The room colour counts only together with the room's OWN `fill_mode: 'custom'`
* (#581). A colour stored without that mode — a room switched back to "as the
* space" by an older editor, or a legacy `glow` token — is an orphan: the
* dialog never named that state, so the plan must not paint it. Reading the
* orphan does not rewrite the config; the next save of that room drops it.
*/
export function roomCustomFillOf(
spaceCustom: unknown,
room: { settings?: { custom_fill?: unknown } | null } | null | undefined,
room: { settings?: { fill_mode?: string | null; custom_fill?: unknown } | null } | null | undefined,
): FillColorEntry {
const spaceFill = customFillOf(spaceCustom);
const own = room?.settings?.custom_fill;
const settings = room?.settings;
if (settings?.fill_mode !== 'custom') return spaceFill;
const own = settings?.custom_fill;
return own && typeof own === 'object' ? customFillOf(own, spaceFill) : spaceFill;
}
+25 -5
View File
@@ -617,16 +617,36 @@ test('custom fill projection is safe and follows room -> space -> default inheri
}
assert.deepEqual(customFillOf({ c: '#abcdef', a: -2 }), { c: '#abcdef', a: 0 });
const space = { c: '#112233', a: 0.25 };
const own = (custom_fill) => ({ settings: { fill_mode: 'custom', custom_fill } });
assert.deepEqual(roomCustomFillOf(space, {}), space);
assert.deepEqual(roomCustomFillOf(space, { settings: { custom_fill: null } }), space);
assert.deepEqual(roomCustomFillOf(space, { settings: { custom_fill: { c: '#445566' } } }),
{ c: '#445566', a: 0.25 });
assert.deepEqual(roomCustomFillOf(space, { settings: { custom_fill: { a: 0.7 } } }),
assert.deepEqual(roomCustomFillOf(space, own(null)), space, 'own mode without a colour paints the space colour');
assert.deepEqual(roomCustomFillOf(space, own({ c: '#445566' })),
{ c: '#445566', a: 0.25 }, 'per-field fallback keeps working under the own mode');
assert.deepEqual(roomCustomFillOf(space, own({ a: 0.7 })),
{ c: '#112233', a: 0.7 });
assert.deepEqual(roomCustomFillOf(space, { settings: { custom_fill: { c: '#445566', a: 0.7 } } }),
assert.deepEqual(roomCustomFillOf(space, own({ c: '#445566', a: 0.7 })),
{ c: '#445566', a: 0.7 });
});
// #581 AC1: the room colour counts only with the room's OWN `custom` mode. A
// colour stored without it — the state "as the space" used to leave behind —
// is an orphan and paints the space colour; reading it changes nothing.
test('#581 AC1: an orphan room colour (no own custom mode) yields the space colour', () => {
const space = { c: '#112233', a: 0.25 };
const orphan = { c: '#445566', a: 0.7 };
assert.deepEqual(roomCustomFillOf(space, { settings: { custom_fill: orphan } }), space,
'Cabinet on the dacha: colour without fill_mode');
for (const mode of ['lqi', 'light', 'temp', 'none', 'glow', '', null, undefined]) {
assert.deepEqual(roomCustomFillOf(space, { settings: { fill_mode: mode, custom_fill: orphan } }), space,
`mode ${String(mode)} does not carry the room colour`);
}
assert.deepEqual(roomCustomFillOf(space, { settings: { fill_mode: 'custom', custom_fill: orphan } }), orphan,
'the own custom mode is the only carrier');
const stored = { settings: { custom_fill: orphan, name_scale: 1.35 } };
roomCustomFillOf(space, stored);
assert.deepEqual(stored, { settings: { custom_fill: orphan, name_scale: 1.35 } }, 'reading never rewrites');
});
test('room temperature range inherits per side and normalises the effective pair (#487)', () => {
assert.deepEqual(roomTempRangeOf(20, 25, null), { min: 20, max: 25 });
assert.deepEqual(roomTempRangeOf(20, 25, { settings: { temp_min: 19, temp_max: 24 } }),