refactor: type Zigbee topology boundaries

Issue: #54
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-09-04 14:45:39 +03:00
parent 85702afc0c
commit 2202b20730
32 changed files with 314 additions and 270 deletions
@@ -1,169 +1,169 @@
{
"schema": 1,
"fingerprint": "355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c",
"fingerprint": "d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 295363,
"initialViewGzipBytes": 295355,
"lazyFiles": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/de-vj2e8G-y.js",
"houseplan-assets/editor-B4uO_Hit.js",
"houseplan-assets/fr-B2dlk0Va.js",
"houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js",
"houseplan-assets/hp-zigbee-topology-overlay-rOIQtlN5.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js",
"houseplan-assets/zigbee-topology-runtime-DHrStvLy.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/de-DFeGpePx.js",
"houseplan-assets/editor-CCrJdJ9P.js",
"houseplan-assets/fr-B-xb3XPO.js",
"houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js",
"houseplan-assets/hp-zigbee-topology-overlay-BebiHDnX.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js",
"houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js"
],
"lazyGzipBytes": 234005,
"lazyGzipBytes": 234058,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/editor-B4uO_Hit.js",
"houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/editor-CCrJdJ9P.js",
"houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js"
],
"lazyEditorGzipBytes": 173855,
"lazyEditorGzipBytes": 173910,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js"
],
"lazyOnboardingGzipBytes": 14239,
"lazyOnboardingGzipBytes": 14235,
"lazyLocaleFiles": [
"houseplan-assets/de-vj2e8G-y.js",
"houseplan-assets/fr-B2dlk0Va.js"
"houseplan-assets/de-DFeGpePx.js",
"houseplan-assets/fr-B-xb3XPO.js"
],
"lazyLocaleGzipBytes": 49119,
"lazyLocaleGzipBytes": 49089,
"files": [
{
"path": "houseplan-assets/backdrop-pick-DVnyEcZa.js",
"sha256": "bfb00d8595149d7468a205ee5fc178a499257afd609f96ae7f3894c2ba4d6ce5",
"path": "houseplan-assets/backdrop-pick-MVjPRxdI.js",
"sha256": "9c2e03431acab819075bef26947d344e14315439ff46554e77b8a7c13d1f49bc",
"rawBytes": 21418,
"gzipBytes": 7284,
"gzipBytes": 7280,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-vj2e8G-y.js",
"sha256": "b59b4bd44e1c546448137f6d8f1b6e5c6d0e06a51d9886749ceb8ac15473c07a",
"path": "houseplan-assets/de-DFeGpePx.js",
"sha256": "98b868c66f86ff49b532cfd06fd9c3febf1a6a2fc5b82ab4cf704c8d1fdea37f",
"rawBytes": 82182,
"gzipBytes": 24830,
"gzipBytes": 24814,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-B4uO_Hit.js",
"sha256": "b484a2f0ec27ce089a214d1c399d3d293e0d34cb0f9fc82a7543ea43a0481503",
"path": "houseplan-assets/editor-CCrJdJ9P.js",
"sha256": "25805b82349ce1a5553ebe639abea2d8c01c7bf0de9604555f095f390f963504",
"rawBytes": 3826,
"gzipBytes": 1581,
"gzipBytes": 1580,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-B2dlk0Va.js",
"sha256": "cadea9f03538e80cd8d72268adfb6865437e573a69725fd3d8c004174e634672",
"path": "houseplan-assets/fr-B-xb3XPO.js",
"sha256": "fd859570cdd6bb6e35700cfd5f3ca80cf8fdeb77aaa9fb6472d6286a064d5e19",
"rawBytes": 84312,
"gzipBytes": 24289,
"gzipBytes": 24275,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-BcCJlm2v.js",
"sha256": "6ddaa80e5d1eadb3c1b75850367413559a9852048d0b272116b65bc54647baa6",
"path": "houseplan-assets/houseplan-card-DUGW724g.js",
"sha256": "5182a288cd5aafd3155b7380539576955aa1aa80c5914cecfcba48119d79d184",
"rawBytes": 1048696,
"gzipBytes": 294567,
"gzipBytes": 294561,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-vj2e8G-y.js",
"houseplan-assets/editor-B4uO_Hit.js",
"houseplan-assets/fr-B2dlk0Va.js",
"houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js",
"houseplan-assets/hp-zigbee-topology-overlay-rOIQtlN5.js"
"houseplan-assets/de-DFeGpePx.js",
"houseplan-assets/editor-CCrJdJ9P.js",
"houseplan-assets/fr-B-xb3XPO.js",
"houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js",
"houseplan-assets/hp-zigbee-topology-overlay-BebiHDnX.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"sha256": "066fb86f20424f03b246d5ae8055c9e0ee282c103ae5a13339a95450a52a7e49",
"path": "houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"sha256": "0132e5d538d9bd4a99efd2b8f86b6e01bf465c9be1de72a0399b114204a94189",
"rawBytes": 608944,
"gzipBytes": 160244,
"gzipBytes": 160238,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-DHrStvLy.js"
"houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js",
"sha256": "01fa5e1aa7ed3a37a62183d62b79b99b43572e5ec81eb5873fab4ca1ebfa0413",
"path": "houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js",
"sha256": "64f4fcf484d2e23b30c79c2a6b03c8af684da972f583151764e4957e391c1d1c",
"rawBytes": 28088,
"gzipBytes": 6955,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-rOIQtlN5.js",
"sha256": "14e0452ab14bdd3bf27e1f8194825faacd7c5919aa1975cb887a7c3db20eb6a4",
"path": "houseplan-assets/hp-zigbee-topology-overlay-BebiHDnX.js",
"sha256": "ec91560c32a0fbe0af6e066a5cbde4de470ece320b6a189fcdbbb1d690477e53",
"rawBytes": 6007,
"gzipBytes": 2380,
"gzipBytes": 2378,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js",
"houseplan-assets/zigbee-topology-runtime-DHrStvLy.js"
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js",
"houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-BoNFdBaW.js",
"sha256": "ab6139986f9dcc63cd9ca62fac1f31bc19622d47e0131f0a9453812870702d97",
"rawBytes": 12072,
"gzipBytes": 4746,
"path": "houseplan-assets/zigbee-topology-CjJau-_w.js",
"sha256": "44bdc047f04343477b564cdd5515860c2ac58827c78b4990d13e93aaf80d43f5",
"rawBytes": 12250,
"gzipBytes": 4812,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-DHrStvLy.js",
"sha256": "0c061e55b92f9a73ebf8c97da4dad736d9408eecaba503a11abcac01ae1d8d69",
"rawBytes": 3817,
"gzipBytes": 1696,
"path": "houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js",
"sha256": "e09fdfac0d02509ebb3b65739eb5e148db5b328f607b6e03d6ccd03abb81d076",
"rawBytes": 3878,
"gzipBytes": 1726,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js"
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "3d510618b0952ac50f9ac166f4751b604cd0a719fba428dd9eed771feecdb097",
"sha256": "bd8bbfbfe67ce7465845f9c52f0ab2c7678e9450cd9f238b2bb5714979f937f0",
"rawBytes": 1183,
"gzipBytes": 796,
"gzipBytes": 794,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
@@ -1,14 +1,14 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";import{k as e,l as o,t,dq as s,A as a,dr as i,ds as l,dt as r,E as n,c}from"./houseplan-card-BcCJlm2v.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 r="string"==typeof this._config?.default_floor?this._config.default_floor:"",n=[...e];return r&&!e.some(e=>e.value===r)&&n.unshift({value:r,label:r}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:n}}}:{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 r();if("warm"===e)return n;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__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";import{k as e,l as o,t,dq as s,A as a,dr as i,ds as l,dt as r,E as n,c}from"./houseplan-card-DUGW724g.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 r="string"==typeof this._config?.default_floor?this._config.default_floor:"",n=[...e];return r&&!e.some(e=>e.value===r)&&n.unshift({value:r,label:r}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:n}}}:{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 r();if("warm"===e)return n;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
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";import{k as e,A as t,du as i,aw as s,c as o,l as r,j as n}from"./houseplan-card-BcCJlm2v.js";import{a,r as h,t as d}from"./zigbee-topology-BoNFdBaW.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-DHrStvLy.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o`
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";import{k as e,A as t,du as i,aw as s,c as o,l as r,j as n}from"./houseplan-card-DUGW724g.js";import{a,r as h,t as d}from"./zigbee-topology-CjJau-_w.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-ne8Tm9gB.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o`
${l.length?s`<svg viewBox="0 0 100 100" preserveAspectRatio="none"
aria-hidden="true" data-hp="zigbee-topology-lines">
${l.map(e=>s`<line x1=${c.x} y1=${c.y}
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";import{n as t,b as n}from"./zigbee-topology-BoNFdBaW.js";import{y as e}from"./houseplan-card-BcCJlm2v.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 u(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function l(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){const n=t?.payload??t;if("string"!=typeof n)return n;try{return JSON.parse(n)}catch{return null}}async function h(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const u=e(o);return c(s,`z2m:${u||String(o)}`,async()=>{if(f(t),!u)throw a("invalid_topic");const e=t?.connection?.subscribeMessage;if("function"!=typeof e||"function"!=typeof t?.callService)throw a("unsupported");const o=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),i=Date.now()+Math.max(1,r);let s=null,c=null;const l=new Promise(t=>{s=t}),p=new Promise(t=>{c=t}),m=[];try{const r=await e.call(t.connection,t=>{!0===t?.retain&&d(t)&&s?.()},{type:"mqtt/subscribe",topic:`${u}/bridge/info`});"function"==typeof r&&m.push(r);const f=await e.call(t.connection,t=>{if(!0===t?.retain)return;const n=d(t);n&&function(t){const n=t?.transaction??t?.data?.transaction;return"string"==typeof n||"number"==typeof n?String(n):null}(n)===o&&c?.(n)},{type:"mqtt/subscribe",topic:`${u}/bridge/response/networkmap`});"function"==typeof f&&m.push(f),await h(l,Math.min(4e3,Math.max(1,i-Date.now()))),await t.callService("mqtt","publish",{topic:`${u}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:o}),qos:0,retain:!1});const g=await h(p,i-Date.now());if(g?.status&&"ok"!==g.status)throw a("provider");return n(g,u)}finally{for(const t of m)try{t()}catch{}}})}export{p as readZhaTopology,m as refreshZ2mTopology,l as subscribeZigbeeTopology,u as zigbeeTopologyRuntimeSnapshot};
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";import{n as t,b as n}from"./zigbee-topology-CjJau-_w.js";import{y as e}from"./houseplan-card-DUGW724g.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 u(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function l(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function m(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function g(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const u=e(o);return c(s,`z2m:${u||String(o)}`,async()=>{if(p(t),!u)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=null,l=null;const f=new Promise(t=>{c=t}),g=new Promise(t=>{l=t}),y=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&c?.()},{type:"mqtt/subscribe",topic:`${u}/bridge/info`});"function"==typeof r&&y.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);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&&l?.(n)},{type:"mqtt/subscribe",topic:`${u}/bridge/response/networkmap`});"function"==typeof p&&y.push(p),await m(f,Math.min(4e3,Math.max(1,s-Date.now()))),await t.callService("mqtt","publish",{topic:`${u}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const w=await m(g,s-Date.now()),b=d(w)?.status;if(b&&"ok"!==b)throw a("provider");return n(w,u)}finally{for(const t of y)try{t()}catch{}}})}export{f as readZhaTopology,g as refreshZ2mTopology,l as subscribeZigbeeTopology,u as zigbeeTopologyRuntimeSnapshot};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";try{await import("./houseplan-assets/houseplan-card-BcCJlm2v.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 entry: the main chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";try{await import("./houseplan-assets/houseplan-card-DUGW724g.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 entry: the main chunk is unavailable",e)}
+78 -78
View File
@@ -1,169 +1,169 @@
{
"schema": 1,
"fingerprint": "355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c",
"fingerprint": "d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 295363,
"initialViewGzipBytes": 295355,
"lazyFiles": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/de-vj2e8G-y.js",
"houseplan-assets/editor-B4uO_Hit.js",
"houseplan-assets/fr-B2dlk0Va.js",
"houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js",
"houseplan-assets/hp-zigbee-topology-overlay-rOIQtlN5.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js",
"houseplan-assets/zigbee-topology-runtime-DHrStvLy.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/de-DFeGpePx.js",
"houseplan-assets/editor-CCrJdJ9P.js",
"houseplan-assets/fr-B-xb3XPO.js",
"houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js",
"houseplan-assets/hp-zigbee-topology-overlay-BebiHDnX.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js",
"houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js"
],
"lazyGzipBytes": 234005,
"lazyGzipBytes": 234058,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/editor-B4uO_Hit.js",
"houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/editor-CCrJdJ9P.js",
"houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js"
],
"lazyEditorGzipBytes": 173855,
"lazyEditorGzipBytes": 173910,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js"
],
"lazyOnboardingGzipBytes": 14239,
"lazyOnboardingGzipBytes": 14235,
"lazyLocaleFiles": [
"houseplan-assets/de-vj2e8G-y.js",
"houseplan-assets/fr-B2dlk0Va.js"
"houseplan-assets/de-DFeGpePx.js",
"houseplan-assets/fr-B-xb3XPO.js"
],
"lazyLocaleGzipBytes": 49119,
"lazyLocaleGzipBytes": 49089,
"files": [
{
"path": "houseplan-assets/backdrop-pick-DVnyEcZa.js",
"sha256": "bfb00d8595149d7468a205ee5fc178a499257afd609f96ae7f3894c2ba4d6ce5",
"path": "houseplan-assets/backdrop-pick-MVjPRxdI.js",
"sha256": "9c2e03431acab819075bef26947d344e14315439ff46554e77b8a7c13d1f49bc",
"rawBytes": 21418,
"gzipBytes": 7284,
"gzipBytes": 7280,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-vj2e8G-y.js",
"sha256": "b59b4bd44e1c546448137f6d8f1b6e5c6d0e06a51d9886749ceb8ac15473c07a",
"path": "houseplan-assets/de-DFeGpePx.js",
"sha256": "98b868c66f86ff49b532cfd06fd9c3febf1a6a2fc5b82ab4cf704c8d1fdea37f",
"rawBytes": 82182,
"gzipBytes": 24830,
"gzipBytes": 24814,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-B4uO_Hit.js",
"sha256": "b484a2f0ec27ce089a214d1c399d3d293e0d34cb0f9fc82a7543ea43a0481503",
"path": "houseplan-assets/editor-CCrJdJ9P.js",
"sha256": "25805b82349ce1a5553ebe639abea2d8c01c7bf0de9604555f095f390f963504",
"rawBytes": 3826,
"gzipBytes": 1581,
"gzipBytes": 1580,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-B2dlk0Va.js",
"sha256": "cadea9f03538e80cd8d72268adfb6865437e573a69725fd3d8c004174e634672",
"path": "houseplan-assets/fr-B-xb3XPO.js",
"sha256": "fd859570cdd6bb6e35700cfd5f3ca80cf8fdeb77aaa9fb6472d6286a064d5e19",
"rawBytes": 84312,
"gzipBytes": 24289,
"gzipBytes": 24275,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-BcCJlm2v.js",
"sha256": "6ddaa80e5d1eadb3c1b75850367413559a9852048d0b272116b65bc54647baa6",
"path": "houseplan-assets/houseplan-card-DUGW724g.js",
"sha256": "5182a288cd5aafd3155b7380539576955aa1aa80c5914cecfcba48119d79d184",
"rawBytes": 1048696,
"gzipBytes": 294567,
"gzipBytes": 294561,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-vj2e8G-y.js",
"houseplan-assets/editor-B4uO_Hit.js",
"houseplan-assets/fr-B2dlk0Va.js",
"houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js",
"houseplan-assets/hp-zigbee-topology-overlay-rOIQtlN5.js"
"houseplan-assets/de-DFeGpePx.js",
"houseplan-assets/editor-CCrJdJ9P.js",
"houseplan-assets/fr-B-xb3XPO.js",
"houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js",
"houseplan-assets/hp-zigbee-topology-overlay-BebiHDnX.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-Bya6M8Zb.js",
"sha256": "066fb86f20424f03b246d5ae8055c9e0ee282c103ae5a13339a95450a52a7e49",
"path": "houseplan-assets/houseplan-editor-runtime-BjPEC4JN.js",
"sha256": "0132e5d538d9bd4a99efd2b8f86b6e01bf465c9be1de72a0399b114204a94189",
"rawBytes": 608944,
"gzipBytes": 160244,
"gzipBytes": 160238,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js"
],
"dynamicImports": [
"houseplan-assets/zigbee-topology-runtime-DHrStvLy.js"
"houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js"
]
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BZHqs9mL.js",
"sha256": "01fa5e1aa7ed3a37a62183d62b79b99b43572e5ec81eb5873fab4ca1ebfa0413",
"path": "houseplan-assets/houseplan-onboarding-runtime-zzJPsMn1.js",
"sha256": "64f4fcf484d2e23b30c79c2a6b03c8af684da972f583151764e4957e391c1d1c",
"rawBytes": 28088,
"gzipBytes": 6955,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-DVnyEcZa.js",
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/backdrop-pick-MVjPRxdI.js",
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/hp-zigbee-topology-overlay-rOIQtlN5.js",
"sha256": "14e0452ab14bdd3bf27e1f8194825faacd7c5919aa1975cb887a7c3db20eb6a4",
"path": "houseplan-assets/hp-zigbee-topology-overlay-BebiHDnX.js",
"sha256": "ec91560c32a0fbe0af6e066a5cbde4de470ece320b6a189fcdbbb1d690477e53",
"rawBytes": 6007,
"gzipBytes": 2380,
"gzipBytes": 2378,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js",
"houseplan-assets/zigbee-topology-runtime-DHrStvLy.js"
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js",
"houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-BoNFdBaW.js",
"sha256": "ab6139986f9dcc63cd9ca62fac1f31bc19622d47e0131f0a9453812870702d97",
"rawBytes": 12072,
"gzipBytes": 4746,
"path": "houseplan-assets/zigbee-topology-CjJau-_w.js",
"sha256": "44bdc047f04343477b564cdd5515860c2ac58827c78b4990d13e93aaf80d43f5",
"rawBytes": 12250,
"gzipBytes": 4812,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/zigbee-topology-runtime-DHrStvLy.js",
"sha256": "0c061e55b92f9a73ebf8c97da4dad736d9408eecaba503a11abcac01ae1d8d69",
"rawBytes": 3817,
"gzipBytes": 1696,
"path": "houseplan-assets/zigbee-topology-runtime-ne8Tm9gB.js",
"sha256": "e09fdfac0d02509ebb3b65739eb5e148db5b328f607b6e03d6ccd03abb81d076",
"rawBytes": 3878,
"gzipBytes": 1726,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js",
"houseplan-assets/zigbee-topology-BoNFdBaW.js"
"houseplan-assets/houseplan-card-DUGW724g.js",
"houseplan-assets/zigbee-topology-CjJau-_w.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "3d510618b0952ac50f9ac166f4751b604cd0a719fba428dd9eed771feecdb097",
"sha256": "bd8bbfbfe67ce7465845f9c52f0ab2c7678e9450cd9f238b2bb5714979f937f0",
"rawBytes": 1183,
"gzipBytes": 796,
"gzipBytes": 794,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-BcCJlm2v.js"
"houseplan-assets/houseplan-card-DUGW724g.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,14 +1,14 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";import{k as e,l as o,t,dq as s,A as a,dr as i,ds as l,dt as r,E as n,c}from"./houseplan-card-BcCJlm2v.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 r="string"==typeof this._config?.default_floor?this._config.default_floor:"",n=[...e];return r&&!e.some(e=>e.value===r)&&n.unshift({value:r,label:r}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:n}}}:{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 r();if("warm"===e)return n;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__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";import{k as e,l as o,t,dq as s,A as a,dr as i,ds as l,dt as r,E as n,c}from"./houseplan-card-DUGW724g.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 r="string"==typeof this._config?.default_floor?this._config.default_floor:"",n=[...e];return r&&!e.some(e=>e.value===r)&&n.unshift({value:r,label:r}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:n}}}:{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 r();if("warm"===e)return n;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
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";import{k as e,A as t,du as i,aw as s,c as o,l as r,j as n}from"./houseplan-card-BcCJlm2v.js";import{a,r as h,t as d}from"./zigbee-topology-BoNFdBaW.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-DHrStvLy.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o`
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";import{k as e,A as t,du as i,aw as s,c as o,l as r,j as n}from"./houseplan-card-DUGW724g.js";import{a,r as h,t as d}from"./zigbee-topology-CjJau-_w.js";import{zigbeeTopologyRuntimeSnapshot as p,subscribeZigbeeTopology as c}from"./zigbee-topology-runtime-ne8Tm9gB.js";const l={revision:0,topologies:[],states:{}};class v extends e{constructor(){super(...arguments),this.devices=[],this.currentSpace="",this._runtime=l,this._hovered="",this._pointerOver=e=>{if(!this._mouseAllowed(e))return void this._clear();const t=this._deviceFromEvent(e),i=t?.dataset.id||"";i&&i!==this._hovered&&(this._hovered=i,this.requestUpdate())},this._pointerOut=e=>{const t=this._deviceFromEvent(e);if(!t||t.dataset.id!==this._hovered)return;const i=e.relatedTarget instanceof Element?e.relatedTarget.closest('[data-hp="device"]'):null;i?.dataset.id!==this._hovered&&this._clear()},this._pointerDown=e=>{"mouse"!==e.pointerType&&this._clear()}}connectedCallback(){super.connectedCallback();const e=this.getRootNode();e.host&&"undefined"!=typeof MutationObserver&&(this._hoverGateObserver=new MutationObserver(()=>{e.host.hasAttribute("data-pointer-hover")||this._clear()}),this._hoverGateObserver.observe(e.host,{attributes:!0,attributeFilter:["data-pointer-hover"]})),queueMicrotask(()=>this._connectParent())}disconnectedCallback(){this._release?.(),this._release=void 0,this._disconnectParent(),this._hoverGateObserver?.disconnect(),this._hoverGateObserver=void 0,this._hovered="",super.disconnectedCallback()}updated(e){e.has("hass")&&(this._release?.(),this._runtime=p(this.hass),this._mappedMemo=void 0,this._release=c(this.hass,()=>{this._runtime=p(this.hass),this._mappedMemo=void 0,this.requestUpdate()})),(e.has("currentSpace")||e.has("devices")||e.has("registry"))&&(this._mappedMemo=void 0,this.devices.some(e=>e.id===this._hovered&&e.space===this.currentSpace)||(this._hovered=""))}_connectParent(){const e=this.parentElement;e&&e!==this._parent&&(this._disconnectParent(),this._parent=e,e.addEventListener("pointerover",this._pointerOver),e.addEventListener("pointerout",this._pointerOut),e.addEventListener("pointerdown",this._pointerDown,!0))}_disconnectParent(){this._parent?.removeEventListener("pointerover",this._pointerOver),this._parent?.removeEventListener("pointerout",this._pointerOut),this._parent?.removeEventListener("pointerdown",this._pointerDown,!0),this._parent=void 0}_deviceFromEvent(e){for(const t of e.composedPath())if(t instanceof HTMLElement&&"device"===t.dataset.hp)return t;return null}_mouseAllowed(e){const t=this.getRootNode();return"mouse"===e.pointerType&&t.host?.hasAttribute?.("data-pointer-hover")}_clear(){this._hovered&&(this._hovered="",this.requestUpdate())}_position(e){const t=[...this._parent?.querySelectorAll(".dev[data-id]")||[]].find(t=>t.dataset.id===e);if(!t)return null;const i=Number.parseFloat(t.style.left),s=Number.parseFloat(t.style.top),o=t.getBoundingClientRect();return Number.isFinite(i)&&Number.isFinite(s)?{x:i,y:s,width:o.width,height:o.height}:null}render(){if(!this._hovered||!this.registry||!this._runtime.topologies.length)return t;const e=this._mappedMemo,n=e&&e.runtime===this._runtime&&e.devices===this.devices&&e.registry===this.registry?e.mapped:a(this._runtime.topologies,this.devices,this.registry);e&&n===e.mapped||(this._mappedMemo={runtime:this._runtime,devices:this.devices,registry:this.registry,mapped:n});const p=h(n,this.currentSpace,this._hovered),c=this._position(this._hovered);if(!c)return t;const l=p.lines.map(e=>({...e,point:this._position(e.neighborMarkerId)})).filter(e=>!!e.point);return o`
${l.length?s`<svg viewBox="0 0 100 100" preserveAspectRatio="none"
aria-hidden="true" data-hp="zigbee-topology-lines">
${l.map(e=>s`<line x1=${c.x} y1=${c.y}
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";import{n as t,b as n}from"./zigbee-topology-BoNFdBaW.js";import{y as e}from"./houseplan-card-BcCJlm2v.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 u(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function l(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){const n=t?.payload??t;if("string"!=typeof n)return n;try{return JSON.parse(n)}catch{return null}}async function h(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function m(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const u=e(o);return c(s,`z2m:${u||String(o)}`,async()=>{if(f(t),!u)throw a("invalid_topic");const e=t?.connection?.subscribeMessage;if("function"!=typeof e||"function"!=typeof t?.callService)throw a("unsupported");const o=function(){const t=globalThis.crypto;return"function"==typeof t?.randomUUID?`houseplan-${t.randomUUID()}`:`houseplan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}(),i=Date.now()+Math.max(1,r);let s=null,c=null;const l=new Promise(t=>{s=t}),p=new Promise(t=>{c=t}),m=[];try{const r=await e.call(t.connection,t=>{!0===t?.retain&&d(t)&&s?.()},{type:"mqtt/subscribe",topic:`${u}/bridge/info`});"function"==typeof r&&m.push(r);const f=await e.call(t.connection,t=>{if(!0===t?.retain)return;const n=d(t);n&&function(t){const n=t?.transaction??t?.data?.transaction;return"string"==typeof n||"number"==typeof n?String(n):null}(n)===o&&c?.(n)},{type:"mqtt/subscribe",topic:`${u}/bridge/response/networkmap`});"function"==typeof f&&m.push(f),await h(l,Math.min(4e3,Math.max(1,i-Date.now()))),await t.callService("mqtt","publish",{topic:`${u}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:o}),qos:0,retain:!1});const g=await h(p,i-Date.now());if(g?.status&&"ok"!==g.status)throw a("provider");return n(g,u)}finally{for(const t of m)try{t()}catch{}}})}export{p as readZhaTopology,m as refreshZ2mTopology,l as subscribeZigbeeTopology,u as zigbeeTopologyRuntimeSnapshot};
@@ -0,0 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";import{n as t,b as n}from"./zigbee-topology-CjJau-_w.js";import{y as e}from"./houseplan-card-DUGW724g.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 u(t){const n=i(t);return n?{revision:n.revision,topologies:n.topologies,states:n.states}:{revision:0,topologies:[],states:{}}}function l(t,n){const e=i(t);return e?(e.listeners.add(n),()=>e.listeners.delete(n)):()=>{}}function p(t){if(!0!==t?.user?.is_admin)throw a("permission")}function f(n){const e=i(n);return e?c(e,"zha",async()=>{if(p(n),"function"!=typeof n?.callWS)throw a("unsupported");return t(await n.callWS({type:"zha/devices"}))}):Promise.resolve()}function d(t){return null!==t&&"object"==typeof t?t:null}function h(t){const n=d(t),e=n?.payload??t;if("string"!=typeof e)return e;try{return JSON.parse(e)}catch{return null}}async function m(t,n){let e;try{return await Promise.race([t,new Promise((t,o)=>{e=globalThis.setTimeout(()=>o(a("timeout")),Math.max(1,n))})])}finally{void 0!==e&&globalThis.clearTimeout(e)}}function g(t,o,r=15e4){const s=i(t);if(!s)return Promise.resolve();const u=e(o);return c(s,`z2m:${u||String(o)}`,async()=>{if(p(t),!u)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=null,l=null;const f=new Promise(t=>{c=t}),g=new Promise(t=>{l=t}),y=[];try{const r=await o.call(e,t=>{!0===d(t)?.retain&&h(t)&&c?.()},{type:"mqtt/subscribe",topic:`${u}/bridge/info`});"function"==typeof r&&y.push(r);const p=await o.call(e,t=>{if(!0===d(t)?.retain)return;const n=h(t);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&&l?.(n)},{type:"mqtt/subscribe",topic:`${u}/bridge/response/networkmap`});"function"==typeof p&&y.push(p),await m(f,Math.min(4e3,Math.max(1,s-Date.now()))),await t.callService("mqtt","publish",{topic:`${u}/bridge/request/networkmap`,payload:JSON.stringify({type:"raw",routes:!1,transaction:i}),qos:0,retain:!1});const w=await m(g,s-Date.now()),b=d(w)?.status;if(b&&"ok"!==b)throw a("provider");return n(w,u)}finally{for(const t of y)try{t()}catch{}}})}export{f as readZhaTopology,g as refreshZ2mTopology,l as subscribeZigbeeTopology,u as zigbeeTopologyRuntimeSnapshot};
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="355345b81df5df7c2cef0804f6844712b1f27064f0ac17c14c8255e18c05c73c";try{await import("./houseplan-assets/houseplan-card-BcCJlm2v.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 entry: the main chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4bbc03b3b6fe5e661d2b8bb8d6831e9cccfae5d526d96a3d87c624189f96a3e";try{await import("./houseplan-assets/houseplan-card-DUGW724g.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 entry: the main chunk is unavailable",e)}
+1 -1
View File
@@ -10264,7 +10264,7 @@ public _updateDecorStyle(next: DecorStyle): void {
try {
const cfg = this.host._serverCfg!;
const isDefault = JSON.stringify(d.colors) === JSON.stringify(DEFAULT_FILL_COLORS);
let settings: any = { ...cfg.settings };
let settings: ServerConfig['settings'] & Record<string, unknown> = { ...cfg.settings };
if (isDefault) delete settings.fill_colors;
else settings.fill_colors = d.colors;
const cm = this.host._imperial ? d.glowRadius * 30.48 : d.glowRadius * 100;
+2 -2
View File
@@ -7,7 +7,7 @@ import {
} from './zigbee-topology';
import {
subscribeZigbeeTopology, zigbeeTopologyRuntimeSnapshot,
type ZigbeeTopologyRuntimeSnapshot,
type ZigbeeTopologyHass, type ZigbeeTopologyRuntimeSnapshot,
} from './zigbee-topology-runtime';
import type { HaRegistrySnapshot } from './ha-binding-status';
import type { DevItem } from './types';
@@ -24,7 +24,7 @@ export class HpZigbeeTopologyOverlay extends LitElement {
viewKey: { attribute: false },
};
hass: any;
hass!: ZigbeeTopologyHass;
devices: readonly DevItem[] = [];
registry!: HaRegistrySnapshot;
currentSpace = '';
+2 -2
View File
@@ -8,7 +8,7 @@ import { mapTopologyNodes, TOPOLOGY_STALE_MS } from './zigbee-topology';
import type { DevItem } from './types';
import type { HaRegistrySnapshot } from './ha-binding-status';
import type {
ZigbeeProviderState, ZigbeeTopologyRuntimeSnapshot,
ZigbeeProviderState, ZigbeeTopologyHass, ZigbeeTopologyRuntimeSnapshot,
} from './zigbee-topology-runtime';
const EMPTY_RUNTIME: ZigbeeTopologyRuntimeSnapshot = { revision: 0, topologies: [], states: {} };
@@ -22,7 +22,7 @@ export class HpZigbeeTopologySettings extends LitElement {
registry: { attribute: false },
};
hass: any;
hass!: ZigbeeTopologyHass;
value: ZigbeeTopologySettings = { enabled: false, z2mBaseTopics: [] };
savedEnabled = false;
devices: readonly DevItem[] = [];
+2 -1
View File
@@ -2,9 +2,10 @@ import { html, nothing, type TemplateResult } from 'lit';
import { zigbeeTopologySettingsOf } from './zigbee-topology-settings';
import type { HaRegistrySnapshot } from './ha-binding-status';
import type { DevItem } from './types';
import type { ZigbeeTopologyHass } from './zigbee-topology-runtime';
export function renderZigbeeTopologyOverlay(input: {
hass: any;
hass: ZigbeeTopologyHass;
settings: unknown;
devices: readonly DevItem[];
registry: HaRegistrySnapshot;
+47 -20
View File
@@ -22,6 +22,21 @@ export interface ZigbeeTopologyRuntimeSnapshot {
states: Record<string, ZigbeeProviderState>;
}
export interface ZigbeeTopologyHass {
user?: { is_admin?: boolean };
language?: string;
connection?: {
subscribeMessage?: (
callback: (message: unknown) => void,
message: { type: 'mqtt/subscribe'; topic: string },
) => Promise<() => void>;
};
callWS?: (message: { type: string }) => Promise<unknown>;
callService?: (
domain: string, service: string, data: Record<string, unknown>,
) => Promise<unknown>;
}
type Cache = ZigbeeTopologyRuntimeSnapshot & {
listeners: Set<() => void>;
inflight: Map<string, Promise<void>>;
@@ -29,12 +44,12 @@ type Cache = ZigbeeTopologyRuntimeSnapshot & {
const caches = new WeakMap<object, Cache>();
function keyOf(hass: any): object | null {
function keyOf(hass: ZigbeeTopologyHass | null | undefined): object | null {
const key = hass?.connection || hass;
return key && (typeof key === 'object' || typeof key === 'function') ? key : null;
}
function cacheOf(hass: any): Cache | null {
function cacheOf(hass: ZigbeeTopologyHass | null | undefined): Cache | null {
const key = keyOf(hass);
if (!key) return null;
let cache = caches.get(key);
@@ -70,7 +85,7 @@ function errorCode(error: unknown): ZigbeeTopologyErrorCode {
const code = (error as { code?: unknown } | null)?.code;
if (code === 'permission' || code === 'unsupported' || code === 'timeout'
|| code === 'invalid_topic' || code === 'invalid_payload') return code;
const message = String((error as any)?.message || '').toLowerCase();
const message = String((error as { message?: unknown } | null)?.message || '').toLowerCase();
if (message.includes('unauthor') || message.includes('permission')) return 'permission';
if (message.includes('unknown_command') || message.includes('not found')) return 'unsupported';
return 'provider';
@@ -96,26 +111,30 @@ function run(cache: Cache, key: string, task: () => Promise<ZigbeeTopology>): Pr
return promise;
}
export function zigbeeTopologyRuntimeSnapshot(hass: any): ZigbeeTopologyRuntimeSnapshot {
export function zigbeeTopologyRuntimeSnapshot(
hass: ZigbeeTopologyHass | null | undefined,
): ZigbeeTopologyRuntimeSnapshot {
const cache = cacheOf(hass);
return cache
? { revision: cache.revision, topologies: cache.topologies, states: cache.states }
: { revision: 0, topologies: [], states: {} };
}
export function subscribeZigbeeTopology(hass: any, listener: () => void): () => void {
export function subscribeZigbeeTopology(
hass: ZigbeeTopologyHass | null | undefined, listener: () => void,
): () => void {
const cache = cacheOf(hass);
if (!cache) return () => undefined;
cache.listeners.add(listener);
return () => cache.listeners.delete(listener);
}
function requireAdmin(hass: any): void {
function requireAdmin(hass: ZigbeeTopologyHass | null | undefined): void {
if (hass?.user?.is_admin !== true) throw fail('permission');
}
/** Read the cached ZHA graph. This must never call zha/topology/update. */
export function readZhaTopology(hass: any): Promise<void> {
export function readZhaTopology(hass: ZigbeeTopologyHass): Promise<void> {
const cache = cacheOf(hass);
if (!cache) return Promise.resolve();
return run(cache, 'zha', async () => {
@@ -125,14 +144,20 @@ export function readZhaTopology(hass: any): Promise<void> {
});
}
function parseMessage(message: any): any {
const raw = message?.payload ?? message;
function recordOf(value: unknown): Record<string, unknown> | null {
return value !== null && typeof value === 'object' ? value as Record<string, unknown> : null;
}
function parseMessage(message: unknown): unknown {
const record = recordOf(message);
const raw = record?.payload ?? message;
if (typeof raw !== 'string') return raw;
try { return JSON.parse(raw); } catch { return null; }
}
function transactionOf(value: any): string | null {
const transaction = value?.transaction ?? value?.data?.transaction;
function transactionOf(value: unknown): string | null {
const record = recordOf(value);
const transaction = record?.transaction ?? recordOf(record?.data)?.transaction;
return typeof transaction === 'string' || typeof transaction === 'number'
? String(transaction) : null;
}
@@ -159,7 +184,7 @@ async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
/** Explicit, completion-aware Z2M raw network-map request through HA MQTT. */
export function refreshZ2mTopology(
hass: any, baseTopic: string, timeoutMs = 150_000,
hass: ZigbeeTopologyHass, baseTopic: string, timeoutMs = 150_000,
): Promise<void> {
const cache = cacheOf(hass);
if (!cache) return Promise.resolve();
@@ -168,22 +193,23 @@ export function refreshZ2mTopology(
return run(cache, cacheKey, async () => {
requireAdmin(hass);
if (!topic) throw fail('invalid_topic');
const subscribe = hass?.connection?.subscribeMessage;
const connection = hass.connection;
const subscribe = connection?.subscribeMessage;
if (typeof subscribe !== 'function' || typeof hass?.callService !== 'function') throw fail('unsupported');
const transaction = randomTransaction();
const deadline = Date.now() + Math.max(1, timeoutMs);
let infoResolve: (() => void) | null = null;
let responseResolve: ((value: any) => void) | null = null;
let responseResolve: ((value: unknown) => void) | null = null;
const info = new Promise<void>((resolve) => { infoResolve = resolve; });
const response = new Promise<any>((resolve) => { responseResolve = resolve; });
const response = new Promise<unknown>((resolve) => { responseResolve = resolve; });
const unsubscribers: Array<() => void> = [];
try {
const unsubInfo = await subscribe.call(hass.connection, (message: any) => {
if (message?.retain === true && parseMessage(message)) infoResolve?.();
const unsubInfo = await subscribe.call(connection, (message: unknown) => {
if (recordOf(message)?.retain === true && parseMessage(message)) infoResolve?.();
}, { type: 'mqtt/subscribe', topic: `${topic}/bridge/info` });
if (typeof unsubInfo === 'function') unsubscribers.push(unsubInfo);
const unsubResponse = await subscribe.call(hass.connection, (message: any) => {
if (message?.retain === true) return;
const unsubResponse = await subscribe.call(connection, (message: unknown) => {
if (recordOf(message)?.retain === true) return;
const value = parseMessage(message);
if (value && transactionOf(value) === transaction) responseResolve?.(value);
}, { type: 'mqtt/subscribe', topic: `${topic}/bridge/response/networkmap` });
@@ -196,7 +222,8 @@ export function refreshZ2mTopology(
retain: false,
});
const value = await withTimeout(response, deadline - Date.now());
if (value?.status && value.status !== 'ok') throw fail('provider');
const status = recordOf(value)?.status;
if (status && status !== 'ok') throw fail('provider');
return normalizeZ2mTopology(value, topic);
} finally {
for (const unsubscribe of unsubscribers) {
+48 -32
View File
@@ -65,6 +65,10 @@ export const TOPOLOGY_MAX_PAYLOAD_BYTES = 2 * 1024 * 1024;
export const TOPOLOGY_MAX_NODES = 1000;
export const TOPOLOGY_MAX_LINKS = 6000;
function recordOf(value: unknown): Record<string, unknown> | null {
return value !== null && typeof value === 'object' ? value as Record<string, unknown> : null;
}
export function normalizeIeee(value: unknown): string | null {
if (typeof value !== 'string' && typeof value !== 'number') return null;
let text = String(value).trim().toLowerCase();
@@ -90,14 +94,15 @@ function lqiOf(value: unknown): number | undefined {
return Number.isFinite(n) && n >= 0 && n <= 255 ? Math.round(n) : undefined;
}
function observation(value: any): ZigbeeDirectionalObservation {
const lqi = lqiOf(value?.lqi ?? value?.linkquality ?? value?.link_quality);
const relationship = typeof value?.relationship === 'string'
? value.relationship.slice(0, 40) : undefined;
function observation(value: unknown): ZigbeeDirectionalObservation {
const record = recordOf(value);
const lqi = lqiOf(record?.lqi ?? record?.linkquality ?? record?.link_quality);
const relationship = typeof record?.relationship === 'string'
? record.relationship.slice(0, 40) : undefined;
return { ...(lqi === undefined ? {} : { lqi }), ...(relationship ? { relationship } : {}) };
}
function boundedArray(value: unknown, max: number): any[] | null {
function boundedArray(value: unknown, max: number): unknown[] | null {
return Array.isArray(value) && value.length <= max ? value : null;
}
@@ -108,7 +113,7 @@ function safePayload(value: unknown): boolean {
function pushDirectionalLink(
links: Map<string, ZigbeeTopologyLink>, warnings: ZigbeeTopologyWarning[],
from: string, to: string, value: any,
from: string, to: string, value: unknown,
): void {
if (from === to) { warnings.push({ code: 'self_link', nodeKey: from }); return; }
const forward = from < to;
@@ -129,28 +134,31 @@ export function normalizeZhaTopology(payload: unknown, now = Date.now()): Zigbee
const nodes = new Map<string, ZigbeeTopologyNode>();
const links = new Map<string, ZigbeeTopologyLink>();
for (const row of rows) {
const ieee = normalizeIeee(row?.ieee ?? row?.ieee_address);
const record = recordOf(row);
const ieee = normalizeIeee(record?.ieee ?? record?.ieee_address);
if (!ieee) { warnings.push({ code: 'invalid_payload' }); continue; }
const key = nodeKey('zha', 'zha', ieee);
nodes.set(key, {
key, ieee,
...(typeof row?.device_reg_id === 'string' ? { deviceId: row.device_reg_id } : {}),
role: roleOf(row?.device_type ?? row?.type),
...(typeof row?.available === 'boolean' ? { available: row.available } : {}),
...(typeof record?.device_reg_id === 'string' ? { deviceId: record.device_reg_id } : {}),
role: roleOf(record?.device_type ?? record?.type),
...(typeof record?.available === 'boolean' ? { available: record.available } : {}),
});
}
for (const row of rows) {
const ieee = normalizeIeee(row?.ieee ?? row?.ieee_address);
const record = recordOf(row);
const ieee = normalizeIeee(record?.ieee ?? record?.ieee_address);
if (!ieee) continue;
const from = nodeKey('zha', 'zha', ieee);
const neighbors = boundedArray(row?.neighbors, TOPOLOGY_MAX_LINKS);
if (!neighbors) { if (row?.neighbors != null) warnings.push({ code: 'invalid_payload', nodeKey: from }); continue; }
const neighbors = boundedArray(record?.neighbors, TOPOLOGY_MAX_LINKS);
if (!neighbors) { if (record?.neighbors != null) warnings.push({ code: 'invalid_payload', nodeKey: from }); continue; }
for (const neighbor of neighbors) {
if (links.size >= TOPOLOGY_MAX_LINKS) { warnings.push({ code: 'invalid_payload' }); break; }
const otherIeee = normalizeIeee(neighbor?.ieee ?? neighbor?.ieee_address);
const neighborRecord = recordOf(neighbor);
const otherIeee = normalizeIeee(neighborRecord?.ieee ?? neighborRecord?.ieee_address);
if (!otherIeee) { warnings.push({ code: 'invalid_payload', nodeKey: from }); continue; }
const to = nodeKey('zha', 'zha', otherIeee);
if (!nodes.has(to)) nodes.set(to, { key: to, ieee: otherIeee, role: roleOf(neighbor?.device_type) });
if (!nodes.has(to)) nodes.set(to, { key: to, ieee: otherIeee, role: roleOf(neighborRecord?.device_type) });
pushDirectionalLink(links, warnings, from, to, neighbor);
}
}
@@ -158,13 +166,15 @@ export function normalizeZhaTopology(payload: unknown, now = Date.now()): Zigbee
freshness: 'provider-cache', nodes: [...nodes.values()], links: [...links.values()], warnings };
}
function z2mValue(payload: any): any {
function z2mValue(payload: unknown): unknown {
let value = payload;
if (typeof value === 'string') {
if (value.length > TOPOLOGY_MAX_PAYLOAD_BYTES) return null;
try { value = JSON.parse(value); } catch { return null; }
}
value = value?.data?.value ?? value?.value ?? value?.data ?? value;
const envelope = recordOf(value);
const data = recordOf(envelope?.data);
value = data?.value ?? envelope?.value ?? envelope?.data ?? value;
if (typeof value === 'string') {
if (value.length > TOPOLOGY_MAX_PAYLOAD_BYTES) return null;
try { value = JSON.parse(value); } catch { return null; }
@@ -172,10 +182,11 @@ function z2mValue(payload: any): any {
return value;
}
function endpointIeee(value: any, nodeById: Map<string, string>): string | null {
const direct = normalizeIeee(value?.ieee_address ?? value?.ieee ?? value);
function endpointIeee(value: unknown, nodeById: Map<string, string>): string | null {
const record = recordOf(value);
const direct = normalizeIeee(record?.ieee_address ?? record?.ieee ?? value);
if (direct) return direct;
const id = value?.id ?? value?.network_address ?? value;
const id = record?.id ?? record?.network_address ?? value;
return id == null ? null : nodeById.get(String(id)) || null;
}
@@ -184,24 +195,27 @@ export function normalizeZ2mTopology(payload: unknown, baseTopic: string, now =
const warnings: ZigbeeTopologyWarning[] = [];
const topic = normalizeZ2mBaseTopic(baseTopic) || 'invalid';
const raw = safePayload(payload) ? z2mValue(payload) : null;
const rows = boundedArray(raw?.nodes, TOPOLOGY_MAX_NODES);
const rawLinks = boundedArray(raw?.links, TOPOLOGY_MAX_LINKS);
const rawRecord = recordOf(raw);
const rows = boundedArray(rawRecord?.nodes, TOPOLOGY_MAX_NODES);
const rawLinks = boundedArray(rawRecord?.links, TOPOLOGY_MAX_LINKS);
if (!rows || !rawLinks) return { provider: 'z2m', instanceId: topic, obtainedAt: now,
freshness: 'fresh-scan', nodes: [], links: [], warnings: [{ code: 'invalid_payload' }] };
const nodes = new Map<string, ZigbeeTopologyNode>();
const nodeById = new Map<string, string>();
for (const row of rows) {
const ieee = normalizeIeee(row?.ieee_address ?? row?.ieee);
const record = recordOf(row);
const ieee = normalizeIeee(record?.ieee_address ?? record?.ieee);
if (!ieee) { warnings.push({ code: 'invalid_payload' }); continue; }
const key = nodeKey('z2m', topic, ieee);
nodes.set(key, { key, ieee, role: roleOf(row?.type ?? row?.device_type),
...(typeof row?.failed === 'boolean' ? { available: !row.failed } : {}) });
for (const id of [row?.id, row?.network_address]) if (id != null) nodeById.set(String(id), ieee);
nodes.set(key, { key, ieee, role: roleOf(record?.type ?? record?.device_type),
...(typeof record?.failed === 'boolean' ? { available: !record.failed } : {}) });
for (const id of [record?.id, record?.network_address]) if (id != null) nodeById.set(String(id), ieee);
}
const links = new Map<string, ZigbeeTopologyLink>();
for (const row of rawLinks) {
const fromIeee = endpointIeee(row?.source, nodeById);
const toIeee = endpointIeee(row?.target, nodeById);
const record = recordOf(row);
const fromIeee = endpointIeee(record?.source, nodeById);
const toIeee = endpointIeee(record?.target, nodeById);
if (!fromIeee || !toIeee) { warnings.push({ code: 'invalid_payload' }); continue; }
const from = nodeKey('z2m', topic, fromIeee);
const to = nodeKey('z2m', topic, toIeee);
@@ -224,14 +238,16 @@ function ieeeFromRegistryIdentifier(value: unknown): string | null {
function deviceIdsForNode(node: ZigbeeTopologyNode, registry: HaRegistrySnapshot): string[] {
if (node.deviceId && registry.devices[node.deviceId]) return [node.deviceId];
const ids = new Set<string>();
for (const [id, device] of Object.entries<any>(registry.devices)) {
for (const [id, rawDevice] of Object.entries(registry.devices as Record<string, unknown>)) {
const device = recordOf(rawDevice);
const identifiers = Array.isArray(device?.identifiers) ? device.identifiers : [];
if (identifiers.some((pair: any) => Array.isArray(pair)
if (identifiers.some((pair: unknown) => Array.isArray(pair)
&& ieeeFromRegistryIdentifier(pair[1]) === node.ieee)) ids.add(id);
}
if (ids.size) return [...ids];
for (const entity of Object.values<any>(registry.entities)) {
if (!entity?.device_id || typeof entity?.unique_id !== 'string') continue;
for (const rawEntity of Object.values(registry.entities as Record<string, unknown>)) {
const entity = recordOf(rawEntity);
if (typeof entity?.device_id !== 'string' || typeof entity.unique_id !== 'string') continue;
if (ieeeFromRegistryIdentifier(entity.unique_id) === node.ieee) ids.add(entity.device_id);
}
return [...ids];