fix: quantise the light aperture amount of moving covers (#366)

A gate or door bound to a position-reporting cover fed current_position
into the light-barrier signature at toFixed(3) precision: every percent of
movement produced a new fingerprint, a full physicalBodyParts recompute
and a recut — up to ~100 heavy passes per gate cycle, plus LRU churn.

The light pipeline now consumes one quantised amount
(OPENING_LIGHT_AMOUNT_QUANTUM = 0.05, exact 0 and 1 nodes) at the single
point that feeds BOTH the signature and the cut geometry, so the cache key
and the drawn aperture agree by construction and a full sweep costs at
most 21 recomputes. The door LEAF animation stays smooth — _openingAmt is
quantised only for the light pipeline, nowhere else. Binary contact doors
are byte-identical to the previous behaviour (pinned by unit).

Assumption recorded in the spec: the 5% visual step of the light cut is
indistinguishable on real plans; if field impressions disagree, the
quantum is a one-constant change (0.02 => <=51 recomputes) or the decision
falls back to a debounce. LIGHT.md §Caching documents the grid.

Issue: #366
User-Visible: yes
This commit is contained in:
Codex
2026-08-29 08:15:51 +00:00
committed by claude[bot]
parent 0d4010f87d
commit e758c3d9fb
34 changed files with 391 additions and 310 deletions
@@ -1,113 +1,113 @@
{
"schema": 1,
"fingerprint": "2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a",
"fingerprint": "92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-z66sNkRH.js",
"houseplan-assets/houseplan-card-7JITXtyy.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 273551,
"initialViewGzipBytes": 273581,
"lazyFiles": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/de-nY6CPgRE.js",
"houseplan-assets/editor-Btuj24RH.js",
"houseplan-assets/houseplan-editor-runtime-CztWrxSd.js",
"houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/de-DMfFz19N.js",
"houseplan-assets/editor-36HAh3Je.js",
"houseplan-assets/houseplan-editor-runtime-BuN48ldr.js",
"houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js"
],
"lazyGzipBytes": 168764,
"lazyGzipBytes": 168780,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/editor-Btuj24RH.js",
"houseplan-assets/houseplan-editor-runtime-CztWrxSd.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/editor-36HAh3Je.js",
"houseplan-assets/houseplan-editor-runtime-BuN48ldr.js"
],
"lazyEditorGzipBytes": 139471,
"lazyEditorGzipBytes": 139483,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js"
],
"lazyOnboardingGzipBytes": 9453,
"lazyOnboardingGzipBytes": 9456,
"lazyLocaleFiles": [
"houseplan-assets/de-nY6CPgRE.js"
"houseplan-assets/de-DMfFz19N.js"
],
"lazyLocaleGzipBytes": 22796,
"lazyLocaleGzipBytes": 22799,
"files": [
{
"path": "houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"sha256": "db0d4c348fee148aff02781bfb2c1d0fa4afb50b7f7c7670768b748675f5389a",
"path": "houseplan-assets/backdrop-pick-B3oldFOv.js",
"sha256": "bca0256fc222b1c3f6045a97d7a773f499701c1b9b3dcc8498c40cdd26de403b",
"rawBytes": 6967,
"gzipBytes": 2956,
"gzipBytes": 2958,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-nY6CPgRE.js",
"sha256": "1ea533abc21c2d2585941122c21212deeec234adf5099ae8afce611bc3a6cd49",
"path": "houseplan-assets/de-DMfFz19N.js",
"sha256": "307982665ac112914972383bbed35da47ce8e917009daa792e29119dfbe263ec",
"rawBytes": 74836,
"gzipBytes": 22796,
"gzipBytes": 22799,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-Btuj24RH.js",
"sha256": "e462de3e6a34cce0b93fabf0804a430ce81bcfd2fe6c26e65557ec65504cd964",
"path": "houseplan-assets/editor-36HAh3Je.js",
"sha256": "c0a8bb4c6cd7868ae5ad78b8d2ecca80e9a449f1d3c03b5c0eadb20a791a8285",
"rawBytes": 3831,
"gzipBytes": 1585,
"gzipBytes": 1586,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-z66sNkRH.js",
"sha256": "33ee209a395a96a13e293ca1d48c2a5af686141b094bb92966d369f9dbcc8813",
"rawBytes": 963751,
"gzipBytes": 272797,
"path": "houseplan-assets/houseplan-card-7JITXtyy.js",
"sha256": "663801a4536057db0e907a740e0cc96d01feabc475a3e7a9b60567259aef7fb2",
"rawBytes": 963878,
"gzipBytes": 272826,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-nY6CPgRE.js",
"houseplan-assets/editor-Btuj24RH.js",
"houseplan-assets/houseplan-editor-runtime-CztWrxSd.js",
"houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js"
"houseplan-assets/de-DMfFz19N.js",
"houseplan-assets/editor-36HAh3Je.js",
"houseplan-assets/houseplan-editor-runtime-BuN48ldr.js",
"houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-CztWrxSd.js",
"sha256": "ac4ed4d1f057b7bae3ca52141d71beb81fd0ff971fc81e28d71073ad9bd5a5b2",
"path": "houseplan-assets/houseplan-editor-runtime-BuN48ldr.js",
"sha256": "e05365fb1ce4c1d97cacba2b243d07b9ecb4203d79596ba022096de3356eda5d",
"rawBytes": 522033,
"gzipBytes": 134930,
"gzipBytes": 134939,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js",
"sha256": "9094f910509b9881e8d7882a9ad1855e39610da15a2a163088af4d1e96c8687f",
"path": "houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js",
"sha256": "cf09b5cf51ed2220e4afd6b9523a66d013764eb0a68d1be1bbce8083fd5516d9",
"rawBytes": 26195,
"gzipBytes": 6497,
"gzipBytes": 6498,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "cdd74054b0eda637b8e8184c96291b395fd558678f8e55af673931615e01b085",
"sha256": "2c76779b5485023d77e090cee3a9a59a5c558c60e30c7b25ed0b3bb3e34fa05e",
"rawBytes": 1089,
"gzipBytes": 754,
"gzipBytes": 755,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-z66sNkRH.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__="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-7JITXtyy.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,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-z66sNkRH.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-Tyu0BfFF.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-7JITXtyy.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-B3oldFOv.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
${s.map(s=>{return _`
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";try{await import("./houseplan-assets/houseplan-card-z66sNkRH.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__="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";try{await import("./houseplan-assets/houseplan-card-7JITXtyy.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)}
+50 -50
View File
@@ -1,113 +1,113 @@
{
"schema": 1,
"fingerprint": "2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a",
"fingerprint": "92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-z66sNkRH.js",
"houseplan-assets/houseplan-card-7JITXtyy.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 273551,
"initialViewGzipBytes": 273581,
"lazyFiles": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/de-nY6CPgRE.js",
"houseplan-assets/editor-Btuj24RH.js",
"houseplan-assets/houseplan-editor-runtime-CztWrxSd.js",
"houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/de-DMfFz19N.js",
"houseplan-assets/editor-36HAh3Je.js",
"houseplan-assets/houseplan-editor-runtime-BuN48ldr.js",
"houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js"
],
"lazyGzipBytes": 168764,
"lazyGzipBytes": 168780,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/editor-Btuj24RH.js",
"houseplan-assets/houseplan-editor-runtime-CztWrxSd.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/editor-36HAh3Je.js",
"houseplan-assets/houseplan-editor-runtime-BuN48ldr.js"
],
"lazyEditorGzipBytes": 139471,
"lazyEditorGzipBytes": 139483,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js"
],
"lazyOnboardingGzipBytes": 9453,
"lazyOnboardingGzipBytes": 9456,
"lazyLocaleFiles": [
"houseplan-assets/de-nY6CPgRE.js"
"houseplan-assets/de-DMfFz19N.js"
],
"lazyLocaleGzipBytes": 22796,
"lazyLocaleGzipBytes": 22799,
"files": [
{
"path": "houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"sha256": "db0d4c348fee148aff02781bfb2c1d0fa4afb50b7f7c7670768b748675f5389a",
"path": "houseplan-assets/backdrop-pick-B3oldFOv.js",
"sha256": "bca0256fc222b1c3f6045a97d7a773f499701c1b9b3dcc8498c40cdd26de403b",
"rawBytes": 6967,
"gzipBytes": 2956,
"gzipBytes": 2958,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-nY6CPgRE.js",
"sha256": "1ea533abc21c2d2585941122c21212deeec234adf5099ae8afce611bc3a6cd49",
"path": "houseplan-assets/de-DMfFz19N.js",
"sha256": "307982665ac112914972383bbed35da47ce8e917009daa792e29119dfbe263ec",
"rawBytes": 74836,
"gzipBytes": 22796,
"gzipBytes": 22799,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-Btuj24RH.js",
"sha256": "e462de3e6a34cce0b93fabf0804a430ce81bcfd2fe6c26e65557ec65504cd964",
"path": "houseplan-assets/editor-36HAh3Je.js",
"sha256": "c0a8bb4c6cd7868ae5ad78b8d2ecca80e9a449f1d3c03b5c0eadb20a791a8285",
"rawBytes": 3831,
"gzipBytes": 1585,
"gzipBytes": 1586,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-z66sNkRH.js",
"sha256": "33ee209a395a96a13e293ca1d48c2a5af686141b094bb92966d369f9dbcc8813",
"rawBytes": 963751,
"gzipBytes": 272797,
"path": "houseplan-assets/houseplan-card-7JITXtyy.js",
"sha256": "663801a4536057db0e907a740e0cc96d01feabc475a3e7a9b60567259aef7fb2",
"rawBytes": 963878,
"gzipBytes": 272826,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-nY6CPgRE.js",
"houseplan-assets/editor-Btuj24RH.js",
"houseplan-assets/houseplan-editor-runtime-CztWrxSd.js",
"houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js"
"houseplan-assets/de-DMfFz19N.js",
"houseplan-assets/editor-36HAh3Je.js",
"houseplan-assets/houseplan-editor-runtime-BuN48ldr.js",
"houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-CztWrxSd.js",
"sha256": "ac4ed4d1f057b7bae3ca52141d71beb81fd0ff971fc81e28d71073ad9bd5a5b2",
"path": "houseplan-assets/houseplan-editor-runtime-BuN48ldr.js",
"sha256": "e05365fb1ce4c1d97cacba2b243d07b9ecb4203d79596ba022096de3356eda5d",
"rawBytes": 522033,
"gzipBytes": 134930,
"gzipBytes": 134939,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-G4p88P9H.js",
"sha256": "9094f910509b9881e8d7882a9ad1855e39610da15a2a163088af4d1e96c8687f",
"path": "houseplan-assets/houseplan-onboarding-runtime-DB_uoFX7.js",
"sha256": "cf09b5cf51ed2220e4afd6b9523a66d013764eb0a68d1be1bbce8083fd5516d9",
"rawBytes": 26195,
"gzipBytes": 6497,
"gzipBytes": 6498,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Tyu0BfFF.js",
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/backdrop-pick-B3oldFOv.js",
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "cdd74054b0eda637b8e8184c96291b395fd558678f8e55af673931615e01b085",
"sha256": "2c76779b5485023d77e090cee3a9a59a5c558c60e30c7b25ed0b3bb3e34fa05e",
"rawBytes": 1089,
"gzipBytes": 754,
"gzipBytes": 755,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-z66sNkRH.js"
"houseplan-assets/houseplan-card-7JITXtyy.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-z66sNkRH.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__="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";import{c as e,l as o,t,cZ as s,A as a,c_ as i,c$ as l,d0 as n,E as r,b as c}from"./houseplan-card-7JITXtyy.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}
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-z66sNkRH.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-Tyu0BfFF.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cR as d,ap as u,ar as g,cS as m}from"./houseplan-card-7JITXtyy.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-B3oldFOv.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
${s.map(s=>{return _`
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="2f53621bc0d0f8ce3e04153700c0c17286b37d4dae42519acf74d6023333442a";try{await import("./houseplan-assets/houseplan-card-z66sNkRH.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__="92b6c0d37be41565c819aabf24176ece7969b13a5fd56613d47138f26080f773";try{await import("./houseplan-assets/houseplan-card-7JITXtyy.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 gate or door bound to a position-reporting cover no longer stutters the
whole card while it moves: the light cut through the opening now steps on a
5% grid, cutting the heavy geometry recomputes from about a hundred per
cycle to at most twenty
([#366](https://github.com/Matysh/houseplan-card/issues/366)).
- Furniture outline width now follows plan zoom like the same physical width
on lines and shapes, while independent width/depth resizing still keeps
horizontal, vertical and diagonal strokes equally thick
+5
View File
@@ -8,6 +8,11 @@
## Не выпущено
- Ворота или дверь на cover-сущности с позицией больше не дёргают карточку
во время движения: световой вырез проёма ступает по сетке 5%, и тяжёлых
пересчётов геометрии вместо ~сотни за цикл — не больше двадцати
([#366](https://github.com/Matysh/houseplan-card/issues/366)).
- Толщина контура мебели теперь меняется вместе с масштабом плана, как та же
физическая толщина у линий и фигур, а независимое изменение ширины и глубины
предмета по-прежнему не искажает горизонтальные, вертикальные и диагональные
+4 -1
View File
@@ -139,7 +139,10 @@ pass over the layer costs a fifth of one blurred mask per source.
## Caching
Barriers are keyed by their geometry fingerprint plus a compact, sorted
signature of bound interior door/gate opening amounts, never by `_cfgEpoch`:
signature of bound interior door/gate opening amounts (quantised to the
0.05 grid of `OPENING_LIGHT_AMOUNT_QUANTUM`, #366 — a moving cover steps the
light in 5% increments instead of forcing a recompute per percent), never by
`_cfgEpoch`:
the epoch lags behind geometry edited in place, and a stale barrier set is
invisible — the plan simply keeps lighting through a wall or closed door that
now exists. Unrelated HA updates preserve the signature and hit the bounded
Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 141 KiB

+21 -21
View File
@@ -3,7 +3,7 @@
"fixture": "synthetic-only",
"chromium": "151.0.7922.34",
"oxipng": null,
"sourceFingerprint": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"sourceFingerprint": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"captureScriptSha256": "fe76c8e2e27501e9a377d7aa15dd6cba6a018d55c07deb602dbee097f0a0a5ba",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -15,8 +15,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "36223106c073f07d8cc3ecf8eaab37192ebb2687daba65c5c21047d0b7890de0"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "0ca982495a7749907c48ebbddfd089c54385a4347f79f64e94af2c5a8d5c03eb"
},
"view-touch": {
"file": "02-view-touch.png",
@@ -26,8 +26,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "a4aa3af48578ee41594a9b1a6e98b4d39f3324a949a9fd9e63d5854576d5db85"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "3be239a5a494f6776540a9c0049694ee6f9d2ba927c8bcb4b26ee5a69a228a7d"
},
"space-create": {
"file": "03-space-create.png",
@@ -37,8 +37,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "0dc024726327f60f4a9ceaf3044381691f81f1090af81d1812e870f22d9343ba"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "a55b463055a565892ca139014e314a8f63fb5301c87fff2e48d7e69210fe9b6c"
},
"room-contour-close": {
"file": "04-room-contour-close.png",
@@ -48,8 +48,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "44cfaa95bd51e2cd628400e28db0ad8b2f0cd904385845bd402494f3f5c0d93c"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "dfad9a27371cfa397a3fd8dce7005f42d24649743f5322105ee894067d3a9f16"
},
"plan-context-tray": {
"file": "05-plan-context-tray.png",
@@ -59,8 +59,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "e0662429b423fb74151b583dcc2c8635b001b637d03bbf7a6b16aec46399c3f8"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "b75f348c632296da5c2fc142d53df613f757e1516acac315be50f5b4557729cc"
},
"device-editor": {
"file": "06-device-editor.png",
@@ -70,8 +70,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "2f861b3e0f3e09cbe462873887fc6a4412940393d37d782bcd0be58c9e82879e"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "8affadfce926fe2f6cf344c33f611c44fcbc79c1969f69c3a6935ff2e99c36c3"
},
"device-display-preview": {
"file": "06-device-display-preview.png",
@@ -81,8 +81,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "2cdabae1f89c3286e4fac0ce30f757ee1690b707ab8a5488748b7cd420626160"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "6d323b6f641b2cc7db64252f70567348bd3f2551f82e61fd3a2b4674d244918e"
},
"background-editor": {
"file": "07-background-editor.png",
@@ -92,8 +92,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "30147bb00a90eea7136b4cee30995f6e6a9217b5132f3e8d3ad7471413b1af8a"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "a1d1a905f1410f73231d2ea5d1dfbd8ae79c2fe8b0a79cff87ebb977ac7a3103"
},
"room-card": {
"file": "08-room-card.png",
@@ -103,8 +103,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "48a5685039a5e0de2a28190d857b193c86e2e63606e3c7a8c40fc9d2b190da4e"
},
"device-info": {
"file": "09-device-info.png",
@@ -114,8 +114,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "efd6edaeba9496509727bec13d19495b799339bdfe2176c791d3abcee83f45a5",
"imageSha256": "dd492f53150b7149085daada5cce9eeae9bde9e7ea1d86679a54b3041f72f517"
"sourceSha256": "d02374a4194e4601cb0a2047dc556f70b9fdf354894bdfeea9288d74c14742d8",
"imageSha256": "29a342811aaedaebc54ba337792d60cb454a7cf4c3063b210f6c4bafe0a4d87b"
}
}
}
+12
View File
@@ -680,6 +680,18 @@ const MUTANT_DEFINITIONS = [
replace: ' this._languageFailureUnsub = undefined;',
}],
},
{
id: 'opening-light-quantum-identity',
guard: 'node --test --test-name-pattern="#366" test/logic.test.mjs',
because: 'an identity quantum brings back ~100 barrier recomputes per moving-gate cycle — '
+ 'the sweep unit must bound distinct signatures to the 0.05 grid (#366)',
patches: [{
file: 'src/logic.ts',
find: ' return Math.min(1, Math.max(0,\n'
+ ' Math.round(safe / OPENING_LIGHT_AMOUNT_QUANTUM) * OPENING_LIGHT_AMOUNT_QUANTUM));',
replace: ' return safe;',
}],
},
{
id: 'backdrop-probe-always-safe',
guard: 'node demo/smoke_backdrop_guard.mjs',
+4 -1
View File
@@ -22,6 +22,7 @@ import {
pointOnBoundary, mergeRooms, splitRoomPath, polygonArea, closestPointOnBoundary, pointStrictlyInside as ptInside, islandsOf, sharedBoundary, distToSegment, outlineWithout, cutSegments, alignGuides, segmentAngle, is45, isExact45Vector, type AlignGuide, swipeTarget, clampScale, migratePdfUrls, roomFillModeOf, roomGlowOf, contentUrl,
snapToWall, snapPointAlongPoly, openingAmount, openingShoulders, interiorPoint,
isInteriorLightOpeningType, openingLightApertureLength, openingLightStateSignature,
quantizeOpeningLightAmount,
openingEntityReferences, filterOpeningEntityCandidates,
poleOfInaccessibility, subst,
averageLqi, fitView, declump, safeUrl, floorsOf, type FloorInfo,
@@ -10375,7 +10376,9 @@ export class HouseplanCard extends LitElement {
const ny = Math.cos(rad);
const interior = onFloor([o.rx + nx * probe, o.ry + ny * probe])
&& onFloor([o.rx - nx * probe, o.ry - ny * probe]);
return interior ? [{ opening: o, amount: this._openingAmt(o) }] : [];
// #366: the light pipeline consumes the quantised amount — the door
// LEAF animation stays smooth (_openingAmt elsewhere), only light steps.
return interior ? [{ opening: o, amount: quantizeOpeningLightAmount(this._openingAmt(o)) }] : [];
});
const openingStateSignature = openingLightStateSignature(
passageStates.map(({ opening, amount }) => ({
+16
View File
@@ -342,6 +342,22 @@ export interface OpeningLightStateEntry {
amount: number;
}
/**
* #366: one grid for the light pipeline's aperture amount. A moving cover
* publishes current_position by the percent; unquantised it forces a full
* barrier recompute per tick (~100 per gate cycle). Both the signature AND
* the cut geometry consume the same quantised value, so the cache key and
* the drawn aperture agree by construction. 0 and 1 are exact grid nodes —
* binary contact doors are byte-identical to the pre-#366 behaviour.
*/
export const OPENING_LIGHT_AMOUNT_QUANTUM = 0.05;
export function quantizeOpeningLightAmount(amount: number): number {
const safe = Number.isFinite(amount) ? Math.max(0, Math.min(1, amount)) : 0;
return Math.min(1, Math.max(0,
Math.round(safe / OPENING_LIGHT_AMOUNT_QUANTUM) * OPENING_LIGHT_AMOUNT_QUANTUM));
}
/** State-only identity for interior light apertures. Structural geometry is
* fingerprinted separately, so unrelated HA updates cannot evict barriers. */
export function openingLightStateSignature(
+37 -1
View File
@@ -24,7 +24,9 @@ import {
normalizeDeviceDisplay, isAlarmCapable,
liveText, liveTextValue, liveTextReference, liveTextToken,
hassValue, valueWithUnit, decorTextScale, decorTextLines,
LIVE_TEXT_DASH, LIVE_TEXT_VALUE_MAX, DECOR_TEXT_SCALE_MIN, DECOR_TEXT_SCALE_MAX } from '../test-build/logic.js';
LIVE_TEXT_DASH, LIVE_TEXT_VALUE_MAX, DECOR_TEXT_SCALE_MIN, DECOR_TEXT_SCALE_MAX,
quantizeOpeningLightAmount,
} from '../test-build/logic.js';
import {
iconFor, compileIconRules, isValidPattern, iconFromDeviceClasses,
} from '../test-build/rules.js';
@@ -1530,3 +1532,37 @@ test('liveTextValue: a formatter that omits the unit still gets one (the demo st
assert.equal(liveTextValue(h, { entity: 'sensor.tank' }), '68 %',
'formatEntityState returning a bare state must not cost the label its unit');
});
test('квант amount света: границы, узлы и ≤21 сигнатура за полный ход (#366)', () => {
// AC1: точные узлы и зажимы
assert.equal(quantizeOpeningLightAmount(0), 0);
assert.equal(quantizeOpeningLightAmount(1), 1);
assert.equal(quantizeOpeningLightAmount(NaN), 0);
assert.equal(quantizeOpeningLightAmount(-1), 0);
assert.equal(quantizeOpeningLightAmount(2), 1);
assert.equal(quantizeOpeningLightAmount(0.30), quantizeOpeningLightAmount(0.31), 'один узел');
assert.notEqual(quantizeOpeningLightAmount(0.30), quantizeOpeningLightAmount(0.33), 'разные узлы');
// AC2: свип 0..1 шагом 0.001 через сигнатуру — не больше 21 различной
const signatures = new Set();
for (let step = 0; step <= 1000; step++) {
const amount = quantizeOpeningLightAmount(step / 1000);
signatures.add(openingLightStateSignature([
{ id: 'gate', type: 'gate', contact: 'cover.gate', amount },
]));
}
assert.ok(signatures.size <= 21, `сигнатур ${signatures.size}, ожидалось ≤21`);
// конечные положения совпадают с неквантованными (binary-регресс, AC3)
const closed = openingLightStateSignature([
{ id: 'door', type: 'door', contact: 'binary_sensor.d', amount: 0 },
]);
const open = openingLightStateSignature([
{ id: 'door', type: 'door', contact: 'binary_sensor.d', amount: 1 },
]);
assert.equal(closed, 'door:0.000');
assert.equal(open, 'door:1.000');
assert.equal(openingLightStateSignature([
{ id: 'door', type: 'door', contact: 'binary_sensor.d', amount: quantizeOpeningLightAmount(1) },
]), open, 'квант единицы байт-в-байт');
});