mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 13:18:58 +00:00
feat: режим «Значение + статичный значок» (#588)
Пятый вариант «Отображение»: маркер показывает выбранное значение и при этом
никогда не меняет цвет — ни по состоянию, ни по тревоге, ни по недоступности,
ни под цвет RGB-света, ни по активности.
Четыре прежних режима задавали одним выбором две независимые вещи: что
нарисовано внутри маркера и красится ли он состоянием. Поэтому вместо
сравнения с одним токеном появились два производных предиката рядом с
`normalizeDeviceDisplay` — `displayWantsValue` и `displayIsNeutral`, — и их
спрашивают политика, слой пульсации, внешний бейдж, редактор и три ветки
живого слоя пылесоса.
Две ловушки, из-за которых режим не сводится к одной строке в словаре:
- быстрый путь «статичному маркеру источники не нужны» (`sourceDetails: false`)
— это основной путь рендера плана, карточки пространства и PDF. Он оставлен
только режиму без значения: иначе число пропало бы именно на плане и
осталось в предпросмотре редактора;
- три ветки живого пылесоса сравнивают режим строкой и не читают политику,
поэтому зелёная политика их не гарантирует. Свидетель держит две
конфигурации карты: сопоставленная доказывает puck и след, несопоставленная
— бейдж маршрута (при совпадающей калибровке маршрут `ready`, и бейджа не
было бы ни в одном режиме). Бейдж проверяется при наполненном буфере
позиций, иначе его отсутствие объяснялось бы первой веткой.
Потолок initial View перецентрирован 291_700 → 292_400 без изменения общего
бюджета: измеренный факт 291 346 Б оставлял под прежним центром 354 Б —
внутри шумовой полосы метрики.
Эталон: в `device-icon-state-table-{light,dark}` включённая RGB-лампа
переведена в новый режим. Кадры обязаны разойтись; приёмка — отдельным
коммитом класса D с полного линуксового артефакта Validate.
Issue: #588
User-Visible: yes
This commit is contained in:
@@ -1,114 +1,114 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e",
|
||||
"fingerprint": "34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e",
|
||||
"entry": "houseplan-card.js",
|
||||
"panelEntry": "houseplan-panel.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 291156,
|
||||
"initialViewGzipBytes": 291346,
|
||||
"initialPanelFiles": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelGzipBytes": 292678,
|
||||
"initialPanelGzipBytes": 292868,
|
||||
"initialPanelOnlyFiles": [
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelOnlyGzipBytes": 2325,
|
||||
"initialPanelOnlyGzipBytes": 2323,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/de-BIKFm0nu.js",
|
||||
"houseplan-assets/editor-Btih960P.js",
|
||||
"houseplan-assets/fr-Dj-Pk_jJ.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DryrwG8G.js",
|
||||
"houseplan-assets/iso-scene-render-CALxqRmU.js",
|
||||
"houseplan-assets/live-interaction-runtime-C_kbCgWS.js",
|
||||
"houseplan-assets/pdf-export-BSpK2TsR.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-j85exgxX.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/de-JnsB57l8.js",
|
||||
"houseplan-assets/editor-CAGtS_SG.js",
|
||||
"houseplan-assets/fr-BAV7hmLH.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-Dn1oMTHf.js",
|
||||
"houseplan-assets/iso-scene-render-CCE-TeKM.js",
|
||||
"houseplan-assets/live-interaction-runtime-Ba7Bs1o7.js",
|
||||
"houseplan-assets/pdf-export-BbdvUxVO.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js"
|
||||
],
|
||||
"lazyGzipBytes": 433641,
|
||||
"lazyGzipBytes": 433790,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/editor-Btih960P.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/editor-CAGtS_SG.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 218068,
|
||||
"lazyEditorGzipBytes": 218093,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 13918,
|
||||
"lazyOnboardingGzipBytes": 13912,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-BIKFm0nu.js",
|
||||
"houseplan-assets/fr-Dj-Pk_jJ.js"
|
||||
"houseplan-assets/de-JnsB57l8.js",
|
||||
"houseplan-assets/fr-BAV7hmLH.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 54913,
|
||||
"lazyLocaleGzipBytes": 55046,
|
||||
"lazyIsometricFiles": [
|
||||
"houseplan-assets/iso-scene-render-CALxqRmU.js"
|
||||
"houseplan-assets/iso-scene-render-CCE-TeKM.js"
|
||||
],
|
||||
"lazyIsometricGzipBytes": 15015,
|
||||
"lazyIsometricGzipBytes": 15011,
|
||||
"lazyFurnitureArtFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js"
|
||||
],
|
||||
"lazyFurnitureArtGzipBytes": 23353,
|
||||
"lazyPdfFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/pdf-export-BSpK2TsR.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/pdf-export-BbdvUxVO.js"
|
||||
],
|
||||
"lazyPdfGzipBytes": 133225,
|
||||
"lazyPdfGzipBytes": 133223,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"sha256": "aa81af28bf4f25fe2b3765b741a6b6981c6c8e5b8acf20eb2692961ca9e4c0d9",
|
||||
"path": "houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"sha256": "7c243ec99dc24ad4ef2151c37bc44635af0f6c06bbd8268b1641ab7db50b1228",
|
||||
"rawBytes": 19956,
|
||||
"gzipBytes": 6883,
|
||||
"gzipBytes": 6880,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/de-BIKFm0nu.js",
|
||||
"sha256": "0ae2f5d0afbf485c035cbde94094ce6d99d7fdd9870437af1d90c39fff776170",
|
||||
"rawBytes": 91590,
|
||||
"gzipBytes": 27739,
|
||||
"path": "houseplan-assets/de-JnsB57l8.js",
|
||||
"sha256": "91f902f727bec6af4a0b8fac380f904eb9f5094f721c0fb402035e395da463da",
|
||||
"rawBytes": 91971,
|
||||
"gzipBytes": 27815,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-Btih960P.js",
|
||||
"sha256": "692281371a9603f7647c653c320edaafa9f1f446c6eacff7495dfad8dd36bf51",
|
||||
"path": "houseplan-assets/editor-CAGtS_SG.js",
|
||||
"sha256": "4166246b90475646d572f6d3626f60508c03c6b5db6fd0a89a204634f6129491",
|
||||
"rawBytes": 3831,
|
||||
"gzipBytes": 1582,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/fr-Dj-Pk_jJ.js",
|
||||
"sha256": "3bdaf2b6bb4eb18b7b6afaf65a238e7c31c6ffba540b11a7d13f89a272a282e5",
|
||||
"rawBytes": 93884,
|
||||
"gzipBytes": 27174,
|
||||
"path": "houseplan-assets/fr-BAV7hmLH.js",
|
||||
"sha256": "2360732a1b796fd6f888de25d792f30bfc963d57b28f49cb03b0734647f0e1b9",
|
||||
"rawBytes": 94305,
|
||||
"gzipBytes": 27231,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"sha256": "a0e124957c5d81e089bbdacf7619965157b1c28860a5cf6bedc1918d43982825",
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"sha256": "c61b80a3df3afdaa1a1afc1f5329bfec6c34ce964a2d0bc50dd631a1633d6a23",
|
||||
"rawBytes": 90517,
|
||||
"gzipBytes": 23353,
|
||||
"isEntry": false,
|
||||
@@ -116,180 +116,180 @@
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"sha256": "c5ad243829110642e76f2b9caeff58e354d9bdd2484d6ba3aeda0a66354fb4b7",
|
||||
"rawBytes": 1034131,
|
||||
"gzipBytes": 290353,
|
||||
"path": "houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"sha256": "cb5f49f3de3f06b8c88b247f4aa601124f78a48369a7836e229bbc851df458ae",
|
||||
"rawBytes": 1035084,
|
||||
"gzipBytes": 290545,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-BIKFm0nu.js",
|
||||
"houseplan-assets/editor-Btih960P.js",
|
||||
"houseplan-assets/fr-Dj-Pk_jJ.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DryrwG8G.js",
|
||||
"houseplan-assets/iso-scene-render-CALxqRmU.js",
|
||||
"houseplan-assets/live-interaction-runtime-C_kbCgWS.js",
|
||||
"houseplan-assets/pdf-export-BSpK2TsR.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js"
|
||||
"houseplan-assets/de-JnsB57l8.js",
|
||||
"houseplan-assets/editor-CAGtS_SG.js",
|
||||
"houseplan-assets/fr-BAV7hmLH.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-Dn1oMTHf.js",
|
||||
"houseplan-assets/iso-scene-render-CCE-TeKM.js",
|
||||
"houseplan-assets/live-interaction-runtime-Ba7Bs1o7.js",
|
||||
"houseplan-assets/pdf-export-BbdvUxVO.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"sha256": "393529f4d7b2bda81deb3cb478e6c8d7e5a940316e71ed8acd32e58ab13328d3",
|
||||
"rawBytes": 670161,
|
||||
"gzipBytes": 178821,
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"sha256": "dd6b9eb38ff60c997cd860c27d377c7a1880f10116832188fc6d9b6912ac2056",
|
||||
"rawBytes": 670235,
|
||||
"gzipBytes": 178853,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/zigbee-topology-runtime-j85exgxX.js"
|
||||
"houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js",
|
||||
"sha256": "b301cf1f4a91250ca6163fb05c5421d1613dc65557ec6510d61ccc85ab258cc4",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js",
|
||||
"sha256": "40c1aa5283e0ddaad87a412ce7ada42c2a7ef2b72d0b69611fa17ac8c1cfca84",
|
||||
"rawBytes": 28287,
|
||||
"gzipBytes": 7035,
|
||||
"gzipBytes": 7032,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-DryrwG8G.js",
|
||||
"sha256": "b9891e1d96dec35723cbf35955a5e75fa19292fd8398097aa46020595899e6e1",
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-Dn1oMTHf.js",
|
||||
"sha256": "19cec6f0e9a185acbfaacc56cff85cfe52974b8d152e59009d22aab0096b9bab",
|
||||
"rawBytes": 10756,
|
||||
"gzipBytes": 3683,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-j85exgxX.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/iso-scene-render-CALxqRmU.js",
|
||||
"sha256": "f4f66222fd36572fc34c694474daf248c040c5fb635160301bb4dd39ceaec446",
|
||||
"path": "houseplan-assets/iso-scene-render-CCE-TeKM.js",
|
||||
"sha256": "d3ffcf53f84db6a8c510a5001af1b2b53aa9b18781c09c56ebf4bd25d6db442c",
|
||||
"rawBytes": 44439,
|
||||
"gzipBytes": 15015,
|
||||
"gzipBytes": 15011,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/live-interaction-runtime-C_kbCgWS.js",
|
||||
"sha256": "7a6aacdb402472213bd6997f0bbb3fdbde4e25f8c9f0881e2bb0907fa2cd8108",
|
||||
"path": "houseplan-assets/live-interaction-runtime-Ba7Bs1o7.js",
|
||||
"sha256": "ec5125376ea07c2226e195f680bb96282fc0663254ab1f8b8cf192872243e4e5",
|
||||
"rawBytes": 8216,
|
||||
"gzipBytes": 3205,
|
||||
"gzipBytes": 3208,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/pdf-export-BSpK2TsR.js",
|
||||
"sha256": "7e4b945d5a45e438dd7ef484651f182c09f6d3e40a3db72c52bd853bd8ba445e",
|
||||
"path": "houseplan-assets/pdf-export-BbdvUxVO.js",
|
||||
"sha256": "c72e1b5276c2b14a7af50aa5a61e9f10ddeb42b63e9e6e8b16883193edb7e43c",
|
||||
"rawBytes": 236244,
|
||||
"gzipBytes": 109872,
|
||||
"gzipBytes": 109870,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-editor-QJQxYKiA.js",
|
||||
"sha256": "97cecc2b4589331ca3b691705fcc637beff63fb02b152af66987803546a2d2d6",
|
||||
"path": "houseplan-assets/summary-panel-editor-AdqXisOL.js",
|
||||
"sha256": "b2b06fd3d05fb9f0e0b968b2db06382a70138d2cc15eab633683ced152087581",
|
||||
"rawBytes": 14824,
|
||||
"gzipBytes": 3395,
|
||||
"gzipBytes": 3400,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-metrics-CuoIyiO4.js",
|
||||
"sha256": "ed3919d2ce63a029335b4a079821139cd365a860c0fb8e09c05eb9cc0e344b16",
|
||||
"path": "houseplan-assets/summary-panel-metrics-Opp08Jxc.js",
|
||||
"sha256": "85a3a361187bee8843f3cb0831e361d381ed15846120c5680d5bda85e6858137",
|
||||
"rawBytes": 2610,
|
||||
"gzipBytes": 1304,
|
||||
"gzipBytes": 1305,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js",
|
||||
"sha256": "e5a6a1430e12b6f2f533d446a7fc33a0f5b2d78d7760f3fa06c5543cf12f2f7c",
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js",
|
||||
"sha256": "3e8a8b3c6632befadb081043fc6afa6685effc4672d05a3747029642f60c6052",
|
||||
"rawBytes": 73366,
|
||||
"gzipBytes": 20089,
|
||||
"gzipBytes": 20085,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/summary-panel-editor-QJQxYKiA.js",
|
||||
"houseplan-assets/summary-panel-metrics-CuoIyiO4.js"
|
||||
"houseplan-assets/summary-panel-editor-AdqXisOL.js",
|
||||
"houseplan-assets/summary-panel-metrics-Opp08Jxc.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-De4vOxpt.js",
|
||||
"sha256": "cb567b3217e3e45bf943aea822e0b97310a60b96c48f3bedb9816c5a07d6a82a",
|
||||
"path": "houseplan-assets/zigbee-topology-CW_HKDoT.js",
|
||||
"sha256": "0bd4758722344d63391a9f4c45e591a260fc6acf1ab4e8511479edef2c08fce1",
|
||||
"rawBytes": 18961,
|
||||
"gzipBytes": 7429,
|
||||
"gzipBytes": 7425,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-j85exgxX.js",
|
||||
"sha256": "2874c621bd38d7f01054228604b6fded2f6047af80aa01b01efee95378592452",
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js",
|
||||
"sha256": "c1eadf604fa8e2f3129c48da7a37727e8c0a0408c81182403cbcb3aed518f58a",
|
||||
"rawBytes": 3941,
|
||||
"gzipBytes": 1761,
|
||||
"gzipBytes": 1762,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "3b101e0a9f209b044bc177318f28432cb95b9b85fb4f0803772cf018b8f90996",
|
||||
"sha256": "8f5fbf9f62363032c898284d72e25336c59aaf2c9de126d22e3ce6d43c293103",
|
||||
"rawBytes": 1205,
|
||||
"gzipBytes": 803,
|
||||
"gzipBytes": 801,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-panel.js",
|
||||
"sha256": "22cc3ae9e23a17c016bc57a60f9cc0ab3bd23a2f84286775357ce10285737d86",
|
||||
"sha256": "a07ab338c2158001a3b8087118b6ee72dfc91ce60cde2b95e56eea2fefff1a6a",
|
||||
"rawBytes": 6165,
|
||||
"gzipBytes": 2325,
|
||||
"gzipBytes": 2323,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
+1
-1
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
+5
-5
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{q as e,l as o,v as t,e5 as s,A as a,e6 as i,e7 as l,e8 as n,E as r,c}from"./houseplan-card-CwdwKGP9.js";class f 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,f={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")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{q as e,l as o,v as t,e7 as s,A as a,e8 as i,e9 as l,ea as n,E as r,c}from"./houseplan-card-BKJrmMEj.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=>f[e.name]||e.name}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(h.slice(0,3))}
|
||||
${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:_})}
|
||||
</div>`:a}
|
||||
${d(h.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)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
|
||||
${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
+1
-1
File diff suppressed because one or more lines are too long
+90
-90
File diff suppressed because one or more lines are too long
+76
-76
File diff suppressed because one or more lines are too long
+28
-28
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{c as a,A as e}from"./houseplan-card-CwdwKGP9.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-Crai8dhf.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__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{c as a,A as e}from"./houseplan-card-BKJrmMEj.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-B6QGWMyY.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
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{eb 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 d,ec as u,ed as f,bu as m,ee as g}from"./houseplan-card-CwdwKGP9.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=d(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const 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,d="device"===o?a?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.area_id,u="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||d||"",f=u&&e.areaToSpace[u]||i.space||e.firstSpaceId;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===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*h(c,l,d=_){try{let u=0;for(const f of l){const l=c.spaces.find(e=>String(e?.id)===f.id);if(!l)continue;const m=e(l,f),g=d(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const c=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,r,o,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(c,m.physicalBodies)),yield}const _=m.cellCm/r;u+=a(v)*_*_/1e4}return u}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=u(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__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{ed 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 l,a3 as c,aA as d,ee as u,ef as f,bu as m,eg as g}from"./houseplan-card-BKJrmMEj.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=d(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const 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,l="entity"===o?e.registry.entities?.[a]:null,c="device"===o?a:l?.device_id,d="device"===o?s?.area_id:l?.area_id||c&&e.registry.devices?.[c]?.area_id,u="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||d||"",f=u&&e.areaToSpace[u]||i.space||e.firstSpaceId;c&&e.registry.devices?.[c]&&e.spaceIds.has(f)&&(!n.devices.has(c)||"entity"===o&&n.liveEntities.has(a))&&t.add(c)}return t}function y(e,t){return e?.length?t?.length?g(e,t):e:t}function _(e,t){const n=l(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,r,o);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:c(e.rooms,t.walls,t.openCuts,i,t.cellCm,r,o)}}function*h(l,c,d=_){try{let u=0;for(const f of c){const c=l.spaces.find(e=>String(e?.id)===f.id);if(!c)continue;const m=e(c,f),g=d(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const s=t(e);if(!s)continue;const l=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,r,o,g.roomGeom,g.multiWallNodes)||s;v=y(v,a(l,m.physicalBodies)),yield}const _=m.cellCm/r;u+=s(v)*_*_/1e4}return u}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=u(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};
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{n as t,b as n}from"./zigbee-topology-De4vOxpt.js";import{F as e}from"./houseplan-card-CwdwKGP9.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};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{n as t,b as n}from"./zigbee-topology-CW_HKDoT.js";import{F as e}from"./houseplan-card-BKJrmMEj.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__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";try{await import("./houseplan-assets/houseplan-card-CwdwKGP9.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__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";try{await import("./houseplan-assets/houseplan-card-BKJrmMEj.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
@@ -1883,7 +1883,7 @@ MARKER_SCHEMA = vol.Schema(
|
||||
vol.Optional("room_id"): vol.Any(str, None),
|
||||
# Keep in sync with DISPLAY_MODES in src/logic.ts. `ripple` is no longer
|
||||
# offered, but remains accepted while old stores migrate to icon_ripple.
|
||||
vol.Optional("display"): vol.Any("badge", "ripple", "icon_ripple", "value", "static_icon", None),
|
||||
vol.Optional("display"): vol.Any("badge", "ripple", "icon_ripple", "value", "static_icon", "value_static_icon", None),
|
||||
vol.Optional("ripple_color"): vol.Any(None, _COLOR),
|
||||
vol.Optional("ripple_size"): vol.Any(vol.All(vol.Coerce(float), vol.Range(min=1, max=20)), None),
|
||||
vol.Optional("size"): vol.Any(vol.All(vol.Coerce(float), vol.Range(min=0.2, max=6)), None),
|
||||
|
||||
@@ -754,7 +754,11 @@ export const GOLDEN_SCENARIOS = Object.freeze([
|
||||
id: `device-icon-state-table-${theme}`, fixture: 'visual', space: 'golden-lighting', mode: 'view',
|
||||
glowEnabled: false, sunRays: false,
|
||||
markerOverrides: [
|
||||
{ id: 'golden-light-one', binding: 'device:golden-light-one' },
|
||||
// #588: пятый режим показан там же, где живут остальные четыре. Источник
|
||||
// выбран цветной (включённая RGB-лампа) намеренно: на нейтральном кадр
|
||||
// совпал бы с прежним, и эталон не доказывал бы ничего.
|
||||
{ id: 'golden-light-one', binding: 'device:golden-light-one',
|
||||
display: 'value_static_icon' },
|
||||
{ id: 'golden-light-two', binding: 'device:golden-light-two' },
|
||||
{ id: 'golden-light-three', binding: 'device:golden-light-three' },
|
||||
{ id: 'golden-presence', binding: 'device:golden-presence' },
|
||||
|
||||
@@ -172,6 +172,150 @@ const out = await page.evaluate(async () => {
|
||||
}
|
||||
await staticCard.updateComplete;
|
||||
const staticCardFace = domFace(staticCard.renderRoot?.querySelector(`.dev[data-id="${markerId}"]`));
|
||||
// ------------------ #588: значение при неизменном цвете ------------------
|
||||
// AC6 (К2а). Три ветки живого пылесоса сравнивают режим строкой и общей
|
||||
// политики не читают, поэтому зелёная политика их не гарантирует. Тот же
|
||||
// маркер, который выше доказал подавление для «Всегда статичный значок»,
|
||||
// проходит их и для нового режима — и каждый раз возвращается в `badge`,
|
||||
// иначе отсутствие puck доказывало бы лишь сломанную фикстуру.
|
||||
const vacPuck = () => root().querySelector(`.vacpuck[data-mid="${markerId}"]`);
|
||||
const vacTrail = () => root().querySelector('.vactrail');
|
||||
const vacWarn = () => root().querySelector('.vacwarn');
|
||||
const setVacDisplay = async (mode) => {
|
||||
savedMarker.display = mode;
|
||||
card._cfgEpoch++;
|
||||
card.hass = { ...card.hass };
|
||||
await update();
|
||||
};
|
||||
const setMapName = async (name) => {
|
||||
const camera = card.hass.states['camera.static_robot_map'];
|
||||
card.hass = {
|
||||
...card.hass,
|
||||
states: {
|
||||
...card.hass.states,
|
||||
'camera.static_robot_map': {
|
||||
...camera, attributes: { ...camera.attributes, map_name: name },
|
||||
},
|
||||
},
|
||||
};
|
||||
await update();
|
||||
};
|
||||
// Сопоставленная карта (калибровка знает `m1`): puck и след существуют.
|
||||
await setVacDisplay('badge');
|
||||
const mappedBadgeShowsLive = !!vacPuck() && !!vacTrail();
|
||||
await setVacDisplay('value_static_icon');
|
||||
const valueStaticHidesLive = !vacPuck() && !vacTrail();
|
||||
const valueStaticKeepsBufferEmpty = !card._vacRt.get(markerId);
|
||||
// Несопоставленная карта (`m9` нет в калибровке): только здесь появляется
|
||||
// бейдж маршрута. При совпадающей калибровке маршрут `ready`, предупреждения
|
||||
// нет ни в одном режиме — и третий факт был бы тавтологией.
|
||||
await setVacDisplay('badge');
|
||||
await setMapName('m9');
|
||||
const unmappedBadgeWarns = !!vacWarn();
|
||||
// Режим меняется БЕЗ нового снимка hass: `_vacTick` живёт на приёме hass,
|
||||
// поэтому буфер позиций остаётся наполненным, а `moving` — истинным. Это
|
||||
// важно: иначе исчезнувший бейдж объяснялся бы пустым буфером (его чистит
|
||||
// первая ветка), и дефектная третья ветка прошла бы свидетеля зелёной.
|
||||
savedMarker.display = 'value_static_icon';
|
||||
card._cfgEpoch++;
|
||||
card._regSignature = '';
|
||||
card._maybeRebuildDevices();
|
||||
await update();
|
||||
const valueStaticHidesRouteWarn = !vacWarn();
|
||||
const routeBufferStillMoving = card._vacRt.get(markerId)?.moving === true;
|
||||
await setMapName('m1');
|
||||
await setVacDisplay('static_icon');
|
||||
|
||||
// AC5/AC7. Маркер со значением: число видно на плане, а цвет не отзывается
|
||||
// ни на включённую лампу, ни на недоступность источника. Источник — лампа, а
|
||||
// не дверь: у крышки состояние живёт в значке, подложка нейтральна в обоих
|
||||
// режимах, и контраст ниже ничего бы не доказал.
|
||||
const valueId = 'value-static-light';
|
||||
card._serverCfg.markers.push({
|
||||
id: valueId, binding: 'device:d_light1', space: 'f1', area: 'living_room',
|
||||
display: 'value_static_icon', icon: 'mdi:ceiling-light', size: 1.4,
|
||||
value_source: {
|
||||
kind: 'entity_attribute', entity_id: 'light.ceiling', attribute: 'brightness',
|
||||
},
|
||||
});
|
||||
card._layout[valueId] = { s: 'f1', x: 0.62, y: 0.3 };
|
||||
const alarmId = 'value-static-leak';
|
||||
card._serverCfg.markers.push({
|
||||
id: alarmId, binding: 'device:d_leak', space: 'f1', area: 'living_room',
|
||||
display: 'value_static_icon', icon: 'mdi:water',
|
||||
});
|
||||
card._layout[alarmId] = { s: 'f1', x: 0.72, y: 0.5 };
|
||||
const setLight = async (state, attributes) => {
|
||||
card.hass = {
|
||||
...card.hass,
|
||||
states: {
|
||||
...card.hass.states,
|
||||
'light.ceiling': {
|
||||
entity_id: 'light.ceiling', state,
|
||||
attributes: { friendly_name: 'Ceiling light', ...attributes },
|
||||
},
|
||||
},
|
||||
};
|
||||
await update();
|
||||
};
|
||||
const setValueDisplay = async (mode) => {
|
||||
card._serverCfg.markers.find((marker) => marker.id === valueId).display = mode;
|
||||
card._cfgEpoch++;
|
||||
card.hass = { ...card.hass };
|
||||
await update();
|
||||
};
|
||||
const valueNode = () => root().querySelector(`.dev[data-id="${valueId}"]`);
|
||||
const valueText = () => valueNode()?.querySelector('.valtext')?.textContent?.trim() || '';
|
||||
// Собственные классы режима не считаются состоянием: `static-icon` —
|
||||
// стилевой хук нейтральной подложки, `valonly` — форма лица значения.
|
||||
const stateClasses = (node) => [...(node?.classList || [])].filter((name) => [
|
||||
'on', 'open', 'alarm', 'unavail',
|
||||
'activity-running', 'activity-event', 'activity-presence', 'activity-transition',
|
||||
].includes(name));
|
||||
card._regSignature = '';
|
||||
card._cfgEpoch++;
|
||||
await setLight('on', { brightness: 128, rgb_color: [255, 196, 112] });
|
||||
const valueOnText = valueText();
|
||||
const valueOnClasses = stateClasses(valueNode());
|
||||
const valueOnNoPulse = !valueNode()?.querySelector('.pulse,.ripple')
|
||||
&& !valueNode()?.querySelector('.lqi');
|
||||
await setLight('off', { brightness: 0 });
|
||||
const valueOffText = valueText();
|
||||
const valueOffClasses = stateClasses(valueNode());
|
||||
await setLight('unavailable', {});
|
||||
const valueUnavailableClasses = stateClasses(valueNode());
|
||||
// Контраст: тот же маркер и тот же источник в «Значение + состояние» красится
|
||||
// включённой лампой — нейтральность выше принадлежит режиму, а не фикстуре.
|
||||
await setLight('on', { brightness: 128, rgb_color: [255, 196, 112] });
|
||||
await setValueDisplay('value');
|
||||
const dynamicText = valueText();
|
||||
const dynamicClasses = stateClasses(valueNode());
|
||||
await setValueDisplay('value_static_icon');
|
||||
|
||||
// AC7. Редактор: новая опция последняя, поле источника значения показано,
|
||||
// предупреждение о тревоге показано, секция внешнего бейджа выключена.
|
||||
card._setMode('devices');
|
||||
const valueDevice = card._devices.find((item) => item.id === valueId);
|
||||
card._openMarkerDialog(valueDevice);
|
||||
await update();
|
||||
const displayOptions = [...root().querySelectorAll('#marker-display option')]
|
||||
.map((option) => option.value);
|
||||
const editorShowsValueSource = !!root().querySelector('#marker-value-source');
|
||||
const badgeToggle = root().querySelector('.markerbadgegroup input[type="checkbox"]');
|
||||
const editorDisablesBadge = !!badgeToggle?.disabled
|
||||
&& !!root().querySelector('.markerbadgegroup .markerlightdisabled');
|
||||
card._markerDialog = null;
|
||||
await update();
|
||||
const alarmDevice = card._devices.find((item) => item.id === alarmId);
|
||||
card._openMarkerDialog(alarmDevice);
|
||||
await update();
|
||||
const editorWarnsAboutAlarm = !!root().querySelector('.habindingbanner');
|
||||
card._markerDialog = { ...card._markerDialog, display: 'badge' };
|
||||
await update();
|
||||
const badgeHidesAlarmWarning = !root().querySelector('.habindingbanner');
|
||||
card._markerDialog = null;
|
||||
await update();
|
||||
|
||||
return {
|
||||
staticAlarmNeutral: smokeOn.status === 'neutral'
|
||||
&& smokeOn.activity === 'none'
|
||||
@@ -193,6 +337,27 @@ const out = await page.evaluate(async () => {
|
||||
dynamicVacuumOverlayRestored,
|
||||
returnCreatesNoActivity,
|
||||
returnHidesVacuumOverlay,
|
||||
// #588 AC6
|
||||
mappedBadgeShowsLive,
|
||||
valueStaticHidesLive,
|
||||
valueStaticKeepsBufferEmpty,
|
||||
valueStaticHidesRouteWarn,
|
||||
routeBufferStillMoving,
|
||||
unmappedBadgeWarns,
|
||||
// #588 AC5
|
||||
valueStaticShowsValue: valueOnText === '50 %',
|
||||
valueStaticNeverColoured: valueOnClasses.length === 0
|
||||
&& valueOffClasses.length === 0 && valueUnavailableClasses.length === 0,
|
||||
valueStaticFollowsSource: valueOffText === '0 %',
|
||||
valueStaticHasNoPulseOrLqi: valueOnNoPulse,
|
||||
dynamicValueColoured: dynamicText === '50 %' && dynamicClasses.includes('on'),
|
||||
// #588 AC7
|
||||
editorListsNewModeLast: displayOptions.join(',')
|
||||
=== 'badge,icon_ripple,value,static_icon,value_static_icon',
|
||||
editorShowsValueSource,
|
||||
editorDisablesBadge,
|
||||
editorWarnsAboutAlarm,
|
||||
badgeHidesAlarmWarning,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Vendored
+133
-133
@@ -1,114 +1,114 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e",
|
||||
"fingerprint": "34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e",
|
||||
"entry": "houseplan-card.js",
|
||||
"panelEntry": "houseplan-panel.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 291156,
|
||||
"initialViewGzipBytes": 291346,
|
||||
"initialPanelFiles": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelGzipBytes": 292678,
|
||||
"initialPanelGzipBytes": 292868,
|
||||
"initialPanelOnlyFiles": [
|
||||
"houseplan-panel.js"
|
||||
],
|
||||
"initialPanelOnlyGzipBytes": 2325,
|
||||
"initialPanelOnlyGzipBytes": 2323,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/de-BIKFm0nu.js",
|
||||
"houseplan-assets/editor-Btih960P.js",
|
||||
"houseplan-assets/fr-Dj-Pk_jJ.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DryrwG8G.js",
|
||||
"houseplan-assets/iso-scene-render-CALxqRmU.js",
|
||||
"houseplan-assets/live-interaction-runtime-C_kbCgWS.js",
|
||||
"houseplan-assets/pdf-export-BSpK2TsR.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-j85exgxX.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/de-JnsB57l8.js",
|
||||
"houseplan-assets/editor-CAGtS_SG.js",
|
||||
"houseplan-assets/fr-BAV7hmLH.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-Dn1oMTHf.js",
|
||||
"houseplan-assets/iso-scene-render-CCE-TeKM.js",
|
||||
"houseplan-assets/live-interaction-runtime-Ba7Bs1o7.js",
|
||||
"houseplan-assets/pdf-export-BbdvUxVO.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js"
|
||||
],
|
||||
"lazyGzipBytes": 433641,
|
||||
"lazyGzipBytes": 433790,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/editor-Btih960P.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/editor-CAGtS_SG.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 218068,
|
||||
"lazyEditorGzipBytes": 218093,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 13918,
|
||||
"lazyOnboardingGzipBytes": 13912,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-BIKFm0nu.js",
|
||||
"houseplan-assets/fr-Dj-Pk_jJ.js"
|
||||
"houseplan-assets/de-JnsB57l8.js",
|
||||
"houseplan-assets/fr-BAV7hmLH.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 54913,
|
||||
"lazyLocaleGzipBytes": 55046,
|
||||
"lazyIsometricFiles": [
|
||||
"houseplan-assets/iso-scene-render-CALxqRmU.js"
|
||||
"houseplan-assets/iso-scene-render-CCE-TeKM.js"
|
||||
],
|
||||
"lazyIsometricGzipBytes": 15015,
|
||||
"lazyIsometricGzipBytes": 15011,
|
||||
"lazyFurnitureArtFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js"
|
||||
],
|
||||
"lazyFurnitureArtGzipBytes": 23353,
|
||||
"lazyPdfFiles": [
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/pdf-export-BSpK2TsR.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/pdf-export-BbdvUxVO.js"
|
||||
],
|
||||
"lazyPdfGzipBytes": 133225,
|
||||
"lazyPdfGzipBytes": 133223,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"sha256": "aa81af28bf4f25fe2b3765b741a6b6981c6c8e5b8acf20eb2692961ca9e4c0d9",
|
||||
"path": "houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"sha256": "7c243ec99dc24ad4ef2151c37bc44635af0f6c06bbd8268b1641ab7db50b1228",
|
||||
"rawBytes": 19956,
|
||||
"gzipBytes": 6883,
|
||||
"gzipBytes": 6880,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/de-BIKFm0nu.js",
|
||||
"sha256": "0ae2f5d0afbf485c035cbde94094ce6d99d7fdd9870437af1d90c39fff776170",
|
||||
"rawBytes": 91590,
|
||||
"gzipBytes": 27739,
|
||||
"path": "houseplan-assets/de-JnsB57l8.js",
|
||||
"sha256": "91f902f727bec6af4a0b8fac380f904eb9f5094f721c0fb402035e395da463da",
|
||||
"rawBytes": 91971,
|
||||
"gzipBytes": 27815,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-Btih960P.js",
|
||||
"sha256": "692281371a9603f7647c653c320edaafa9f1f446c6eacff7495dfad8dd36bf51",
|
||||
"path": "houseplan-assets/editor-CAGtS_SG.js",
|
||||
"sha256": "4166246b90475646d572f6d3626f60508c03c6b5db6fd0a89a204634f6129491",
|
||||
"rawBytes": 3831,
|
||||
"gzipBytes": 1582,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/fr-Dj-Pk_jJ.js",
|
||||
"sha256": "3bdaf2b6bb4eb18b7b6afaf65a238e7c31c6ffba540b11a7d13f89a272a282e5",
|
||||
"rawBytes": 93884,
|
||||
"gzipBytes": 27174,
|
||||
"path": "houseplan-assets/fr-BAV7hmLH.js",
|
||||
"sha256": "2360732a1b796fd6f888de25d792f30bfc963d57b28f49cb03b0734647f0e1b9",
|
||||
"rawBytes": 94305,
|
||||
"gzipBytes": 27231,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"sha256": "a0e124957c5d81e089bbdacf7619965157b1c28860a5cf6bedc1918d43982825",
|
||||
"path": "houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"sha256": "c61b80a3df3afdaa1a1afc1f5329bfec6c34ce964a2d0bc50dd631a1633d6a23",
|
||||
"rawBytes": 90517,
|
||||
"gzipBytes": 23353,
|
||||
"isEntry": false,
|
||||
@@ -116,180 +116,180 @@
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"sha256": "c5ad243829110642e76f2b9caeff58e354d9bdd2484d6ba3aeda0a66354fb4b7",
|
||||
"rawBytes": 1034131,
|
||||
"gzipBytes": 290353,
|
||||
"path": "houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"sha256": "cb5f49f3de3f06b8c88b247f4aa601124f78a48369a7836e229bbc851df458ae",
|
||||
"rawBytes": 1035084,
|
||||
"gzipBytes": 290545,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-BIKFm0nu.js",
|
||||
"houseplan-assets/editor-Btih960P.js",
|
||||
"houseplan-assets/fr-Dj-Pk_jJ.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-DryrwG8G.js",
|
||||
"houseplan-assets/iso-scene-render-CALxqRmU.js",
|
||||
"houseplan-assets/live-interaction-runtime-C_kbCgWS.js",
|
||||
"houseplan-assets/pdf-export-BSpK2TsR.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js"
|
||||
"houseplan-assets/de-JnsB57l8.js",
|
||||
"houseplan-assets/editor-CAGtS_SG.js",
|
||||
"houseplan-assets/fr-BAV7hmLH.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js",
|
||||
"houseplan-assets/hp-zigbee-topology-overlay-Dn1oMTHf.js",
|
||||
"houseplan-assets/iso-scene-render-CCE-TeKM.js",
|
||||
"houseplan-assets/live-interaction-runtime-Ba7Bs1o7.js",
|
||||
"houseplan-assets/pdf-export-BbdvUxVO.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-CSKyBQUJ.js",
|
||||
"sha256": "393529f4d7b2bda81deb3cb478e6c8d7e5a940316e71ed8acd32e58ab13328d3",
|
||||
"rawBytes": 670161,
|
||||
"gzipBytes": 178821,
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-BmdEXc9r.js",
|
||||
"sha256": "dd6b9eb38ff60c997cd860c27d377c7a1880f10116832188fc6d9b6912ac2056",
|
||||
"rawBytes": 670235,
|
||||
"gzipBytes": 178853,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/zigbee-topology-runtime-j85exgxX.js"
|
||||
"houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-BVbVJSvL.js",
|
||||
"sha256": "b301cf1f4a91250ca6163fb05c5421d1613dc65557ec6510d61ccc85ab258cc4",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DE7rHApS.js",
|
||||
"sha256": "40c1aa5283e0ddaad87a412ce7ada42c2a7ef2b72d0b69611fa17ac8c1cfca84",
|
||||
"rawBytes": 28287,
|
||||
"gzipBytes": 7035,
|
||||
"gzipBytes": 7032,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/backdrop-pick-CfVEE1c3.js",
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/backdrop-pick-eS5vEdSu.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-DryrwG8G.js",
|
||||
"sha256": "b9891e1d96dec35723cbf35955a5e75fa19292fd8398097aa46020595899e6e1",
|
||||
"path": "houseplan-assets/hp-zigbee-topology-overlay-Dn1oMTHf.js",
|
||||
"sha256": "19cec6f0e9a185acbfaacc56cff85cfe52974b8d152e59009d22aab0096b9bab",
|
||||
"rawBytes": 10756,
|
||||
"gzipBytes": 3683,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-j85exgxX.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js",
|
||||
"houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/iso-scene-render-CALxqRmU.js",
|
||||
"sha256": "f4f66222fd36572fc34c694474daf248c040c5fb635160301bb4dd39ceaec446",
|
||||
"path": "houseplan-assets/iso-scene-render-CCE-TeKM.js",
|
||||
"sha256": "d3ffcf53f84db6a8c510a5001af1b2b53aa9b18781c09c56ebf4bd25d6db442c",
|
||||
"rawBytes": 44439,
|
||||
"gzipBytes": 15015,
|
||||
"gzipBytes": 15011,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/live-interaction-runtime-C_kbCgWS.js",
|
||||
"sha256": "7a6aacdb402472213bd6997f0bbb3fdbde4e25f8c9f0881e2bb0907fa2cd8108",
|
||||
"path": "houseplan-assets/live-interaction-runtime-Ba7Bs1o7.js",
|
||||
"sha256": "ec5125376ea07c2226e195f680bb96282fc0663254ab1f8b8cf192872243e4e5",
|
||||
"rawBytes": 8216,
|
||||
"gzipBytes": 3205,
|
||||
"gzipBytes": 3208,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/pdf-export-BSpK2TsR.js",
|
||||
"sha256": "7e4b945d5a45e438dd7ef484651f182c09f6d3e40a3db72c52bd853bd8ba445e",
|
||||
"path": "houseplan-assets/pdf-export-BbdvUxVO.js",
|
||||
"sha256": "c72e1b5276c2b14a7af50aa5a61e9f10ddeb42b63e9e6e8b16883193edb7e43c",
|
||||
"rawBytes": 236244,
|
||||
"gzipBytes": 109872,
|
||||
"gzipBytes": 109870,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/furniture-plan-art.generated-HHfE6G8m.js",
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/furniture-plan-art.generated-BfO1XOcD.js",
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-editor-QJQxYKiA.js",
|
||||
"sha256": "97cecc2b4589331ca3b691705fcc637beff63fb02b152af66987803546a2d2d6",
|
||||
"path": "houseplan-assets/summary-panel-editor-AdqXisOL.js",
|
||||
"sha256": "b2b06fd3d05fb9f0e0b968b2db06382a70138d2cc15eab633683ced152087581",
|
||||
"rawBytes": 14824,
|
||||
"gzipBytes": 3395,
|
||||
"gzipBytes": 3400,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-metrics-CuoIyiO4.js",
|
||||
"sha256": "ed3919d2ce63a029335b4a079821139cd365a860c0fb8e09c05eb9cc0e344b16",
|
||||
"path": "houseplan-assets/summary-panel-metrics-Opp08Jxc.js",
|
||||
"sha256": "85a3a361187bee8843f3cb0831e361d381ed15846120c5680d5bda85e6858137",
|
||||
"rawBytes": 2610,
|
||||
"gzipBytes": 1304,
|
||||
"gzipBytes": 1305,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-Crai8dhf.js",
|
||||
"sha256": "e5a6a1430e12b6f2f533d446a7fc33a0f5b2d78d7760f3fa06c5543cf12f2f7c",
|
||||
"path": "houseplan-assets/summary-panel-runtime-loaded-B6QGWMyY.js",
|
||||
"sha256": "3e8a8b3c6632befadb081043fc6afa6685effc4672d05a3747029642f60c6052",
|
||||
"rawBytes": 73366,
|
||||
"gzipBytes": 20089,
|
||||
"gzipBytes": 20085,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/summary-panel-editor-QJQxYKiA.js",
|
||||
"houseplan-assets/summary-panel-metrics-CuoIyiO4.js"
|
||||
"houseplan-assets/summary-panel-editor-AdqXisOL.js",
|
||||
"houseplan-assets/summary-panel-metrics-Opp08Jxc.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-De4vOxpt.js",
|
||||
"sha256": "cb567b3217e3e45bf943aea822e0b97310a60b96c48f3bedb9816c5a07d6a82a",
|
||||
"path": "houseplan-assets/zigbee-topology-CW_HKDoT.js",
|
||||
"sha256": "0bd4758722344d63391a9f4c45e591a260fc6acf1ab4e8511479edef2c08fce1",
|
||||
"rawBytes": 18961,
|
||||
"gzipBytes": 7429,
|
||||
"gzipBytes": 7425,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-j85exgxX.js",
|
||||
"sha256": "2874c621bd38d7f01054228604b6fded2f6047af80aa01b01efee95378592452",
|
||||
"path": "houseplan-assets/zigbee-topology-runtime-DU0yDsuI.js",
|
||||
"sha256": "c1eadf604fa8e2f3129c48da7a37727e8c0a0408c81182403cbcb3aed518f58a",
|
||||
"rawBytes": 3941,
|
||||
"gzipBytes": 1761,
|
||||
"gzipBytes": 1762,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js",
|
||||
"houseplan-assets/zigbee-topology-De4vOxpt.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js",
|
||||
"houseplan-assets/zigbee-topology-CW_HKDoT.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "3b101e0a9f209b044bc177318f28432cb95b9b85fb4f0803772cf018b8f90996",
|
||||
"sha256": "8f5fbf9f62363032c898284d72e25336c59aaf2c9de126d22e3ce6d43c293103",
|
||||
"rawBytes": 1205,
|
||||
"gzipBytes": 803,
|
||||
"gzipBytes": 801,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-panel.js",
|
||||
"sha256": "22cc3ae9e23a17c016bc57a60f9cc0ab3bd23a2f84286775357ce10285737d86",
|
||||
"sha256": "a07ab338c2158001a3b8087118b6ee72dfc91ce60cde2b95e56eea2fefff1a6a",
|
||||
"rawBytes": 6165,
|
||||
"gzipBytes": 2325,
|
||||
"gzipBytes": 2323,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-CwdwKGP9.js"
|
||||
"houseplan-assets/houseplan-card-BKJrmMEj.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
+5
-5
@@ -1,14 +1,14 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{q as e,l as o,v as t,e5 as s,A as a,e6 as i,e7 as l,e8 as n,E as r,c}from"./houseplan-card-CwdwKGP9.js";class f 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,f={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")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{q as e,l as o,v as t,e7 as s,A as a,e8 as i,e9 as l,ea as n,E as r,c}from"./houseplan-card-BKJrmMEj.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=>f[e.name]||e.name}
|
||||
.computeLabel=${e=>h[e.name]||e.name}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>`;return c`
|
||||
${d(h.slice(0,3))}
|
||||
${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:_})}
|
||||
</div>`:a}
|
||||
${d(h.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)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
|
||||
${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);
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+90
-90
File diff suppressed because one or more lines are too long
+76
-76
File diff suppressed because one or more lines are too long
+28
-28
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{c as a,A as e}from"./houseplan-card-CwdwKGP9.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-Crai8dhf.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__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{c as a,A as e}from"./houseplan-card-BKJrmMEj.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-B6QGWMyY.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
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{eb 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 d,ec as u,ed as f,bu as m,ee as g}from"./houseplan-card-CwdwKGP9.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=d(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const 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,d="device"===o?a?.area_id:c?.area_id||l&&e.registry.devices?.[l]?.area_id,u="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||d||"",f=u&&e.areaToSpace[u]||i.space||e.firstSpaceId;l&&e.registry.devices?.[l]&&e.spaceIds.has(f)&&(!n.devices.has(l)||"entity"===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*h(c,l,d=_){try{let u=0;for(const f of l){const l=c.spaces.find(e=>String(e?.id)===f.id);if(!l)continue;const m=e(l,f),g=d(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const a=t(e);if(!a)continue;const c=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,r,o,g.roomGeom,g.multiWallNodes)||a;v=y(v,s(c,m.physicalBodies)),yield}const _=m.cellCm/r;u+=a(v)*_*_/1e4}return u}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=u(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__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{ed 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 l,a3 as c,aA as d,ee as u,ef as f,bu as m,eg as g}from"./houseplan-card-BKJrmMEj.js";function v(e){if(!e.registry.authoritative)return null;const t=new Set,n=d(e.markers);for(const i of Object.values(e.registry.devices||{}))i?.id&&"service"!==i.entry_type&&!n.devices.has(i.id)&&i.area_id&&e.areaToSpace[i.area_id]&&t.add(i.id);for(const i of e.markers||[]){if(i.removed||"virtual"===i.binding)continue;const 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,l="entity"===o?e.registry.entities?.[a]:null,c="device"===o?a:l?.device_id,d="device"===o?s?.area_id:l?.area_id||c&&e.registry.devices?.[c]?.area_id,u="string"==typeof i.room_id&&i.room_id.length>0&&null===i.area?"":i.area||d||"",f=u&&e.areaToSpace[u]||i.space||e.firstSpaceId;c&&e.registry.devices?.[c]&&e.spaceIds.has(f)&&(!n.devices.has(c)||"entity"===o&&n.liveEntities.has(a))&&t.add(c)}return t}function y(e,t){return e?.length?t?.length?g(e,t):e:t}function _(e,t){const n=l(e.rooms,t.walls,t.openCuts,[],i,t.cellCm,r,o);return{roomGeom:"ok"===n.status||"degraded-extra"===n.status?n.roomGeom:void 0,multiWallNodes:c(e.rooms,t.walls,t.openCuts,i,t.cellCm,r,o)}}function*h(l,c,d=_){try{let u=0;for(const f of c){const c=l.spaces.find(e=>String(e?.id)===f.id);if(!c)continue;const m=e(c,f),g=d(f,m);let v=null;for(const e of f.rooms){if(!e.id)continue;const s=t(e);if(!s)continue;const l=n(f.rooms,e.id,m.walls,m.openCuts,i,m.cellCm,r,o,g.roomGeom,g.multiWallNodes)||s;v=y(v,a(l,m.physicalBodies)),yield}const _=m.cellCm/r;u+=s(v)*_*_/1e4}return u}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=u(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};
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";import{n as t,b as n}from"./zigbee-topology-De4vOxpt.js";import{F as e}from"./houseplan-card-CwdwKGP9.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};
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";import{n as t,b as n}from"./zigbee-topology-CW_HKDoT.js";import{F as e}from"./houseplan-card-BKJrmMEj.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};
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="82d67fade42d86838ff98dbd4ec9c8d6f19dcc50bb4cfc5a31368dc008f58c3e";try{await import("./houseplan-assets/houseplan-card-CwdwKGP9.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__="34d1dbfc181acaf58842dfced0e6aa3a899aef6b8d1eeba53732ef729a63137e";try{await import("./houseplan-assets/houseplan-card-BKJrmMEj.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)}
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -2,6 +2,16 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- The Device editor offers a fifth **Display** option — **Value + static icon**.
|
||||
The marker shows the same number or state as **Value + state** while staying
|
||||
one colour forever: state, alarm, unavailability, live RGB colour and activity
|
||||
never paint it, and there is no pulse. The compact °/% and LQI readings and the
|
||||
separate value badge are hidden exactly as in **Always static icon** (the badge
|
||||
setting is preserved); a vacuum draws no live puck, trail or route warning, and
|
||||
its stored history is kept. An older card meeting the new mode shows the marker
|
||||
as an ordinary coloured icon — the configuration is not damaged
|
||||
([#588](https://github.com/Matysh/houseplan-card/issues/588)).
|
||||
|
||||
## v1.77.0-beta.1 — 2026-09-18
|
||||
|
||||
- Furniture items with the same dimensions now look the same: the width and
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- В настройках устройства появился пятый вариант «Отображение» — **«Значение +
|
||||
статичный значок»**. Маркер показывает то же число или состояние, что и
|
||||
«Значение + состояние», и при этом остаётся одного цвета всегда: состояние,
|
||||
тревога, недоступность, цвет RGB-света и активность его не красят, пульсации
|
||||
нет. Компактные °/% и LQI и внешний бейдж со значением скрыты, как в режиме
|
||||
«Всегда статичный значок» (настройка бейджа сохраняется); у пылесоса не
|
||||
рисуются живой puck, след и предупреждение о маршруте, сохранённая история не
|
||||
удаляется. Карточка старой версии, встретив новый режим, покажет маркер как
|
||||
обычный цветной значок — конфигурация при этом не портится
|
||||
([#588](https://github.com/Matysh/houseplan-card/issues/588)).
|
||||
|
||||
## v1.77.0-beta.1 — 2026-09-18
|
||||
|
||||
- Предметы мебели с одинаковыми размерами теперь и выглядят одинаково: указанные
|
||||
|
||||
@@ -685,8 +685,18 @@ space transfer remaps an internal target and disables/counts a link whose
|
||||
target is outside the transfer. Older clients ignore the field and may erase
|
||||
it if they reconstruct the same marker after a downgrade.
|
||||
|
||||
`marker.display` gained a fifth accepted token, `value_static_icon` (#588),
|
||||
beside `badge`, `icon_ripple`, `value` and `static_icon` (plus the read-only
|
||||
legacy `ripple`). The shape of the field does not change and there is no
|
||||
migration. A client older than the mode meets an unknown token at its existing
|
||||
read boundary (`normalizeDeviceDisplay`) and projects it to `badge`: the marker
|
||||
appears as an ordinary coloured icon. The degradation is visible but safe — the
|
||||
stored value is not rewritten, and only an explicit re-save of that marker by
|
||||
the user would replace it. The backend accepts the new token on write and keeps
|
||||
accepting every previous one.
|
||||
|
||||
`marker.value_source` is an optional explicit source for the inner face of a
|
||||
`display: value` marker. Absence or `null` preserves the historical automatic
|
||||
`display: value` or `display: value_static_icon` marker. Absence or `null` preserves the historical automatic
|
||||
entity-state choice; an object uses exactly the same discriminated source
|
||||
contract and formatter as `marker.value_badge.source`. A missing explicit
|
||||
source stays selected and renders `—` rather than silently falling back. The
|
||||
|
||||
@@ -2652,6 +2652,12 @@ require hands on real hardware — they remain for the human pass.
|
||||
cards; preview still names the real HA state/source and explains the static result
|
||||
- [ ] Switching a static vacuum back to a dynamic display restores applicable
|
||||
live/server trails; choosing static never deletes stored trail history
|
||||
- [ ] `value_static_icon` (#588) shows the same value as `value` — including on
|
||||
the plan itself, the space card and PDF, where sources are resolved
|
||||
lazily — while state, alarm, unavailability, RGB and activity never paint
|
||||
it; no pulse, no °/%/LQI, value badge suppressed with its setting kept,
|
||||
and the live vacuum puck, trail and route warning stay hidden
|
||||
[auto: `smoke_static_icon.mjs`, golden `device-icon-state-table-*`]
|
||||
- [ ] Activity colour and size (×2..×8) apply per device; alarm ignores them
|
||||
- [ ] Icon size ×0.5..×3 and rotation 0..355° apply per device; the
|
||||
temp/humidity badges scale with the icon
|
||||
|
||||
+12
-2
@@ -860,9 +860,19 @@ authoritative; the package size default is 1.5 diameters. `prefers-reduced-motio
|
||||
replaces ordinary motion with a compact colored indicator while the static red
|
||||
alarm remains clear.
|
||||
|
||||
The four display choices are icon + state; icon + state + activity; value +
|
||||
state; and always-static icon. A separate value badge can show an entity state,
|
||||
The five display choices are icon + state; icon + state + activity; value +
|
||||
state; always-static icon; and value + static icon. A separate value badge can
|
||||
show an entity state,
|
||||
useful attribute, average LQI or linked light state on any side of the marker.
|
||||
**Value + static icon** combines the two: the marker content follows the same
|
||||
rules as **value + state** — the same Value source, the same numbers and
|
||||
localized states, the same icon fallback with its reason — while the colour
|
||||
behaves like **always-static icon**. State, alarm, unavailability, live RGB
|
||||
colour and activity never change the marker; there is no pulse at all, the
|
||||
compact °/% and LQI readings stay hidden, and the separate value badge is
|
||||
suppressed the same way (the setting is kept), because the value is already
|
||||
inside the marker. A vacuum in this mode draws no live puck, trail or route
|
||||
warning, and its saved history is not deleted.
|
||||
For **value + state**, the Device editor also offers **Value source**. Keep
|
||||
**Automatic (as before)** for the legacy choice, or select one of those same
|
||||
readings—for example, cover position—to replace the icon with `42 %` rather
|
||||
|
||||
+13
-1
@@ -1277,7 +1277,7 @@ Hover включается только после события от наст
|
||||
нет; при системном уменьшении движения обычная активность обозначается
|
||||
компактной цветной точкой, а тревога остаётся понятной по красной подложке.
|
||||
|
||||
### Четыре варианта «Отображение»
|
||||
### Пять вариантов «Отображение»
|
||||
|
||||
| Опция | Иконка | Подложка статуса | Активность | Значение |
|
||||
|---|---:|---:|---:|---:|
|
||||
@@ -1285,6 +1285,7 @@ Hover включается только после события от наст
|
||||
| Значок + состояние и активность | Да | Да | Да | Отдельные компактные °/% и LQI возможны |
|
||||
| Значение + состояние | При автоматическом выборе — если значение получить нельзя или источник неоднозначен; при явном — только для virtual marker | Да | Только тревога | Автоматическое либо явно выбранное состояние, атрибут, LQI или состояние связанного источника света |
|
||||
| Всегда статичный значок | Да | Нейтральная в цветах текущей темы | Нет | Нет |
|
||||
| Значение + статичный значок | Как в «Значение + состояние» | Нейтральная в цветах текущей темы | Нет | Как в «Значение + состояние» |
|
||||
|
||||
В режиме «Значение вместо иконки» поддерживаются и числа, и локализованные HA
|
||||
текстовые состояния. Поле **«Источник значения»** позволяет оставить
|
||||
@@ -1324,6 +1325,17 @@ Hover включается только после события от наст
|
||||
комнате» теперь отвечает только за среднее значение комнаты и не управляет
|
||||
внешним бейджем.
|
||||
|
||||
«Значение + статичный значок» соединяет две строки таблицы: содержимое маркера
|
||||
выбирается ровно так же, как в «Значение + состояние» — тот же «Источник
|
||||
значения», те же числа и локализованные состояния, тот же откат к значку с
|
||||
объяснением причины, — а цвет ведёт себя как у «Всегда статичный значок».
|
||||
Состояние, тревога, недоступность, цвет RGB-света и активность не меняют маркер
|
||||
никогда. Пульсации нет, включая тревожную; компактные °/% и LQI не
|
||||
показываются; внешний бейдж со значением скрыт так же, как в статичном режиме
|
||||
(настройка сохраняется), потому что число уже внутри маркера. Для пылесоса
|
||||
подавляются живой puck, след и предупреждение о маршруте — сохранённая история
|
||||
не удаляется.
|
||||
|
||||
«Всегда статичный значок» — осознанное исключение из общей матрицы состояний.
|
||||
Маркер не меняет подложку или иконку, не показывает тревогу, недоступность,
|
||||
RGB-цвет, значение, температуру, влажность и LQI. Для пылесоса также скрываются
|
||||
|
||||
@@ -282,8 +282,16 @@ export const LOW_HEADROOM_WARNING_BYTES = 15_000;
|
||||
* синхронный View до первого движущегося кадра; общий renderer бумажных фигур
|
||||
* удержал объединённый факт на 291 058 Б — 642 Б сверху и 1 358 Б до нижней
|
||||
* границы полосы.
|
||||
*
|
||||
* 2026-09-18, #588: потолок перецентрирован 291_700 → 292_400 без изменения
|
||||
* общего бюджета. Пятый режим «Отображение» — это два производных предиката
|
||||
* вместо одной проверки токена и пятая строка в двух словарях ярлыков; всё это
|
||||
* синхронный View, потому что режим решает, что нарисовано внутри маркера при
|
||||
* первом кадре. Факт на `dev` был 291 156 Б, измеренный факт 291 346 Б (+190 Б)
|
||||
* оставлял под прежним центром 354 Б сверху — внутри шумовой полосы метрики.
|
||||
* Новый центр даёт 1 054 Б сверху и 946 Б до нижней границы полосы.
|
||||
*/
|
||||
export const INITIAL_VIEW_GZIP_CEILING = 291_700;
|
||||
export const INITIAL_VIEW_GZIP_CEILING = 292_400;
|
||||
export const INITIAL_VIEW_CEILING_BAND = 2_000;
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
{
|
||||
"const": "static_icon"
|
||||
},
|
||||
{
|
||||
"const": "value_static_icon"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
|
||||
@@ -6035,8 +6035,10 @@ const MUTANT_DEFINITIONS = [
|
||||
+ 'metrics, pulse and vacuum live together rather than relying on renderer-specific checks',
|
||||
patches: [{
|
||||
file: 'src/device-presentation-policy.ts',
|
||||
find: ' } else if (staticIcon) {\n visual = NEUTRAL_VISUAL;',
|
||||
replace: ' } else if (false && staticIcon) {\n visual = NEUTRAL_VISUAL;',
|
||||
// #588 переименовал переменную: она отвечает уже не за один токен, а за
|
||||
// «нейтральное лицо» обоих статичных режимов. Якорь переехал, смысл нет.
|
||||
find: ' } else if (neutralFace) {\n visual = NEUTRAL_VISUAL;',
|
||||
replace: ' } else if (false && neutralFace) {\n visual = NEUTRAL_VISUAL;',
|
||||
}],
|
||||
},
|
||||
{
|
||||
@@ -6100,7 +6102,9 @@ const MUTANT_DEFINITIONS = [
|
||||
+ 'single gate either hides valid values or revives ambiguous arbitrary registry rows',
|
||||
patches: [{
|
||||
file: 'src/device-presentation-policy.ts',
|
||||
find: " const face: PresentationFace = input.display === 'value'\n"
|
||||
// #588: лицо значения теперь спрашивают предикатом, а не сравнением с
|
||||
// одним токеном — режимов со значением стало два. Якорь переехал.
|
||||
find: ' const face: PresentationFace = wantsValue\n'
|
||||
+ " && !effectiveHidden && input.valueAvailable ? 'value' : 'icon';",
|
||||
replace: " const face: PresentationFace = false\n"
|
||||
+ " && !effectiveHidden && input.valueAvailable ? 'value' : 'icon';",
|
||||
@@ -6115,7 +6119,9 @@ const MUTANT_DEFINITIONS = [
|
||||
+ 'satellites escape a static face even though the core itself remains neutral',
|
||||
patches: [{
|
||||
file: 'src/device-presentation-policy.ts',
|
||||
find: ' const metrics = !staticIcon && !effectiveHidden;',
|
||||
// #588: переменная переименована в `neutralFace` — режимов с нейтральным
|
||||
// лицом стало два. Якорь переехал, смысл прежний.
|
||||
find: ' const metrics = !neutralFace && !effectiveHidden;',
|
||||
replace: ' const metrics = true;',
|
||||
}],
|
||||
},
|
||||
@@ -10043,6 +10049,90 @@ const MUTANT_DEFINITIONS = [
|
||||
replace: " if (candidateTag && isStableTag(candidateTag) && candidateTag === 'v0.0.0-mutant') {",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'value-static-icon-keeps-live-colour',
|
||||
guard: 'node --test test/device-presentation-policy.test.mjs',
|
||||
because: '#588 AC1: «никогда не меняет цвет» — весь смысл режима. Стоит нейтральности '
|
||||
+ 'снова стать свойством одного токена, и маркер начнёт краснеть тревогой, гаснуть '
|
||||
+ 'недоступностью и брать цвет RGB-лампы — молча, потому что значение при этом видно',
|
||||
patches: [{
|
||||
file: 'src/device-presentation-policy.ts',
|
||||
find: ' const neutralFace = displayIsNeutral(input.display);',
|
||||
replace: " const neutralFace = input.display === 'static_icon';",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'value-static-icon-loses-value-fallback',
|
||||
guard: 'node --test test/device-presentation.test.mjs',
|
||||
because: '#588 AC2: когда значение получить нельзя, лицо откатывается к значку, и '
|
||||
+ 'причина отката — единственное, что объясняет человеку, почему числа нет. Потеря '
|
||||
+ 'причины оставляет пустой значок без объяснения ровно в новом режиме',
|
||||
patches: [{
|
||||
file: 'src/device-presentation.ts',
|
||||
find: ' fallbackReason: displayWantsValue(display) ? value.fallback : null,',
|
||||
replace: " fallbackReason: display === 'value' ? value.fallback : null,",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'value-static-icon-takes-the-sourceless-fast-path',
|
||||
guard: 'node --test test/device-presentation.test.mjs',
|
||||
because: '#588 AC3: быстрый путь «статичному маркеру источники не нужны» действует на '
|
||||
+ 'основном пути рендера плана. Распространить его на режим со значением — значит '
|
||||
+ 'потерять число именно на плане и сохранить его в предпросмотре редактора',
|
||||
patches: [{
|
||||
file: 'src/device-presentation.ts',
|
||||
find: " const sourceless = display === 'static_icon';",
|
||||
replace: ' const sourceless = neutralFace;',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'value-static-icon-pulses-on-alarm',
|
||||
guard: 'node --test test/device-pulse.test.mjs',
|
||||
because: '#588 AC4: тревога пробивает и выключенные живые состояния, и отсутствие '
|
||||
+ 'активности. Если пульсация снова принадлежит одному токену, «маркер не меняет цвет» '
|
||||
+ 'перестаёт быть правдой в самый заметный момент',
|
||||
patches: [{
|
||||
file: 'src/device-pulse.ts',
|
||||
find: ' || displayIsNeutral(display)) {',
|
||||
replace: " || display === 'static_icon') {",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'value-static-icon-keeps-live-vacuum',
|
||||
guard: 'node demo/smoke_static_icon.mjs',
|
||||
because: '#588 AC6 (К2а): три ветки живого слоя пылесоса сравнивают режим строкой и не '
|
||||
+ 'читают политику, поэтому зелёный AC1 их не гарантирует. Возвращённые к одному токену, '
|
||||
+ 'они оставляют над «неизменным» маркером едущий puck, след и бейдж маршрута',
|
||||
patches: [{
|
||||
file: 'src/houseplan-card.ts',
|
||||
find: " if (displayIsNeutral(normalizeDeviceDisplay(d.marker?.display))) {\n this._vacRt.delete(d.id);",
|
||||
replace: " if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') {\n this._vacRt.delete(d.id);",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'value-static-icon-keeps-vacuum-overlay',
|
||||
guard: 'node demo/smoke_static_icon.mjs',
|
||||
because: '#588 AC6 (К2а): вторая из трёх веток — рендер puck и следа. Она не зависит от '
|
||||
+ '`vacuumLive` политики, и её одной достаточно, чтобы над нейтральным маркером ездил '
|
||||
+ 'робот',
|
||||
patches: [{
|
||||
file: 'src/houseplan-card.ts',
|
||||
find: ' if (displayIsNeutral(normalizeDeviceDisplay(d.marker?.display))) continue;',
|
||||
replace: " if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') continue;",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'value-static-icon-keeps-route-warning',
|
||||
guard: 'node demo/smoke_static_icon.mjs',
|
||||
because: '#588 AC6 (К2а): третья ветка — бейдж предупреждения о маршруте. Он появляется '
|
||||
+ 'только на несопоставленной карте, поэтому свидетель держит две конфигурации: при '
|
||||
+ 'совпадающей калибровке маршрут `ready` и бейджа нет ни в одном режиме',
|
||||
patches: [{
|
||||
file: 'src/houseplan-card.ts',
|
||||
find: ' if (displayIsNeutral(normalizeDeviceDisplay(d.marker?.display))) return nothing;',
|
||||
replace: " if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') return nothing;",
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
const mutationCardSource = readFileSync(join(repoRoot, 'src/houseplan-card.ts'), 'utf8');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { DeviceAvailability, DeviceVisualState } from './device-visual';
|
||||
import type { DeviceDisplayMode } from './logic';
|
||||
import { displayIsNeutral, displayWantsValue, type DeviceDisplayMode } from './logic';
|
||||
|
||||
export type PresentationReason =
|
||||
| 'neutral'
|
||||
@@ -23,6 +23,7 @@ export type PresentationReason =
|
||||
| 'composite_power_source'
|
||||
| 'activity_display_disabled'
|
||||
| 'static_icon'
|
||||
| 'value_static_icon'
|
||||
| 'ha_disabled'
|
||||
| 'orphaned';
|
||||
|
||||
@@ -94,7 +95,8 @@ export function resolveDevicePresentationPolicy(
|
||||
decisions.push('lifecycle.active');
|
||||
}
|
||||
|
||||
const staticIcon = input.display === 'static_icon';
|
||||
const neutralFace = displayIsNeutral(input.display);
|
||||
const wantsValue = displayWantsValue(input.display);
|
||||
let visual = input.controllerFace
|
||||
? { ...input.sourceVisual, availability: input.controllerAvailability }
|
||||
: input.sourceVisual;
|
||||
@@ -107,7 +109,7 @@ export function resolveDevicePresentationPolicy(
|
||||
if (effectiveHidden) {
|
||||
visual = NEUTRAL_VISUAL;
|
||||
decisions.push('face.hidden');
|
||||
} else if (staticIcon) {
|
||||
} else if (neutralFace) {
|
||||
visual = NEUTRAL_VISUAL;
|
||||
decisions.push('face.static');
|
||||
} else if (input.sourceVisual.status !== 'alarm' && !input.liveStates) {
|
||||
@@ -117,7 +119,7 @@ export function resolveDevicePresentationPolicy(
|
||||
decisions.push('face.dynamic');
|
||||
}
|
||||
|
||||
if (!staticIcon && !effectiveHidden && input.liveStates
|
||||
if (!neutralFace && !effectiveHidden && input.liveStates
|
||||
&& input.sourceVisual.status !== 'alarm' && input.shortActivity) {
|
||||
visual = { ...visual, activity: input.shortActivity };
|
||||
decisions.push(`activity.short_${input.shortActivity}`);
|
||||
@@ -129,17 +131,17 @@ export function resolveDevicePresentationPolicy(
|
||||
else if (visual.status === 'open') decisions.push('status.open');
|
||||
else decisions.push('status.neutral');
|
||||
|
||||
const face: PresentationFace = input.display === 'value'
|
||||
const face: PresentationFace = wantsValue
|
||||
&& !effectiveHidden && input.valueAvailable ? 'value' : 'icon';
|
||||
decisions.push(face === 'value' ? 'content.value' : input.display === 'value'
|
||||
decisions.push(face === 'value' ? 'content.value' : wantsValue
|
||||
? 'content.value_fallback_icon' : 'content.icon');
|
||||
if (input.display === 'value' && !input.valueAvailable && input.valueFallback) {
|
||||
if (wantsValue && !input.valueAvailable && input.valueFallback) {
|
||||
decisions.push(`content.${input.valueFallback}`);
|
||||
}
|
||||
|
||||
const dynamicIcon = input.liveStates && !staticIcon && !effectiveHidden;
|
||||
const metrics = !staticIcon && !effectiveHidden;
|
||||
const pulseEligible = !effectiveHidden && !bindingUnavailable && !staticIcon
|
||||
const dynamicIcon = input.liveStates && !neutralFace && !effectiveHidden;
|
||||
const metrics = !neutralFace && !effectiveHidden;
|
||||
const pulseEligible = !effectiveHidden && !bindingUnavailable && !neutralFace
|
||||
&& visual.availability === 'available'
|
||||
&& (visual.status === 'alarm' || (input.liveStates && input.display === 'icon_ripple'));
|
||||
const vacuumLive = metrics && input.vacuumLiveRequested;
|
||||
@@ -177,6 +179,7 @@ export function resolvePresentationReason(input: PresentationReasonInput): Prese
|
||||
if (input.lifecycle === 'ha_disabled') return 'ha_disabled';
|
||||
if (input.lifecycle === 'orphaned') return 'orphaned';
|
||||
if (input.display === 'static_icon') return 'static_icon';
|
||||
if (input.display === 'value_static_icon') return 'value_static_icon';
|
||||
if (input.visual.status === 'alarm') return 'alarm';
|
||||
if (!input.liveStates) return 'live_states_disabled';
|
||||
if (input.visual.availability === 'unavailable') return 'unavailable';
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
type DeviceActivity, type DeviceAvailability, type DeviceVisualState, type EntityVisualSample,
|
||||
} from './device-visual';
|
||||
import {
|
||||
displayIsNeutral, displayWantsValue,
|
||||
hassValue, lightColorOf, lqiColor, normalizeDeviceDisplay, stateIcon, valueWithUnit,
|
||||
type DeviceDisplayMode,
|
||||
} from './logic';
|
||||
@@ -86,7 +87,7 @@ export function deviceAccessibleLabel(
|
||||
export function deviceA11yState(presentation: Pick<
|
||||
ResolvedDevicePresentation, 'visual' | 'lockState' | 'display'
|
||||
>): DeviceA11yState {
|
||||
if (presentation.display === 'static_icon') return 'neutral';
|
||||
if (displayIsNeutral(presentation.display)) return 'neutral';
|
||||
if (presentation.visual.status === 'alarm') return 'alarm';
|
||||
if (presentation.visual.availability === 'unavailable') return 'unavailable';
|
||||
if (presentation.lockState) return presentation.lockState;
|
||||
@@ -645,7 +646,7 @@ export function presentationClasses(presentation: Pick<
|
||||
'visual' | 'lockState' | 'activity' | 'display' | 'effectiveHidden' | 'activityGeneration' | 'pulse'
|
||||
>): string[] {
|
||||
if (presentation.effectiveHidden) return [];
|
||||
if (presentation.display === 'static_icon') return ['static-icon'];
|
||||
if (displayIsNeutral(presentation.display)) return ['static-icon'];
|
||||
const classes: string[] = [];
|
||||
const { visual } = presentation;
|
||||
if (presentation.pulse.kind === 'alarm') classes.push('alarm');
|
||||
@@ -672,8 +673,16 @@ export function resolveDevicePresentation(
|
||||
options: ResolvePresentationOptions,
|
||||
): ResolvedDevicePresentation {
|
||||
const display = normalizeDeviceDisplay(d.marker?.display);
|
||||
const staticIcon = display === 'static_icon';
|
||||
const sources = staticIcon && options.sourceDetails === false
|
||||
// Two independent questions, and conflating them is the trap this mode was
|
||||
// built to avoid (#588). `neutralFace` answers "does anything colour the
|
||||
// marker"; `sourceless` answers "can we skip resolving sources at all", and
|
||||
// only the value-free static mode can. `sourceDetails: false` is the main
|
||||
// plan render path, so answering the second question with the first would
|
||||
// drop the value exactly where the user looks and keep it in the editor
|
||||
// preview, where sources are always resolved.
|
||||
const neutralFace = displayIsNeutral(display);
|
||||
const sourceless = display === 'static_icon';
|
||||
const sources = sourceless && options.sourceDetails === false
|
||||
? EMPTY_SOURCES : resolvePresentationSources(
|
||||
hass, d, options.lightDevices || [d], options.lightSources,
|
||||
options.registryHass || hass,
|
||||
@@ -712,7 +721,7 @@ export function resolveDevicePresentation(
|
||||
).length > 0;
|
||||
const controllerFace = sources.sourceKind === 'controls'
|
||||
|| (configuredController && sources.sourceKind !== 'light' && sources.sourceKind !== 'cover');
|
||||
const value = staticIcon && options.sourceDetails === false
|
||||
const value = sourceless && options.sourceDetails === false
|
||||
? { source: null, text: null, fullText: null, fallback: null, explicit: false }
|
||||
: resolveValue(hass, d, sources, options.showTemperature, markerStates);
|
||||
const sourceSignature = signatureOf(d, sources, value.source);
|
||||
@@ -780,7 +789,7 @@ export function resolveDevicePresentation(
|
||||
const rippleScale = Number(d.marker?.ripple_size) > 0
|
||||
? Number(d.marker!.ripple_size) : DEVICE_PULSE_DEFAULT_SCALE;
|
||||
const configuredRippleColor = safeStoredColor(d.marker?.ripple_color, null);
|
||||
const rippleColor = staticIcon ? null : configuredRippleColor || lightColor || null;
|
||||
const rippleColor = neutralFace ? null : configuredRippleColor || lightColor || null;
|
||||
const pulse = resolveDevicePulse({
|
||||
display,
|
||||
visual,
|
||||
@@ -809,7 +818,7 @@ export function resolveDevicePresentation(
|
||||
});
|
||||
const notices: PresentationReason[] = [];
|
||||
if (options.designPreview && userHidden) notices.push('hidden_design_preview');
|
||||
if (!staticIcon && d.marker?.vacuum?.live === true) notices.push('vacuum_live_plan_only');
|
||||
if (!neutralFace && d.marker?.vacuum?.live === true) notices.push('vacuum_live_plan_only');
|
||||
const powerSource = sources.visualSources.some((source) =>
|
||||
source.role === 'power_gate' || isDevicePowerSwitch(hass, source.eid),
|
||||
);
|
||||
@@ -817,7 +826,7 @@ export function resolveDevicePresentation(
|
||||
eid.startsWith('switch.') && !hass?.entities?.[eid]?.entity_category,
|
||||
).length;
|
||||
if (powerSource && uncategorisedSwitches > 1) notices.push('composite_power_source');
|
||||
if (display !== 'icon_ripple' && display !== 'static_icon'
|
||||
if (display !== 'icon_ripple' && !neutralFace
|
||||
&& combined.status !== 'alarm' && combined.activity !== 'none') {
|
||||
notices.push('activity_display_disabled');
|
||||
}
|
||||
@@ -843,7 +852,7 @@ export function resolveDevicePresentation(
|
||||
icon,
|
||||
valueText,
|
||||
valueFullText: policy.face === 'value' ? value.fullText : null,
|
||||
fallbackReason: display === 'value' ? value.fallback : null,
|
||||
fallbackReason: displayWantsValue(display) ? value.fallback : null,
|
||||
activity,
|
||||
activityGeneration: rt?.gen || 1,
|
||||
pulse,
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import type { DeviceActivity, DeviceVisualState } from './device-visual';
|
||||
import type { DeviceDisplayMode } from './logic';
|
||||
import { displayIsNeutral, type DeviceDisplayMode } from './logic';
|
||||
|
||||
export type DevicePulseKind = 'none' | 'alarm' | 'short' | 'continuous';
|
||||
export type DevicePulseReason = 'none' | 'alarm' | 'event' | 'presence' | 'transition' | 'running';
|
||||
@@ -79,7 +79,7 @@ export function resolveDevicePulse(options: ResolveDevicePulseOptions): Resolved
|
||||
? Math.max(1, Number(options.diameterScale)) : DEVICE_PULSE_DEFAULT_SCALE;
|
||||
const color = options.color || semanticPulseColor(visual, semanticActivity);
|
||||
if (effectiveHidden || bindingUnavailable || visual.availability === 'unavailable'
|
||||
|| display === 'static_icon') {
|
||||
|| displayIsNeutral(display)) {
|
||||
return noPulse(generation, color, diameterScale);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
climateTempFor, humFor, isHumEntity, isTempEntity, lqiFor, resolvedLightSources, tempFor,
|
||||
} from './devices';
|
||||
import { isRegistryEntryEnabled } from './ha-binding-status';
|
||||
import { hassValue, valueWithUnit } from './logic';
|
||||
import { displayIsNeutral, displayWantsValue, hassValue, valueWithUnit, type DeviceDisplayMode } from './logic';
|
||||
import type {
|
||||
DevItem, MarkerValueBadge, ValueBadgePosition, ValueBadgeSource,
|
||||
} from './types';
|
||||
@@ -47,7 +47,7 @@ export interface ValueBadgeCandidate {
|
||||
export interface ResolveValueBadgeOptions {
|
||||
showTemperature: boolean;
|
||||
showSignal: boolean;
|
||||
display: string;
|
||||
display: DeviceDisplayMode;
|
||||
effectiveHidden: boolean;
|
||||
/** Canonical marker:* states already projected by presentation/light graph. */
|
||||
markerStates?: readonly { ref: string; on: boolean; name: string }[];
|
||||
@@ -276,7 +276,7 @@ export function resolveDeviceValueBadge(
|
||||
hass: any, d: DevItem, options: ResolveValueBadgeOptions,
|
||||
): ResolvedValueBadge | null {
|
||||
const configured = d.marker?.value_badge != null;
|
||||
if (options.effectiveHidden || options.display === 'static_icon') return null;
|
||||
if (options.effectiveHidden || displayIsNeutral(options.display)) return null;
|
||||
const stored = d.marker?.value_badge;
|
||||
if (configured) {
|
||||
if (!stored?.enabled) return null;
|
||||
@@ -302,7 +302,7 @@ export function resolveDeviceValueBadge(
|
||||
...resolveValueSource(hass, d, stored.source, options.markerStates),
|
||||
};
|
||||
}
|
||||
if (!options.showTemperature || options.display === 'value') return null;
|
||||
if (!options.showTemperature || displayWantsValue(options.display)) return null;
|
||||
const source = legacySource(hass, d);
|
||||
if (!source) return null;
|
||||
let legacyText: string | null = null;
|
||||
|
||||
+9
-12
@@ -45,7 +45,7 @@ import {
|
||||
DEFAULT_TEMP_MIN, DEFAULT_TEMP_MAX, type SpaceDisplay,
|
||||
referencedContentUrls,
|
||||
DISPLAY_MODES, TAP_ACTIONS, SPACE_FILL_UI_MODES, ROOM_FILL_MODES,
|
||||
normalizeDeviceDisplay, isAlarmCapable, type DeviceDisplayMode,
|
||||
normalizeDeviceDisplay, isAlarmCapable, displayIsNeutral, type DeviceDisplayMode,
|
||||
liveText, liveTextReference, liveTextToken, hassValue, valueWithUnit, decorTextScale, decorTextLines,
|
||||
DECOR_TEXT_BASE,
|
||||
} from './logic';
|
||||
@@ -450,16 +450,13 @@ type ResizePreview = { space: string; sp: any };
|
||||
type ResizeWallUnion = ReturnType<typeof wallBodiesUnionPath>;
|
||||
type ResizeWallArtifact = ReturnType<typeof wallBodiesGeometry>;
|
||||
const DISPLAY_LABEL_KEYS: Record<DeviceDisplayMode, I18nKey> = {
|
||||
badge: 'display.badge',
|
||||
icon_ripple: 'display.icon_ripple',
|
||||
value: 'display.value',
|
||||
static_icon: 'display.static_icon',
|
||||
badge: 'display.badge', icon_ripple: 'display.icon_ripple', value: 'display.value',
|
||||
static_icon: 'display.static_icon', value_static_icon: 'display.value_static_icon',
|
||||
};
|
||||
const DISPLAY_HINT_KEYS: Record<DeviceDisplayMode, I18nKey> = {
|
||||
badge: 'marker.display_hint_badge',
|
||||
icon_ripple: 'marker.display_hint_icon_ripple',
|
||||
value: 'marker.display_hint_value',
|
||||
static_icon: 'marker.display_hint_static_icon',
|
||||
badge: 'marker.display_hint_badge', icon_ripple: 'marker.display_hint_icon_ripple',
|
||||
value: 'marker.display_hint_value', static_icon: 'marker.display_hint_static_icon',
|
||||
value_static_icon: 'marker.display_hint_value_static_icon',
|
||||
};
|
||||
/** Keeps every previously valid scale at the maximum 20 cm grid scale lossless. */
|
||||
const DECOR_TEXT_CM_MAX = 2000;
|
||||
@@ -12195,7 +12192,7 @@ export class HouseplanCard extends LitElement {
|
||||
if (!this.hass) return;
|
||||
for (const d of this._devices) {
|
||||
if (d.hidden || !this._isVacDev(d)) continue;
|
||||
if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') {
|
||||
if (displayIsNeutral(normalizeDeviceDisplay(d.marker?.display))) {
|
||||
this._vacRt.delete(d.id);
|
||||
continue;
|
||||
}
|
||||
@@ -12411,7 +12408,7 @@ export class HouseplanCard extends LitElement {
|
||||
const trails: TemplateResult[] = [];
|
||||
for (const d of devs) {
|
||||
if (d.hidden || !this._isVacDev(d)) continue;
|
||||
if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') continue;
|
||||
if (displayIsNeutral(normalizeDeviceDisplay(d.marker?.display))) continue;
|
||||
const fact = this._renderDeviceSnapshot?.facts.get(`vacuum:${d.id}`) as any;
|
||||
const src = fact?.source ?? this._vacSource(d, this._renderPlanHass);
|
||||
if (!src) continue;
|
||||
@@ -12590,7 +12587,7 @@ export class HouseplanCard extends LitElement {
|
||||
*/
|
||||
private _vacRouteBadge(d: DevItem): TemplateResult | typeof nothing {
|
||||
if (!this._isVacDev(d) || d.hidden) return nothing;
|
||||
if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') return nothing;
|
||||
if (displayIsNeutral(normalizeDeviceDisplay(d.marker?.display))) return nothing;
|
||||
const fact = this._renderDeviceSnapshot?.facts.get(`vacuum:${d.id}`) as
|
||||
{ resolution?: VacuumRouteResolution } | undefined;
|
||||
const reason = routeWarningKey(fact?.resolution, this._vacRt.get(d.id)?.moving ?? false);
|
||||
|
||||
@@ -49,7 +49,7 @@ import {
|
||||
DEFAULT_TEMP_MIN, DEFAULT_TEMP_MAX, type SpaceDisplay,
|
||||
referencedContentUrls,
|
||||
DISPLAY_MODES, TAP_ACTIONS, SPACE_FILL_UI_MODES, ROOM_FILL_MODES,
|
||||
normalizeDeviceDisplay, isAlarmCapable, type DeviceDisplayMode,
|
||||
normalizeDeviceDisplay, isAlarmCapable, displayIsNeutral, displayWantsValue, type DeviceDisplayMode,
|
||||
liveText, liveTextReference, liveTextToken, hassValue, valueWithUnit, decorTextScale, decorTextLines,
|
||||
DECOR_TEXT_BASE,
|
||||
} from './logic';
|
||||
@@ -414,16 +414,13 @@ type ResizePreview = { space: string; sp: any };
|
||||
type ResizeWallUnion = ReturnType<typeof wallBodiesUnionPath>;
|
||||
type ResizeWallArtifact = ReturnType<typeof wallBodiesGeometry>;
|
||||
const DISPLAY_LABEL_KEYS: Record<DeviceDisplayMode, I18nKey> = {
|
||||
badge: 'display.badge',
|
||||
icon_ripple: 'display.icon_ripple',
|
||||
value: 'display.value',
|
||||
static_icon: 'display.static_icon',
|
||||
badge: 'display.badge', icon_ripple: 'display.icon_ripple', value: 'display.value',
|
||||
static_icon: 'display.static_icon', value_static_icon: 'display.value_static_icon',
|
||||
};
|
||||
const DISPLAY_HINT_KEYS: Record<DeviceDisplayMode, I18nKey> = {
|
||||
badge: 'marker.display_hint_badge',
|
||||
icon_ripple: 'marker.display_hint_icon_ripple',
|
||||
value: 'marker.display_hint_value',
|
||||
static_icon: 'marker.display_hint_static_icon',
|
||||
badge: 'marker.display_hint_badge', icon_ripple: 'marker.display_hint_icon_ripple',
|
||||
value: 'marker.display_hint_value', static_icon: 'marker.display_hint_static_icon',
|
||||
value_static_icon: 'marker.display_hint_value_static_icon',
|
||||
};
|
||||
/** Keeps every previously valid scale at the maximum 20 cm grid scale lossless. */
|
||||
const DECOR_TEXT_CM_MAX = 2000;
|
||||
@@ -13394,7 +13391,7 @@ public _renderMarkerDialog(): TemplateResult {
|
||||
</option>`)}
|
||||
</select>
|
||||
<p class="muted">${this.host._t(DISPLAY_HINT_KEYS[d.display])}</p>
|
||||
${d.display === 'value' ? html`<div class="markerhelpfield markervaluesource">
|
||||
${displayWantsValue(d.display) ? html`<div class="markerhelpfield markervaluesource">
|
||||
<div class="markerhelplabel">
|
||||
<label for="marker-value-source">${this.host._t('marker.value_source')}</label>
|
||||
${this._help('marker.value_source.help')}
|
||||
@@ -13427,7 +13424,7 @@ public _renderMarkerDialog(): TemplateResult {
|
||||
<ha-icon icon="mdi:alert-outline"></ha-icon>${this.host._t('marker.value_source_missing_hint')}
|
||||
</p>` : nothing}
|
||||
</div>` : nothing}
|
||||
${d.display === 'static_icon' && this.host._bindingHasAlarm(d.binding)
|
||||
${displayIsNeutral(d.display) && this.host._bindingHasAlarm(d.binding)
|
||||
? html`<div class="habindingbanner" role="note">
|
||||
<ha-icon icon="mdi:alert-outline"></ha-icon>
|
||||
<span>${this.host._t('marker.static_alarm_warning')}</span>
|
||||
@@ -13444,10 +13441,10 @@ public _renderMarkerDialog(): TemplateResult {
|
||||
valueBadgeSource: source,
|
||||
valueBadgeTouched: true,
|
||||
};
|
||||
}, d.display === 'static_icon' || (!badgeCandidates.length && !d.valueBadgeSource))}
|
||||
}, displayIsNeutral(d.display) || (!badgeCandidates.length && !d.valueBadgeSource))}
|
||||
<span>${this.host._t('marker.value_badge_enabled')}</span>
|
||||
</label>
|
||||
${d.display === 'static_icon'
|
||||
${displayIsNeutral(d.display)
|
||||
? html`<p class="muted markerlightdisabled" role="note">
|
||||
<ha-icon icon="mdi:information-outline"></ha-icon>${this.host._t('marker.value_badge_static')}
|
||||
</p>`
|
||||
@@ -13483,7 +13480,7 @@ public _renderMarkerDialog(): TemplateResult {
|
||||
${badgeSourceMissing ? html`<p class="muted markerlightwarning" role="status">
|
||||
<ha-icon icon="mdi:alert-outline"></ha-icon>${this.host._t('marker.value_badge_missing_hint')}
|
||||
</p>` : nothing}
|
||||
${d.display === 'value' && badgeSourceKey === innerValueSourceKey
|
||||
${displayWantsValue(d.display) && badgeSourceKey === innerValueSourceKey
|
||||
? html`<p class="muted markerlightwarning" role="note">
|
||||
<ha-icon icon="mdi:information-outline"></ha-icon>${this.host._t('marker.value_badge_duplicate')}
|
||||
</p>` : nothing}
|
||||
|
||||
@@ -184,10 +184,12 @@
|
||||
"display.badge": "Symbol + Zustand",
|
||||
"display.icon_ripple": "Symbol + Zustand und Aktivität",
|
||||
"display.static_icon": "Immer statisches Symbol",
|
||||
"display.value_static_icon": "Wert + statisches Symbol",
|
||||
"marker.display_hint_badge": "Das Symbol und die dynamische Platte zeigen den Gerätezustand ohne den üblichen Aktivitätspuls. Rote Alarme bleiben sichtbar.",
|
||||
"marker.display_hint_icon_ripple": "Das Symbol, die dynamische Platte und der Puls zeigen einen kurzen Puls für Ereignisse und einen kontinuierlichen Puls für Arbeit, Bewegung oder Anwesenheit. Rote Alarme haben eine separate Priorität.",
|
||||
"marker.display_hint_value": "Ein ausgewählter oder automatisch ermittelter Home Assistant-Wert ersetzt das Symbol, während die Platte weiterhin den Zustand anzeigt. Rote Alarme bleiben sichtbar.",
|
||||
"marker.display_hint_static_icon": "Die themenabhängige Hülle und das Symbol bleiben immer gleich. Zustand, Aktivität, Nichtverfügbarkeit und Alarme verändern nicht das Erscheinungsbild.",
|
||||
"marker.display_hint_value_static_icon": "Der Marker zeigt den gewählten Wert, während die Hülle themenabhängig bleibt. Zustand, Aktivität, Nichtverfügbarkeit und Alarme ändern die Markerfarbe nicht.",
|
||||
"marker.static_alarm_warning": "Dieses Gerät kann Alarme melden. Die statische Anzeige verbirgt die visuelle Alarmanzeige des Markers.",
|
||||
"marker.preview.title": "Anzeigevorschau",
|
||||
"marker.preview.actual": "Jetzt",
|
||||
@@ -236,6 +238,7 @@
|
||||
"marker.preview.reason.ha_disabled": "Die Home Assistant-Verbindung ist deaktiviert und wird im Plan ausgeblendet",
|
||||
"marker.preview.reason.orphaned": "Die gespeicherte Home Assistant-Verbindung konnte nicht mehr gefunden werden",
|
||||
"marker.preview.reason.static_icon": "Statischer Modus: Der Gerätestatus ändert das Symbol nicht",
|
||||
"marker.preview.reason.value_static_icon": "Statischer Modus mit Wert: Der Gerätestatus ändert die Markerfarbe nicht",
|
||||
"marker.activity_color": "Aktivitätspulsfarbe",
|
||||
"marker.ripple_size": "Aktivitätspulsgröße",
|
||||
"marker.activity_alarm_note": "Farbe und Größe betreffen nur gewöhnliche Aktivität und ändern keine roten Alarme.",
|
||||
|
||||
@@ -184,10 +184,12 @@
|
||||
"display.badge": "Icon + state",
|
||||
"display.icon_ripple": "Icon + state and activity",
|
||||
"display.static_icon": "Always static icon",
|
||||
"display.value_static_icon": "Value + static icon",
|
||||
"marker.display_hint_badge": "The icon and dynamic plate show device state without the ordinary activity pulse. Red alarms remain visible.",
|
||||
"marker.display_hint_icon_ripple": "The icon, dynamic plate and pulse show a short pulse for events and a continuous pulse for work, motion or presence. Red alarms have separate priority.",
|
||||
"marker.display_hint_value": "A selected or automatically resolved Home Assistant value replaces the icon while the plate continues to show state. Red alarms remain visible.",
|
||||
"marker.display_hint_static_icon": "The theme-aware shell and icon always stay the same. State, activity, unavailability and alarms do not change the face.",
|
||||
"marker.display_hint_value_static_icon": "The marker shows the selected value while the shell stays theme-aware. State, activity, unavailability and alarms do not change the marker colour.",
|
||||
"marker.static_alarm_warning": "This device can report alarms. Static display hides the marker's visual alarm indication.",
|
||||
"marker.preview.title": "Display preview",
|
||||
"marker.preview.actual": "Now",
|
||||
@@ -236,6 +238,7 @@
|
||||
"marker.preview.reason.ha_disabled": "The Home Assistant binding is disabled and will be hidden on the plan",
|
||||
"marker.preview.reason.orphaned": "The saved Home Assistant binding can no longer be found",
|
||||
"marker.preview.reason.static_icon": "Static mode: device state does not change the icon",
|
||||
"marker.preview.reason.value_static_icon": "Static mode with a value: device state does not change the marker colour",
|
||||
"marker.activity_color": "Activity pulse color",
|
||||
"marker.ripple_size": "Activity pulse size",
|
||||
"marker.activity_alarm_note": "Color and size affect ordinary activity only and do not change red alarms.",
|
||||
|
||||
@@ -184,10 +184,12 @@
|
||||
"display.badge": "Icône + état",
|
||||
"display.icon_ripple": "Icône + état et activité",
|
||||
"display.static_icon": "Icône toujours statique",
|
||||
"display.value_static_icon": "Valeur + icône statique",
|
||||
"marker.display_hint_badge": "L’icône et le fond dynamique affichent l’état de l’appareil sans la pulsation d’activité habituelle. Les alarmes rouges restent visibles.",
|
||||
"marker.display_hint_icon_ripple": "L’icône, le fond dynamique et la pulsation affichent une brève animation pour les événements et une animation continue pour le fonctionnement, le mouvement ou la présence. Les alarmes rouges restent prioritaires.",
|
||||
"marker.display_hint_value": "Une valeur Home Assistant sélectionnée ou résolue automatiquement remplace l’icône tandis que le fond continue d’indiquer l’état. Les alarmes rouges restent visibles.",
|
||||
"marker.display_hint_static_icon": "Le contour adapté au thème et l’icône restent toujours identiques. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas l’apparence.",
|
||||
"marker.display_hint_value_static_icon": "Le marqueur affiche la valeur choisie tandis que le contour reste adapté au thème. L’état, l’activité, l’indisponibilité et les alarmes ne modifient pas la couleur du marqueur.",
|
||||
"marker.static_alarm_warning": "Cet appareil peut signaler des alarmes. L’affichage statique masque l’indication visuelle d’alarme du marqueur.",
|
||||
"marker.preview.title": "Aperçu de l’affichage",
|
||||
"marker.preview.actual": "Actuel",
|
||||
@@ -236,6 +238,7 @@
|
||||
"marker.preview.reason.ha_disabled": "L’association Home Assistant est désactivée et sera masquée sur le plan",
|
||||
"marker.preview.reason.orphaned": "L’association Home Assistant enregistrée est introuvable",
|
||||
"marker.preview.reason.static_icon": "Mode statique : l’état de l’appareil ne modifie pas l’icône",
|
||||
"marker.preview.reason.value_static_icon": "Mode statique avec valeur : l’état de l’appareil ne modifie pas la couleur du marqueur",
|
||||
"marker.activity_color": "Couleur de la pulsation d’activité",
|
||||
"marker.ripple_size": "Taille de la pulsation d’activité",
|
||||
"marker.activity_alarm_note": "La couleur et la taille n’affectent que l’activité normale et ne modifient pas les alarmes rouges.",
|
||||
|
||||
@@ -184,10 +184,12 @@
|
||||
"display.badge": "Значок + состояние",
|
||||
"display.icon_ripple": "Значок + состояние и активность",
|
||||
"display.static_icon": "Всегда статичный значок",
|
||||
"display.value_static_icon": "Значение + статичный значок",
|
||||
"marker.display_hint_badge": "Значок и динамическая подложка показывают состояние устройства без обычной пульсации активности. Красная тревога сохраняется.",
|
||||
"marker.display_hint_icon_ripple": "Значок, динамическая подложка и пульсация: короткая — для событий, постоянная — для работы, движения или присутствия. Красная тревога имеет отдельный приоритет.",
|
||||
"marker.display_hint_value": "Значок заменяется выбранным или автоматически найденным значением Home Assistant; подложка продолжает показывать состояние. Красная тревога сохраняется.",
|
||||
"marker.display_hint_static_icon": "Подложка в цветах темы и значок всегда остаются одинаковыми. Состояния, активность, недоступность и тревоги не меняют отображение.",
|
||||
"marker.display_hint_value_static_icon": "Маркер показывает выбранное значение, а подложка остаётся в цветах темы. Состояния, активность, недоступность и тревоги не меняют цвет маркера.",
|
||||
"marker.static_alarm_warning": "Это устройство может сообщать о тревогах. Статичный режим скрывает визуальную тревогу маркера.",
|
||||
"marker.preview.title": "Предпросмотр отображения",
|
||||
"marker.preview.actual": "Сейчас",
|
||||
@@ -236,6 +238,7 @@
|
||||
"marker.preview.reason.ha_disabled": "Привязка отключена в Home Assistant, поэтому устройство будет скрыто на плане",
|
||||
"marker.preview.reason.orphaned": "Сохранённая привязка Home Assistant больше не найдена",
|
||||
"marker.preview.reason.static_icon": "Статичный режим: состояние устройства не меняет значок",
|
||||
"marker.preview.reason.value_static_icon": "Статичный режим со значением: состояние устройства не меняет цвет маркера",
|
||||
"marker.activity_color": "Цвет пульсации активности",
|
||||
"marker.ripple_size": "Размер пульсации активности",
|
||||
"marker.activity_alarm_note": "Цвет и размер относятся только к обычной активности и не влияют на красную тревогу.",
|
||||
|
||||
+14
-1
@@ -895,9 +895,22 @@ export function safeUrl(url: string | null | undefined): string | null {
|
||||
*/
|
||||
/** UI presentations. `ripple` remains a backend/read compatibility value only
|
||||
* and is normalised to `icon_ripple` by the shared normalizer. */
|
||||
export const DISPLAY_MODES = ['badge', 'icon_ripple', 'value', 'static_icon'] as const;
|
||||
export const DISPLAY_MODES = ['badge', 'icon_ripple', 'value', 'static_icon', 'value_static_icon'] as const;
|
||||
export type DeviceDisplayMode = typeof DISPLAY_MODES[number];
|
||||
|
||||
/* The four original modes tied two independent things together: what is drawn
|
||||
* inside the marker (icon or value) and whether the marker takes colour from
|
||||
* state. `value_static_icon` (#588) is the missing fourth combination, so the
|
||||
* two questions are asked separately from here on. Every renderer, the pulse
|
||||
* layer, the badge layer and the live vacuum branches ask these two predicates
|
||||
* instead of comparing the token, which is what kept the modes consistent. */
|
||||
/** The marker draws the resolved value instead of its icon when one exists. */
|
||||
export const displayWantsValue = (display: DeviceDisplayMode): boolean =>
|
||||
display === 'value' || display === 'value_static_icon';
|
||||
/** State, alarm, availability, live colour and activity never touch the face. */
|
||||
export const displayIsNeutral = (display: DeviceDisplayMode): boolean =>
|
||||
display === 'static_icon' || display === 'value_static_icon';
|
||||
|
||||
/** One read boundary for persisted/legacy display values. Unknown manual data
|
||||
* safely falls back to the default dynamic badge; the backend still rejects it
|
||||
* on write. Keep every renderer and editor on this normalizer. */
|
||||
|
||||
+1
-1
@@ -196,7 +196,7 @@ export interface Marker {
|
||||
* only use. */
|
||||
room_id?: string | null;
|
||||
/** `ripple` is legacy read compatibility; UI maps it to icon_ripple. */
|
||||
display?: 'badge' | 'ripple' | 'icon_ripple' | 'value' | 'static_icon' | null; // how the device is drawn
|
||||
display?: 'badge' | 'ripple' | 'icon_ripple' | 'value' | 'static_icon' | 'value_static_icon' | null; // how the device is drawn
|
||||
ripple_color?: string | null;
|
||||
ripple_size?: number | null; // max ring diameter, in icon diameters (default 1.5)
|
||||
size?: number | null; // icon size multiplier (default 1)
|
||||
|
||||
@@ -643,3 +643,65 @@ test('renderer-ready decision trace covers value, metrics, LQI, vacuum and pulse
|
||||
assert.match(sourceFiles[1], /resolveDevicePresentation\(/);
|
||||
assert.match(sourceFiles[2], /renderDeviceFace\(shown/);
|
||||
});
|
||||
|
||||
// #588. Два родителя нового режима задавали лицо и цвет одним выбором, поэтому
|
||||
// таблица ниже проверяет их порознь: лицо остаётся «значением» при любом
|
||||
// входном статусе, а цвет не отзывается ни на один из них.
|
||||
test('#588 AC1: value_static_icon рисует значение и не берёт цвет ни от одного статуса', () => {
|
||||
const inputs = [
|
||||
{ availability: 'available', status: 'neutral', activity: 'none' },
|
||||
{ availability: 'available', status: 'working', activity: 'running' },
|
||||
{ availability: 'available', status: 'open', activity: 'none' },
|
||||
{ availability: 'available', status: 'alarm', activity: 'running' },
|
||||
{ availability: 'unavailable', status: 'neutral', activity: 'none' },
|
||||
];
|
||||
for (const sourceVisual of inputs) {
|
||||
const r = resolveDevicePresentationPolicy(basePolicy({
|
||||
display: 'value_static_icon', valueAvailable: true, liveStates: true,
|
||||
shortActivity: 'event', vacuumLiveRequested: true, sourceVisual,
|
||||
}));
|
||||
const where = `${sourceVisual.availability}/${sourceVisual.status}`;
|
||||
assert.equal(r.face, 'value', where);
|
||||
assert.deepEqual(r.visual, neutral, where);
|
||||
assert.deepEqual(
|
||||
[r.dynamicIcon, r.metrics, r.liveColor, r.pulseEligible, r.vacuumLive],
|
||||
[false, false, false, false, false], where,
|
||||
);
|
||||
assertDecision(r, 'face.static');
|
||||
assertDecision(r, 'content.value');
|
||||
}
|
||||
// Контраст: тот же вход в «Значение + состояние» красится и пульсирует —
|
||||
// иначе нейтральность выше доказывала бы только сломанную фикстуру.
|
||||
const dynamic = resolveDevicePresentationPolicy(basePolicy({
|
||||
display: 'value', valueAvailable: true, liveStates: true,
|
||||
sourceVisual: { availability: 'available', status: 'alarm', activity: 'none' },
|
||||
}));
|
||||
assert.equal(dynamic.face, 'value');
|
||||
assert.equal(dynamic.visual.status, 'alarm');
|
||||
assert.equal(dynamic.pulseEligible, true);
|
||||
});
|
||||
|
||||
// #588 AC2. Откат лица общий с `value`: нейтральный цвет не должен отменять
|
||||
// объяснение, почему вместо числа нарисован значок.
|
||||
test('#588 AC2: value_static_icon откатывается к значку и называет ту же причину, что value', () => {
|
||||
const reasons = ['value_no_state', 'value_ambiguous_sources', 'value_non_scalar', 'value_virtual'];
|
||||
for (const valueFallback of reasons) {
|
||||
for (const display of ['value', 'value_static_icon']) {
|
||||
const r = resolveDevicePresentationPolicy(basePolicy({
|
||||
display, valueAvailable: false, valueFallback,
|
||||
}));
|
||||
assert.equal(r.face, 'icon', `${display}/${valueFallback}`);
|
||||
assertDecision(r, 'content.value_fallback_icon');
|
||||
assertDecision(r, `content.${valueFallback}`);
|
||||
}
|
||||
}
|
||||
// «Всегда статичный значок» значения не просит вовсе — и потому причины не
|
||||
// объявляет: откат принадлежит лицу значения, а не нейтральному цвету.
|
||||
const staticOnly = resolveDevicePresentationPolicy(basePolicy({
|
||||
display: 'static_icon', valueAvailable: false, valueFallback: 'value_no_state',
|
||||
}));
|
||||
assert.equal(staticOnly.face, 'icon');
|
||||
assertDecision(staticOnly, 'content.icon');
|
||||
assert.ok(!staticOnly.decisionIds.includes('content.value_no_state'),
|
||||
staticOnly.decisionIds.join(', '));
|
||||
});
|
||||
|
||||
@@ -1391,3 +1391,97 @@ test('issue 369г: устройство с полностью отключённ
|
||||
assert.equal(disabledResult.effectiveHidden, true);
|
||||
assert.equal(disabledResult.visual.status, 'neutral');
|
||||
});
|
||||
|
||||
// #588 AC3. `sourceDetails: false` — основной путь рендера плана, карточки
|
||||
// пространства и PDF. Быстрый путь «статичному маркеру источники не нужны»
|
||||
// принадлежит только режиму без значения; распространить его на новый режим
|
||||
// значит потерять число ровно там, где на него смотрят, и сохранить его в
|
||||
// предпросмотре редактора, где источники резолвятся всегда.
|
||||
test('#588 AC3: value_static_icon резолвит значение и на быстром пути плана', () => {
|
||||
const h = hass({
|
||||
'cover.door': state('cover.door', 'open', { device_class: 'door', current_position: 42 }),
|
||||
}, { 'cover.door': { entity_id: 'cover.door', device_id: 'd1', platform: 'demo' } });
|
||||
const base = device({
|
||||
icon: 'mdi:door', entities: ['cover.door'], primary: 'cover.door',
|
||||
marker: {
|
||||
id: 'd1', binding: 'device:d1',
|
||||
value_source: { kind: 'entity_attribute', entity_id: 'cover.door', attribute: 'current_position' },
|
||||
},
|
||||
});
|
||||
const planOptions = { ...options, sourceDetails: false };
|
||||
const valueStatic = resolveDevicePresentation(h, {
|
||||
...base, marker: { ...base.marker, display: 'value_static_icon' },
|
||||
}, planOptions);
|
||||
assert.equal(valueStatic.valueText, '42 %');
|
||||
assert.equal(valueStatic.valueSource?.eid, 'cover.door');
|
||||
assert.ok(valueStatic.visualSources.length > 0);
|
||||
// Тот же маркер в «Всегда статичный значок» остаётся без источников —
|
||||
// экономия быстрого пути не потеряна.
|
||||
const staticIcon = resolveDevicePresentation(h, {
|
||||
...base, marker: { ...base.marker, display: 'static_icon' },
|
||||
}, planOptions);
|
||||
assert.equal(staticIcon.valueText, null);
|
||||
assert.equal(staticIcon.valueSource, null);
|
||||
assert.deepEqual(staticIcon.visualSources, []);
|
||||
|
||||
// AC2 на том же пути: в автоматическом режиме недоступное значение откатывает
|
||||
// лицо к значку и называет причину. Без причины человек видит пустой значок и
|
||||
// не узнаёт, почему числа нет, — а режим выглядит исправным. Явно выбранный
|
||||
// источник ведёт себя иначе (`—`), и это проверено выше по контракту К1.
|
||||
const offline = hass({
|
||||
'cover.door': state('cover.door', 'unavailable', { device_class: 'door' }),
|
||||
}, { 'cover.door': { entity_id: 'cover.door', device_id: 'd1', platform: 'demo' } });
|
||||
const fallback = resolveDevicePresentation(offline, {
|
||||
...base,
|
||||
marker: { id: 'd1', binding: 'device:d1', display: 'value_static_icon' },
|
||||
}, planOptions);
|
||||
assert.equal(fallback.valueText, null);
|
||||
assert.equal(fallback.fallbackReason, 'value_no_state');
|
||||
});
|
||||
|
||||
// #588 AC2/AC4. Тревога — самый дорогой случай: она пробивает и выключенные
|
||||
// живые состояния, и отсутствие активности. «Никогда не меняет цвет» читается
|
||||
// буквально, поэтому её здесь нет ни в подложке, ни в пульсации, ни в бейдже.
|
||||
test('#588 AC2/AC4: тревожный источник не красит и не пульсирует value_static_icon', () => {
|
||||
const h = hass({
|
||||
'binary_sensor.smoke': state('binary_sensor.smoke', 'on', { device_class: 'smoke' }),
|
||||
'sensor.device_linkquality': state('sensor.device_linkquality', '40'),
|
||||
}, {
|
||||
'binary_sensor.smoke': { entity_id: 'binary_sensor.smoke', device_id: 'd1', platform: 'demo' },
|
||||
'sensor.device_linkquality': {
|
||||
entity_id: 'sensor.device_linkquality', device_id: 'd1', platform: 'demo',
|
||||
},
|
||||
});
|
||||
const base = device({
|
||||
icon: 'mdi:smoke-detector',
|
||||
entities: ['binary_sensor.smoke', 'sensor.device_linkquality'],
|
||||
primary: 'binary_sensor.smoke',
|
||||
bindingStatus: {
|
||||
kind: 'active',
|
||||
enabledEntityIds: ['binary_sensor.smoke', 'sensor.device_linkquality'],
|
||||
allEntityIds: ['binary_sensor.smoke', 'sensor.device_linkquality'],
|
||||
},
|
||||
marker: {
|
||||
id: 'd1', binding: 'device:d1', display: 'value_static_icon',
|
||||
value_badge: { enabled: true, source: { kind: 'derived_lqi' }, position: 'right' },
|
||||
},
|
||||
});
|
||||
const result = resolveDevicePresentation(h, base, options);
|
||||
assert.equal(result.visual.status, 'neutral');
|
||||
assert.equal(result.pulse.kind, 'none');
|
||||
assert.deepEqual(result.classes, ['static-icon']);
|
||||
assert.equal(result.lqiText, null);
|
||||
assert.equal(result.valueBadge, null);
|
||||
assert.equal(result.rippleColor, null);
|
||||
assert.equal(deviceA11yState(result), 'neutral');
|
||||
assert.equal(result.explanation.reason, 'value_static_icon');
|
||||
assert.ok(result.valueText, 'значение остаётся видимым');
|
||||
// Тот же прибор в обычном режиме тревожится — иначе фикстура доказывала бы
|
||||
// только то, что тревоги в ней нет.
|
||||
const alarmed = resolveDevicePresentation(h, {
|
||||
...base, marker: { ...base.marker, display: 'badge' },
|
||||
}, options);
|
||||
assert.equal(alarmed.visual.status, 'alarm');
|
||||
assert.equal(alarmed.pulse.kind, 'alarm');
|
||||
assert.ok(alarmed.valueBadge);
|
||||
});
|
||||
|
||||
@@ -15,6 +15,12 @@ const resolve = (overrides = {}) => resolveDevicePulse({
|
||||
test('pulse priority is hidden/static, alarm, finite event, continuous state', () => {
|
||||
assert.equal(resolve({ effectiveHidden: true, visual: visual('alarm') }).kind, 'none');
|
||||
assert.equal(resolve({ display: 'static_icon', visual: visual('alarm') }).kind, 'none');
|
||||
// #588 AC4: значение внутри маркера не возвращает ему ни тревожную пульсацию,
|
||||
// ни обычную — нейтральность принадлежит цвету, а не лицу.
|
||||
assert.equal(resolve({ display: 'value_static_icon', visual: visual('alarm') }).kind, 'none');
|
||||
assert.equal(resolve({
|
||||
display: 'value_static_icon', semanticActivity: 'presence',
|
||||
}).kind, 'none');
|
||||
assert.equal(resolve({ display: 'badge', visual: visual('alarm') }).kind, 'alarm');
|
||||
assert.equal(resolve({
|
||||
shortReason: 'event', shortExpiresAt: 2_000, semanticActivity: 'running',
|
||||
|
||||
@@ -732,6 +732,17 @@ test('device icon state-table goldens cover both themes and design facets', () =
|
||||
fixture.config.markers.find((marker) => marker.id === 'golden-right-temperature').display,
|
||||
'static_icon',
|
||||
);
|
||||
// #588. Пятый режим стоит на включённой RGB-лампе, и это существенно:
|
||||
// «Значение + состояние» красит её маркер, новый режим обязан оставить его
|
||||
// нейтральным. На нейтральном источнике (сенсор, крышка) обе стороны дали
|
||||
// бы один и тот же кадр, и пересъёмка доказывала бы только сама себя.
|
||||
assert.equal(
|
||||
fixture.config.markers.find((marker) => marker.id === 'golden-light-one').display,
|
||||
'value_static_icon',
|
||||
);
|
||||
assert.equal(fixture.states['light.golden_light_one'].state, 'on');
|
||||
assert.deepEqual(fixture.states['light.golden_light_one'].attributes.rgb_color,
|
||||
[255, 196, 112]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+9
-1
@@ -21,7 +21,8 @@ import {
|
||||
openingShoulders, openingEntityReferences, fillColorsOf, lerpColor, roomFillStyle,
|
||||
resolveEffectiveRoomFill, stateIcon, lightColorOf, isAlarmState, parseRoomRef,
|
||||
diffNewDevices, poleOfInaccessibility, runServiceFor, coverMoving,
|
||||
normalizeDeviceDisplay, isAlarmCapable, TAP_ACTIONS,
|
||||
normalizeDeviceDisplay, isAlarmCapable, TAP_ACTIONS, DISPLAY_MODES,
|
||||
displayWantsValue, displayIsNeutral,
|
||||
liveText, liveTextValue, liveTextReference, liveTextToken,
|
||||
hassValue, valueWithUnit, decorTextScale, decorTextLines,
|
||||
LIVE_TEXT_DASH, LIVE_TEXT_VALUE_MAX, DECOR_TEXT_SCALE_MIN, DECOR_TEXT_SCALE_MAX,
|
||||
@@ -51,7 +52,14 @@ test('display normalization and alarm-capable metadata share one contract', () =
|
||||
assert.equal(normalizeDeviceDisplay(undefined), 'badge');
|
||||
assert.equal(normalizeDeviceDisplay('ripple'), 'icon_ripple');
|
||||
assert.equal(normalizeDeviceDisplay('static_icon'), 'static_icon');
|
||||
assert.equal(normalizeDeviceDisplay('value_static_icon'), 'value_static_icon');
|
||||
assert.equal(normalizeDeviceDisplay('not-a-mode'), 'badge');
|
||||
// #588: порядок списка виден пользователю — он задаёт порядок опций в
|
||||
// селекторе «Отображение», поэтому новый режим добавлен последним.
|
||||
assert.deepEqual([...DISPLAY_MODES],
|
||||
['badge', 'icon_ripple', 'value', 'static_icon', 'value_static_icon']);
|
||||
assert.deepEqual(DISPLAY_MODES.filter(displayWantsValue), ['value', 'value_static_icon']);
|
||||
assert.deepEqual(DISPLAY_MODES.filter(displayIsNeutral), ['static_icon', 'value_static_icon']);
|
||||
assert.equal(isAlarmCapable('alarm_control_panel', ''), true);
|
||||
assert.equal(isAlarmCapable('siren', ''), true);
|
||||
assert.equal(isAlarmCapable('binary_sensor', 'smoke'), true);
|
||||
|
||||
@@ -396,6 +396,21 @@ def test_value_badge_projection_drops_malformed_scalar_fields(
|
||||
assert "sentinel" not in json.dumps(projected)
|
||||
|
||||
|
||||
def test_issue_588_display_mode_survives_the_projection() -> None:
|
||||
"""The support package carries the display mode verbatim.
|
||||
|
||||
#588 adds a fifth mode. The projection copies the field without looking at
|
||||
its value, so nothing had to change here — but a diagnostic package that
|
||||
silently dropped the mode would describe a plan the user does not see, and
|
||||
that is exactly the class of report that costs a day of guessing.
|
||||
"""
|
||||
projected = support_package._project_marker(
|
||||
support_package._Pseudonyms("display"),
|
||||
{"id": "marker", "binding": "virtual", "display": "value_static_icon"},
|
||||
)
|
||||
assert projected["display"] == "value_static_icon"
|
||||
|
||||
|
||||
def test_value_badge_projection_omits_empty_result():
|
||||
projected = support_package._project_marker(
|
||||
support_package._Pseudonyms("badge"),
|
||||
|
||||
@@ -942,6 +942,9 @@ def test_every_display_mode_the_editor_offers_is_accepted():
|
||||
"""
|
||||
modes = _ts_list("DISPLAY_MODES")
|
||||
assert "value" in modes, "the regression this test exists for"
|
||||
# #588: the fifth mode is named here as well, so deleting it from the TS
|
||||
# list cannot make this test vacuously green on an empty enumeration.
|
||||
assert "value_static_icon" in modes, "#588 combines a value face with a neutral shell"
|
||||
for mode in modes:
|
||||
v.MARKER_SCHEMA(_marker(display=mode))
|
||||
# Read/write compatibility for stores created before the unified activity UI.
|
||||
|
||||
Reference in New Issue
Block a user