diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 973e58ed..abff64ff 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31249bbb6bcbe46a0148d8eb488eb1";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,O=/>/g,N=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=B(1),U=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=z;for(let e=0;e"===l[0]?(r=n??z,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?N:'"'===l[3]?L:H):r===L||r===H?r=N:r===F||r===O?r=z:(r=N,n=void 0);const d=r===N&&t[e+1].startsWith("/>")?" ":"";o+=r===z?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=rt,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,r=o!==t;if(r){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||r){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let r=0;for(const e of t)n[r]=s?s(e,r):r,o[r]=i(e,r),r++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:r}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=r,o;const a=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(a[d]===r[p])l[p]=vt(n[d],o[p]),d++,p++;else if(a[u]===r[_])l[_]=vt(n[u],o[_]),u--,_--;else if(a[d]===r[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(a[u]===r[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(r,p,_),h=kt(a,d,u)),c.has(a[d]))if(c.has(a[u])){const e=h.get(r[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=r,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6535c0f4f884d1ef5f5b2969c8ddf8ece6ca4247b993fd8563f8c34e6c30aaf3";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,O=/>/g,N=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=B(1),U=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=z;for(let e=0;e"===l[0]?(r=n??z,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?N:'"'===l[3]?L:H):r===L||r===H?r=N:r===F||r===O?r=z:(r=N,n=void 0);const d=r===N&&t[e+1].startsWith("/>")?" ":"";o+=r===z?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=rt,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,r=o!==t;if(r){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||r){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let r=0;for(const e of t)n[r]=s?s(e,r):r,o[r]=i(e,r),r++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:r}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=r,o;const a=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(a[d]===r[p])l[p]=vt(n[d],o[p]),d++,p++;else if(a[u]===r[_])l[_]=vt(n[u],o[_]),u--,_--;else if(a[d]===r[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(a[u]===r[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(r,p,_),h=kt(a,d,u)),c.has(a[d]))if(c.has(a[u])){const e=h.get(r[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=r,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W` ${this.icon?W``:G} ${this.title} `;return this._useHaDialog?W` - `:W``}_openingAt(t){if(!this._openingsR.length)return null;const e=this._openingsR.flatMap(e=>{const i=e.angle*Math.PI/180,s=t[0]-e.rx,n=t[1]-e.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>e.rlen/2+12?[]:[{o:e,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!e.length)return null;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return e.find(({o:t,localY:e})=>{const i="gate"===t.type?!t.flip_v:t.flip_v,n=this._spaceWalls.length||"gate"===t.type?fa(s,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:i}):{ox:0,oy:0,cm:0,side:-1},o=od({type:t.type,length:t.rlen,angle:t.angle,amount:this._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:n});return Math.abs(e)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this._openingPreset;if("opening"!==this._tool||!e)return null;const i=this._spaceModel(),s=this._openCuts(),n=this._openingWallIndexFor(i,s);this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===n.key||(this._openingPlacementIntervalsCache={key:n.key,value:sa(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)});const o=ud({pointer:[t[0],t[1]],preset:e,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(e.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender(this._boundaryCoarse?10:6),gridStep:this._gridPitch});if(!o)return null;const r="gate"===o.type?!o.flipV:o.flipV,a=fa(n.value,{x:o.x,y:o.y,angle:o.angle,length:o.renderedLength,flip_v:r}),l="mi"===this.hass?.config?.unit_system?.length,c=ni([o.x,o.y],o.angle,o.renderedLength,i.rooms,this._gridPitch/2),h=(c?[{distance:c.sideA,midpoint:c.midA},{distance:c.sideB,midpoint:c.midB}]:o.measure.labels).map(t=>({x:t.midpoint[0],y:t.midpoint[1],text:Je(t.distance/this._gridPitch*this._cellCm,l)}));return{...o,face:a,measure:{labels:h,guide:o.measure.guide}}}_activateOpeningPlacement(t){this._cancelPath(),this._openingPreset=function(t,e){return{type:t,lengthCm:ld(t),flipH:!1,flipV:!1,revision:e}}(t,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null,this._tool="opening"}_clearOpeningPlacement(t){this._openingHoverCandidate=null,t&&(this._openingPreset=null)}_openingClick(t){const e=this._openingAt(t);if(e)return void this._editOpening(e);const i=this._openingPreset;if(!i)return;const s=this._openingHoverCandidate,n=s&&pd(s,[t[0],t[1]],i.revision,this._cfgEpoch)?s:this._resolveOpeningPlacement(t);if(!n){const e=1.5*this._gridPitch,i=si(t,this._spaceModel().rooms,e);return i&&function(t,e,i,s,n){for(const o of s)if(!(Cs([t,e],o)>n)&&qr([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(i.x,i.y,i.angle,this._openCuts(),e)?void this._showToast(this._t("toast.opening_on_virtual")):void this._showToast(this._t("toast.opening_no_wall"))}this._openingDialog={type:n.type,lengthCm:n.lengthCm,contact:"",lock:"",invert:!1,flipH:n.flipH,flipV:n.flipV,x:n.x,y:n.y,angle:n.angle},this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Hd(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=si(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ad);this._opMeasure=o.measure;const r=o.x/Ad,a=o.y/this._spaceH,l=n.x!==r||n.y!==a||n.angle!==i.angle;l&&(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let n=t.x,o=t.y,r=ni([n,o],t.angle,e,i,s);if(r&&r.centered&&(n!==r.wallCenter[0]||o!==r.wallCenter[1]))[n,o]=r.wallCenter,r=ni([n,o],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,u=h-l,p=Math.hypot(d,u);if(p>0){const c=this._gridPitch,h=Math.min(e/2,p/2);let _=Math.round(((n-a)*d+(o-l)*u)/p/c)*c;_=Math.max(h,Math.min(p-h,_)),n=a+_/p*d,o=l+_/p*u,r=ni([n,o],t.angle,e,i,s)||r}}if(!r)return{x:n,y:o,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Je(t/this._gridPitch*this._cellCm,a)});return{x:n,y:o,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ad,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ad,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const o=e.openings.findIndex(t=>t.id===n.id);o>=0?e.openings[o]=n:e.openings.push(n),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._planEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._planEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?ti(n):null,r=ti(i),a=o&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Ve(bi(t),bi(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===n);r?(r.poly=t.poly.map(t=>[t[0]/Ad,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?ti(i):null;if(!i||!s)return void(this._splitSel=null);const n=.02*this._gridPitch,o=6*this._gridPitch,r=function(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=o?a:null,c=!!l&&ci(l,s,n),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return hi(e,s,n)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(ri(s,n,i))return null;const o=wi(t,s,i),r=wi(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!hi(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),ki(r,i)};let c,h;if(o===r){const r=ki([...e],i);if(r.length<3||yi(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ki(a,i),h=r}else c=ki([...l(s,o,n,r),...[...a].reverse()],i),h=ki([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(t))>Math.max(i,1e-6*yi(t))?null:[c,h]}(s,[...h,l],n);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,p]=d,_=yi(u)>=yi(p)?u:p,m=_===u?p:u;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(n,t.shiftKey):this._snap(n)}get _openingPreview(){const t=this._openingPreset;if("opening"!==this._tool||!t||!this._cursorPt)return null;const e=this._cursorPt,i=this._openingHoverCandidate;if(i&&pd(i,[e[0],e[1]],t.revision,this._cfgEpoch))return i;if(this._openingAt(e))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(e);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit,n=s?function(t,e,i,s,n,o,r=1){let a=[];const l=sa(t,e,i,s,n,o,r);for(const t of l)!t.open&&t.cm>0&&(a=Wr(a,t.a,t.b,t.cm,s,r));return a}(this._spaceModel().rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Ad):null,o=s&&Array.isArray(t.walls)?t.walls:null;let r;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ad,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const a=this._areaSel?this.hass.areas[this._areaSel]?.name:"",l={id:"r"+Date.now().toString(36),name:this._nameSel||a||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(t=>[t[0]/Ad,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(l),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s){this._commitOpenSpans();const e=this._normalizeWalls(n,this._openCuts());e.length?t.walls=e:o?.length?t.walls=o:delete t.walls}if(!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||xr],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=ta(e,i,o,n,r,t);if(!a)return t?t.slice():[];const l=ea(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=Wr(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,l.id,i,this._wallKeyPitch,s,Ad);for(const t of sa(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)){if(t.roomId!==l.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],o=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return Cs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});o>=0&&(n=Wr(n,t.a,t.b,e[o]||i,this._wallKeyPitch,Ad))}n=this._normalizeWalls(n,s),n.length?t.walls=n:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const c=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let h=0;if(c){const t=Ad,e={...this._layout};for(const i of this._devices){if(i.area!==c||i.space!==this._space)continue;if(h++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ad,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const d=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(c?this._t("toast.room_saved",{n:d,added:h}):this._t("toast.room_saved_no_area",{n:d}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),a=hs(e?.glow_color),l=this._devicePresentation(t,!0).valueBadge,c=co(this._planHass,t,this._devices),h=ho(this._planHass,t,c);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ti(t.marker?.display),rippleColor:Dt(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Hc(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:kn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:e?.light_entity,glowMode:null!=a?.bri?"fixed":a?"color":"auto",glowColor:a?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(a?.bri??1))),glowColorDrafted:!!a,glowBrightnessDrafted:null!=a?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!a||null===e?.glow_color),originalGlowColor:a||(null===e?.glow_color?null:void 0),valueBadgeEnabled:o?!0===e?.value_badge?.enabled:!!l,valueBadgeSource:e?.value_badge?.source||l?.source||h,valueBadgePosition:e?.value_badge?.position||l?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:e?.value_badge,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ii)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=Bn(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(r+"|"+(e.area_id||""))||o.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,a]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(Wn(t,n,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:a.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||n.has(l)||a.hidden&&!i.has(l))continue;if(Wn(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,u=d&&(d.name_by_user||d.name)||"";o.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>Da(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Ma(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),n=(t.wall_columns||[]).map(t=>Ca(t,this._cellCm,this._gridPitch)),o=[...i,...s,...n];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:n,all:o},o}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=wd(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...o),Math.min(...r),Math.max(...o),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?Aa(e,l):null,h={floor:e,geom:c,path:c?Sa(c):"",area:Ea(c||[[[...e,e[0]]]])};return this._rszPreview||kd(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":qn(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":qn(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,i=this._t(t);if(i!==t)return i}return this._errText(t)}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:Dt(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}async _saveMarker(){const t=this._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding)if("virtual"!==t.binding||t.name.trim())if("run"!==e||t.tapTarget)if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)this._showToast(this._t("toast.value_badge_source_required"));else{if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const i=this._serverCfg;let s;i.markers=i.markers||[];const n=Ps(t.room);let o=n?.space||null,r=n?.area||null;const a=n?.roomId||null;"virtual"!==t.binding||o||(o=this._space),s=$i(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const l=t.devId,c="virtual"===t.binding?[]:i.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=c.length>0,d=kn(t.binding,t.controls,this._bindingEntities(t.binding)),u=i.markers.find(t=>t.id===s||t.id===l)?.vacuum||null,p={id:s,vacuum:u,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:d.length?d:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=bd(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=o,p.area=r,p.room_id=a);const _=l?this._devices.find(t=>t.id===l):null,m=_?.marker?.room_id??null,g=!!t.room&&null!=_&&(_.space!==o||_.area!==r||m!==a);let f=!1;const v=t.uploadId||l;if(v&&v!==s&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:v,to_id:s}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(p.pdfs,v,s,e),f=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}l&&l!==s&&(i.markers=function(t,e,i){if(!e||!i||e===i)return[...t];const s=`marker:${e}`,n=`marker:${i}`;return t.map(t=>{const e=Array.isArray(t.controls)?t.controls.map(t=>t===s?n:t):t.controls,i="derived_marker_state"===t.value_badge?.source?.kind&&t.value_badge.source.ref===s?{...t.value_badge,source:{...t.value_badge.source,ref:n}}:t.value_badge;return e===t.controls&&i===t.value_badge?t:{...t,controls:e,value_badge:i}})}(i.markers,l,s)),l&&l!==s&&"derived_marker_state"===p.value_badge?.source?.kind&&p.value_badge.source.ref===`marker:${l}`&&(p.value_badge.source={kind:"derived_marker_state",ref:`marker:${s}`}),i.markers=i.markers.filter(t=>t.id!==s&&t.id!==l&&("virtual"===p.binding||t.binding!==p.binding)),i.markers.push(p);let y=null;const b=o||_?.space||this._space,w=l?this._layout[l]:null,k=w?{s:w.s||_?.space||this._space,x:w.x,y:w.y}:l&&_&&this._defPos[l]?this._normPos(_.space,this._defPos[l].x,this._defPos[l].y):null;if(!h&&k&&k.s===b)s===l&&this._layout[s]&&!g||(y={s:k.s,x:k.x,y:k.y},this._layout={...this._layout,[s]:y});else if(h||!this._layout[s]||g){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,i=t.vb[1]+t.vb[3]/2;const n=a?t.rooms.find(t=>t.id===a):r?t.rooms.find(t=>t.area===r):void 0;n&&([e,i]=this._roomCenter(n)),y=this._normPos(o||this._space,e,i),this._layout={...this._layout,[s]:y}}await this._saveConfigNow(),y&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:s,pos:y}));const $=new Set(c);l&&l!==s&&$.add(l),$.delete(s);for(const t of $)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});f&&v&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:v}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const r=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);var a,l;s.markers=(a=r.markers,l=r.cleanupIds,a.map(t=>{if(!Array.isArray(t.controls))return t;const e=t.controls.filter(t=>!("string"==typeof t&&t.startsWith("marker:")&&l.has(t.slice(7))));return e.length===t.controls.length?t:{...t,controls:e.length?e:null}}));const c=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of c){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ad,y:i/Ad}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Zi(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Yi(i.settings.custom_fill):null,o="none"===s.fill?{...n||Ki,a:0}:n;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:lr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:Gi,roomOpacity:Vi,fillMode:"custom",customFill:{...Ki,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingAt(t){if(!this._openingsR.length)return null;const e=this._openingsR.flatMap(e=>{const i=e.angle*Math.PI/180,s=t[0]-e.rx,n=t[1]-e.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>e.rlen/2+12?[]:[{o:e,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!e.length)return null;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return e.find(({o:t,localY:e})=>{const i="gate"===t.type?!t.flip_v:t.flip_v,n=this._spaceWalls.length||"gate"===t.type?fa(s,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:i}):{ox:0,oy:0,cm:0,side:-1},o=od({type:t.type,length:t.rlen,angle:t.angle,amount:this._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:n});return Math.abs(e)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this._openingPreset;if("opening"!==this._tool||!e)return null;const i=this._spaceModel(),s=this._openCuts(),n=this._openingWallIndexFor(i,s);this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===n.key||(this._openingPlacementIntervalsCache={key:n.key,value:sa(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)});const o=ud({pointer:[t[0],t[1]],preset:e,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(e.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender(this._boundaryCoarse?10:6),gridStep:this._gridPitch});if(!o)return null;const r="gate"===o.type?!o.flipV:o.flipV,a=fa(n.value,{x:o.x,y:o.y,angle:o.angle,length:o.renderedLength,flip_v:r}),l="mi"===this.hass?.config?.unit_system?.length,c=ni([o.x,o.y],o.angle,o.renderedLength,i.rooms,this._gridPitch/2),h=(c?[{distance:c.sideA,midpoint:c.midA},{distance:c.sideB,midpoint:c.midB}]:o.measure.labels).map(t=>({x:t.midpoint[0],y:t.midpoint[1],text:Je(t.distance/this._gridPitch*this._cellCm,l)}));return{...o,face:a,measure:{labels:h,guide:o.measure.guide}}}_activateOpeningPlacement(t){this._cancelPath(),this._openingPreset=function(t,e){return{type:t,lengthCm:ld(t),flipH:!1,flipV:!1,revision:e}}(t,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null,this._tool="opening"}_clearOpeningPlacement(t){this._openingHoverCandidate=null,t&&(this._openingPreset=null)}_openingClick(t){const e=this._openingAt(t);if(e)return void this._editOpening(e);const i=this._openingPreset;if(!i)return;const s=this._openingHoverCandidate,n=s&&pd(s,[t[0],t[1]],i.revision,this._cfgEpoch)?s:this._resolveOpeningPlacement(t);if(!n){const e=1.5*this._gridPitch,i=si(t,this._spaceModel().rooms,e);return i&&function(t,e,i,s,n){for(const o of s)if(!(Cs([t,e],o)>n)&&qr([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(i.x,i.y,i.angle,this._openCuts(),e)?void this._showToast(this._t("toast.opening_on_virtual")):void this._showToast(this._t("toast.opening_no_wall"))}this._openingDialog={type:n.type,lengthCm:n.lengthCm,contact:"",lock:"",invert:!1,flipH:n.flipH,flipV:n.flipV,x:n.x,y:n.y,angle:n.angle},this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Hd(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=si(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ad);this._opMeasure=o.measure;const r=o.x/Ad,a=o.y/this._spaceH,l=n.x!==r||n.y!==a||n.angle!==i.angle;l&&(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let n=t.x,o=t.y,r=ni([n,o],t.angle,e,i,s);if(r&&r.centered&&(n!==r.wallCenter[0]||o!==r.wallCenter[1]))[n,o]=r.wallCenter,r=ni([n,o],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,u=h-l,p=Math.hypot(d,u);if(p>0){const c=this._gridPitch,h=Math.min(e/2,p/2);let _=Math.round(((n-a)*d+(o-l)*u)/p/c)*c;_=Math.max(h,Math.min(p-h,_)),n=a+_/p*d,o=l+_/p*u,r=ni([n,o],t.angle,e,i,s)||r}}if(!r)return{x:n,y:o,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Je(t/this._gridPitch*this._cellCm,a)});return{x:n,y:o,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ad,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ad,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const o=e.openings.findIndex(t=>t.id===n.id);o>=0?e.openings[o]=n:e.openings.push(n),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._openingEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?ti(n):null,r=ti(i),a=o&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Ve(bi(t),bi(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===n);r?(r.poly=t.poly.map(t=>[t[0]/Ad,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?ti(i):null;if(!i||!s)return void(this._splitSel=null);const n=.02*this._gridPitch,o=6*this._gridPitch,r=function(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=o?a:null,c=!!l&&ci(l,s,n),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return hi(e,s,n)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(ri(s,n,i))return null;const o=wi(t,s,i),r=wi(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!hi(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),ki(r,i)};let c,h;if(o===r){const r=ki([...e],i);if(r.length<3||yi(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ki(a,i),h=r}else c=ki([...l(s,o,n,r),...[...a].reverse()],i),h=ki([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(t))>Math.max(i,1e-6*yi(t))?null:[c,h]}(s,[...h,l],n);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,p]=d,_=yi(u)>=yi(p)?u:p,m=_===u?p:u;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(n,t.shiftKey):this._snap(n)}get _openingPreview(){const t=this._openingPreset;if("opening"!==this._tool||!t||!this._cursorPt)return null;const e=this._cursorPt,i=this._openingHoverCandidate;if(i&&pd(i,[e[0],e[1]],t.revision,this._cfgEpoch))return i;if(this._openingAt(e))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(e);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit,n=s?function(t,e,i,s,n,o,r=1){let a=[];const l=sa(t,e,i,s,n,o,r);for(const t of l)!t.open&&t.cm>0&&(a=Wr(a,t.a,t.b,t.cm,s,r));return a}(this._spaceModel().rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Ad):null,o=s&&Array.isArray(t.walls)?t.walls:null;let r;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ad,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const a=this._areaSel?this.hass.areas[this._areaSel]?.name:"",l={id:"r"+Date.now().toString(36),name:this._nameSel||a||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(t=>[t[0]/Ad,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(l),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s){this._commitOpenSpans();const e=this._normalizeWalls(n,this._openCuts());e.length?t.walls=e:o?.length?t.walls=o:delete t.walls}if(!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||xr],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=ta(e,i,o,n,r,t);if(!a)return t?t.slice():[];const l=ea(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=Wr(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,l.id,i,this._wallKeyPitch,s,Ad);for(const t of sa(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)){if(t.roomId!==l.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],o=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return Cs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});o>=0&&(n=Wr(n,t.a,t.b,e[o]||i,this._wallKeyPitch,Ad))}n=this._normalizeWalls(n,s),n.length?t.walls=n:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const c=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let h=0;if(c){const t=Ad,e={...this._layout};for(const i of this._devices){if(i.area!==c||i.space!==this._space)continue;if(h++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ad,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const d=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(c?this._t("toast.room_saved",{n:d,added:h}):this._t("toast.room_saved_no_area",{n:d}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),a=hs(e?.glow_color),l=this._devicePresentation(t,!0).valueBadge,c=co(this._planHass,t,this._devices),h=ho(this._planHass,t,c);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ti(t.marker?.display),rippleColor:Dt(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Hc(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:kn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:e?.light_entity,glowMode:null!=a?.bri?"fixed":a?"color":"auto",glowColor:a?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(a?.bri??1))),glowColorDrafted:!!a,glowBrightnessDrafted:null!=a?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!a||null===e?.glow_color),originalGlowColor:a||(null===e?.glow_color?null:void 0),valueBadgeEnabled:o?!0===e?.value_badge?.enabled:!!l,valueBadgeSource:e?.value_badge?.source||l?.source||h,valueBadgePosition:e?.value_badge?.position||l?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:e?.value_badge,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ii)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=Bn(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(r+"|"+(e.area_id||""))||o.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,a]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(Wn(t,n,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:a.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||n.has(l)||a.hidden&&!i.has(l))continue;if(Wn(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,u=d&&(d.name_by_user||d.name)||"";o.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>Da(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Ma(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),n=(t.wall_columns||[]).map(t=>Ca(t,this._cellCm,this._gridPitch)),o=[...i,...s,...n];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:n,all:o},o}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=wd(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...o),Math.min(...r),Math.max(...o),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?Aa(e,l):null,h={floor:e,geom:c,path:c?Sa(c):"",area:Ea(c||[[[...e,e[0]]]])};return this._rszPreview||kd(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":qn(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":qn(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,i=this._t(t);if(i!==t)return i}return this._errText(t)}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:Dt(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}async _saveMarker(){const t=this._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding)if("virtual"!==t.binding||t.name.trim())if("run"!==e||t.tapTarget)if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)this._showToast(this._t("toast.value_badge_source_required"));else{if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const i=this._serverCfg;let s;i.markers=i.markers||[];const n=Ps(t.room);let o=n?.space||null,r=n?.area||null;const a=n?.roomId||null;"virtual"!==t.binding||o||(o=this._space),s=$i(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const l=t.devId,c="virtual"===t.binding?[]:i.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=c.length>0,d=kn(t.binding,t.controls,this._bindingEntities(t.binding)),u=i.markers.find(t=>t.id===s||t.id===l)?.vacuum||null,p={id:s,vacuum:u,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:d.length?d:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=bd(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=o,p.area=r,p.room_id=a);const _=l?this._devices.find(t=>t.id===l):null,m=_?.marker?.room_id??null,g=!!t.room&&null!=_&&(_.space!==o||_.area!==r||m!==a);let f=!1;const v=t.uploadId||l;if(v&&v!==s&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:v,to_id:s}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(p.pdfs,v,s,e),f=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}l&&l!==s&&(i.markers=function(t,e,i){if(!e||!i||e===i)return[...t];const s=`marker:${e}`,n=`marker:${i}`;return t.map(t=>{const e=Array.isArray(t.controls)?t.controls.map(t=>t===s?n:t):t.controls,i="derived_marker_state"===t.value_badge?.source?.kind&&t.value_badge.source.ref===s?{...t.value_badge,source:{...t.value_badge.source,ref:n}}:t.value_badge;return e===t.controls&&i===t.value_badge?t:{...t,controls:e,value_badge:i}})}(i.markers,l,s)),l&&l!==s&&"derived_marker_state"===p.value_badge?.source?.kind&&p.value_badge.source.ref===`marker:${l}`&&(p.value_badge.source={kind:"derived_marker_state",ref:`marker:${s}`}),i.markers=i.markers.filter(t=>t.id!==s&&t.id!==l&&("virtual"===p.binding||t.binding!==p.binding)),i.markers.push(p);let y=null;const b=o||_?.space||this._space,w=l?this._layout[l]:null,k=w?{s:w.s||_?.space||this._space,x:w.x,y:w.y}:l&&_&&this._defPos[l]?this._normPos(_.space,this._defPos[l].x,this._defPos[l].y):null;if(!h&&k&&k.s===b)s===l&&this._layout[s]&&!g||(y={s:k.s,x:k.x,y:k.y},this._layout={...this._layout,[s]:y});else if(h||!this._layout[s]||g){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,i=t.vb[1]+t.vb[3]/2;const n=a?t.rooms.find(t=>t.id===a):r?t.rooms.find(t=>t.area===r):void 0;n&&([e,i]=this._roomCenter(n)),y=this._normPos(o||this._space,e,i),this._layout={...this._layout,[s]:y}}await this._saveConfigNow(),y&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:s,pos:y}));const $=new Set(c);l&&l!==s&&$.add(l),$.delete(s);for(const t of $)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});f&&v&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:v}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const r=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);var a,l;s.markers=(a=r.markers,l=r.cleanupIds,a.map(t=>{if(!Array.isArray(t.controls))return t;const e=t.controls.filter(t=>!("string"==typeof t&&t.startsWith("marker:")&&l.has(t.slice(7))));return e.length===t.controls.length?t:{...t,controls:e.length?e:null}}));const c=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of c){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ad,y:i/Ad}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Zi(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Yi(i.settings.custom_fill):null,o="none"===s.fill?{...n||Ki,a:0}:n;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:lr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:Gi,roomOpacity:Vi,fillMode:"custom",customFill:{...Ki,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
${e.map(e=>W`
@@ -5313,13 +5313,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31 `})} `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return U``}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._renderEntityAvailable(t.contact)?this._renderPlanHass.states[t.contact]?.state:null;return oi(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!Wn(this._fullRegistryHass,t,Bn(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderEntityAvailable(t){return!!t&&(!Wn(this._renderPlanHass,t,Bn(this._markers))&&(!!this._renderPlanHass.entities?.[t]&&!!this._renderPlanHass.states?.[t]))}_renderOpeningPlacementPreview(){const t=this._openingPreview;if(!t)return U``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:oi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:t.face};return U``}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._renderOpeningEntityAvailable(t.contact)?this._renderPlanHass.states[t.contact]?.state:null;return oi(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!Wn(this._fullRegistryHass,t,Bn(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderEntityAvailable(t){return!!t&&(!Wn(this._renderPlanHass,t,Bn(this._markers))&&(!!this._renderPlanHass.entities?.[t]&&!!this._renderPlanHass.states?.[t]))}_openingEntityAvailable(t){return function(t,e,i=on(t)){return!!e&&"active"===pn(t,`entity:${e}`,i).kind}(this.hass,t,this._haRegistry)}_renderOpeningEntityAvailable(t){return e=this._renderPlanHass,!!(i=t)&&!!e?.entities?.[i]&&!!e?.states?.[i];var e,i}_renderOpeningPlacementPreview(){const t=this._openingPreview;if(!t)return U``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:oi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:t.face};return U` `}_renderOpenings(t){const e=this._openingsR;if(!e.length)return U``;const i=t.color,s=this._spaceWalls,n=this._openPairs().flatMap(t=>t.segs),o=this._openingWallIndexFor(this._spaceModel(),n).value;return U`${e.map(t=>{const e=this._openingAmt(t),n=e>0&&!!t.contact&&this._renderEntityAvailable(t.contact)?"var(--hp-open)":i,r="gate"===t.type?!t.flip_v:t.flip_v,a=s.length||"gate"===t.type?fa(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:r}):{ox:0,oy:0,cm:0,side:-1},l={type:t.type,length:t.rlen,angle:t.angle,amount:e,flipH:!!t.flip_h,flipV:!!t.flip_v,base:i,tone:n,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=od(l);return U``}_renderOpenings(t){const e=this._openingsR;if(!e.length)return U``;const i=t.color,s=this._spaceWalls,n=this._openPairs().flatMap(t=>t.segs),o=this._openingWallIndexFor(this._spaceModel(),n).value;return U`${e.map(t=>{const e=this._openingAmt(t),n=e>0&&!!t.contact&&this._renderOpeningEntityAvailable(t.contact)?"var(--hp-open)":i,r="gate"===t.type?!t.flip_v:t.flip_v,a=s.length||"gate"===t.type?fa(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:r}):{ox:0,oy:0,cm:0,side:-1},l={type:t.type,length:t.rlen,angle:t.angle,amount:e,flipH:!!t.flip_h,flipV:!!t.flip_v,base:i,tone:n,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=od(l);return U` ${rd(l)} @@ -5329,11 +5329,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31 @pointermove=${e=>this._opPointerMove(e,t)} @pointerup=${e=>this._opPointerUp(e,t)} @pointercancel=${e=>this._opPointerUp(e,t)}> - `})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._renderEntityAvailable(t.lock));if(!e.length)return W``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return W`${e.map(e=>{const i=this._renderPlanHass.states[e.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?fa(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,u=(h-t.y)/t.h*100;return W`
`})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._renderOpeningEntityAvailable(t.lock));if(!e.length)return W``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return W`${e.map(e=>{const i=this._renderPlanHass.states[e.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?fa(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,u=(h-t.y)/t.h*100;return W`
{t.stopPropagation(),"view"===this._mode&&(this._openingInfo=e)}}> -
`})}`}_lockAction(t,e){if(this._planEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._planEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._planEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>W`
${e}${i}
`;return W``})}`}_lockAction(t,e){if(this._openingEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._openingEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._openingEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>W`
${e}${i}
`;return W`this._openingInfo=null}>
diff --git a/demo/smoke_opening_binding.mjs b/demo/smoke_opening_binding.mjs new file mode 100644 index 00000000..3ff93abb --- /dev/null +++ b/demo/smoke_opening_binding.mjs @@ -0,0 +1,140 @@ +// #104 pre-beta smoke: an opening owns exact contact/lock references +// independently of the standalone marker tombstone. This file is added during +// implementation but intentionally runs only in the pre-beta smoke gate. +import { launch, checkAll, finish } from './serve.mjs'; + +const { page, browser } = await launch(); +const out = await page.evaluate(async () => { + const c = window.__card; + const sr = () => c.shadowRoot || c.renderRoot; + const settle = async () => { + c.requestUpdate(); + await c.updateComplete; + await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))); + }; + const openingRefs = () => c._serverCfg.spaces + .find((space) => space.id === 'f1').openings + .map(({ id, contact, lock }) => ({ id, contact, lock })); + + const space = c._serverCfg.spaces.find((item) => item.id === 'f1'); + space.openings = [ + { + id: 'hp104-a', type: 'door', x: 0.22, y: 0.14, angle: 0, length: 0.09, + contact: 'binary_sensor.window', lock: 'lock.front_door', + }, + { + id: 'hp104-b', type: 'door', x: 0.40, y: 0.14, angle: 0, length: 0.09, + contact: 'binary_sensor.window', lock: 'lock.front_door', + }, + ]; + const refsBefore = JSON.stringify(openingRefs()); + c._serverCfg = { + ...c._serverCfg, + markers: [ + { id: 'window-gone', binding: 'entity:binary_sensor.window', removed: true, hidden: true }, + { id: 'lock-gone', binding: 'device:d_lock', removed: true, hidden: true }, + ], + }; + c._regSignature = ''; + c._maybeRebuildDevices(); + c.hass = { ...c.hass }; + await settle(); + + const contactValues = c._contactCandidates().map((item) => item.value); + const lockValues = c._lockCandidates().map((item) => item.value); + const openings = c._openingsR; + const result = { + entityTombstoneCandidate: contactValues.includes('binary_sensor.window'), + deviceTombstoneCandidate: lockValues.includes('lock.front_door'), + markerContributionsStaySuppressed: + !c._planEntityAvailable('binary_sensor.window') + && !c._planEntityAvailable('lock.front_door'), + exactOpeningReferencesStayActive: + c._openingEntityAvailable('binary_sensor.window') + && c._openingEntityAvailable('lock.front_door'), + sharedContactDrivesBoth: openings.length === 2 + && openings.every((opening) => c._openingAmt(opening) === 1), + sharedLockRendersTwice: sr().querySelectorAll('.oplock').length === 2, + deletionKeepsOpeningFields: JSON.stringify(openingRefs()) === refsBefore, + }; + + // The opening info card remains the one deliberate lock surface. + c._openingInfo = openings[0]; + await settle(); + result.infoHasContactAndLock = sr().querySelectorAll('.oprow').length === 2; + result.infoHasOneLockAction = sr().querySelectorAll('.lockact').length === 1; + const calls = []; + c.hass = { + ...c.hass, + callService: async (domain, service, data) => calls.push({ domain, service, data }), + }; + c._lockAction('lock.front_door', 'lock'); + result.tombstonedMarkerDoesNotBlockExplicitLock = calls.length === 1 + && calls[0].domain === 'lock' && calls[0].service === 'lock'; + + // Re-adding replaces only the marker record; exact opening fields remain + // byte-for-byte the same and therefore cannot be duplicated or retargeted. + c._serverCfg = { + ...c._serverCfg, + markers: [ + { id: 'window-back', binding: 'entity:binary_sensor.window', hidden: false }, + { id: 'lock-back', binding: 'device:d_lock', hidden: false }, + ], + }; + c._regSignature = ''; + c._maybeRebuildDevices(); + await settle(); + result.readdKeepsOpeningFields = JSON.stringify(openingRefs()) === refsBefore; + + // Active registry rows with unknown transport state keep the reference but + // expose no lock action; disabled and orphaned exact rows fail closed. + c.hass = { + ...c.hass, + devices: { + ...c.hass.devices, + hp104_disabled_parent: { id: 'hp104_disabled_parent', disabled_by: 'user' }, + }, + entities: { + ...c.hass.entities, + 'lock.hp104_disabled': { + entity_id: 'lock.hp104_disabled', device_id: null, disabled_by: 'user', + }, + 'lock.hp104_disabled_parent': { + entity_id: 'lock.hp104_disabled_parent', device_id: 'hp104_disabled_parent', disabled_by: null, + }, + 'lock.hp104_orphan': { + entity_id: 'lock.hp104_orphan', device_id: 'hp104_missing_parent', disabled_by: null, + }, + }, + states: { + ...c.hass.states, + 'lock.front_door': { ...c.hass.states['lock.front_door'], state: 'unavailable' }, + 'lock.hp104_disabled': { entity_id: 'lock.hp104_disabled', state: 'locked', attributes: {} }, + 'lock.hp104_disabled_parent': { + entity_id: 'lock.hp104_disabled_parent', state: 'locked', attributes: {}, + }, + 'lock.hp104_orphan': { entity_id: 'lock.hp104_orphan', state: 'locked', attributes: {} }, + }, + callService: async (domain, service, data) => calls.push({ domain, service, data }), + }; + await settle(); + c._openingInfo = c._openingsR[0]; + await settle(); + const unavailableCandidates = c._lockCandidates().map((item) => item.value); + result.unavailableRemainsCandidate = unavailableCandidates.includes('lock.front_door'); + result.unavailableShowsUnknownWithoutAction = sr().querySelector('.oplock.unknown') != null + && sr().querySelector('.lockact') == null; + result.disabledAndOrphanedAreRejected = !unavailableCandidates.includes('lock.hp104_disabled') + && !unavailableCandidates.includes('lock.hp104_disabled_parent') + && !unavailableCandidates.includes('lock.hp104_orphan'); + const callsBeforeRejected = calls.length; + c._lockAction('lock.hp104_disabled', 'lock'); + c._lockAction('lock.hp104_disabled_parent', 'lock'); + c._lockAction('lock.hp104_orphan', 'lock'); + result.rejectedLocksNeverCallService = calls.length === callsBeforeRejected; + + return result; +}); + +checkAll(out); +await finish(browser, out); diff --git a/demo/srv/assets/houseplan-card.js b/demo/srv/assets/houseplan-card.js index 973e58ed..abff64ff 100755 --- a/demo/srv/assets/houseplan-card.js +++ b/demo/srv/assets/houseplan-card.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31249bbb6bcbe46a0148d8eb488eb1";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,O=/>/g,N=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=B(1),U=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=z;for(let e=0;e"===l[0]?(r=n??z,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?N:'"'===l[3]?L:H):r===L||r===H?r=N:r===F||r===O?r=z:(r=N,n=void 0);const d=r===N&&t[e+1].startsWith("/>")?" ":"";o+=r===z?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=rt,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,r=o!==t;if(r){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||r){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let r=0;for(const e of t)n[r]=s?s(e,r):r,o[r]=i(e,r),r++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:r}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=r,o;const a=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(a[d]===r[p])l[p]=vt(n[d],o[p]),d++,p++;else if(a[u]===r[_])l[_]=vt(n[u],o[_]),u--,_--;else if(a[d]===r[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(a[u]===r[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(r,p,_),h=kt(a,d,u)),c.has(a[d]))if(c.has(a[u])){const e=h.get(r[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=r,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6535c0f4f884d1ef5f5b2969c8ddf8ece6ca4247b993fd8563f8c34e6c30aaf3";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,O=/>/g,N=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=B(1),U=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=z;for(let e=0;e"===l[0]?(r=n??z,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?N:'"'===l[3]?L:H):r===L||r===H?r=N:r===F||r===O?r=z:(r=N,n=void 0);const d=r===N&&t[e+1].startsWith("/>")?" ":"";o+=r===z?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=rt,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,r=o!==t;if(r){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||r){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let r=0;for(const e of t)n[r]=s?s(e,r):r,o[r]=i(e,r),r++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:r}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=r,o;const a=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(a[d]===r[p])l[p]=vt(n[d],o[p]),d++,p++;else if(a[u]===r[_])l[_]=vt(n[u],o[_]),u--,_--;else if(a[d]===r[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(a[u]===r[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(r,p,_),h=kt(a,d,u)),c.has(a[d]))if(c.has(a[u])){const e=h.get(r[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=r,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W` ${this.icon?W``:G} ${this.title} `;return this._useHaDialog?W`
-
`:W``}_openingAt(t){if(!this._openingsR.length)return null;const e=this._openingsR.flatMap(e=>{const i=e.angle*Math.PI/180,s=t[0]-e.rx,n=t[1]-e.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>e.rlen/2+12?[]:[{o:e,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!e.length)return null;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return e.find(({o:t,localY:e})=>{const i="gate"===t.type?!t.flip_v:t.flip_v,n=this._spaceWalls.length||"gate"===t.type?fa(s,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:i}):{ox:0,oy:0,cm:0,side:-1},o=od({type:t.type,length:t.rlen,angle:t.angle,amount:this._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:n});return Math.abs(e)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this._openingPreset;if("opening"!==this._tool||!e)return null;const i=this._spaceModel(),s=this._openCuts(),n=this._openingWallIndexFor(i,s);this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===n.key||(this._openingPlacementIntervalsCache={key:n.key,value:sa(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)});const o=ud({pointer:[t[0],t[1]],preset:e,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(e.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender(this._boundaryCoarse?10:6),gridStep:this._gridPitch});if(!o)return null;const r="gate"===o.type?!o.flipV:o.flipV,a=fa(n.value,{x:o.x,y:o.y,angle:o.angle,length:o.renderedLength,flip_v:r}),l="mi"===this.hass?.config?.unit_system?.length,c=ni([o.x,o.y],o.angle,o.renderedLength,i.rooms,this._gridPitch/2),h=(c?[{distance:c.sideA,midpoint:c.midA},{distance:c.sideB,midpoint:c.midB}]:o.measure.labels).map(t=>({x:t.midpoint[0],y:t.midpoint[1],text:Je(t.distance/this._gridPitch*this._cellCm,l)}));return{...o,face:a,measure:{labels:h,guide:o.measure.guide}}}_activateOpeningPlacement(t){this._cancelPath(),this._openingPreset=function(t,e){return{type:t,lengthCm:ld(t),flipH:!1,flipV:!1,revision:e}}(t,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null,this._tool="opening"}_clearOpeningPlacement(t){this._openingHoverCandidate=null,t&&(this._openingPreset=null)}_openingClick(t){const e=this._openingAt(t);if(e)return void this._editOpening(e);const i=this._openingPreset;if(!i)return;const s=this._openingHoverCandidate,n=s&&pd(s,[t[0],t[1]],i.revision,this._cfgEpoch)?s:this._resolveOpeningPlacement(t);if(!n){const e=1.5*this._gridPitch,i=si(t,this._spaceModel().rooms,e);return i&&function(t,e,i,s,n){for(const o of s)if(!(Cs([t,e],o)>n)&&qr([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(i.x,i.y,i.angle,this._openCuts(),e)?void this._showToast(this._t("toast.opening_on_virtual")):void this._showToast(this._t("toast.opening_no_wall"))}this._openingDialog={type:n.type,lengthCm:n.lengthCm,contact:"",lock:"",invert:!1,flipH:n.flipH,flipV:n.flipV,x:n.x,y:n.y,angle:n.angle},this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Hd(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=si(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ad);this._opMeasure=o.measure;const r=o.x/Ad,a=o.y/this._spaceH,l=n.x!==r||n.y!==a||n.angle!==i.angle;l&&(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let n=t.x,o=t.y,r=ni([n,o],t.angle,e,i,s);if(r&&r.centered&&(n!==r.wallCenter[0]||o!==r.wallCenter[1]))[n,o]=r.wallCenter,r=ni([n,o],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,u=h-l,p=Math.hypot(d,u);if(p>0){const c=this._gridPitch,h=Math.min(e/2,p/2);let _=Math.round(((n-a)*d+(o-l)*u)/p/c)*c;_=Math.max(h,Math.min(p-h,_)),n=a+_/p*d,o=l+_/p*u,r=ni([n,o],t.angle,e,i,s)||r}}if(!r)return{x:n,y:o,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Je(t/this._gridPitch*this._cellCm,a)});return{x:n,y:o,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ad,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ad,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const o=e.openings.findIndex(t=>t.id===n.id);o>=0?e.openings[o]=n:e.openings.push(n),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._planEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._planEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?ti(n):null,r=ti(i),a=o&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Ve(bi(t),bi(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===n);r?(r.poly=t.poly.map(t=>[t[0]/Ad,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?ti(i):null;if(!i||!s)return void(this._splitSel=null);const n=.02*this._gridPitch,o=6*this._gridPitch,r=function(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=o?a:null,c=!!l&&ci(l,s,n),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return hi(e,s,n)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(ri(s,n,i))return null;const o=wi(t,s,i),r=wi(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!hi(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),ki(r,i)};let c,h;if(o===r){const r=ki([...e],i);if(r.length<3||yi(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ki(a,i),h=r}else c=ki([...l(s,o,n,r),...[...a].reverse()],i),h=ki([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(t))>Math.max(i,1e-6*yi(t))?null:[c,h]}(s,[...h,l],n);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,p]=d,_=yi(u)>=yi(p)?u:p,m=_===u?p:u;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(n,t.shiftKey):this._snap(n)}get _openingPreview(){const t=this._openingPreset;if("opening"!==this._tool||!t||!this._cursorPt)return null;const e=this._cursorPt,i=this._openingHoverCandidate;if(i&&pd(i,[e[0],e[1]],t.revision,this._cfgEpoch))return i;if(this._openingAt(e))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(e);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit,n=s?function(t,e,i,s,n,o,r=1){let a=[];const l=sa(t,e,i,s,n,o,r);for(const t of l)!t.open&&t.cm>0&&(a=Wr(a,t.a,t.b,t.cm,s,r));return a}(this._spaceModel().rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Ad):null,o=s&&Array.isArray(t.walls)?t.walls:null;let r;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ad,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const a=this._areaSel?this.hass.areas[this._areaSel]?.name:"",l={id:"r"+Date.now().toString(36),name:this._nameSel||a||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(t=>[t[0]/Ad,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(l),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s){this._commitOpenSpans();const e=this._normalizeWalls(n,this._openCuts());e.length?t.walls=e:o?.length?t.walls=o:delete t.walls}if(!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||xr],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=ta(e,i,o,n,r,t);if(!a)return t?t.slice():[];const l=ea(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=Wr(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,l.id,i,this._wallKeyPitch,s,Ad);for(const t of sa(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)){if(t.roomId!==l.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],o=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return Cs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});o>=0&&(n=Wr(n,t.a,t.b,e[o]||i,this._wallKeyPitch,Ad))}n=this._normalizeWalls(n,s),n.length?t.walls=n:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const c=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let h=0;if(c){const t=Ad,e={...this._layout};for(const i of this._devices){if(i.area!==c||i.space!==this._space)continue;if(h++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ad,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const d=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(c?this._t("toast.room_saved",{n:d,added:h}):this._t("toast.room_saved_no_area",{n:d}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),a=hs(e?.glow_color),l=this._devicePresentation(t,!0).valueBadge,c=co(this._planHass,t,this._devices),h=ho(this._planHass,t,c);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ti(t.marker?.display),rippleColor:Dt(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Hc(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:kn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:e?.light_entity,glowMode:null!=a?.bri?"fixed":a?"color":"auto",glowColor:a?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(a?.bri??1))),glowColorDrafted:!!a,glowBrightnessDrafted:null!=a?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!a||null===e?.glow_color),originalGlowColor:a||(null===e?.glow_color?null:void 0),valueBadgeEnabled:o?!0===e?.value_badge?.enabled:!!l,valueBadgeSource:e?.value_badge?.source||l?.source||h,valueBadgePosition:e?.value_badge?.position||l?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:e?.value_badge,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ii)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=Bn(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(r+"|"+(e.area_id||""))||o.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,a]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(Wn(t,n,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:a.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||n.has(l)||a.hidden&&!i.has(l))continue;if(Wn(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,u=d&&(d.name_by_user||d.name)||"";o.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>Da(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Ma(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),n=(t.wall_columns||[]).map(t=>Ca(t,this._cellCm,this._gridPitch)),o=[...i,...s,...n];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:n,all:o},o}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=wd(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...o),Math.min(...r),Math.max(...o),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?Aa(e,l):null,h={floor:e,geom:c,path:c?Sa(c):"",area:Ea(c||[[[...e,e[0]]]])};return this._rszPreview||kd(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":qn(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":qn(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,i=this._t(t);if(i!==t)return i}return this._errText(t)}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:Dt(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}async _saveMarker(){const t=this._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding)if("virtual"!==t.binding||t.name.trim())if("run"!==e||t.tapTarget)if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)this._showToast(this._t("toast.value_badge_source_required"));else{if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const i=this._serverCfg;let s;i.markers=i.markers||[];const n=Ps(t.room);let o=n?.space||null,r=n?.area||null;const a=n?.roomId||null;"virtual"!==t.binding||o||(o=this._space),s=$i(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const l=t.devId,c="virtual"===t.binding?[]:i.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=c.length>0,d=kn(t.binding,t.controls,this._bindingEntities(t.binding)),u=i.markers.find(t=>t.id===s||t.id===l)?.vacuum||null,p={id:s,vacuum:u,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:d.length?d:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=bd(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=o,p.area=r,p.room_id=a);const _=l?this._devices.find(t=>t.id===l):null,m=_?.marker?.room_id??null,g=!!t.room&&null!=_&&(_.space!==o||_.area!==r||m!==a);let f=!1;const v=t.uploadId||l;if(v&&v!==s&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:v,to_id:s}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(p.pdfs,v,s,e),f=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}l&&l!==s&&(i.markers=function(t,e,i){if(!e||!i||e===i)return[...t];const s=`marker:${e}`,n=`marker:${i}`;return t.map(t=>{const e=Array.isArray(t.controls)?t.controls.map(t=>t===s?n:t):t.controls,i="derived_marker_state"===t.value_badge?.source?.kind&&t.value_badge.source.ref===s?{...t.value_badge,source:{...t.value_badge.source,ref:n}}:t.value_badge;return e===t.controls&&i===t.value_badge?t:{...t,controls:e,value_badge:i}})}(i.markers,l,s)),l&&l!==s&&"derived_marker_state"===p.value_badge?.source?.kind&&p.value_badge.source.ref===`marker:${l}`&&(p.value_badge.source={kind:"derived_marker_state",ref:`marker:${s}`}),i.markers=i.markers.filter(t=>t.id!==s&&t.id!==l&&("virtual"===p.binding||t.binding!==p.binding)),i.markers.push(p);let y=null;const b=o||_?.space||this._space,w=l?this._layout[l]:null,k=w?{s:w.s||_?.space||this._space,x:w.x,y:w.y}:l&&_&&this._defPos[l]?this._normPos(_.space,this._defPos[l].x,this._defPos[l].y):null;if(!h&&k&&k.s===b)s===l&&this._layout[s]&&!g||(y={s:k.s,x:k.x,y:k.y},this._layout={...this._layout,[s]:y});else if(h||!this._layout[s]||g){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,i=t.vb[1]+t.vb[3]/2;const n=a?t.rooms.find(t=>t.id===a):r?t.rooms.find(t=>t.area===r):void 0;n&&([e,i]=this._roomCenter(n)),y=this._normPos(o||this._space,e,i),this._layout={...this._layout,[s]:y}}await this._saveConfigNow(),y&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:s,pos:y}));const $=new Set(c);l&&l!==s&&$.add(l),$.delete(s);for(const t of $)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});f&&v&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:v}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const r=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);var a,l;s.markers=(a=r.markers,l=r.cleanupIds,a.map(t=>{if(!Array.isArray(t.controls))return t;const e=t.controls.filter(t=>!("string"==typeof t&&t.startsWith("marker:")&&l.has(t.slice(7))));return e.length===t.controls.length?t:{...t,controls:e.length?e:null}}));const c=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of c){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ad,y:i/Ad}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Zi(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Yi(i.settings.custom_fill):null,o="none"===s.fill?{...n||Ki,a:0}:n;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:lr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:Gi,roomOpacity:Vi,fillMode:"custom",customFill:{...Ki,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingAt(t){if(!this._openingsR.length)return null;const e=this._openingsR.flatMap(e=>{const i=e.angle*Math.PI/180,s=t[0]-e.rx,n=t[1]-e.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>e.rlen/2+12?[]:[{o:e,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!e.length)return null;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return e.find(({o:t,localY:e})=>{const i="gate"===t.type?!t.flip_v:t.flip_v,n=this._spaceWalls.length||"gate"===t.type?fa(s,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:i}):{ox:0,oy:0,cm:0,side:-1},o=od({type:t.type,length:t.rlen,angle:t.angle,amount:this._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:n});return Math.abs(e)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this._openingPreset;if("opening"!==this._tool||!e)return null;const i=this._spaceModel(),s=this._openCuts(),n=this._openingWallIndexFor(i,s);this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===n.key||(this._openingPlacementIntervalsCache={key:n.key,value:sa(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)});const o=ud({pointer:[t[0],t[1]],preset:e,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(e.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender(this._boundaryCoarse?10:6),gridStep:this._gridPitch});if(!o)return null;const r="gate"===o.type?!o.flipV:o.flipV,a=fa(n.value,{x:o.x,y:o.y,angle:o.angle,length:o.renderedLength,flip_v:r}),l="mi"===this.hass?.config?.unit_system?.length,c=ni([o.x,o.y],o.angle,o.renderedLength,i.rooms,this._gridPitch/2),h=(c?[{distance:c.sideA,midpoint:c.midA},{distance:c.sideB,midpoint:c.midB}]:o.measure.labels).map(t=>({x:t.midpoint[0],y:t.midpoint[1],text:Je(t.distance/this._gridPitch*this._cellCm,l)}));return{...o,face:a,measure:{labels:h,guide:o.measure.guide}}}_activateOpeningPlacement(t){this._cancelPath(),this._openingPreset=function(t,e){return{type:t,lengthCm:ld(t),flipH:!1,flipV:!1,revision:e}}(t,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null,this._tool="opening"}_clearOpeningPlacement(t){this._openingHoverCandidate=null,t&&(this._openingPreset=null)}_openingClick(t){const e=this._openingAt(t);if(e)return void this._editOpening(e);const i=this._openingPreset;if(!i)return;const s=this._openingHoverCandidate,n=s&&pd(s,[t[0],t[1]],i.revision,this._cfgEpoch)?s:this._resolveOpeningPlacement(t);if(!n){const e=1.5*this._gridPitch,i=si(t,this._spaceModel().rooms,e);return i&&function(t,e,i,s,n){for(const o of s)if(!(Cs([t,e],o)>n)&&qr([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(i.x,i.y,i.angle,this._openCuts(),e)?void this._showToast(this._t("toast.opening_on_virtual")):void this._showToast(this._t("toast.opening_no_wall"))}this._openingDialog={type:n.type,lengthCm:n.lengthCm,contact:"",lock:"",invert:!1,flipH:n.flipH,flipV:n.flipV,x:n.x,y:n.y,angle:n.angle},this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Hd(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=si(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ad);this._opMeasure=o.measure;const r=o.x/Ad,a=o.y/this._spaceH,l=n.x!==r||n.y!==a||n.angle!==i.angle;l&&(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let n=t.x,o=t.y,r=ni([n,o],t.angle,e,i,s);if(r&&r.centered&&(n!==r.wallCenter[0]||o!==r.wallCenter[1]))[n,o]=r.wallCenter,r=ni([n,o],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,u=h-l,p=Math.hypot(d,u);if(p>0){const c=this._gridPitch,h=Math.min(e/2,p/2);let _=Math.round(((n-a)*d+(o-l)*u)/p/c)*c;_=Math.max(h,Math.min(p-h,_)),n=a+_/p*d,o=l+_/p*u,r=ni([n,o],t.angle,e,i,s)||r}}if(!r)return{x:n,y:o,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Je(t/this._gridPitch*this._cellCm,a)});return{x:n,y:o,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ad,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ad,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const o=e.openings.findIndex(t=>t.id===n.id);o>=0?e.openings[o]=n:e.openings.push(n),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._openingEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?ti(n):null,r=ti(i),a=o&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Ve(bi(t),bi(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===n);r?(r.poly=t.poly.map(t=>[t[0]/Ad,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?ti(i):null;if(!i||!s)return void(this._splitSel=null);const n=.02*this._gridPitch,o=6*this._gridPitch,r=function(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=o?a:null,c=!!l&&ci(l,s,n),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return hi(e,s,n)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(ri(s,n,i))return null;const o=wi(t,s,i),r=wi(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!hi(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),ki(r,i)};let c,h;if(o===r){const r=ki([...e],i);if(r.length<3||yi(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ki(a,i),h=r}else c=ki([...l(s,o,n,r),...[...a].reverse()],i),h=ki([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(t))>Math.max(i,1e-6*yi(t))?null:[c,h]}(s,[...h,l],n);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,p]=d,_=yi(u)>=yi(p)?u:p,m=_===u?p:u;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(n,t.shiftKey):this._snap(n)}get _openingPreview(){const t=this._openingPreset;if("opening"!==this._tool||!t||!this._cursorPt)return null;const e=this._cursorPt,i=this._openingHoverCandidate;if(i&&pd(i,[e[0],e[1]],t.revision,this._cfgEpoch))return i;if(this._openingAt(e))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(e);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit,n=s?function(t,e,i,s,n,o,r=1){let a=[];const l=sa(t,e,i,s,n,o,r);for(const t of l)!t.open&&t.cm>0&&(a=Wr(a,t.a,t.b,t.cm,s,r));return a}(this._spaceModel().rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Ad):null,o=s&&Array.isArray(t.walls)?t.walls:null;let r;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ad,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const a=this._areaSel?this.hass.areas[this._areaSel]?.name:"",l={id:"r"+Date.now().toString(36),name:this._nameSel||a||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(t=>[t[0]/Ad,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(l),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s){this._commitOpenSpans();const e=this._normalizeWalls(n,this._openCuts());e.length?t.walls=e:o?.length?t.walls=o:delete t.walls}if(!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||xr],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=ta(e,i,o,n,r,t);if(!a)return t?t.slice():[];const l=ea(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=Wr(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,l.id,i,this._wallKeyPitch,s,Ad);for(const t of sa(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)){if(t.roomId!==l.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],o=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return Cs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});o>=0&&(n=Wr(n,t.a,t.b,e[o]||i,this._wallKeyPitch,Ad))}n=this._normalizeWalls(n,s),n.length?t.walls=n:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const c=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let h=0;if(c){const t=Ad,e={...this._layout};for(const i of this._devices){if(i.area!==c||i.space!==this._space)continue;if(h++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ad,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const d=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(c?this._t("toast.room_saved",{n:d,added:h}):this._t("toast.room_saved_no_area",{n:d}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),a=hs(e?.glow_color),l=this._devicePresentation(t,!0).valueBadge,c=co(this._planHass,t,this._devices),h=ho(this._planHass,t,c);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ti(t.marker?.display),rippleColor:Dt(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Hc(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:kn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:e?.light_entity,glowMode:null!=a?.bri?"fixed":a?"color":"auto",glowColor:a?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(a?.bri??1))),glowColorDrafted:!!a,glowBrightnessDrafted:null!=a?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!a||null===e?.glow_color),originalGlowColor:a||(null===e?.glow_color?null:void 0),valueBadgeEnabled:o?!0===e?.value_badge?.enabled:!!l,valueBadgeSource:e?.value_badge?.source||l?.source||h,valueBadgePosition:e?.value_badge?.position||l?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:e?.value_badge,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ii)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=Bn(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(r+"|"+(e.area_id||""))||o.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,a]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(Wn(t,n,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:a.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||n.has(l)||a.hidden&&!i.has(l))continue;if(Wn(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,u=d&&(d.name_by_user||d.name)||"";o.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>Da(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Ma(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),n=(t.wall_columns||[]).map(t=>Ca(t,this._cellCm,this._gridPitch)),o=[...i,...s,...n];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:n,all:o},o}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=wd(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...o),Math.min(...r),Math.max(...o),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?Aa(e,l):null,h={floor:e,geom:c,path:c?Sa(c):"",area:Ea(c||[[[...e,e[0]]]])};return this._rszPreview||kd(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":qn(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":qn(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,i=this._t(t);if(i!==t)return i}return this._errText(t)}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:Dt(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}async _saveMarker(){const t=this._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding)if("virtual"!==t.binding||t.name.trim())if("run"!==e||t.tapTarget)if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)this._showToast(this._t("toast.value_badge_source_required"));else{if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const i=this._serverCfg;let s;i.markers=i.markers||[];const n=Ps(t.room);let o=n?.space||null,r=n?.area||null;const a=n?.roomId||null;"virtual"!==t.binding||o||(o=this._space),s=$i(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const l=t.devId,c="virtual"===t.binding?[]:i.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=c.length>0,d=kn(t.binding,t.controls,this._bindingEntities(t.binding)),u=i.markers.find(t=>t.id===s||t.id===l)?.vacuum||null,p={id:s,vacuum:u,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:d.length?d:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=bd(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=o,p.area=r,p.room_id=a);const _=l?this._devices.find(t=>t.id===l):null,m=_?.marker?.room_id??null,g=!!t.room&&null!=_&&(_.space!==o||_.area!==r||m!==a);let f=!1;const v=t.uploadId||l;if(v&&v!==s&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:v,to_id:s}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(p.pdfs,v,s,e),f=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}l&&l!==s&&(i.markers=function(t,e,i){if(!e||!i||e===i)return[...t];const s=`marker:${e}`,n=`marker:${i}`;return t.map(t=>{const e=Array.isArray(t.controls)?t.controls.map(t=>t===s?n:t):t.controls,i="derived_marker_state"===t.value_badge?.source?.kind&&t.value_badge.source.ref===s?{...t.value_badge,source:{...t.value_badge.source,ref:n}}:t.value_badge;return e===t.controls&&i===t.value_badge?t:{...t,controls:e,value_badge:i}})}(i.markers,l,s)),l&&l!==s&&"derived_marker_state"===p.value_badge?.source?.kind&&p.value_badge.source.ref===`marker:${l}`&&(p.value_badge.source={kind:"derived_marker_state",ref:`marker:${s}`}),i.markers=i.markers.filter(t=>t.id!==s&&t.id!==l&&("virtual"===p.binding||t.binding!==p.binding)),i.markers.push(p);let y=null;const b=o||_?.space||this._space,w=l?this._layout[l]:null,k=w?{s:w.s||_?.space||this._space,x:w.x,y:w.y}:l&&_&&this._defPos[l]?this._normPos(_.space,this._defPos[l].x,this._defPos[l].y):null;if(!h&&k&&k.s===b)s===l&&this._layout[s]&&!g||(y={s:k.s,x:k.x,y:k.y},this._layout={...this._layout,[s]:y});else if(h||!this._layout[s]||g){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,i=t.vb[1]+t.vb[3]/2;const n=a?t.rooms.find(t=>t.id===a):r?t.rooms.find(t=>t.area===r):void 0;n&&([e,i]=this._roomCenter(n)),y=this._normPos(o||this._space,e,i),this._layout={...this._layout,[s]:y}}await this._saveConfigNow(),y&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:s,pos:y}));const $=new Set(c);l&&l!==s&&$.add(l),$.delete(s);for(const t of $)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});f&&v&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:v}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const r=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);var a,l;s.markers=(a=r.markers,l=r.cleanupIds,a.map(t=>{if(!Array.isArray(t.controls))return t;const e=t.controls.filter(t=>!("string"==typeof t&&t.startsWith("marker:")&&l.has(t.slice(7))));return e.length===t.controls.length?t:{...t,controls:e.length?e:null}}));const c=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of c){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ad,y:i/Ad}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Zi(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Yi(i.settings.custom_fill):null,o="none"===s.fill?{...n||Ki,a:0}:n;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:lr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:Gi,roomOpacity:Vi,fillMode:"custom",customFill:{...Ki,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
${e.map(e=>W`
@@ -5313,13 +5313,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31 `})} `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return U``}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._renderEntityAvailable(t.contact)?this._renderPlanHass.states[t.contact]?.state:null;return oi(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!Wn(this._fullRegistryHass,t,Bn(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderEntityAvailable(t){return!!t&&(!Wn(this._renderPlanHass,t,Bn(this._markers))&&(!!this._renderPlanHass.entities?.[t]&&!!this._renderPlanHass.states?.[t]))}_renderOpeningPlacementPreview(){const t=this._openingPreview;if(!t)return U``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:oi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:t.face};return U``}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._renderOpeningEntityAvailable(t.contact)?this._renderPlanHass.states[t.contact]?.state:null;return oi(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!Wn(this._fullRegistryHass,t,Bn(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderEntityAvailable(t){return!!t&&(!Wn(this._renderPlanHass,t,Bn(this._markers))&&(!!this._renderPlanHass.entities?.[t]&&!!this._renderPlanHass.states?.[t]))}_openingEntityAvailable(t){return function(t,e,i=on(t)){return!!e&&"active"===pn(t,`entity:${e}`,i).kind}(this.hass,t,this._haRegistry)}_renderOpeningEntityAvailable(t){return e=this._renderPlanHass,!!(i=t)&&!!e?.entities?.[i]&&!!e?.states?.[i];var e,i}_renderOpeningPlacementPreview(){const t=this._openingPreview;if(!t)return U``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:oi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:t.face};return U` `}_renderOpenings(t){const e=this._openingsR;if(!e.length)return U``;const i=t.color,s=this._spaceWalls,n=this._openPairs().flatMap(t=>t.segs),o=this._openingWallIndexFor(this._spaceModel(),n).value;return U`${e.map(t=>{const e=this._openingAmt(t),n=e>0&&!!t.contact&&this._renderEntityAvailable(t.contact)?"var(--hp-open)":i,r="gate"===t.type?!t.flip_v:t.flip_v,a=s.length||"gate"===t.type?fa(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:r}):{ox:0,oy:0,cm:0,side:-1},l={type:t.type,length:t.rlen,angle:t.angle,amount:e,flipH:!!t.flip_h,flipV:!!t.flip_v,base:i,tone:n,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=od(l);return U``}_renderOpenings(t){const e=this._openingsR;if(!e.length)return U``;const i=t.color,s=this._spaceWalls,n=this._openPairs().flatMap(t=>t.segs),o=this._openingWallIndexFor(this._spaceModel(),n).value;return U`${e.map(t=>{const e=this._openingAmt(t),n=e>0&&!!t.contact&&this._renderOpeningEntityAvailable(t.contact)?"var(--hp-open)":i,r="gate"===t.type?!t.flip_v:t.flip_v,a=s.length||"gate"===t.type?fa(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:r}):{ox:0,oy:0,cm:0,side:-1},l={type:t.type,length:t.rlen,angle:t.angle,amount:e,flipH:!!t.flip_h,flipV:!!t.flip_v,base:i,tone:n,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=od(l);return U` ${rd(l)} @@ -5329,11 +5329,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31 @pointermove=${e=>this._opPointerMove(e,t)} @pointerup=${e=>this._opPointerUp(e,t)} @pointercancel=${e=>this._opPointerUp(e,t)}> - `})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._renderEntityAvailable(t.lock));if(!e.length)return W``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return W`${e.map(e=>{const i=this._renderPlanHass.states[e.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?fa(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,u=(h-t.y)/t.h*100;return W`
`})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._renderOpeningEntityAvailable(t.lock));if(!e.length)return W``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return W`${e.map(e=>{const i=this._renderPlanHass.states[e.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?fa(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,u=(h-t.y)/t.h*100;return W`
{t.stopPropagation(),"view"===this._mode&&(this._openingInfo=e)}}> -
`})}`}_lockAction(t,e){if(this._planEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._planEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._planEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>W`
${e}${i}
`;return W``})}`}_lockAction(t,e){if(this._openingEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._openingEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._openingEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>W`
${e}${i}
`;return W`this._openingInfo=null}>
diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index 973e58ed..abff64ff 100755 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -1,4 +1,4 @@ -globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31249bbb6bcbe46a0148d8eb488eb1";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,O=/>/g,N=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=B(1),U=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=z;for(let e=0;e"===l[0]?(r=n??z,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?N:'"'===l[3]?L:H):r===L||r===H?r=N:r===F||r===O?r=z:(r=N,n=void 0);const d=r===N&&t[e+1].startsWith("/>")?" ":"";o+=r===z?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=rt,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,r=o!==t;if(r){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||r){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let r=0;for(const e of t)n[r]=s?s(e,r):r,o[r]=i(e,r),r++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:r}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=r,o;const a=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(a[d]===r[p])l[p]=vt(n[d],o[p]),d++,p++;else if(a[u]===r[_])l[_]=vt(n[u],o[_]),u--,_--;else if(a[d]===r[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(a[u]===r[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(r,p,_),h=kt(a,d,u)),c.has(a[d]))if(c.has(a[u])){const e=h.get(r[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=r,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W` +globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="6535c0f4f884d1ef5f5b2969c8ddf8ece6ca4247b993fd8563f8c34e6c30aaf3";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},r=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:u}=Object,p=globalThis,_=p.trustedTypes,m=_?_.emptyScript:"",g=p.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!a(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),p.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=u(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(r(t))}else void 0!==t&&e.push(r(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(p.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,$=t=>t,x=k.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,T=`<${D}>`,P=document,R=()=>P.createComment(""),A=t=>null===t||"object"!=typeof t&&"function"!=typeof t,E=Array.isArray,I="[ \t\n\f\r]",z=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,F=/-->/g,O=/>/g,N=RegExp(`>|${I}(?:([^\\s"'>=/]+)(${I}*=${I}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,q=/^(?:script|style|textarea|title)$/i,B=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=B(1),U=B(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=P.createTreeWalker(P,129);function Y(t,e){if(!E(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const X=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"",r=z;for(let e=0;e"===l[0]?(r=n??z,c=-1):void 0===l[1]?c=-2:(c=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?N:'"'===l[3]?L:H):r===L||r===H?r=N:r===F||r===O?r=z:(r=N,n=void 0);const d=r===N&&t[e+1].startsWith("/>")?" ":"";o+=r===z?i+T:c>=0?(s.push(a),i.slice(0,c)+M+i.slice(c)+C+d):i+C+(-2===c?e:d)}return[Y(t,o+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const r=t.length-1,a=this.parts,[l,c]=X(t,e);if(this.el=Z.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&a.length0){s.textContent=x?x.emptyScript:"";for(let i=0;iE(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&A(this._$AH)?this._$AA.nextSibling.data=t:this.T(P.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=Z.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new Z(t)),e}k(t){E(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=J(this,t,e,0),o=!A(t)||t!==this._$AH&&t!==j,o&&(this._$AH=t);else{const s=t;let r,a;for(t=n[0],r=0;r{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const ut=2;let pt=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const{I:_t}=rt,mt=t=>t,gt=()=>document.createComment(""),ft=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(gt(),n),o=s.insertBefore(gt(),n);i=new _t(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,r=o!==t;if(r){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||r){let t=i._$AA;for(;t!==e;){const e=mt(t).nextSibling;mt(s).insertBefore(t,n),t=e}}}return i},vt=(t,e,i=t)=>(t._$AI(e,i),t),yt={},bt=(t,e=yt)=>t._$AH=e,wt=t=>{t._$AR(),t._$AA.remove()},kt=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},$t=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pt{constructor(t){if(super(t),t.type!==ut)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let r=0;for(const e of t)n[r]=s?s(e,r):r,o[r]=i(e,r),r++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:r}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=r,o;const a=this.ut??=[],l=[];let c,h,d=0,u=n.length-1,p=0,_=o.length-1;for(;d<=u&&p<=_;)if(null===n[d])d++;else if(null===n[u])u--;else if(a[d]===r[p])l[p]=vt(n[d],o[p]),d++,p++;else if(a[u]===r[_])l[_]=vt(n[u],o[_]),u--,_--;else if(a[d]===r[_])l[_]=vt(n[d],o[_]),ft(t,l[_+1],n[d]),d++,_--;else if(a[u]===r[p])l[p]=vt(n[u],o[p]),ft(t,n[d],n[u]),u--,p++;else if(void 0===c&&(c=kt(r,p,_),h=kt(a,d,u)),c.has(a[d]))if(c.has(a[u])){const e=h.get(r[p]),i=void 0!==e?n[e]:null;if(null===i){const e=ft(t,n[d]);vt(e,o[p]),l[p]=e}else l[p]=vt(i,o[p]),ft(t,n[d],i),n[e]=null;p++}else wt(n[u]),u--;else wt(n[d]),d++;for(;p<=_;){const e=ft(t,l[_+1]);vt(e,o[p]),l[p++]=e}for(;d<=u;){const t=n[d++];null!==t&&wt(t)}return this.ut=r,bt(t,l),j}}),xt=new WeakMap;let St=0;class Mt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++St,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=xt.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),xt.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?xt.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=xt.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void xt.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W` ${this.icon?W``:G} ${this.title} `;return this._useHaDialog?W`
-
`:W``}_openingAt(t){if(!this._openingsR.length)return null;const e=this._openingsR.flatMap(e=>{const i=e.angle*Math.PI/180,s=t[0]-e.rx,n=t[1]-e.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>e.rlen/2+12?[]:[{o:e,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!e.length)return null;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return e.find(({o:t,localY:e})=>{const i="gate"===t.type?!t.flip_v:t.flip_v,n=this._spaceWalls.length||"gate"===t.type?fa(s,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:i}):{ox:0,oy:0,cm:0,side:-1},o=od({type:t.type,length:t.rlen,angle:t.angle,amount:this._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:n});return Math.abs(e)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this._openingPreset;if("opening"!==this._tool||!e)return null;const i=this._spaceModel(),s=this._openCuts(),n=this._openingWallIndexFor(i,s);this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===n.key||(this._openingPlacementIntervalsCache={key:n.key,value:sa(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)});const o=ud({pointer:[t[0],t[1]],preset:e,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(e.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender(this._boundaryCoarse?10:6),gridStep:this._gridPitch});if(!o)return null;const r="gate"===o.type?!o.flipV:o.flipV,a=fa(n.value,{x:o.x,y:o.y,angle:o.angle,length:o.renderedLength,flip_v:r}),l="mi"===this.hass?.config?.unit_system?.length,c=ni([o.x,o.y],o.angle,o.renderedLength,i.rooms,this._gridPitch/2),h=(c?[{distance:c.sideA,midpoint:c.midA},{distance:c.sideB,midpoint:c.midB}]:o.measure.labels).map(t=>({x:t.midpoint[0],y:t.midpoint[1],text:Je(t.distance/this._gridPitch*this._cellCm,l)}));return{...o,face:a,measure:{labels:h,guide:o.measure.guide}}}_activateOpeningPlacement(t){this._cancelPath(),this._openingPreset=function(t,e){return{type:t,lengthCm:ld(t),flipH:!1,flipV:!1,revision:e}}(t,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null,this._tool="opening"}_clearOpeningPlacement(t){this._openingHoverCandidate=null,t&&(this._openingPreset=null)}_openingClick(t){const e=this._openingAt(t);if(e)return void this._editOpening(e);const i=this._openingPreset;if(!i)return;const s=this._openingHoverCandidate,n=s&&pd(s,[t[0],t[1]],i.revision,this._cfgEpoch)?s:this._resolveOpeningPlacement(t);if(!n){const e=1.5*this._gridPitch,i=si(t,this._spaceModel().rooms,e);return i&&function(t,e,i,s,n){for(const o of s)if(!(Cs([t,e],o)>n)&&qr([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(i.x,i.y,i.angle,this._openCuts(),e)?void this._showToast(this._t("toast.opening_on_virtual")):void this._showToast(this._t("toast.opening_no_wall"))}this._openingDialog={type:n.type,lengthCm:n.lengthCm,contact:"",lock:"",invert:!1,flipH:n.flipH,flipV:n.flipV,x:n.x,y:n.y,angle:n.angle},this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Hd(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=si(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ad);this._opMeasure=o.measure;const r=o.x/Ad,a=o.y/this._spaceH,l=n.x!==r||n.y!==a||n.angle!==i.angle;l&&(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let n=t.x,o=t.y,r=ni([n,o],t.angle,e,i,s);if(r&&r.centered&&(n!==r.wallCenter[0]||o!==r.wallCenter[1]))[n,o]=r.wallCenter,r=ni([n,o],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,u=h-l,p=Math.hypot(d,u);if(p>0){const c=this._gridPitch,h=Math.min(e/2,p/2);let _=Math.round(((n-a)*d+(o-l)*u)/p/c)*c;_=Math.max(h,Math.min(p-h,_)),n=a+_/p*d,o=l+_/p*u,r=ni([n,o],t.angle,e,i,s)||r}}if(!r)return{x:n,y:o,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Je(t/this._gridPitch*this._cellCm,a)});return{x:n,y:o,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ad,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ad,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const o=e.openings.findIndex(t=>t.id===n.id);o>=0?e.openings[o]=n:e.openings.push(n),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._planEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._planEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?ti(n):null,r=ti(i),a=o&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Ve(bi(t),bi(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===n);r?(r.poly=t.poly.map(t=>[t[0]/Ad,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?ti(i):null;if(!i||!s)return void(this._splitSel=null);const n=.02*this._gridPitch,o=6*this._gridPitch,r=function(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=o?a:null,c=!!l&&ci(l,s,n),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return hi(e,s,n)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(ri(s,n,i))return null;const o=wi(t,s,i),r=wi(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!hi(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),ki(r,i)};let c,h;if(o===r){const r=ki([...e],i);if(r.length<3||yi(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ki(a,i),h=r}else c=ki([...l(s,o,n,r),...[...a].reverse()],i),h=ki([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(t))>Math.max(i,1e-6*yi(t))?null:[c,h]}(s,[...h,l],n);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,p]=d,_=yi(u)>=yi(p)?u:p,m=_===u?p:u;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(n,t.shiftKey):this._snap(n)}get _openingPreview(){const t=this._openingPreset;if("opening"!==this._tool||!t||!this._cursorPt)return null;const e=this._cursorPt,i=this._openingHoverCandidate;if(i&&pd(i,[e[0],e[1]],t.revision,this._cfgEpoch))return i;if(this._openingAt(e))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(e);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit,n=s?function(t,e,i,s,n,o,r=1){let a=[];const l=sa(t,e,i,s,n,o,r);for(const t of l)!t.open&&t.cm>0&&(a=Wr(a,t.a,t.b,t.cm,s,r));return a}(this._spaceModel().rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Ad):null,o=s&&Array.isArray(t.walls)?t.walls:null;let r;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ad,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const a=this._areaSel?this.hass.areas[this._areaSel]?.name:"",l={id:"r"+Date.now().toString(36),name:this._nameSel||a||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(t=>[t[0]/Ad,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(l),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s){this._commitOpenSpans();const e=this._normalizeWalls(n,this._openCuts());e.length?t.walls=e:o?.length?t.walls=o:delete t.walls}if(!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||xr],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=ta(e,i,o,n,r,t);if(!a)return t?t.slice():[];const l=ea(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=Wr(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,l.id,i,this._wallKeyPitch,s,Ad);for(const t of sa(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)){if(t.roomId!==l.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],o=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return Cs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});o>=0&&(n=Wr(n,t.a,t.b,e[o]||i,this._wallKeyPitch,Ad))}n=this._normalizeWalls(n,s),n.length?t.walls=n:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const c=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let h=0;if(c){const t=Ad,e={...this._layout};for(const i of this._devices){if(i.area!==c||i.space!==this._space)continue;if(h++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ad,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const d=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(c?this._t("toast.room_saved",{n:d,added:h}):this._t("toast.room_saved_no_area",{n:d}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),a=hs(e?.glow_color),l=this._devicePresentation(t,!0).valueBadge,c=co(this._planHass,t,this._devices),h=ho(this._planHass,t,c);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ti(t.marker?.display),rippleColor:Dt(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Hc(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:kn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:e?.light_entity,glowMode:null!=a?.bri?"fixed":a?"color":"auto",glowColor:a?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(a?.bri??1))),glowColorDrafted:!!a,glowBrightnessDrafted:null!=a?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!a||null===e?.glow_color),originalGlowColor:a||(null===e?.glow_color?null:void 0),valueBadgeEnabled:o?!0===e?.value_badge?.enabled:!!l,valueBadgeSource:e?.value_badge?.source||l?.source||h,valueBadgePosition:e?.value_badge?.position||l?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:e?.value_badge,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ii)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=Bn(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(r+"|"+(e.area_id||""))||o.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,a]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(Wn(t,n,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:a.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||n.has(l)||a.hidden&&!i.has(l))continue;if(Wn(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,u=d&&(d.name_by_user||d.name)||"";o.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>Da(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Ma(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),n=(t.wall_columns||[]).map(t=>Ca(t,this._cellCm,this._gridPitch)),o=[...i,...s,...n];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:n,all:o},o}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=wd(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...o),Math.min(...r),Math.max(...o),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?Aa(e,l):null,h={floor:e,geom:c,path:c?Sa(c):"",area:Ea(c||[[[...e,e[0]]]])};return this._rszPreview||kd(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":qn(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":qn(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,i=this._t(t);if(i!==t)return i}return this._errText(t)}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:Dt(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}async _saveMarker(){const t=this._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding)if("virtual"!==t.binding||t.name.trim())if("run"!==e||t.tapTarget)if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)this._showToast(this._t("toast.value_badge_source_required"));else{if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const i=this._serverCfg;let s;i.markers=i.markers||[];const n=Ps(t.room);let o=n?.space||null,r=n?.area||null;const a=n?.roomId||null;"virtual"!==t.binding||o||(o=this._space),s=$i(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const l=t.devId,c="virtual"===t.binding?[]:i.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=c.length>0,d=kn(t.binding,t.controls,this._bindingEntities(t.binding)),u=i.markers.find(t=>t.id===s||t.id===l)?.vacuum||null,p={id:s,vacuum:u,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:d.length?d:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=bd(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=o,p.area=r,p.room_id=a);const _=l?this._devices.find(t=>t.id===l):null,m=_?.marker?.room_id??null,g=!!t.room&&null!=_&&(_.space!==o||_.area!==r||m!==a);let f=!1;const v=t.uploadId||l;if(v&&v!==s&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:v,to_id:s}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(p.pdfs,v,s,e),f=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}l&&l!==s&&(i.markers=function(t,e,i){if(!e||!i||e===i)return[...t];const s=`marker:${e}`,n=`marker:${i}`;return t.map(t=>{const e=Array.isArray(t.controls)?t.controls.map(t=>t===s?n:t):t.controls,i="derived_marker_state"===t.value_badge?.source?.kind&&t.value_badge.source.ref===s?{...t.value_badge,source:{...t.value_badge.source,ref:n}}:t.value_badge;return e===t.controls&&i===t.value_badge?t:{...t,controls:e,value_badge:i}})}(i.markers,l,s)),l&&l!==s&&"derived_marker_state"===p.value_badge?.source?.kind&&p.value_badge.source.ref===`marker:${l}`&&(p.value_badge.source={kind:"derived_marker_state",ref:`marker:${s}`}),i.markers=i.markers.filter(t=>t.id!==s&&t.id!==l&&("virtual"===p.binding||t.binding!==p.binding)),i.markers.push(p);let y=null;const b=o||_?.space||this._space,w=l?this._layout[l]:null,k=w?{s:w.s||_?.space||this._space,x:w.x,y:w.y}:l&&_&&this._defPos[l]?this._normPos(_.space,this._defPos[l].x,this._defPos[l].y):null;if(!h&&k&&k.s===b)s===l&&this._layout[s]&&!g||(y={s:k.s,x:k.x,y:k.y},this._layout={...this._layout,[s]:y});else if(h||!this._layout[s]||g){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,i=t.vb[1]+t.vb[3]/2;const n=a?t.rooms.find(t=>t.id===a):r?t.rooms.find(t=>t.area===r):void 0;n&&([e,i]=this._roomCenter(n)),y=this._normPos(o||this._space,e,i),this._layout={...this._layout,[s]:y}}await this._saveConfigNow(),y&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:s,pos:y}));const $=new Set(c);l&&l!==s&&$.add(l),$.delete(s);for(const t of $)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});f&&v&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:v}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const r=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);var a,l;s.markers=(a=r.markers,l=r.cleanupIds,a.map(t=>{if(!Array.isArray(t.controls))return t;const e=t.controls.filter(t=>!("string"==typeof t&&t.startsWith("marker:")&&l.has(t.slice(7))));return e.length===t.controls.length?t:{...t,controls:e.length?e:null}}));const c=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of c){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ad,y:i/Ad}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Zi(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Yi(i.settings.custom_fill):null,o="none"===s.fill?{...n||Ki,a:0}:n;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:lr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:Gi,roomOpacity:Vi,fillMode:"custom",customFill:{...Ki,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
+
`:W``}_openingAt(t){if(!this._openingsR.length)return null;const e=this._openingsR.flatMap(e=>{const i=e.angle*Math.PI/180,s=t[0]-e.rx,n=t[1]-e.ry,o=s*Math.cos(i)+n*Math.sin(i);return Math.abs(o)>e.rlen/2+12?[]:[{o:e,localY:-s*Math.sin(i)+n*Math.cos(i)}]});if(!e.length)return null;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return e.find(({o:t,localY:e})=>{const i="gate"===t.type?!t.flip_v:t.flip_v,n=this._spaceWalls.length||"gate"===t.type?fa(s,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:i}):{ox:0,oy:0,cm:0,side:-1},o=od({type:t.type,length:t.rlen,angle:t.angle,amount:this._openingAmt(t),flipH:!!t.flip_h,flipV:!!t.flip_v,cellCm:this._cellCm,gridPitch:this._gridPitch,face:n});return Math.abs(e)<=o.hitHalf})?.o||null}_resolveOpeningPlacement(t){const e=this._openingPreset;if("opening"!==this._tool||!e)return null;const i=this._spaceModel(),s=this._openCuts(),n=this._openingWallIndexFor(i,s);this._openingPlacementIntervalsCache&&this._openingPlacementIntervalsCache.key===n.key||(this._openingPlacementIntervalsCache={key:n.key,value:sa(i.rooms,this._spaceWalls,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)});const o=ud({pointer:[t[0],t[1]],preset:e,geometryRevision:this._cfgEpoch,renderedLength:this._cmToUnits(e.lengthCm),intervals:this._openingPlacementIntervalsCache.value,baseTolerance:1.5*this._gridPitch,bodyPointerPadding:this._cssPxToRender(this._boundaryCoarse?10:6),gridStep:this._gridPitch});if(!o)return null;const r="gate"===o.type?!o.flipV:o.flipV,a=fa(n.value,{x:o.x,y:o.y,angle:o.angle,length:o.renderedLength,flip_v:r}),l="mi"===this.hass?.config?.unit_system?.length,c=ni([o.x,o.y],o.angle,o.renderedLength,i.rooms,this._gridPitch/2),h=(c?[{distance:c.sideA,midpoint:c.midA},{distance:c.sideB,midpoint:c.midB}]:o.measure.labels).map(t=>({x:t.midpoint[0],y:t.midpoint[1],text:Je(t.distance/this._gridPitch*this._cellCm,l)}));return{...o,face:a,measure:{labels:h,guide:o.measure.guide}}}_activateOpeningPlacement(t){this._cancelPath(),this._openingPreset=function(t,e){return{type:t,lengthCm:ld(t),flipH:!1,flipV:!1,revision:e}}(t,++this._openingPresetRevision),this._openingHoverCandidate=null,this._cursorPt=null,this._tool="opening"}_clearOpeningPlacement(t){this._openingHoverCandidate=null,t&&(this._openingPreset=null)}_openingClick(t){const e=this._openingAt(t);if(e)return void this._editOpening(e);const i=this._openingPreset;if(!i)return;const s=this._openingHoverCandidate,n=s&&pd(s,[t[0],t[1]],i.revision,this._cfgEpoch)?s:this._resolveOpeningPlacement(t);if(!n){const e=1.5*this._gridPitch,i=si(t,this._spaceModel().rooms,e);return i&&function(t,e,i,s,n){for(const o of s)if(!(Cs([t,e],o)>n)&&qr([o[0],o[1]],[o[2],o[3]],i))return!0;return!1}(i.x,i.y,i.angle,this._openCuts(),e)?void this._showToast(this._t("toast.opening_on_virtual")):void this._showToast(this._t("toast.opening_no_wall"))}this._openingDialog={type:n.type,lengthCm:n.lengthCm,contact:"",lock:"",invert:!1,flipH:n.flipH,flipV:n.flipV,x:n.x,y:n.y,angle:n.angle},this._openingHoverCandidate=null,this._cursorPt=null}_editOpening(t){this._openingDialog={id:t.id,type:t.type,lengthCm:Math.round(t.rlen/this._gridPitch*this._cellCm),contact:t.contact||"",lock:t.lock||"",invert:!!t.invert,flipH:!!t.flip_h,flipV:!!t.flip_v,x:t.rx,y:t.ry,angle:t.angle}}_opPointerDown(t,e){if("plan"===this._mode&&"resize"!==this._tool){t.preventDefault(),t.stopPropagation();try{Hd(t)}catch{}this._opDrag={id:e.id,moved:!1,sx:t.clientX,sy:t.clientY,dirty:!1,before:this._geometrySnapshot()}}}_opPointerMove(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;if(Math.abs(t.clientX-this._opDrag.sx)+Math.abs(t.clientY-this._opDrag.sy)<=3)return;const i=si(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,n=s?.openings?.find(t=>t.id===e.id);if(!n)return;const o=this._opRuler(i,n.length*Ad);this._opMeasure=o.measure;const r=o.x/Ad,a=o.y/this._spaceH,l=n.x!==r||n.y!==a||n.angle!==i.angle;l&&(this._opDrag.dirty=!0),n.x=r,n.y=a,n.angle=i.angle,l&&this._cfgEpoch++,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let n=t.x,o=t.y,r=ni([n,o],t.angle,e,i,s);if(r&&r.centered&&(n!==r.wallCenter[0]||o!==r.wallCenter[1]))[n,o]=r.wallCenter,r=ni([n,o],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[c,h]=r.wallB,d=c-a,u=h-l,p=Math.hypot(d,u);if(p>0){const c=this._gridPitch,h=Math.min(e/2,p/2);let _=Math.round(((n-a)*d+(o-l)*u)/p/c)*c;_=Math.max(h,Math.min(p-h,_)),n=a+_/p*d,o=l+_/p*u,r=ni([n,o],t.angle,e,i,s)||r}}if(!r)return{x:n,y:o,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Je(t/this._gridPitch*this._cellCm,a)});return{x:n,y:o,angle:t.angle,measure:{labels:[l(r.sideA,r.midA),l(r.sideB,r.midB)],guide:r.centered?{x:r.wallCenter[0],y:r.wallCenter[1],angle:t.angle}:null}}}_opPointerUp(t,e){if(!this._opDrag||this._opDrag.id!==e.id)return;const i=this._opDrag,s=i.moved;this._opMeasure=null,s&&i.dirty&&(this._recordGeometry(this._t("history.move_opening"),i.before),this._saveConfig()),s?window.setTimeout(()=>this._opDrag=null,0):this._opDrag=null}_opClick(t,e){"plan"===this._mode&&"resize"===this._tool||(t.stopPropagation(),this._opDrag?.moved||"plan"===this._mode&&this._editOpening(e))}_saveOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/Ad,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/Ad,contact:t.contact||null,lock:"window"!==t.type&&t.lock||null,invert:t.invert||void 0,flip_h:"gate"!==t.type&&t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const o=e.openings.findIndex(t=>t.id===n.id);o>=0?e.openings[o]=n:e.openings.push(n),this._recordGeometry(this._t(t.id?"history.edit_opening":"history.add_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_deleteOpening(){const t=this._openingDialog,e=this._curSpaceCfg;if(!t?.id||!e?.openings)return;const i=this._geometrySnapshot();e.openings=e.openings.filter(e=>e.id!==t.id),this._recordGeometry(this._t("history.delete_opening"),i),this._saveConfig(),this._openingDialog=null,this.requestUpdate()}_contactCandidates(){const t=[];for(const e of Object.keys(this.hass.states)){if(!this._openingEntityAvailable(e))continue;const i=e.split(".")[0];if("binary_sensor"!==i&&"cover"!==i)continue;const s=this.hass.states[e],n=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||n)&&t.push([e,s?.attributes?.friendly_name||e,n?0:1])}return t.sort((t,e)=>t[2]-e[2]||t[1].localeCompare(e[1])).map(([t,e])=>({value:t,label:e}))}_lockCandidates(){return Object.keys(this.hass.states).filter(t=>t.startsWith("lock.")&&this._openingEntityAvailable(t)).map(t=>({value:t,label:this.hass.states[t]?.attributes?.friendly_name||t})).sort((t,e)=>t.label.localeCompare(e.label))}_mergeClick(t){const e=this._spaceModel().rooms,i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;const s=i.id;if(!this._mergeSel||this._mergeSel===s)return void(this._mergeSel=this._mergeSel===s?null:s);const n=e.find(t=>t.id===this._mergeSel),o=n?ti(n):null,r=ti(i),a=o&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=Ve(bi(t),bi(e));if(1!==i.length)return null;if(1!==i[0].length)return null;const s=i[0][0].slice(0,-1).map(t=>[t[0],t[1]]);return s.length>=3?s:null}(o,r):null;if(!a)return this._showToast(this._t("toast.merge_not_adjacent")),void(this._mergeSel=null);this._mergeDialog={aId:this._mergeSel,bId:s,poly:a,pick:"a"},this._mergeSel=null}_commitMerge(){const t=this._mergeDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=this._geometrySnapshot(),s=this._spaceH,n="a"===t.pick?t.aId:t.bId,o="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===n);r?(r.poly=t.poly.map(t=>[t[0]/Ad,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==o),this._commitOpenSpans(),this._recordGeometry(this._t("history.merge_rooms"),i),this._saveConfig(),this._mergeDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.rooms_merged",{name:r.name||""}))):this._mergeDialog=null}_splitClick(t){const e=this._spaceModel().rooms;if(!this._splitSel){const i=[...e].reverse().find(e=>this._pointInRoom(t,e));if(!i?.id)return;return void(this._splitSel={roomId:i.id,pts:[]})}const i=e.find(t=>t.id===this._splitSel.roomId),s=i?ti(i):null;if(!i||!s)return void(this._splitSel=null);const n=.02*this._gridPitch,o=6*this._gridPitch,r=function(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let n=0;n=n)continue;n=_;const m=Math.sqrt(u),g=(i>0?Math.max(0,Math.min(m,Math.round(p*m/i)*i)):p*m)/m;s=[r+g*h,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=o?a:null,c=!!l&&ci(l,s,n),h=this._splitSel.pts;if(!h.length)return c?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!c){const e=this._snap(t);return hi(e,s,n)?void(this._splitSel={...this._splitSel,pts:[...h,e]}):void this._showToast(this._t("toast.split_pick_inside"))}const d=function(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(ri(s,n,i))return null;const o=wi(t,s,i),r=wi(t,n,i);if(o<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!hi(e,t,i))return null;for(let i=0;i{const r=[e];let a=(s+1)%t.length;for(let e=0;e<=t.length&&(r.push(t[a]),a!==o);e++)a=(a+1)%t.length;return r.push(n),ki(r,i)};let c,h;if(o===r){const r=ki([...e],i);if(r.length<3||yi(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}c=ki(a,i),h=r}else c=ki([...l(s,o,n,r),...[...a].reverse()],i),h=ki([...l(n,r,s,o),...a],i);return c.length<3||h.length<3||yi(c)<=i||yi(h)<=i||Math.abs(yi(c)+yi(h)-yi(t))>Math.max(i,1e-6*yi(t))?null:[c,h]}(s,[...h,l],n);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[u,p]=d,_=yi(u)>=yi(p)?u:p,m=_===u?p:u;this._pendingSplit={roomId:i.id,mainPoly:_,newPoly:m},this._cursorPt=null,this._nameSel="",this._areaSel="",this._roomDialog=!0}get _contourClosed(){return this._path.length>=4&&this._samePt(this._path[0],this._path[this._path.length-1])}_markupMove(t){if(!this._markup)return;const e=t.pointerType;if(e&&(this._boundaryPointerType=e),"column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"boundary"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const i=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,s="split"===this._tool&&!!this._splitSel?.pts?.length;if(!i&&!s)return;const n=this._svgPoint(t);this._cursorPt=i?this._snapDrawPoint(n,t.shiftKey):this._snap(n)}get _openingPreview(){const t=this._openingPreset;if("opening"!==this._tool||!t||!this._cursorPt)return null;const e=this._cursorPt,i=this._openingHoverCandidate;if(i&&pd(i,[e[0],e[1]],t.revision,this._cfgEpoch))return i;if(this._openingAt(e))return this._openingHoverCandidate=null,null;const s=this._resolveOpeningPlacement(e);return this._openingHoverCandidate=s,s}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){(this._areaSel||this._nameSel.trim())&&this._commitRoom()}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit,n=s?function(t,e,i,s,n,o,r=1){let a=[];const l=sa(t,e,i,s,n,o,r);for(const t of l)!t.open&&t.cm>0&&(a=Wr(a,t.a,t.b,t.cm,s,r));return a}(this._spaceModel().rooms,t.walls,this._openCuts(),this._wallKeyPitch,this._cellCm,this._gridPitch,Ad):null,o=s&&Array.isArray(t.walls)?t.walls:null;let r;if(this._pendingSplit){const e=t.rooms.find(t=>t.id===this._pendingSplit.roomId);if(!e)return this._pendingSplit=null,this._splitSel=null,void(this._roomDialog=!1);e.poly=this._pendingSplit.mainPoly.map(t=>[t[0]/Ad,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,r=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;r=this._path.slice(0,-1)}const a=this._areaSel?this.hass.areas[this._areaSel]?.name:"",l={id:"r"+Date.now().toString(36),name:this._nameSel||a||this._t("room.default_name"),area:this._areaSel||null,poly:r.map(t=>[t[0]/Ad,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(l),!s&&this._activeDraftId&&Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),s){this._commitOpenSpans();const e=this._normalizeWalls(n,this._openCuts());e.length?t.walls=e:o?.length?t.walls=o:delete t.walls}if(!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||xr],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,n,o=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=ta(e,i,o,n,r,t);if(!a)return t?t.slice():[];const l=ea(t,a,n,r);let c=t?t.slice():[];for(let t=0;t0||(c=Wr(c,e,i,s,n,r))}return c}(t.walls,this._spaceModel().rooms,l.id,i,this._wallKeyPitch,s,Ad);for(const t of sa(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,Ad)){if(t.roomId!==l.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],o=r.findIndex((t,e)=>{const i=r[(e+1)%r.length];return Cs(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});o>=0&&(n=Wr(n,t.a,t.b,e[o]||i,this._wallKeyPitch,Ad))}n=this._normalizeWalls(n,s),n.length?t.walls=n:delete t.walls}}this._recordGeometry(this._t(s?"history.split_room":"history.add_room"),e),this._saveConfig(),this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._pendingSplit=null,this._splitSel=null;const c=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let h=0;if(c){const t=Ad,e={...this._layout};for(const i of this._devices){if(i.area!==c||i.space!==this._space)continue;if(h++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/Ad,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const d=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(c?this._t("toast.room_saved",{n:d,added:h}):this._t("toast.room_saved_no_area",{n:d}))}_cancelPath(){this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._roomDialog=!1,this._pendingSplit=null,this._splitSel=null,this._mergeSel=null,this._mergeDialog=null,this._openWallAnchor=null,this._boundaryRestoreGuard=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._clearOpeningPlacement(!0)}_resumeLastDraft(){const t=this._resumeDraftBySpace[this._space];if(!t)return;const e=this._spaceModel().room_drafts.find(e=>e.id===t);e?(this._activeDraftId=t,this._path=e.points.map(t=>[...t]),this._draftSegmentCms=e.segments.map(t=>t.cm),this._cursorPt=null):delete this._resumeDraftBySpace[this._space]}_roomDialogCancel(){return this._roomDialog=!1,this._roomEditId?(this._roomEditId=null,this._nameSel="",void(this._areaSel="")):this._pendingSplit?(this._pendingSplit=null,void(this._splitSel=null)):void this._undoPoint()}get _freeAreas(){const t=new Set;for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area&&t.add(i.area);return Object.values(this.hass?.areas||{}).filter(e=>!t.has(e.area_id)).sort((t,e)=>(t.name||"").localeCompare(e.name||""))}_openMarkerDialog(t){if(this._vacAllCamerasFor=null,this._vacAllCameraCache=null,t&&this._ackNewDevice(t.id),this._norm)if(t){const e=t.marker,i=Object.prototype.hasOwnProperty.call(e||{},"is_light"),s=Object.prototype.hasOwnProperty.call(e||{},"light_entity"),n=Object.prototype.hasOwnProperty.call(e||{},"glow_color"),o=Object.prototype.hasOwnProperty.call(e||{},"value_badge"),r=Object.prototype.hasOwnProperty.call(e||{},"tap_action"),a=hs(e?.glow_color),l=this._devicePresentation(t,!0).valueBadge,c=co(this._planHass,t,this._devices),h=ho(this._planHass,t,c);this._markerDialog={devId:t.id,name:t.name,binding:"virtual"===t.bindingKind?"virtual":t.bindingKind+":"+t.bindingRef,bindingMode:"virtual"===t.bindingKind?"virtual":"ha",bindingOpen:!1,showEntities:"entity"===t.bindingKind&&!!this._fullRegistryHass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:Ti(t.marker?.display),rippleColor:Dt(t.marker?.ripple_color,""),rippleSize:Number(t.marker?.ripple_size)>0?Number(t.marker.ripple_size):3,size:Number(t.marker?.size)>0?Number(t.marker.size):1,angle:Number(t.marker?.angle)||0,tapAction:Hc(t.marker?.tap_action,t.primary?.split(".")[0]),tapActionTouched:!1,originalHasTapAction:r,originalTapAction:t.marker?.tap_action,tapHintAnnouncement:"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",controls:kn(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",lightRole:!0===e?.is_light?"always":!1===e?.is_light?"never":"auto",lightRoleTouched:!1,originalHasIsLight:i,originalIsLight:e?.is_light,lightEntity:e?.light_entity||"",lightEntityTouched:!1,originalHasLightEntity:s,originalLightEntity:e?.light_entity,glowMode:null!=a?.bri?"fixed":a?"color":"auto",glowColor:a?.c||this._fillColors.glow_light.c,glowBrightness:Math.max(1,Math.round(100*(a?.bri??1))),glowColorDrafted:!!a,glowBrightnessDrafted:null!=a?.bri,glowTouched:!1,originalHasGlowColor:n&&(!!a||null===e?.glow_color),originalGlowColor:a||(null===e?.glow_color?null:void 0),valueBadgeEnabled:o?!0===e?.value_badge?.enabled:!!l,valueBadgeSource:e?.value_badge?.source||l?.source||h,valueBadgePosition:e?.value_badge?.position||l?.position||"right",valueBadgeTouched:!1,originalHasValueBadge:o,originalValueBadge:e?.value_badge,useClimateTemp:!0===t.marker?.use_climate_temp,glowRadius:Number(t.marker?.glow_radius_cm)>0?String(this._imperial?Math.round(Number(t.marker.glow_radius_cm)/30.48*10)/10:Math.round(Number(t.marker.glow_radius_cm))/100):"",model:t.model||"",link:t.link||"",description:t.description||"",pdfs:[...t.pdfs||[]],room:t.marker?.room_id?t.space+"#@"+t.marker.room_id:t.space&&t.area?t.space+"#"+t.area:"",hideFromPlan:!0===t.marker?.hidden,busy:!1}}else this._markerDialog={name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"info",tapActionTouched:!1,originalHasTapAction:!1,originalTapAction:void 0,tapHintAnnouncement:"",tapTarget:"",tapConfirm:!1,runFilter:"",controls:[],controlsFilter:"",lightRole:"auto",lightRoleTouched:!1,originalHasIsLight:!1,originalIsLight:void 0,lightEntity:"",lightEntityTouched:!1,originalHasLightEntity:!1,originalLightEntity:void 0,glowMode:"auto",glowColor:this._fillColors.glow_light.c,glowBrightness:100,glowColorDrafted:!1,glowBrightnessDrafted:!1,glowTouched:!1,originalHasGlowColor:!1,originalGlowColor:void 0,valueBadgeEnabled:!1,valueBadgeSource:null,valueBadgePosition:"right",valueBadgeTouched:!1,originalHasValueBadge:!1,originalValueBadge:void 0,useClimateTemp:!1,glowRadius:"",model:"",link:"",description:"",pdfs:[],room:"",hideFromPlan:!1,busy:!1,uploadId:"up_"+Date.now().toString(36)+Math.random().toString(36).slice(2,6)};else this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of Ii)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&this._planEntityAvailable(i)&&t.push({value:i,label:s?.attributes?.friendly_name||i,sub:this._t("run."+e)});return t.sort((t,e)=>t.sub.localeCompare(e.sub)||t.label.localeCompare(e.label))}_bindingCandidates(){const t=this._planHass,e=Bn(this._markers),i=new Set(this._markers.filter(t=>t.removed).map(t=>t.binding)),s=new Set;for(const t of this._devices)t.id!==this._markerDialog?.devId&&("device"===t.bindingKind&&t.bindingRef&&s.add("device:"+t.bindingRef),"entity"===t.bindingKind&&t.bindingRef&&s.add("entity:"+t.bindingRef));const n=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&n.add(t.name.trim()+"|"+(t.area||""));const o=[];for(const e of Object.values(t.devices)){if("service"===e.entry_type)continue;const t="device:"+e.id;if(s.has(t))continue;const r=(e.name_by_user||e.name||e.id).trim();t!==this._markerDialog?.binding&&!i.has(t)&&n.has(r+"|"+(e.area_id||""))||o.push({value:t,label:r,sub:(e.model||this._t("marker.sub_device"))+("Group"===e.model?this._t("marker.sub_z2m_group"):"")})}const r=new Set(["group","template","derivative","min_max","threshold","integration","statistics","trend","utility_meter","tod","switch_as_x","schedule"]);for(const[n,a]of Object.entries(t.entities)){const l="entity:"+n;if(s.has(l))continue;if(Wn(t,n,e)&&!i.has(l))continue;const c=r.has(a.platform),h="group"===a.platform;if(!c&&!h)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[n];o.push({value:l,label:a.name||d?.attributes?.friendly_name||n,sub:n.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const n=new Set(o.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||n.has(l)||a.hidden&&!i.has(l))continue;if(Wn(t,r,e)&&!i.has(l))continue;const c=t.states[r],h=a.name||c?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,u=d&&(d.name_by_user||d.name)||"";o.push({value:l,label:h,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(u?" · "+u:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?o.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):o;return l.sort((t,e)=>t.label.localeCompare(e.label)),l.slice(0,200)}_physicalBodiesR(t=this._spaceModel()){const e=`${t.id}|${this._cfgEpoch}|${this._cellCm}|${this._gridPitch}`;if(this._physicalBodiesCache?.key===e)return this._physicalBodiesCache.all;const i=(t.room_drafts||[]).flatMap(t=>Da(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Ma(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),n=(t.wall_columns||[]).map(t=>Ca(t,this._cellCm,this._gridPitch)),o=[...i,...s,...n];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:n,all:o},o}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,n=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=wd(this._cleanFloorCache,n);if(t.hit)return t.value}const o=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...o),Math.min(...r),Math.max(...o),Math.max(...r)],l=this._physicalBodiesR(i).filter(t=>{const e=t.map(t=>t[0]),i=t.map(t=>t[1]);return Math.max(...e)>=a[0]&&Math.min(...e)<=a[2]&&Math.max(...i)>=a[1]&&Math.min(...i)<=a[3]}),c=l.length?Aa(e,l):null,h={floor:e,geom:c,path:c?Sa(c):"",area:Ea(c||[[[...e,e[0]]]])};return this._rszPreview||kd(this._cleanFloorCache,n,h,600),h}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";const s=this._fullRegistryHass,n=this._bindingStatus(t),o="active"===n.kind?n.enabledEntityIds:n.allEntityIds;if("device"===e){const t=s.devices?.[i];if(!t)return"mdi:help-circle";const e=o;return e.some(t=>t.startsWith("lock."))?"mdi:lock":qn(s,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=s.entities?.[i],e=this.hass.states?.[i],n=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":qn(s,n,"",[i],this._iconRules)}return""}_allRoomsFlat(){const t=[];for(const e of this._serverCfg?.spaces||[])for(const i of e.rooms||[])i.area?t.push({value:e.id+"#"+i.area,label:(e.title||e.id)+" · "+i.name}):i.id&&t.push({value:e.id+"#@"+i.id,label:(e.title||e.id)+" · "+i.name+" · "+this._t("marker.subarea")});return t}_errText(t){if(!t)return this._t("err.unknown");if("string"==typeof t)return t;if(t.message)return t.message;if(t.error)return t.error;if(null!=t.code)return this._t("err.code",{code:t.code});try{return JSON.stringify(t)}catch{return String(t)}}_backupErrorText(t){const e=t?.code??t?.error;if("string"==typeof e){const t=`backup.error.${e}`,i=this._t(t);if(i!==t)return i}return this._errText(t)}async _pickMarkerFiles(t){const e=t.target,i=e.files?[...e.files]:[];if(e.value="",!i.length||!this._markerDialog)return;const s=this._markerDialog.uploadId||this._markerDialog.devId||"new",n=[];for(const t of i)try{const e=new FormData;e.append("marker_id",s),e.append("file",t,t.name);const i=this.hass?.fetchWithAuth?await this.hass.fetchWithAuth("/api/houseplan/upload",{method:"POST",body:e}):await fetch("/api/houseplan/upload",{method:"POST",body:e,headers:this.hass?.auth?.data?.access_token?{authorization:`Bearer ${this.hass.auth.data.access_token}`}:{}}),o=await i.json().catch(()=>({}));if(!i.ok||o.error){const t={too_large:this._t("err.too_large",{mb:o.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[o.error]||o.error||"HTTP "+i.status)}n.push({name:o.name||t.name,url:o.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}n.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...n]},this._showToast(this._t("toast.files_attached",{n:n.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}_markerLightFields(t){const e={};if(t.lightRoleTouched?"always"===t.lightRole?e.is_light=!0:"never"===t.lightRole&&(e.is_light=!1):t.originalHasIsLight&&(e.is_light=t.originalIsLight??null),t.lightEntityTouched?t.lightEntity&&(e.light_entity=t.lightEntity):t.originalHasLightEntity&&(e.light_entity=t.originalLightEntity??null),t.glowTouched){if("auto"!==t.glowMode){const i={c:Dt(t.glowColor,this._fillColors.glow_light.c)};"fixed"===t.glowMode&&(i.bri=Math.max(.01,Math.min(1,Math.round(t.glowBrightness)/100))),e.glow_color=i}}else t.originalHasGlowColor&&(e.glow_color=t.originalGlowColor??null);return e}_markerTapActionFields(t){return t.tapActionTouched?{tap_action:t.tapAction||null}:t.originalHasTapAction?{tap_action:t.originalTapAction??null}:{}}async _saveMarker(){const t=this._markerDialog;if(!t||t.busy)return;const e=this._effectiveMarkerTapAction(t);if("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding)if("virtual"!==t.binding||t.name.trim())if("run"!==e||t.tapTarget)if(t.valueBadgeTouched&&t.valueBadgeEnabled&&!t.valueBadgeSource)this._showToast(this._t("toast.value_badge_source_required"));else{if("ha"===t.bindingMode){const e=this._bindingStatus(t.binding),i=t.devId?this._markers.find(e=>e.id===t.devId):null,s=!i||i.binding!==t.binding;if("active"!==e.kind&&s)return void this._showToast(this._t("ha_disabled"===e.kind?"toast.ha_disabled_add":"toast.ha_binding_unverified"));if("ha_disabled"===e.kind&&!0===i?.hidden&&!t.hideFromPlan)return this._markerDialog={...t,hideFromPlan:!0},void this._showToast(this._t("entity"===e.reason?"toast.ha_disabled_show_entity":"toast.ha_disabled_show_device"))}this._markerDialog={...t,busy:!0};try{const i=this._serverCfg;let s;i.markers=i.markers||[];const n=Ps(t.room);let o=n?.space||null,r=n?.area||null;const a=n?.roomId||null;"virtual"!==t.binding||o||(o=this._space),s=$i(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const l=t.devId,c="virtual"===t.binding?[]:i.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=c.length>0,d=kn(t.binding,t.controls,this._bindingEntities(t.binding)),u=i.markers.find(t=>t.id===s||t.id===l)?.vacuum||null,p={id:s,vacuum:u,binding:t.binding,name:t.name.trim()||null,icon:t.icon||null,display:"badge"!==t.display?t.display:null,ripple_color:"icon_ripple"===t.display&&t.rippleColor?t.rippleColor:null,ripple_size:"icon_ripple"===t.display&&3!==t.rippleSize?t.rippleSize:null,size:1!==t.size?t.size:null,angle:t.angle?t.angle:null,...this._markerTapActionFields(t),tap_target:"run"===e&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:d.length?d:null,...this._markerLightFields(t),...this._markerValueBadgeFields(t),use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=bd(t.glowRadius);return null==e||e<=0?null:Math.round(this._imperial?30.48*e:100*e)})(),model:t.model.trim()||null,link:t.link.trim()||null,description:t.description.trim()||null,pdfs:t.pdfs,hidden:!!t.hideFromPlan};("virtual"===t.binding||t.room)&&(p.space=o,p.area=r,p.room_id=a);const _=l?this._devices.find(t=>t.id===l):null,m=_?.marker?.room_id??null,g=!!t.room&&null!=_&&(_.space!==o||_.area!==r||m!==a);let f=!1;const v=t.uploadId||l;if(v&&v!==s&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:v,to_id:s}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const n="/files/"+e+"/",o="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(n))return t;const e=t.url.split(n)[1]||"",[i,r]=[e.split("?")[0],e.includes("?")?"?"+e.split("?")[1]:""];if(s){const e=s[decodeURIComponent(i)]??s[i];return e?{...t,url:t.url.split(n+i)[0]+o+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(n).join(o)}})}(p.pdfs,v,s,e),f=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}l&&l!==s&&(i.markers=function(t,e,i){if(!e||!i||e===i)return[...t];const s=`marker:${e}`,n=`marker:${i}`;return t.map(t=>{const e=Array.isArray(t.controls)?t.controls.map(t=>t===s?n:t):t.controls,i="derived_marker_state"===t.value_badge?.source?.kind&&t.value_badge.source.ref===s?{...t.value_badge,source:{...t.value_badge.source,ref:n}}:t.value_badge;return e===t.controls&&i===t.value_badge?t:{...t,controls:e,value_badge:i}})}(i.markers,l,s)),l&&l!==s&&"derived_marker_state"===p.value_badge?.source?.kind&&p.value_badge.source.ref===`marker:${l}`&&(p.value_badge.source={kind:"derived_marker_state",ref:`marker:${s}`}),i.markers=i.markers.filter(t=>t.id!==s&&t.id!==l&&("virtual"===p.binding||t.binding!==p.binding)),i.markers.push(p);let y=null;const b=o||_?.space||this._space,w=l?this._layout[l]:null,k=w?{s:w.s||_?.space||this._space,x:w.x,y:w.y}:l&&_&&this._defPos[l]?this._normPos(_.space,this._defPos[l].x,this._defPos[l].y):null;if(!h&&k&&k.s===b)s===l&&this._layout[s]&&!g||(y={s:k.s,x:k.x,y:k.y},this._layout={...this._layout,[s]:y});else if(h||!this._layout[s]||g){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,i=t.vb[1]+t.vb[3]/2;const n=a?t.rooms.find(t=>t.id===a):r?t.rooms.find(t=>t.area===r):void 0;n&&([e,i]=this._roomCenter(n)),y=this._normPos(o||this._space,e,i),this._layout={...this._layout,[s]:y}}await this._saveConfigNow(),y&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:s,pos:y}));const $=new Set(c);l&&l!==s&&$.add(l),$.delete(s);for(const t of $)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});f&&v&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:v}).catch(()=>{}),this._markerDialog=null,this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_saved"))}catch(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}else this._showToast(this._t("toast.run_target_required"));else this._showToast(this._t("toast.virtual_name_required"))}async _deleteMarker(){const t=this._markerDialog;if(!t||t.busy||!t.devId)return;const e=t.devId?this._devices.find(e=>e.id===t.devId):null;if(!e)return;const i=t.name||this._t("device.fallback");if(!confirm(this._t("confirm.remove_marker",{name:i})))return;const s=this._serverCfg;s.markers=s.markers||[];const n=s.markers,o="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!o)return;const r=function(t,e,i,s){const n=new Set([e]),o=t.filter(t=>{const o=t.id===e||!s&&t.binding===i;return o&&n.add(t.id),!o});return{markers:s?o:[...o,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:n}}(s.markers,e.id,o,"virtual"===e.bindingKind);var a,l;s.markers=(a=r.markers,l=r.cleanupIds,a.map(t=>{if(!Array.isArray(t.controls))return t;const e=t.controls.filter(t=>!("string"==typeof t&&t.startsWith("marker:")&&l.has(t.slice(7))));return e.length===t.controls.length?t:{...t,controls:e.length?e:null}}));const c=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of c){delete this._layout[t],delete this._defPos[t],this._dirtyPos.delete(t),this._sentPos.delete(t);const e=this._activityRt.get(t);e&&clearTimeout(e.timer),this._activityRt.delete(t),this._vacRt.delete(t),delete this._vacSrvTrails[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{}),await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:t}).catch(()=>{}),await this.hass.callWS({type:"houseplan/trail/delete",marker_id:t}).catch(()=>{})}this._markerDialog=null,this._infoCard?.id===e.id&&this._closeInfoCard(),this._selId===e.id&&(this._selId=null),this._drag?.id===e.id&&(this._drag=null),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.marker_removed"))}catch(t){this._serverCfg===s&&(s.markers=n),this._markerDialog&&(this._markerDialog={...this._markerDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}_normPos(t,e,i){return{s:t,x:e/Ad,y:i/Ad}}_openSpaceDialog(t,e){if(this._serverStorage&&this._serverCfg)if("edit"===t){const i=this._serverCfg.spaces.find(t=>t.id===e);if(!i)return;const s=Zi(i),n=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?Yi(i.settings.custom_fill):null,o="none"===s.fill?{...n||Ki,a:0}:n;this._spaceDialog={mode:t,spaceId:e,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:s.showBorders,showNames:s.showNames,hideDecor:s.hideDecor,hideOpenings:s.hideOpenings,roomColor:s.color,roomOpacity:s.opacity,fillMode:"none"===s.fill?"custom":s.fill,customFill:o,glowEnabled:s.glow,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:lr({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:s.tempMin,tempMax:s.tempMax,showLqi:s.showLqi??this._config?.show_signal??!0,cardFontScale:s.cardFontScale,labelTemp:s.labelTemp,labelHum:s.labelHum,labelLqi:s.labelLqi,labelLight:s.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,busy:!1}}else this._spaceDialog={mode:t,title:"",planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:Gi,roomOpacity:Vi,fillMode:"custom",customFill:{...Ki,a:0},glowEnabled:!0,bgColor:null,bgMode:null,northDeg:null,sunRays:null,tempMin:20,tempMax:25,showLqi:this._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:5,busy:!1};else this._showToast(this._t("toast.integration_missing"))}async _pickPlanFile(t){const e=t.target,i=e.files?.[0];if(!i||!this._spaceDialog)return;const s={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/webp":"webp"}[i.type]||(i.name.toLowerCase().endsWith(".svg")?"svg":"");if(!s)return void this._showToast(this._t("toast.plan_formats"));const n=new Uint8Array(await i.arrayBuffer());let o="";for(let t=0;t{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:1.414),e.onerror=()=>t(1.414),e.src=a});URL.revokeObjectURL(a),this._spaceDialog={...this._spaceDialog,planFile:{ext:s,b64:r,aspect:l,name:i.name}}}_useServerPlan(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let e=0;e<40;e++){const e=this._display(t);if(e){const i=await new Promise(t=>{const i=new Image;i.onload=()=>t(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:0),i.onerror=()=>t(0),i.src=e}),s=this._spaceDialog;return s&&s.planUrl===t&&Number.isFinite(i)&&i>0?(this._spaceDialog={...s,savedAspect:i},i):0}if(await new Promise(t=>setTimeout(t,150)),this._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this._t("confirm.delete_plan",{name:t})))try{await this.hass.callWS({type:"houseplan/plans/delete",name:t});const e=this._spaceDialog;e?.saved&&(this._spaceDialog={...e,saved:e.saved.filter(e=>e.name!==t)})}catch(t){this._showToast(this._t("toast.plan_delete_failed",{err:this._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return W`
${this._t("space.loading")}
`;const e=t.saved||[];if(!e.length)return W`
${this._t("space.no_saved")}
`;return W`
${e.map(e=>W`
@@ -5313,13 +5313,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31 `})} `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return U``}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._renderEntityAvailable(t.contact)?this._renderPlanHass.states[t.contact]?.state:null;return oi(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!Wn(this._fullRegistryHass,t,Bn(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderEntityAvailable(t){return!!t&&(!Wn(this._renderPlanHass,t,Bn(this._markers))&&(!!this._renderPlanHass.entities?.[t]&&!!this._renderPlanHass.states?.[t]))}_renderOpeningPlacementPreview(){const t=this._openingPreview;if(!t)return U``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:oi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:t.face};return U``}_roomCenter(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}_openingAmt(t){const e=t.contact&&this._renderOpeningEntityAvailable(t.contact)?this._renderPlanHass.states[t.contact]?.state:null;return oi(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&(!Wn(this._fullRegistryHass,t,Bn(this._markers))&&"active"===this._bindingStatus("entity:"+t).kind)}_renderEntityAvailable(t){return!!t&&(!Wn(this._renderPlanHass,t,Bn(this._markers))&&(!!this._renderPlanHass.entities?.[t]&&!!this._renderPlanHass.states?.[t]))}_openingEntityAvailable(t){return function(t,e,i=on(t)){return!!e&&"active"===pn(t,`entity:${e}`,i).kind}(this.hass,t,this._haRegistry)}_renderOpeningEntityAvailable(t){return e=this._renderPlanHass,!!(i=t)&&!!e?.entities?.[i]&&!!e?.states?.[i];var e,i}_renderOpeningPlacementPreview(){const t=this._openingPreview;if(!t)return U``;const e={type:t.type,length:t.renderedLength,angle:t.angle,amount:oi(t.type,null),flipH:t.flipH,flipV:t.flipV,base:"var(--hp-open)",tone:"var(--hp-open)",cellCm:this._cellCm,gridPitch:this._gridPitch,face:t.face};return U` `}_renderOpenings(t){const e=this._openingsR;if(!e.length)return U``;const i=t.color,s=this._spaceWalls,n=this._openPairs().flatMap(t=>t.segs),o=this._openingWallIndexFor(this._spaceModel(),n).value;return U`${e.map(t=>{const e=this._openingAmt(t),n=e>0&&!!t.contact&&this._renderEntityAvailable(t.contact)?"var(--hp-open)":i,r="gate"===t.type?!t.flip_v:t.flip_v,a=s.length||"gate"===t.type?fa(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:r}):{ox:0,oy:0,cm:0,side:-1},l={type:t.type,length:t.rlen,angle:t.angle,amount:e,flipH:!!t.flip_h,flipV:!!t.flip_v,base:i,tone:n,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=od(l);return U``}_renderOpenings(t){const e=this._openingsR;if(!e.length)return U``;const i=t.color,s=this._spaceWalls,n=this._openPairs().flatMap(t=>t.segs),o=this._openingWallIndexFor(this._spaceModel(),n).value;return U`${e.map(t=>{const e=this._openingAmt(t),n=e>0&&!!t.contact&&this._renderOpeningEntityAvailable(t.contact)?"var(--hp-open)":i,r="gate"===t.type?!t.flip_v:t.flip_v,a=s.length||"gate"===t.type?fa(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:r}):{ox:0,oy:0,cm:0,side:-1},l={type:t.type,length:t.rlen,angle:t.angle,amount:e,flipH:!!t.flip_h,flipV:!!t.flip_v,base:i,tone:n,cellCm:this._cellCm,gridPitch:this._gridPitch,face:a},{half:c,outlineHalf:h,hitHalf:d}=od(l);return U` ${rd(l)} @@ -5329,11 +5329,11 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="37bffbb1a48700f48ca9db30d52a7d537b31 @pointermove=${e=>this._opPointerMove(e,t)} @pointerup=${e=>this._opPointerUp(e,t)} @pointercancel=${e=>this._opPointerUp(e,t)}> - `})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._renderEntityAvailable(t.lock));if(!e.length)return W``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return W`${e.map(e=>{const i=this._renderPlanHass.states[e.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?fa(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,u=(h-t.y)/t.h*100;return W`
`})}`}_renderOpeningLocks(t){const e=this._openingsR.filter(t=>"window"!==t.type&&t.lock&&this._renderOpeningEntityAvailable(t.lock));if(!e.length)return W``;const i=this._openPairs().flatMap(t=>t.segs),s=this._openingWallIndexFor(this._spaceModel(),i).value;return W`${e.map(e=>{const i=this._renderPlanHass.states[e.lock]?.state,n="locked"===i,o=n||["unlocked","open","opening","unlocking","locking"].includes(String(i)),r=(e.angle+90)*Math.PI/180,a="gate"===e.type?fa(s,{x:e.rx,y:e.ry,angle:e.angle,length:e.rlen,flip_v:!e.flip_v}):null,l=a?-16*a.side:16*(e.flip_v?-1:1),c=e.rx+Math.cos(r)*l,h=e.ry+Math.sin(r)*l,d=(c-t.x)/t.w*100,u=(h-t.y)/t.h*100;return W`
{t.stopPropagation(),"view"===this._mode&&(this._openingInfo=e)}}> -
`})}`}_lockAction(t,e){if(this._planEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._planEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._planEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>W`
${e}${i}
`;return W``})}`}_lockAction(t,e){if(this._openingEntityAvailable(t)){if("unlock"===e){const e=this.hass?.states?.[t]?.attributes?.friendly_name||t;if(!confirm(this._t("confirm.unlock",{name:e})))return}this.hass?.callService?.("lock",e,{entity_id:t})}}_renderOpeningInfoCard(){const t=this._openingInfo,e=t.contact&&this._openingEntityAvailable(t.contact)?t.contact:null,i=t.lock&&this._openingEntityAvailable(t.lock)?t.lock:null,s=e?this.hass.states[e]?.state:null,n=this._openingAmt(t),o=i?this.hass.states[i]?.state:null,r="door"===t.type?"opening.door":"gate"===t.type?"opening.gate":"opening.window",a="door"===t.type?"mdi:door":"gate"===t.type?"mdi:gate":"mdi:window-closed-variant",l="gate"===t.type?n>0?"mdi:gate-open":"mdi:gate":n>0?"mdi:door-open":"mdi:door-closed",c=(t,e,i,s="")=>W`
${e}${i}
`;return W`this._openingInfo=null}>
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 20b627eb..be6108f7 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -245,7 +245,13 @@ HA binding against automatic discovery while intentionally exposing that same binding to the Add picker. A device tombstone excludes all data of that device; an entity tombstone excludes the standalone entity binding but does not mutate the same entity out of a still-live parent device. Runtime-filtered references -such as `controls` remain persisted and become active again after re-add. +such as `controls` and live text remain persisted and become active again after +re-add. Exact `opening.contact` / `opening.lock` fields are a separate +architectural-object role: their HA availability ignores marker tombstones but +still uses `resolveHaBindingStatus()` to reject disabled, orphaned or unverified +entities. Their painted state comes from the immutable active-registry frame, +not directly from live `hass`. Re-adding a marker therefore cannot duplicate or +rewrite an opening reference. Re-adding replaces the tombstone; virtual markers need no tombstone because they have no discovery source. @@ -413,6 +419,12 @@ Its default width in the editor is 300 cm. `openingAmount` (pure) maps the conta drag along walls (continuous re-snap, saved on release), click → status card (250 ms timer), double click → properties dialog. In markup mode the "Opening" tool handles clicks instead. +Contact and lock are exact HA references owned by the opening, not aliases of +standalone markers. Their candidate/action path follows HA binding status while +their render path follows the frozen active-registry projection; neither path +consults marker tombstones. Plan-level consumers keep the tombstone policy +described above. + For a wall with thickness, one `OpeningWallIndex` resolves the atomic wall interval and adjacent room on each side of the centreline. Opening symbols, wall cuts and room-coloured tunnel patches all consume this association; none diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8233917d..498baf8d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Deleting a standalone sensor or lock marker no longer breaks its explicit + door, window or gate binding: the entity stays available in opening settings + and continues to drive its state without restoring the marker on the plan + ([#104](https://github.com/Matysh/houseplan-card/issues/104)). - Fixed a crash after deleting the last space: the supported empty state now stays usable so a new space can be added ([#111](https://github.com/Matysh/houseplan-card/issues/111)). diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index 68d69458..ffbaf9aa 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -8,6 +8,10 @@ ## Unreleased +- Удаление самостоятельного маркера датчика или замка больше не разрывает его + явную привязку к двери, окну или воротам: сущность остаётся доступной в + настройках проёма и продолжает показывать состояние, не возвращая маркер на + план ([#104](https://github.com/Matysh/houseplan-card/issues/104)). - Исправлено падение после удаления последнего пространства: предусмотренное пустое состояние остаётся рабочим, и из него можно добавить новое пространство ([#111](https://github.com/Matysh/houseplan-card/issues/111)). diff --git a/docs/FILTERING.md b/docs/FILTERING.md index 265f8e7f..34c00570 100644 --- a/docs/FILTERING.md +++ b/docs/FILTERING.md @@ -104,10 +104,15 @@ the old behaviour until an editing client materialises it. standalone plan object; it does not remove the same entity from the data of a still-live parent HA device. Tombstones are binding-scoped, not mutations of the HA registry. -- References in openings, live text and another marker's persisted `controls` - are retained but become inactive (no lock/contact badge, `—`, or control - action respectively). Adding the binding again restores them without - reconstructing configuration or performing an unrelated Save first. +- Exact `opening.contact` and `opening.lock` references are independent of a + standalone plan marker: deleting that marker does not remove the entity from + the opening picker and does not stop the saved opening from following its HA + state. The tombstone still suppresses every marker-level contribution and is + not removed or rewritten by choosing the entity in an opening. +- References in live text and another marker's persisted `controls` are + retained but become inactive (`—` or no control action respectively). + Adding the binding again restores those references without reconstructing + configuration or performing an unrelated Save first. - The same inactive rule applies while a saved binding is HA-disabled. The marker, room metrics, Glow/light fill, registry-wide climate, live text, openings, controls, vacuum puck and trails all ignore it. Reactivation of the diff --git a/docs/STATUS.md b/docs/STATUS.md index 3bd44579..ca1435a1 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -98,8 +98,10 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate. - **True plan deletion** (v1.60.0-beta.1, 2026-08-07): confirmed Delete beside Hide/Show; a minimal `marker.removed` tombstone prevents auto-rediscovery but exposes the binding to Add. Deleted devices are absent from every plan aggregate and - linked presentation; layout/files/trails are cleaned and stale layout writes - are rejected. + linked marker presentation; layout/files/trails are cleaned and stale layout + writes are rejected. Exact contact/lock references owned by architectural + openings remain active without restoring the standalone marker; live text + and other marker controls retain the older re-add-to-reactivate contract. - **Yellow = working right now** (v1.51.0): climate uses `hvac_action` when available and falls back to a current advertised non-off HVAC mode only when the integration omits the action; service switches can no longer become diff --git a/docs/USER-GUIDE.ru.md b/docs/USER-GUIDE.ru.md index 7edb73ac..3d21b4b3 100644 --- a/docs/USER-GUIDE.ru.md +++ b/docs/USER-GUIDE.ru.md @@ -392,7 +392,11 @@ Glow и солнечные лучи, но при Resize комнаты оста | Открывается в другую сторону | Да | Да | Да; по умолчанию — наружу | | Замок `lock.*` | Да | Нет | Да | -Контакт выбирается из подходящих `binary_sensor` и дверных `cover`. Состояние датчика анимирует створку/полотно. Инверсия нужна, если интеграция отдаёт противоположную логику. +Контакт выбирается из подходящих `binary_sensor` и дверных `cover`. Состояние +датчика анимирует створку/полотно. Инверсия нужна, если интеграция отдаёт +противоположную логику. Контактный датчик и замок — самостоятельные точные +привязки проёма: удаление отдельного маркера той же сущности с плана не убирает +её из этих списков и не выключает уже сохранённую связь. ### Поведение в разных режимах @@ -491,10 +495,13 @@ House Plan читает реестры устройств, сущностей и Удаление и скрытие — разные операции. Скрытый маркер можно показать через **Скрытые и деактивированные**, и он продолжает участвовать в предусмотренных для скрытых устройств данных. Удалённый маркер там не показывается, не участвует в LQI, -температуре, влажности, свете, Glow, карточке комнаты, управлении и живых -ссылках плана. Его привязка снова появляется в списке **Добавить**. Повторное -добавление создаёт новую позицию; сохранённая конфигурация проёмов и текстовых -переменных снова оживает. Позиция, вложения и след пылесоса удаляются. +температуре, влажности, свете, Glow, карточке комнаты, управлении, текстовых +переменных и внешних controls других маркеров. Его привязка снова появляется в +списке **Добавить**. Контактный датчик или замок, явно выбранный в свойствах +проёма, остаётся отдельной рабочей связью и не восстанавливает удалённый маркер. +Повторное добавление создаёт маркеру новую позицию и снова оживляет сохранённые +текстовые переменные и controls, но не дублирует и не переписывает связь проёма. +Позиция, вложения и след пылесоса при удалении маркера удаляются. ### Скрытые устройства diff --git a/src/ha-binding-status.ts b/src/ha-binding-status.ts index 0b45338a..b57c7f6c 100644 --- a/src/ha-binding-status.ts +++ b/src/ha-binding-status.ts @@ -515,6 +515,35 @@ export function resolveHaBindingStatus( return { kind: 'unverified', reason: 'registry_unavailable', enabledEntityIds: [], allEntityIds: [] }; } +/** + * Availability of an exact entity explicitly assigned to an architectural + * opening. This is deliberately independent of plan-marker tombstones: the + * opening is its own configured consumer of the HA entity. Registry status + * still rejects disabled, orphaned and unverified references. + */ +export function openingEntityAvailable( + hass: any, + entityId: string | null | undefined, + snapshot = haRegistrySnapshot(hass), +): boolean { + return !!entityId + && resolveHaBindingStatus(hass, `entity:${entityId}`, snapshot).kind === 'active'; +} + +/** + * Frame-local counterpart of `openingEntityAvailable`. The caller supplies an + * immutable active-registry projection, so both the registry row and state + * must belong to the same painted frame. Registry-less render parity is #117. + */ +export function renderOpeningEntityAvailable( + projectedHass: any, + entityId: string | null | undefined, +): boolean { + return !!entityId + && !!projectedHass?.entities?.[entityId] + && !!projectedHass?.states?.[entityId]; +} + export function haRegistryDiagnostics(hass: any): { access: HaRegistrySnapshot['access']; authoritative: boolean; revision: number; lastSuccess: number; error?: string; diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts index 7a03f479..324f2ec6 100755 --- a/src/houseplan-card.ts +++ b/src/houseplan-card.ts @@ -175,7 +175,8 @@ import { import { acquireHaRegistries, activeRegistryHass, cacheHaBindingStatuses, fullRegistryHass, haRegistryBuildSignature, haRegistryDiagnostics, haRegistrySnapshot, - refreshHaRegistries, resolveHaBindingStatus, + openingEntityAvailable, refreshHaRegistries, renderOpeningEntityAvailable, + resolveHaBindingStatus, type HaBindingStatus, type HaRegistrySnapshot, } from './ha-binding-status'; import type { DecorShape, DecorStyle } from './editors/decor/types'; @@ -10362,7 +10363,7 @@ class HouseplanCard extends LitElement { private _contactCandidates(): { value: string; label: string }[] { const out: [string, string, number][] = []; for (const eid of Object.keys(this.hass.states)) { - if (!this._planEntityAvailable(eid)) continue; + if (!this._openingEntityAvailable(eid)) continue; const dom = eid.split('.')[0]; if (dom !== 'binary_sensor' && dom !== 'cover') continue; const st = this.hass.states[eid]; @@ -10378,7 +10379,7 @@ class HouseplanCard extends LitElement { private _lockCandidates(): { value: string; label: string }[] { return Object.keys(this.hass.states) - .filter((eid) => eid.startsWith('lock.') && this._planEntityAvailable(eid)) + .filter((eid) => eid.startsWith('lock.') && this._openingEntityAvailable(eid)) .map((eid) => ({ value: eid, label: this.hass.states[eid]?.attributes?.friendly_name || eid })) .sort((a, b) => a.label.localeCompare(b.label)); } @@ -15512,7 +15513,7 @@ class HouseplanCard extends LitElement { /** Live state of an opening's contact, 0..1 drawn amount. */ private _openingAmt(o: OpeningCfg): number { - const st = o.contact && this._renderEntityAvailable(o.contact) + const st = o.contact && this._renderOpeningEntityAvailable(o.contact) ? this._renderPlanHass.states[o.contact]?.state : null; return openingAmount(o.type, st, !!o.invert); @@ -15532,6 +15533,16 @@ class HouseplanCard extends LitElement { return !!this._renderPlanHass.entities?.[eid] && !!this._renderPlanHass.states?.[eid]; } + /** Exact opening references ignore marker tombstones but keep HA status. */ + private _openingEntityAvailable(eid: string | null | undefined): boolean { + return openingEntityAvailable(this.hass, eid, this._haRegistry); + } + + /** Opening availability from the same immutable projection as the frame. */ + private _renderOpeningEntityAvailable(eid: string | null | undefined): boolean { + return renderOpeningEntityAvailable(this._renderPlanHass, eid); + } + /** * Doors, windows and gates, drawn in plan (SVG) coordinates so they scale and * pan with the plan. Doors/windows follow the easy-floorplan (MIT) symbol @@ -15571,7 +15582,7 @@ class HouseplanCard extends LitElement { const openingWallIndex = this._openingWallIndexFor(this._spaceModel(), openCuts).value; return svg`${items.map((o) => { const amt = this._openingAmt(o); - const active = amt > 0 && !!o.contact && this._renderEntityAvailable(o.contact); + const active = amt > 0 && !!o.contact && this._renderOpeningEntityAvailable(o.contact); const tone = active ? 'var(--hp-open)' : base; // A normal door/window uses the selected room-side face. A gate's // architectural symbol sits and opens on the opposite (exterior) face; @@ -15617,7 +15628,7 @@ class HouseplanCard extends LitElement { /** Padlock badges for door-like openings with a lock entity. */ private _renderOpeningLocks(view: { x: number; y: number; w: number; h: number }): TemplateResult { const items = this._openingsR.filter( - (o) => o.type !== 'window' && o.lock && this._renderEntityAvailable(o.lock), + (o) => o.type !== 'window' && o.lock && this._renderOpeningEntityAvailable(o.lock), ); if (!items.length) return html``; const openCuts = this._openPairs().flatMap((pair) => pair.segs); @@ -15662,7 +15673,7 @@ class HouseplanCard extends LitElement { // button is a deliberate, labeled control inside an opened card, the same // contract as Home Assistant's own more-info dialog. Unlocking additionally // asks for confirmation; locking does not (locking is never destructive). - if (!this._planEntityAvailable(entityId)) return; + if (!this._openingEntityAvailable(entityId)) return; if (action === 'unlock') { const name = this.hass?.states?.[entityId]?.attributes?.friendly_name || entityId; if (!confirm(this._t('confirm.unlock', { name }))) return; @@ -15672,8 +15683,8 @@ class HouseplanCard extends LitElement { private _renderOpeningInfoCard(): TemplateResult { const o = this._openingInfo!; - const contact = o.contact && this._planEntityAvailable(o.contact) ? o.contact : null; - const lock = o.lock && this._planEntityAvailable(o.lock) ? o.lock : null; + const contact = o.contact && this._openingEntityAvailable(o.contact) ? o.contact : null; + const lock = o.lock && this._openingEntityAvailable(o.lock) ? o.lock : null; const cSt = contact ? this.hass.states[contact]?.state : null; const amt = this._openingAmt(o); const lSt = lock ? this.hass.states[lock]?.state : null; diff --git a/test/ha-binding-status.test.mjs b/test/ha-binding-status.test.mjs index a6f3e789..05c7062c 100644 --- a/test/ha-binding-status.test.mjs +++ b/test/ha-binding-status.test.mjs @@ -6,6 +6,8 @@ import { activeRegistryHass, cacheHaBindingStatuses, haRegistrySnapshot, + openingEntityAvailable, + renderOpeningEntityAvailable, resolveHaBindingStatus, } from '../test-build/ha-binding-status.js'; @@ -61,6 +63,8 @@ test('limited registry never guesses missing rows are disabled or orphaned', () assert.equal(resolveHaBindingStatus(hass, 'entity:switch.live_only', snapshot).kind, 'active'); assert.equal(resolveHaBindingStatus(hass, 'entity:switch.unknown', snapshot).kind, 'unverified'); assert.equal(resolveHaBindingStatus(hass, 'device:unknown', snapshot).kind, 'unverified'); + assert.equal(openingEntityAvailable(hass, 'switch.live_only', snapshot), true); + assert.equal(openingEntityAvailable(hass, 'switch.unknown', snapshot), false); }); test('authoritative registry accepts an exact live YAML entity without a registry row', () => { @@ -170,9 +174,14 @@ test('limited live rows cannot override the last authoritative disabled result', getItem: () => '{}', setItem: () => undefined, }; - cacheHaBindingStatuses(new Map([['device:cached-disabled', { - kind: 'ha_disabled', reason: 'all_entities', enabledEntityIds: [], allEntityIds: ['switch.cached'], - }]])); + cacheHaBindingStatuses(new Map([ + ['device:cached-disabled', { + kind: 'ha_disabled', reason: 'all_entities', enabledEntityIds: [], allEntityIds: ['switch.cached'], + }], + ['entity:switch.cached', { + kind: 'ha_disabled', reason: 'entity', enabledEntityIds: [], allEntityIds: ['switch.cached'], + }], + ])); const devices = { 'cached-disabled': { id: 'cached-disabled', disabled_by: null } }; const entities = { 'switch.cached': { entity_id: 'switch.cached', device_id: 'cached-disabled', disabled_by: null }, @@ -181,6 +190,7 @@ test('limited live rows cannot override the last authoritative disabled result', assert.deepEqual(resolveHaBindingStatus(hass, 'device:cached-disabled', limited(devices, entities)), { kind: 'ha_disabled', reason: 'all_entities', enabledEntityIds: [], allEntityIds: ['switch.cached'], }); + assert.equal(openingEntityAvailable(hass, 'switch.cached', limited(devices, entities)), false); globalThis.localStorage = previousStorage; }); @@ -193,3 +203,63 @@ test('entity with a missing parent reports device_missing', () => { 'device_missing', ); }); + +test('opening entity availability ignores marker lifecycle and follows exact HA status', () => { + const devices = { + active: { id: 'active', disabled_by: null }, + disabled: { id: 'disabled', disabled_by: 'user' }, + }; + const entities = { + 'binary_sensor.door': { + entity_id: 'binary_sensor.door', device_id: 'active', disabled_by: null, + }, + 'lock.front': { entity_id: 'lock.front', device_id: 'active', disabled_by: null }, + 'lock.disabled_entity': { + entity_id: 'lock.disabled_entity', device_id: 'active', disabled_by: 'user', + }, + 'lock.disabled_parent': { + entity_id: 'lock.disabled_parent', device_id: 'disabled', disabled_by: null, + }, + 'lock.orphan': { entity_id: 'lock.orphan', device_id: 'missing', disabled_by: null }, + }; + const hass = { + devices, entities, + states: { + 'binary_sensor.door': { state: 'unavailable' }, + 'lock.front': { state: 'locked' }, + 'lock.disabled_entity': { state: 'locked' }, + 'lock.disabled_parent': { state: 'locked' }, + 'lock.orphan': { state: 'locked' }, + }, + }; + const snapshot = full(devices, entities); + + // Marker lifecycle is intentionally not an input: the opening is an exact + // independent consumer before deletion, after either tombstone and re-add. + for (const _markerLifecycle of ['present', 'entity-tombstone', 'device-tombstone', 're-added']) { + assert.equal(openingEntityAvailable(hass, 'binary_sensor.door', snapshot), true); + assert.equal(openingEntityAvailable(hass, 'lock.front', snapshot), true); + } + assert.equal(openingEntityAvailable(hass, 'lock.disabled_entity', snapshot), false); + assert.equal(openingEntityAvailable(hass, 'lock.disabled_parent', snapshot), false); + assert.equal(openingEntityAvailable(hass, 'lock.orphan', snapshot), false); + assert.equal(openingEntityAvailable(hass, 'lock.missing', snapshot), false); + assert.equal(openingEntityAvailable(hass, null, snapshot), false); +}); + +test('opening render availability requires one frozen registry-backed frame', () => { + const frame = { + entities: { + 'binary_sensor.door': { entity_id: 'binary_sensor.door' }, + 'lock.no_state': { entity_id: 'lock.no_state' }, + }, + states: { + 'binary_sensor.door': { state: 'unknown' }, + 'binary_sensor.yaml_only': { state: 'on' }, + }, + }; + assert.equal(renderOpeningEntityAvailable(frame, 'binary_sensor.door'), true); + assert.equal(renderOpeningEntityAvailable(frame, 'lock.no_state'), false); + assert.equal(renderOpeningEntityAvailable(frame, 'binary_sensor.yaml_only'), false, '#117 owns YAML parity'); + assert.equal(renderOpeningEntityAvailable(frame, ''), false); +}); diff --git a/test/render-device-snapshot.test.mjs b/test/render-device-snapshot.test.mjs index accd16d7..f7893438 100644 --- a/test/render-device-snapshot.test.mjs +++ b/test/render-device-snapshot.test.mjs @@ -63,7 +63,7 @@ test('RenderDeviceSnapshot keeps immutable facts and excludes live HA capabiliti }); const methodBody = (source, name) => { - const start = source.indexOf(`private ${name}(`); + const start = source.search(new RegExp(`private\\s+(?:async\\s+)?${name}\\(`)); assert.notEqual(start, -1, `${name} exists`); const tail = source.slice(start + 1); const next = tail.search(/\n (?:private|protected|public)\s/); @@ -95,3 +95,44 @@ test('the card gates snapshot positions on the render model', () => { 'the previous unconditional position path must not return', ); }); + +test('opening references use their own availability policy without weakening plan tombstones', () => { + const source = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8'); + for (const name of [ + '_contactCandidates', '_lockCandidates', '_openingAmt', '_renderOpenings', + '_renderOpeningLocks', '_renderOpeningInfoCard', '_lockAction', + ]) { + assert.match( + methodBody(source, name), + /_openingEntityAvailable|_renderOpeningEntityAvailable/, + `${name} uses the explicit opening-reference policy`, + ); + } + const planAvailability = methodBody(source, '_planEntityAvailable'); + const renderAvailability = methodBody(source, '_renderEntityAvailable'); + assert.match(planAvailability, /isRemovedPlanEntity/); + assert.match(renderAvailability, /isRemovedPlanEntity/); + assert.doesNotMatch(planAvailability, /openingEntityAvailable/); + assert.doesNotMatch(renderAvailability, /renderOpeningEntityAvailable/); +}); + +test('lock actuation remains guarded inside the one sanctioned opening-card method', () => { + const source = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8'); + const action = methodBody(source, '_lockAction'); + const guardAt = action.indexOf('_openingEntityAvailable(entityId)'); + const confirmAt = action.indexOf('confirm('); + const serviceAt = action.indexOf("callService?.('lock'"); + assert.ok(guardAt >= 0 && guardAt < confirmAt && confirmAt < serviceAt); + assert.equal(source.match(/callService\?\.\('lock'/g)?.length, 1); + assert.equal(source.match(/this\._lockAction\(/g)?.length, 1, 'only the opening card button calls it'); +}); + +test('marker delete/re-add and opening save remain separate config transactions', () => { + const source = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8'); + const saveOpening = methodBody(source, '_saveOpening'); + const saveMarker = methodBody(source, '_saveMarker'); + assert.match(saveOpening, /sp\.openings/); + assert.doesNotMatch(saveOpening, /cfg\.markers|this\._markers/); + assert.match(saveMarker, /cfg\.markers/); + assert.doesNotMatch(saveMarker, /\.openings/); +});