feat: warn on the dock when a moving robot has no floor to draw on

User-Visible: yes
Issue: #162
This commit is contained in:
Codex
2026-09-03 19:18:13 +03:00
parent ca6ebc3035
commit 567b456890
29 changed files with 379 additions and 214 deletions
@@ -1,125 +1,125 @@
{
"schema": 1,
"fingerprint": "d2e52a98c720731b44a0fe51783aa8191a5218f555f1f86893701f28e99f200f",
"fingerprint": "bd7029808eb6094140d9fa8245f50816cb452430a353f4231d16e02c97078a06",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-C1YZDmj6.js",
"houseplan-assets/houseplan-card-BTXh6SvG.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 292278,
"initialViewGzipBytes": 292859,
"lazyFiles": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/de-DYuCBO-k.js",
"houseplan-assets/editor-CVgkVajn.js",
"houseplan-assets/fr-C2GpV0Jw.js",
"houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js",
"houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/de-Cm5MlI_Y.js",
"houseplan-assets/editor-CxXW-QO8.js",
"houseplan-assets/fr-CDWnrdfn.js",
"houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js",
"houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js"
],
"lazyGzipBytes": 219265,
"lazyGzipBytes": 219508,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/editor-CVgkVajn.js",
"houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/editor-CxXW-QO8.js",
"houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js"
],
"lazyEditorGzipBytes": 163598,
"lazyEditorGzipBytes": 163600,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js"
],
"lazyOnboardingGzipBytes": 14135,
"lazyOnboardingGzipBytes": 14138,
"lazyLocaleFiles": [
"houseplan-assets/de-DYuCBO-k.js",
"houseplan-assets/fr-C2GpV0Jw.js"
"houseplan-assets/de-Cm5MlI_Y.js",
"houseplan-assets/fr-CDWnrdfn.js"
],
"lazyLocaleGzipBytes": 48711,
"lazyLocaleGzipBytes": 48949,
"files": [
{
"path": "houseplan-assets/backdrop-pick-CfOh3CA7.js",
"sha256": "8e6708537de7c137d4329702fe275413c56b07f14ac1376febfc19065c9fbdd0",
"path": "houseplan-assets/backdrop-pick-DxKIBNjn.js",
"sha256": "b63841500de9009bdab1dda314af5c1ca3eead311f1dd05f1830f0da589b0f4c",
"rawBytes": 20996,
"gzipBytes": 7179,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-DYuCBO-k.js",
"sha256": "d2a1c7f04fdb2e127905f083a6e8581b6ce975a301fe776c6a9f65763d86a8c4",
"rawBytes": 81588,
"gzipBytes": 24627,
"path": "houseplan-assets/de-Cm5MlI_Y.js",
"sha256": "cacdb232ff6403d2fdfb40a304df67aee139fbba4a6a6a2992a176e733ea4edb",
"rawBytes": 81940,
"gzipBytes": 24744,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-CVgkVajn.js",
"sha256": "cde4b2f73563f74dc3c66a210b01bd83dd9c8fda30914a02cd845db2feaf029e",
"path": "houseplan-assets/editor-CxXW-QO8.js",
"sha256": "3cda68688d097f4198daa63b06d46eeb60c60ceb9399c4a83c586adae61852e3",
"rawBytes": 3826,
"gzipBytes": 1583,
"gzipBytes": 1584,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-C2GpV0Jw.js",
"sha256": "f111011a8f45476386a3d0743402d3fad8b5f4d6d3b61be30036aa983f58e3f1",
"rawBytes": 83710,
"gzipBytes": 24084,
"path": "houseplan-assets/fr-CDWnrdfn.js",
"sha256": "74c6d85b8e5ae259fa0d40948e19848a3010caf717edea225a8f2129d7cf2227",
"rawBytes": 84059,
"gzipBytes": 24205,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-C1YZDmj6.js",
"sha256": "c6de4fca90b84eb8ab47f5e6e5b23d40f9929ca58ac58809cd1be4398e4a1f05",
"rawBytes": 1036950,
"gzipBytes": 291480,
"path": "houseplan-assets/houseplan-card-BTXh6SvG.js",
"sha256": "a2278cdaf370fb864adb820cfdbd884671ffa4b4ea61785f375ed384907d8562",
"rawBytes": 1039135,
"gzipBytes": 292060,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-DYuCBO-k.js",
"houseplan-assets/editor-CVgkVajn.js",
"houseplan-assets/fr-C2GpV0Jw.js",
"houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js",
"houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js"
"houseplan-assets/de-Cm5MlI_Y.js",
"houseplan-assets/editor-CxXW-QO8.js",
"houseplan-assets/fr-CDWnrdfn.js",
"houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js",
"houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js",
"sha256": "0710c7a4a73f7a2532d041984b1294727abc5a1a76a17ab395c9f347ee1a05ab",
"path": "houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js",
"sha256": "ccca4dc1b21b421cf2fb37a1bb27ab417492cf1d3478fa2ad8b0f555d9496798",
"rawBytes": 589868,
"gzipBytes": 154836,
"gzipBytes": 154837,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js",
"sha256": "37bbf57bbaede7f9ff15617e537978faf5ab76de510a758cb286e016c6717676",
"path": "houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js",
"sha256": "be730d372a4390126890a15fee9af9ae154ac94d7df4d7dd51a3b639128cab91",
"rawBytes": 28088,
"gzipBytes": 6956,
"gzipBytes": 6959,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "4bdef27d1c8e8265021aca1648b32ba32265ed970b6743f8e9c89942dec5ed32",
"sha256": "ac8fe2593f1937bd0bdc86f131b0b58ac668f59092c70f0b3af4092edba54138",
"rawBytes": 1183,
"gzipBytes": 798,
"gzipBytes": 799,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
@@ -1,14 +1,14 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d2e52a98c720731b44a0fe51783aa8191a5218f555f1f86893701f28e99f200f";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-C1YZDmj6.js";class f extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="bd7029808eb6094140d9fa8245f50816cb452430a353f4231d16e02c97078a06";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-BTXh6SvG.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
.computeLabel=${e=>f[e.name]||e.name}
.computeLabel=${e=>h[e.name]||e.name}
@value-changed=${this._valueChanged}
></ha-form>`;return c`
${d(h.slice(0,3))}
${d(f.slice(0,3))}
${_?c`<div class="default-floor-error" role="alert"
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
${t(s,"editor.default_floor_missing",{id:_})}
</div>`:a}
${d(h.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
${d(f.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d2e52a98c720731b44a0fe51783aa8191a5218f555f1f86893701f28e99f200f";try{await import("./houseplan-assets/houseplan-card-C1YZDmj6.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__="bd7029808eb6094140d9fa8245f50816cb452430a353f4231d16e02c97078a06";try{await import("./houseplan-assets/houseplan-card-BTXh6SvG.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)}
+57 -57
View File
@@ -1,125 +1,125 @@
{
"schema": 1,
"fingerprint": "d2e52a98c720731b44a0fe51783aa8191a5218f555f1f86893701f28e99f200f",
"fingerprint": "bd7029808eb6094140d9fa8245f50816cb452430a353f4231d16e02c97078a06",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-C1YZDmj6.js",
"houseplan-assets/houseplan-card-BTXh6SvG.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 292278,
"initialViewGzipBytes": 292859,
"lazyFiles": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/de-DYuCBO-k.js",
"houseplan-assets/editor-CVgkVajn.js",
"houseplan-assets/fr-C2GpV0Jw.js",
"houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js",
"houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/de-Cm5MlI_Y.js",
"houseplan-assets/editor-CxXW-QO8.js",
"houseplan-assets/fr-CDWnrdfn.js",
"houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js",
"houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js"
],
"lazyGzipBytes": 219265,
"lazyGzipBytes": 219508,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/editor-CVgkVajn.js",
"houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/editor-CxXW-QO8.js",
"houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js"
],
"lazyEditorGzipBytes": 163598,
"lazyEditorGzipBytes": 163600,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js"
],
"lazyOnboardingGzipBytes": 14135,
"lazyOnboardingGzipBytes": 14138,
"lazyLocaleFiles": [
"houseplan-assets/de-DYuCBO-k.js",
"houseplan-assets/fr-C2GpV0Jw.js"
"houseplan-assets/de-Cm5MlI_Y.js",
"houseplan-assets/fr-CDWnrdfn.js"
],
"lazyLocaleGzipBytes": 48711,
"lazyLocaleGzipBytes": 48949,
"files": [
{
"path": "houseplan-assets/backdrop-pick-CfOh3CA7.js",
"sha256": "8e6708537de7c137d4329702fe275413c56b07f14ac1376febfc19065c9fbdd0",
"path": "houseplan-assets/backdrop-pick-DxKIBNjn.js",
"sha256": "b63841500de9009bdab1dda314af5c1ca3eead311f1dd05f1830f0da589b0f4c",
"rawBytes": 20996,
"gzipBytes": 7179,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-DYuCBO-k.js",
"sha256": "d2a1c7f04fdb2e127905f083a6e8581b6ce975a301fe776c6a9f65763d86a8c4",
"rawBytes": 81588,
"gzipBytes": 24627,
"path": "houseplan-assets/de-Cm5MlI_Y.js",
"sha256": "cacdb232ff6403d2fdfb40a304df67aee139fbba4a6a6a2992a176e733ea4edb",
"rawBytes": 81940,
"gzipBytes": 24744,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-CVgkVajn.js",
"sha256": "cde4b2f73563f74dc3c66a210b01bd83dd9c8fda30914a02cd845db2feaf029e",
"path": "houseplan-assets/editor-CxXW-QO8.js",
"sha256": "3cda68688d097f4198daa63b06d46eeb60c60ceb9399c4a83c586adae61852e3",
"rawBytes": 3826,
"gzipBytes": 1583,
"gzipBytes": 1584,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-C2GpV0Jw.js",
"sha256": "f111011a8f45476386a3d0743402d3fad8b5f4d6d3b61be30036aa983f58e3f1",
"rawBytes": 83710,
"gzipBytes": 24084,
"path": "houseplan-assets/fr-CDWnrdfn.js",
"sha256": "74c6d85b8e5ae259fa0d40948e19848a3010caf717edea225a8f2129d7cf2227",
"rawBytes": 84059,
"gzipBytes": 24205,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-C1YZDmj6.js",
"sha256": "c6de4fca90b84eb8ab47f5e6e5b23d40f9929ca58ac58809cd1be4398e4a1f05",
"rawBytes": 1036950,
"gzipBytes": 291480,
"path": "houseplan-assets/houseplan-card-BTXh6SvG.js",
"sha256": "a2278cdaf370fb864adb820cfdbd884671ffa4b4ea61785f375ed384907d8562",
"rawBytes": 1039135,
"gzipBytes": 292060,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-DYuCBO-k.js",
"houseplan-assets/editor-CVgkVajn.js",
"houseplan-assets/fr-C2GpV0Jw.js",
"houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js",
"houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js"
"houseplan-assets/de-Cm5MlI_Y.js",
"houseplan-assets/editor-CxXW-QO8.js",
"houseplan-assets/fr-CDWnrdfn.js",
"houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js",
"houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-Bpo_GpxS.js",
"sha256": "0710c7a4a73f7a2532d041984b1294727abc5a1a76a17ab395c9f347ee1a05ab",
"path": "houseplan-assets/houseplan-editor-runtime-CmI5Go3m.js",
"sha256": "ccca4dc1b21b421cf2fb37a1bb27ab417492cf1d3478fa2ad8b0f555d9496798",
"rawBytes": 589868,
"gzipBytes": 154836,
"gzipBytes": 154837,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-DCUDjODM.js",
"sha256": "37bbf57bbaede7f9ff15617e537978faf5ab76de510a758cb286e016c6717676",
"path": "houseplan-assets/houseplan-onboarding-runtime-BB8OHZOL.js",
"sha256": "be730d372a4390126890a15fee9af9ae154ac94d7df4d7dd51a3b639128cab91",
"rawBytes": 28088,
"gzipBytes": 6956,
"gzipBytes": 6959,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-CfOh3CA7.js",
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/backdrop-pick-DxKIBNjn.js",
"houseplan-assets/houseplan-card-BTXh6SvG.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "4bdef27d1c8e8265021aca1648b32ba32265ed970b6743f8e9c89942dec5ed32",
"sha256": "ac8fe2593f1937bd0bdc86f131b0b58ac668f59092c70f0b3af4092edba54138",
"rawBytes": 1183,
"gzipBytes": 798,
"gzipBytes": 799,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-C1YZDmj6.js"
"houseplan-assets/houseplan-card-BTXh6SvG.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__="d2e52a98c720731b44a0fe51783aa8191a5218f555f1f86893701f28e99f200f";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-C1YZDmj6.js";class f extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="bd7029808eb6094140d9fa8245f50816cb452430a353f4231d16e02c97078a06";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-BTXh6SvG.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
.computeLabel=${e=>f[e.name]||e.name}
.computeLabel=${e=>h[e.name]||e.name}
@value-changed=${this._valueChanged}
></ha-form>`;return c`
${d(h.slice(0,3))}
${d(f.slice(0,3))}
${_?c`<div class="default-floor-error" role="alert"
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
${t(s,"editor.default_floor_missing",{id:_})}
</div>`:a}
${d(h.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
${d(f.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d2e52a98c720731b44a0fe51783aa8191a5218f555f1f86893701f28e99f200f";try{await import("./houseplan-assets/houseplan-card-C1YZDmj6.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__="bd7029808eb6094140d9fa8245f50816cb452430a353f4231d16e02c97078a06";try{await import("./houseplan-assets/houseplan-card-BTXh6SvG.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)}
+4
View File
@@ -2,6 +2,10 @@
## Unreleased
- When a robot vacuum is cleaning but its current map has no floor assigned, is
not calibrated, is ambiguous or points at a deleted space, the dock now shows
an amber warning with the exact reason instead of the plan silently showing no
robot ([#162](https://github.com/Matysh/houseplan-card/issues/162)).
- The device editor now has a “Maps and floors” block: each map a robot vacuum
reports can be assigned to a space, calibrated against that space and moved or
removed later, with the dock left where it is
+5
View File
@@ -8,6 +8,11 @@
## Не выпущено
- Если робот убирает, а его текущая карта не сопоставлена этажу, не
откалибрована, неоднозначна или ссылается на удалённое пространство, у дока
появляется янтарное предупреждение с точной причиной — вместо плана, на
котором робота просто нет
([#162](https://github.com/Matysh/houseplan-card/issues/162)).
- В редакторе устройства появился блок «Карты и этажи»: каждую карту робота
можно сопоставить пространству, откалибровать её именно в этом пространстве, а
позже перенести или удалить — док при этом остаётся на своём месте
+12
View File
@@ -342,6 +342,18 @@ const MUTANT_DEFINITIONS = [
replace: ' if False:',
}],
},
{
id: 'vacuum-route-warning-stays-silent',
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
+ '&& node --test test/vacuum-routes.test.mjs',
because: 'a moving robot that is drawn nowhere must say so on the dock: silence reads as '
+ '"not cleaning", and the user cannot discover an unassigned map (#162)',
patches: [{
file: 'src/vacuum-routes.ts',
find: " case 'unmapped': case 'needs_calibration': case 'ambiguous': case 'missing_space':\n return resolution.kind;",
replace: " case 'нет такого':\n return resolution.kind as any;",
}],
},
{
id: 'area-snapshot-cleanup-ignores-authority',
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
+20 -1
View File
@@ -122,7 +122,7 @@ import {
type VacSourceResolution, type VacSourceStatus,
} from './vacuum';
import {
effectiveRoutes, observedMapIds, resolveRoute, planVacuumOverlay,
effectiveRoutes, observedMapIds, resolveRoute, planVacuumOverlay, routeWarningKey,
type VacuumMapRoute, type VacuumRouteResolution,
} from './vacuum-routes';
import {
@@ -12525,9 +12525,28 @@ export class HouseplanCard extends LitElement {
newDeviceTitle: this._t('device.new'),
disabledTitle: presentation.haDisabled ? ghostLabel : '',
})}
${this._vacRouteBadge(d)}
</div>`;
}
/**
* The dock says out loud that the moving robot is drawn nowhere (#162).
*
* Silence would be the worse failure: a plan with no robot on it reads as
* "the robot is not cleaning", and the user has no way to learn that a map
* simply has no floor assigned.
*/
private _vacRouteBadge(d: DevItem): TemplateResult | typeof nothing {
if (!this._isVacDev(d) || d.hidden) return nothing;
if (normalizeDeviceDisplay(d.marker?.display) === 'static_icon') return nothing;
const fact = this._renderDeviceSnapshot?.facts.get(`vacuum:${d.id}`) as any;
const reason = routeWarningKey(fact?.resolution, this._vacRt.get(d.id)?.moving ?? false);
if (!reason) return nothing;
const text = this._t((`vac.route_warn_${reason}`) as any);
return html`<span class="vacwarn" role="img" aria-label=${text} title=${text}>
<ha-icon icon="mdi:alert-outline"></ha-icon></span>`;
}
/** Clean-floor area shown in the room tooltip (same rule as resize labels). */
private _roomArea(r: RoomCfg): string | null {
const poly = roomPoly(r);
+4
View File
@@ -815,6 +815,10 @@
"vac.live": "Aktuelle Position im Plan",
"vac.trail": "Roboterpfad anzeigen",
"vac.autocal_no_rooms": "Die Integration meldet keine Raumliste — öffnen Sie \"Manuell anpassen\"",
"vac.route_warn_unmapped": "Der Roboter reinigt eine Karte, die keiner Etage zugeordnet ist",
"vac.route_warn_needs_calibration": "Die Karte ist einer Etage zugeordnet, aber nicht kalibriert",
"vac.route_warn_ambiguous": "Zwei Karten sind nicht unterscheidbar — keine Etage gewählt",
"vac.route_warn_missing_space": "Der Raum dieser Karte wurde gelöscht",
"vac.autocal_no_match": "Raumnamen stimmten nicht überein (mindestens 3 müssen übereinstimmen) — öffnen Sie \"Manuell anpassen\"",
"vac.autocal_done": "Fertig: über {rooms} Räume zugeordnet. Eine Reinigung starten und das Ergebnis prüfen.",
"vac.cal_need_pos": "Der Roboter meldet keine Koordinaten — starten Sie eine Reinigung und pausieren Sie sie",
+4
View File
@@ -815,6 +815,10 @@
"vac.live": "Live position on the plan",
"vac.trail": "Show the robot's path",
"vac.autocal_no_rooms": "The integration reports no room list — open “Fit manually”",
"vac.route_warn_unmapped": "The robot is cleaning a map that is not assigned to any floor",
"vac.route_warn_needs_calibration": "The map is assigned to a floor but not calibrated",
"vac.route_warn_ambiguous": "Two maps are indistinguishable — no floor was chosen",
"vac.route_warn_missing_space": "The space of this map was deleted",
"vac.autocal_no_match": "Room names did not match (need ≥3 in common) — open “Fit manually”",
"vac.autocal_done": "Done: bound via {rooms} rooms. Start a cleanup and check",
"vac.cal_need_pos": "The robot is not reporting coordinates — start a cleanup and pause it",
+4
View File
@@ -815,6 +815,10 @@
"vac.live": "Position en direct sur le plan",
"vac.trail": "Afficher le trajet du robot",
"vac.autocal_no_rooms": "L’intégration ne fournit aucune liste de pièces — ouvrez « Ajuster manuellement »",
"vac.route_warn_unmapped": "Le robot nettoie une carte qui n'est assignée à aucun étage",
"vac.route_warn_needs_calibration": "La carte est assignée à un étage mais n'est pas calibrée",
"vac.route_warn_ambiguous": "Deux cartes sont indiscernables — aucun étage choisi",
"vac.route_warn_missing_space": "L'espace de cette carte a été supprimé",
"vac.autocal_no_match": "Les noms de pièces ne correspondent pas (il en faut ≥3 en commun) — ouvrez « Ajuster manuellement »",
"vac.autocal_done": "Terminé : association via {rooms} pièces. Lancez un nettoyage et vérifiez",
"vac.cal_need_pos": "Le robot ne transmet pas de coordonnées — lancez un nettoyage puis mettez-le en pause",
+4
View File
@@ -815,6 +815,10 @@
"vac.live": "Живая позиция на плане",
"vac.trail": "Показывать путь робота",
"vac.autocal_no_rooms": "Интеграция не отдаёт список комнат — откройте «Подогнать вручную»",
"vac.route_warn_unmapped": "Робот убирает на карте, которая не сопоставлена ни одному этажу",
"vac.route_warn_needs_calibration": "Карта сопоставлена этажу, но не откалибрована",
"vac.route_warn_ambiguous": "Две карты неразличимы — этаж не выбран",
"vac.route_warn_missing_space": "Пространство этой карты удалено",
"vac.autocal_no_match": "Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»",
"vac.autocal_done": "Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте",
"vac.cal_need_pos": "Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу",
+23
View File
@@ -463,6 +463,29 @@ export const devicesStyles = css`
color: var(--hp-muted);
font-size: var(--fs-s);
}
/* #162: the dock says the moving robot is drawn nowhere. Amber is not the
only signal — the alert glyph carries the same meaning without colour,
and the accessible name carries the exact reason. */
.vacwarn {
position: absolute;
top: -12%;
right: -12%;
width: calc(var(--device-base-size, 2.25cqw) * 0.42);
height: calc(var(--device-base-size, 2.25cqw) * 0.42);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: light-dark(#fff, #252525);
border: 1px solid #E5A100;
color: #E5A100;
z-index: 7;
pointer-events: none;
}
.vacwarn ha-icon {
--mdc-icon-size: calc(var(--device-base-size, 2.25cqw) * 0.32);
color: inherit;
}
/* live vacuum: a round puck, no badge plate, soft pulse (docs/VACUUM.md) */
.vacpuck {
position: absolute;
+20
View File
@@ -331,3 +331,23 @@ export function planVacuumOverlay(input: VacuumOverlayInput): VacuumOverlayPlan
const previous = previousAllowed ? normalizeRouteMatrix(previousRoute!.calibration) : null;
return { live, currentRunMatches, previous };
}
export type VacuumRouteWarning = 'unmapped' | 'needs_calibration' | 'ambiguous' | 'missing_space';
/**
* Why a moving robot is drawn nowhere — the reason the dock badge announces.
*
* Only while the robot actually moves: a docked robot with an unmapped map is
* not a problem the user has to see right now, and a badge that is always on
* is a badge nobody reads.
*/
export function routeWarningKey(
resolution: VacuumRouteResolution | null | undefined, moving: boolean,
): VacuumRouteWarning | null {
if (!moving || !resolution) return null;
switch (resolution.kind) {
case 'unmapped': case 'needs_calibration': case 'ambiguous': case 'missing_space':
return resolution.kind;
default: return null;
}
}
+17 -1
View File
@@ -4,7 +4,7 @@ import { readFileSync } from 'node:fs';
import {
validateMarkerRoutes, effectiveRoutes, legacyRouteId, resolveRoute, adoptLegacyRun,
normalizeRouteMatrix, isEntityIdLike, VAC_ROUTE_LIMIT, VAC_ROUTE_ERROR,
observedMapIds, runRoute, planVacuumOverlay,
observedMapIds, runRoute, planVacuumOverlay, routeWarningKey,
} from '../test-build/vacuum-routes.js';
import {
newRouteId, addRoute, removeRoute, changeRouteSpace, saveRouteCalibration, convertLegacyRoutes,
@@ -286,3 +286,19 @@ test('конверсия легаси переносит ВСЕ матрицы
assert.equal(convertLegacyRoutes(marker, 'floor1', '', () => 'vr'), null, 'без источника конверсии нет');
assert.equal(convertLegacyRoutes({ calibration: {} }, 'floor1', 'camera.robot', () => 'vr'), null);
});
test('док предупреждает только о движущемся роботе, которому некуда рисоваться', () => {
const cases = {
unmapped: { kind: 'unmapped', source: 'camera.robot', mapId: 'm9' },
needs_calibration: { kind: 'needs_calibration', route: twoFloors[0] },
ambiguous: { kind: 'ambiguous', routeIds: ['r1', 'r2'] },
missing_space: { kind: 'missing_space', route: twoFloors[0] },
};
for (const [expected, resolution] of Object.entries(cases)) {
assert.equal(routeWarningKey(resolution, true), expected);
assert.equal(routeWarningKey(resolution, false), null, 'док молчит, пока робот стоит');
}
assert.equal(routeWarningKey({ kind: 'ready', route: twoFloors[0] }, true), null);
assert.equal(routeWarningKey({ kind: 'none' }, true), null);
assert.equal(routeWarningKey(null, true), null);
});