fix: a cold View resolves taps without the lazy editor runtime (#357)
Field report from the dacha: the wall switch "Гостиная основной свет", whose controls name three virtual light sources, periodically ignored taps — no toggle, no glow — until its settings dialog was opened once with no changes. "Periodically" was every fresh tab: the #337 lazy split left _toggleIntent (and the confirm-line helpers) on the card as stubs delegating into the editor runtime, so a plain View tap on a cold tab threw `Houseplan editor runtime is not loaded` synchronously inside the click handler. Opening any editor surface loaded the runtime and "healed" the tab for its lifetime. The View card now owns toggle resolution: _toggleIntent calls resolveToggleIntent directly (device-toggle.ts was already in the initial graph; the card owns _planHass/_fullRegistryHass/_virtualLights), and _toggleStateText/_toggleConfirmationStateText/_toggleConfirmationLines moved with it. The editor runtime delegates back to the host — one source of truth, editor consumers (dialog preview, hint lines) unchanged. Every product smoke preloads the runtime, so none of them could see this class of regression. The new smoke_cold_view_toggle mirrors the field config on a genuinely cold tab: a real switch drives three passive virtual lamps with one tap, a controlled lamp drives its switch back, tap_confirm renders its state lines and confirms, and the editor chunk is never requested. A registry mutant restores the old delegation and is killed by that smoke. Issue: #357 User-Visible: yes
@@ -1,97 +1,97 @@
|
|||||||
{
|
{
|
||||||
"schema": 1,
|
"schema": 1,
|
||||||
"fingerprint": "c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43",
|
"fingerprint": "f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467",
|
||||||
"entry": "houseplan-card.js",
|
"entry": "houseplan-card.js",
|
||||||
"initialViewFiles": [
|
"initialViewFiles": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"houseplan-card.js"
|
"houseplan-card.js"
|
||||||
],
|
],
|
||||||
"initialViewGzipBytes": 270793,
|
"initialViewGzipBytes": 271143,
|
||||||
"lazyFiles": [
|
"lazyFiles": [
|
||||||
"houseplan-assets/de-Cuf6hSGS.js",
|
"houseplan-assets/de-Bm8vPaKB.js",
|
||||||
"houseplan-assets/editor-Drr9z9rK.js",
|
"houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js",
|
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"lazyGzipBytes": 166311,
|
"lazyGzipBytes": 165964,
|
||||||
"lazyEditorFiles": [
|
"lazyEditorFiles": [
|
||||||
"houseplan-assets/editor-Drr9z9rK.js",
|
"houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"lazyEditorGzipBytes": 137204,
|
"lazyEditorGzipBytes": 136854,
|
||||||
"lazyOnboardingFiles": [
|
"lazyOnboardingFiles": [
|
||||||
"houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js",
|
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"lazyOnboardingGzipBytes": 7198,
|
"lazyOnboardingGzipBytes": 7198,
|
||||||
"lazyLocaleFiles": [
|
"lazyLocaleFiles": [
|
||||||
"houseplan-assets/de-Cuf6hSGS.js"
|
"houseplan-assets/de-Bm8vPaKB.js"
|
||||||
],
|
],
|
||||||
"lazyLocaleGzipBytes": 22462,
|
"lazyLocaleGzipBytes": 22465,
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/de-Cuf6hSGS.js",
|
"path": "houseplan-assets/de-Bm8vPaKB.js",
|
||||||
"sha256": "fff8ca0681b5dbd33a6dea7f5d05fbbebdec6caa6659f8b89bfc59fbb1ab50d1",
|
"sha256": "e8e5acd5cda2ac637e178a08ae4eb4ea1127472b736b8cab7c19ae2ab6b8bb0a",
|
||||||
"rawBytes": 73919,
|
"rawBytes": 73919,
|
||||||
"gzipBytes": 22462,
|
"gzipBytes": 22465,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/editor-Drr9z9rK.js",
|
"path": "houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"sha256": "045707acaa6535cf617dedd8492ed8a2285c2cbe995b0fc88f3e263682522a60",
|
"sha256": "661537fb7547eaaf3d5c6cfd040db88c74ae95b40554998ef0360c8bccfe3de0",
|
||||||
"rawBytes": 3831,
|
"rawBytes": 3831,
|
||||||
"gzipBytes": 1583,
|
"gzipBytes": 1583,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js"
|
"houseplan-assets/houseplan-card-Bit484fR.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"path": "houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"sha256": "1e09aeec8c7ba2de3e665b132e4a4ea8a1f411274b924b4655bd7c9654f94cac",
|
"sha256": "9069adcc9c0be4411d89d822d3610de6e1154598693cb3cb8575d9eaa6743bcc",
|
||||||
"rawBytes": 955358,
|
"rawBytes": 956442,
|
||||||
"gzipBytes": 270040,
|
"gzipBytes": 270390,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"dynamicImports": [
|
"dynamicImports": [
|
||||||
"houseplan-assets/de-Cuf6hSGS.js",
|
"houseplan-assets/de-Bm8vPaKB.js",
|
||||||
"houseplan-assets/editor-Drr9z9rK.js",
|
"houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js"
|
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"path": "houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"sha256": "9834239d2910161215d70d12147ad83a0fe1d3062abbe6ea9f5165feb8a20d78",
|
"sha256": "30bd78227922512c03aa6d0177f776b17508048ff311eac0b8b993e4c3ca0602",
|
||||||
"rawBytes": 521651,
|
"rawBytes": 520370,
|
||||||
"gzipBytes": 135068,
|
"gzipBytes": 134718,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js",
|
"path": "houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
|
||||||
"sha256": "cbd7d63ad1fdb1b16b58ba99cbc93a02d4ea6f70317a926018cffe56417c6ba2",
|
"sha256": "3115311691b5b2164c3fd4d00d311040d1c430a2c0cb6637fafbf1a6775c328a",
|
||||||
"rawBytes": 26356,
|
"rawBytes": 26356,
|
||||||
"gzipBytes": 6645,
|
"gzipBytes": 6645,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/space-deletion-CrURpMqK.js",
|
"path": "houseplan-assets/space-deletion-1ap2PeUr.js",
|
||||||
"sha256": "7c9566acfd9fa8531e97cc955cba29dffae614c3c5e114d6235c71d44b6811b1",
|
"sha256": "f972b71546589bc2601e2432022d835bf10349f876c32d81fa957767ba72c7c4",
|
||||||
"rawBytes": 911,
|
"rawBytes": 911,
|
||||||
"gzipBytes": 553,
|
"gzipBytes": 553,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
@@ -100,12 +100,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-card.js",
|
"path": "houseplan-card.js",
|
||||||
"sha256": "a69e76fc9673ef374d20aceba2d0d160b2a42e9d0b4bcfc3baf616a58c89b93f",
|
"sha256": "a1123da36e218354a3633fe550fe099f6ffb0fb7062dec34c0b38ba3b912ab6c",
|
||||||
"rawBytes": 1089,
|
"rawBytes": 1089,
|
||||||
"gzipBytes": 753,
|
"gzipBytes": 753,
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js"
|
"houseplan-assets/houseplan-card-Bit484fR.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43";import{c as e,l as o,t,d1 as s,A as a,d2 as i,d3 as l,d4 as n,E as r,b as c}from"./houseplan-card-DZo3gC2T.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__="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
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.data=${this._formData}
|
.data=${this._formData}
|
||||||
.schema=${e}
|
.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)),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__="c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43";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)),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};
|
||||||
@@ -1 +1 @@
|
|||||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43";try{await import("./houseplan-assets/houseplan-card-DZo3gC2T.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__="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)}
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
// #357: a plain View tap must work on a COLD tab that never loaded the lazy
|
||||||
|
// editor runtime (#337). The field failure (dacha, "Гостиная основной свет"):
|
||||||
|
// a real wall switch whose controls name three passive virtual lamps did
|
||||||
|
// nothing on tap — _toggleIntent was a stub delegating into the not-yet-loaded
|
||||||
|
// editor runtime and threw synchronously inside the click handler; opening any
|
||||||
|
// editor surface "healed" the tab. Every product smoke preloads the runtime
|
||||||
|
// (launch()), so only a cold-view scenario can see this class of regression.
|
||||||
|
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 virtualIds = ['v_cold_1', 'v_cold_2', 'v_cold_3'];
|
||||||
|
const controllerId = 'd_kettle'; // real switch device — the wall-switch analog
|
||||||
|
const confirmId = 'v_cold_confirm';
|
||||||
|
const serverConfig = structuredClone(card._serverCfg);
|
||||||
|
// The exact field shape: passive virtual lamps owned by a real switch.
|
||||||
|
serverConfig.markers = [...(serverConfig.markers || []),
|
||||||
|
...virtualIds.map((id, index) => ({
|
||||||
|
id,
|
||||||
|
name: `Cold main light ${index + 1}`,
|
||||||
|
binding: 'virtual',
|
||||||
|
is_light: true,
|
||||||
|
tap_action: 'toggle',
|
||||||
|
space: space.id,
|
||||||
|
area: room.area,
|
||||||
|
room_id: room.id,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
id: confirmId,
|
||||||
|
name: 'Cold confirmed lamp',
|
||||||
|
binding: 'virtual',
|
||||||
|
is_light: true,
|
||||||
|
tap_action: 'toggle',
|
||||||
|
tap_confirm: true,
|
||||||
|
space: space.id,
|
||||||
|
area: room.area,
|
||||||
|
room_id: room.id,
|
||||||
|
}];
|
||||||
|
// Demo devices come straight from the registry without explicit markers —
|
||||||
|
// persist one for the switch exactly like the marker dialog would.
|
||||||
|
serverConfig.markers.push({
|
||||||
|
id: controllerId,
|
||||||
|
binding: `device:${controllerId}`,
|
||||||
|
name: 'Cold wall switch',
|
||||||
|
tap_action: 'toggle',
|
||||||
|
controls: virtualIds.map((id) => `marker:${id}`),
|
||||||
|
space: space.id,
|
||||||
|
area: room.area,
|
||||||
|
room_id: room.id,
|
||||||
|
});
|
||||||
|
const serverLayout = { ...card._layout };
|
||||||
|
[...virtualIds, confirmId].forEach((id, index) => {
|
||||||
|
serverLayout[id] = { s: space.id, x: 0.2 + index * 0.12, y: 0.72 };
|
||||||
|
});
|
||||||
|
const virtual = { rev: 0, config_rev: card._cfgRev, off: [] };
|
||||||
|
const subscriptions = new Map();
|
||||||
|
const serviceCalls = [];
|
||||||
|
let toggleCalls = 0;
|
||||||
|
const emit = (event, data) => {
|
||||||
|
for (const listener of subscriptions.get(event) || []) listener({ data });
|
||||||
|
};
|
||||||
|
const baseHass = card.hass;
|
||||||
|
const baseCallWS = baseHass.callWS.bind(baseHass);
|
||||||
|
const makeHass = () => ({
|
||||||
|
...baseHass,
|
||||||
|
states: { ...baseHass.states },
|
||||||
|
connection: {
|
||||||
|
...baseHass.connection,
|
||||||
|
subscribeEvents: async (callback, event) => {
|
||||||
|
const listeners = subscriptions.get(event) || new Set();
|
||||||
|
listeners.add(callback);
|
||||||
|
subscriptions.set(event, listeners);
|
||||||
|
return () => listeners.delete(callback);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
callService: async (domain, service, data) => {
|
||||||
|
serviceCalls.push({ domain, service, data });
|
||||||
|
// The stub HA honestly flips the switch and pushes a fresh state frame.
|
||||||
|
const target = [].concat(data?.entity_id || [])[0];
|
||||||
|
const current = hass.states[target]?.state;
|
||||||
|
const next = service === 'toggle' ? (current === 'on' ? 'off' : 'on')
|
||||||
|
: service === 'turn_on' ? 'on' : 'off';
|
||||||
|
const fresh = makeHass();
|
||||||
|
fresh.states[target] = { ...hass.states[target], state: next };
|
||||||
|
hass = fresh;
|
||||||
|
card.hass = fresh;
|
||||||
|
await card.updateComplete;
|
||||||
|
},
|
||||||
|
callWS: async (message) => {
|
||||||
|
if (message.type === 'houseplan/config/get') return {
|
||||||
|
config: serverConfig,
|
||||||
|
rev: virtual.config_rev,
|
||||||
|
can_write: true,
|
||||||
|
virtual_lights: structuredClone(virtual),
|
||||||
|
};
|
||||||
|
if (message.type === 'houseplan/layout/get') return {
|
||||||
|
layout: serverLayout,
|
||||||
|
rev: card._layoutRev,
|
||||||
|
};
|
||||||
|
if (message.type === 'houseplan/virtual_light/toggle') {
|
||||||
|
toggleCalls++;
|
||||||
|
const off = new Set(virtual.off);
|
||||||
|
if (off.has(message.marker_id)) off.delete(message.marker_id);
|
||||||
|
else off.add(message.marker_id);
|
||||||
|
virtual.off = [...off].sort();
|
||||||
|
virtual.rev++;
|
||||||
|
const reply = {
|
||||||
|
marker_id: message.marker_id,
|
||||||
|
on: !off.has(message.marker_id),
|
||||||
|
rev: virtual.rev,
|
||||||
|
};
|
||||||
|
queueMicrotask(() => emit('houseplan_virtual_light_updated', reply));
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
return baseCallWS(message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let hass = makeHass();
|
||||||
|
hass.states[`switch.kettle`] = { ...hass.states['switch.kettle'], state: 'on' };
|
||||||
|
card._unsubVirtual?.();
|
||||||
|
card._unsubVirtual = null;
|
||||||
|
card.hass = hass;
|
||||||
|
await card._reloadConfigOnly(true);
|
||||||
|
card._layout = serverLayout;
|
||||||
|
card._regSignature = '';
|
||||||
|
card._maybeRebuildDevices();
|
||||||
|
card._ensureLiveSyncSubscriptions();
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
await waitFor(() => card._devices.some((device) => device.id === virtualIds[2]));
|
||||||
|
await card.updateComplete;
|
||||||
|
|
||||||
|
const node = (id) => card.renderRoot.querySelector(`.dev[data-id="${id}"]`);
|
||||||
|
const isOn = (id) => card._stateClass(card._devices.find((d) => d.id === id)).includes('on');
|
||||||
|
const allVirtualOn = () => virtualIds.every(isOn);
|
||||||
|
const out = { runtimeColdBefore: !card._editorRuntime };
|
||||||
|
|
||||||
|
// Passive lamps follow their controller: with the switch on they glow.
|
||||||
|
out.initialAllOn = await waitFor(allVirtualOn);
|
||||||
|
|
||||||
|
// One tap on the wall switch drives its own entity; all three follow off.
|
||||||
|
node(controllerId).dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
||||||
|
out.controllerTapCallsSwitch = await waitFor(() => serviceCalls.length === 1
|
||||||
|
&& [].concat(serviceCalls[0].data?.entity_id || [])[0] === 'switch.kettle');
|
||||||
|
out.controllerTapTurnsAllOff = await waitFor(() => !virtualIds.some(isOn));
|
||||||
|
node(controllerId).dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
||||||
|
out.controllerTapTurnsAllOn = await waitFor(() => serviceCalls.length === 2 && allVirtualOn());
|
||||||
|
|
||||||
|
// A tap on a controlled lamp drives the same wall switch (wired semantics).
|
||||||
|
node(virtualIds[0]).dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
||||||
|
out.lampTapDrivesSwitch = await waitFor(() => serviceCalls.length === 3
|
||||||
|
&& [].concat(serviceCalls[2].data?.entity_id || [])[0] === 'switch.kettle');
|
||||||
|
out.lampTapTurnsAllOff = await waitFor(() => !virtualIds.some(isOn));
|
||||||
|
|
||||||
|
// An independent manual virtual lamp with tap_confirm: the dialog opens with
|
||||||
|
// rendered state lines and the confirmation toggles it over the WS API.
|
||||||
|
node(confirmId).dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
||||||
|
await card.updateComplete;
|
||||||
|
out.confirmDialogShown = !!card._tapConfirm
|
||||||
|
&& Array.isArray(card._tapConfirm.lines) && card._tapConfirm.lines.length > 0;
|
||||||
|
card._tapConfirm?.exec?.();
|
||||||
|
out.confirmExecTogglesVirtual = await waitFor(() => toggleCalls === 1);
|
||||||
|
card._tapConfirm = null;
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -1,97 +1,97 @@
|
|||||||
{
|
{
|
||||||
"schema": 1,
|
"schema": 1,
|
||||||
"fingerprint": "c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43",
|
"fingerprint": "f3b9d9068e440e59a8abb9e4390a828ab8a3ffeb691e2d0777997c1e56c3c467",
|
||||||
"entry": "houseplan-card.js",
|
"entry": "houseplan-card.js",
|
||||||
"initialViewFiles": [
|
"initialViewFiles": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"houseplan-card.js"
|
"houseplan-card.js"
|
||||||
],
|
],
|
||||||
"initialViewGzipBytes": 270793,
|
"initialViewGzipBytes": 271143,
|
||||||
"lazyFiles": [
|
"lazyFiles": [
|
||||||
"houseplan-assets/de-Cuf6hSGS.js",
|
"houseplan-assets/de-Bm8vPaKB.js",
|
||||||
"houseplan-assets/editor-Drr9z9rK.js",
|
"houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js",
|
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"lazyGzipBytes": 166311,
|
"lazyGzipBytes": 165964,
|
||||||
"lazyEditorFiles": [
|
"lazyEditorFiles": [
|
||||||
"houseplan-assets/editor-Drr9z9rK.js",
|
"houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"lazyEditorGzipBytes": 137204,
|
"lazyEditorGzipBytes": 136854,
|
||||||
"lazyOnboardingFiles": [
|
"lazyOnboardingFiles": [
|
||||||
"houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js",
|
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"lazyOnboardingGzipBytes": 7198,
|
"lazyOnboardingGzipBytes": 7198,
|
||||||
"lazyLocaleFiles": [
|
"lazyLocaleFiles": [
|
||||||
"houseplan-assets/de-Cuf6hSGS.js"
|
"houseplan-assets/de-Bm8vPaKB.js"
|
||||||
],
|
],
|
||||||
"lazyLocaleGzipBytes": 22462,
|
"lazyLocaleGzipBytes": 22465,
|
||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/de-Cuf6hSGS.js",
|
"path": "houseplan-assets/de-Bm8vPaKB.js",
|
||||||
"sha256": "fff8ca0681b5dbd33a6dea7f5d05fbbebdec6caa6659f8b89bfc59fbb1ab50d1",
|
"sha256": "e8e5acd5cda2ac637e178a08ae4eb4ea1127472b736b8cab7c19ae2ab6b8bb0a",
|
||||||
"rawBytes": 73919,
|
"rawBytes": 73919,
|
||||||
"gzipBytes": 22462,
|
"gzipBytes": 22465,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/editor-Drr9z9rK.js",
|
"path": "houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"sha256": "045707acaa6535cf617dedd8492ed8a2285c2cbe995b0fc88f3e263682522a60",
|
"sha256": "661537fb7547eaaf3d5c6cfd040db88c74ae95b40554998ef0360c8bccfe3de0",
|
||||||
"rawBytes": 3831,
|
"rawBytes": 3831,
|
||||||
"gzipBytes": 1583,
|
"gzipBytes": 1583,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js"
|
"houseplan-assets/houseplan-card-Bit484fR.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"path": "houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"sha256": "1e09aeec8c7ba2de3e665b132e4a4ea8a1f411274b924b4655bd7c9654f94cac",
|
"sha256": "9069adcc9c0be4411d89d822d3610de6e1154598693cb3cb8575d9eaa6743bcc",
|
||||||
"rawBytes": 955358,
|
"rawBytes": 956442,
|
||||||
"gzipBytes": 270040,
|
"gzipBytes": 270390,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"dynamicImports": [
|
"dynamicImports": [
|
||||||
"houseplan-assets/de-Cuf6hSGS.js",
|
"houseplan-assets/de-Bm8vPaKB.js",
|
||||||
"houseplan-assets/editor-Drr9z9rK.js",
|
"houseplan-assets/editor-CcQvuoGr.js",
|
||||||
"houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js"
|
"houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/houseplan-editor-runtime-CCBmTE0A.js",
|
"path": "houseplan-assets/houseplan-editor-runtime-CWaq9LTE.js",
|
||||||
"sha256": "9834239d2910161215d70d12147ad83a0fe1d3062abbe6ea9f5165feb8a20d78",
|
"sha256": "30bd78227922512c03aa6d0177f776b17508048ff311eac0b8b993e4c3ca0602",
|
||||||
"rawBytes": 521651,
|
"rawBytes": 520370,
|
||||||
"gzipBytes": 135068,
|
"gzipBytes": 134718,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/houseplan-onboarding-runtime-CJAdI7qX.js",
|
"path": "houseplan-assets/houseplan-onboarding-runtime-Tm1y6xbk.js",
|
||||||
"sha256": "cbd7d63ad1fdb1b16b58ba99cbc93a02d4ea6f70317a926018cffe56417c6ba2",
|
"sha256": "3115311691b5b2164c3fd4d00d311040d1c430a2c0cb6637fafbf1a6775c328a",
|
||||||
"rawBytes": 26356,
|
"rawBytes": 26356,
|
||||||
"gzipBytes": 6645,
|
"gzipBytes": 6645,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js",
|
"houseplan-assets/houseplan-card-Bit484fR.js",
|
||||||
"houseplan-assets/space-deletion-CrURpMqK.js"
|
"houseplan-assets/space-deletion-1ap2PeUr.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-assets/space-deletion-CrURpMqK.js",
|
"path": "houseplan-assets/space-deletion-1ap2PeUr.js",
|
||||||
"sha256": "7c9566acfd9fa8531e97cc955cba29dffae614c3c5e114d6235c71d44b6811b1",
|
"sha256": "f972b71546589bc2601e2432022d835bf10349f876c32d81fa957767ba72c7c4",
|
||||||
"rawBytes": 911,
|
"rawBytes": 911,
|
||||||
"gzipBytes": 553,
|
"gzipBytes": 553,
|
||||||
"isEntry": false,
|
"isEntry": false,
|
||||||
@@ -100,12 +100,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "houseplan-card.js",
|
"path": "houseplan-card.js",
|
||||||
"sha256": "a69e76fc9673ef374d20aceba2d0d160b2a42e9d0b4bcfc3baf616a58c89b93f",
|
"sha256": "a1123da36e218354a3633fe550fe099f6ffb0fb7062dec34c0b38ba3b912ab6c",
|
||||||
"rawBytes": 1089,
|
"rawBytes": 1089,
|
||||||
"gzipBytes": 753,
|
"gzipBytes": 753,
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"houseplan-assets/houseplan-card-DZo3gC2T.js"
|
"houseplan-assets/houseplan-card-Bit484fR.js"
|
||||||
],
|
],
|
||||||
"dynamicImports": []
|
"dynamicImports": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43";import{c as e,l as o,t,d1 as s,A as a,d2 as i,d3 as l,d4 as n,E as r,b as c}from"./houseplan-card-DZo3gC2T.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__="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
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.data=${this._formData}
|
.data=${this._formData}
|
||||||
.schema=${e}
|
.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)),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__="c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43";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)),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};
|
||||||
@@ -1 +1 @@
|
|||||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c219ead6633cb4404f86d670c606d3f1c02a69cc43a411cf25ebd6beb45fcd43";try{await import("./houseplan-assets/houseplan-card-DZo3gC2T.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__="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)}
|
||||||
|
|||||||
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- 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
|
||||||
|
tab until the device settings were opened once — the View card now resolves
|
||||||
|
the tap entirely on its own, without the lazy editor code
|
||||||
|
([#357](https://github.com/Matysh/houseplan-card/issues/357)).
|
||||||
|
|
||||||
## v1.69.0-beta.2 — 2026-08-28
|
## v1.69.0-beta.2 — 2026-08-28
|
||||||
|
|
||||||
- The Background furniture library now has 56 top-view symbols and a compact
|
- The Background furniture library now has 56 top-view symbols and a compact
|
||||||
|
|||||||
@@ -8,6 +8,13 @@
|
|||||||
|
|
||||||
## Не выпущено
|
## Не выпущено
|
||||||
|
|
||||||
|
- Нажатие на устройство на только что открытой странице снова работает без
|
||||||
|
предварительного захода в какой-либо редактор. Выключатель, управляющий
|
||||||
|
виртуальными источниками света (и любое другое устройство с тап-переключением),
|
||||||
|
молча игнорировал нажатие в холодной вкладке, пока настройки устройства не
|
||||||
|
откроют один раз — теперь View-карточка разрешает нажатие сама, без ленивого
|
||||||
|
кода редактора ([#357](https://github.com/Matysh/houseplan-card/issues/357)).
|
||||||
|
|
||||||
## v1.69.0-beta.2 — 2026-08-28
|
## v1.69.0-beta.2 — 2026-08-28
|
||||||
|
|
||||||
- В библиотеке мебели редактора подложки теперь 56 видов сверху и компактный
|
- В библиотеке мебели редактора подложки теперь 56 видов сверху и компактный
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 198 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 334 KiB |
|
Before Width: | Height: | Size: 289 KiB After Width: | Height: | Size: 319 KiB |
|
Before Width: | Height: | Size: 291 KiB After Width: | Height: | Size: 317 KiB |
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 296 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 141 KiB |
@@ -3,7 +3,7 @@
|
|||||||
"fixture": "synthetic-only",
|
"fixture": "synthetic-only",
|
||||||
"chromium": "151.0.7922.34",
|
"chromium": "151.0.7922.34",
|
||||||
"oxipng": null,
|
"oxipng": null,
|
||||||
"sourceFingerprint": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceFingerprint": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"captureScriptSha256": "fe76c8e2e27501e9a377d7aa15dd6cba6a018d55c07deb602dbee097f0a0a5ba",
|
"captureScriptSha256": "fe76c8e2e27501e9a377d7aa15dd6cba6a018d55c07deb602dbee097f0a0a5ba",
|
||||||
"command": "npm run build && node demo/docs/capture.mjs",
|
"command": "npm run build && node demo/docs/capture.mjs",
|
||||||
"scenarios": {
|
"scenarios": {
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "36223106c073f07d8cc3ecf8eaab37192ebb2687daba65c5c21047d0b7890de0"
|
"imageSha256": "0ca982495a7749907c48ebbddfd089c54385a4347f79f64e94af2c5a8d5c03eb"
|
||||||
},
|
},
|
||||||
"view-touch": {
|
"view-touch": {
|
||||||
"file": "02-view-touch.png",
|
"file": "02-view-touch.png",
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "a4aa3af48578ee41594a9b1a6e98b4d39f3324a949a9fd9e63d5854576d5db85"
|
"imageSha256": "3be239a5a494f6776540a9c0049694ee6f9d2ba927c8bcb4b26ee5a69a228a7d"
|
||||||
},
|
},
|
||||||
"space-create": {
|
"space-create": {
|
||||||
"file": "03-space-create.png",
|
"file": "03-space-create.png",
|
||||||
@@ -37,8 +37,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "0dc024726327f60f4a9ceaf3044381691f81f1090af81d1812e870f22d9343ba"
|
"imageSha256": "a55b463055a565892ca139014e314a8f63fb5301c87fff2e48d7e69210fe9b6c"
|
||||||
},
|
},
|
||||||
"room-contour-close": {
|
"room-contour-close": {
|
||||||
"file": "04-room-contour-close.png",
|
"file": "04-room-contour-close.png",
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "44cfaa95bd51e2cd628400e28db0ad8b2f0cd904385845bd402494f3f5c0d93c"
|
"imageSha256": "dfad9a27371cfa397a3fd8dce7005f42d24649743f5322105ee894067d3a9f16"
|
||||||
},
|
},
|
||||||
"plan-context-tray": {
|
"plan-context-tray": {
|
||||||
"file": "05-plan-context-tray.png",
|
"file": "05-plan-context-tray.png",
|
||||||
@@ -59,8 +59,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "e0662429b423fb74151b583dcc2c8635b001b637d03bbf7a6b16aec46399c3f8"
|
"imageSha256": "b75f348c632296da5c2fc142d53df613f757e1516acac315be50f5b4557729cc"
|
||||||
},
|
},
|
||||||
"device-editor": {
|
"device-editor": {
|
||||||
"file": "06-device-editor.png",
|
"file": "06-device-editor.png",
|
||||||
@@ -70,8 +70,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "d0ffd31ce80bfde21ab75da356a5fc1af38246f2b301030880320620c228d89d"
|
"imageSha256": "6b9cd281450e7782cff292f11efc58d227bedd0df86afc3be987feb2edec9dc3"
|
||||||
},
|
},
|
||||||
"device-display-preview": {
|
"device-display-preview": {
|
||||||
"file": "06-device-display-preview.png",
|
"file": "06-device-display-preview.png",
|
||||||
@@ -81,8 +81,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "2cdabae1f89c3286e4fac0ce30f757ee1690b707ab8a5488748b7cd420626160"
|
"imageSha256": "6f628abe6b89d7c4b352e6d73ac0b126526f6fdba5927bb774c5b884e484ca19"
|
||||||
},
|
},
|
||||||
"background-editor": {
|
"background-editor": {
|
||||||
"file": "07-background-editor.png",
|
"file": "07-background-editor.png",
|
||||||
@@ -92,8 +92,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "30147bb00a90eea7136b4cee30995f6e6a9217b5132f3e8d3ad7471413b1af8a"
|
"imageSha256": "a1d1a905f1410f73231d2ea5d1dfbd8ae79c2fe8b0a79cff87ebb977ac7a3103"
|
||||||
},
|
},
|
||||||
"room-card": {
|
"room-card": {
|
||||||
"file": "08-room-card.png",
|
"file": "08-room-card.png",
|
||||||
@@ -103,8 +103,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
|
"imageSha256": "48a5685039a5e0de2a28190d857b193c86e2e63606e3c7a8c40fc9d2b190da4e"
|
||||||
},
|
},
|
||||||
"device-info": {
|
"device-info": {
|
||||||
"file": "09-device-info.png",
|
"file": "09-device-info.png",
|
||||||
@@ -114,8 +114,8 @@
|
|||||||
},
|
},
|
||||||
"theme": "dark",
|
"theme": "dark",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"sourceSha256": "232b156a56885d74f52e69608111212dbfae2a01ee5b1e8f39de695c5b9c70d1",
|
"sourceSha256": "5ccd1afa75c0554258872575cd2681f76563983f762347af5390ffddec00ce41",
|
||||||
"imageSha256": "dd492f53150b7149085daada5cce9eeae9bde9e7ea1d86679a54b3041f72f517"
|
"imageSha256": "29a342811aaedaebc54ba337792d60cb454a7cf4c3063b210f6c4bafe0a4d87b"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -680,6 +680,34 @@ const MUTANT_DEFINITIONS = [
|
|||||||
replace: ' this._languageFailureUnsub = undefined;',
|
replace: ' this._languageFailureUnsub = undefined;',
|
||||||
}],
|
}],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'cold-view-toggle-delegated-to-runtime',
|
||||||
|
guard: 'node demo/smoke_cold_view_toggle.mjs',
|
||||||
|
because: 'the View card must resolve a tap without the lazy editor runtime — the #337 stub '
|
||||||
|
+ 'threw inside the click handler on every cold tab and a wall switch controlling three '
|
||||||
|
+ 'virtual lamps did nothing until an editor surface was opened (#357)',
|
||||||
|
patches: [{
|
||||||
|
file: 'src/houseplan-card.ts',
|
||||||
|
find: ' public _toggleIntent(\n'
|
||||||
|
+ ' device: DevItem,\n'
|
||||||
|
+ ' devices: readonly DevItem[] = this._devices,\n'
|
||||||
|
+ ' ): ResolvedToggleIntent | null {\n'
|
||||||
|
+ ' return resolveToggleIntent({\n'
|
||||||
|
+ ' hass: this._planHass,\n'
|
||||||
|
+ ' registryHass: this._fullRegistryHass,\n'
|
||||||
|
+ ' devices,\n'
|
||||||
|
+ ' device,\n'
|
||||||
|
+ ' virtualLights: this._virtualLights,\n'
|
||||||
|
+ ' });\n'
|
||||||
|
+ ' }',
|
||||||
|
replace: ' public _toggleIntent(\n'
|
||||||
|
+ ' device: DevItem,\n'
|
||||||
|
+ ' devices: readonly DevItem[] = this._devices,\n'
|
||||||
|
+ ' ): ResolvedToggleIntent | null {\n'
|
||||||
|
+ " return this._editorRuntimeOrThrow()._toggleIntent(device, devices);\n"
|
||||||
|
+ ' }',
|
||||||
|
}],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'lazy-loader-network-failure-terminal',
|
id: 'lazy-loader-network-failure-terminal',
|
||||||
guard: 'node --test --test-name-pattern="#353 AC1" test/editor-runtime-loader.test.mjs',
|
guard: 'node --test --test-name-pattern="#353 AC1" test/editor-runtime-loader.test.mjs',
|
||||||
|
|||||||
@@ -44,6 +44,17 @@ export const SMOKE_LINKS = [
|
|||||||
+ 'requests and four root render gates: neutral cold frame, atomic German commit, page-cache '
|
+ '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)',
|
+ 'reuse and the bounded retry-to-English path cannot be proven from pure registry tests (#348)',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
symbols: [
|
||||||
|
'resolveToggleIntent', 'toggleOperation', 'projectedTapAction',
|
||||||
|
'toggleOriginOf', 'resolvedLightSources',
|
||||||
|
],
|
||||||
|
smokes: ['smoke_cold_view_toggle.mjs'],
|
||||||
|
because: 'the cold-view scenario proves a plain tap resolves and executes WITHOUT the lazy '
|
||||||
|
+ 'editor runtime: a wall switch drives three passive virtual lamps, a controlled lamp '
|
||||||
|
+ 'drives its switch, tap_confirm renders and confirms, and no editor chunk is requested '
|
||||||
|
+ '(#357 — every warm smoke preloads the runtime and cannot see this class)',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
symbols: ['quantizeKeyCoord', 'INCIDENT_EPS', 'KEY_FACTOR'],
|
symbols: ['quantizeKeyCoord', 'INCIDENT_EPS', 'KEY_FACTOR'],
|
||||||
smokes: ['smoke_junction_limits.mjs'],
|
smokes: ['smoke_junction_limits.mjs'],
|
||||||
|
|||||||
@@ -12766,11 +12766,25 @@ export class HouseplanCard extends LitElement {
|
|||||||
return this._editorRuntimeOrThrow()._markerPreviewDevices(preview);
|
return this._editorRuntimeOrThrow()._markerPreviewDevices(preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleIntent(
|
/**
|
||||||
|
* #357: toggle resolution belongs to the eager View card. The lazy split
|
||||||
|
* #337 left these four methods delegating into the editor runtime, so a
|
||||||
|
* plain tap on a cold tab (kiosk, fresh page) threw synchronously inside
|
||||||
|
* the click handler until someone opened any editor surface. The resolver
|
||||||
|
* chain is a pure eager module (device-toggle.ts) over card-owned state —
|
||||||
|
* the editor runtime now delegates back here, not the other way round.
|
||||||
|
*/
|
||||||
|
public _toggleIntent(
|
||||||
device: DevItem,
|
device: DevItem,
|
||||||
devices: readonly DevItem[] = this._devices,
|
devices: readonly DevItem[] = this._devices,
|
||||||
): ResolvedToggleIntent | null {
|
): ResolvedToggleIntent | null {
|
||||||
return this._editorRuntimeOrThrow()._toggleIntent(device, devices);
|
return resolveToggleIntent({
|
||||||
|
hass: this._planHass,
|
||||||
|
registryHass: this._fullRegistryHass,
|
||||||
|
devices,
|
||||||
|
device,
|
||||||
|
virtualLights: this._virtualLights,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleIntentForDialog(
|
private _toggleIntentForDialog(
|
||||||
@@ -12779,18 +12793,57 @@ export class HouseplanCard extends LitElement {
|
|||||||
return this._editorRuntimeOrThrow()._toggleIntentForDialog(d);
|
return this._editorRuntimeOrThrow()._toggleIntentForDialog(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleStateText(entityId: string, fallback: string): string {
|
public _toggleStateText(entityId: string, fallback: string): string {
|
||||||
return this._editorRuntimeOrThrow()._toggleStateText(entityId, fallback);
|
const state = this._planHass?.states?.[entityId] || this.hass?.states?.[entityId];
|
||||||
|
try {
|
||||||
|
return state && typeof this.hass?.formatEntityState === 'function'
|
||||||
|
? this.hass.formatEntityState(state) : fallback;
|
||||||
|
} catch {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Current-state copy is localized without teaching the dialog command semantics. */
|
/** Current-state copy is localized without teaching the dialog command semantics. */
|
||||||
private _toggleConfirmationStateText(target: ResolvedToggleTarget): string {
|
public _toggleConfirmationStateText(target: ResolvedToggleTarget): string {
|
||||||
return this._editorRuntimeOrThrow()._toggleConfirmationStateText(target);
|
const raw = String(target.state || 'unknown');
|
||||||
|
const formatted = target.entityId ? this._toggleStateText(target.entityId, raw) : raw;
|
||||||
|
if (formatted.trim().toLocaleLowerCase() !== raw.trim().toLocaleLowerCase()) return formatted;
|
||||||
|
const known: Partial<Record<string, I18nKey>> = {
|
||||||
|
on: 'confirm.state_on',
|
||||||
|
off: 'confirm.state_off',
|
||||||
|
open: 'confirm.state_open',
|
||||||
|
closed: 'confirm.state_closed',
|
||||||
|
opening: 'confirm.state_opening',
|
||||||
|
closing: 'confirm.state_closing',
|
||||||
|
unknown: 'confirm.state_unknown',
|
||||||
|
};
|
||||||
|
const key = known[raw];
|
||||||
|
if (key) return this._t(key);
|
||||||
|
// A future integration-specific token stays honest and readable even in a
|
||||||
|
// harness without HA's formatter; underscores are never exposed as UI.
|
||||||
|
return raw.replaceAll('_', ' ').replaceAll('-', ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Snapshot lines shown by Toggle confirmation; execution still re-resolves later. */
|
/** Snapshot lines shown by Toggle confirmation; execution still re-resolves later. */
|
||||||
private _toggleConfirmationLines(intent: ResolvedToggleIntent): string[] {
|
public _toggleConfirmationLines(intent: ResolvedToggleIntent): string[] {
|
||||||
return this._editorRuntimeOrThrow()._toggleConfirmationLines(intent);
|
const effectKeys: Record<Exclude<ToggleNextEffect, 'toggle'>, I18nKey> = {
|
||||||
|
'turn-on': 'confirm.state_on',
|
||||||
|
'turn-off': 'confirm.state_off',
|
||||||
|
open: 'confirm.state_open',
|
||||||
|
close: 'confirm.state_closed',
|
||||||
|
stop: 'confirm.state_stopped',
|
||||||
|
};
|
||||||
|
return formatToggleConfirmation(intent, {
|
||||||
|
state: (target) => this._toggleConfirmationStateText(target),
|
||||||
|
current: (state) => this._t('confirm.current_state', { state }),
|
||||||
|
expected: (state) => this._t('confirm.expected_state', { state }),
|
||||||
|
groupCurrent: (on, total) => this._t('confirm.group_current', { on, total }),
|
||||||
|
groupAllOn: () => this._t('confirm.group_all_on'),
|
||||||
|
groupAllOff: () => this._t('confirm.group_all_off'),
|
||||||
|
unavailable: (count) => this._t('confirm.unavailable_targets', { count }),
|
||||||
|
effect: (effect) => this._t(effectKeys[effect]),
|
||||||
|
expectedByHa: () => this._t('confirm.expected_by_ha'),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleHintLines(intent: ResolvedToggleIntent | null): string[] {
|
private _toggleHintLines(intent: ResolvedToggleIntent | null): string[] {
|
||||||
|
|||||||
@@ -1082,7 +1082,11 @@ export interface HouseplanEditorHostPort {
|
|||||||
_t: (key: I18nKey, vars?: Record<string, string | number>) => string;
|
_t: (key: I18nKey, vars?: Record<string, string | number>) => string;
|
||||||
_thickWallCuts: () => number[][];
|
_thickWallCuts: () => number[][];
|
||||||
_tip: { x: number; y: number; title: string; meta: string; lqi?: number | null; temp?: number | null; hum?: number | null; } | null;
|
_tip: { x: number; y: number; title: string; meta: string; lqi?: number | null; temp?: number | null; hum?: number | null; } | null;
|
||||||
|
_toggleConfirmationLines: (intent: ResolvedToggleIntent) => string[];
|
||||||
|
_toggleConfirmationStateText: (target: ResolvedToggleTarget) => string;
|
||||||
|
_toggleIntent: (device: DevItem, devices?: readonly DevItem[]) => ResolvedToggleIntent | null;
|
||||||
_toggleMarkerDialogVisibility: () => void;
|
_toggleMarkerDialogVisibility: () => void;
|
||||||
|
_toggleStateText: (entityId: string, fallback: string) => string;
|
||||||
_tool: MarkupTool;
|
_tool: MarkupTool;
|
||||||
_undoKind: "optimize" | "import" | null;
|
_undoKind: "optimize" | "import" | null;
|
||||||
_undoPoint: () => void;
|
_undoPoint: () => void;
|
||||||
@@ -11630,13 +11634,9 @@ public _toggleIntent(
|
|||||||
device: DevItem,
|
device: DevItem,
|
||||||
devices: readonly DevItem[] = this.host._devices,
|
devices: readonly DevItem[] = this.host._devices,
|
||||||
): ResolvedToggleIntent | null {
|
): ResolvedToggleIntent | null {
|
||||||
return resolveToggleIntent({
|
// #357: the View card owns toggle resolution — a plain tap must work on a
|
||||||
hass: this.host._planHass,
|
// cold tab that never loaded this runtime. Editor consumers delegate back.
|
||||||
registryHass: this.host._fullRegistryHass,
|
return this.host._toggleIntent(device, devices);
|
||||||
devices,
|
|
||||||
device,
|
|
||||||
virtualLights: this.host._virtualLights,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public _toggleIntentForDialog(
|
public _toggleIntentForDialog(
|
||||||
@@ -11649,54 +11649,15 @@ public _toggleIntentForDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _toggleStateText(entityId: string, fallback: string): string {
|
public _toggleStateText(entityId: string, fallback: string): string {
|
||||||
const state = this.host._planHass?.states?.[entityId] || this.host.hass?.states?.[entityId];
|
return this.host._toggleStateText(entityId, fallback);
|
||||||
try {
|
|
||||||
return state && typeof this.host.hass?.formatEntityState === 'function'
|
|
||||||
? this.host.hass.formatEntityState(state) : fallback;
|
|
||||||
} catch {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public _toggleConfirmationStateText(target: ResolvedToggleTarget): string {
|
public _toggleConfirmationStateText(target: ResolvedToggleTarget): string {
|
||||||
const raw = String(target.state || 'unknown');
|
return this.host._toggleConfirmationStateText(target);
|
||||||
const formatted = target.entityId ? this._toggleStateText(target.entityId, raw) : raw;
|
|
||||||
if (formatted.trim().toLocaleLowerCase() !== raw.trim().toLocaleLowerCase()) return formatted;
|
|
||||||
const known: Partial<Record<string, I18nKey>> = {
|
|
||||||
on: 'confirm.state_on',
|
|
||||||
off: 'confirm.state_off',
|
|
||||||
open: 'confirm.state_open',
|
|
||||||
closed: 'confirm.state_closed',
|
|
||||||
opening: 'confirm.state_opening',
|
|
||||||
closing: 'confirm.state_closing',
|
|
||||||
unknown: 'confirm.state_unknown',
|
|
||||||
};
|
|
||||||
const key = known[raw];
|
|
||||||
if (key) return this.host._t(key);
|
|
||||||
// A future integration-specific token stays honest and readable even in a
|
|
||||||
// harness without HA's formatter; underscores are never exposed as UI.
|
|
||||||
return raw.replaceAll('_', ' ').replaceAll('-', ' ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public _toggleConfirmationLines(intent: ResolvedToggleIntent): string[] {
|
public _toggleConfirmationLines(intent: ResolvedToggleIntent): string[] {
|
||||||
const effectKeys: Record<Exclude<ToggleNextEffect, 'toggle'>, I18nKey> = {
|
return this.host._toggleConfirmationLines(intent);
|
||||||
'turn-on': 'confirm.state_on',
|
|
||||||
'turn-off': 'confirm.state_off',
|
|
||||||
open: 'confirm.state_open',
|
|
||||||
close: 'confirm.state_closed',
|
|
||||||
stop: 'confirm.state_stopped',
|
|
||||||
};
|
|
||||||
return formatToggleConfirmation(intent, {
|
|
||||||
state: (target) => this._toggleConfirmationStateText(target),
|
|
||||||
current: (state) => this.host._t('confirm.current_state', { state }),
|
|
||||||
expected: (state) => this.host._t('confirm.expected_state', { state }),
|
|
||||||
groupCurrent: (on, total) => this.host._t('confirm.group_current', { on, total }),
|
|
||||||
groupAllOn: () => this.host._t('confirm.group_all_on'),
|
|
||||||
groupAllOff: () => this.host._t('confirm.group_all_off'),
|
|
||||||
unavailable: (count) => this.host._t('confirm.unavailable_targets', { count }),
|
|
||||||
effect: (effect) => this.host._t(effectKeys[effect]),
|
|
||||||
expectedByHa: () => this.host._t('confirm.expected_by_ha'),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public _toggleHintLines(intent: ResolvedToggleIntent | null): string[] {
|
public _toggleHintLines(intent: ResolvedToggleIntent | null): string[] {
|
||||||
|
|||||||