@@ -1,111 +1,111 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf",
|
||||
"fingerprint": "0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f",
|
||||
"entry": "houseplan-card.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 256046,
|
||||
"initialViewGzipBytes": 256091,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/de-C7PgDVgU.js",
|
||||
"houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/de-SftP4Auy.js",
|
||||
"houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"lazyGzipBytes": 160338,
|
||||
"lazyGzipBytes": 160351,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 131830,
|
||||
"lazyEditorGzipBytes": 131833,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 7192,
|
||||
"lazyOnboardingGzipBytes": 7198,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-C7PgDVgU.js"
|
||||
"houseplan-assets/de-SftP4Auy.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 21866,
|
||||
"lazyLocaleGzipBytes": 21873,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/de-C7PgDVgU.js",
|
||||
"sha256": "0a7d0b50dde016600e4a01b38063b13eb08179de87091fc30af08597bd09d49e",
|
||||
"path": "houseplan-assets/de-SftP4Auy.js",
|
||||
"sha256": "3bee20ff5a52b6c6726cf857545bc3e1061a1249a515df5660491fd662218c94",
|
||||
"rawBytes": 71477,
|
||||
"gzipBytes": 21866,
|
||||
"gzipBytes": 21873,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"sha256": "00cfc5201c071a576ab5c2db63d1d8467bff3f702e81ca49c297700c6ba1712b",
|
||||
"path": "houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"sha256": "d90a38e511769dc68d69081375ffb5067b196d429ab59fc4de06068baeafcac8",
|
||||
"rawBytes": 3831,
|
||||
"gzipBytes": 1582,
|
||||
"gzipBytes": 1584,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"sha256": "8ed3aca1f9b05e41e123c54584bc498cfbabe35d232b708768487185d282f7eb",
|
||||
"rawBytes": 910277,
|
||||
"gzipBytes": 255864,
|
||||
"path": "houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"sha256": "1937126237e02356adad8b8c34b4adb29892bc183d6bcaa17e2542404907c811",
|
||||
"rawBytes": 910424,
|
||||
"gzipBytes": 255907,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-C7PgDVgU.js",
|
||||
"houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js"
|
||||
"houseplan-assets/de-SftP4Auy.js",
|
||||
"houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"sha256": "da7c8f5cb805e66604343cf899d8eadf33c6a349ba7d865067ff5cf7fb639bb7",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"sha256": "51fdd8f9766569a3b1af3b04d91dea81e6b555d9784bf5be53c6f82a42fbe51e",
|
||||
"rawBytes": 504959,
|
||||
"gzipBytes": 129698,
|
||||
"gzipBytes": 129696,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js",
|
||||
"sha256": "3047ab81aef88f76caa129e43ba4746cc560de6036405759dbd00d1ad98983d6",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js",
|
||||
"sha256": "19299e14098c6e413ca410d7b2c3715d57c945fdb690991f92fa2dc769d9c84d",
|
||||
"rawBytes": 26356,
|
||||
"gzipBytes": 6642,
|
||||
"gzipBytes": 6645,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/space-deletion-niSjMtPX.js",
|
||||
"sha256": "2935c48da7310444e002c3426b3b50ec693c7b1484c8b067173621692a4d19a5",
|
||||
"path": "houseplan-assets/space-deletion-DIa5HjfJ.js",
|
||||
"sha256": "93f68c225dc00086ea86ac8a97ddfdf49b99ff17a8221f3cc3796f92d5e9f3cb",
|
||||
"rawBytes": 911,
|
||||
"gzipBytes": 550,
|
||||
"gzipBytes": 553,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "147f3e320c09da67cbe342bcb38567c0df6e6b685d0e52bab4277ee69b896361",
|
||||
"sha256": "823dc598d07959ff800a942ca1b705bd54b94dd4def67d66668f2a5552cc271f",
|
||||
"rawBytes": 190,
|
||||
"gzipBytes": 182,
|
||||
"gzipBytes": 184,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";import{c as e,l as o,t,d2 as s,A as a,d3 as i,d4 as l,d5 as n,E as r,b as c}from"./houseplan-card-DqEPY1YF.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__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";import{c as e,l as o,t,d2 as s,A as a,d3 as i,d4 as l,d5 as n,E as r,b as c}from"./houseplan-card-BU843YpJ.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__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as c,b$ as n,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-DqEPY1YF.js";import{i as b,c as $,a as v,s as f,t as y}from"./space-deletion-niSjMtPX.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";class C{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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),c=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:c,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as c,b$ as n,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-BU843YpJ.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-DIa5HjfJ.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";class C{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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),c=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:c,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
${s.map(s=>{return p`
|
||||
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
|
||||
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
|
||||
@@ -46,7 +46,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
<label>${this.host._t("space.plan_label")}</label>
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"file"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=f(t,"file")} />
|
||||
@change=${()=>this.host._spaceDialog=y(t,"file")} />
|
||||
<span>${this.host._t("space.source_file")}</span>
|
||||
</label>
|
||||
${"file"===t.source?p`<div class="planrow">
|
||||
@@ -68,7 +68,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
${t.pickSaved?this._renderServerPlans(t):_}`:_}
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"draw"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=f(t,"draw")} />
|
||||
@change=${()=>this.host._spaceDialog=y(t,"draw")} />
|
||||
<span>${this.host._t("space.source_draw")}</span>
|
||||
</label>
|
||||
|
||||
@@ -85,7 +85,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
|
||||
<label class="dispsection">${this.host._t("space.display_section")}</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=y(t,"showBorders",s)})}
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=f(t,"showBorders",s)})}
|
||||
<span>${this.host._t("space.show_borders")}</span>
|
||||
</label>
|
||||
<label>${this.host._t("space.zero_wall_style")}</label>
|
||||
@@ -99,7 +99,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
</select>
|
||||
<div class="rhint">${this.host._t("space.zero_wall_help")}</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=y(t,"showNames",s)})}
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=f(t,"showNames",s)})}
|
||||
<span>${this.host._t("space.show_names")}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
@@ -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__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";export{t as a,e as c,o as i,r as s,s as t};
|
||||
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";export{t as a,e as c,o as i,r as s,s as t};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";export{d6 as HouseplanCard}from"./houseplan-assets/houseplan-card-DqEPY1YF.js";
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";export{d6 as HouseplanCard}from"./houseplan-assets/houseplan-card-BU843YpJ.js";
|
||||
|
||||
@@ -155,6 +155,138 @@ const out = await page.evaluate(async () => {
|
||||
result.activeTargetWorksOnBothSurfaces = activePresentation.visual.status === 'working'
|
||||
&& activePreview?.presentation?.visual?.status === 'working';
|
||||
|
||||
// #318: an active physical HA device may legitimately expose no entity
|
||||
// registry rows. Empty telemetry is not evidence that this controller is
|
||||
// offline; plan and preview must follow only the external target status.
|
||||
const entitylessDeviceId = 'issue-318-entityless-device';
|
||||
const entitylessMarkerId = 'issue-318-entityless-marker';
|
||||
const entitylessTarget = 'light.issue_318_target';
|
||||
const entitylessTargetDeviceId = 'issue-318-lights';
|
||||
const entitylessMarker = {
|
||||
id: entitylessMarkerId,
|
||||
binding: `device:${entitylessDeviceId}`,
|
||||
name: 'Issue 318 entityless switch',
|
||||
icon: 'mdi:light-switch',
|
||||
tap_action: 'toggle',
|
||||
controls: [entitylessTarget],
|
||||
space: c._space,
|
||||
area: room.area,
|
||||
};
|
||||
c.hass = {
|
||||
...c.hass,
|
||||
devices: {
|
||||
...c.hass.devices,
|
||||
[entitylessDeviceId]: {
|
||||
id: entitylessDeviceId,
|
||||
name: 'Issue 318 entityless switch',
|
||||
model: 'Entityless wireless switch',
|
||||
area_id: room.area,
|
||||
identifiers: [['demo', entitylessDeviceId]],
|
||||
entry_type: null,
|
||||
via_device_id: null,
|
||||
},
|
||||
[entitylessTargetDeviceId]: {
|
||||
id: entitylessTargetDeviceId,
|
||||
name: 'Issue 318 lights',
|
||||
model: 'Demo light group',
|
||||
area_id: room.area,
|
||||
identifiers: [['demo', entitylessTargetDeviceId]],
|
||||
entry_type: null,
|
||||
via_device_id: null,
|
||||
},
|
||||
},
|
||||
entities: {
|
||||
...c.hass.entities,
|
||||
[entitylessTarget]: {
|
||||
entity_id: entitylessTarget,
|
||||
device_id: entitylessTargetDeviceId,
|
||||
platform: 'demo',
|
||||
},
|
||||
},
|
||||
states: {
|
||||
...c.hass.states,
|
||||
[entitylessTarget]: { entity_id: entitylessTarget, state: 'on', attributes: {} },
|
||||
},
|
||||
};
|
||||
c._serverCfg = {
|
||||
...c._serverCfg,
|
||||
markers: [
|
||||
...c._serverCfg.markers.filter((item) => item.id !== entitylessMarkerId),
|
||||
entitylessMarker,
|
||||
],
|
||||
};
|
||||
c._layout = {
|
||||
...c._layout,
|
||||
[entitylessMarkerId]: { s: c._space, x: 0.58, y: 0.5 },
|
||||
};
|
||||
c._regSignature = '';
|
||||
c._maybeRebuildDevices();
|
||||
c._setMode('view');
|
||||
c.requestUpdate();
|
||||
await c.updateComplete;
|
||||
|
||||
const entitylessController = () => c._devices.find((item) => item.id === entitylessMarkerId);
|
||||
const onPresentation = c._devicePresentation(entitylessController(), true);
|
||||
const onElement = sr().querySelector(`.dev[data-id="${entitylessMarkerId}"]`);
|
||||
result.entitylessDeviceHasEmptyRoster = entitylessController()?.entities?.length === 0;
|
||||
result.entitylessOnIsAvailableWorking = onPresentation.visual.availability === 'available'
|
||||
&& onPresentation.visual.status === 'working'
|
||||
&& onElement?.getAttribute('data-state') === 'working'
|
||||
&& !onElement?.classList.contains('unavail');
|
||||
|
||||
c._setMode('devices');
|
||||
await c.updateComplete;
|
||||
c._openMarkerDialog(entitylessController());
|
||||
await c.updateComplete;
|
||||
const entitylessPreview = sr().querySelector('hp-device-preview');
|
||||
await entitylessPreview?.updateComplete;
|
||||
result.entitylessPreviewMatchesOnPlan = entitylessPreview?.presentation?.visual?.availability
|
||||
=== onPresentation.visual.availability
|
||||
&& entitylessPreview?.presentation?.visual?.status === onPresentation.visual.status
|
||||
&& entitylessPreview?.presentation?.sourceKind === onPresentation.sourceKind
|
||||
&& JSON.stringify(entitylessPreview?.presentation?.classes)
|
||||
=== JSON.stringify(onPresentation.classes);
|
||||
|
||||
c.hass = {
|
||||
...c.hass,
|
||||
states: {
|
||||
...c.hass.states,
|
||||
[entitylessTarget]: { ...c.hass.states[entitylessTarget], state: 'off' },
|
||||
},
|
||||
};
|
||||
c.requestUpdate();
|
||||
await c.updateComplete;
|
||||
await entitylessPreview?.updateComplete;
|
||||
const offPresentation = c._devicePresentation(entitylessController(), true, true);
|
||||
result.entitylessOffIsAvailableNeutralEverywhere = offPresentation.visual.availability
|
||||
=== 'available'
|
||||
&& offPresentation.visual.status === 'neutral'
|
||||
&& entitylessPreview?.presentation?.visual?.availability === 'available'
|
||||
&& entitylessPreview?.presentation?.visual?.status === 'neutral'
|
||||
&& entitylessPreview?.presentation?.sourceKind === offPresentation.sourceKind
|
||||
&& JSON.stringify(entitylessPreview?.presentation?.classes)
|
||||
=== JSON.stringify(offPresentation.classes);
|
||||
|
||||
c.hass = {
|
||||
...c.hass,
|
||||
states: {
|
||||
...c.hass.states,
|
||||
[entitylessTarget]: { ...c.hass.states[entitylessTarget], state: 'unavailable' },
|
||||
},
|
||||
};
|
||||
c.requestUpdate();
|
||||
await c.updateComplete;
|
||||
await entitylessPreview?.updateComplete;
|
||||
const unavailableTargetPresentation = c._devicePresentation(entitylessController(), true, true);
|
||||
result.entitylessUnavailableTargetIsAvailableNeutralEverywhere
|
||||
= unavailableTargetPresentation.visual.availability === 'available'
|
||||
&& unavailableTargetPresentation.visual.status === 'neutral'
|
||||
&& entitylessPreview?.presentation?.visual?.availability === 'available'
|
||||
&& entitylessPreview?.presentation?.visual?.status === 'neutral'
|
||||
&& entitylessPreview?.presentation?.sourceKind === unavailableTargetPresentation.sourceKind
|
||||
&& JSON.stringify(entitylessPreview?.presentation?.classes)
|
||||
=== JSON.stringify(unavailableTargetPresentation.classes);
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,111 +1,111 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"fingerprint": "13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf",
|
||||
"fingerprint": "0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f",
|
||||
"entry": "houseplan-card.js",
|
||||
"initialViewFiles": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"houseplan-card.js"
|
||||
],
|
||||
"initialViewGzipBytes": 256046,
|
||||
"initialViewGzipBytes": 256091,
|
||||
"lazyFiles": [
|
||||
"houseplan-assets/de-C7PgDVgU.js",
|
||||
"houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/de-SftP4Auy.js",
|
||||
"houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"lazyGzipBytes": 160338,
|
||||
"lazyGzipBytes": 160351,
|
||||
"lazyEditorFiles": [
|
||||
"houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"lazyEditorGzipBytes": 131830,
|
||||
"lazyEditorGzipBytes": 131833,
|
||||
"lazyOnboardingFiles": [
|
||||
"houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"lazyOnboardingGzipBytes": 7192,
|
||||
"lazyOnboardingGzipBytes": 7198,
|
||||
"lazyLocaleFiles": [
|
||||
"houseplan-assets/de-C7PgDVgU.js"
|
||||
"houseplan-assets/de-SftP4Auy.js"
|
||||
],
|
||||
"lazyLocaleGzipBytes": 21866,
|
||||
"lazyLocaleGzipBytes": 21873,
|
||||
"files": [
|
||||
{
|
||||
"path": "houseplan-assets/de-C7PgDVgU.js",
|
||||
"sha256": "0a7d0b50dde016600e4a01b38063b13eb08179de87091fc30af08597bd09d49e",
|
||||
"path": "houseplan-assets/de-SftP4Auy.js",
|
||||
"sha256": "3bee20ff5a52b6c6726cf857545bc3e1061a1249a515df5660491fd662218c94",
|
||||
"rawBytes": 71477,
|
||||
"gzipBytes": 21866,
|
||||
"gzipBytes": 21873,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"sha256": "00cfc5201c071a576ab5c2db63d1d8467bff3f702e81ca49c297700c6ba1712b",
|
||||
"path": "houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"sha256": "d90a38e511769dc68d69081375ffb5067b196d429ab59fc4de06068baeafcac8",
|
||||
"rawBytes": 3831,
|
||||
"gzipBytes": 1582,
|
||||
"gzipBytes": 1584,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"sha256": "8ed3aca1f9b05e41e123c54584bc498cfbabe35d232b708768487185d282f7eb",
|
||||
"rawBytes": 910277,
|
||||
"gzipBytes": 255864,
|
||||
"path": "houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"sha256": "1937126237e02356adad8b8c34b4adb29892bc183d6bcaa17e2542404907c811",
|
||||
"rawBytes": 910424,
|
||||
"gzipBytes": 255907,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": [
|
||||
"houseplan-assets/de-C7PgDVgU.js",
|
||||
"houseplan-assets/editor-CPiO_Ss0.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js"
|
||||
"houseplan-assets/de-SftP4Auy.js",
|
||||
"houseplan-assets/editor-DuOmG3Hn.js",
|
||||
"houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-BgHN32PH.js",
|
||||
"sha256": "da7c8f5cb805e66604343cf899d8eadf33c6a349ba7d865067ff5cf7fb639bb7",
|
||||
"path": "houseplan-assets/houseplan-editor-runtime-pSbu_R5t.js",
|
||||
"sha256": "51fdd8f9766569a3b1af3b04d91dea81e6b555d9784bf5be53c6f82a42fbe51e",
|
||||
"rawBytes": 504959,
|
||||
"gzipBytes": 129698,
|
||||
"gzipBytes": 129696,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-yqs9kwpI.js",
|
||||
"sha256": "3047ab81aef88f76caa129e43ba4746cc560de6036405759dbd00d1ad98983d6",
|
||||
"path": "houseplan-assets/houseplan-onboarding-runtime-DXJhYLas.js",
|
||||
"sha256": "19299e14098c6e413ca410d7b2c3715d57c945fdb690991f92fa2dc769d9c84d",
|
||||
"rawBytes": 26356,
|
||||
"gzipBytes": 6642,
|
||||
"gzipBytes": 6645,
|
||||
"isEntry": false,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js",
|
||||
"houseplan-assets/space-deletion-niSjMtPX.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js",
|
||||
"houseplan-assets/space-deletion-DIa5HjfJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-assets/space-deletion-niSjMtPX.js",
|
||||
"sha256": "2935c48da7310444e002c3426b3b50ec693c7b1484c8b067173621692a4d19a5",
|
||||
"path": "houseplan-assets/space-deletion-DIa5HjfJ.js",
|
||||
"sha256": "93f68c225dc00086ea86ac8a97ddfdf49b99ff17a8221f3cc3796f92d5e9f3cb",
|
||||
"rawBytes": 911,
|
||||
"gzipBytes": 550,
|
||||
"gzipBytes": 553,
|
||||
"isEntry": false,
|
||||
"imports": [],
|
||||
"dynamicImports": []
|
||||
},
|
||||
{
|
||||
"path": "houseplan-card.js",
|
||||
"sha256": "147f3e320c09da67cbe342bcb38567c0df6e6b685d0e52bab4277ee69b896361",
|
||||
"sha256": "823dc598d07959ff800a942ca1b705bd54b94dd4def67d66668f2a5552cc271f",
|
||||
"rawBytes": 190,
|
||||
"gzipBytes": 182,
|
||||
"gzipBytes": 184,
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"houseplan-assets/houseplan-card-DqEPY1YF.js"
|
||||
"houseplan-assets/houseplan-card-BU843YpJ.js"
|
||||
],
|
||||
"dynamicImports": []
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";import{c as e,l as o,t,d2 as s,A as a,d3 as i,d4 as l,d5 as n,E as r,b as c}from"./houseplan-card-DqEPY1YF.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__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";import{c as e,l as o,t,d2 as s,A as a,d3 as i,d4 as l,d5 as n,E as r,b as c}from"./houseplan-card-BU843YpJ.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__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as c,b$ as n,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-DqEPY1YF.js";import{i as b,c as $,a as v,s as f,t as y}from"./space-deletion-niSjMtPX.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";class C{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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),c=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:c,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";import{bU as t,bV as s,bW as e,bX as o,aq as a,bY as l,c1 as i,bZ as h,b_ as c,b$ as n,c0 as r,b as p,A as _,cV as d,ap as u,ar as g,cW as m}from"./houseplan-card-BU843YpJ.js";import{i as b,c as $,a as v,s as y,t as f}from"./space-deletion-DIa5HjfJ.js";const w=1e3,D=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},S="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";class C{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;const o={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[e.type]||(e.name.toLowerCase().endsWith(".svg")?"svg":"");if(!o)return void this.host._showToast(this.host._t("toast.plan_formats"));const a=new Uint8Array(await e.arrayBuffer());let l="";for(let t=0;t<a.length;t+=32768)l+=String.fromCharCode(...a.subarray(t,t+32768));const i=btoa(l),h=URL.createObjectURL(e),c=await new Promise(t=>{const s=new Image;s.onload=()=>t(s.naturalWidth&&s.naturalHeight?s.naturalWidth/s.naturalHeight:1.414),s.onerror=()=>t(1.414),s.src=h});URL.revokeObjectURL(h),this.host._spaceDialog={...this.host._spaceDialog,planFile:{ext:o,b64:i,aspect:c,name:e.name}}}_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 p`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return p`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return p`<div class="savedplans">
|
||||
${s.map(s=>{return p`
|
||||
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
|
||||
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
|
||||
@@ -46,7 +46,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
<label>${this.host._t("space.plan_label")}</label>
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"file"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=f(t,"file")} />
|
||||
@change=${()=>this.host._spaceDialog=y(t,"file")} />
|
||||
<span>${this.host._t("space.source_file")}</span>
|
||||
</label>
|
||||
${"file"===t.source?p`<div class="planrow">
|
||||
@@ -68,7 +68,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
${t.pickSaved?this._renderServerPlans(t):_}`:_}
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${"draw"===t.source}
|
||||
@change=${()=>this.host._spaceDialog=f(t,"draw")} />
|
||||
@change=${()=>this.host._spaceDialog=y(t,"draw")} />
|
||||
<span>${this.host._t("space.source_draw")}</span>
|
||||
</label>
|
||||
|
||||
@@ -85,7 +85,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
|
||||
<label class="dispsection">${this.host._t("space.display_section")}</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=y(t,"showBorders",s)})}
|
||||
${this._boolInput(t.showBorders,s=>{this.host._spaceDialog=f(t,"showBorders",s)})}
|
||||
<span>${this.host._t("space.show_borders")}</span>
|
||||
</label>
|
||||
<label>${this.host._t("space.zero_wall_style")}</label>
|
||||
@@ -99,7 +99,7 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f
|
||||
</select>
|
||||
<div class="rhint">${this.host._t("space.zero_wall_help")}</div>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=y(t,"showNames",s)})}
|
||||
${this._boolInput(t.showNames,s=>{this.host._spaceDialog=f(t,"showNames",s)})}
|
||||
<span>${this.host._t("space.show_names")}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
@@ -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__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";export{t as a,e as c,o as i,r as s,s as t};
|
||||
function e(e,o){return{id:e,title:o,plan_url:null,view_box:[0,0,1,1],rooms:[],wall_segments:[]}}function o(e="file"){const o="draw"===e;return{source:e,showBorders:o,showNames:o,displayTouched:!1}}function r(e,o){if(e.displayTouched)return{...e,source:o};const r="draw"===o;return{...e,source:o,showBorders:r,showNames:r}}function s(e,o,r){return{...e,[o]:r,displayTouched:!0}}function t(e,o,r){const s=(e?.spaces||[]).find(e=>e?.id===r),t=new Set((s?.rooms||[]).map(e=>String(e?.id||"")).filter(Boolean)),n=[...new Set((e?.markers||[]).filter(e=>!0!==e?.removed&&"string"==typeof e?.id).filter(e=>e.space===r||"string"==typeof e.room_id&&t.has(e.room_id)||o?.[e.id]?.s===r).map(e=>e.id))].sort((e,o)=>e.localeCompare(o));return{markerIds:n,count:n.length}}globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";export{t as a,e as c,o as i,r as s,s as t};
|
||||
@@ -1 +1 @@
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="13fcebb68eb62b100bccb7b77eb09b8fce2f41e6c77bcbe8870e6dc4d97eedbf";export{d6 as HouseplanCard}from"./houseplan-assets/houseplan-card-DqEPY1YF.js";
|
||||
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="0c61742c98d55aec077478fcee7b8acf52e2ac1b70e2ed23532483b845ca884f";export{d6 as HouseplanCard}from"./houseplan-assets/houseplan-card-BU843YpJ.js";
|
||||
|
||||
@@ -1315,8 +1315,13 @@ its configured space.
|
||||
icon working state mirrors the effective light graph. Controller availability
|
||||
is deliberately separate (#251): at least one live own active entity
|
||||
(including battery/LQI/update diagnostics) keeps a physical controller
|
||||
available, while an all-unavailable target graph is neutral. A virtual
|
||||
controller is available by definition. An explicit Toggle whose configured
|
||||
available, while an all-unavailable target graph is neutral. An active
|
||||
physical `device:` binding with an empty own entity roster is also available:
|
||||
absence of telemetry is not proof that the device is offline, and its target
|
||||
graph still decides working versus neutral (#318). Once that own roster is
|
||||
non-empty, all-missing/`unknown`/`unavailable` states remain positive offline
|
||||
evidence and fade the controller. A virtual controller is available by
|
||||
definition. An explicit Toggle whose configured
|
||||
group has no executable unavailable/missing/HA-disabled target produces the
|
||||
card's standard local explanatory toast and no service/press feedback;
|
||||
partial groups keep executing their available subset.
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Active physical wall controllers that Home Assistant exposes without any own
|
||||
entities no longer look permanently unavailable: they now stay opaque and
|
||||
follow their controlled target as yellow when working or neutral otherwise
|
||||
([#318](https://github.com/Matysh/houseplan-card/issues/318)).
|
||||
|
||||
- An outdated tab or third-party client can no longer silently replace a newer
|
||||
saved plan by omitting its configuration revision. House Plan rejects that
|
||||
write as a conflict and keeps the server copy intact; revision-less bootstrap
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Активные физические настенные выключатели, для которых Home Assistant не
|
||||
предоставляет собственных сущностей, больше не выглядят постоянно
|
||||
недоступными: теперь они остаются непрозрачными и становятся жёлтыми при
|
||||
работающей управляемой цели или нейтральными в остальных случаях
|
||||
([#318](https://github.com/Matysh/houseplan-card/issues/318)).
|
||||
|
||||
- Устаревшая вкладка или сторонний клиент больше не может молча заменить новый
|
||||
сохранённый план, не передав ревизию конфигурации. House Plan отклоняет такую
|
||||
запись как конфликт и сохраняет серверную копию; инициализация без ревизии
|
||||
|
||||
@@ -39,7 +39,7 @@ mutation evidence в одном pull request.
|
||||
| S02 | cover — побочная capability при light/role | `source.cover_capability_bypassed` | cover не перехватывает выбранный face | normal | `presentation-source-decision-trace`; `presentation-row-contract` |
|
||||
| S03 | есть active external controls | `source.controls` + `availability.controller_available` | status берётся от целей, availability — от контроллера | normal | `controller-availability-follows-target`; `presentation-row-contract` |
|
||||
| S04 | цели unavailable, controller имеет live diagnostic | `availability.controller_available` | доступная нейтральная подложка, не `unavail` | normal | `controller-diagnostics-do-not-prove-online`; `presentation-row-contract` |
|
||||
| S05 | цель работает, controller не имеет live entity | `availability.controller_unavailable` | faded controller; нет yellow/pulse | normal action сохраняется | `controller-availability-follows-target`; `presentation-row-contract` |
|
||||
| S05 | у controller есть собственные entities, но ни одной live entity; цель работает | `availability.controller_unavailable` | faded controller; нет yellow/pulse | normal action сохраняется | `controller-availability-follows-target`; `presentation-row-contract` |
|
||||
| S06 | virtual controller + controls | `source.virtual_controller` | controller доступен; status следует цели | normal | `presentation-source-decision-trace`; `presentation-row-contract` |
|
||||
| S07 | все saved controls отфильтрованы tombstone | `source.filtered_saved_controls` | controller-role сохранён, availability дают свои diagnostics | normal | `wireless-controller-loses-filtered-target-role`; `wireless-controller-preview-drops-sibling-markers` |
|
||||
| S08 | manual virtual light с outgoing controls | `source.manual_virtual_light` | собственный manual source владеет face | normal | `presentation-source-decision-trace`; `presentation-row-contract` |
|
||||
@@ -49,6 +49,7 @@ mutation evidence в одном pull request.
|
||||
| S12 | пригодного source нет | `source.none` | neutral base-icon fallback | normal | `presentation-source-decision-trace`; `presentation-row-contract` |
|
||||
| S13 | critical alarm sibling вне обычного source | `source.critical_sibling` + `status.alarm` | alarm добавлен в aggregate и побеждает normal status | normal | `device-presentation-policy-alarm`; `presentation-row-contract` |
|
||||
| S14 | static plan fast path без source details | `source.skipped_static_fast_path` | source graph намеренно не вычисляется; static face остаётся neutral | normal; без source-derived данных | `presentation-static-source-fast-path`; `presentation-row-contract` |
|
||||
| S15 | active physical `device:` controller с пустым собственным roster + controls | `availability.controller_available` | status следует цели: working = yellow, off/unavailable/missing = neutral; controller не faded | normal | `entityless-active-controller-stays-available`; `presentation-row-contract` |
|
||||
|
||||
## Финальное лицо, контент и диагностика
|
||||
|
||||
|
||||
@@ -1608,7 +1608,13 @@ separately promised workflows:
|
||||
controller is encountered first. Controller availability is independent
|
||||
(#251): live battery/LQI/update keeps it neutral and opaque when every
|
||||
target is unavailable, all unavailable own entities fade it even if a
|
||||
target is on, and a virtual controller remains available. A separately
|
||||
target is on, and a virtual controller remains available. An active
|
||||
physical `device:` controller with an empty own roster remains available
|
||||
(#318): target `on` is yellow, while target `off`, unavailable, missing or
|
||||
runtime-filtered is neutral; the plan and device-dialog preview must agree.
|
||||
This exception must not apply to a non-empty all-unavailable roster,
|
||||
`entity:` bindings, HA-disabled/orphaned bindings or virtual lifecycle.
|
||||
A separately
|
||||
deleted target marker also cannot turn a live wireless controller into
|
||||
`unavailable` or make dialog preview disagree with the saved plan (#274):
|
||||
both projections consume the complete marker roster, preserve the live
|
||||
@@ -1620,6 +1626,7 @@ separately promised workflows:
|
||||
device-presentation.test.mjs, device-toggle.test.mjs; golden:
|
||||
device-icon-state-table light/dark; mutation: controller-availability-follows-target,
|
||||
controller-diagnostics-do-not-prove-online,
|
||||
entityless-active-controller-stays-available,
|
||||
wireless-controller-loses-filtered-target-role,
|
||||
wireless-controller-preview-drops-sibling-markers,
|
||||
unavailable-toggle-stays-silent, partial-group-shows-noop-toast; #274:
|
||||
|
||||
@@ -601,8 +601,13 @@ For a controller with `controls`, target work and controller availability are
|
||||
independent. The controlled lights still decide whether the marker is yellow,
|
||||
but only the controller's own active entities decide whether it fades. A live
|
||||
battery, Zigbee LQI or update entity therefore keeps a wireless switch neutral
|
||||
and opaque when all of its lamps are unavailable. A controller with no live own
|
||||
entity fades even if a target is on; a virtual controller is always available.
|
||||
and opaque when all of its lamps are unavailable. If an active physical HA
|
||||
device exposes no entities at all, House Plan also keeps its controller opaque:
|
||||
missing telemetry alone is not evidence that the device is offline, so its
|
||||
controlled target makes it yellow when working and neutral otherwise. If the
|
||||
device does expose own entities but all of them are missing, `unknown` or
|
||||
`unavailable`, the controller fades even if a target is on. A virtual controller
|
||||
is always available.
|
||||
This remains true when the same target was separately removed from the plan:
|
||||
the removed marker is not restored, but it cannot make the controller look
|
||||
offline or make its editor preview disagree with the plan.
|
||||
|
||||
@@ -976,9 +976,13 @@ hover → состояние → нейтрально**. Если все сущ
|
||||
активные сущности контроллера. Живой `battery`, `linkquality` или `update`
|
||||
удерживает беспроводной выключатель непрозрачным, даже если все лампы
|
||||
недоступны; в таком случае выключатель нейтрален, а не жёлтый. Если ни одна
|
||||
собственная сущность не имеет живого состояния, контроллер бледнеет даже при
|
||||
включённой цели. Виртуальный контроллер не имеет физического HA-устройства и
|
||||
считается доступным.
|
||||
собственная сущность из непустого списка не имеет живого состояния, контроллер
|
||||
бледнеет даже при включённой цели. Но если активное физическое устройство HA
|
||||
вообще не предоставляет собственных сущностей, House Plan не считает отсутствие
|
||||
телеметрии доказательством недоступности: такой контроллер остаётся непрозрачным,
|
||||
становится жёлтым при работающей цели и нейтральным во всех остальных состояниях
|
||||
цели. Виртуальный контроллер не имеет физического HA-устройства и считается
|
||||
доступным.
|
||||
То же правило действует, если маркер этой цели отдельно удалён с плана:
|
||||
удалённый маркер не возвращается, но он не может сделать контроллер визуально
|
||||
недоступным или вызвать расхождение плана и preview в редакторе устройства.
|
||||
|
||||
|
Before Width: | Height: | Size: 199 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 335 KiB After Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 289 KiB |
|
Before Width: | Height: | Size: 319 KiB After Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 134 KiB |
@@ -3,7 +3,7 @@
|
||||
"fixture": "synthetic-only",
|
||||
"chromium": "151.0.7922.34",
|
||||
"oxipng": null,
|
||||
"sourceFingerprint": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"sourceFingerprint": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"captureScriptSha256": "fe76c8e2e27501e9a377d7aa15dd6cba6a018d55c07deb602dbee097f0a0a5ba",
|
||||
"command": "npm run build && node demo/docs/capture.mjs",
|
||||
"scenarios": {
|
||||
@@ -15,8 +15,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "9190bd86a07b0cb0502019d76d3286c0542181e1b4a4f862077d9cefa120613c"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "36223106c073f07d8cc3ecf8eaab37192ebb2687daba65c5c21047d0b7890de0"
|
||||
},
|
||||
"view-touch": {
|
||||
"file": "02-view-touch.png",
|
||||
@@ -26,8 +26,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "f234e55b02d2558c58935b3bc6340615fb343d3cf92b7d3a98303b1a2f4f346d"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "a4aa3af48578ee41594a9b1a6e98b4d39f3324a949a9fd9e63d5854576d5db85"
|
||||
},
|
||||
"space-create": {
|
||||
"file": "03-space-create.png",
|
||||
@@ -37,8 +37,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "a86d4211af24923d5048e9094d0d35f9b28129394d00cd72582a8f8c2dd7ff75"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "0dc024726327f60f4a9ceaf3044381691f81f1090af81d1812e870f22d9343ba"
|
||||
},
|
||||
"room-contour-close": {
|
||||
"file": "04-room-contour-close.png",
|
||||
@@ -48,8 +48,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "c7effd4ccd19bcaff9138458583368aaab488dcb6fc7ea5ab766383cb5377ac4"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "44cfaa95bd51e2cd628400e28db0ad8b2f0cd904385845bd402494f3f5c0d93c"
|
||||
},
|
||||
"plan-context-tray": {
|
||||
"file": "05-plan-context-tray.png",
|
||||
@@ -59,8 +59,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "af1194f073f00b03af02b949a072cffb21bfd29995aa65a067eaaaec74ef8490"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "e0662429b423fb74151b583dcc2c8635b001b637d03bbf7a6b16aec46399c3f8"
|
||||
},
|
||||
"device-editor": {
|
||||
"file": "06-device-editor.png",
|
||||
@@ -70,8 +70,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "f7fb7495c82c89d49acfca61738b63d9a97da057e37c0e6549c4866fcf677d89"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "d0ffd31ce80bfde21ab75da356a5fc1af38246f2b301030880320620c228d89d"
|
||||
},
|
||||
"device-display-preview": {
|
||||
"file": "06-device-display-preview.png",
|
||||
@@ -81,8 +81,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "7151c96dc048381f7a7070ecb85a82a890b2f4c703628f14fa472eb8dacbf3dc"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "2cdabae1f89c3286e4fac0ce30f757ee1690b707ab8a5488748b7cd420626160"
|
||||
},
|
||||
"background-editor": {
|
||||
"file": "07-background-editor.png",
|
||||
@@ -92,8 +92,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "9f08f3f3711b100d68fb1a05af269e43e47d8f8d12c0cbe4f9ab557138722989"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "30147bb00a90eea7136b4cee30995f6e6a9217b5132f3e8d3ad7471413b1af8a"
|
||||
},
|
||||
"room-card": {
|
||||
"file": "08-room-card.png",
|
||||
@@ -103,8 +103,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "66d8b0a509909822ff6b891a483bad9d2c72b20f08e287355883aa5205c6cee0"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
|
||||
},
|
||||
"device-info": {
|
||||
"file": "09-device-info.png",
|
||||
@@ -114,8 +114,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "4293c7df2738c5efb43d8278a9f3cfe2a6406b50fd90980bc9729c1eedfe9e76",
|
||||
"imageSha256": "f4d3bda00a2fce54f408b1f20e03b5c590ba4b168fd214a28aafb1692b1f3124"
|
||||
"sourceSha256": "19a7251bbfeac721a2d2e08aa441e00c2bd751924b56cd5fa3d223b4b023b745",
|
||||
"imageSha256": "dd492f53150b7149085daada5cce9eeae9bde9e7ea1d86679a54b3041f72f517"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,10 +231,10 @@ npm run bundle:budget
|
||||
node scripts/check-docs.mjs
|
||||
node scripts/smoke-select.mjs --base origin/dev --head HEAD
|
||||
node demo/smoke_wireless_controller_parity.mjs
|
||||
node scripts/mutation-gate.mjs --only <new-empty-roster-mutant>
|
||||
node scripts/mutation-gate.mjs --only controller-availability-follows-target
|
||||
node scripts/mutation-gate.mjs --only wireless-controller-loses-filtered-target-role
|
||||
node scripts/mutation-gate.mjs --only wireless-controller-preview-drops-sibling-markers
|
||||
node scripts/mutation-gate.mjs --id=<new-empty-roster-mutant>
|
||||
node scripts/mutation-gate.mjs --id=controller-availability-follows-target
|
||||
node scripts/mutation-gate.mjs --id=wireless-controller-loses-filtered-target-role
|
||||
node scripts/mutation-gate.mjs --id=wireless-controller-preview-drops-sibling-markers
|
||||
```
|
||||
|
||||
Если `smoke-select` выберет дополнительные smokes, они также обязательны.
|
||||
|
||||
@@ -2316,11 +2316,24 @@ const MUTANT_DEFINITIONS = [
|
||||
+ 'controllers; excluding those siblings restores the field defect (#251)',
|
||||
patches: [{
|
||||
file: 'src/device-presentation.ts',
|
||||
find: ' const live = (d.entities || []).some((eid) => {',
|
||||
replace: " const live = (d.entities || []).filter((eid) => !eid.startsWith('sensor.') "
|
||||
find: ' const live = ownEntities.some((eid) => {',
|
||||
replace: " const live = ownEntities.filter((eid) => !eid.startsWith('sensor.') "
|
||||
+ "&& !eid.startsWith('update.')).some((eid) => {",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'entityless-active-controller-stays-available',
|
||||
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
|
||||
+ '&& node --test --test-name-pattern="issue 318 keeps an active entityless physical controller available" '
|
||||
+ 'test/device-presentation.test.mjs',
|
||||
because: 'an active physical controller whose HA device exposes no own entities has no offline '
|
||||
+ 'evidence; its face must remain available while controls decide working versus neutral (#318)',
|
||||
patches: [{
|
||||
file: 'src/device-presentation.ts',
|
||||
find: " if (activeEntitylessDevice) return 'available';",
|
||||
replace: " if (activeEntitylessDevice) return 'unavailable';",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'wireless-controller-loses-filtered-target-role',
|
||||
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
|
||||
|
||||
@@ -202,14 +202,21 @@ function sourceStateText(hass: any, eid: string): string {
|
||||
*
|
||||
* HA exposes entity states rather than one device state. Controls are not
|
||||
* controller evidence: a live battery/LQI/update sibling is, while an
|
||||
* event-only `unknown` entity is not. A virtual controller has no physical HA
|
||||
* binding which can go offline, so it is available by definition.
|
||||
* event-only `unknown` entity is not. An active physical device with no entity
|
||||
* roster has no evidence that it is offline, so it remains available while
|
||||
* its target status is resolved independently. A virtual controller has no
|
||||
* physical HA binding which can go offline, so it is available by definition.
|
||||
*/
|
||||
export function controllerAvailability(hass: any, d: DevItem): DeviceAvailability {
|
||||
if (d.virtual || d.bindingKind === 'virtual' || d.marker?.binding === 'virtual') {
|
||||
return 'available';
|
||||
}
|
||||
const live = (d.entities || []).some((eid) => {
|
||||
const ownEntities = d.entities || [];
|
||||
const activeEntitylessDevice = ownEntities.length === 0
|
||||
&& (d.bindingKind === 'device' || d.marker?.binding?.startsWith('device:'))
|
||||
&& d.bindingStatus?.kind === 'active';
|
||||
if (activeEntitylessDevice) return 'available';
|
||||
const live = ownEntities.some((eid) => {
|
||||
const state = String(hass?.states?.[eid]?.state ?? '').trim().toLowerCase();
|
||||
return state !== '' && state !== 'unknown' && state !== 'unavailable';
|
||||
});
|
||||
|
||||
@@ -208,6 +208,14 @@ const rowRunners = {
|
||||
assert.equal(r.sourceKind, 'none');
|
||||
assert.deepEqual(r.visual, neutral);
|
||||
}),
|
||||
S15: presentationRow({
|
||||
entities: [], primary: null,
|
||||
bindingStatus: { kind: 'active', enabledEntityIds: [], allEntityIds: [] },
|
||||
marker: { id: 'd1', binding: 'device:d1', controls: ['light.main'] },
|
||||
}, {}, (r) => {
|
||||
assert.equal(r.sourceKind, 'controls');
|
||||
assert.deepEqual([r.visual.availability, r.visual.status], ['available', 'working']);
|
||||
}),
|
||||
F01: policyRow({
|
||||
sourceVisual: { availability: 'available', status: 'alarm', activity: 'none' },
|
||||
}, (r) => assert.equal(r.visual.status, 'alarm')),
|
||||
|
||||
@@ -352,6 +352,105 @@ test('issue 251 separates controller availability from controlled target status'
|
||||
assert.ok(alarm.classes.includes('alarm'));
|
||||
});
|
||||
|
||||
test('issue 318 keeps an active entityless physical controller available', () => {
|
||||
const h = hass({
|
||||
'light.wall_group': state('light.wall_group', 'on', { friendly_name: 'Wall lights' }),
|
||||
}, {
|
||||
'light.wall_group': {
|
||||
entity_id: 'light.wall_group', device_id: 'lights', platform: 'demo',
|
||||
},
|
||||
});
|
||||
const controller = device({
|
||||
id: 'entityless-wall', name: 'Entityless wall switch', entities: [], primary: null,
|
||||
bindingRef: 'entityless-wall',
|
||||
bindingStatus: { kind: 'active', enabledEntityIds: [], allEntityIds: [] },
|
||||
marker: {
|
||||
id: 'entityless-wall', binding: 'device:entityless-wall', tap_action: 'toggle',
|
||||
controls: ['light.wall_group'],
|
||||
},
|
||||
});
|
||||
|
||||
let result = resolveDevicePresentation(h, controller, options);
|
||||
assert.equal(result.sourceKind, 'controls');
|
||||
assert.deepEqual(result.visual, {
|
||||
availability: 'available', status: 'working', activity: 'running',
|
||||
});
|
||||
assert.ok(result.classes.includes('on'));
|
||||
assert.ok(!result.classes.includes('unavail'));
|
||||
assert.equal(deviceA11yState(result), 'working');
|
||||
|
||||
h.states['light.wall_group'].state = 'off';
|
||||
result = resolveDevicePresentation(h, controller, options);
|
||||
assert.deepEqual(result.visual, {
|
||||
availability: 'available', status: 'neutral', activity: 'none',
|
||||
});
|
||||
assert.ok(!result.classes.includes('on'));
|
||||
assert.ok(!result.classes.includes('unavail'));
|
||||
assert.equal(deviceA11yState(result), 'neutral');
|
||||
|
||||
h.states['light.wall_group'].state = 'unavailable';
|
||||
result = resolveDevicePresentation(h, controller, options);
|
||||
assert.deepEqual(result.visual, {
|
||||
availability: 'available', status: 'neutral', activity: 'none',
|
||||
});
|
||||
|
||||
delete h.states['light.wall_group'];
|
||||
result = resolveDevicePresentation(h, controller, options);
|
||||
assert.equal(result.visual.availability, 'available');
|
||||
assert.equal(result.visual.status, 'neutral');
|
||||
|
||||
const runtimeFiltered = resolveDevicePresentation(h, {
|
||||
...controller,
|
||||
controls: [],
|
||||
}, options);
|
||||
assert.equal(runtimeFiltered.sourceKind, 'none');
|
||||
assert.deepEqual(runtimeFiltered.visual, {
|
||||
availability: 'available', status: 'neutral', activity: 'none',
|
||||
});
|
||||
|
||||
h.states['light.wall_group'] = state('light.wall_group', 'on');
|
||||
const provenOffline = resolveDevicePresentation(h, {
|
||||
...controller,
|
||||
entities: ['event.entityless_wall_action'],
|
||||
primary: 'event.entityless_wall_action',
|
||||
bindingStatus: {
|
||||
kind: 'active',
|
||||
enabledEntityIds: ['event.entityless_wall_action'],
|
||||
allEntityIds: ['event.entityless_wall_action'],
|
||||
},
|
||||
}, options);
|
||||
assert.equal(provenOffline.visual.availability, 'unavailable');
|
||||
assert.equal(provenOffline.visual.status, 'working');
|
||||
assert.ok(provenOffline.classes.includes('unavail'));
|
||||
assert.ok(!provenOffline.classes.includes('on'));
|
||||
assert.equal(deviceA11yState(provenOffline), 'unavailable');
|
||||
|
||||
const entityBinding = resolveDevicePresentation(h, {
|
||||
...controller,
|
||||
bindingKind: 'entity', bindingRef: 'switch.entityless_wall',
|
||||
marker: {
|
||||
...controller.marker,
|
||||
binding: 'entity:switch.entityless_wall',
|
||||
},
|
||||
}, options);
|
||||
assert.equal(entityBinding.visual.availability, 'unavailable');
|
||||
|
||||
const orphaned = resolveDevicePresentation(h, {
|
||||
...controller,
|
||||
bindingStatus: { kind: 'orphaned', reason: 'device_missing' },
|
||||
}, options);
|
||||
assert.equal(orphaned.visual.availability, 'unavailable');
|
||||
|
||||
const virtual = resolveDevicePresentation(h, {
|
||||
...controller,
|
||||
virtual: true,
|
||||
bindingKind: 'virtual', bindingRef: '',
|
||||
marker: { ...controller.marker, binding: 'virtual' },
|
||||
}, options);
|
||||
assert.equal(virtual.visual.availability, 'available');
|
||||
assert.equal(virtual.visual.status, 'working');
|
||||
});
|
||||
|
||||
test('issue 274 keeps a wireless controller available and gives its draft the full marker roster', () => {
|
||||
const own = [
|
||||
'event.wall_action', 'sensor.wall_battery', 'sensor.wall_linkquality', 'update.wall',
|
||||
|
||||
@@ -25,6 +25,7 @@ const ROWS = [
|
||||
['S12', 'source.none', 'presentation-source-decision-trace'],
|
||||
['S13', 'source.critical_sibling', 'device-presentation-policy-alarm'],
|
||||
['S14', 'source.skipped_static_fast_path', 'presentation-static-source-fast-path'],
|
||||
['S15', 'availability.controller_available', 'entityless-active-controller-stays-available'],
|
||||
['F01', 'status.alarm', 'device-presentation-policy-alarm'],
|
||||
['F02', 'status.unavailable', 'device-presentation-policy-unavailable'],
|
||||
['F03', 'source.device_role', 'presentation-source-decision-trace'],
|
||||
|
||||