fix: a cold View survives a telemetry-bearing vacuum (#358)

Systematic audit after #357 ("can there be more bugs with this root
cause?"): _vacMapId was the one remaining hard stub reachable from the
eager View path. It runs inside willUpdate for every vacuum whose
integration reports live telemetry (Tasshack, XCME, Valetudo), so on a
cold tab the #337 stub threw there and the exception took the whole Lit
update cycle with it — the card froze on its very first frame. The demo
mower has no position attributes, telemetry resolved to null, and every
existing smoke (warm and cold) sailed past the branch.

The card now owns the implementation (both dependencies — _vacEntity and
vacMapIdWithFallback — were already eager); the editor runtime delegates
back to the host. The HP-1541-01 invariant (selected_map: 0 is a real map
id, nullish not truthy) moves verbatim and is pinned by the new smoke.

Hardened alongside (audit Lows): _decorShapeDown gets the same
cold-tab guard its twin _decorShapeDbl received in #337 — decor shapes
render in View and CSS pointer-events alone must not be what prevents a
throw; the _vacCalConfirm dialog renders behind the same _editorRuntime
gate as every other editor dialog instead of relying on the implicit
"only the runtime ever sets it".

smoke_cold_view_vacuum: cold tab, vacuum with vacuum_position and
selected_map: 0 — the card commits three successive telemetry frames
(willUpdate alive, not merely the first paint), map id resolves to '0',
no editor chunk requested, a decor pointerdown is a quiet no-op. A
registry mutant restores the delegation and is killed by that smoke.

Issue: #358
User-Visible: yes
This commit is contained in:
Codex
2026-08-28 21:49:50 +03:00
parent 5a662d829d
commit bb3c1fd12f
25 changed files with 522 additions and 364 deletions
@@ -1,111 +1,111 @@
{
"schema": 1,
"fingerprint": "f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467",
"fingerprint": "c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-Bit484fR.js",
"houseplan-assets/houseplan-card-CmrzFyoP.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 271143,
"initialViewGzipBytes": 271168,
"lazyFiles": [
"houseplan-assets/de-Bm8vPaKB.js",
"houseplan-assets/editor-CcQvuoGr.js",
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/de-B04VJMDv.js",
"houseplan-assets/editor-C0gbLi11.js",
"houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"lazyGzipBytes": 165964,
"lazyGzipBytes": 165928,
"lazyEditorFiles": [
"houseplan-assets/editor-CcQvuoGr.js",
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/editor-C0gbLi11.js",
"houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"lazyEditorGzipBytes": 136854,
"lazyEditorGzipBytes": 136819,
"lazyOnboardingFiles": [
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"lazyOnboardingGzipBytes": 7198,
"lazyOnboardingGzipBytes": 7201,
"lazyLocaleFiles": [
"houseplan-assets/de-Bm8vPaKB.js"
"houseplan-assets/de-B04VJMDv.js"
],
"lazyLocaleGzipBytes": 22465,
"lazyLocaleGzipBytes": 22462,
"files": [
{
"path": "houseplan-assets/de-Bm8vPaKB.js",
"sha256": "e8e5acd5cda2ac637e178a08ae4eb4ea1127472b736b8cab7c19ae2ab6b8bb0a",
"path": "houseplan-assets/de-B04VJMDv.js",
"sha256": "0b2029018d30a920e040e0e8cc0213031782aecec97550726a2832bcba4c571e",
"rawBytes": 73919,
"gzipBytes": 22465,
"gzipBytes": 22462,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-CcQvuoGr.js",
"sha256": "661537fb7547eaaf3d5c6cfd040db88c74ae95b40554998ef0360c8bccfe3de0",
"path": "houseplan-assets/editor-C0gbLi11.js",
"sha256": "71b26e609aa270fa02c90b56757e82b9a081f23a3605eacd0dd3ae32e3bb5782",
"rawBytes": 3831,
"gzipBytes": 1583,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-Bit484fR.js",
"sha256": "9069adcc9c0be4411d89d822d3610de6e1154598693cb3cb8575d9eaa6743bcc",
"rawBytes": 956442,
"gzipBytes": 270390,
"path": "houseplan-assets/houseplan-card-CmrzFyoP.js",
"sha256": "19d936972988d0561b927cad612ee34675783cf0d0c1bfcd7d796156aedc7305",
"rawBytes": 956498,
"gzipBytes": 270414,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-Bm8vPaKB.js",
"houseplan-assets/editor-CcQvuoGr.js",
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js"
"houseplan-assets/de-B04VJMDv.js",
"houseplan-assets/editor-C0gbLi11.js",
"houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"sha256": "30bd78227922512c03aa6d0177f776b17508048ff311eac0b8b993e4c3ca0602",
"rawBytes": 520370,
"gzipBytes": 134718,
"path": "houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"sha256": "cff3061e998268f86098047985b0c3b4f0d14740e8981b0a46e43ffa98a90590",
"rawBytes": 520292,
"gzipBytes": 134682,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
"sha256": "3115311691b5b2164c3fd4d00d311040d1c430a2c0cb6637fafbf1a6775c328a",
"path": "houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js",
"sha256": "bbf33f54ddf0b5c9025351822042f17615ef9195a180ddc7616c9c1da7f11142",
"rawBytes": 26356,
"gzipBytes": 6645,
"gzipBytes": 6647,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/space-deletion-1ap2PeUr.js",
"sha256": "f972b71546589bc2601e2432022d835bf10349f876c32d81fa957767ba72c7c4",
"path": "houseplan-assets/space-deletion-BfZFFcpf.js",
"sha256": "ffae5e9921eacc23faf7db2551eb327f76f99677c549adf89ab3b9c7d5de27d3",
"rawBytes": 911,
"gzipBytes": 553,
"gzipBytes": 554,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "a1123da36e218354a3633fe550fe099f6ffb0fb7062dec34c0b38ba3b912ab6c",
"sha256": "f1f688dceb0fc517658234151a7999de73f9111b8249b51a8997c6d21915d2b4",
"rawBytes": 1089,
"gzipBytes": 753,
"gzipBytes": 754,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js"
],
"dynamicImports": []
}
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467";import{c as e,l as o,t,c$ as s,A as a,d0 as i,d1 as l,d2 as n,E as r,b as c}from"./houseplan-card-Bit484fR.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__="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";import{c as e,l as o,t,c_ as s,A as a,c$ as i,d0 as l,d1 as n,E as r,b as c}from"./houseplan-card-CmrzFyoP.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}
@@ -1 +1 @@
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),a=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:a,count:a.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467";export{t as a,e as c,o as i,r as s,s as t};
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";export{t as a,e as c,o as i,r as s,s as t};
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467";try{await import("./houseplan-assets/houseplan-card-Bit484fR.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).":"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__="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";try{await import("./houseplan-assets/houseplan-card-CmrzFyoP.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).":"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)}
+111
View File
@@ -0,0 +1,111 @@
// #358: a vacuum with REAL telemetry (position/map — Tasshack, XCME…) must not
// kill a cold View. The #337 stub _vacMapId threw inside willUpdate on a tab
// that never loaded the editor runtime, and the exception took the whole Lit
// update cycle with it: the card froze on its first frame. The demo fixture's
// mower has no position attributes, so telemetry was null and every existing
// smoke (warm AND cold) sailed past this branch.
import { launchColdView, checkAll, finish } from './serve.mjs';
const { page, browser } = await launchColdView();
const requested = [];
page.on('request', (request) => requested.push(new URL(request.url()).pathname));
let pageErrors = 0;
page.on('pageerror', (error) => { pageErrors++; console.log('EXC', error.message); });
const out = await page.evaluate(async () => {
const card = window.__card;
const space = card._model[0];
const room = space.rooms.find((candidate) => candidate.id && candidate.area);
const vacState = (x, y) => ({
entity_id: 'vacuum.real',
state: 'cleaning',
attributes: {
friendly_name: 'Робот',
vacuum_position: { x, y, a: 90 },
// no map_name/current_map in telemetry — the entity's selected_map: 0
// must win as the map id (HP-1541-01: zero is a real map, not falsy)
selected_map: 0,
},
});
const makeHass = (x, y) => ({
...card.hass,
states: { ...card.hass.states, 'vacuum.real': vacState(x, y) },
entities: {
...card.hass.entities,
'vacuum.real': {
entity_id: 'vacuum.real', device_id: 'd_realvac',
platform: 'tasshack', disabled_by: null,
},
},
devices: {
...card.hass.devices,
d_realvac: { id: 'd_realvac', name: 'Робот', area_id: room.area },
},
});
const cfg = structuredClone(card._serverCfg);
cfg.markers = [...(cfg.markers || []), {
id: 'd_realvac', binding: 'device:d_realvac', name: 'Робот',
space: space.id, area: room.area, room_id: room.id,
}];
// The decor guard (#358 К2): one shape rendered in View must be a quiet
// no-op on pointerdown, not an exception behind CSS pointer-events.
const sp = cfg.spaces.find((s) => s.id === space.id) || cfg.spaces[0];
sp.decor = [...(sp.decor || []), { id: 'dc_cold', kind: 'line', a: [200, 200], b: [400, 300] }];
const baseWS = card.hass.callWS.bind(card.hass);
const hass = makeHass(120, 340);
hass.callWS = async (message) => message.type === 'houseplan/config/get'
? {
config: cfg, rev: card._cfgRev, can_write: true,
virtual_lights: { rev: 0, config_rev: card._cfgRev, off: [] },
}
: baseWS(message);
card.hass = hass;
await card._reloadConfigOnly(true);
card._regSignature = '';
card._maybeRebuildDevices();
card.requestUpdate();
await card.updateComplete;
const waitFor = async (predicate, timeout = 6000) => {
const started = Date.now();
while (!predicate() && Date.now() - started < timeout) {
await new Promise((resolve) => setTimeout(resolve, 40));
}
return !!predicate();
};
const out = { runtimeColdBefore: !card._editorRuntime };
out.vacuumDeviceBuilt = await waitFor(() =>
card._devices.some((device) => device.id === 'd_realvac'));
out.firstFrameAlive = await waitFor(() =>
!!card.renderRoot.querySelector('.dev[data-id="d_realvac"]'));
// The proof that willUpdate SURVIVES telemetry: push fresh robot positions
// and require the card to keep committing frames, not just the first one.
const snapshotOf = () => card._renderDeviceSnapshot?.facts.get('vacuum:d_realvac');
const before = snapshotOf();
card.hass = { ...makeHass(500, 700), callWS: hass.callWS };
await card.updateComplete;
out.secondFrameCommitted = await waitFor(() => {
const fact = snapshotOf();
return !!fact && fact !== before && fact.telemetry?.pos?.x === 500;
});
// HP-1541-01 preserved through the move: selected_map 0 resolves to '0'.
out.mapIdResolved = snapshotOf()?.mapId === '0';
card.hass = { ...makeHass(800, 200), callWS: hass.callWS };
await card.updateComplete;
out.thirdFrameCommitted = await waitFor(() =>
snapshotOf()?.telemetry?.pos?.x === 800);
// К2: decor shape pointerdown on a cold tab is a quiet no-op.
const shape = card.renderRoot.querySelector('.dshape');
out.decorShapeRendered = !!shape;
shape?.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, composed: true }));
await card.updateComplete;
out.runtimeColdAfter = !card._editorRuntime;
return out;
});
out.noEditorRuntimeRequest = requested.every((path) => !/houseplan-editor-runtime-/.test(path));
out.noPageErrors = pageErrors === 0;
checkAll(out);
await finish(browser, out);
+49 -49
View File
@@ -1,111 +1,111 @@
{
"schema": 1,
"fingerprint": "f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467",
"fingerprint": "c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-Bit484fR.js",
"houseplan-assets/houseplan-card-CmrzFyoP.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 271143,
"initialViewGzipBytes": 271168,
"lazyFiles": [
"houseplan-assets/de-Bm8vPaKB.js",
"houseplan-assets/editor-CcQvuoGr.js",
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/de-B04VJMDv.js",
"houseplan-assets/editor-C0gbLi11.js",
"houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"lazyGzipBytes": 165964,
"lazyGzipBytes": 165928,
"lazyEditorFiles": [
"houseplan-assets/editor-CcQvuoGr.js",
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/editor-C0gbLi11.js",
"houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"lazyEditorGzipBytes": 136854,
"lazyEditorGzipBytes": 136819,
"lazyOnboardingFiles": [
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"lazyOnboardingGzipBytes": 7198,
"lazyOnboardingGzipBytes": 7201,
"lazyLocaleFiles": [
"houseplan-assets/de-Bm8vPaKB.js"
"houseplan-assets/de-B04VJMDv.js"
],
"lazyLocaleGzipBytes": 22465,
"lazyLocaleGzipBytes": 22462,
"files": [
{
"path": "houseplan-assets/de-Bm8vPaKB.js",
"sha256": "e8e5acd5cda2ac637e178a08ae4eb4ea1127472b736b8cab7c19ae2ab6b8bb0a",
"path": "houseplan-assets/de-B04VJMDv.js",
"sha256": "0b2029018d30a920e040e0e8cc0213031782aecec97550726a2832bcba4c571e",
"rawBytes": 73919,
"gzipBytes": 22465,
"gzipBytes": 22462,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-CcQvuoGr.js",
"sha256": "661537fb7547eaaf3d5c6cfd040db88c74ae95b40554998ef0360c8bccfe3de0",
"path": "houseplan-assets/editor-C0gbLi11.js",
"sha256": "71b26e609aa270fa02c90b56757e82b9a081f23a3605eacd0dd3ae32e3bb5782",
"rawBytes": 3831,
"gzipBytes": 1583,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-Bit484fR.js",
"sha256": "9069adcc9c0be4411d89d822d3610de6e1154598693cb3cb8575d9eaa6743bcc",
"rawBytes": 956442,
"gzipBytes": 270390,
"path": "houseplan-assets/houseplan-card-CmrzFyoP.js",
"sha256": "19d936972988d0561b927cad612ee34675783cf0d0c1bfcd7d796156aedc7305",
"rawBytes": 956498,
"gzipBytes": 270414,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-Bm8vPaKB.js",
"houseplan-assets/editor-CcQvuoGr.js",
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js"
"houseplan-assets/de-B04VJMDv.js",
"houseplan-assets/editor-C0gbLi11.js",
"houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
"sha256": "30bd78227922512c03aa6d0177f776b17508048ff311eac0b8b993e4c3ca0602",
"rawBytes": 520370,
"gzipBytes": 134718,
"path": "houseplan-assets/houseplan-editor-runtime-BbpRSsiv.js",
"sha256": "cff3061e998268f86098047985b0c3b4f0d14740e8981b0a46e43ffa98a90590",
"rawBytes": 520292,
"gzipBytes": 134682,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
"sha256": "3115311691b5b2164c3fd4d00d311040d1c430a2c0cb6637fafbf1a6775c328a",
"path": "houseplan-assets/houseplan-onboarding-runtime-BkJZgs7A.js",
"sha256": "bbf33f54ddf0b5c9025351822042f17615ef9195a180ddc7616c9c1da7f11142",
"rawBytes": 26356,
"gzipBytes": 6645,
"gzipBytes": 6647,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js",
"houseplan-assets/space-deletion-1ap2PeUr.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js",
"houseplan-assets/space-deletion-BfZFFcpf.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/space-deletion-1ap2PeUr.js",
"sha256": "f972b71546589bc2601e2432022d835bf10349f876c32d81fa957767ba72c7c4",
"path": "houseplan-assets/space-deletion-BfZFFcpf.js",
"sha256": "ffae5e9921eacc23faf7db2551eb327f76f99677c549adf89ab3b9c7d5de27d3",
"rawBytes": 911,
"gzipBytes": 553,
"gzipBytes": 554,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "a1123da36e218354a3633fe550fe099f6ffb0fb7062dec34c0b38ba3b912ab6c",
"sha256": "f1f688dceb0fc517658234151a7999de73f9111b8249b51a8997c6d21915d2b4",
"rawBytes": 1089,
"gzipBytes": 753,
"gzipBytes": 754,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-Bit484fR.js"
"houseplan-assets/houseplan-card-CmrzFyoP.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467";import{c as e,l as o,t,c$ as s,A as a,d0 as i,d1 as l,d2 as n,E as r,b as c}from"./houseplan-card-Bit484fR.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__="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";import{c as e,l as o,t,c_ as s,A as a,c$ as i,d0 as l,d1 as n,E as r,b as c}from"./houseplan-card-CmrzFyoP.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}
@@ -1 +1 @@
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),a=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:a,count:a.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467";export{t as a,e as c,o as i,r as s,s as t};
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";export{t as a,e as c,o as i,r as s,s as t};
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467";try{await import("./houseplan-assets/houseplan-card-Bit484fR.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).":"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__="c673cca61c08a44252a2303b34ba28b96411e9d5fddb18c938f79fbbc00ccd7f";try{await import("./houseplan-assets/houseplan-card-CmrzFyoP.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).":"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)}
+6
View File
@@ -2,6 +2,12 @@
## Unreleased
- A robot vacuum that reports its live position no longer freezes the card
on a freshly opened page: the map-id lookup ran inside the render cycle
and required editor code that a cold tab has not loaded yet, so the very
first frame was also the last one
([#358](https://github.com/Matysh/houseplan-card/issues/358)).
- Tapping a device on a freshly opened page works again without first
visiting any editor screen. A wall switch controlling virtual light sources
(and every other tap-to-toggle device) silently ignored the tap on a cold
+6
View File
@@ -8,6 +8,12 @@
## Не выпущено
- Робот-пылесос с живой позицией больше не замораживает карточку на только
что открытой странице: определение id карты выполнялось внутри цикла
отрисовки и требовало код редактора, который холодная вкладка ещё не
загрузила, — первый кадр оказывался последним
([#358](https://github.com/Matysh/houseplan-card/issues/358)).
- Нажатие на устройство на только что открытой странице снова работает без
предварительного захода в какой-либо редактор. Выключатель, управляющий
виртуальными источниками света (и любое другое устройство с тап-переключением),
Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 198 KiB

+12 -12
View File
@@ -3,7 +3,7 @@
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
"oxipng": null,
"sourceFingerprint": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceFingerprint": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"captureScriptSha256": "fe76c8e2e27501e9a377d7aa15dd6cba6a018d55c07deb602dbee097f0a0a5ba",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -15,8 +15,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"imageSha256": "0ca982495a7749907c48ebbddfd089c54385a4347f79f64e94af2c5a8d5c03eb"
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "78d328498bd17a9ee1ec44b389d244b61fca9ac52c9aad6631aa85c53233fec0"
},
"view-touch": {
"file": "02-view-touch.png",
@@ -26,7 +26,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "3be239a5a494f6776540a9c0049694ee6f9d2ba927c8bcb4b26ee5a69a228a7d"
},
"space-create": {
@@ -37,7 +37,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "a55b463055a565892ca139014e314a8f63fb5301c87fff2e48d7e69210fe9b6c"
},
"room-contour-close": {
@@ -48,7 +48,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "dfad9a27371cfa397a3fd8dce7005f42d24649743f5322105ee894067d3a9f16"
},
"plan-context-tray": {
@@ -59,7 +59,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "b75f348c632296da5c2fc142d53df613f757e1516acac315be50f5b4557729cc"
},
"device-editor": {
@@ -70,7 +70,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "6b9cd281450e7782cff292f11efc58d227bedd0df86afc3be987feb2edec9dc3"
},
"device-display-preview": {
@@ -81,7 +81,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "6f628abe6b89d7c4b352e6d73ac0b126526f6fdba5927bb774c5b884e484ca19"
},
"background-editor": {
@@ -92,7 +92,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "a1d1a905f1410f73231d2ea5d1dfbd8ae79c2fe8b0a79cff87ebb977ac7a3103"
},
"room-card": {
@@ -103,7 +103,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "48a5685039a5e0de2a28190d857b193c86e2e63606e3c7a8c40fc9d2b190da4e"
},
"device-info": {
@@ -114,7 +114,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
"sourceSha256": "2bb218477956888a84706e4a8a76248b823d70dc26fc80bcc5272c30e4556a38",
"imageSha256": "29a342811aaedaebc54ba337792d60cb454a7cf4c3063b210f6c4bafe0a4d87b"
}
}
+14
View File
@@ -680,6 +680,20 @@ const MUTANT_DEFINITIONS = [
replace: ' this._languageFailureUnsub = undefined;',
}],
},
{
id: 'cold-view-vacuum-mapid-delegated',
guard: 'node demo/smoke_cold_view_vacuum.mjs',
because: 'map-id resolution runs inside willUpdate for every vacuum with telemetry — the '
+ '#337 stub threw there on a cold tab and froze the whole Lit update cycle (#358); the '
+ 'demo mower has no position attributes, so only this telemetry-bearing cold smoke sees it',
patches: [{
file: 'src/houseplan-card.ts',
find: ' const ve = this._vacEntity(d);\n'
+ ' const sel = ve ? planHass?.states?.[ve]?.attributes?.selected_map : null;\n'
+ ' return vacMapIdWithFallback(tele.mapId, sel);',
replace: " return this._editorRuntimeOrThrow()._vacMapId(d, tele, planHass);",
}],
},
{
id: 'cold-view-toggle-delegated-to-runtime',
guard: 'node demo/smoke_cold_view_toggle.mjs',
+8
View File
@@ -44,6 +44,14 @@ export const SMOKE_LINKS = [
+ 'requests and four root render gates: neutral cold frame, atomic German commit, page-cache '
+ 'reuse and the bounded retry-to-English path cannot be proven from pure registry tests (#348)',
},
{
symbols: ['vacMapIdWithFallback', 'vacMapIdFromAttrs', 'readVacTelemetry'],
smokes: ['smoke_cold_view_vacuum.mjs'],
because: 'the telemetry-bearing cold scenario proves the vacuum render path (willUpdate '
+ 'snapshot, map-id fallback with selected_map: 0, live position frames) survives without '
+ 'the lazy editor runtime — the demo mower has no position attributes, so every other '
+ 'smoke skips this branch (#358)',
},
{
symbols: [
'resolveToggleIntent', 'toggleOperation', 'projectedTapAction',
+19 -5
View File
@@ -7628,7 +7628,11 @@ export class HouseplanCard extends LitElement {
/** Select tool: pointerdown on a shape starts moving it. */
private _decorShapeDown(ev: PointerEvent, shape: DecorShape): void {
return this._editorRuntimeOrThrow()._decorShapeDown(ev, shape);
// #358: decor shapes render in View too; CSS pointer-events alone must
// not be what keeps a cold tab from throwing (its twin _decorShapeDbl
// got this guard in #337 already).
if (!this._editorRuntime) return;
return this._editorRuntime._decorShapeDown(ev, shape);
}
private _decorMoveUpdate(ev: PointerEvent): void {
@@ -11348,7 +11352,7 @@ export class HouseplanCard extends LitElement {
: nothing}
${this._deviceInbox ? this._editorRuntime ? this._renderDeviceInbox() : nothing : nothing}
${this._markerDialog ? this._editorRuntime ? this._renderMarkerDialog() : nothing : nothing}
${this._vacCalConfirm ? html`<hp-dialog .hass=${this.hass}
${this._vacCalConfirm ? this._editorRuntime ? html`<hp-dialog .hass=${this.hass}
.title=${this._t('vac.residual_title')} icon="mdi:map-marker-alert-outline"
dismiss-on-scrim @hp-close=${() => (this._vacCalConfirm = null)}>
<div class="body">
@@ -11362,7 +11366,7 @@ export class HouseplanCard extends LitElement {
<ha-icon icon="mdi:check"></ha-icon>${this._t('vac.apply_proposal')}
</button>
</div>
</hp-dialog>` : nothing}
</hp-dialog>` : nothing : nothing}
${this._infoCard ? this._renderInfoCard() : nothing}
${this._rulesDialog ? this._editorRuntime ? this._renderRulesDialog() : nothing : nothing}
${this._settingsDialog ? this._editorRuntime ? this._renderSettingsDialog() : nothing : nothing}
@@ -11704,8 +11708,18 @@ export class HouseplanCard extends LitElement {
* human-readable one on the vacuum entity (selected_map, verified against a
* live X50 Master) — without this both floors would share one matrix.
*/
private _vacMapId(d: DevItem, tele: { mapId: string }, planHass = this._planHass): string {
return this._editorRuntimeOrThrow()._vacMapId(d, tele, planHass);
/**
* #358: map-id resolution belongs to the eager View card — it runs inside
* willUpdate for every vacuum with telemetry, and the #337 stub killed the
* whole Lit update cycle on a cold tab. Same move as #357: the card owns
* the implementation, the editor runtime delegates back.
*/
public _vacMapId(d: DevItem, tele: { mapId: string }, planHass = this._planHass): string {
// HP-1541-01: nullish, not truthy — selected_map: 0 is a real map id and
// must equal what trails.py resolve_map_id stores server-side.
const ve = this._vacEntity(d);
const sel = ve ? planHass?.states?.[ve]?.attributes?.selected_map : null;
return vacMapIdWithFallback(tele.mapId, sel);
}
/** Persist a solved matrix into marker.vacuum.calibration[mapId].
+4 -5
View File
@@ -1095,6 +1095,7 @@ export interface HouseplanEditorHostPort {
_vacCalConfirm: { markerId: string; source: string; mapId: string; matrix: Affine; rooms: number; error: string; } | null;
_vacEnsureMarker: (d: DevItem) => Marker | null;
_vacEntity: (d: DevItem) => string | null;
_vacMapId: (d: DevItem, tele: { mapId: string }, planHass?: any) => string;
_vacFit: { markerId: string; source: string; mapId: string; p: FitParams; drag: null | { kind: "move" | "scale"; sx: number; sy: number; p0: FitParams; fx: number; fy: number; }; } | null;
_vacOpenAllCameras: (d: DevItem) => void;
_vacRt: Map<string, { trail: VacPt[]; lastKey: string; lastTs: number; moving: boolean; jump: boolean; endedTs: number; lastPos: VacPt | null; }>;
@@ -9977,11 +9978,9 @@ public _renderVacSection(dlg: any): TemplateResult | typeof nothing {
}
public _vacMapId(d: DevItem, tele: { mapId: string }, planHass = this.host._planHass): string {
// HP-1541-01: nullish, not truthy — selected_map: 0 is a real map id and
// must equal what trails.py resolve_map_id stores server-side.
const ve = this.host._vacEntity(d);
const sel = ve ? planHass?.states?.[ve]?.attributes?.selected_map : null;
return vacMapIdWithFallback(tele.mapId, sel);
// #358: the View card owns map-id resolution — it runs inside willUpdate
// for every vacuum with telemetry on tabs that never load this runtime.
return this.host._vacMapId(d, tele, planHass);
}
public _vacSaveMatrix(markerId: string, source: string, mapId: string, matrix: Affine): boolean {