fix: type the new vacuum routing code and add map_routes to the config schema

User-Visible: no
Issue: #162
This commit is contained in:
Codex
2026-09-03 19:41:55 +03:00
parent 4032b810b7
commit 8e2892b95f
28 changed files with 256 additions and 199 deletions
@@ -1,125 +1,125 @@
{ {
"schema": 1, "schema": 1,
"fingerprint": "eeba3bd848dcc963129aef216061c60ac878ad129c553317d8bfd01ff5c8c50e", "fingerprint": "fb1a7d387b2900fb1a108215450e0b1cb0c649e3fe4cb21ba558be186c23c71b",
"entry": "houseplan-card.js", "entry": "houseplan-card.js",
"initialViewFiles": [ "initialViewFiles": [
"houseplan-assets/houseplan-card-D8zflggZ.js", "houseplan-assets/houseplan-card-6G7OmtFg.js",
"houseplan-card.js" "houseplan-card.js"
], ],
"initialViewGzipBytes": 292971, "initialViewGzipBytes": 292971,
"lazyFiles": [ "lazyFiles": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/de-CsRas6jx.js", "houseplan-assets/de-IEcTyTk-.js",
"houseplan-assets/editor-S0HhJYix.js", "houseplan-assets/editor-CmKrBzQX.js",
"houseplan-assets/fr-DdgK8UIB.js", "houseplan-assets/fr-DBwRNHhe.js",
"houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js", "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js",
"houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js" "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js"
], ],
"lazyGzipBytes": 219799, "lazyGzipBytes": 219796,
"lazyEditorFiles": [ "lazyEditorFiles": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/editor-S0HhJYix.js", "houseplan-assets/editor-CmKrBzQX.js",
"houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js" "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js"
], ],
"lazyEditorGzipBytes": 163776, "lazyEditorGzipBytes": 163777,
"lazyOnboardingFiles": [ "lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js" "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js"
], ],
"lazyOnboardingGzipBytes": 14230, "lazyOnboardingGzipBytes": 14226,
"lazyLocaleFiles": [ "lazyLocaleFiles": [
"houseplan-assets/de-CsRas6jx.js", "houseplan-assets/de-IEcTyTk-.js",
"houseplan-assets/fr-DdgK8UIB.js" "houseplan-assets/fr-DBwRNHhe.js"
], ],
"lazyLocaleGzipBytes": 49071, "lazyLocaleGzipBytes": 49069,
"files": [ "files": [
{ {
"path": "houseplan-assets/backdrop-pick-BLxmEavR.js", "path": "houseplan-assets/backdrop-pick-O29mDmnF.js",
"sha256": "7ddf179e36cbde0bf7e9022b0c485fbea78ef6b795353258a61a79f44b1deeaa", "sha256": "e6cf619bbdb1c9a849f3169bc390b01cd69a9357c22e2a62bc4aed8856d400d6",
"rawBytes": 21378, "rawBytes": 21418,
"gzipBytes": 7278, "gzipBytes": 7276,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/de-CsRas6jx.js", "path": "houseplan-assets/de-IEcTyTk-.js",
"sha256": "e2aa870d33f74396775b0967bd51c772a4f832f4fec49d90db419a58dc1440d2", "sha256": "d5feb2e10ba499dcbebe492ead8ee7d20fd2d83e2514b724e607d7668e65daee",
"rawBytes": 82140, "rawBytes": 82140,
"gzipBytes": 24805, "gzipBytes": 24804,
"isEntry": false, "isEntry": false,
"imports": [], "imports": [],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/editor-S0HhJYix.js", "path": "houseplan-assets/editor-CmKrBzQX.js",
"sha256": "fdbe9fc3fc2951da28e3c0c670a0032cd8f090bbf233d1a134a3b170c6543eef", "sha256": "c05ed91e68c0a2a70f7732822f9ff87c9713cc601680c6fe5b4b597bd4ef46a3",
"rawBytes": 3826, "rawBytes": 3826,
"gzipBytes": 1581, "gzipBytes": 1578,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/fr-DdgK8UIB.js", "path": "houseplan-assets/fr-DBwRNHhe.js",
"sha256": "aad556cbbf53185d79c761d65ad8e12635d78e3a309e417285d09d56656a3c82", "sha256": "b7aaa4f782027a4ff175682ca84f7dc605123f96c3d4712e12f7d09647b65284",
"rawBytes": 84267, "rawBytes": 84267,
"gzipBytes": 24266, "gzipBytes": 24265,
"isEntry": false, "isEntry": false,
"imports": [], "imports": [],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/houseplan-card-D8zflggZ.js", "path": "houseplan-assets/houseplan-card-6G7OmtFg.js",
"sha256": "857327b87b7b74ea580699c5023efbf35c59bafada3f65a4f1ca3ed1414fa9ec", "sha256": "ce95ec4e5177e6e945f2a9ec87dfe552d9a5a62a01f09bdd14b258743290f130",
"rawBytes": 1039585, "rawBytes": 1039585,
"gzipBytes": 292176, "gzipBytes": 292176,
"isEntry": false, "isEntry": false,
"imports": [], "imports": [],
"dynamicImports": [ "dynamicImports": [
"houseplan-assets/de-CsRas6jx.js", "houseplan-assets/de-IEcTyTk-.js",
"houseplan-assets/editor-S0HhJYix.js", "houseplan-assets/editor-CmKrBzQX.js",
"houseplan-assets/fr-DdgK8UIB.js", "houseplan-assets/fr-DBwRNHhe.js",
"houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js", "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js",
"houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js" "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js"
] ]
}, },
{ {
"path": "houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js", "path": "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js",
"sha256": "23baee7febc624f323b1850067aca264554abdd4995a6e90c786d44524233881", "sha256": "e8ab0b4106df9b7c16cd1d94bc42b41b98a66af12f6510ce7ec04fce88cc9bcd",
"rawBytes": 590156, "rawBytes": 590169,
"gzipBytes": 154917, "gzipBytes": 154923,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js", "path": "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js",
"sha256": "03ce43da4034c5dc3e05e0946b39e68aeeece3aef0cff782eb27ca3c8033a3c5", "sha256": "d93dc2524abe1caefe40b979aba45ebaf411b9903a83f95e80e675cf92c8f2f2",
"rawBytes": 28088, "rawBytes": 28088,
"gzipBytes": 6952, "gzipBytes": 6950,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-card.js", "path": "houseplan-card.js",
"sha256": "e9bfe783eae1d4aa0cefb16d58fc8aca3171461e990723f0281e9acf45bbc4d8", "sha256": "9d46f73cda0b6057802373cb28192deb133617bbf458f196d9a9506c5260b209",
"rawBytes": 1183, "rawBytes": 1183,
"gzipBytes": 795, "gzipBytes": 795,
"isEntry": true, "isEntry": true,
"imports": [ "imports": [
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
} }
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="eeba3bd848dcc963129aef216061c60ac878ad129c553317d8bfd01ff5c8c50e";import{g as e,l as o,t,dm as s,A as a,dn as i,dp as l,dq as n,E as r,c}from"./houseplan-card-D8zflggZ.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="fb1a7d387b2900fb1a108215450e0b1cb0c649e3fe4cb21ba558be186c23c71b";import{g as e,l as o,t,dm as s,A as a,dn as i,dp as l,dq as n,E as r,c}from"./houseplan-card-6G7OmtFg.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} .hass=${this.hass}
.data=${this._formData} .data=${this._formData}
.schema=${e} .schema=${e}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="eeba3bd848dcc963129aef216061c60ac878ad129c553317d8bfd01ff5c8c50e";try{await import("./houseplan-assets/houseplan-card-D8zflggZ.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__="fb1a7d387b2900fb1a108215450e0b1cb0c649e3fe4cb21ba558be186c23c71b";try{await import("./houseplan-assets/houseplan-card-6G7OmtFg.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)}
+16
View File
@@ -1774,6 +1774,22 @@ MARKER_SCHEMA = vol.Schema(
{str: vol.All([_finite], vol.Length(min=6, max=6))} {str: vol.All([_finite], vol.Length(min=6, max=6))}
), ),
vol.Optional("segment_map"): vol.Schema({str: str}), vol.Optional("segment_map"): vol.Schema({str: str}),
# #162: canonical map -> space routing. The schema checks the
# shape only; identity uniqueness and the referential space
# check live in validate_marker_vacuum_routes, which is
# change-aware and must not reject untouched legacy data.
vol.Optional("map_routes"): vol.Any(
None,
vol.All([vol.Schema({
vol.Required("id"): vol.All(str, vol.Length(min=1, max=128)),
vol.Required("source"): vol.All(str, vol.Length(min=1, max=255)),
vol.Required("map_id"): vol.All(str, vol.Length(max=255)),
vol.Required("space"): vol.All(str, vol.Length(min=1, max=64)),
vol.Optional("calibration"): vol.Any(
None, vol.All([_finite], vol.Length(min=6, max=6)),
),
}, extra=vol.ALLOW_EXTRA)], vol.Length(max=32)),
),
}), }),
), ),
vol.Optional("controls"): vol.Any(None, vol.All([_TEXT], vol.Length(max=MAX_CONTROLS))), vol.Optional("controls"): vol.Any(None, vol.All([_TEXT], vol.Length(max=MAX_CONTROLS))),
+54 -54
View File
@@ -1,125 +1,125 @@
{ {
"schema": 1, "schema": 1,
"fingerprint": "eeba3bd848dcc963129aef216061c60ac878ad129c553317d8bfd01ff5c8c50e", "fingerprint": "fb1a7d387b2900fb1a108215450e0b1cb0c649e3fe4cb21ba558be186c23c71b",
"entry": "houseplan-card.js", "entry": "houseplan-card.js",
"initialViewFiles": [ "initialViewFiles": [
"houseplan-assets/houseplan-card-D8zflggZ.js", "houseplan-assets/houseplan-card-6G7OmtFg.js",
"houseplan-card.js" "houseplan-card.js"
], ],
"initialViewGzipBytes": 292971, "initialViewGzipBytes": 292971,
"lazyFiles": [ "lazyFiles": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/de-CsRas6jx.js", "houseplan-assets/de-IEcTyTk-.js",
"houseplan-assets/editor-S0HhJYix.js", "houseplan-assets/editor-CmKrBzQX.js",
"houseplan-assets/fr-DdgK8UIB.js", "houseplan-assets/fr-DBwRNHhe.js",
"houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js", "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js",
"houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js" "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js"
], ],
"lazyGzipBytes": 219799, "lazyGzipBytes": 219796,
"lazyEditorFiles": [ "lazyEditorFiles": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/editor-S0HhJYix.js", "houseplan-assets/editor-CmKrBzQX.js",
"houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js" "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js"
], ],
"lazyEditorGzipBytes": 163776, "lazyEditorGzipBytes": 163777,
"lazyOnboardingFiles": [ "lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js" "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js"
], ],
"lazyOnboardingGzipBytes": 14230, "lazyOnboardingGzipBytes": 14226,
"lazyLocaleFiles": [ "lazyLocaleFiles": [
"houseplan-assets/de-CsRas6jx.js", "houseplan-assets/de-IEcTyTk-.js",
"houseplan-assets/fr-DdgK8UIB.js" "houseplan-assets/fr-DBwRNHhe.js"
], ],
"lazyLocaleGzipBytes": 49071, "lazyLocaleGzipBytes": 49069,
"files": [ "files": [
{ {
"path": "houseplan-assets/backdrop-pick-BLxmEavR.js", "path": "houseplan-assets/backdrop-pick-O29mDmnF.js",
"sha256": "7ddf179e36cbde0bf7e9022b0c485fbea78ef6b795353258a61a79f44b1deeaa", "sha256": "e6cf619bbdb1c9a849f3169bc390b01cd69a9357c22e2a62bc4aed8856d400d6",
"rawBytes": 21378, "rawBytes": 21418,
"gzipBytes": 7278, "gzipBytes": 7276,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/de-CsRas6jx.js", "path": "houseplan-assets/de-IEcTyTk-.js",
"sha256": "e2aa870d33f74396775b0967bd51c772a4f832f4fec49d90db419a58dc1440d2", "sha256": "d5feb2e10ba499dcbebe492ead8ee7d20fd2d83e2514b724e607d7668e65daee",
"rawBytes": 82140, "rawBytes": 82140,
"gzipBytes": 24805, "gzipBytes": 24804,
"isEntry": false, "isEntry": false,
"imports": [], "imports": [],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/editor-S0HhJYix.js", "path": "houseplan-assets/editor-CmKrBzQX.js",
"sha256": "fdbe9fc3fc2951da28e3c0c670a0032cd8f090bbf233d1a134a3b170c6543eef", "sha256": "c05ed91e68c0a2a70f7732822f9ff87c9713cc601680c6fe5b4b597bd4ef46a3",
"rawBytes": 3826, "rawBytes": 3826,
"gzipBytes": 1581, "gzipBytes": 1578,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/fr-DdgK8UIB.js", "path": "houseplan-assets/fr-DBwRNHhe.js",
"sha256": "aad556cbbf53185d79c761d65ad8e12635d78e3a309e417285d09d56656a3c82", "sha256": "b7aaa4f782027a4ff175682ca84f7dc605123f96c3d4712e12f7d09647b65284",
"rawBytes": 84267, "rawBytes": 84267,
"gzipBytes": 24266, "gzipBytes": 24265,
"isEntry": false, "isEntry": false,
"imports": [], "imports": [],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/houseplan-card-D8zflggZ.js", "path": "houseplan-assets/houseplan-card-6G7OmtFg.js",
"sha256": "857327b87b7b74ea580699c5023efbf35c59bafada3f65a4f1ca3ed1414fa9ec", "sha256": "ce95ec4e5177e6e945f2a9ec87dfe552d9a5a62a01f09bdd14b258743290f130",
"rawBytes": 1039585, "rawBytes": 1039585,
"gzipBytes": 292176, "gzipBytes": 292176,
"isEntry": false, "isEntry": false,
"imports": [], "imports": [],
"dynamicImports": [ "dynamicImports": [
"houseplan-assets/de-CsRas6jx.js", "houseplan-assets/de-IEcTyTk-.js",
"houseplan-assets/editor-S0HhJYix.js", "houseplan-assets/editor-CmKrBzQX.js",
"houseplan-assets/fr-DdgK8UIB.js", "houseplan-assets/fr-DBwRNHhe.js",
"houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js", "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js",
"houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js" "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js"
] ]
}, },
{ {
"path": "houseplan-assets/houseplan-editor-runtime-DDOnx5O4.js", "path": "houseplan-assets/houseplan-editor-runtime-Bd2EFlH2.js",
"sha256": "23baee7febc624f323b1850067aca264554abdd4995a6e90c786d44524233881", "sha256": "e8ab0b4106df9b7c16cd1d94bc42b41b98a66af12f6510ce7ec04fce88cc9bcd",
"rawBytes": 590156, "rawBytes": 590169,
"gzipBytes": 154917, "gzipBytes": 154923,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-assets/houseplan-onboarding-runtime-4HdY4AXX.js", "path": "houseplan-assets/houseplan-onboarding-runtime-DHEHMmfG.js",
"sha256": "03ce43da4034c5dc3e05e0946b39e68aeeece3aef0cff782eb27ca3c8033a3c5", "sha256": "d93dc2524abe1caefe40b979aba45ebaf411b9903a83f95e80e675cf92c8f2f2",
"rawBytes": 28088, "rawBytes": 28088,
"gzipBytes": 6952, "gzipBytes": 6950,
"isEntry": false, "isEntry": false,
"imports": [ "imports": [
"houseplan-assets/backdrop-pick-BLxmEavR.js", "houseplan-assets/backdrop-pick-O29mDmnF.js",
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
}, },
{ {
"path": "houseplan-card.js", "path": "houseplan-card.js",
"sha256": "e9bfe783eae1d4aa0cefb16d58fc8aca3171461e990723f0281e9acf45bbc4d8", "sha256": "9d46f73cda0b6057802373cb28192deb133617bbf458f196d9a9506c5260b209",
"rawBytes": 1183, "rawBytes": 1183,
"gzipBytes": 795, "gzipBytes": 795,
"isEntry": true, "isEntry": true,
"imports": [ "imports": [
"houseplan-assets/houseplan-card-D8zflggZ.js" "houseplan-assets/houseplan-card-6G7OmtFg.js"
], ],
"dynamicImports": [] "dynamicImports": []
} }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="eeba3bd848dcc963129aef216061c60ac878ad129c553317d8bfd01ff5c8c50e";import{g as e,l as o,t,dm as s,A as a,dn as i,dp as l,dq as n,E as r,c}from"./houseplan-card-D8zflggZ.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="fb1a7d387b2900fb1a108215450e0b1cb0c649e3fe4cb21ba558be186c23c71b";import{g as e,l as o,t,dm as s,A as a,dn as i,dp as l,dq as n,E as r,c}from"./houseplan-card-6G7OmtFg.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} .hass=${this.hass}
.data=${this._formData} .data=${this._formData}
.schema=${e} .schema=${e}
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 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="eeba3bd848dcc963129aef216061c60ac878ad129c553317d8bfd01ff5c8c50e";try{await import("./houseplan-assets/houseplan-card-D8zflggZ.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__="fb1a7d387b2900fb1a108215450e0b1cb0c649e3fe4cb21ba558be186c23c71b";try{await import("./houseplan-assets/houseplan-card-6G7OmtFg.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)}
+11 -11
View File
@@ -3,7 +3,7 @@
"fixture": "synthetic-only", "fixture": "synthetic-only",
"chromium": "151.0.7922.34", "chromium": "151.0.7922.34",
"oxipng": "oxipng 10.2.0", "oxipng": "oxipng 10.2.0",
"sourceFingerprint": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceFingerprint": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"captureScriptSha256": "cadb8e1bcab9f1dcdd7d75b3b90ddcbaaeb2b8c2a098f575a21f39ff70f5c59c", "captureScriptSha256": "cadb8e1bcab9f1dcdd7d75b3b90ddcbaaeb2b8c2a098f575a21f39ff70f5c59c",
"command": "npm run build && node demo/docs/capture.mjs", "command": "npm run build && node demo/docs/capture.mjs",
"scenarios": { "scenarios": {
@@ -15,7 +15,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "80a70361dc18dd0461568df332062e6482c633af5d280954f8b675701418a76d" "imageSha256": "80a70361dc18dd0461568df332062e6482c633af5d280954f8b675701418a76d"
}, },
"view-touch": { "view-touch": {
@@ -26,7 +26,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "4106cc28847047505f46921ff95765d8abdf5b382d9d17d4c5e4ad129dd8f6be" "imageSha256": "4106cc28847047505f46921ff95765d8abdf5b382d9d17d4c5e4ad129dd8f6be"
}, },
"space-create": { "space-create": {
@@ -37,7 +37,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "617b51b3648498787b5039980c9f3eceb75ba56ed63a1a20e616bc05bc304362" "imageSha256": "617b51b3648498787b5039980c9f3eceb75ba56ed63a1a20e616bc05bc304362"
}, },
"room-contour-close": { "room-contour-close": {
@@ -48,7 +48,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "1dab6cc3b9d1bf7d8c40f0e5137f8c688683c9b7eabc5167da99d41ecfdd5b79" "imageSha256": "1dab6cc3b9d1bf7d8c40f0e5137f8c688683c9b7eabc5167da99d41ecfdd5b79"
}, },
"plan-context-tray": { "plan-context-tray": {
@@ -59,7 +59,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "94ef50762753c0ac6ddc84d2521c4232a3f9ecd89843810d2df61c517592bed7" "imageSha256": "94ef50762753c0ac6ddc84d2521c4232a3f9ecd89843810d2df61c517592bed7"
}, },
"device-editor": { "device-editor": {
@@ -70,7 +70,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "7a601769de38aa19c2e280f6ff4cf3695b854b1d1c6d1f8de4e6b5e8f7eb1559" "imageSha256": "7a601769de38aa19c2e280f6ff4cf3695b854b1d1c6d1f8de4e6b5e8f7eb1559"
}, },
"device-display-preview": { "device-display-preview": {
@@ -81,7 +81,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "0939875f8631694f4de0ef4fd01032010ede8c7d49fb7c0a857612d4b3afff93" "imageSha256": "0939875f8631694f4de0ef4fd01032010ede8c7d49fb7c0a857612d4b3afff93"
}, },
"background-editor": { "background-editor": {
@@ -92,7 +92,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "054170fd9ef45762b602b4d5c9c3b9ea9724858be61af137970c246f485c13bb" "imageSha256": "054170fd9ef45762b602b4d5c9c3b9ea9724858be61af137970c246f485c13bb"
}, },
"room-card": { "room-card": {
@@ -103,7 +103,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "2ae4a58853d98e10d12456b2078ec2f6a0b597722c8310d7b016abd2bc42561e" "imageSha256": "2ae4a58853d98e10d12456b2078ec2f6a0b597722c8310d7b016abd2bc42561e"
}, },
"device-info": { "device-info": {
@@ -114,7 +114,7 @@
}, },
"theme": "dark", "theme": "dark",
"language": "en", "language": "en",
"sourceSha256": "4b8e50872070b871bc5b93acc8a6cc47f73223051f3e7430ff8ef6c81224d349", "sourceSha256": "99b38e3524cc28064ca82f660c0d485f7852bb89776b38966bee82c34301eb4f",
"imageSha256": "cb37f7eefd936f98ef44969b21dfe8e40ee27d1f558abe8d53c0223f6488b8a6" "imageSha256": "cb37f7eefd936f98ef44969b21dfe8e40ee27d1f558abe8d53c0223f6488b8a6"
} }
} }
+9
View File
@@ -244,6 +244,15 @@
"required": false, "required": false,
"type": "bool" "type": "bool"
}, },
"config.markers[].vacuum.map_routes": {
"list": {
"allowExtra": true,
"object": true
},
"maxLength": 32,
"nullable": true,
"required": false
},
"config.markers[].vacuum.room_highlight": { "config.markers[].vacuum.room_highlight": {
"nullable": true, "nullable": true,
"required": false, "required": false,
+1 -1
View File
@@ -377,7 +377,7 @@ const MUTANT_DEFINITIONS = [
+ 'them behind keeps a route nothing can resolve and a robot that never appears (#162, AC16)', + 'them behind keeps a route nothing can resolve and a robot that never appears (#162, AC16)',
patches: [{ patches: [{
file: 'src/space-deletion.ts', file: 'src/space-deletion.ts',
find: ' const kept = routes.filter((route: any) => route?.space !== spaceId);', find: ' const kept = routes.filter((route) => route?.space !== spaceId);',
replace: ' const kept = routes;', replace: ' const kept = routes;',
}], }],
}, },
+29 -13
View File
@@ -7,22 +7,37 @@
* than in a core file. * than in a core file.
*/ */
import { TemplateResult, html, nothing } from 'lit'; import { TemplateResult, html, nothing } from 'lit';
import { langOf } from '../i18n'; import type { HpConfirmRequest } from '../danger-confirm';
import type { DevItem } from '../types';
import { langOf, type I18nKey } from '../i18n';
import { supportT, type SupportI18nKey } from '../i18n/support'; import { supportT, type SupportI18nKey } from '../i18n/support';
import { VacuumMapRoute, effectiveRoutes, observedMapIds, resolveRoute } from '../vacuum-routes'; import { VacuumMapRoute, effectiveRoutes, observedMapIds, resolveRoute } from '../vacuum-routes';
import { import {
addRoute, changeRouteSpace, convertLegacyRoutes, newRouteId, removeRoute, addRoute, changeRouteSpace, convertLegacyRoutes, newRouteId, removeRoute,
} from '../vacuum-route-edit'; } from '../vacuum-route-edit';
export interface VacuumMapsHost { interface SpaceRow { id?: unknown; name?: unknown }
host: any;
_saveConfig: () => void; /** The narrow slice of the card this block reads and writes through. */
_vacAutoCalibrate: (dev: any) => void; export interface VacuumMapsCardHost {
_vacStartFit: (dev: any, routeId?: string) => void; hass?: { states?: Record<string, unknown> } | null;
_config?: { language?: string | null } | null;
_serverCfg?: { spaces?: SpaceRow[] } | null;
_t: (key: I18nKey, vars?: Record<string, string | number>) => string;
_vacSource: (dev: DevItem) => string | null;
_vacObservedMapId: (dev: DevItem, source: string) => string | undefined;
_confirmDanger: (request: HpConfirmRequest) => Promise<boolean>;
} }
const spaceName = (host: any, spaceId: string): string => { export interface VacuumMapsHost {
const space = (host._serverCfg?.spaces || []).find((item: any) => item?.id === spaceId); host: VacuumMapsCardHost;
_saveConfig: () => void;
_vacAutoCalibrate: (dev: DevItem) => void;
_vacStartFit: (dev: DevItem, routeId?: string) => void;
}
const spaceName = (host: VacuumMapsCardHost, spaceId: string): string => {
const space = (host._serverCfg?.spaces || []).find((item) => item?.id === spaceId);
return String(space?.name || space?.id || spaceId); return String(space?.name || space?.id || spaceId);
}; };
@@ -33,7 +48,7 @@ const spaceName = (host: any, spaceId: string): string => {
* routing change goes through one save path with everything else. * routing change goes through one save path with everything else.
*/ */
export function renderVacuumMapsSection( export function renderVacuumMapsSection(
runtime: VacuumMapsHost, dev: any, setVac: (patch: Record<string, unknown>) => void, runtime: VacuumMapsHost, dev: DevItem, setVac: (patch: Record<string, unknown>) => void,
): TemplateResult | typeof nothing { ): TemplateResult | typeof nothing {
const host = runtime.host; const host = runtime.host;
const lang = langOf(host.hass, host._config?.language); const lang = langOf(host.hass, host._config?.language);
@@ -44,8 +59,9 @@ export function renderVacuumMapsSection(
const rootSource: string = host._vacSource(dev) || ''; const rootSource: string = host._vacSource(dev) || '';
const routes: VacuumMapRoute[] = effectiveRoutes(dev.id, vacuum, dev.space, rootSource); const routes: VacuumMapRoute[] = effectiveRoutes(dev.id, vacuum, dev.space, rootSource);
const spaces: Array<{ id: string; name: string }> = (host._serverCfg?.spaces || []) const spaces: Array<{ id: string; name: string }> = (host._serverCfg?.spaces || [])
.filter((space: any) => typeof space?.id === 'string' && space.id) .filter((space): space is { id: string; name?: unknown } =>
.map((space: any) => ({ id: space.id, name: String(space.name || space.id) })); typeof space?.id === 'string' && !!space.id)
.map((space) => ({ id: space.id, name: String(space.name || space.id) }));
const spaceIds = new Set(spaces.map((space) => space.id)); const spaceIds = new Set(spaces.map((space) => space.id));
const observed = observedMapIds(routes, [rootSource], const observed = observedMapIds(routes, [rootSource],
(source) => host._vacObservedMapId(dev, source)); (source) => host._vacObservedMapId(dev, source));
@@ -56,7 +72,7 @@ export function renderVacuumMapsSection(
/** Every routing edit converts legacy data first — all of it, or none. */ /** Every routing edit converts legacy data first — all of it, or none. */
const writeRoutes = (next: (current: VacuumMapRoute[]) => VacuumMapRoute[]): boolean => { const writeRoutes = (next: (current: VacuumMapRoute[]) => VacuumMapRoute[]): boolean => {
let base: VacuumMapRoute[] | null = explicit ? vacuum.map_routes : null; let base: VacuumMapRoute[] | null = explicit ? (vacuum.map_routes ?? null) : null;
if (!base) { if (!base) {
base = convertLegacyRoutes(vacuum, dev.space, rootSource, base = convertLegacyRoutes(vacuum, dev.space, rootSource,
(taken) => newRouteId(taken)) ?? []; (taken) => newRouteId(taken)) ?? [];
@@ -98,7 +114,7 @@ export function renderVacuumMapsSection(
const drop = async (route: VacuumMapRoute) => { const drop = async (route: VacuumMapRoute) => {
const accepted = await host._confirmDanger({ const accepted = await host._confirmDanger({
key: 'vacuum_route_delete', key: 'vacuum_route_delete',
kind: 'danger', kind: 'destructive',
title: t('vac.route_delete_title'), title: t('vac.route_delete_title'),
message: t('vac.route_delete_body'), message: t('vac.route_delete_body'),
objectName: route.map_id || t('vac.route_map_default'), objectName: route.map_id || t('vac.route_map_default'),
+4 -3
View File
@@ -12247,7 +12247,7 @@ export class HouseplanCard extends LitElement {
} }
/** The map id an exact source reports right now, or undefined if silent. */ /** The map id an exact source reports right now, or undefined if silent. */
private _vacObservedMapId(d: DevItem, source: string, planHass = this._planHass): string | undefined { public _vacObservedMapId(d: DevItem, source: string, planHass = this._planHass): string | undefined {
const tele = readVacTelemetry(planHass?.states?.[source]?.attributes); const tele = readVacTelemetry(planHass?.states?.[source]?.attributes);
return tele ? this._vacMapId(d, tele, planHass) : undefined; return tele ? this._vacMapId(d, tele, planHass) : undefined;
} }
@@ -12539,10 +12539,11 @@ export class HouseplanCard extends LitElement {
private _vacRouteBadge(d: DevItem): TemplateResult | typeof nothing { private _vacRouteBadge(d: DevItem): TemplateResult | typeof nothing {
if (!this._isVacDev(d) || d.hidden) return nothing; if (!this._isVacDev(d) || d.hidden) return nothing;
if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') return nothing; if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') return nothing;
const fact = this._renderDeviceSnapshot?.facts.get(`vacuum:${d.id}`) as any; 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); const reason = routeWarningKey(fact?.resolution, this._vacRt.get(d.id)?.moving ?? false);
if (!reason) return nothing; if (!reason) return nothing;
const text = this._t((`vac.route_warn_${reason}`) as any); const text = this._t(`vac.route_warn_${reason}` as I18nKey);
return html`<span class="vacwarn" role="img" aria-label=${text} title=${text}> return html`<span class="vacwarn" role="img" aria-label=${text} title=${text}>
<ha-icon icon="mdi:alert-outline"></ha-icon></span>`; <ha-icon icon="mdi:alert-outline"></ha-icon></span>`;
} }
+2 -1
View File
@@ -1155,6 +1155,7 @@ export interface HouseplanEditorHostPort {
_vacOpenAllCameras: (d: DevItem) => void; _vacOpenAllCameras: (d: DevItem) => void;
_vacRt: Map<string, { trail: VacPt[]; lastKey: string; lastTs: number; moving: boolean; jump: boolean; endedTs: number; lastPos: VacPt | null; }>; _vacRt: Map<string, { trail: VacPt[]; lastKey: string; lastTs: number; moving: boolean; jump: boolean; endedTs: number; lastPos: VacPt | null; }>;
_vacSource: (d: DevItem, planHass?: any) => string | null; _vacSource: (d: DevItem, planHass?: any) => string | null;
_vacObservedMapId: (d: DevItem, source: string, planHass?: any) => string | undefined;
_vacSourceResolution: (d: DevItem, includeAllCameras?: boolean, planHass?: any) => VacSourceResolution; _vacSourceResolution: (d: DevItem, includeAllCameras?: boolean, planHass?: any) => VacSourceResolution;
_vacSrvTrails: Record<string, any>; _vacSrvTrails: Record<string, any>;
_view: { x: number; y: number; w: number; h: number; } | null; _view: { x: number; y: number; w: number; h: number; } | null;
@@ -10990,7 +10991,7 @@ public _vacSaveMatrix(
if (!m) return false; if (!m) return false;
// #162: with explicit routes the matrix belongs to the route, not to a // #162: with explicit routes the matrix belongs to the route, not to a
// marker-wide dictionary that cannot tell two floors apart. // marker-wide dictionary that cannot tell two floors apart.
m.vacuum = writeVacuumMatrix(m.vacuum || {}, { source, mapId, routeId, matrix }) as any; m.vacuum = writeVacuumMatrix(m.vacuum || {}, { source, mapId, routeId, matrix });
this.host._regSignature = ''; this.host._regSignature = '';
this.host._maybeRebuildDevices(); this.host._maybeRebuildDevices();
this._saveConfig(); this._saveConfig();
+26 -17
View File
@@ -12,11 +12,20 @@ export interface SpaceDeletionDependencyReport {
routeCount: number; routeCount: number;
} }
/** Just enough of a marker to answer "does it route a map into this space?". */
interface RouteCarrier {
id?: unknown;
space?: unknown;
room_id?: unknown;
removed?: unknown;
vacuum?: { map_routes?: Array<{ id?: unknown; space?: unknown }> | null } | null;
}
/** Ids of routes this marker points at the space being deleted. */ /** Ids of routes this marker points at the space being deleted. */
const routesIntoSpace = (marker: any, spaceId: string): string[] => const routesIntoSpace = (marker: RouteCarrier, spaceId: string): string[] =>
(marker?.vacuum?.map_routes || []) (marker?.vacuum?.map_routes || [])
.filter((route: any) => route && route.space === spaceId && typeof route.id === 'string') .filter((route) => route && route.space === spaceId && typeof route.id === 'string')
.map((route: any) => route.id); .map((route) => String(route.id));
const clone = <T>(value: T): T => JSON.parse(JSON.stringify(value)); const clone = <T>(value: T): T => JSON.parse(JSON.stringify(value));
@@ -27,20 +36,20 @@ export function collectSpaceMarkerDependencies(
): SpaceDeletionDependencyReport { ): SpaceDeletionDependencyReport {
const space = (config?.spaces || []).find((item: any) => item?.id === spaceId); const space = (config?.spaces || []).find((item: any) => item?.id === spaceId);
const roomIds = new Set( const roomIds = new Set(
(space?.rooms || []).map((room: any) => String(room?.id || '')).filter(Boolean), (space?.rooms || []).map((room: { id?: unknown }) => String(room?.id || '')).filter(Boolean),
); );
const markerIds = [...new Set<string>((config?.markers || []) const markerIds = [...new Set<string>((config?.markers || [])
.filter((marker: any) => marker?.removed !== true && typeof marker?.id === 'string') .filter((marker: RouteCarrier) => marker?.removed !== true && typeof marker?.id === 'string')
.filter((marker: any) => marker.space === spaceId .filter((marker: RouteCarrier) => marker.space === spaceId
|| (typeof marker.room_id === 'string' && roomIds.has(marker.room_id)) || (typeof marker.room_id === 'string' && roomIds.has(marker.room_id))
|| layout?.[marker.id]?.s === spaceId) || layout?.[String(marker.id)]?.s === spaceId)
.map((marker: any) => marker.id))] .map((marker: RouteCarrier) => String(marker.id)))]
.sort((a: string, b: string) => a.localeCompare(b)); .sort((a: string, b: string) => a.localeCompare(b));
const routeMarkerIds = (config?.markers || []) const routeMarkerIds = (config?.markers || [] as RouteCarrier[])
.filter((marker: any) => marker?.removed !== true && typeof marker?.id === 'string') .filter((marker: RouteCarrier) => marker?.removed !== true && typeof marker?.id === 'string')
.filter((marker: any) => !markerIds.includes(marker.id)) .filter((marker: RouteCarrier) => !markerIds.includes(String(marker.id)))
.filter((marker: any) => routesIntoSpace(marker, spaceId).length > 0) .filter((marker: RouteCarrier) => routesIntoSpace(marker, spaceId).length > 0)
.map((marker: any) => marker.id) .map((marker: RouteCarrier) => String(marker.id))
.sort((a: string, b: string) => a.localeCompare(b)); .sort((a: string, b: string) => a.localeCompare(b));
return { return {
markerIds, count: markerIds.length, markerIds, count: markerIds.length,
@@ -92,14 +101,14 @@ export function createSpaceDeletionCandidate(
if (typeof marker.room_id === 'string' && roomIds.has(marker.room_id)) delete marker.room_id; if (typeof marker.room_id === 'string' && roomIds.has(marker.room_id)) delete marker.room_id;
} }
for (const [key, position] of Object.entries(layout)) { for (const [key, position] of Object.entries(layout)) {
if ((position as any)?.s === spaceId) delete layout[key]; if ((position as { s?: unknown } | null)?.s === spaceId) delete layout[key];
} }
// #162: a robot docked elsewhere keeps its dock and its other maps; only the // #162: a robot docked elsewhere keeps its dock and its other maps; only the
// routes that pointed here go, in the same logical operation as the space. // routes that pointed here go, in the same logical operation as the space.
for (const marker of config.markers || []) { for (const marker of (config.markers || []) as RouteCarrier[]) {
const routes = marker?.vacuum?.map_routes; const routes = marker?.vacuum?.map_routes;
if (!Array.isArray(routes)) continue; if (!Array.isArray(routes) || !marker.vacuum) continue;
const kept = routes.filter((route: any) => route?.space !== spaceId); const kept = routes.filter((route) => route?.space !== spaceId);
if (kept.length !== routes.length) marker.vacuum.map_routes = kept; if (kept.length !== routes.length) marker.vacuum.map_routes = kept;
} }
return { config, layout, dependencies }; return { config, layout, dependencies };
+8 -4
View File
@@ -5,7 +5,11 @@
* editor, so these helpers live in the lazy editor graph: the View card must * editor, so these helpers live in the lazy editor graph: the View card must
* not pay for code it can never run (docs/ARCHITECTURE.md, #367). * not pay for code it can never run (docs/ARCHITECTURE.md, #367).
*/ */
import type { Marker } from './types';
import { FitParams, fitFromMatrix, initialFit, VacRoom } from './vacuum'; import { FitParams, fitFromMatrix, initialFit, VacRoom } from './vacuum';
/** The marker's vacuum block exactly as the config stores it. */
export type MarkerVacuumCfg = NonNullable<Marker['vacuum']>;
import { import {
Affine, VacuumMapRoute, VacuumRouteMarkerCfg, effectiveRoutes, normalizeRouteMatrix, Affine, VacuumMapRoute, VacuumRouteMarkerCfg, effectiveRoutes, normalizeRouteMatrix,
} from './vacuum-routes'; } from './vacuum-routes';
@@ -87,9 +91,9 @@ export function convertLegacyRoutes(
* calibration the user just solved would lose real work over bookkeeping. * calibration the user just solved would lose real work over bookkeeping.
*/ */
export function writeVacuumMatrix( export function writeVacuumMatrix(
vacuum: VacuumRouteMarkerCfg | null | undefined, vacuum: MarkerVacuumCfg | null | undefined,
opts: { source: string; mapId: string; routeId?: string; matrix: Affine }, opts: { source: string; mapId: string; routeId?: string; matrix: Affine },
): VacuumRouteMarkerCfg { ): MarkerVacuumCfg {
const rounded = opts.matrix.map((n) => Number(n.toFixed(6))) as Affine; const rounded = opts.matrix.map((n) => Number(n.toFixed(6))) as Affine;
const routes = vacuum?.map_routes; const routes = vacuum?.map_routes;
if (Array.isArray(routes) && routes.length) { if (Array.isArray(routes) && routes.length) {
@@ -121,7 +125,7 @@ export interface VacuumFitPlan {
*/ */
export function planVacuumFit( export function planVacuumFit(
markerId: string, markerId: string,
vacuum: VacuumRouteMarkerCfg | null | undefined, vacuum: MarkerVacuumCfg | null | undefined,
opts: { opts: {
routeId?: string; source: string; mapId: string; dockSpace: string; rooms: VacRoom[]; routeId?: string; source: string; mapId: string; dockSpace: string; rooms: VacRoom[];
viewBoxOf: (spaceId: string) => [number, number, number, number] | null; viewBoxOf: (spaceId: string) => [number, number, number, number] | null;
@@ -148,7 +152,7 @@ export function planVacuumFit(
*/ */
export function calibrationTarget( export function calibrationTarget(
markerId: string, markerId: string,
vacuum: VacuumRouteMarkerCfg | null | undefined, vacuum: MarkerVacuumCfg | null | undefined,
dockSpace: string, dockSpace: string,
source: string, source: string,
mapId: string, mapId: string,
+2 -1
View File
@@ -2524,7 +2524,8 @@ def test_issue_162_full_export_round_trips_every_map_route(tmp_path: Path) -> No
config["markers"][0]["vacuum"] = {"source": "camera.robot", "map_routes": routes} config["markers"][0]["vacuum"] = {"source": "camera.robot", "map_routes": routes}
document, _ = create_export( document, _ = create_export(
SimpleNamespace(instance_id="instance-a"), {"config": config}, SimpleNamespace(instance_id="instance-a"), {"config": config},
{"layout": {}}, kind="full", card_version="1.61.0", config_root=tmp_path, {"layout": {}}, kind="full", space_id=None, card_version="1.61.0",
config_root=tmp_path,
) )
assert document["payload"]["config"]["markers"][0]["vacuum"]["map_routes"] == routes assert document["payload"]["config"]["markers"][0]["vacuum"]["map_routes"] == routes
assert document["transfer"]["dropped_marker_links"] == 0 assert document["transfer"]["dropped_marker_links"] == 0