From f1537b210879038a940efe3b82619f54702ccabc Mon Sep 17 00:00:00 2001 From: Matysh Date: Sat, 8 Aug 2026 00:12:47 +0300 Subject: [PATCH] v1.60.2-beta.2: harden device state and editor interactions --- custom_components/houseplan/const.py | 2 +- .../houseplan/frontend/houseplan-card.js | 199 ++++++++++-------- custom_components/houseplan/manifest.json | 2 +- demo/smoke_card_controls.mjs | 1 + demo/smoke_decor.mjs | 25 +++ demo/smoke_editor_tabs.mjs | 11 + demo/smoke_merge_split.mjs | 2 +- demo/srv/assets/houseplan-card.js | 199 ++++++++++-------- dist/houseplan-card.js | 199 ++++++++++-------- docs/ARCHITECTURE.md | 10 +- docs/CHANGELOG.md | 17 ++ docs/CHANGELOG.ru.md | 18 ++ docs/FILTERING.md | 8 +- docs/STATUS.md | 6 +- docs/TESTING-DEMO.md | 4 +- docs/USER-GUIDE.ru.md | 3 +- package-lock.json | 4 +- package.json | 2 +- src/device-visual.ts | 49 +++++ src/devices.ts | 18 +- src/houseplan-card.ts | 68 +++--- src/i18n/en.json | 3 - src/i18n/ru.json | 3 - src/styles.ts | 25 ++- test/device-visual.test.mjs | 30 +++ test/devices.test.mjs | 39 ++++ 26 files changed, 627 insertions(+), 320 deletions(-) diff --git a/custom_components/houseplan/const.py b/custom_components/houseplan/const.py index 5a8eadd6..a4710fab 100755 --- a/custom_components/houseplan/const.py +++ b/custom_components/houseplan/const.py @@ -45,7 +45,7 @@ PLAN_ORPHAN_TTL_S = 3600 SCHEDULED_GRACE_S = 30 * 24 * 3600 FILES_DIR = "houseplan/files" CONF_ADMIN_ONLY = "admin_only" -VERSION = "1.60.2-beta.1" +VERSION = "1.60.2-beta.2" DEFAULT_CONFIG: dict = { "spaces": [], diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index 81aa68d4..5c7347e0 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1,4 +1,4 @@ -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 o=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 n=(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 o(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 o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:h,getOwnPropertyNames:c,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.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"),u.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:o}=h(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(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=[...c(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"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),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 o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),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),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(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}),(u.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,P=`<${D}>`,z=document,T=()=>z.createComment(""),R=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,N="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,O=/>/g,F=RegExp(`>|${N}(?:([^\\s"'>=/]+)(${N}*=${N}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,q=/"/g,H=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=U(1),B=U(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,h=-1):void 0===l[1]?h=-2:(h=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?q:L):r===q||r===L?r=F:r===I||r===O?r=E:(r=F,o=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+P:h>=0?(s.push(a),i.slice(0,h)+M+i.slice(h)+C+d):i+C+(-2===h?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,h]=J(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;iA(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&&R(this._$AH)?this._$AA.nextSibling.data=t:this.T(z.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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(T()),this.O(T()),this,this.options)):i=e[s],i._$AI(o),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 o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!R(t)||t!==this._$AH&&t!==j,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(T(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ht=at.litElementPolyfillSupport;ht?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ct=new WeakMap;let dt=0;class pt extends lt{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._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.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)return t.preventDefault(),t.stopImmediatePropagation(),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(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.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=ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ct.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._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ct.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ct.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ct.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.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(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=W` +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 o=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 n=(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 o(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 o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:h,getOwnPropertyNames:c,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.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"),u.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:o}=h(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(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=[...c(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"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),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 o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),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),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(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}),(u.reactiveElementVersions??=[]).push("2.1.2");const $=globalThis,k=t=>t,x=$.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,z=document,T=()=>z.createComment(""),R=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,N="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,O=/>/g,F=RegExp(`>|${N}(?:([^\\s"'>=/]+)(${N}*=${N}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,q=/"/g,H=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=U(1),B=U(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,h=-1):void 0===l[1]?h=-2:(h=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?q:L):r===q||r===L?r=F:r===I||r===O?r=E:(r=F,o=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+P:h>=0?(s.push(a),i.slice(0,h)+M+i.slice(h)+C+d):i+C+(-2===h?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,h]=J(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;iA(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&&R(this._$AH)?this._$AA.nextSibling.data=t:this.T(z.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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(T()),this.O(T()),this,this.options)):i=e[s],i._$AI(o),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 o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!R(t)||t!==this._$AH&&t!==j,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(T(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ht=at.litElementPolyfillSupport;ht?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ct=new WeakMap;let dt=0;class pt extends lt{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._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.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)return t.preventDefault(),t.stopImmediatePropagation(),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(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.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=ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ct.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._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ct.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ct.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ct.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.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(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=W` ${this.icon?W``:G} ${this.title} `;return this._useHaDialog?W`0||t===e?e:e-1}function Nt(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sh^i?1:-1;for(a=(l=o.length)<(h=n.length)?l:h,r=0;rn[r]^i?1:-1;return l==h?0:l>h^i?1:-1}function It(t,e,i,s){if(ti||t!==xt(t))throw Error(St+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Ot(t){var e=t.c.length-1;return At(t.e/Dt)==e&&t.c[e]%2!=0}function Ft(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Lt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!kt.test(d=String(t)))return o(p,d,h);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(It(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,m);if(d=String(t),h="number"==typeof t){if(0*t!=0)return o(p,d,h,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Mt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,c=d.length;la){a=c;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),h,e)}h=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(c=d.length;48===d.charCodeAt(--c););if(d=d.slice(l,++c)){if(c-=l,h&&M.DEBUG&&c>15&&(t>Pt||t!==xt(t)))throw Error(Mt+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Ft(l,r):Lt(l,r,"0");else if(n=(t=z(new M(t),e,i)).e,a=(l=Nt(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Lt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Dt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Dt,r=e,l=d[h=0],c=xt(l/p[o-r-1]%10);else if((h=$t((n+1)/Dt))>=d.length){if(!s)break t;for(;d.length<=h;d.push(0));l=c=0,o=1,r=(n%=Dt)-Dt+1}else{for(l=a=d[h],o=1;a>=10;a/=10,o++);c=(r=(n%=Dt)-Dt+o)<0?0:xt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[h+1]||(r<0?l:l%p[o-r-1]),s=i<4?(c||s)&&(0==i||i==(t.s<0?3:2)):c>5||5==c&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[h-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Dt-e%Dt)%Dt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=h,a=1,h--):(d.length=h+1,a=p[Dt-n],d[h]=r>0?xt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==h){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Ct&&(d[0]=1));break}if(d[h]+=a,d[h]!=Ct)break;d[h--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Ft(e,i):Lt(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(St+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(It(i=t[e],0,Rt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(It(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(It(i[0],-Rt,0,e),It(i[1],0,Rt,e),g=i[0],f=i[1]):(It(i,-Rt,Rt,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)It(i[0],-Rt,-1,e),It(i[1],1,Rt,e),v=i[0],y=i[1];else{if(It(i,-Rt,Rt,e),!i)throw Error(St+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(St+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(St+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(It(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(It(i=t[e],0,Rt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(St+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(St+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:$,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Rt&&o<=Rt&&o===xt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Dt)<1&&(e+=Dt),String(s[0]).length==e){for(e=0;e=Ct||i!==xt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(St+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return xt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],h=new M(u);if(null==t?t=_:It(t,0,Rt),o=$t(t/Dt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(St+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,h,c,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),k=d,f.c=e(Lt(Nt(u.c),u.e,"0"),10,n,t),f.e=f.c.length),c=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--c:(u.c=g,u.e=c,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,c=u.e),v=g[h=c+y+1],d=n/2,p=p||h<0||null!=g[h+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[h-1]||b==(u.s<0?8:7)),h<1||!g[0])s=p?Lt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=h,p)for(--n;++g[--h]>n;)g[h]=0,h||(++c,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=Lt(s,c,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,h=e%Tt,c=e/Tt|0;for(t=t.slice();l--;)a=((o=h*(n=t[l]%Tt)+(s=c*n+(r=t[l]/Tt|0)*h)%Tt*Tt+a)/i|0)+(s/Tt|0)+c*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,h,c,d,p,u,_,m,g,f,v,y,b,w,k,$,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(h=s.e-o.e)+1,a||(a=Ct,h=At(s.e/Dt)-At(o.e/Dt),S=S/Dt|0),c=0;D[c]==(C[c]||0);c++);if(D[c]>(C[c]||0)&&h--,S<0)g.push(1),d=!0;else{for(w=C.length,$=D.length,c=0,S+=2,(p=xt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=a/2&&k++;do{if(p=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*a+(f[1]||0)),(p=xt(y/k))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,$<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,c++);z(m,n+(m.e=c+h*Dt-1)+1,r,d)}else m.e=h,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,h=/^\.([^.]+)$/,c=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(c.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(h,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(St+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return Et(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return It(t,0,Rt),null==e?e=m:It(e,0,8),z(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-At(this.e/Dt))*Dt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,h,c=this;if((t=new M(t)).c&&!t.isInteger())throw Error(St+"Exponent not an integer: "+T(t));if(null!=e&&(e=new M(e)),r=t.e>14,!c.c||!c.c[0]||1==c.c[0]&&!c.e&&1==c.c.length||!t.c||!t.c[0])return h=new M(Math.pow(+T(c),r?t.s*(2-Ot(t)):+T(t))),e?h.mod(e):h;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&c.isInteger()&&e.isInteger())&&(c=c.mod(e))}else{if(t.e>9&&(c.e>0||c.e<-1||(0==c.e?c.c[0]>1||r&&c.c[1]>=24e7:c.c[0]<8e13||r&&c.c[0]<=9999975e7)))return n=c.s<0&&Ot(t)?-0:0,c.e>-1&&(n=1/n),new M(a?1/n:n);k&&(n=$t(k/Dt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Ot(t)):l=(o=Math.abs(+T(t)))%2,h=new M(u);;){if(l){if(!(h=h.times(c)).c)break;n?h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}if(o){if(0===(o=xt(o/2)))break;l=o%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Ot(t);else{if(0===(o=+T(t)))break;l=o%2}c=c.times(c),n?c.c&&c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}return s?h:(a&&(h=u.div(h)),e?h.mod(e):n?z(h,k,m,void 0):h)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:It(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Et(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Et(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Et(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&At(this.e/Dt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Et(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Et(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Dt,h=t.e/Dt,c=r.c,d=t.c;if(!l||!h){if(!c||!d)return c?(t.s=-e,t):new M(d?r:NaN);if(!c[0]||!d[0])return d[0]?(t.s=-e,t):new M(c[0]?r:3==m?-0:0)}if(l=At(l),h=At(h),c=c.slice(),a=l-h){for((n=a<0)?(a=-a,o=c):(h=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=c.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;c[i++]=0);for(e=Ct-1;s>a;){if(c[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((h=p*(h=v[--r]%g)+(a=u*h+(c=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*c,_[n--]=h%m;_[n]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Dt,r=t.e/Dt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=At(n),r=At(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Ct|0,a[e]=Ct===a[e]?0:a[e]%Ct;return o&&(a=[o].concat(a),++r),P(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return It(t,1,Rt),null==e?e=m:It(e,0,8),z(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Dt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return It(t,-9007199254740991,Pt),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,h=r.e,c=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+T(r)))||l==1/0?(((e=Nt(a)).length+h)%2==0&&(e+="0"),l=Math.sqrt(+e),h=At((h+1)/2)-(h<0||h%2),s=new M(e=l==1/0?"5e"+h:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+h)):s=new M(l+""),s.c[0])for((l=(h=s.e)+c)<3&&(l=0);;)if(n=s,s=d.times(n.plus(i(r,n,c,1))),Nt(n.c).slice(0,l)===(e=Nt(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,c=u.substr(0,n);n<_;n+=a)c+=h+u.substr(n,a);l>0&&(c+=h+u.slice(n)),p&&(c="-"+c)}s=d?c+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):c}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,h,c,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(St+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+T(l));if(!f)return new M(g);for(e=new M(u),c=s=new M(u),o=h=new M(u),_=Nt(f),r=e.e=_.length-g.e-1,e.c[0]=zt[(a=r%Dt)<0?Dt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:c:l,a=y,y=1/0,l=new M(_),h.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,c=h.plus(d.times(n=c)),h=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),h=h.plus(n.times(c)),s=s.plus(n.times(o)),h.s=c.s=g.s,p=i(c,o,r*=2,m).minus(g).abs().comparedTo(i(h,s,r,m).minus(g).abs())<1?[c,o]:[h,s],y=a,p},p.toNumber=function(){return+T(this)},p.toPrecision=function(t,e){return null!=t&&It(t,1,Rt),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Ft(Nt(i.c),n):Lt(Nt(i.c),n,"0"):10===t&&S?e=Lt(Nt((i=z(new M(i),_+n+1,m)).c),i.e,"0"):(It(t,2,x.length,"Base"),e=s(Lt(Nt(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return T(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Ht=class{key;left=null;right=null;constructor(t){this.key=t}},Ut=class extends Ht{constructor(t){super(t)}},Wt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Bt=class t extends Wt{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new Ut(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Ut(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Ut(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new Vt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Gt(this.wrap())}[Symbol.toStringTag]="[object Set]"},jt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Gt=class extends jt{getValue(t){return t.key}},Vt=class extends jt{getValue(t){return[t.key,t.key]}},Kt=t=>()=>t,Yt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Kt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Jt(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):Kt(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var Zt=t=>t,Xt=t=>{if(t){const e=new Bt(Yt(t)),i=new Bt(Yt(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new qt(0),y:new qt(0)}),o}return Zt},Qt=t=>({set:t=>{te=Qt(t)},reset:()=>Qt(t),compare:Yt(t),snap:Xt(t),orient:Jt(t)}),te=Qt(),ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),ie=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},se=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),oe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ne=t=>oe(t,t).sqrt(),re=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return se(o,s).div(ne(o)).div(ne(s))},ae=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return oe(o,s).div(ne(o)).div(ne(s))},le=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},he=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},ce=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:ve.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:re(this.point,t.point,s.point),cosine:ae(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},de=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},pe=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(h))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(h))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),c=h.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&c.isLessThan(d))return 1;if(t.isLessThan(e)&&c.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(h)?-1:l.isGreaterThan(h)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++fe,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ce.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ce(o,!0),h=new ce(n,!1);return new t(l,h,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return te.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=ie(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ee(e,r)&&0===this.comparePoint(r),h=ee(i,o)&&0===t.comparePoint(o),c=ee(e,a)&&0===this.comparePoint(a),d=ee(i,n)&&0===t.comparePoint(n);if(h&&l)return d&&!c?n:!d&&c?a:null;if(h)return c&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&c)return null;if(d)return n;if(c)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return he(i,s,t.x);if(s.x.isZero())return he(t,e,i.x);if(e.y.isZero())return le(i,s,t.y);if(s.y.isZero())return le(t,e,i.y);const o=se(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=se(n,e).div(o),a=se(n,s).div(o),l=t.x.plus(a.times(e.x)),h=i.x.plus(r.times(s.x)),c=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(h).div(2),y:c.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ee(s,p)?te.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ce(e,!0),n=new ce(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ce.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ce.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ye=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=te.snap({x:new qt(t[0][0]),y:new qt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;ege.run("union",t,e),$e=(t,...e)=>ge.run("intersection",t,e),xe=(t,...e)=>ge.run("difference",t,e);function Se(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Me(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function Ce(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function De(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function ze(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function Te(t){const e=[],i=new Set;for(const s of t||[]){const t=Pe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*h,y:r+l*c,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function Ae(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,h=n;for(const e of Te(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],c=Math.min(o,r),d=Math.max(o,r),p=c>0?c:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(c+d)/2;return{wallA:m(c),wallB:m(d),sideA:u,sideB:_,midA:m((c-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ne(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"door"===t?1:0;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function Ee(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Oe(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function Fe(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function We(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let h=0,c=0,d=0;for(let e=0;e1e-9?[c/(3*h),d/(3*h)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ie([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=h,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Be(t)||t[0]}function Be(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(qe(s,t,e))return s;for(let s=0;sGe(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ge(e,t,i)))}function Ye(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ze(t,e,i){for(let s=0;s1&&Ee(i[0],i[i.length-1],e)&&i.pop(),i}function Qe(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function ti(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function ei(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const di=["automation","script","scene"];const pi="—",ui="{}";function _i(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function mi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=_i(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function gi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function fi(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function vi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return pi;const n=(e?.attr||"").trim(),r=gi(t,i,n||null);if(null===r)return pi;return fi(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const yi=20;function bi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function wi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const ki="#55606c",$i=.55;function xi(t){const e=t?.settings||{},i=!t?.plan_url;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:"string"==typeof e.room_color&&/^#[0-9a-f]{6}$/i.test(e.room_color)?e.room_color:ki,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):$i,fill:["lqi","light","temp","glow"].includes(e.fill_mode)?e.fill_mode:"none",tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:"string"==typeof e.bg_color&&/^#[0-9a-f]{6}$/i.test(e.bg_color)?e.bg_color:null,hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Si(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return"string"==typeof i&&/^#[0-9a-f]{6}$/i.test(i)?i:""}const Mi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}},Ci=/^#[0-9a-f]{6}$/i;function Di(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Mi)){const s=Mi[t],o=i[t];e[t]={c:o&&"string"==typeof o.c&&Ci.test(o.c)?o.c:s.c,a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function Pi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function zi(t,e,i,s,o,n,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:Pi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}const Ti={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},Ri=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function Ai(t){for(const e of[...Object.values(Ti),...Ri])if(t===e[0]||t===e[1])return e;return null}function Ni(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Ai(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=Ti[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Ai(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Ei(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),o=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,n=i.rgb_color;if(Array.isArray(n)&&n.length>=3&&n.every(t=>Number.isFinite(t)))return{c:`rgb(${n[0]}, ${n[1]}, ${n[2]})`,bri:o};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:o}}return{c:e,bri:o}}function Ii(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,h;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),c=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*c>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+c*_*u],g=[i[0]+a*_*u,i[1]+c*_*u],f=[e[0]-a*_*u,e[1]-c*_*u],v=[i[0]-a*_*u,i[1]-c*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),k=b(g),$=b(f),x=b(v),S=Math.max(Math.min(w,k),Math.min($,x)),M=Math.min(Math.max(w,k),Math.max($,x));if(!(M-S>1e-9))return null;l=S,h=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),h=Math.atan2(i[1]-t[1],i[0]-t[0])-l;h>Math.PI;)h-=2*Math.PI;for(;h<-Math.PI;)h+=2*Math.PI}const c=o*Math.PI/180;if(Math.abs(h)>c){const t=l+h/2;h=c*Math.sign(h),l=t-h/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+h*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Oi(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>qe(l,t,1e-9))}function Fi(t){return t.startsWith("light.")||t.startsWith("switch.")}function Li(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*c+(o[1]-n[1])*d,_=(r[0]-n[0])*c+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(h,Math.max(u,_));g-m>i&&s.push([n[0]+c*m,n[1]+d*m,n[0]+c*g,n[1]+d*g])}}return s}function qi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function Hi(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*h+(s[1]-t[1])*c,a=(s[2]-t[0])*h+(s[3]-t[1])*c,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+h*p,t[1]+c*p,t[0]+h*e,t[1]+c*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+h*p,t[1]+c*p,n[0],n[1]])}return s}function Ui(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=Gi(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function Gi(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function Vi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Ki(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Yi(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ji(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Zi(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const Xi=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Qi=(t,e)=>[t[0]-e[0],t[1]-e[1]],ts=(t,e)=>[t[0]+e[0],t[1]+e[1]],es=(t,e)=>t[0]*e[0]+t[1]*e[1],is=t=>Math.hypot(t[0],t[1]);function ss(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function rs(t,e,i,s){const o=s||ns(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function as(t,e,i,s){const o=[],n=Qi(i,e),r=is(n);if(rs||c>s)continue;const d=es(Qi(n,e),a),p=es(Qi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function ls(t,e,i,s,o){const n=as(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>os(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=es(Qi(e,h),u),y=es(Qi(i,h),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wis(Qi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=is(Qi(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function cs(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],h=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],c=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=h(e);o<=c||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ps(t,e,i){if(Ve(t,e,i))return!0;if(Ge(t,e,i)||Ge(e,t,i))return!1;let s=0;try{const i=$e([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of i)t?.[0]&&(s+=Ye(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function us(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=rs(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[ts(i.a,n),ts(i.b,n)]];continue}const t=as(e.poly,i.a,i.b,o);if(!t.length)continue;const a=ls(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[ts(t,n),ts(e,n)]))}for(const t of e)os([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function _s(t,e,i){for(const s of e)for(let e=0;ei)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-i&&h+t.length/2<=a+i)return!0}return!1}function ms(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!cs(o))return!1;const l=ss(s.poly),c=ss(o);if(Math.abs(c)t.find(t=>t.id===e).poly),d=t.map(h);for(const t of ms(e,c,2*r)){const e=a.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function fs(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const h=t.filter(t=>t.id!==i).map(t=>t.poly),c=fs(l,e,h,s,o,2*a),d=c.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ms(e,[l.poly],2*a)){const e=c.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ys(t,e,i){const s=i/e;return Ye(t)*s*s/1e4}function bs(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const ws=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],ks=t=>Math.min(1,Math.max(0,t));function $s(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=ws[ws.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Ie(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Ss(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Ms(t,e,i,s){return[[t[0],t[1]],[e[0],e[1]],[e[0]+i[0]*s,e[1]+i[1]*s],[t[0]+i[0]*s,t[1]+i[1]*s]]}function Cs(t,e){try{const i=$e([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]),s=[];for(const t of i){const e=t?.[0];!Array.isArray(e)||e.length<4||s.push(e.slice(0,e.length-1).map(t=>[t[0],t[1]]))}return s}catch{return[]}}function Ds(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],h=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),c=[];for(const i of e){if(!(i.length>0))continue;const e=xs(i,t);if(!e||!Ss(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=h*i.length,$=Cs(Ms(b,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];c.push({openingId:i.id,roomId:e.roomId,polys:$,a:b,b:w,dir:l,len:k,normal:_,depth:k*x})}return c}function Ps(t){return Math.round(10*(Number(t)||0))/10}const zs={clear:1,sunny:1,"clear-night":1,windy:1,exceptional:1,partlycloudy:.7,"windy-variant":.7,cloudy:.4,overcast:.25,fog:.25,rainy:0,pouring:0,snowy:0,"snowy-rainy":0,hail:0,lightning:0,"lightning-rainy":0};const Ts=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Rs(t,e){const i=Ts(e?.north_deg);return null!==i?i:Ts(t?.north_deg)}function As(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Ns(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Es(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Is(t){const e=t?.states?.["sun.sun"]?.attributes,i=Number(e?.azimuth),s=Number(e?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Os={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Fs=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ls=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function qs(t,e,i){return Ls(e)?[t.x,t.y]:i([t.x,t.y])}const Hs=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Us=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Ws=(t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Us(n,e,i):s};function Bs(t,e){return{color:t.color,opacity:Fs(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Fs(t.fillOpacity,.25)}:{}}}function js(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ls(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Gs(t){const e=js(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Vs(t,e,i,s,o,n,r,a){const l=Ls(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*h+g*d,v=_+m*c+g*p,y=s-f,b=o-v;let w=(y*h+b*c)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),$=null;if(n){const e=w/Math.max(t.w,a),i=k/Math.max(t.h,a);$=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*$,k=t.h*$}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round(($??1)/o)*o);w=t.w*r,k=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,k=w*(t.h/Math.max(t.w,a))):(k=Math.round(k/r)*r,w=k*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,k=Math.round(k/r)*r;w=Math.max(a,w),k=Math.max(a,k);return{x:f+(e>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*d-w/2,y:v+(e>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*p-k/2,w:w,h:k,angle:Ls(t.angle)||void 0}}const Ks=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Ys=15;function Js(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Zs(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Xs(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Qs(t,e,i){const s=Number(e)>0?Number(e):5;return Zs(t)/s*i}function to(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function eo(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function io(t,e,i){const s=to((t[0]+e[0])/2,i),o=to((t[1]+e[1])/2,i),[n,r]=eo(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,h=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(h)},${o.toFixed(h)}@${l.toFixed(4)}`}function so(t,e,i,s){return 1===s?io(t,e,i):io([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function oo(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function no(t,e,i,s,o){const n=o>0?o:1;return{key:so(t,e,s,n),cm:Zs(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function ro(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function ao(t,e){const[i,s]=eo(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function lo(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function ho(t,e,i,s,o=1){if(!t?.length)return null;const n=so(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,h=(e[1]+i[1])/2,c=ao(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of ro(t,a))if(lo(e.ang,c)&&Math.hypot(e.x-l,e.y-h)<=d)return e.w;return null}function co(t,e,i,s,o=1){const n=ho(t,e,i,s,o);return n&&n.cm>0?Zs(n.cm):0}function po(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Te(e);for(const t of r)n.add(so([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=oo(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],h=n[1]-i[1],c=Math.hypot(a,h);if(c<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return lo(ao(i,n),ao(e,s))&&Ro(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ro(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!lo(ao(i,n),ao(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*h-(t[1]-i[1])*a)/c;if(o(e)>l||o(s)>l)return!1;const r=c*c,d=((e[0]-i[0])*a+(e[1]-i[1])*h)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*h)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/c});return!d})(t))&&t.cm>=1&&t.cm<=100)}function uo(t,e,i,s=8){const[o,n]=eo(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function _o(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,h=[];for(const o of t){let t="",c=null;const d=oo(o,r);if(d)for(let o=0;oa||Ro(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[h[0]+(p[0]-h[0])*e,h[1]+(p[1]-h[1])*e]};c=[g(d[0]),g(d[1])],t=so(c[0],c[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=ro([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ro(n.x,n.y,l[0],l[1],h[0],h[1])>a)continue;const g=c[0]+(d[0]-c[0])*Math.max(0,Math.min(1,m)),f=c[1]+(d[1]-c[1])*Math.max(0,Math.min(1,m)),[v,y]=eo(c,d),b=Math.max(s*r,1e-6);t=so([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),h.push(c?no(c[0],c[1],o.cm,s,r):{...o,key:t,cm:Zs(o.cm)}))}return h}function mo(t,e,i,s,o,n=1){const r=so(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,no(e,i,s,o,n)]}function go(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Mo(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Io(i,t),o=$o(i,t);if(s&&o)return`${xo(s)} ${xo(So(o))}`}}let s="";for(let i=0;i.05)&&Ro(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function yo(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function bo(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const h=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([h[0]+.001*a,h[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function wo(t,e){const i=t[0]*e[1]-t[1]*e[0],s=t[0]*e[0]+t[1]*e[1];return Math.abs(i)<1e-9&&s>0}function ko(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function $o(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]+p*c,e[1]+u*c],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=ko(k,b,$,w),S=Math.max(c,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}c>0&&s.push([t[o][0]+p*c,t[o][1]+u*c]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),c>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function xo(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Pe(r);if(!a||a.length<3)return null;const l=yo(s,o),h=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Pe(i);if(t)for(const e of Li(a,t,l))h.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])h.push([t[0],t[1]],[t[2],t[3]]);for(const t of n||[]){const e=oo(t,o);e&&h.push(e[0],e[1])}const c=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:c,parent:d,orig:a}}function Co(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=Zs(h.cm),r.add(h.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,h=Math.max(.5*i,1e-9)*l,c=ro(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=ao(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of c){if(r.has(t.w.key))continue;if(!lo(t.ang,a))continue;const e=oo(t.w,l);let i=!1,n=0;if(e){if(!lo(ao(e[0],e[1]),a))continue;if(Ro(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>h||Ro(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>h)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ro(t.x,t.y,s[0],s[1],o[0],o[1])>h)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Pe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Pe(s);if(t)for(const e of Li(o,t,i))n.push(e)}return n}(t,e,yo(o,a)),c=function(t,e,i,s,o){const n=yo(s,o),r=[];for(let a=0;aRo(c,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,h,s,o,a),d=Co(i,l,o,a),p=d.map((t,e)=>c[e]&&t>0?Qs(t,n,r)/2:0);return{...l,kinds:c,cms:d,offsets:p}}function Po(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(h)for(let t=0;t0&&!l.has(h.key)&&(l.add(h.key),a.push(h));const h=[];for(const a of t||[]){if(!a?.id)continue;const l=Do(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&h.push({a:[c[0],c[1]],b:[d[0],d[1]],key:so(c,d,s,r),cm:o,len:p}),t=n+1}}}h.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const c=[],d=new Set,p=new Set,u=4*yo(s,r);for(const t of h){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&lo(ao(e.a,e.b),ao(t.a,t.b))&&Ro(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ro(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)));return c}function To(t,e,i,s,o,n,r,a=1){const l=yo(o,a),h=(s[0]+s[2])/2,c=(s[1]+s[3])/2,d=ao([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Po(t,e,i,o,n,r,a)){if(!lo(ao(s.a,s.b),d))continue;const t=Ro(h,c,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),h=Pe(l);if(!h||h.length<3)return null;if(!i?.length)return h.map(t=>[t[0],t[1]]);const c=Do(t,e,i,s,o,n,r,a);return c&&c.offsets.some(t=>t>0)&&$o(c.poly,c.offsets)||h.map(t=>[t[0],t[1]])}function No(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Eo(t,e,i,s=[],o,n,r,a=1,l=[]){if(!e?.length&&!l.length)return null;const h=[];let c=0;for(const s of t||[]){if(!s?.id)continue;const l=Do(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(c=Math.max(c,2*t));const d=Io(l.poly,l.offsets),p=$o(l.poly,l.offsets);d&&h.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));c=Math.max(c,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*yo(s,r),l=new Map;for(const a of Po(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const h=[...l.values()];if(h.length<2)return[];const c=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])c.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||c.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of c){const e=h.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),c,u,p]:[t.slice(),p,u,c])}}return d}(t,e,i,o,n,r,a);try{const t=t=>{const e=No(t.outset);return t.inset?xe(e,No(t.inset)):e};let e=h.length?t(h[0]):null;for(let i=1;i0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),n=Math.sin(i),r=-n,l=s,h=t.length/2,d=1.25*Math.max(c,o*a),p=[[t.x-s*h-r*d,t.y-n*h-l*d],[t.x+s*h-r*d,t.y+n*h-l*d],[t.x+s*h+r*d,t.y+n*h+l*d],[t.x-s*h+r*d,t.y-n*h+l*d]];e&&(e=xe(e,No(p)))}for(const t of l)t.length<3||(e=e?ke(e,No(t)):[No(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+xo(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:c,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(!h||h.poly.length<3||!h.offsets.some(t=>t>0))continue;const c=Io(h.poly,h.offsets),d=$o(h.poly,h.offsets);if(!c||!d)continue;const p=`${xo(c)} ${xo(So(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=so(h.poly[t],h.poly[(t+1)%h.poly.length],s,r),_=h.kinds[t]||"outer",m=h.cms[t],g=Qs(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),h=l.map(t=>xo(t)).join(" ");return s.length||h?{d:[s.map(t=>t.d).join(" "),h].filter(Boolean).join(" "),depthUnits:c,fillRule:"nonzero"}:null}}function Io(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);So(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-h*r],n=[e[0]-c*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=ko(k,b,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-h*r]),a>0&&s.push([t[o][0]-c*a,t[o][1]-d*a])}return s.length>=3?s:null}function Oo(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const h=Pe(l);if(h&&h.length>=3){const c=Do(t,l.id,e,i,s,o,n,r),d=c&&c.offsets.some(t=>t>0)?Io(c.poly,c.offsets):null,p=d||h;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Fo(t,e,i,s,o,n,r=1){let a=null;for(const i of t||[]){const t=Pe(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=co(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const h=Qs(l,o,n),c=Pe(a.room),[d,p]=bo(c,a.edge),u=e.flip_v?-1:1,_=h/2;return{ox:d*_*u,oy:p*_*u,cm:l}}const Lo=150;function qo(t){return Number.isFinite(t)?Math.max(1,Math.min(Lo,t)):1}function Ho(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Uo=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Wo(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Bo(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=Qs(i,s,o)/2,h=-r/a*l,c=n/a*l;return[[t[0]+h,t[1]+c],[e[0]+h,e[1]+c],[e[0]-h,e[1]-c],[t[0]-h,t[1]-c]]}function jo(t,e,i){const s=Number(e)>0?Number(e):5,o=qo(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Ho(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*h-e*c,r+t*c+e*h])}function Go(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Uo(t));return e.length?ke(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return xe(Uo(t),i)}catch{}let i=[Uo(t)];for(const t of e)if(!(t.length<3))try{i=xe(i,Uo(t))}catch{}return i}function Ko(t){let e=0;for(const i of t||[])if(i?.length){e+=Ye(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jo(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zo(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const h=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+h*r),e[1]-(i[1]+h*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*c+h*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+h/p*g],v=[e[0]+c/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function Zo(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function Xo(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(qo(t.cm)-qo(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Ho(t.angle)-Ho(e.angle));return Math.min(s,90-s)<=1e-6}const Qo=1e3;function tn(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const en=.01,sn=100;function on(t,e=1e3){const i=tn(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(sn,Math.max(en,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(sn,Math.max(en,n)):o,l=Number.isFinite(r)&&r>0?Math.min(sn,Math.max(en,r)):o,h=Number(t?.plan_x),c=Number(t?.plan_y),d=Ls(t?.plan_angle);return{x:i.x+(Number.isFinite(h)?mn(h):0)*e,y:i.y+(Number.isFinite(c)?mn(c):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function nn(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function rn(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qo,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Qo,i[1]*e,i[2]*Qo,i[3]*e],bg:t.plan_url?{href:Gi(t.plan_url),...on(t,Qo)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...nn(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Qo:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qo:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qo,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qo,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qo,t.a[1]*e],b:[t.b[0]*Qo,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*Qo,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Ho(t.angle)}}))}}):[]}const an=5e3,ln=an*Qo,hn=240,cn=Qo/hn,dn=1/hn;function pn(t){if(!Number.isFinite(t))return t;const e=Math.round(t*hn/Qo)*Qo/hn;return Math.abs(e-t)<=1e-9*cn?t:e}function un(t){return{x:pn(t.x),y:pn(t.y)}}function _n(t){return Number.isFinite(t)?Math.min(ln,Math.max(-ln,t)):0}function mn(t){return Number.isFinite(t)?Math.min(an,Math.max(-an,t)):0}const gn=1/3,fn=200;function vn(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function yn(t){return t.poly&&t.poly.length?vn(t.poly):null==t.x||null==t.y?null:vn([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function bn(t,e){const i=[];for(const e of t.rooms||[]){const t=yn(e);t&&i.push(t)}if(t.bg){const e=vn(js(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=vn([t]);e&&i.push(e)}else i.push(t);return i}const wn=t=>{if(!t.length)return 0;const e=[...t].sort((t,e)=>t-e),i=e.length>>1;return e.length%2?e[i]:(e[i-1]+e[i])/2},kn=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function $n(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=fn),n<30&&(s=s+n/2-100,n=fn);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function xn(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ln&&Math.abs(t.maxX)<=ln&&Math.abs(t.minY)<=ln&&Math.abs(t.maxY)<=ln);if(!n.length)return{core:null,all:null,outliers:0};const r=kn(n);if(n.length<4){const t=$n(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),h=wn(a),c=wn(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-h),Math.abs(l[e]-c))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*gn?n.filter((t,e)=>!u[e]):n;return{core:$n(kn(m)||r,i),all:$n(r,i),outliers:m===n?0:_}}function Sn(t,e,i=.05){const s=xn(bn(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,Qo,Qo];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Mn(t){const e=Sn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Cn(t){const e=[];for(const i of t.rooms||[]){const t=yn(i);t&&e.push(t)}const i=xn(e,{pad:0}).core;return i?Math.max(Qo,Math.min(ln,Math.max(i.w,i.h))):Qo}function Dn(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Cn(e)*n/o}const Pn=[1,2,5,10,20,50,100,200,500,1e3];function zn(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Tn(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)]}const Rn=["furniture","appliance","sanitary","other"],An=[{id:"sofa",group:"furniture",w:220,h:90,g:[["r",0,0,1,1],["l",.09,.26,.91,.26],["l",.09,.26,.09,1],["l",.91,.26,.91,1],["l",.5,.26,.5,1]]},{id:"armchair",group:"furniture",w:90,h:85,g:[["r",0,0,1,1],["l",.14,.28,.86,.28],["l",.14,.28,.14,1],["l",.86,.28,.86,1]]},{id:"coffee_table",group:"furniture",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",w:120,h:60,g:[["r",0,0,1,1],["r",.63,.07,.31,.86],["l",.63,.5,.94,.5]]},{id:"bed_double",group:"furniture",w:160,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.06,.1,.4,.15],["r",.54,.1,.4,.15],["l",0,.33,1,.33]]},{id:"bed_single",group:"furniture",w:90,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.15,.1,.7,.15],["l",0,.33,1,.33]]},{id:"nightstand",group:"furniture",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["e",.29,.31,.15,.15],["e",.71,.31,.15,.15],["e",.29,.71,.15,.15],["e",.71,.71,.15,.15]]},{id:"dishwasher",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["r",.1,.12,.8,.76],["e",.5,.5,.27,.27],["e",.5,.5,.13,.13]]},{id:"washer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["e",.5,.57,.14,.14]]},{id:"dryer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["p",.36,.5,.5,.64,.64,.5]]},{id:"tv",group:"appliance",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",w:90,h:25,g:[["r",0,0,1,1],["l",.05,.55,.95,.55],["l",.05,.79,.95,.79]]},{id:"water_heater",group:"appliance",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",w:90,h:90,g:[["r",0,0,1,1],["l",0,0,1,1],["l",1,0,0,1],["e",.5,.5,.08,.08]]},{id:"sink",group:"sanitary",w:60,h:45,g:[["r",0,0,1,1],["e",.5,.6,.34,.3],["e",.5,.15,.07,.07]]},{id:"kitchen_sink",group:"sanitary",w:80,h:60,g:[["r",0,0,1,1],["r",.06,.24,.44,.64],["r",.54,.24,.4,.64],["e",.5,.12,.06,.06]]},{id:"bidet",group:"sanitary",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",w:100,h:280,g:[["r",0,0,1,1],["l",0,.111,1,.111],["l",0,.222,1,.222],["l",0,.333,1,.333],["l",0,.444,1,.444],["l",0,.556,1,.556],["l",0,.667,1,.667],["l",0,.778,1,.778],["l",0,.889,1,.889],["l",.5,.93,.5,.06],["p",.38,.16,.5,.06,.62,.16]]},{id:"fireplace",group:"other",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",w:40,h:40,g:[["e",.5,.5,.22,.22],["l",.5,.28,.5,.02],["l",.5,.72,.5,.98],["l",.28,.5,.02,.5],["l",.72,.5,.98,.5],["l",.34,.34,.13,.13],["l",.66,.66,.87,.87],["l",.66,.34,.87,.13],["l",.34,.66,.13,.87]]},{id:"rug",group:"other",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],Nn=new Map(An.map(t=>[t.id,t]));function En(t){return t&&Nn.get(t)||null}function In(t){return An.filter(e=>e.group===t)}function On(t,e,i=cn,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Fn=5e-4,Ln=an;function qn(t){return Number.isFinite(t)?Math.max(Fn,Math.min(Ln,t)):Fn}const Hn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Un(t,e,i){const s=En(t);if(!(s&&e>0&&i>0))return"";const o=t=>Hn(t*e),n=t=>Hn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,h]=t;r.push(`M${o(s-l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Bn(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,h,c]=o,d=h-s,p=c-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Wn(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function jn(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Gn(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Vn(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Kn(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Vn(t.a)||!Vn(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function Yn(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Jn(t,e){return Yn(t,e).q}function Zn(t,e,i,s,o){const n=Yn(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+c*g,h+d*g]}function Xn(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt.seg)}(e,s);let n=null;for(const r of e||[]){if(!r?.id)continue;const e=Pe(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of o)if(Zi([(l[0]+h[0])/2,(l[1]+h[1])/2],t)<2*s){p=!0;break}p||(!n||dt?.id),r=Kn(e);if(r.length)return rr(r,t,i,s).map(t=>Gn(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tjn([t[0],t[1]],[t[2],t[3]],e))}function ir(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tZi(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function sr(t,e,i){let s=null;for(const o of e){const e=Zi(t,o);e<=i&&(!s||e.05)continue;const d=co(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-c[0],p[1]-c[1]);u0?l:n}(t,e,i,s,o,n);return mo(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function nr(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(Zi(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Zi([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])Zi(e,t)<=2*i&&s.push(e);return s}function rr(t,e,i,s){const o=Kn(t);if(!o.length)return[];const n=Xn(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=Gn(t,i),o=e[0],l=e[1],h=e[2]-o,c=e[3]-l,d=Math.hypot(h,c);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,h=(t[2]-o)*p+(t[3]-l)*u,c=Math.max(0,Math.min(r,h)),m=Math.min(d,Math.max(r,h));if(m-ct.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=h&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=h);c||(c={pair:t,origin:[e[0],e[1]],ux:n,uy:r,ranges:[]},l.push(c));const d=(e[0]-c.origin[0])*c.ux+(e[1]-c.origin[1])*c.uy,p=(e[2]-c.origin[0])*c.ux+(e[3]-c.origin[1])*c.uy;c.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const c=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&c.push(jn(e,o,i))}}return c}function ar(t,e,i,s){const o=Kn(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=Gn(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,h=a;for(let t=0;t{const e=Yn(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=c[0]-n[0],r=c[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(Yn([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),h=d(a);break}}n.push(jn(l,h,s))}return n}class lr{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=Gi(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{const e=Number(t);return Number.isFinite(e)?e:null};function pr(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dr(e.x)&&null!=dr(e.y)?{x:dr(e.x),y:dr(e.y),a:dr(e.a??e.angle??e.theta)}:null;let s=null;const o=t.path?.points??t.path;if(Array.isArray(o)&&o.length){s=[];for(const t of o){const e=dr(Array.isArray(t)?t[0]:t?.x),i=dr(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const n=[],r=t.rooms,a=Array.isArray(r)?r.map((t,e)=>[String(t?.id??e),t]):r&&"object"==typeof r?Object.entries(r):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim();let s=dr(e.cx??e.center?.x),o=dr(e.cy??e.center?.y);if(null==s||null==o){const t=dr(e.x0),i=dr(e.y0),n=dr(e.x1),r=dr(e.y1);null!=t&&null!=i&&null!=n&&null!=r&&(s=(t+n)/2,o=(i+r)/2)}if(null!=s&&null!=o||(s=dr(e.x),o=dr(e.y)),i&&null!=s&&null!=o){const r={id:t,name:i,cx:s,cy:o},a=dr(e.x0),l=dr(e.y0),h=dr(e.x1),c=dr(e.y1);null!=a&&null!=l&&null!=h&&null!=c&&(r.x0=Math.min(a,h),r.y0=Math.min(l,c),r.x1=Math.max(a,h),r.y1=Math.max(l,c)),n.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s?{pos:i,path:s,rooms:n,mapId:l}:null}function ur(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const _r=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function mr(t,e){const i=new Map(e.map(t=>[_r(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(_r(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,h=0,c=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,h+=m,c+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,h]=u,c=e*(n*h-r*l)-i*(o*h-r*a)+s*(o*l-n*a);if(!Number.isFinite(c)||Math.abs(c)<1e-9)return null;const d=[(n*h-r*l)/c,(s*l-i*h)/c,(i*r-s*n)/c,(r*a-o*h)/c,(e*h-s*a)/c,(s*o-e*r)/c,(o*l-n*a)/c,(i*a-e*l)/c,(e*n-i*o)/c];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,h]),g=_([c,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:cr(n,s)}:null}function gr(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,h]=t[n],c=l-r,d=h-a,p=Math.hypot(c,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function fr(t){return"cleaning"===t||"returning"===t||"on"===t}const vr={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function yr(t){const[e,i]=vr[t.rot]||[1,0],s=t.mir?-1:1;return[t.s*e*s,-t.s*i,t.ox,t.s*i*s,t.s*e,t.oy]}function br(t,e,i,s){const[o,n]=hr(yr(e),i,s),r=yr({...t,ox:0,oy:0}),[a,l]=hr(r,i,s);return{...t,ox:o-a,oy:n-l}}function wr(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const kr={availability:"available",status:"alarm",activity:"none"},$r=new Set(["motion","vibration","sound"]),xr=new Set(["occupancy","presence"]),Sr=new Set(["door","window","garage_door","opening"]),Mr=new Set(["running","power"]),Cr=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Dr=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Pr=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),zr=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Tr=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Rr=t=>String(t??"").trim().toLowerCase();function Ar(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Rr(t?.[e]);if(Dr.has(i)||Pr.has(i))return i}return""}function Nr(t,e){const i=t?.states?.[e],s=i?Rr(i.state):"__missing__",o=String(e||"").split(".")[0],n=Rr(i?.attributes?.device_class),r={eid:e,state:s,availability:Tr(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Xi.has(e))}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return $r.has(n)?{...r,edge:"rising"}:xr.has(n)?{...r,activity:"on"===s?"presence":"none"}:Sr.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:Mr.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=Ar(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Rr):[],o=!Pr.has(s)&&(zr.has(s)||Dr.has(s)||e.includes(s));return(t?Dr.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=Ar(i.attributes);return Dr.has(a)||Dr.has(s)&&!Pr.has(s)?{...r,status:"working",activity:"running"}:r}function Er(t,e){if(!(t=>!!t&&!Tr(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}function Ir(t){const e={};for(const[i,s]of Object.entries(t.entities))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}function Or(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Fr(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const Lr=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],qr=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Hr(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of Lr){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return qr(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Rr(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return $r.has(i)||xr.has(i)||Sr.has(i)||Mr.has(i)||"moving"===i||Cr.has(i)}(t,e.eid));if(n.length)return qr(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length)return qr(r).map(t=>t.eid);const a=[];for(const t of wt)a.push(...qr(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...qr(o.filter(t=>!wt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function Ur(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>Fr(t,e.eid));if(i)return i.eid}return Hr(t,e)[0]}function Wr(t,e,i=[]){return[...new Set(Br(t,e,i))].filter(t=>Fi(t))}function Br(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function jr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).some(t=>t.startsWith("switch.")&&s.has(t))}function Gr(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Vr(t,e){const i=Wr(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light||jr(e.marker?.binding,e.marker?.controls,e.entities)){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Fi),s=t.primary&&Fi(t.primary)?t.primary:null;return(e&&Fi(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||Hr(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function Kr(t,e,i){const s=[],o=new Set;for(const n of e){if(n.hidden||null!=i&&!Gr(n,i))continue;const e=Vr(t,n);for(const{eid:i,via:r}of e)i&&!o.has(i)&&(o.add(i),s.push({eid:i,device:n,via:r,on:"on"===t.states[i]?.state}))}return s}function Yr(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function Jr(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return Qe(i)}function Zr(t,e){for(const i of e){if(!Fr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function Xr(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function Qr(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ta(t,e){for(const i of e){if(!Qr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function ea(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function ia(t,e,i,s,o){const n=bt(e,i,o);if(n!==yt)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=vt[e];if(t)return t}return null}(r)??yt}function sa(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function oa(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function na(t,e,i,s){const o=Wr(e.binding,e.controls,t.entities).filter(t=>!oa(i,t,s));t.marker=e,t.controls=o,e.hidden&&(t.hidden=!0),e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function ra(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,showAll:r,firstSpaceId:a,loc:l,iconRules:h}=t,c=!1!==o.group_lights,d=sa(s),p=ea(e,c).filter(t=>!oa(e,t.eid,d)),u=new Set(p.map(t=>t.area)),_=Ir(e),m=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||m.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),f={},v=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(m.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!o.filter_seeded)continue;const d=_[t.id]||[],p=Or(e,t,d),y=!o.filter_seeded;if(y&&!r){if(n.has(p))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===p&&t.via_device_id)continue}const b=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=b+"|"+s;let k=ia(e,b,t.model,d,h);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),y&&!r&&c&&"mdi:lightbulb"===k&&u.has(s))continue;f[w]=(f[w]||0)+1;const $=f[w]>1?b+" "+f[w]:b,x={id:t.id,name:$,model:t.model||"",area:s,space:i[s],icon:k,entities:d,bindingKind:"device",bindingRef:t.id,pdfs:[]};x.primary=Ur(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=Zr(e,d)),x.primary&&Qr(e,x.primary)&&(x.hum=ta(e,d)),v.push(x)}for(const t of p)i[t.area]&&(m.has("entity:"+t.eid)||v.push({id:"lg_"+t.eid,name:t.name,model:l("device.light_group"),area:t.area,space:i[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of s){if(t.removed)continue;if(t.hidden&&!o.filter_seeded)continue;const[s,n]=t.binding.split(":");if("device"===s){const s=e.devices[n],o=t.area||s?.area_id||"",r=o&&i[o]||t.space||a,c=s&&_[s.id]||[];let p=s?ia(e,s.name_by_user||s.name||"",s.model,c,h):"mdi:help-circle";c.some(t=>t.startsWith("lock."))&&(p="mdi:lock");const u={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:o,space:r,icon:p,entities:c,bindingKind:"device",bindingRef:n};u.primary=Ur(e,c,p),"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Zr(e,c)),u.primary&&Qr(e,u.primary)&&(u.hum=ta(e,c)),u.primary&&Qr(e,u.primary)&&(u.hum=ta(e,c)),na(u,t,e,d),v.push(u)}else if("entity"===s){if(oa(e,n,d))continue;const s=e.entities[n],o=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=o&&i[o]||t.space||a,l=e.states[n],c=s?.name||l?.attributes?.friendly_name||n;let p=ia(e,c,"",[n],h);n.startsWith("lock.")&&(p="mdi:lock");const u={id:t.id,name:c,model:"",area:o,space:r,icon:p,entities:[n],primary:n,bindingKind:"entity",bindingRef:n};"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Zr(e,[n])),Qr(e,n)&&(u.hum=ta(e,[n])),na(u,t,e,d),v.push(u)}else{const s=t.area||"",o=t.space||s&&i[s]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:s,space:o,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};na(n,t,e,d),v.push(n)}}return v}function aa(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=sa(i);return"device"===o?r.devices.has(n):"entity"===o&&oa(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?Zr(t,e):ta(t,e)}return null}const la=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");var ha={"btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","btn.no_area":"No area","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden devices (ghosted, this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_closewall":"Physical wall: click a dashed virtual stretch","title.no_area_room":"Decorative room without an HA area (e.g. a hallway)","title.choose_area":"Select a Home Assistant area","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.closewall":"Physical wall","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary stretch","history.close_boundary":"Close boundary opening","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit door or window opening","history.move_opening":"Move door or window opening","history.delete_opening":"Delete door or window opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Doors & windows: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Door / window","opening.door":"Door","opening.window":"Window","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon","display.icon_ripple":"Icon + activity","marker.display_hint":"A yellow plate means actual work. Activity adds events, presence and movement; alarms are always visible.","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide doors and windows","space.hide_openings_tip":"The symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value instead of an icon","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Show hidden","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors and windows, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","fill.glow":"Light sources (dark house, glowing lamps)","gs.glow_group":"Light-sources fill","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius (light-sources fill)","marker.glow_radius_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.openwall":"Virtual wall","title.markup_openwall":"Virtual wall — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Crosshair cursor.","toast.openwall_pick":"Click a wall shared by two rooms","toast.openwall_shared_only":"Only a shared wall between two rooms can be opened","toast.openwall_short":"Stretch too short — pick a farther point","toast.openwall_opened_span":"Virtual stretch opened","toast.openwall_closed_span":"Virtual stretch closed","toast.closewall_pick":"Click a dashed virtual stretch","toast.closewall_use_tool":"Use “Physical wall” for this stretch","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Makes the icon glow in the “Light sources” fill even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Show hidden" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.status_found":"Position source found: {name}","vac.status_none":"The integration reports no coordinates — the robot will only be shown at its base","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_res_warn":"Matched {rooms} rooms but the fit is rough — verify and refine via “Fit manually” if needed","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the sun (day/night)","gs.bg_daynight_hint":"The stage follows the sun: neutral day, warm golden hour, deep night. Needs the compass below.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the sun features stay off.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Point the arrow at north (1° steps, 15° with Shift) or type the degrees — until then the sun features stay off.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.weather":"Weather entity","gs.weather_ph":"weather.home (optional)","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug"};const ca={en:ha,ru:{"btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","btn.no_area":"Без зоны","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые устройства (полупрозрачными, только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_closewall":"Физическая стена: кликните по пунктирному виртуальному участку","title.no_area_room":"Декоративная комната без привязки к зоне (например, холл)","title.choose_area":"Выберите зону Home Assistant","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.closewall":"Физическая стена","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие участка границы","history.close_boundary":"Закрытие проёма в границе","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение дверного или оконного проёма","history.move_opening":"Перемещение дверного или оконного проёма","history.delete_opening":"Удаление дверного или оконного проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Двери и окна: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Дверь / окно","opening.door":"Дверь","opening.window":"Окно","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок","display.icon_ripple":"Значок + активность","marker.display_hint":"Жёлтая подложка означает фактическую работу. Активность добавляет события, присутствие и движение; тревога видна всегда.","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить (свет/розетки)","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери и окна не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Показать скрытые","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери и окна, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","fill.glow":"Свет по источникам (тёмный дом, пятна света)","gs.glow_group":"Заливка «Свет по источникам»","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения (заливка «Свет по источникам»)","marker.glow_radius_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.openwall":"Виртуальная стена","title.markup_openwall":"Виртуальная стена — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает виртуальный участок. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","toast.closewall_pick":"Кликните по пунктирному виртуальному участку","toast.closewall_use_tool":"Для этого участка выберите «Физическая стена»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Использовать датчик температуры устройства","marker.use_climate_temp_tip":"Кондиционеры и термостаты знают температуру комнаты (current_temperature). Показывает её плашкой у значка и учитывает в средней температуре комнаты — как обычный термометр.","marker.is_light":"Это устройство — источник света","marker.is_light_tip":"Даёт ореол в заливке «Свет по источникам» даже без light-сущности — для умного выключателя с обычными светильниками. Ореол следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Показать скрытые» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.status_found":"Источник координат найден: {name}","vac.status_none":"Интеграция не отдаёт координаты — робот будет показан только на базе","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_res_warn":"Совпало комнат: {rooms}, но привязка грубовата — проверьте и при необходимости откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за солнцем (день/ночь)","gs.bg_daynight_hint":"Фон следует за солнцем: нейтральный день, тёплый золотой час, глубокая ночь. Нужен компас ниже.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — солнечные функции выключены.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Направьте стрелку на север (шаг 1°, с Shift 15°) или введите градусы — до этого солнечные функции молчат.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.weather":"Сущность погоды","gs.weather_ph":"weather.home (необязательно)","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр"}};function da(t,e){if(e&&e in ca)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function pa(t,e,i){return wi(ca[t][e]??ha[e]??e,i)}class ua extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return da(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[],e=this._lang;return[{name:"title",selector:{text:{}}},t.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:t}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:[{value:"",label:pa(e,"editor.lang_auto")},{value:"en",label:pa(e,"editor.lang_en")},{value:"ru",label:pa(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:pa(t,"editor.title"),default_floor:pa(t,"editor.default_floor"),language:pa(t,"editor.language"),icon_size:pa(t,"editor.icon_size"),show_temperature:pa(t,"editor.show_temperature"),live_states:pa(t,"editor.live_states"),show_signal:pa(t,"editor.show_signal"),kiosk:pa(t,"editor.kiosk"),cycle:pa(t,"editor.cycle")};return W`0||t===e?e:e-1}function Nt(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sh^i?1:-1;for(a=(l=o.length)<(h=n.length)?l:h,r=0;rn[r]^i?1:-1;return l==h?0:l>h^i?1:-1}function It(t,e,i,s){if(ti||t!==xt(t))throw Error(St+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Ot(t){var e=t.c.length-1;return At(t.e/Dt)==e&&t.c[e]%2!=0}function Ft(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Lt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!$t.test(d=String(t)))return o(p,d,h);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(It(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,m);if(d=String(t),h="number"==typeof t){if(0*t!=0)return o(p,d,h,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Mt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,c=d.length;la){a=c;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),h,e)}h=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(c=d.length;48===d.charCodeAt(--c););if(d=d.slice(l,++c)){if(c-=l,h&&M.DEBUG&&c>15&&(t>Pt||t!==xt(t)))throw Error(Mt+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Ft(l,r):Lt(l,r,"0");else if(n=(t=z(new M(t),e,i)).e,a=(l=Nt(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Lt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Dt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Dt,r=e,l=d[h=0],c=xt(l/p[o-r-1]%10);else if((h=kt((n+1)/Dt))>=d.length){if(!s)break t;for(;d.length<=h;d.push(0));l=c=0,o=1,r=(n%=Dt)-Dt+1}else{for(l=a=d[h],o=1;a>=10;a/=10,o++);c=(r=(n%=Dt)-Dt+o)<0?0:xt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[h+1]||(r<0?l:l%p[o-r-1]),s=i<4?(c||s)&&(0==i||i==(t.s<0?3:2)):c>5||5==c&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[h-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Dt-e%Dt)%Dt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=h,a=1,h--):(d.length=h+1,a=p[Dt-n],d[h]=r>0?xt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==h){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Ct&&(d[0]=1));break}if(d[h]+=a,d[h]!=Ct)break;d[h--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Ft(e,i):Lt(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(St+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(It(i=t[e],0,Rt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(It(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(It(i[0],-Rt,0,e),It(i[1],0,Rt,e),g=i[0],f=i[1]):(It(i,-Rt,Rt,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)It(i[0],-Rt,-1,e),It(i[1],1,Rt,e),v=i[0],y=i[1];else{if(It(i,-Rt,Rt,e),!i)throw Error(St+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(St+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(St+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(It(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(It(i=t[e],0,Rt,e),$=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(St+e+" not an object: "+i);k=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(St+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:$,FORMAT:k,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Rt&&o<=Rt&&o===xt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Dt)<1&&(e+=Dt),String(s[0]).length==e){for(e=0;e=Ct||i!==xt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(St+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return xt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],h=new M(u);if(null==t?t=_:It(t,0,Rt),o=kt(t/Dt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(St+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,h,c,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=$,$=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),$=d,f.c=e(Lt(Nt(u.c),u.e,"0"),10,n,t),f.e=f.c.length),c=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--c:(u.c=g,u.e=c,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,c=u.e),v=g[h=c+y+1],d=n/2,p=p||h<0||null!=g[h+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[h-1]||b==(u.s<0?8:7)),h<1||!g[0])s=p?Lt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=h,p)for(--n;++g[--h]>n;)g[h]=0,h||(++c,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=Lt(s,c,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,h=e%Tt,c=e/Tt|0;for(t=t.slice();l--;)a=((o=h*(n=t[l]%Tt)+(s=c*n+(r=t[l]/Tt|0)*h)%Tt*Tt+a)/i|0)+(s/Tt|0)+c*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,h,c,d,p,u,_,m,g,f,v,y,b,w,$,k,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(h=s.e-o.e)+1,a||(a=Ct,h=At(s.e/Dt)-At(o.e/Dt),S=S/Dt|0),c=0;D[c]==(C[c]||0);c++);if(D[c]>(C[c]||0)&&h--,S<0)g.push(1),d=!0;else{for(w=C.length,k=D.length,c=0,S+=2,(p=xt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),k=D.length,w=C.length),b=k,v=(f=C.slice(0,k)).length;v=a/2&&$++;do{if(p=0,(l=e(D,f,k,v))<0){if(y=f[0],k!=v&&(y=y*a+(f[1]||0)),(p=xt(y/$))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,k<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,c++);z(m,n+(m.e=c+h*Dt-1)+1,r,d)}else m.e=h,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,h=/^\.([^.]+)$/,c=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(c.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(h,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(St+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return Et(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return It(t,0,Rt),null==e?e=m:It(e,0,8),z(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-At(this.e/Dt))*Dt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,h,c=this;if((t=new M(t)).c&&!t.isInteger())throw Error(St+"Exponent not an integer: "+T(t));if(null!=e&&(e=new M(e)),r=t.e>14,!c.c||!c.c[0]||1==c.c[0]&&!c.e&&1==c.c.length||!t.c||!t.c[0])return h=new M(Math.pow(+T(c),r?t.s*(2-Ot(t)):+T(t))),e?h.mod(e):h;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&c.isInteger()&&e.isInteger())&&(c=c.mod(e))}else{if(t.e>9&&(c.e>0||c.e<-1||(0==c.e?c.c[0]>1||r&&c.c[1]>=24e7:c.c[0]<8e13||r&&c.c[0]<=9999975e7)))return n=c.s<0&&Ot(t)?-0:0,c.e>-1&&(n=1/n),new M(a?1/n:n);$&&(n=kt($/Dt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Ot(t)):l=(o=Math.abs(+T(t)))%2,h=new M(u);;){if(l){if(!(h=h.times(c)).c)break;n?h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}if(o){if(0===(o=xt(o/2)))break;l=o%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Ot(t);else{if(0===(o=+T(t)))break;l=o%2}c=c.times(c),n?c.c&&c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}return s?h:(a&&(h=u.div(h)),e?h.mod(e):n?z(h,$,m,void 0):h)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:It(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Et(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Et(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Et(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&At(this.e/Dt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Et(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Et(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Dt,h=t.e/Dt,c=r.c,d=t.c;if(!l||!h){if(!c||!d)return c?(t.s=-e,t):new M(d?r:NaN);if(!c[0]||!d[0])return d[0]?(t.s=-e,t):new M(c[0]?r:3==m?-0:0)}if(l=At(l),h=At(h),c=c.slice(),a=l-h){for((n=a<0)?(a=-a,o=c):(h=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=c.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;c[i++]=0);for(e=Ct-1;s>a;){if(c[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((h=p*(h=v[--r]%g)+(a=u*h+(c=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*c,_[n--]=h%m;_[n]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Dt,r=t.e/Dt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=At(n),r=At(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Ct|0,a[e]=Ct===a[e]?0:a[e]%Ct;return o&&(a=[o].concat(a),++r),P(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return It(t,1,Rt),null==e?e=m:It(e,0,8),z(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Dt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return It(t,-9007199254740991,Pt),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,h=r.e,c=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+T(r)))||l==1/0?(((e=Nt(a)).length+h)%2==0&&(e+="0"),l=Math.sqrt(+e),h=At((h+1)/2)-(h<0||h%2),s=new M(e=l==1/0?"5e"+h:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+h)):s=new M(l+""),s.c[0])for((l=(h=s.e)+c)<3&&(l=0);;)if(n=s,s=d.times(n.plus(i(r,n,c,1))),Nt(n.c).slice(0,l)===(e=Nt(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,c=u.substr(0,n);n<_;n+=a)c+=h+u.substr(n,a);l>0&&(c+=h+u.slice(n)),p&&(c="-"+c)}s=d?c+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):c}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,h,c,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(St+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+T(l));if(!f)return new M(g);for(e=new M(u),c=s=new M(u),o=h=new M(u),_=Nt(f),r=e.e=_.length-g.e-1,e.c[0]=zt[(a=r%Dt)<0?Dt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:c:l,a=y,y=1/0,l=new M(_),h.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,c=h.plus(d.times(n=c)),h=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),h=h.plus(n.times(c)),s=s.plus(n.times(o)),h.s=c.s=g.s,p=i(c,o,r*=2,m).minus(g).abs().comparedTo(i(h,s,r,m).minus(g).abs())<1?[c,o]:[h,s],y=a,p},p.toNumber=function(){return+T(this)},p.toPrecision=function(t,e){return null!=t&&It(t,1,Rt),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Ft(Nt(i.c),n):Lt(Nt(i.c),n,"0"):10===t&&S?e=Lt(Nt((i=z(new M(i),_+n+1,m)).c),i.e,"0"):(It(t,2,x.length,"Base"),e=s(Lt(Nt(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return T(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Ht=class{key;left=null;right=null;constructor(t){this.key=t}},Ut=class extends Ht{constructor(t){super(t)}},Wt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Bt=class t extends Wt{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new Ut(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Ut(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Ut(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new Vt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Gt(this.wrap())}[Symbol.toStringTag]="[object Set]"},jt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Gt=class extends jt{getValue(t){return t.key}},Vt=class extends jt{getValue(t){return[t.key,t.key]}},Kt=t=>()=>t,Yt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Kt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Jt(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):Kt(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var Zt=t=>t,Xt=t=>{if(t){const e=new Bt(Yt(t)),i=new Bt(Yt(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new qt(0),y:new qt(0)}),o}return Zt},Qt=t=>({set:t=>{te=Qt(t)},reset:()=>Qt(t),compare:Yt(t),snap:Xt(t),orient:Jt(t)}),te=Qt(),ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),ie=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},se=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),oe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ne=t=>oe(t,t).sqrt(),re=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return se(o,s).div(ne(o)).div(ne(s))},ae=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return oe(o,s).div(ne(o)).div(ne(s))},le=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},he=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},ce=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:ve.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:re(this.point,t.point,s.point),cosine:ae(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},de=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},pe=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(h))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(h))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),c=h.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&c.isLessThan(d))return 1;if(t.isLessThan(e)&&c.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(h)?-1:l.isGreaterThan(h)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++fe,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ce.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ce(o,!0),h=new ce(n,!1);return new t(l,h,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return te.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=ie(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ee(e,r)&&0===this.comparePoint(r),h=ee(i,o)&&0===t.comparePoint(o),c=ee(e,a)&&0===this.comparePoint(a),d=ee(i,n)&&0===t.comparePoint(n);if(h&&l)return d&&!c?n:!d&&c?a:null;if(h)return c&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&c)return null;if(d)return n;if(c)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return he(i,s,t.x);if(s.x.isZero())return he(t,e,i.x);if(e.y.isZero())return le(i,s,t.y);if(s.y.isZero())return le(t,e,i.y);const o=se(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=se(n,e).div(o),a=se(n,s).div(o),l=t.x.plus(a.times(e.x)),h=i.x.plus(r.times(s.x)),c=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(h).div(2),y:c.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ee(s,p)?te.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ce(e,!0),n=new ce(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ce.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ce.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ye=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=te.snap({x:new qt(t[0][0]),y:new qt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;ege.run("union",t,e),ke=(t,...e)=>ge.run("intersection",t,e),xe=(t,...e)=>ge.run("difference",t,e);function Se(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Me(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function Ce(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function De(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function ze(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function Te(t){const e=[],i=new Set;for(const s of t||[]){const t=Pe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*h,y:r+l*c,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function Ae(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,h=n;for(const e of Te(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],c=Math.min(o,r),d=Math.max(o,r),p=c>0?c:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(c+d)/2;return{wallA:m(c),wallB:m(d),sideA:u,sideB:_,midA:m((c-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ne(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"door"===t?1:0;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function Ee(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Oe(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function Fe(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function We(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let h=0,c=0,d=0;for(let e=0;e1e-9?[c/(3*h),d/(3*h)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ie([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=h,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Be(t)||t[0]}function Be(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(qe(s,t,e))return s;for(let s=0;sGe(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ge(e,t,i)))}function Ye(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ze(t,e,i){for(let s=0;s1&&Ee(i[0],i[i.length-1],e)&&i.pop(),i}function Qe(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function ti(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function ei(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const di=["automation","script","scene"];const pi="—",ui="{}";function _i(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function mi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=_i(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function gi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function fi(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function vi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return pi;const n=(e?.attr||"").trim(),r=gi(t,i,n||null);if(null===r)return pi;return fi(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const yi=20;function bi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function wi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const $i="#55606c",ki=.55;function xi(t){const e=t?.settings||{},i=!t?.plan_url;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:"string"==typeof e.room_color&&/^#[0-9a-f]{6}$/i.test(e.room_color)?e.room_color:$i,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):ki,fill:["lqi","light","temp","glow"].includes(e.fill_mode)?e.fill_mode:"none",tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:"string"==typeof e.bg_color&&/^#[0-9a-f]{6}$/i.test(e.bg_color)?e.bg_color:null,hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Si(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return"string"==typeof i&&/^#[0-9a-f]{6}$/i.test(i)?i:""}const Mi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}},Ci=/^#[0-9a-f]{6}$/i;function Di(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Mi)){const s=Mi[t],o=i[t];e[t]={c:o&&"string"==typeof o.c&&Ci.test(o.c)?o.c:s.c,a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function Pi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function zi(t,e,i,s,o,n,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:Pi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}const Ti={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},Ri=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function Ai(t){for(const e of[...Object.values(Ti),...Ri])if(t===e[0]||t===e[1])return e;return null}function Ni(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Ai(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=Ti[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Ai(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Ei(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),o=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,n=i.rgb_color;if(Array.isArray(n)&&n.length>=3&&n.every(t=>Number.isFinite(t)))return{c:`rgb(${n[0]}, ${n[1]}, ${n[2]})`,bri:o};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:o}}return{c:e,bri:o}}function Ii(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,h;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),c=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*c>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+c*_*u],g=[i[0]+a*_*u,i[1]+c*_*u],f=[e[0]-a*_*u,e[1]-c*_*u],v=[i[0]-a*_*u,i[1]-c*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),$=b(g),k=b(f),x=b(v),S=Math.max(Math.min(w,$),Math.min(k,x)),M=Math.min(Math.max(w,$),Math.max(k,x));if(!(M-S>1e-9))return null;l=S,h=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),h=Math.atan2(i[1]-t[1],i[0]-t[0])-l;h>Math.PI;)h-=2*Math.PI;for(;h<-Math.PI;)h+=2*Math.PI}const c=o*Math.PI/180;if(Math.abs(h)>c){const t=l+h/2;h=c*Math.sign(h),l=t-h/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+h*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Oi(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>qe(l,t,1e-9))}function Fi(t){return t.startsWith("light.")||t.startsWith("switch.")}function Li(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*c+(o[1]-n[1])*d,_=(r[0]-n[0])*c+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(h,Math.max(u,_));g-m>i&&s.push([n[0]+c*m,n[1]+d*m,n[0]+c*g,n[1]+d*g])}}return s}function qi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function Hi(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*h+(s[1]-t[1])*c,a=(s[2]-t[0])*h+(s[3]-t[1])*c,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+h*p,t[1]+c*p,t[0]+h*e,t[1]+c*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+h*p,t[1]+c*p,n[0],n[1]])}return s}function Ui(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=Gi(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function Gi(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function Vi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Ki(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Yi(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ji(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Zi(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const Xi=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Qi=(t,e)=>[t[0]-e[0],t[1]-e[1]],ts=(t,e)=>[t[0]+e[0],t[1]+e[1]],es=(t,e)=>t[0]*e[0]+t[1]*e[1],is=t=>Math.hypot(t[0],t[1]);function ss(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function rs(t,e,i,s){const o=s||ns(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function as(t,e,i,s){const o=[],n=Qi(i,e),r=is(n);if(rs||c>s)continue;const d=es(Qi(n,e),a),p=es(Qi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function ls(t,e,i,s,o){const n=as(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>os(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=es(Qi(e,h),u),y=es(Qi(i,h),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wis(Qi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=is(Qi(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function cs(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],h=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],c=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=h(e);o<=c||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ps(t,e,i){if(Ve(t,e,i))return!0;if(Ge(t,e,i)||Ge(e,t,i))return!1;let s=0;try{const i=ke([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of i)t?.[0]&&(s+=Ye(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function us(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=rs(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[ts(i.a,n),ts(i.b,n)]];continue}const t=as(e.poly,i.a,i.b,o);if(!t.length)continue;const a=ls(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[ts(t,n),ts(e,n)]))}for(const t of e)os([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function _s(t,e,i){for(const s of e)for(let e=0;ei)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-i&&h+t.length/2<=a+i)return!0}return!1}function ms(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!cs(o))return!1;const l=ss(s.poly),c=ss(o);if(Math.abs(c)t.find(t=>t.id===e).poly),d=t.map(h);for(const t of ms(e,c,2*r)){const e=a.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function fs(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const h=t.filter(t=>t.id!==i).map(t=>t.poly),c=fs(l,e,h,s,o,2*a),d=c.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ms(e,[l.poly],2*a)){const e=c.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ys(t,e,i){const s=i/e;return Ye(t)*s*s/1e4}function bs(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const ws=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],$s=t=>Math.min(1,Math.max(0,t));function ks(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=ws[ws.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Ie(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Ss(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Ms(t,e,i,s){return[[t[0],t[1]],[e[0],e[1]],[e[0]+i[0]*s,e[1]+i[1]*s],[t[0]+i[0]*s,t[1]+i[1]*s]]}function Cs(t,e){try{const i=ke([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]),s=[];for(const t of i){const e=t?.[0];!Array.isArray(e)||e.length<4||s.push(e.slice(0,e.length-1).map(t=>[t[0],t[1]]))}return s}catch{return[]}}function Ds(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],h=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),c=[];for(const i of e){if(!(i.length>0))continue;const e=xs(i,t);if(!e||!Ss(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],$=h*i.length,k=Cs(Ms(b,w,l,$),d);if(!k.length)continue;const x=l[0]*_[0]+l[1]*_[1];c.push({openingId:i.id,roomId:e.roomId,polys:k,a:b,b:w,dir:l,len:$,normal:_,depth:$*x})}return c}function Ps(t){return Math.round(10*(Number(t)||0))/10}const zs={clear:1,sunny:1,"clear-night":1,windy:1,exceptional:1,partlycloudy:.7,"windy-variant":.7,cloudy:.4,overcast:.25,fog:.25,rainy:0,pouring:0,snowy:0,"snowy-rainy":0,hail:0,lightning:0,"lightning-rainy":0};const Ts=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Rs(t,e){const i=Ts(e?.north_deg);return null!==i?i:Ts(t?.north_deg)}function As(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Ns(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Es(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Is(t){const e=t?.states?.["sun.sun"]?.attributes,i=Number(e?.azimuth),s=Number(e?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Os={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Fs=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ls=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function qs(t,e,i){return Ls(e)?[t.x,t.y]:i([t.x,t.y])}const Hs=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Us=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Ws=(t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Us(n,e,i):s};function Bs(t,e){return{color:t.color,opacity:Fs(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Fs(t.fillOpacity,.25)}:{}}}function js(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ls(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Gs(t){const e=js(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Vs(t,e,i,s,o,n,r,a){const l=Ls(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*h+g*d,v=_+m*c+g*p,y=s-f,b=o-v;let w=(y*h+b*c)*(e>0?1:-1),$=(y*d+b*p)*(i>0?1:-1),k=null;if(n){const e=w/Math.max(t.w,a),i=$/Math.max(t.h,a);k=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*k,$=t.h*k}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round((k??1)/o)*o);w=t.w*r,$=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,$=w*(t.h/Math.max(t.w,a))):($=Math.round($/r)*r,w=$*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,$=Math.round($/r)*r;w=Math.max(a,w),$=Math.max(a,$);return{x:f+(e>0?w/2:-w/2)*h+(i>0?$/2:-$/2)*d-w/2,y:v+(e>0?w/2:-w/2)*c+(i>0?$/2:-$/2)*p-$/2,w:w,h:$,angle:Ls(t.angle)||void 0}}const Ks=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Ys=15;function Js(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Zs(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Xs(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Qs(t,e,i){const s=Number(e)>0?Number(e):5;return Zs(t)/s*i}function to(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function eo(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function io(t,e,i){const s=to((t[0]+e[0])/2,i),o=to((t[1]+e[1])/2,i),[n,r]=eo(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,h=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(h)},${o.toFixed(h)}@${l.toFixed(4)}`}function so(t,e,i,s){return 1===s?io(t,e,i):io([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function oo(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function no(t,e,i,s,o){const n=o>0?o:1;return{key:so(t,e,s,n),cm:Zs(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function ro(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function ao(t,e){const[i,s]=eo(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function lo(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function ho(t,e,i,s,o=1){if(!t?.length)return null;const n=so(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,h=(e[1]+i[1])/2,c=ao(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of ro(t,a))if(lo(e.ang,c)&&Math.hypot(e.x-l,e.y-h)<=d)return e.w;return null}function co(t,e,i,s,o=1){const n=ho(t,e,i,s,o);return n&&n.cm>0?Zs(n.cm):0}function po(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Te(e);for(const t of r)n.add(so([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=oo(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],h=n[1]-i[1],c=Math.hypot(a,h);if(c<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return lo(ao(i,n),ao(e,s))&&Ro(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ro(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!lo(ao(i,n),ao(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*h-(t[1]-i[1])*a)/c;if(o(e)>l||o(s)>l)return!1;const r=c*c,d=((e[0]-i[0])*a+(e[1]-i[1])*h)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*h)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/c});return!d})(t))&&t.cm>=1&&t.cm<=100)}function uo(t,e,i,s=8){const[o,n]=eo(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function _o(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,h=[];for(const o of t){let t="",c=null;const d=oo(o,r);if(d)for(let o=0;oa||Ro(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[h[0]+(p[0]-h[0])*e,h[1]+(p[1]-h[1])*e]};c=[g(d[0]),g(d[1])],t=so(c[0],c[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=ro([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ro(n.x,n.y,l[0],l[1],h[0],h[1])>a)continue;const g=c[0]+(d[0]-c[0])*Math.max(0,Math.min(1,m)),f=c[1]+(d[1]-c[1])*Math.max(0,Math.min(1,m)),[v,y]=eo(c,d),b=Math.max(s*r,1e-6);t=so([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),h.push(c?no(c[0],c[1],o.cm,s,r):{...o,key:t,cm:Zs(o.cm)}))}return h}function mo(t,e,i,s,o,n=1){const r=so(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,no(e,i,s,o,n)]}function go(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Mo(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Io(i,t),o=ko(i,t);if(s&&o)return`${xo(s)} ${xo(So(o))}`}}let s="";for(let i=0;i.05)&&Ro(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function yo(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function bo(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const h=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([h[0]+.001*a,h[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function wo(t,e){const i=t[0]*e[1]-t[1]*e[0],s=t[0]*e[0]+t[1]*e[1];return Math.abs(i)<1e-9&&s>0}function $o(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function ko(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]+p*c,e[1]+u*c],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=$o($,b,k,w),S=Math.max(c,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}c>0&&s.push([t[o][0]+p*c,t[o][1]+u*c]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),c>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function xo(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Pe(r);if(!a||a.length<3)return null;const l=yo(s,o),h=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Pe(i);if(t)for(const e of Li(a,t,l))h.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])h.push([t[0],t[1]],[t[2],t[3]]);for(const t of n||[]){const e=oo(t,o);e&&h.push(e[0],e[1])}const c=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:c,parent:d,orig:a}}function Co(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=Zs(h.cm),r.add(h.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,h=Math.max(.5*i,1e-9)*l,c=ro(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=ao(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of c){if(r.has(t.w.key))continue;if(!lo(t.ang,a))continue;const e=oo(t.w,l);let i=!1,n=0;if(e){if(!lo(ao(e[0],e[1]),a))continue;if(Ro(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>h||Ro(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>h)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ro(t.x,t.y,s[0],s[1],o[0],o[1])>h)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Pe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Pe(s);if(t)for(const e of Li(o,t,i))n.push(e)}return n}(t,e,yo(o,a)),c=function(t,e,i,s,o){const n=yo(s,o),r=[];for(let a=0;aRo(c,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,h,s,o,a),d=Co(i,l,o,a),p=d.map((t,e)=>c[e]&&t>0?Qs(t,n,r)/2:0);return{...l,kinds:c,cms:d,offsets:p}}function Po(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(h)for(let t=0;t0&&!l.has(h.key)&&(l.add(h.key),a.push(h));const h=[];for(const a of t||[]){if(!a?.id)continue;const l=Do(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&h.push({a:[c[0],c[1]],b:[d[0],d[1]],key:so(c,d,s,r),cm:o,len:p}),t=n+1}}}h.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const c=[],d=new Set,p=new Set,u=4*yo(s,r);for(const t of h){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&lo(ao(e.a,e.b),ao(t.a,t.b))&&Ro(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ro(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)));return c}function To(t,e,i,s,o,n,r,a=1){const l=yo(o,a),h=(s[0]+s[2])/2,c=(s[1]+s[3])/2,d=ao([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Po(t,e,i,o,n,r,a)){if(!lo(ao(s.a,s.b),d))continue;const t=Ro(h,c,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),h=Pe(l);if(!h||h.length<3)return null;if(!i?.length)return h.map(t=>[t[0],t[1]]);const c=Do(t,e,i,s,o,n,r,a);return c&&c.offsets.some(t=>t>0)&&ko(c.poly,c.offsets)||h.map(t=>[t[0],t[1]])}function No(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Eo(t,e,i,s=[],o,n,r,a=1,l=[]){if(!e?.length&&!l.length)return null;const h=[];let c=0;for(const s of t||[]){if(!s?.id)continue;const l=Do(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(c=Math.max(c,2*t));const d=Io(l.poly,l.offsets),p=ko(l.poly,l.offsets);d&&h.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));c=Math.max(c,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*yo(s,r),l=new Map;for(const a of Po(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const h=[...l.values()];if(h.length<2)return[];const c=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])c.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||c.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of c){const e=h.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),c,u,p]:[t.slice(),p,u,c])}}return d}(t,e,i,o,n,r,a);try{const t=t=>{const e=No(t.outset);return t.inset?xe(e,No(t.inset)):e};let e=h.length?t(h[0]):null;for(let i=1;i0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),n=Math.sin(i),r=-n,l=s,h=t.length/2,d=1.25*Math.max(c,o*a),p=[[t.x-s*h-r*d,t.y-n*h-l*d],[t.x+s*h-r*d,t.y+n*h-l*d],[t.x+s*h+r*d,t.y+n*h+l*d],[t.x-s*h+r*d,t.y-n*h+l*d]];e&&(e=xe(e,No(p)))}for(const t of l)t.length<3||(e=e?$e(e,No(t)):[No(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+xo(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:c,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(!h||h.poly.length<3||!h.offsets.some(t=>t>0))continue;const c=Io(h.poly,h.offsets),d=ko(h.poly,h.offsets);if(!c||!d)continue;const p=`${xo(c)} ${xo(So(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=so(h.poly[t],h.poly[(t+1)%h.poly.length],s,r),_=h.kinds[t]||"outer",m=h.cms[t],g=Qs(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),h=l.map(t=>xo(t)).join(" ");return s.length||h?{d:[s.map(t=>t.d).join(" "),h].filter(Boolean).join(" "),depthUnits:c,fillRule:"nonzero"}:null}}function Io(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);So(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-h*r],n=[e[0]-c*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=$o($,b,k,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-h*r]),a>0&&s.push([t[o][0]-c*a,t[o][1]-d*a])}return s.length>=3?s:null}function Oo(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const h=Pe(l);if(h&&h.length>=3){const c=Do(t,l.id,e,i,s,o,n,r),d=c&&c.offsets.some(t=>t>0)?Io(c.poly,c.offsets):null,p=d||h;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Fo(t,e,i,s,o,n,r=1){let a=null;for(const i of t||[]){const t=Pe(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=co(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const h=Qs(l,o,n),c=Pe(a.room),[d,p]=bo(c,a.edge),u=e.flip_v?-1:1,_=h/2;return{ox:d*_*u,oy:p*_*u,cm:l}}const Lo=150;function qo(t){return Number.isFinite(t)?Math.max(1,Math.min(Lo,t)):1}function Ho(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Uo=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Wo(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Bo(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=Qs(i,s,o)/2,h=-r/a*l,c=n/a*l;return[[t[0]+h,t[1]+c],[e[0]+h,e[1]+c],[e[0]-h,e[1]-c],[t[0]-h,t[1]-c]]}function jo(t,e,i){const s=Number(e)>0?Number(e):5,o=qo(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Ho(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*h-e*c,r+t*c+e*h])}function Go(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Uo(t));return e.length?$e(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return xe(Uo(t),i)}catch{}let i=[Uo(t)];for(const t of e)if(!(t.length<3))try{i=xe(i,Uo(t))}catch{}return i}function Ko(t){let e=0;for(const i of t||[])if(i?.length){e+=Ye(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jo(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zo(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const h=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+h*r),e[1]-(i[1]+h*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*c+h*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+h/p*g],v=[e[0]+c/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function Zo(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function Xo(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(qo(t.cm)-qo(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Ho(t.angle)-Ho(e.angle));return Math.min(s,90-s)<=1e-6}const Qo=1e3;function tn(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const en=.01,sn=100;function on(t,e=1e3){const i=tn(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(sn,Math.max(en,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(sn,Math.max(en,n)):o,l=Number.isFinite(r)&&r>0?Math.min(sn,Math.max(en,r)):o,h=Number(t?.plan_x),c=Number(t?.plan_y),d=Ls(t?.plan_angle);return{x:i.x+(Number.isFinite(h)?mn(h):0)*e,y:i.y+(Number.isFinite(c)?mn(c):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function nn(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function rn(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qo,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Qo,i[1]*e,i[2]*Qo,i[3]*e],bg:t.plan_url?{href:Gi(t.plan_url),...on(t,Qo)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...nn(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Qo:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qo:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qo,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qo,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qo,t.a[1]*e],b:[t.b[0]*Qo,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*Qo,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Ho(t.angle)}}))}}):[]}const an=5e3,ln=an*Qo,hn=240,cn=Qo/hn,dn=1/hn;function pn(t){if(!Number.isFinite(t))return t;const e=Math.round(t*hn/Qo)*Qo/hn;return Math.abs(e-t)<=1e-9*cn?t:e}function un(t){return{x:pn(t.x),y:pn(t.y)}}function _n(t){return Number.isFinite(t)?Math.min(ln,Math.max(-ln,t)):0}function mn(t){return Number.isFinite(t)?Math.min(an,Math.max(-an,t)):0}const gn=1/3,fn=200;function vn(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function yn(t){return t.poly&&t.poly.length?vn(t.poly):null==t.x||null==t.y?null:vn([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function bn(t,e){const i=[];for(const e of t.rooms||[]){const t=yn(e);t&&i.push(t)}if(t.bg){const e=vn(js(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=vn([t]);e&&i.push(e)}else i.push(t);return i}const wn=t=>{if(!t.length)return 0;const e=[...t].sort((t,e)=>t-e),i=e.length>>1;return e.length%2?e[i]:(e[i-1]+e[i])/2},$n=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function kn(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=fn),n<30&&(s=s+n/2-100,n=fn);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function xn(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ln&&Math.abs(t.maxX)<=ln&&Math.abs(t.minY)<=ln&&Math.abs(t.maxY)<=ln);if(!n.length)return{core:null,all:null,outliers:0};const r=$n(n);if(n.length<4){const t=kn(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),h=wn(a),c=wn(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-h),Math.abs(l[e]-c))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*gn?n.filter((t,e)=>!u[e]):n;return{core:kn($n(m)||r,i),all:kn(r,i),outliers:m===n?0:_}}function Sn(t,e,i=.05){const s=xn(bn(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,Qo,Qo];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Mn(t){const e=Sn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Cn(t){const e=[];for(const i of t.rooms||[]){const t=yn(i);t&&e.push(t)}const i=xn(e,{pad:0}).core;return i?Math.max(Qo,Math.min(ln,Math.max(i.w,i.h))):Qo}function Dn(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Cn(e)*n/o}const Pn=[1,2,5,10,20,50,100,200,500,1e3];function zn(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Tn(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)]}const Rn=["furniture","appliance","sanitary","other"],An=[{id:"sofa",group:"furniture",w:220,h:90,g:[["r",0,0,1,1],["l",.09,.26,.91,.26],["l",.09,.26,.09,1],["l",.91,.26,.91,1],["l",.5,.26,.5,1]]},{id:"armchair",group:"furniture",w:90,h:85,g:[["r",0,0,1,1],["l",.14,.28,.86,.28],["l",.14,.28,.14,1],["l",.86,.28,.86,1]]},{id:"coffee_table",group:"furniture",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",w:120,h:60,g:[["r",0,0,1,1],["r",.63,.07,.31,.86],["l",.63,.5,.94,.5]]},{id:"bed_double",group:"furniture",w:160,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.06,.1,.4,.15],["r",.54,.1,.4,.15],["l",0,.33,1,.33]]},{id:"bed_single",group:"furniture",w:90,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.15,.1,.7,.15],["l",0,.33,1,.33]]},{id:"nightstand",group:"furniture",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["e",.29,.31,.15,.15],["e",.71,.31,.15,.15],["e",.29,.71,.15,.15],["e",.71,.71,.15,.15]]},{id:"dishwasher",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["r",.1,.12,.8,.76],["e",.5,.5,.27,.27],["e",.5,.5,.13,.13]]},{id:"washer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["e",.5,.57,.14,.14]]},{id:"dryer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["p",.36,.5,.5,.64,.64,.5]]},{id:"tv",group:"appliance",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",w:90,h:25,g:[["r",0,0,1,1],["l",.05,.55,.95,.55],["l",.05,.79,.95,.79]]},{id:"water_heater",group:"appliance",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",w:90,h:90,g:[["r",0,0,1,1],["l",0,0,1,1],["l",1,0,0,1],["e",.5,.5,.08,.08]]},{id:"sink",group:"sanitary",w:60,h:45,g:[["r",0,0,1,1],["e",.5,.6,.34,.3],["e",.5,.15,.07,.07]]},{id:"kitchen_sink",group:"sanitary",w:80,h:60,g:[["r",0,0,1,1],["r",.06,.24,.44,.64],["r",.54,.24,.4,.64],["e",.5,.12,.06,.06]]},{id:"bidet",group:"sanitary",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",w:100,h:280,g:[["r",0,0,1,1],["l",0,.111,1,.111],["l",0,.222,1,.222],["l",0,.333,1,.333],["l",0,.444,1,.444],["l",0,.556,1,.556],["l",0,.667,1,.667],["l",0,.778,1,.778],["l",0,.889,1,.889],["l",.5,.93,.5,.06],["p",.38,.16,.5,.06,.62,.16]]},{id:"fireplace",group:"other",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",w:40,h:40,g:[["e",.5,.5,.22,.22],["l",.5,.28,.5,.02],["l",.5,.72,.5,.98],["l",.28,.5,.02,.5],["l",.72,.5,.98,.5],["l",.34,.34,.13,.13],["l",.66,.66,.87,.87],["l",.66,.34,.87,.13],["l",.34,.66,.13,.87]]},{id:"rug",group:"other",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],Nn=new Map(An.map(t=>[t.id,t]));function En(t){return t&&Nn.get(t)||null}function In(t){return An.filter(e=>e.group===t)}function On(t,e,i=cn,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Fn=5e-4,Ln=an;function qn(t){return Number.isFinite(t)?Math.max(Fn,Math.min(Ln,t)):Fn}const Hn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Un(t,e,i){const s=En(t);if(!(s&&e>0&&i>0))return"";const o=t=>Hn(t*e),n=t=>Hn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,h]=t;r.push(`M${o(s-l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Bn(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,h,c]=o,d=h-s,p=c-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Wn(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function jn(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Gn(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Vn(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Kn(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Vn(t.a)||!Vn(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function Yn(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Jn(t,e){return Yn(t,e).q}function Zn(t,e,i,s,o){const n=Yn(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+c*g,h+d*g]}function Xn(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt.seg)}(e,s);let n=null;for(const r of e||[]){if(!r?.id)continue;const e=Pe(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of o)if(Zi([(l[0]+h[0])/2,(l[1]+h[1])/2],t)<2*s){p=!0;break}p||(!n||dt?.id),r=Kn(e);if(r.length)return rr(r,t,i,s).map(t=>Gn(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tjn([t[0],t[1]],[t[2],t[3]],e))}function ir(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tZi(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function sr(t,e,i){let s=null;for(const o of e){const e=Zi(t,o);e<=i&&(!s||e.05)continue;const d=co(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-c[0],p[1]-c[1]);u0?l:n}(t,e,i,s,o,n);return mo(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function nr(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(Zi(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Zi([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])Zi(e,t)<=2*i&&s.push(e);return s}function rr(t,e,i,s){const o=Kn(t);if(!o.length)return[];const n=Xn(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=Gn(t,i),o=e[0],l=e[1],h=e[2]-o,c=e[3]-l,d=Math.hypot(h,c);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,h=(t[2]-o)*p+(t[3]-l)*u,c=Math.max(0,Math.min(r,h)),m=Math.min(d,Math.max(r,h));if(m-ct.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=h&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=h);c||(c={pair:t,origin:[e[0],e[1]],ux:n,uy:r,ranges:[]},l.push(c));const d=(e[0]-c.origin[0])*c.ux+(e[1]-c.origin[1])*c.uy,p=(e[2]-c.origin[0])*c.ux+(e[3]-c.origin[1])*c.uy;c.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const c=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&c.push(jn(e,o,i))}}return c}function ar(t,e,i,s){const o=Kn(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=Gn(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,h=a;for(let t=0;t{const e=Yn(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=c[0]-n[0],r=c[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(Yn([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),h=d(a);break}}n.push(jn(l,h,s))}return n}class lr{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=Gi(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{const e=Number(t);return Number.isFinite(e)?e:null};function pr(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dr(e.x)&&null!=dr(e.y)?{x:dr(e.x),y:dr(e.y),a:dr(e.a??e.angle??e.theta)}:null;let s=null;const o=t.path?.points??t.path;if(Array.isArray(o)&&o.length){s=[];for(const t of o){const e=dr(Array.isArray(t)?t[0]:t?.x),i=dr(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const n=[],r=t.rooms,a=Array.isArray(r)?r.map((t,e)=>[String(t?.id??e),t]):r&&"object"==typeof r?Object.entries(r):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim();let s=dr(e.cx??e.center?.x),o=dr(e.cy??e.center?.y);if(null==s||null==o){const t=dr(e.x0),i=dr(e.y0),n=dr(e.x1),r=dr(e.y1);null!=t&&null!=i&&null!=n&&null!=r&&(s=(t+n)/2,o=(i+r)/2)}if(null!=s&&null!=o||(s=dr(e.x),o=dr(e.y)),i&&null!=s&&null!=o){const r={id:t,name:i,cx:s,cy:o},a=dr(e.x0),l=dr(e.y0),h=dr(e.x1),c=dr(e.y1);null!=a&&null!=l&&null!=h&&null!=c&&(r.x0=Math.min(a,h),r.y0=Math.min(l,c),r.x1=Math.max(a,h),r.y1=Math.max(l,c)),n.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s?{pos:i,path:s,rooms:n,mapId:l}:null}function ur(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const _r=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function mr(t,e){const i=new Map(e.map(t=>[_r(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(_r(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,h=0,c=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,h+=m,c+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,h]=u,c=e*(n*h-r*l)-i*(o*h-r*a)+s*(o*l-n*a);if(!Number.isFinite(c)||Math.abs(c)<1e-9)return null;const d=[(n*h-r*l)/c,(s*l-i*h)/c,(i*r-s*n)/c,(r*a-o*h)/c,(e*h-s*a)/c,(s*o-e*r)/c,(o*l-n*a)/c,(i*a-e*l)/c,(e*n-i*o)/c];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,h]),g=_([c,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:cr(n,s)}:null}function gr(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,h]=t[n],c=l-r,d=h-a,p=Math.hypot(c,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function fr(t){return"cleaning"===t||"returning"===t||"on"===t}const vr={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function yr(t){const[e,i]=vr[t.rot]||[1,0],s=t.mir?-1:1;return[t.s*e*s,-t.s*i,t.ox,t.s*i*s,t.s*e,t.oy]}function br(t,e,i,s){const[o,n]=hr(yr(e),i,s),r=yr({...t,ox:0,oy:0}),[a,l]=hr(r,i,s);return{...t,ox:o-a,oy:n-l}}function wr(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const $r={availability:"available",status:"alarm",activity:"none"},kr=new Set(["motion","vibration","sound"]),xr=new Set(["occupancy","presence"]),Sr=new Set(["door","window","garage_door","opening"]),Mr=new Set(["running","power"]),Cr=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Dr=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Pr=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),zr=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Tr=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Rr=t=>String(t??"").trim().toLowerCase();function Ar(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],o=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(o))return!0;if([i.translation_key,i.original_name,i.name].map(Rr).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const n=Rr(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(n)||/(?:^|[\s._-])питание$/.test(n)}function Nr(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Rr(t?.[e]);if(Dr.has(i)||Pr.has(i))return i}return""}function Er(t,e){const i=t?.states?.[e],s=i?Rr(i.state):"__missing__",o=String(e||"").split(".")[0],n=Rr(i?.attributes?.device_class),r={eid:e,state:s,availability:Tr(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Xi.has(e))}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return kr.has(n)?{...r,edge:"rising"}:xr.has(n)?{...r,activity:"on"===s?"presence":"none"}:Sr.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:Mr.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=Nr(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Rr):[],o=!Pr.has(s)&&(zr.has(s)||Dr.has(s)||e.includes(s));return(t?Dr.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=Nr(i.attributes);return Dr.has(a)||Dr.has(s)&&!Pr.has(s)?{...r,status:"working",activity:"running"}:r}function Ir(t,e){if(!(t=>!!t&&!Tr(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}function Or(t){const e={};for(const[i,s]of Object.entries(t.entities))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}function Fr(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Lr(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const qr=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Hr=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Ur(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of qr){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Hr(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Rr(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return kr.has(i)||xr.has(i)||Sr.has(i)||Mr.has(i)||"moving"===i||Cr.has(i)}(t,e.eid));if(n.length)return Hr(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length){const e=Hr(r);return[(e.find(e=>Ar(t,e.eid))||e[0]).eid]}const a=[];for(const t of wt)a.push(...Hr(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...Hr(o.filter(t=>!wt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function Wr(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>Lr(t,e.eid));if(i)return i.eid}return Ur(t,e)[0]}function Br(t,e,i=[]){return[...new Set(jr(t,e,i))].filter(t=>Fi(t))}function jr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function Gr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).some(t=>t.startsWith("switch.")&&s.has(t))}function Vr(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Kr(t,e){const i=Br(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light||Gr(e.marker?.binding,e.marker?.controls,e.entities)){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Fi),s=t.primary&&Fi(t.primary)?t.primary:null;return(e&&Fi(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||Ur(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function Yr(t,e,i){const s=[],o=new Set;for(const n of e){if(n.hidden||null!=i&&!Vr(n,i))continue;const e=Kr(t,n);for(const{eid:i,via:r}of e)i&&!o.has(i)&&(o.add(i),s.push({eid:i,device:n,via:r,on:"on"===t.states[i]?.state}))}return s}function Jr(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function Zr(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return Qe(i)}function Xr(t,e){for(const i of e){if(!Lr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function Qr(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function ta(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ea(t,e){for(const i of e){if(!ta(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function ia(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function sa(t,e,i,s,o){const n=bt(e,i,o);if(n!==yt)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=vt[e];if(t)return t}return null}(r)??yt}function oa(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function na(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function ra(t,e,i,s){const o=Br(e.binding,e.controls,t.entities).filter(t=>!na(i,t,s));t.marker=e,t.controls=o,e.hidden&&(t.hidden=!0),e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function aa(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,showAll:r,firstSpaceId:a,loc:l,iconRules:h}=t,c=!1!==o.group_lights,d=oa(s),p=ia(e,c).filter(t=>!na(e,t.eid,d)),u=new Set(p.map(t=>t.area)),_=Or(e),m=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||m.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),f={},v=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(m.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!o.filter_seeded)continue;const d=_[t.id]||[],p=Fr(e,t,d),y=!o.filter_seeded;if(y&&!r){if(n.has(p))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===p&&t.via_device_id)continue}const b=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=b+"|"+s;let $=sa(e,b,t.model,d,h);if(d.some(t=>t.startsWith("lock."))&&($="mdi:lock"),y&&!r&&c&&"mdi:lightbulb"===$&&u.has(s))continue;f[w]=(f[w]||0)+1;const k=f[w]>1?b+" "+f[w]:b,x={id:t.id,name:k,model:t.model||"",area:s,space:i[s],icon:$,entities:d,bindingKind:"device",bindingRef:t.id,pdfs:[]};x.primary=Wr(e,d,$),"mdi:thermometer"!==$&&"mdi:air-filter"!==$||(x.temp=Xr(e,d)),x.primary&&ta(e,x.primary)&&(x.hum=ea(e,d)),v.push(x)}for(const t of p)i[t.area]&&(m.has("entity:"+t.eid)||v.push({id:"lg_"+t.eid,name:t.name,model:l("device.light_group"),area:t.area,space:i[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of s){if(t.removed)continue;if(t.hidden&&!o.filter_seeded)continue;const[s,n]=t.binding.split(":");if("device"===s){const s=e.devices[n],o=t.area||s?.area_id||"",r=o&&i[o]||t.space||a,c=s&&_[s.id]||[];let p=s?sa(e,s.name_by_user||s.name||"",s.model,c,h):"mdi:help-circle";c.some(t=>t.startsWith("lock."))&&(p="mdi:lock");const u={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:o,space:r,icon:p,entities:c,bindingKind:"device",bindingRef:n};u.primary=Wr(e,c,p),"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Xr(e,c)),u.primary&&ta(e,u.primary)&&(u.hum=ea(e,c)),u.primary&&ta(e,u.primary)&&(u.hum=ea(e,c)),ra(u,t,e,d),v.push(u)}else if("entity"===s){if(na(e,n,d))continue;const s=e.entities[n],o=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=o&&i[o]||t.space||a,l=e.states[n],c=s?.name||l?.attributes?.friendly_name||n;let p=sa(e,c,"",[n],h);n.startsWith("lock.")&&(p="mdi:lock");const u={id:t.id,name:c,model:"",area:o,space:r,icon:p,entities:[n],primary:n,bindingKind:"entity",bindingRef:n};"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Xr(e,[n])),ta(e,n)&&(u.hum=ea(e,[n])),ra(u,t,e,d),v.push(u)}else{const s=t.area||"",o=t.space||s&&i[s]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:s,space:o,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};ra(n,t,e,d),v.push(n)}}return v}function la(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=oa(i);return"device"===o?r.devices.has(n):"entity"===o&&na(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?Xr(t,e):ea(t,e)}return null}const ha=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");var ca={"btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden devices (ghosted, this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_closewall":"Physical wall: click a dashed virtual stretch","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.closewall":"Physical wall","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary stretch","history.close_boundary":"Close boundary opening","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit door or window opening","history.move_opening":"Move door or window opening","history.delete_opening":"Delete door or window opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Doors & windows: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Door / window","opening.door":"Door","opening.window":"Window","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon","display.icon_ripple":"Icon + activity","marker.display_hint":"A yellow plate means actual work. Activity adds events, presence and movement; alarms are always visible.","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide doors and windows","space.hide_openings_tip":"The symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value instead of an icon","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Show hidden","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors and windows, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","fill.glow":"Light sources (dark house, glowing lamps)","gs.glow_group":"Light-sources fill","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius (light-sources fill)","marker.glow_radius_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.openwall":"Virtual wall","title.markup_openwall":"Virtual wall — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Crosshair cursor.","toast.openwall_pick":"Click a wall shared by two rooms","toast.openwall_shared_only":"Only a shared wall between two rooms can be opened","toast.openwall_short":"Stretch too short — pick a farther point","toast.openwall_opened_span":"Virtual stretch opened","toast.openwall_closed_span":"Virtual stretch closed","toast.closewall_pick":"Click a dashed virtual stretch","toast.closewall_use_tool":"Use “Physical wall” for this stretch","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Makes the icon glow in the “Light sources” fill even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Show hidden" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.status_found":"Position source found: {name}","vac.status_none":"The integration reports no coordinates — the robot will only be shown at its base","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_res_warn":"Matched {rooms} rooms but the fit is rough — verify and refine via “Fit manually” if needed","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the sun (day/night)","gs.bg_daynight_hint":"The stage follows the sun: neutral day, warm golden hour, deep night. Needs the compass below.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the sun features stay off.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Point the arrow at north (1° steps, 15° with Shift) or type the degrees — until then the sun features stay off.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.weather":"Weather entity","gs.weather_ph":"weather.home (optional)","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug"};const da={en:ca,ru:{"btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые устройства (полупрозрачными, только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_closewall":"Физическая стена: кликните по пунктирному виртуальному участку","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.closewall":"Физическая стена","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие участка границы","history.close_boundary":"Закрытие проёма в границе","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение дверного или оконного проёма","history.move_opening":"Перемещение дверного или оконного проёма","history.delete_opening":"Удаление дверного или оконного проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Двери и окна: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Дверь / окно","opening.door":"Дверь","opening.window":"Окно","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок","display.icon_ripple":"Значок + активность","marker.display_hint":"Жёлтая подложка означает фактическую работу. Активность добавляет события, присутствие и движение; тревога видна всегда.","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить (свет/розетки)","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери и окна не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Показать скрытые","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери и окна, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","fill.glow":"Свет по источникам (тёмный дом, пятна света)","gs.glow_group":"Заливка «Свет по источникам»","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения (заливка «Свет по источникам»)","marker.glow_radius_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.openwall":"Виртуальная стена","title.markup_openwall":"Виртуальная стена — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает виртуальный участок. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","toast.closewall_pick":"Кликните по пунктирному виртуальному участку","toast.closewall_use_tool":"Для этого участка выберите «Физическая стена»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Использовать датчик температуры устройства","marker.use_climate_temp_tip":"Кондиционеры и термостаты знают температуру комнаты (current_temperature). Показывает её плашкой у значка и учитывает в средней температуре комнаты — как обычный термометр.","marker.is_light":"Это устройство — источник света","marker.is_light_tip":"Даёт ореол в заливке «Свет по источникам» даже без light-сущности — для умного выключателя с обычными светильниками. Ореол следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Показать скрытые» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.status_found":"Источник координат найден: {name}","vac.status_none":"Интеграция не отдаёт координаты — робот будет показан только на базе","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_res_warn":"Совпало комнат: {rooms}, но привязка грубовата — проверьте и при необходимости откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за солнцем (день/ночь)","gs.bg_daynight_hint":"Фон следует за солнцем: нейтральный день, тёплый золотой час, глубокая ночь. Нужен компас ниже.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — солнечные функции выключены.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Направьте стрелку на север (шаг 1°, с Shift 15°) или введите градусы — до этого солнечные функции молчат.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.weather":"Сущность погоды","gs.weather_ph":"weather.home (необязательно)","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр"}};function pa(t,e){if(e&&e in da)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function ua(t,e,i){return wi(da[t][e]??ca[e]??e,i)}class _a extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[],e=this._lang;return[{name:"title",selector:{text:{}}},t.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:t}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:[{value:"",label:ua(e,"editor.lang_auto")},{value:"en",label:ua(e,"editor.lang_en")},{value:"ru",label:ua(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:ua(t,"editor.title"),default_floor:ua(t,"editor.default_floor"),language:ua(t,"editor.language"),icon_size:ua(t,"editor.icon_size"),show_temperature:ua(t,"editor.show_temperature"),live_states:ua(t,"editor.live_states"),show_signal:ua(t,"editor.show_signal"),kiosk:ua(t,"editor.kiosk"),cycle:ua(t,"editor.cycle")};return W`e[t.name]||t.name} @value-changed=${this._valueChanged} - >`}_valueChanged(t){const e={...this._config,...t.detail.value},i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ua.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",ua);const _a=n` + >`}_valueChanged(t){const e={...this._config,...t.detail.value},i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}_a.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",_a);const ma=n` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -1297,6 +1297,21 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .stage.mode-decor.dtool-text .decorlayer .dshape.dtext { cursor: text; } + /* Erasing a hairline must not require pixel-perfect aim. The duplicate + geometry is invisible and exists only while Erase is active. A + non-scaling stroke keeps the target comfortably wide at every zoom. */ + .decorlayer .derasehit { + fill: none; + stroke: transparent; + stroke-width: 16px; + stroke-linecap: round; + stroke-linejoin: round; + vector-effect: non-scaling-stroke; + pointer-events: none; + } + .stage.mode-decor.dtool-erase .decorlayer .derasehit { + pointer-events: stroke; + } .decorlayer .dsel { filter: drop-shadow(0 0 3px var(--hp-accent)); } @@ -2820,17 +2835,21 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .editorchrome-inner.nav-enter, .editorchrome-inner.nav-exit { animation: none; } } - /* Device info can have Edit + Open in HA + Close. A small HA dialog is - narrower than those three Russian-labelled actions; without wrapping, - HA's end-aligned footer pushed the first button outside the surface. */ + /* Device info can have Edit + Open in HA + Close. It uses a wide dialog; + wrapping remains as a phone fallback, but without a flex spacer (which + used to strand Edit alone on the first line). */ hp-dialog .row.infofooter { align-items: center; + justify-content: flex-start; flex-wrap: wrap; gap: var(--sp-3); } hp-dialog .row.infofooter .btn { flex-shrink: 0; } + hp-dialog .row.infofooter .infofooter-close { + margin-left: auto; + } @media (max-width: 480px) { hp-dialog .row.infofooter { padding: var(--sp-4) var(--sp-5); @@ -2966,16 +2985,16 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow z-index: 120; max-width: 90vw; } - `;function ma(t){const e=rn(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=xi(t.cfg.spaces.find(e=>e.id===t.spaceId)),o=t.iconSize??2.5,n=o>8?2.5:o,r={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(r[t.area]=e.id);const a=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):_t,l=gt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:mt),h=ra({hass:t.hass,areaToSpace:r,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:a,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>pa(t.lang,e),iconRules:l}),c=h.filter(e=>e.space===t.spaceId),d=c.filter(t=>!t.hidden),p=function(t,e,i){const s={},o=i/100*Cn(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=zn(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),c=a/h,d=l/Math.max(Math.ceil(e.length/h),1),p=e.map((t,e)=>({x:n.x+r+c*(e%h+.5),y:n.y+r+d*(Math.floor(e/h)+.5)}));ei(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=un(p[e]))}return s}(c,i,n),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qo,e=i.y*Qo;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},m=Array.isArray(_.walls)?_.walls:[],g=Number(_.cell_cm)>0?Number(_.cell_cm):5,f=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Bo(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...Go(o,e,i));for(const o of t.wall_columns||[])s.push(jo(o,e,i));return s}(i,g,cn);for(const t of f){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&u.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const v=Sn(i,u),y=[v.x,v.y,v.w,v.h],b=i.rooms.filter(t=>t.area||s.showBorders||"light"===Vi(s.fill,t)).map(e=>{let o="room "+(i.bg?"overlay":"yard"),n="";const r=Vi(s.fill,e);if(s.showBorders||"none"!==r){o+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a="light"===r?zi("light",null,Yr(Kr(t.hass,c,e)),null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):e.area?zi(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Jr(t,o.entities);null!=e&&s.push(e)}return Qe(s)}(t.hass,c,e.area):null,"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=Zr(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,c,e.area):null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):null;a?(o+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!s.showNames,l=Tn(e),h=e.id||G,d=e.area||G,p=e.poly?B`e.id===t.spaceId);if(!i)return null;const s=xi(t.cfg.spaces.find(e=>e.id===t.spaceId)),o=t.iconSize??2.5,n=o>8?2.5:o,r={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(r[t.area]=e.id);const a=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):_t,l=gt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:mt),h=aa({hass:t.hass,areaToSpace:r,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:a,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>ua(t.lang,e),iconRules:l}),c=h.filter(e=>e.space===t.spaceId),d=c.filter(t=>!t.hidden),p=function(t,e,i){const s={},o=i/100*Cn(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=zn(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),c=a/h,d=l/Math.max(Math.ceil(e.length/h),1),p=e.map((t,e)=>({x:n.x+r+c*(e%h+.5),y:n.y+r+d*(Math.floor(e/h)+.5)}));ei(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=un(p[e]))}return s}(c,i,n),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qo,e=i.y*Qo;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},m=Array.isArray(_.walls)?_.walls:[],g=Number(_.cell_cm)>0?Number(_.cell_cm):5,f=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Bo(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...Go(o,e,i));for(const o of t.wall_columns||[])s.push(jo(o,e,i));return s}(i,g,cn);for(const t of f){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&u.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const v=Sn(i,u),y=[v.x,v.y,v.w,v.h],b=i.rooms.filter(t=>t.area||s.showBorders||"light"===Vi(s.fill,t)).map(e=>{let o="room "+(i.bg?"overlay":"yard"),n="";const r=Vi(s.fill,e);if(s.showBorders||"none"!==r){o+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a="light"===r?zi("light",null,Jr(Yr(t.hass,c,e)),null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):e.area?zi(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Zr(t,o.entities);null!=e&&s.push(e)}return Qe(s)}(t.hass,c,e.area):null,"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=Xr(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,c,e.area):null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):null;a?(o+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!s.showNames,l=Tn(e),h=e.id||G,d=e.area||G,p=e.poly?B``:B``;return B`${p}${a?B`${e.name}`:G}`}),w=d.map(e=>{const s=function(t,e,i,s,o){const n=e[t.id];return n&&n.s===t.space?{x:n.x*Qo,y:n.y*Qo}:s[t.id]?s[t.id]:un(Mn(o))}(e,t.layout,t.cfg,p,i),o=(s.x-y[0])/y[2]*100,n=(s.y-y[1])/y[3]*100,r=Number(e.marker?.size)>0?Number(e.marker.size):1,a=Number(e.marker?.angle)||0,l=[`left:${o}%`,`top:${n}%`];return 1!==r&&l.push(`--dev-scale:${r}`),W`
-
`}),k=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const o=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Qo,y:s.y*Qo};const o=Tn(t);return un({x:o[0],y:o[1]})}(e,i.id,t.layout,t.cfg),n=(o.x-y[0])/y[2]*100,r=(o.y-y[1])/y[3]*100,a=Math.min(1,s.opacity+.25);return W`
`}),$=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const o=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Qo,y:s.y*Qo};const o=Tn(t);return un({x:o[0],y:o[1]})}(e,i.id,t.layout,t.cfg),n=(o.x-y[0])/y[2]*100,r=(o.y-y[1])/y[3]*100,a=Math.min(1,s.opacity+.25);return W`
${e.name}
`}):[],$=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",x=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let S="";if("daynight"===As(t.cfg?.settings,x)&&null!==Rs(t.cfg?.settings,x)){const e=Is(t.hass);e&&(S=$s(e.elevation).bg)}const M=S||Si(t.cfg?.settings,s),C=m.length?Oo(i.rooms,m,[],dn,g,cn,Qo):ze(i.rooms),D=Di(t.cfg.settings),P=(m.length||f.length)&&s.showBorders?Eo(i.rooms,m,[],[],dn,g,cn,Qo,f):null,z=t.stageWidth&&y[2]?t.stageWidth/y[2]:1,T=!!P&&Js(P.depthUnits,z),R=s.color||"#607d8b";return W` + style="left:${n}%;top:${r}%;color:${s.color};opacity:${a}">${e.name}
`}):[],k=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",x=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let S="";if("daynight"===As(t.cfg?.settings,x)&&null!==Rs(t.cfg?.settings,x)){const e=Is(t.hass);e&&(S=ks(e.elevation).bg)}const M=S||Si(t.cfg?.settings,s),C=m.length?Oo(i.rooms,m,[],dn,g,cn,Qo):ze(i.rooms),D=Di(t.cfg.settings),P=(m.length||f.length)&&s.showBorders?Eo(i.rooms,m,[],[],dn,g,cn,Qo,f):null,z=t.stageWidth&&y[2]?t.stageWidth/y[2]:1,T=!!P&&Js(P.depthUnits,z),R=s.color||"#607d8b";return W`
${P?B` @@ -2985,7 +3004,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:G} ${C.map(t=>"poly"in t?B``:B``)} - ${$?B``:G} ${b} @@ -2999,15 +3018,15 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:G} ${""} -
${w}${k}
+
${w}${$}
- `}let ga=null,fa=null,va=!1;const ya=new Set;function ba(){if(ga)return ga;try{const t=JSON.parse(localStorage.getItem("houseplan_card_cfg_v1")||"null");if(t&&t.config&&Array.isArray(t.config.spaces))return{config:t.config,rev:t.rev||0,layout:t.layout||{}}}catch{}return null}function wa(t){return ga?Promise.resolve(ga):fa||(fa=async function(t){const[e,i]=await Promise.all([t.callWS({type:"houseplan/config/get"}),t.callWS({type:"houseplan/layout/get"})]);if(ga={config:e?.config??null,rev:e?.rev??0,layout:i?.layout??{}},!va&&t.connection?.subscribeEvents){va=!0;const e=()=>{ga=null,ya.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{va=!1}}return ga}(t).finally(()=>{fa=null}),fa)}class ka extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return da(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[];return[t.length?{name:"space",selector:{select:{mode:"dropdown",options:t}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={space:pa(t,"editor.space"),title:pa(t,"editor.title"),show_button:pa(t,"editor.show_button"),button_label:pa(t,"editor.button_label"),button_target:pa(t,"editor.button_target"),icon_size:pa(t,"editor.icon_size")};return W`{fa=null,ba.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{ya=!1}}return fa}(t).finally(()=>{va=null}),va)}class ka extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[];return[t.length?{name:"space",selector:{select:{mode:"dropdown",options:t}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={space:ua(t,"editor.space"),title:ua(t,"editor.title"),show_button:ua(t,"editor.show_button"),button_label:ua(t,"editor.button_label"),button_target:ua(t,"editor.button_target"),icon_size:ua(t,"editor.icon_size")};return W`e[t.name]||t.name} @value-changed=${this._valueChanged} - >`}_valueChanged(t){const e={...this._config||{},...t.detail.value};delete e.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ka.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",ka);const $a=t=>{history.pushState(null,"",t),((t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class xa extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new lr(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");$a(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=ba();return{type:"custom:houseplan-space-card",space:rn(e?.config||null)[0]?.id||"",show_button:!0}}setConfig(t){if(!t||!t.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",...t},this._snap=this._snap||ba()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},ya.add(t),()=>ya.delete(t)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._unsub?.(),this._unsub=void 0,this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._signer.dispose(),super.disconnectedCallback()}willUpdate(t){!this.hass||this._loading||this._snap&&!t.has("hass")||this._snap&&this._loadedOnce||this._load()}updated(){const t=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(t===this._observedStage)return;if(this._stageObserver?.disconnect(),this._observedStage=t,!t)return void(this._stageObserver=void 0);const e=()=>{const e=t.clientWidth;e>0&&Math.abs(e-this._stageWidth)>.5&&(this._stageWidth=e,this.requestUpdate())};this._stageObserver=new ResizeObserver(e),this._stageObserver.observe(t),e()}async _load(){if(this.hass&&!this._loading){this._loading=!0;try{const t=await wa(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return da(this.hass,this._config?.language)}getCardSize(){const t=rn(this._snap?.config||null).find(t=>t.id===this._config?.space);if(t){const e=t.vb[3]/t.vb[2];return Math.max(3,Math.round(8*e))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(t){return W`
${t}
`}_referenced(){return ji(this._snap?.config)}render(){if(!this._config)return G;const t=this._snap?.config;if(!t)return W`
${pa(this._lang,"space_card.loading")}
`;const e=this._config.space,i=ma({hass:this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:e,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:t=>this._signer.display(this.hass,t)});if(!i)return this._errorCard(pa(this._lang,"space_card.not_found",{id:e}));const s=rn(t).find(t=>t.id===e),o=void 0!==this._config.title?this._config.title:s?.title||"",n=!1!==this._config.show_button,r=this._config.button_label||pa(this._lang,"space_card.button");return W` + >
`}_valueChanged(t){const e={...this._config||{},...t.detail.value};delete e.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ka.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",ka);const xa=t=>{history.pushState(null,"",t),((t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class Sa extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new lr(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");xa(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=wa();return{type:"custom:houseplan-space-card",space:rn(e?.config||null)[0]?.id||"",show_button:!0}}setConfig(t){if(!t||!t.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",...t},this._snap=this._snap||wa()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},ba.add(t),()=>ba.delete(t)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._unsub?.(),this._unsub=void 0,this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._signer.dispose(),super.disconnectedCallback()}willUpdate(t){!this.hass||this._loading||this._snap&&!t.has("hass")||this._snap&&this._loadedOnce||this._load()}updated(){const t=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(t===this._observedStage)return;if(this._stageObserver?.disconnect(),this._observedStage=t,!t)return void(this._stageObserver=void 0);const e=()=>{const e=t.clientWidth;e>0&&Math.abs(e-this._stageWidth)>.5&&(this._stageWidth=e,this.requestUpdate())};this._stageObserver=new ResizeObserver(e),this._stageObserver.observe(t),e()}async _load(){if(this.hass&&!this._loading){this._loading=!0;try{const t=await $a(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return pa(this.hass,this._config?.language)}getCardSize(){const t=rn(this._snap?.config||null).find(t=>t.id===this._config?.space);if(t){const e=t.vb[3]/t.vb[2];return Math.max(3,Math.round(8*e))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(t){return W`
${t}
`}_referenced(){return ji(this._snap?.config)}render(){if(!this._config)return G;const t=this._snap?.config;if(!t)return W`
${ua(this._lang,"space_card.loading")}
`;const e=this._config.space,i=ga({hass:this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:e,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:t=>this._signer.display(this.hass,t)});if(!i)return this._errorCard(ua(this._lang,"space_card.not_found",{id:e}));const s=rn(t).find(t=>t.id===e),o=void 0!==this._config.title?this._config.title:s?.title||"",n=!1!==this._config.show_button,r=this._config.button_label||ua(this._lang,"space_card.button");return W` ${o?W`
${o}
`:G} ${i} @@ -3015,7 +3034,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:G}
- `}}xa.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},xa.styles=[_a,n` + `}}Sa.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},Sa.styles=[ma,n` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -3090,7 +3109,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",xa),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-space-card"===t.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only static schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Sa=4.166666666666667e-9;function Ma(t){if(!Number.isFinite(t))return t;const e=Math.round(t/dn)*dn;return Math.abs(e-t)<=Sa?t:e}const Ca=(t,e,i,s)=>Math.hypot(i-t,s-e),Da=(t,e,i,s,o,n,r,a)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+r-(t+i))),Math.max(Math.abs(n-e),Math.abs(n+a-(e+s)))),Pa=(t,e,i,s,o,n,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,h=Math.cos(i*l)*a,c=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(Ca(t+h,e+c,o+d,n+p),Ca(t-h,e-c,o-d,n-p)),_=Math.max(Ca(t+h,e+c,o-d,n-p),Ca(t-h,e-c,o+d,n+p));return Math.min(u,_)},za=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const Ta=t=>JSON.parse(JSON.stringify(t)),Ra=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Aa=(t,e,i)=>Math.min(i,Math.max(e,t)),Na=t=>rn({spaces:[t]})[0],Ea=t=>{const e=[];for(const i of t||[]){const t=Pe(i);if(t?.length)for(let i=0;i{let e=0;for(const i of t.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",e++),Array.isArray(i.controls)){jr(i.binding,i.controls)&&!0!==i.is_light&&(i.is_light=!0,e++);const t="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=t?i.controls.filter(e=>e!==t):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,e++)}const t=i.vacuum;t&&Ra(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const i of t.spaces||[]){if(Ra(i,"segments")&&(delete i.segments,e++),Ra(i,"plan_scale")){const t=Number(i.plan_scale);Number.isFinite(t)&&t>=en&&t<=sn&&(Ra(i,"plan_scale_x")||(i.plan_scale_x=t),Ra(i,"plan_scale_y")||(i.plan_scale_y=t),delete i.plan_scale,e++)}const t=Number(i.cell_cm);Ra(i,"cell_cm")&&(!Number.isFinite(t)||t<.1||t>1e3)&&(i.cell_cm=Number.isFinite(t)&&t>0?Aa(t,.1,1e3):5,e++);const s=Number(i.cell_cm),o=Number.isFinite(s)&&s>0?s:5;for(const t of i.wall_columns||[])if("circle"===t.shape)Ra(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Ra(t,"angle")){const i=((Number(t.angle)||0)%90+90)%90;t.angle!==i&&(t.angle=i,e++)}for(const t of i.decor||[]){if(Ra(t,"width")){const i=Number(t.width);(Ra(t,"width_cm")||Number.isFinite(i))&&(Ra(t,"width_cm")||(t.width_cm=Number(Aa(i/cn*o,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Ra(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Ra(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Aa(yi*bi(t)/cn*o,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Ra(t,"scale")||Ra(t,"size"))&&(delete t.scale,delete t.size,e++);let i=String(t.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1]));if(!(Ra(t,"entity")||Ra(t,"attr")||Ra(t,"unit")))continue;const n=String(t.unit??"").trim(),r="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||n?"":mi(t.entity,r);if(s||a){if(a){const e=i.indexOf("{}");i=e>=0?i.slice(0,e)+a+i.slice(e+2):`${i}${i?" ":""}${a}`,t.text=i}delete t.entity,delete t.attr,delete t.unit,e++}}}return e})(i);for(const t of i.spaces||[]){const e=Na(t);if(!e)continue;if(!Kn(t.open_spans).length){const i=tr(e.rooms,null,Qo,.02*cn,!0);i.length&&(t.open_spans=er(i,Qo),r++)}}const a=Ta(i.spaces||[]),l=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let o=0,n=0,r=0,a=0,l="",h=0,c=0;const d={};let p=5;for(const t of i){const e=za(t);null!=t?.id&&(d[String(t.id)]=e),e>p&&(p=e)}const u=(t,e,i,s=!1)=>{if(!(t>Sa||s))return;o++,t>r&&(r=t);const n=t*hn*e;n>a&&(a=n,l=i)};for(const t of i){const e=za(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){n++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[Ma(e[0]),Ma(e[1])];return t=Math.max(t,Ca(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,o=s.w||0,n=s.h||0,r=Ma(e+o),a=Ma(i+n),l=Ma(e),h=Ma(i),c=Math.max(dn,r-l),d=Math.max(dn,a-h);t=Da(e,i,o,n,l,h,c,d),s.x=l,s.y=h,s.w=c,s.h=d}u(t,e,i)}for(const s of t.room_drafts||[]){n++;let t=0;const o=(s.points||[]).map(e=>{const i=[Ma(e[0]),Ma(e[1])];return t=Math.max(t,Ca(e[0],e[1],i[0],i[1])),i}),r=o.length?[o[0]]:[],a=[];for(let t=0;t+1t.points?.length>=2),c+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const s of t.partitions||[]){n++;const t=[Ma(s.a[0]),Ma(s.a[1])],o=[Ma(s.b[0]),Ma(s.b[1])];let r=Math.max(Ca(s.a[0],s.a[1],t[0],t[1]),Ca(s.b[0],s.b[1],o[0],o[1]));Ca(t[0],t[1],o[0],o[1])>Sa?(s.a=t,s.b=o):r=0,u(r,e,i)}for(const s of t.wall_columns||[]){n++;const t=[Ma(s.center[0]),Ma(s.center[1])],o=Ca(s.center[0],s.center[1],t[0],t[1]);s.center=t,u(o,e,i)}for(const s of t.decor||[]){n++;let t=0;if("line"===s.kind){const e=[Ma(s.x1),Ma(s.y1)],i=[Ma(s.x2),Ma(s.y2)];t=Math.max(Ca(s.x1,s.y1,e[0],e[1]),Ca(s.x2,s.y2,i[0],i[1])),s.x1=e[0],s.y1=e[1],s.x2=i[0],s.y2=i[1]}else{const e=Ma(s.x),i=Ma(s.y);if(null!=s.w&&null!=s.h){const o=Ma(s.x+s.w),n=Ma(s.y+s.h),r=Math.max(dn,o-e),a=Math.max(dn,n-i);t=Da(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=Ca(s.x,s.y,e,i);s.x=e,s.y=i}u(t,e,i)}for(const s of t.openings||[]){n++;const o=Re([s.x,s.y],t.rooms||[],.025,{step:dn,length:Number(s.length)||0});if(!o)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===o.angle),l=Pa(s.x,s.y,Number.isFinite(r)?r:o.angle,Number(s.length)||0,o.x,o.y,o.angle);s.x=o.x,s.y=o.y,s.angle=o.angle,a&&h++,u(l,e,i,a)}}for(const[t,e]of Object.entries(s)){if(!e||"object"!=typeof e)continue;const i=e;if("number"!=typeof i.x||"number"!=typeof i.y)continue;n++;const o=Ma(i.x),r=Ma(i.y),a=Ca(i.x,i.y,o,r);s[t]={...i,x:o,y:r};const l="string"==typeof i.s?i.s:"";u(a,d[l]??p,l)}return{spaces:i,layout:s,report:{moved:o,total:n,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:h,removedDrafts:c},changed:o>0}}(i.spaces||[],e||{});i.spaces=l.spaces;const h={...l.report};let c=0,d=0,p=0;for(let t=0;t[t.id,t.open_to||[]]),walls:e.walls||[]}),n=Na(e),r=Na(s);if(!n||!r)continue;const l=Ea(n.rooms),u=Ea(r.rooms),_=.02*cn;let m=ar(Kn(e.open_spans),l,u,Qo);const g=m.length;m=rr(m,r.rooms,Qo,_),m=m.map(t=>{const e=Gn(t,Qo),i=[e[0],e[1]],o=[e[2],e[3]],n=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=e[2]-e[0],a=e[3]-e[1],l=Math.hypot(r,a)||1,c=u.map(([t,e])=>[t[0],t[1],e[0],e[1]]).filter(t=>{const e=t[2]-t[0],s=t[3]-t[1],h=Math.hypot(e,s)||1;return Math.abs(r*s-a*e)/(l*h)<=1e-6&&Yn(n,t).d<=4*_&&Yn(i,t).d<=4*_&&Yn(o,t).d<=4*_}).sort((t,e)=>Math.hypot(e[2]-e[0],e[3]-e[1])-Math.hypot(t[2]-t[0],t[3]-t[1]))[0];if(h.total++,!c)return t;const d=[[c[0],c[1]],[c[2],c[3]]],p=Zn(i,c,d,cn,2*_),m=Zn(o,c,d,cn,2*_);if(Math.hypot(m[0]-p[0],m[1]-p[1])<.5*cn)return t;const g=Math.max(Math.hypot(p[0]-i[0],p[1]-i[1]),Math.hypot(m[0]-o[0],m[1]-o[1]));if(g>1e-6*cn){h.moved++;const t=g/Qo;t>h.maxShift&&(h.maxShift=t);const e=g/cn*(Number(s.cell_cm)>0?Number(s.cell_cm):5);e>h.maxShiftCm&&(h.maxShiftCm=e,h.maxSpace=String(s.id||""))}return jn(p,m,Qo)}),m=rr(m,r.rooms,Qo,_),d+=Math.max(0,g-m.length);const f=m.map(t=>Gn(t,Qo));m.length?s.open_spans=m:delete s.open_spans,ir(s.rooms||[],r.rooms,f,_);const v=Array.isArray(e.walls)?e.walls.length:0;let y=_o(e.walls,l,u,dn,Qo);y=zo(r.rooms,y,f,dn,Number(s.cell_cm)>0?Number(s.cell_cm):5,cn,Qo),y=po(y,s.rooms||[],dn,1,f.map(t=>[t[0]/Qo,t[1]/Qo,t[2]/Qo,t[3]/Qo])),c+=Math.max(0,v-y.length),y.length?s.walls=y:delete s.walls;const b=JSON.stringify({spans:s.open_spans||[],links:(s.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:s.walls||[]});b!==o&&p++}const u=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o;n<5&&u&&(i.model_version=5);const _=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o,m=Number.isInteger(Number(i.model_version))?Number(i.model_version):n;return{config:i,layout:l.layout,report:{...h,modelFrom:n,modelTo:m,migrated:r,canonicalized:p,wallsMerged:c,spansMerged:d},changed:_}}class Oa{constructor(t=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(t))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(t){this._undo.push(t),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const t=this._undo.pop()??null;return t&&this._redo.push(t),t}redo(){const t=this._redo.pop()??null;return t&&this._undo.push(t),t}clear(){this._undo=[],this._redo=[]}}const Fa="1.60.2-beta.1",La=2e3,qa=1e3,Ha=1500,Ua=15e3,Wa=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const i=Number(e);return Number.isFinite(i)?i:null},Ba=(t,e)=>{if(!t.has(e))return{hit:!1};const i=t.get(e);return t.delete(e),t.set(e,i),{hit:!0,value:i}},ja=(t,e,i,s)=>{for(t.delete(e),t.set(e,i);t.size>s;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}};let Ga="undefined"!=typeof document&&"hidden"===document.visibilityState?Date.now():0,Va=0;const Ka=new Map;let Ya=0;const Ja=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Za=1e4;const Xa="houseplan_card_layout_v1",Qa="houseplan_card_cfg_v1",tl="houseplan_card_zoom_v1",el="houseplan_card_nav_v1",il="houseplan_card_kiosk_v1",sl=1e3,ol=3300,nl=(t,e)=>{const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return{x:i,y:s,w:Math.max(t.x+t.w,e.x+e.w)-i,h:Math.max(t.y+t.h,e.y+e.h)-s}},rl=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},al=(t,e)=>{let i,s=null;const o=(...o)=>{clearTimeout(i),s=o,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return o.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},o.pending=()=>void 0!==i,o},ll=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class hl extends lt{constructor(){super(...arguments),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._unsubCfg=null,this._unsubLayout=null,this._layoutRev=0,this._canOptimizeUndo=!1,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Os},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._navMotion="",this._editorSwapAnimations=[],this._editorChromeMode="plan",this._tool="draw",this._geometryHistory=new Oa(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._cleanFloorCache=new Map,this._glowClipCache=new Map,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._path=[],this._cursorPt=null,this._mergeSel=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._openWallAnchor=null,this._splitSel=null,this._pendingSplit=null,this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._alignDialog=null,this._settingsDialog=null,this._sunRaysCache=null,this._skyElev=null,this._skySnap=!1,this._skySnapRaf=0,this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=t=>this._onKey(t),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++Ya,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{if("hidden"!==document.visibilityState){if("visible"===document.visibilityState){const t=Date.now();Ga&&t-Ga>=Ua&&(Va=t),Ga=0,this._vacJumpOnce=!0,this._skyElev=null,this._tip=null,this._hoverRoom=null,t-Va<=15e3&&this._beginResumeSettle(),this.requestUpdate()}}else Ga||(Ga=Date.now())},this._resumeSettling=!1,this._resumeRaf=0,this._resumeStarted=0,this._resumeLastSize="",this._resumeLastChange=0,this._viewportInvalidAt=0,this._vacFit=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._lastTap=0,this._onHashChange=()=>{const t=this._hashSpace();t&&this._model.find(e=>e.id===t)&&t!==this._space&&(this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._selId=null,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=null,this._decorSnapCache=null,this._showHidden=!1,this._connHooked=null,this._onConnReady=()=>{this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._loading||(this._loadOk&&this._unsubCfg?this._reloadConfigOnly():this._loadFromServer())},this._signer=new lr(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=al(()=>{if(this._serverStorage){const t=[...this._dirtyPos];this._dirtyPos.clear();for(const e of t){const t=this._layout[e];t&&(this._sentPos.set(e,t),this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t}).then(t=>this._noteLayoutRev(t)).catch(t=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))).finally(()=>{this._sentPos.get(e)===t&&this._sentPos.delete(e)}))}this._cacheSnapshot()}else localStorage.setItem(Xa,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.undo();t&&(this._applyGeometryState(t.before)?this._showToast(this._t("history.undone",{name:t.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.redo();t&&(this._applyGeometryState(t.after)?this._showToast(this._t("history.redone",{name:t.name})):this._geometryHistory.clear())},this._saveConfigDebounced=al(()=>{this._serverCfg&&this._writeConfig().catch(t=>{"conflict"===t?.code?(this._showToast(this._t("toast.conflict")),this._cancelPath(),this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(t)}))})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=Wa(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Lo:100);const s=this._imperial?2.54*i:i,o="column"===t.kind?Lo:100;if(!Number.isFinite(s)||s<1||s>o)return void this._showPhysicalRange(o);if("column"===t.kind){const e=this._spaceModel().wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Wa(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:s}:{id:t.id,shape:"square",center:e.center,cm:s,angle:i};if(this._spaceModel().wall_columns.some(e=>e.id!==t.id&&Xo(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const n=this._geometrySnapshot();if("partition"===t.kind){const i=(e.partitions||[]).find(e=>e.id===t.id);i&&(i.cm=s)}else if("column"===t.kind){const i=(e.wall_columns||[]).find(e=>e.id===t.id);i&&(i.cm=qo(s),i.shape="circle"===t.shape?"circle":"square","square"===i.shape?i.angle=Wa(t.angle||"0"):delete i.angle)}else{const i=(e.room_drafts||[]).find(e=>e.id===t.id);i?.segments?.[t.segment||0]&&(i.segments[t.segment||0].cm=s)}this._recordGeometry(this._t("history.physical_edit"),n),this._physicalDialog=null,this._saveConfig()},this._deletePhysicalSelection=()=>{const t=this._physicalSel,e=this._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot(),s="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[s]=(e[s]||[]).filter(e=>e.id!==t.id),e[s].length||delete e[s],this._activeDraftId===t.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftWhole=()=>{const t="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,e=this._curSpaceCfg;if(!t||!e||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===t&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftSegment=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(i<0)return;const s=e.room_drafts[i],o=Math.max(0,Math.min(s.segments.length-1,t.segment||0)),n=[],r=s.points.slice(0,o+1),a=s.points.slice(o+1);if(r.length>=2&&n.push({id:s.id,points:r,segments:s.segments.slice(0,o)}),a.length>=2&&n.push({id:n.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:a,segments:s.segments.slice(o+1)}),2===n.length&&e.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(i,1,...n),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=n.length?{kind:"draft",id:n[0].id}:null,this._recordGeometry(this._t("history.draft_segment_delete"),l),this._saveConfig()},this._keepClosedAsPartitions=()=>{if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const t=this._curSpaceCfg,e=this._path.slice(0,-1);if((t.partitions||[]).length+e.length>2e3)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys];t.partitions||=[];const o=Date.now().toString(36);for(let i=0;it.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._recordGeometry(this._t("history.contour_to_partitions"),i),this._saveConfig(),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this._spaceDialog;if(t)if(t.pickSaved)this._spaceDialog={...t,pickSaved:!1};else{this._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.hass.callWS({type:"houseplan/plans/list"}),e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(t)}))}}},this._aspectJob=null,this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const t=this._glowRadiusCm,e=this._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:e,bgColor:Si(this._settings,{bgColor:null})||null,northDeg:Rs(this._settings,{}),bgMode:As(this._settings,{}),sunRays:Ns(this._settings,{}),weatherEntity:Es(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=Ia(this._serverCfg,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),o=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,config:e.config,layout:e.layout,cm:i,where:o,changed:e.changed,busy:!1}},this._optimizeUndoBusy=!1,this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:mt).map(t=>({...t}));this._rulesDialog={rules:e,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}_startNavMotion(t){if(clearTimeout(this._navMotionTimer),this._navMotionTimer=void 0,window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return this._cancelEditorSwapAnimations(),void(this._navMotion="");this._navMotion=t,this._navMotionTimer=window.setTimeout(()=>{this._navMotionTimer=void 0,this._navMotion="",this.requestUpdate()},190)}_cancelEditorSwapAnimations(){for(const t of this._editorSwapAnimations)t.cancel();this._editorSwapAnimations=[],this.renderRoot.querySelector(".editorchrome")?.classList.remove("resizing")}_animateEditorSwap(t){this.updateComplete.then(()=>{if(!this.isConnected||"swap"!==this._navMotion||window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return;const e=this.renderRoot.querySelector(".editorchrome"),i=e?.querySelector(".editorchrome-inner");if(!e||!i)return;this._cancelEditorSwapAnimations();const s=i.getBoundingClientRect().height;e.classList.add("resizing");const o={duration:190,easing:"cubic-bezier(0.2, 0.7, 0.2, 1)"},n=[];if(t>0&&s>0&&Math.abs(t-s)>.5){const i=e.animate([{height:`${t}px`},{height:`${s}px`}],o);i.id="hp-editor-height-swap",n.push(i)}const r=i.animate([{opacity:.42,transform:"translateY(5px) scale(0.995)"},{opacity:1,transform:"translateY(0) scale(1)"}],o);r.id="hp-editor-content-swap",n.push(r),this._editorSwapAnimations=n,Promise.allSettled(n.map(t=>t.finished)).then(()=>{this._editorSwapAnimations===n&&(this._editorSwapAnimations=[],e.classList.remove("resizing"))})})}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate())}_pickSpace(t){if(t===this._space)return;const e=this._model.map(t=>t.id),i=e.indexOf(this._space),s=e.indexOf(t);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(t,i>=0&&s0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const t=this._model.map(t=>t.id),e=t.indexOf(this._space);this._slideTo(t[(e+1)%t.length],"left"),this._showKioskDots()}}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}_hashSpace(){const t=/(?:^|[#&])space=([^&]+)/.exec(window.location.hash||"");return t?decodeURIComponent(t[1]):""}connectedCallback(){document.addEventListener("visibilitychange",this._vacVisHandler),super.connectedCallback(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>ji(this._serverCfg)),this._config?.kiosk&&Number(this._config?.cycle)>0&&(clearInterval(this._cycleTimer),this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle))),window.addEventListener("hashchange",this._onHashChange),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0));const t=Date.now();"visible"===document.visibilityState&&Ga&&(t-Ga>=Ua&&(Va=t),Ga=0),(this._warmLongReturn||t-Va<=15e3)&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._resumeRaf&&(cancelAnimationFrame(this._resumeRaf),this._resumeRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._activityRt.values())clearTimeout(t.timer);window.removeEventListener("keydown",this._keyHandler),clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._navMotionTimer),this._cancelEditorSwapAnimations(),this._slideTimer=void 0,this._navMotionTimer=void 0,this._slide="",this._navMotion="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._resumeSettling=!1,super.disconnectedCallback()}_onKey(t){if("Escape"===t.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void t.stopPropagation();if("Escape"===t.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void(this._infoCard=null);if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return void(this._alignDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}const e=(t.composedPath?.()||[t.target]).some(t=>t?.matches?.('input, textarea, select, [contenteditable="true"]')),i=t.ctrlKey||t.metaKey,s=t.key.toLowerCase(),o=/^[a-z]$/.test(s),n="z"===s||!o&&"KeyZ"===t.code,r="y"===s||!o&&"KeyY"===t.code,a=i&&(n&&t.shiftKey||r),l=i&&n&&!t.shiftKey;if("decor"===this._mode){if((l||a)&&e)return;return a?(t.preventDefault(),void this._redoGeometry()):l?(t.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||e?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():this._furnPalette?this._furnPalette=null:this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(t.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!l&&!a||!e)){if(("Delete"===t.key||"Backspace"===t.key)&&this._physicalSel&&!e)return t.preventDefault(),void this._deletePhysicalSelection();if(a)return t.preventDefault(),void this._redoGeometry();if(l)return t.preventDefault(),this._rszDrag?void this._rszCancelDrag():"draw"!==this._tool&&"partition"!==this._tool||!this._path.length?"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry():void(this._activeDraftId&&this._path.length>1?this._undoGeometry():this._undoPoint());if("Escape"===t.key)return this._physicalDrag||this._physicalRotate?(t.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(t.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length||"partition"===this._tool&&this._path.length?(t.preventDefault(),void this._undoPoint()):this._physicalSel?(t.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():void(this._rszSel?this._rszSel=null:this._tool="draw")):"split"===this._tool?(t.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(t.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(t.preventDefault(),void(this._wallDialog=null)):"openwall"===this._tool||"closewall"===this._tool?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"partition"!==this._tool&&"column"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const t=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const e=this._curSpaceCfg,i=(e.room_drafts||[]).findIndex(t=>t.id===this._activeDraftId);return void(i>=0&&(this._path.length<2?(e.room_drafts.splice(i,1),e.room_drafts.length||delete e.room_drafts,this._activeDraftId=null):e.room_drafts[i]={id:this._activeDraftId,points:this._path.map(t=>[t[0]/sl,t[1]/sl]),segments:this._draftSegmentCms.map(t=>({cm:t}))},this._recordGeometry(this._t("history.draft_segment_delete"),t),this._saveConfig()))}this._path=this._path.slice(0,-1)}}static getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(t){for(const t of Ka.values())for(const e of t)clearTimeout(e.evict);Ka.clear(),Za=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of Ka.values())for(const o of s)t++,o.dlg&&(e++,i.push(o.dlg.kind));return{keys:Ka.size,slots:t,dlgs:e,drafts:i}}setConfig(t){this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...t},this._config.kiosk&&(this._booting=!1,this._bootFading=!1),t.default_floor&&(this._space=t.default_floor);try{this._zoomBySpace=JSON.parse(localStorage.getItem(tl)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(il)||"null");this._kioskScale={icon:Ki(t?.icon),font:Ki(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Qa)||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._layout=e.layout||{},this._serverStorage=!0;const i=this._hashSpace(),s=this._savedNav();i&&this._model.find(t=>t.id===i)?(this._space=i,this._hashApplied=!0):s?.space&&this._model.find(t=>t.id===s.space)?(this._space=s.space,this._navApplied=!0):t.default_floor?this._space=t.default_floor:this._model.find(t=>t.id===this._space)||(this._space=this._model[0]?.id||this._space),s?.mode&&"view"!==s.mode&&!t.kiosk&&(this._canEdit?this._mode=s.mode:this._pendingNavMode=s.mode)}}catch{}"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const t=Ja(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=Ka.get(t);if(!s||!s.length)return;const o=s.find(t=>t.owner===this._warmGen);if(o)return this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Ua,clearTimeout(o.evict),o.evict=0,o.freed=0,o.live=!0,this._warmSlot=o,void(this._warmKey=t);const{slot:n,sure:r}=((t,e,i,s)=>{const o=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let n=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=o(i);t<=0||(t>r?(n=i,r=t,a=1):t===r&&a++)}return!n||a>1?{slot:n||l,sure:!1}:{slot:n,sure:!0}})(s,this._warmGen,e,i);n&&(this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=Ua,this._booting=!1,this._bootFading=!1,this._hdrH=n.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)),this._warmKey=t,r?(clearTimeout(n.evict),n.evict=0,n.owner=this._warmGen,n.place=e?new WeakRef(e):null,n.idx=i,n.live=!0,this._warmSlot=n,this._warmVp=n.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:n.hdrH,stageH:n.stageH,vp:null,dlg:null,freed:0,evict:0},s.push(this._warmSlot),this._warmTrim(s)))}_warmIdx(t){const e=t?.children;if(!e)return-1;for(let t=0;t4;){const e=t.findIndex(t=>!t.live);if(e<0)break;clearTimeout(t[e].evict),t.splice(e,1)}}_warmScheduleEvict(t,e){if(clearTimeout(t.evict),!t.dlg)return;const i=t.freed,s=t.owner;t.evict=window.setTimeout(()=>{if(t.evict=0,t.freed!==i||t.owner!==s)return;t.dlg=null;const o=Ka.get(e);if(!t.live&&o&&o.length>1){const e=o.indexOf(t);e>=0&&o.splice(e,1)}},Za+250)}_warmAdoptViewport(t){const e=this._warmVp;e&&(!this._hashApplied&&this._model.find(t=>t.id===e.space)?(this._space=e.space,this._navApplied=!0,this._mode="view"!==e.mode&&this._canEdit&&!t.kiosk?e.mode:"view",this._pendingNavMode="view"===e.mode||this._canEdit||t.kiosk?null:e.mode,this._zoom=e.zoom,this._view=e.view?{...e.view}:null,this._viewModeSnap=e.snap?{...e.snap}:null,this._tool=e.tool,this._decorTool=e.decorTool,this._showHidden=e.showHidden,this._showFar!==e.showFar&&(this._showFar=e.showFar,this._frame=null),this._selId=e.selId,this._rszSel=e.rszSel,this._decorSel=e.decorSel,this._warmVpArmed=!0):this._warmVp=null)}_warmPatch(t,e=!1){if(this._config?.kiosk)return;const i=Ja(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!e)return;const t=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,place:t?new WeakRef(t):null,idx:this._warmIdx(t),live:!0,hdrH:this._hdrH,stageH:0,vp:null,dlg:null,freed:0,evict:0};const s=Ka.get(i)||[];for(s.push(this._warmSlot),Ka.set(i,s),this._warmTrim(s);Ka.size>8;){const t=Ka.keys().next().value;if(void 0===t||t===i)break;for(const e of Ka.get(t)||[])clearTimeout(e.evict);Ka.delete(t)}}Object.assign(this._warmSlot,t)}}_warmViewportState(){return{space:this._space,mode:this._mode,zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._rszSel,decorSel:this._decorSel}}_warmDialogState(){const t=(t,e)=>({kind:t,space:this._space,mode:this._mode,data:e});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog?null:this._openingInfo?t("openingInfo",this._openingInfo.id):this._infoCard?t("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:t("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:t("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:t("marker",this._markerDialog):this._openingDialog?t("opening",this._openingDialog):this._backdropDialog?t("backdrop",this._backdropDialog):this._decorShapeDialog?t("decorShape",this._decorShapeDialog):this._decorTextDialog?t("decorText",this._decorTextDialog):this._roomDialog?t("room",{editId:this._roomEditId,fill:this._roomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:t("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._resumeSettling||this._config?.kiosk)return;const t={vp:this._warmViewportState()};if(this._warmRevivePending||(t.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const e=this.parentNode;t.place=e?new WeakRef(e):null,t.idx=this._warmIdx(e)}this._warmPatch(t)}_warmReviveDialog(){this._warmRevivePending=!1;const t=this._warmSlot;if(this._warmReviveTimer=void 0,!t||!t.dlg)return;const e=t.dlg,i=t.freed;if(t.dlg=null,t.freed=0,clearTimeout(t.evict),t.evict=0,i&&!(Date.now()-i>Za)&&e.space===this._space&&e.mode===this._mode){switch(e.kind){case"space":this._spaceDialog={...e.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...e.data,busy:!1};break;case"settings":this._settingsDialog={...e.data,busy:!1};break;case"rules":this._rulesDialog={...e.data,busy:!1};break;case"opening":this._openingDialog={...e.data};break;case"backdrop":this._backdropDialog={...e.data};break;case"decorShape":this._decorShapeDialog={...e.data};break;case"decorText":{this._decorTextDialog={...e.data};const t=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:t,end:t};break}case"room":{const t=e.data;this._roomEditId=t.editId,this._roomFill=t.fill,this._roomTempSrc=t.tempSrc,this._roomHumSrc=t.humSrc,this._roomSrcOpen=t.srcOpen,this._roomSrcFilter=t.srcFilter,this._roomNameScale=t.nameScale,this._roomLabelScale=t.labelScale,this._areaSel=t.areaSel,this._nameSel=t.nameSel,this._pendingSplit=t.pendingSplit,this._path=t.path,this._roomDialog=!0;break}case"info":{const t=this._devices.find(t=>t.id===e.data);t&&(this._infoCard=t);break}case"openingInfo":{const t=(this._curSpaceCfg?.openings||[]).find(t=>t.id===e.data);t&&(this._openingInfo=t);break}}this.requestUpdate()}}_cacheSnapshot(){if(this._serverCfg)try{localStorage.setItem(Qa,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,layout:this._layout}))}catch{}}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const t=this._serverCfg?.spaces||[];let e=t.length+":";for(const i of t){e+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const t of i.rooms||[]){const i=t.poly?.[0],s=t.poly?.[t.poly.length-1];e+=(t.poly?.length||0)+"."+(t.id||"")+"."+(t.open_to||[]).join("+")+"."+(t.area||"")+"."+JSON.stringify(t.settings||0)+"."+(t.x??"")+","+(t.y??"")+","+(t.w??"")+","+(t.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(s?s[0]+"/"+s[1]:"")+";"}}return e}get _model(){if(!this._serverCfg)return[];const t=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===t)return this._modelCache.model;const e=this._buildModel();return this._modelCache={key:t,model:e},e}_buildModel(){if(!this._serverCfg)return[];const t=this._renderCfg;return rn(t).map((e,i)=>{const s=t.spaces[i]?.plan_url;return e.bg&&s?{...e,bg:{...e.bg,href:s}}:e})}_spaceModel(t){const e=this._model;return e.find(e=>e.id===(t??this._space))||e[0]}get _areaToSpace(){const t={};for(const e of this._model)for(const i of e.rooms)i.area&&(t[i.area]={space:e.id,room:i});return t}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const t=this._serverCfg,e=function(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,iconRules:r}=t,a=!1!==o.group_lights,l=sa(s),h=ea(e,a).filter(t=>!oa(e,t.eid,l)),c=new Set(h.map(t=>t.area)),d=Ir(e),p=new Set(s.map(t=>t.binding)),u=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(p.has("device:"+t.id))continue;const o=d[t.id]||[],l=Or(e,t,o);let h=n.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;if(!h&&a&&c.has(s)){const i=(t.name_by_user||t.name||"").trim();"mdi:lightbulb"===ia(e,i,t.model,o,r)&&(h=!0)}h&&u.push("device:"+t.id)}return u}({hass:this.hass,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!e.length&&t.settings?.filter_seeded)return;t.markers=t.markers||[];const i=[];for(const s of e){const e="h"+s.slice(s.indexOf(":")+1);t.markers.push({id:e,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...t.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(t=>!i.includes(t))),t.settings=s,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const t=this._settings.icon_rules;if(!t||!Array.isArray(t)||!t.length)return;const e=JSON.stringify(t);return e!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=e,this._rulesCompiled=gt(t)),this._rulesCompiled}get _fillColors(){return Di(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):_t}willUpdate(t){t.has("_serverCfg")&&this._cfgEpoch++,this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick())}updated(){this._skyRelease(),this._warmSnapshot(),this._dtMeasure();const t=this._stageEl;t&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(t)),t&&this._booting&&!this._bootTimer&&this._bootWatch();const e=this.renderRoot.querySelector(".hdr");if(e&&t&&!this._roHdr){const i=()=>{const e=this.renderRoot.querySelector("ha-card");if(!e)return;const i=t.getBoundingClientRect().top-e.getBoundingClientRect().top,s=Math.min(Math.max(e.getBoundingClientRect().top,0),120),o=Math.round(i+s);o>=0&&o!==this._hdrH&&(this._hdrH=o),o>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:o,stageH:t.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(e),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(t&&!this._view&&this._refitView(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const t=function(t){const e=t?.floors;if(!e||"object"!=typeof e)return[];const i=[];for(const t of Object.values(e))t&&t.floor_id&&i.push({id:t.floor_id,name:t.name||t.floor_id,level:t.level??null});return i.sort((t,e)=>{const i=t.level??1e9,s=e.level??1e9;return i!==s?i-s:t.name.localeCompare(e.name)}),i}(this.hass);t.length?this._importDialog={floors:t.map(t=>({...t,checked:!0}))}:this._openSpaceDialog("create")}}async _loadFromServer(){this._loading=!0,this._loadTries++;try{const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._loadOk=!0,this._serverStorage=!0,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._canOptimizeUndo=!(!t?.can_optimize_undo&&!e?.can_optimize_undo),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;this._geometryHistory.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=i&&Array.isArray(i.spaces)?i:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._layout=e?.layout||{},this._layoutRev=e?.rev??0,this._unsubCfg||(this._unsubCfg=await this.hass.connection.subscribeEvents(t=>{const e=Number(t?.data?.rev??-1);e!==this._cfgRev&&this._reloadConfigOnly(!1,e)},"houseplan_config_updated")),this.hass.callWS({type:"houseplan/trail/get"}).then(t=>{this._vacSrvTrails=t?.trails||{},this.requestUpdate()}).catch(()=>{}),this._unsubTrail||(this._unsubTrail=await this.hass.connection.subscribeEvents(async()=>{try{const t=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=t?.trails||{},this.requestUpdate()}catch{}},"houseplan_trail_updated")),this._unsubLayout||(this._unsubLayout=await this.hass.connection.subscribeEvents(t=>this._onLayoutEvent(Number(t?.data?.rev??-1)),"houseplan_layout_updated"));const s=this._hashSpace(),o=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):o?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===o.space)?(this._space=o.space,this._navApplied=!0):this._norm&&!this._model.find(t=>t.id===this._space)&&(this._space=this._model[0]?.id||this._space),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:this._restoreZoom()}catch(t){if(this._serverCfg)this._scheduleLoadRetry();else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(Xa)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}async _reloadConfigOnly(t=!1,e){if(!t){if(void 0!==e&&e<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,e),400))}try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._geometryHistory.clear(),this._clearGeometryGesture(),this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._canOptimizeUndo=!!t?.can_optimize_undo,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null),this._cacheSnapshot(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(t){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(t)}))}}_scheduleLoadRetry(){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),t.addEventListener?.("ready",this._onConnReady),this._connHooked=t)}_display(t){return this._signer.display(this.hass,t)}_resign(){this._signer.resign(this.hass,ji(this._serverCfg))}_onLayoutEvent(t){t<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{t<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(t){const e=t?.rev;"number"==typeof e&&e>this._layoutRev&&(this._layoutRev=e)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;const t=new Map;for(const e of this._dirtyPos)this._layout[e]&&t.set(e,this._layout[e]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[e,i]of this._sentPos)t.set(e,i);try{const e=await this.hass.callWS({type:"houseplan/layout/get"}),i={...e?.layout||{}};for(const[e,s]of t)i[e]=s;this._layout=i,this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!!e?.can_optimize_undo,this._cacheSnapshot(),this.requestUpdate()}catch{}}_maybeRebuildDevices(){const t=this.hass;if(!t?.devices||!t?.entities||!t?.areas)return;const e=Object.keys(t.devices).length+":"+Object.keys(t.entities).length+":"+Object.keys(t.areas).length+":"+(this._norm?"n":"l")+":"+da(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=ra({hass:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:t=>this._t(t),iconRules:this._iconRules}),this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime())}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const t=this._devices.filter(t=>!t.marker&&!t.virtual).map(t=>t.id).sort(),e=t.join(",");if(e===this._newSyncKey)return;this._newSyncKey=e;const i=this._settings,{fresh:s,known:o}=function(t,e){if(!Array.isArray(e))return{fresh:[],known:[...t]};const i=new Set(e),s=t.filter(t=>!i.has(t));return{fresh:s,known:s.length?[...e,...s]:e}}(t,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const t=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:o,new_device_ids:t}},this._saveConfig()}}get _newIds(){const t=this._settings.new_device_ids;return new Set(Array.isArray(t)?t:[])}_ackNewDevice(t){if(!this._newIds.has(t)||!this._serverCfg)return;const e=this._settings;this._serverCfg={...this._serverCfg,settings:{...e,new_device_ids:(e.new_device_ids||[]).filter(e=>e!==t)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(t){if(!t)return null;const e=[];for(const i of this._devices){if(i.area!==t||i.virtual)continue;const s=Jr(this.hass,i.entities);null!=s&&e.push(s)}return Qe(e)}_roomBounds(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}_defaultPositions(){const t={},e=this._config?.icon_size??2.5;for(const i of this._model){const s=e/100*Cn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const o=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!o.length)continue;const n=this._roomBounds(e),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(o.length*a/Math.max(l,1)))),c=Math.ceil(o.length/h),d=a/h,p=l/Math.max(c,1),u=o.map((t,e)=>({x:n.x+r+d*(e%h+.5),y:n.y+r+p*(Math.floor(e/h)+.5)}));ei(u,n,s,.5*r),o.forEach((e,i)=>t[e.id]=un(u[i]))}}return t}_pos(t){const e=this._spaceModel(t.space),i=this._layout[t.id];if(i)if(this._norm){if(i.s===t.space)return{x:i.x*sl,y:i.y*sl}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:un(Mn(e))}_savePos(t,e,i){if(this._norm){const s=this._gridPitch,o=Math.round(e/s)*s,n=Math.round(i/s)*s,r=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:mn(o/sl),y:mn(n/sl),...r?{k:r}:{}}}}else this._layout={...this._layout,[t.id]:{x:Math.round(e),y:Math.round(i)}};this._dirtyPos.add(t.id),this._persistLayout()}_coverIndicator(t){return"cover"===t.tapAction?ci(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_displayOf(t){const e=t.marker?.display;return"ripple"===e?"icon_ripple":e||"badge"}_visualSamples(t){const e=[],i=this._coverIndicator(t),s=Kr(this.hass,[t]);if(i)e.push(i);else if(s.length)e.push(...s.map(t=>t.eid));else{const i=Hr(this.hass,t.entities);i.length?e.push(...i):t.primary&&e.push(t.primary)}for(const i of t.entities||[]){"alarm"!==Nr(this.hass,i).status||e.includes(i)||e.push(i)}return e.map(t=>Nr(this.hass,t))}_deviceVisual(t){if(t.hidden)return{availability:"available",status:"neutral",activity:"none"};const e=this._visualSamples(t),i=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return kr;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(e);if("alarm"===i.status)return i;if(!this._config?.live_states)return{availability:"available",status:"neutral",activity:"none"};if("unavailable"===i.availability)return i;const s=this._activityRt.get(t.id),o=this._activitySourceKey(e);return s?.sources===o&&s.flashTs&&s.flashKind&&Date.now()-s.flashTst?.device_id===e).map(([t])=>t)}_bindingHasClimate(t){return this._bindingEntities(t).some(t=>t.startsWith("climate."))}_bindingCoverEntity(t){return ci(this._bindingEntities(t))}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!hi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Qr(this.hass,t.primary)?ta(this.hass,t.entities):null}_openMoreInfo(t){t?rl(this,"hass-more-info",{entityId:t}):this._showToast(this._t("toast.no_entity"))}_ctxDevice(t,e){"view"===this._mode&&(t.preventDefault(),t.stopPropagation(),e.primary?this._openMoreInfo(e.primary):this._infoCard=e)}_clickDevice(t,e){if(t.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(e);const i=this._coverIndicator(e),s=this._actEntity(e),o=s?s.split(".")[0]:null,n=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=Kr(this.hass,[e]).filter(t=>"controls"===t.via).map(t=>t.eid);if("toggle"===e.tapAction&&r.length){const t=(a=r.map(t=>this.hass.states[t]?.state),a.some(t=>"on"===t)?"turn_off":"turn_on");return void n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant",t,{entity_id:r}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}var a;const l=function(t,e,i,s){const o=t||e||("light"===i?"toggle":"info");return"more-info"===o?"more-info":"run"===o?"run"===t?"run":"info":"cover"===o?"cover"!==t||"cover"!==i||hi.has(String(s||""))?"info":"cover":"toggle"!==o||!i||li.has(i)?"info":"toggle"===t?"toggle":ai.has(i)?"cover"===i&&hi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,o,s?this.hass.states[s]?.attributes?.device_class:null);if("run"===l){const t=e.marker?.tap_target||"",i=function(t){const e=String(t||"").split(".")[0];return"automation"===e?{domain:"automation",service:"trigger"}:"script"===e?{domain:"script",service:"turn_on"}:"scene"===e?{domain:"scene",service:"turn_on"}:null}(t),s=this.hass.states[t];if(!i||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||t;return void n(this._t("confirm.tap_run",{name:o}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>{this._stampActivity(e.id,"event",this._activitySourceKey(this._visualSamples(e))),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}if("cover"===l&&i){const t=function(t){const e=String(t||"");return"closed"===e?"open_cover":"open"===e?"close_cover":"opening"===e||"closing"===e?"stop_cover":"toggle"}(this.hass.states[i]?.state);return void n(this._t("confirm.tap_cover",{name:e.name}),()=>{this.hass.callService("cover",t,{entity_id:i}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}"toggle"===l&&e.primary?n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant","toggle",{entity_id:e.primary}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}):"more-info"===l&&e.primary?this._openMoreInfo(e.primary):this._infoCard=e}_t(t,e){return pa(da(this.hass,this._config?.language),t,e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(t){const e=[];for(const i of this._devices){if(i.space!==t.id||i.hidden)continue;const s=this._pos(i);e.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(t.id===this._space){for(const t of this._openingsR){const i=Number(t.angle)*Math.PI/180,s=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2,n=vn([[t.rx-s,t.ry-o],[t.rx+s,t.ry+o]]);n&&e.push(n)}const i=this._decorH;for(const t of this._decorList){const s=vn("line"===t.kind?[[t.x1*sl,t.y1*i],[t.x2*sl,t.y2*i]]:"text"===t.kind?[[t.x*sl,t.y*i]]:js({x:t.x*sl,y:t.y*i,w:t.w*sl,h:t.h*i,angle:t.angle}));s&&e.push(s)}for(const i of this._physicalBodiesR(t)){const t=vn(i);t&&e.push(t)}}return bn(t,e)}_frameOf(){const t=this._spaceModel(),e=this._frame,i="view"!==this._mode;if(e&&e.id===t.id&&this._bdDrag)return e;if(e&&e.id===t.id&&e.model===t&&e.layout===this._layout&&e.devs===this._devices&&e.far===this._showFar&&e.grow===i)return e;const s=xn(this._contentItems(t));let o=s.all||Sn(t),n=this._showFar?o:s.core||Sn(t);return e&&e.id===t.id&&i&&e.grow&&(n=nl(e.rect,n),o=nl(e.all,o)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:n,all:o,outliers:s.outliers},this._frame}_baseVb(){const t=this._frameOf().rect;return[t.x,t.y,t.w,t.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
+ `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",Sa),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-space-card"===t.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only static schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Ma=4.166666666666667e-9;function Ca(t){if(!Number.isFinite(t))return t;const e=Math.round(t/dn)*dn;return Math.abs(e-t)<=Ma?t:e}const Da=(t,e,i,s)=>Math.hypot(i-t,s-e),Pa=(t,e,i,s,o,n,r,a)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+r-(t+i))),Math.max(Math.abs(n-e),Math.abs(n+a-(e+s)))),za=(t,e,i,s,o,n,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,h=Math.cos(i*l)*a,c=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(Da(t+h,e+c,o+d,n+p),Da(t-h,e-c,o-d,n-p)),_=Math.max(Da(t+h,e+c,o-d,n-p),Da(t-h,e-c,o+d,n+p));return Math.min(u,_)},Ta=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const Ra=t=>JSON.parse(JSON.stringify(t)),Aa=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Na=(t,e,i)=>Math.min(i,Math.max(e,t)),Ea=t=>rn({spaces:[t]})[0],Ia=t=>{const e=[];for(const i of t||[]){const t=Pe(i);if(t?.length)for(let i=0;i{let e=0;for(const i of t.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",e++),Array.isArray(i.controls)){Gr(i.binding,i.controls)&&!0!==i.is_light&&(i.is_light=!0,e++);const t="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=t?i.controls.filter(e=>e!==t):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,e++)}const t=i.vacuum;t&&Aa(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const i of t.spaces||[]){if(Aa(i,"segments")&&(delete i.segments,e++),Aa(i,"plan_scale")){const t=Number(i.plan_scale);Number.isFinite(t)&&t>=en&&t<=sn&&(Aa(i,"plan_scale_x")||(i.plan_scale_x=t),Aa(i,"plan_scale_y")||(i.plan_scale_y=t),delete i.plan_scale,e++)}const t=Number(i.cell_cm);Aa(i,"cell_cm")&&(!Number.isFinite(t)||t<.1||t>1e3)&&(i.cell_cm=Number.isFinite(t)&&t>0?Na(t,.1,1e3):5,e++);const s=Number(i.cell_cm),o=Number.isFinite(s)&&s>0?s:5;for(const t of i.wall_columns||[])if("circle"===t.shape)Aa(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Aa(t,"angle")){const i=((Number(t.angle)||0)%90+90)%90;t.angle!==i&&(t.angle=i,e++)}for(const t of i.decor||[]){if(Aa(t,"width")){const i=Number(t.width);(Aa(t,"width_cm")||Number.isFinite(i))&&(Aa(t,"width_cm")||(t.width_cm=Number(Na(i/cn*o,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Aa(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Aa(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Na(yi*bi(t)/cn*o,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Aa(t,"scale")||Aa(t,"size"))&&(delete t.scale,delete t.size,e++);let i=String(t.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1]));if(!(Aa(t,"entity")||Aa(t,"attr")||Aa(t,"unit")))continue;const n=String(t.unit??"").trim(),r="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||n?"":mi(t.entity,r);if(s||a){if(a){const e=i.indexOf("{}");i=e>=0?i.slice(0,e)+a+i.slice(e+2):`${i}${i?" ":""}${a}`,t.text=i}delete t.entity,delete t.attr,delete t.unit,e++}}}return e})(i);for(const t of i.spaces||[]){const e=Ea(t);if(!e)continue;if(!Kn(t.open_spans).length){const i=tr(e.rooms,null,Qo,.02*cn,!0);i.length&&(t.open_spans=er(i,Qo),r++)}}const a=Ra(i.spaces||[]),l=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let o=0,n=0,r=0,a=0,l="",h=0,c=0;const d={};let p=5;for(const t of i){const e=Ta(t);null!=t?.id&&(d[String(t.id)]=e),e>p&&(p=e)}const u=(t,e,i,s=!1)=>{if(!(t>Ma||s))return;o++,t>r&&(r=t);const n=t*hn*e;n>a&&(a=n,l=i)};for(const t of i){const e=Ta(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){n++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[Ca(e[0]),Ca(e[1])];return t=Math.max(t,Da(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,o=s.w||0,n=s.h||0,r=Ca(e+o),a=Ca(i+n),l=Ca(e),h=Ca(i),c=Math.max(dn,r-l),d=Math.max(dn,a-h);t=Pa(e,i,o,n,l,h,c,d),s.x=l,s.y=h,s.w=c,s.h=d}u(t,e,i)}for(const s of t.room_drafts||[]){n++;let t=0;const o=(s.points||[]).map(e=>{const i=[Ca(e[0]),Ca(e[1])];return t=Math.max(t,Da(e[0],e[1],i[0],i[1])),i}),r=o.length?[o[0]]:[],a=[];for(let t=0;t+1t.points?.length>=2),c+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const s of t.partitions||[]){n++;const t=[Ca(s.a[0]),Ca(s.a[1])],o=[Ca(s.b[0]),Ca(s.b[1])];let r=Math.max(Da(s.a[0],s.a[1],t[0],t[1]),Da(s.b[0],s.b[1],o[0],o[1]));Da(t[0],t[1],o[0],o[1])>Ma?(s.a=t,s.b=o):r=0,u(r,e,i)}for(const s of t.wall_columns||[]){n++;const t=[Ca(s.center[0]),Ca(s.center[1])],o=Da(s.center[0],s.center[1],t[0],t[1]);s.center=t,u(o,e,i)}for(const s of t.decor||[]){n++;let t=0;if("line"===s.kind){const e=[Ca(s.x1),Ca(s.y1)],i=[Ca(s.x2),Ca(s.y2)];t=Math.max(Da(s.x1,s.y1,e[0],e[1]),Da(s.x2,s.y2,i[0],i[1])),s.x1=e[0],s.y1=e[1],s.x2=i[0],s.y2=i[1]}else{const e=Ca(s.x),i=Ca(s.y);if(null!=s.w&&null!=s.h){const o=Ca(s.x+s.w),n=Ca(s.y+s.h),r=Math.max(dn,o-e),a=Math.max(dn,n-i);t=Pa(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=Da(s.x,s.y,e,i);s.x=e,s.y=i}u(t,e,i)}for(const s of t.openings||[]){n++;const o=Re([s.x,s.y],t.rooms||[],.025,{step:dn,length:Number(s.length)||0});if(!o)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===o.angle),l=za(s.x,s.y,Number.isFinite(r)?r:o.angle,Number(s.length)||0,o.x,o.y,o.angle);s.x=o.x,s.y=o.y,s.angle=o.angle,a&&h++,u(l,e,i,a)}}for(const[t,e]of Object.entries(s)){if(!e||"object"!=typeof e)continue;const i=e;if("number"!=typeof i.x||"number"!=typeof i.y)continue;n++;const o=Ca(i.x),r=Ca(i.y),a=Da(i.x,i.y,o,r);s[t]={...i,x:o,y:r};const l="string"==typeof i.s?i.s:"";u(a,d[l]??p,l)}return{spaces:i,layout:s,report:{moved:o,total:n,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:h,removedDrafts:c},changed:o>0}}(i.spaces||[],e||{});i.spaces=l.spaces;const h={...l.report};let c=0,d=0,p=0;for(let t=0;t[t.id,t.open_to||[]]),walls:e.walls||[]}),n=Ea(e),r=Ea(s);if(!n||!r)continue;const l=Ia(n.rooms),u=Ia(r.rooms),_=.02*cn;let m=ar(Kn(e.open_spans),l,u,Qo);const g=m.length;m=rr(m,r.rooms,Qo,_),m=m.map(t=>{const e=Gn(t,Qo),i=[e[0],e[1]],o=[e[2],e[3]],n=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=e[2]-e[0],a=e[3]-e[1],l=Math.hypot(r,a)||1,c=u.map(([t,e])=>[t[0],t[1],e[0],e[1]]).filter(t=>{const e=t[2]-t[0],s=t[3]-t[1],h=Math.hypot(e,s)||1;return Math.abs(r*s-a*e)/(l*h)<=1e-6&&Yn(n,t).d<=4*_&&Yn(i,t).d<=4*_&&Yn(o,t).d<=4*_}).sort((t,e)=>Math.hypot(e[2]-e[0],e[3]-e[1])-Math.hypot(t[2]-t[0],t[3]-t[1]))[0];if(h.total++,!c)return t;const d=[[c[0],c[1]],[c[2],c[3]]],p=Zn(i,c,d,cn,2*_),m=Zn(o,c,d,cn,2*_);if(Math.hypot(m[0]-p[0],m[1]-p[1])<.5*cn)return t;const g=Math.max(Math.hypot(p[0]-i[0],p[1]-i[1]),Math.hypot(m[0]-o[0],m[1]-o[1]));if(g>1e-6*cn){h.moved++;const t=g/Qo;t>h.maxShift&&(h.maxShift=t);const e=g/cn*(Number(s.cell_cm)>0?Number(s.cell_cm):5);e>h.maxShiftCm&&(h.maxShiftCm=e,h.maxSpace=String(s.id||""))}return jn(p,m,Qo)}),m=rr(m,r.rooms,Qo,_),d+=Math.max(0,g-m.length);const f=m.map(t=>Gn(t,Qo));m.length?s.open_spans=m:delete s.open_spans,ir(s.rooms||[],r.rooms,f,_);const v=Array.isArray(e.walls)?e.walls.length:0;let y=_o(e.walls,l,u,dn,Qo);y=zo(r.rooms,y,f,dn,Number(s.cell_cm)>0?Number(s.cell_cm):5,cn,Qo),y=po(y,s.rooms||[],dn,1,f.map(t=>[t[0]/Qo,t[1]/Qo,t[2]/Qo,t[3]/Qo])),c+=Math.max(0,v-y.length),y.length?s.walls=y:delete s.walls;const b=JSON.stringify({spans:s.open_spans||[],links:(s.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:s.walls||[]});b!==o&&p++}const u=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o;n<5&&u&&(i.model_version=5);const _=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o,m=Number.isInteger(Number(i.model_version))?Number(i.model_version):n;return{config:i,layout:l.layout,report:{...h,modelFrom:n,modelTo:m,migrated:r,canonicalized:p,wallsMerged:c,spansMerged:d},changed:_}}class Fa{constructor(t=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(t))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(t){this._undo.push(t),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const t=this._undo.pop()??null;return t&&this._redo.push(t),t}redo(){const t=this._redo.pop()??null;return t&&this._undo.push(t),t}clear(){this._undo=[],this._redo=[]}}const La="1.60.2-beta.2",qa=2e3,Ha=1e3,Ua=1500,Wa=15e3,Ba=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const i=Number(e);return Number.isFinite(i)?i:null},ja=(t,e)=>{if(!t.has(e))return{hit:!1};const i=t.get(e);return t.delete(e),t.set(e,i),{hit:!0,value:i}},Ga=(t,e,i,s)=>{for(t.delete(e),t.set(e,i);t.size>s;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}};let Va="undefined"!=typeof document&&"hidden"===document.visibilityState?Date.now():0,Ka=0;const Ya=new Map;let Ja=0;const Za=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Xa=1e4;const Qa="houseplan_card_layout_v1",tl="houseplan_card_cfg_v1",el="houseplan_card_zoom_v1",il="houseplan_card_nav_v1",sl="houseplan_card_kiosk_v1",ol=1e3,nl=3300,rl=(t,e)=>{const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return{x:i,y:s,w:Math.max(t.x+t.w,e.x+e.w)-i,h:Math.max(t.y+t.h,e.y+e.h)-s}},al=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},ll=(t,e)=>{let i,s=null;const o=(...o)=>{clearTimeout(i),s=o,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return o.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},o.pending=()=>void 0!==i,o},hl=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class cl extends lt{constructor(){super(...arguments),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._unsubCfg=null,this._unsubLayout=null,this._layoutRev=0,this._canOptimizeUndo=!1,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Os},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._navMotion="",this._editorSwapAnimations=[],this._editorChromeMode="plan",this._tool="draw",this._geometryHistory=new Fa(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._cleanFloorCache=new Map,this._glowClipCache=new Map,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._path=[],this._cursorPt=null,this._mergeSel=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._openWallAnchor=null,this._splitSel=null,this._pendingSplit=null,this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._alignDialog=null,this._settingsDialog=null,this._sunRaysCache=null,this._skyElev=null,this._skySnap=!1,this._skySnapRaf=0,this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=t=>this._onKey(t),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++Ja,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{if("hidden"!==document.visibilityState){if("visible"===document.visibilityState){const t=Date.now();Va&&t-Va>=Wa&&(Ka=t),Va=0,this._vacJumpOnce=!0,this._skyElev=null,this._tip=null,this._hoverRoom=null,t-Ka<=15e3&&this._beginResumeSettle(),this.requestUpdate()}}else Va||(Va=Date.now())},this._resumeSettling=!1,this._resumeRaf=0,this._resumeStarted=0,this._resumeLastSize="",this._resumeLastChange=0,this._viewportInvalidAt=0,this._vacFit=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._lastTap=0,this._onHashChange=()=>{const t=this._hashSpace();t&&this._model.find(e=>e.id===t)&&t!==this._space&&(this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._selId=null,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=null,this._decorSnapCache=null,this._showHidden=!1,this._connHooked=null,this._onConnReady=()=>{this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._loading||(this._loadOk&&this._unsubCfg?this._reloadConfigOnly():this._loadFromServer())},this._signer=new lr(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=ll(()=>{if(this._serverStorage){const t=[...this._dirtyPos];this._dirtyPos.clear();for(const e of t){const t=this._layout[e];t&&(this._sentPos.set(e,t),this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t}).then(t=>this._noteLayoutRev(t)).catch(t=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))).finally(()=>{this._sentPos.get(e)===t&&this._sentPos.delete(e)}))}this._cacheSnapshot()}else localStorage.setItem(Qa,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.undo();t&&(this._applyGeometryState(t.before)?this._showToast(this._t("history.undone",{name:t.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.redo();t&&(this._applyGeometryState(t.after)?this._showToast(this._t("history.redone",{name:t.name})):this._geometryHistory.clear())},this._saveConfigDebounced=ll(()=>{this._serverCfg&&this._writeConfig().catch(t=>{"conflict"===t?.code?(this._showToast(this._t("toast.conflict")),this._cancelPath(),this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(t)}))})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=Ba(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Lo:100);const s=this._imperial?2.54*i:i,o="column"===t.kind?Lo:100;if(!Number.isFinite(s)||s<1||s>o)return void this._showPhysicalRange(o);if("column"===t.kind){const e=this._spaceModel().wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Ba(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:s}:{id:t.id,shape:"square",center:e.center,cm:s,angle:i};if(this._spaceModel().wall_columns.some(e=>e.id!==t.id&&Xo(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const n=this._geometrySnapshot();if("partition"===t.kind){const i=(e.partitions||[]).find(e=>e.id===t.id);i&&(i.cm=s)}else if("column"===t.kind){const i=(e.wall_columns||[]).find(e=>e.id===t.id);i&&(i.cm=qo(s),i.shape="circle"===t.shape?"circle":"square","square"===i.shape?i.angle=Ba(t.angle||"0"):delete i.angle)}else{const i=(e.room_drafts||[]).find(e=>e.id===t.id);i?.segments?.[t.segment||0]&&(i.segments[t.segment||0].cm=s)}this._recordGeometry(this._t("history.physical_edit"),n),this._physicalDialog=null,this._saveConfig()},this._deletePhysicalSelection=()=>{const t=this._physicalSel,e=this._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot(),s="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[s]=(e[s]||[]).filter(e=>e.id!==t.id),e[s].length||delete e[s],this._activeDraftId===t.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftWhole=()=>{const t="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,e=this._curSpaceCfg;if(!t||!e||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===t&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftSegment=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(i<0)return;const s=e.room_drafts[i],o=Math.max(0,Math.min(s.segments.length-1,t.segment||0)),n=[],r=s.points.slice(0,o+1),a=s.points.slice(o+1);if(r.length>=2&&n.push({id:s.id,points:r,segments:s.segments.slice(0,o)}),a.length>=2&&n.push({id:n.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:a,segments:s.segments.slice(o+1)}),2===n.length&&e.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(i,1,...n),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=n.length?{kind:"draft",id:n[0].id}:null,this._recordGeometry(this._t("history.draft_segment_delete"),l),this._saveConfig()},this._keepClosedAsPartitions=()=>{if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const t=this._curSpaceCfg,e=this._path.slice(0,-1);if((t.partitions||[]).length+e.length>2e3)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys];t.partitions||=[];const o=Date.now().toString(36);for(let i=0;it.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._recordGeometry(this._t("history.contour_to_partitions"),i),this._saveConfig(),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this._spaceDialog;if(t)if(t.pickSaved)this._spaceDialog={...t,pickSaved:!1};else{this._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.hass.callWS({type:"houseplan/plans/list"}),e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(t)}))}}},this._aspectJob=null,this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const t=this._glowRadiusCm,e=this._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:e,bgColor:Si(this._settings,{bgColor:null})||null,northDeg:Rs(this._settings,{}),bgMode:As(this._settings,{}),sunRays:Ns(this._settings,{}),weatherEntity:Es(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=Oa(this._serverCfg,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),o=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,config:e.config,layout:e.layout,cm:i,where:o,changed:e.changed,busy:!1}},this._optimizeUndoBusy=!1,this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:mt).map(t=>({...t}));this._rulesDialog={rules:e,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}_startNavMotion(t){if(clearTimeout(this._navMotionTimer),this._navMotionTimer=void 0,window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return this._cancelEditorSwapAnimations(),void(this._navMotion="");this._navMotion=t,this._navMotionTimer=window.setTimeout(()=>{this._navMotionTimer=void 0,this._navMotion="",this.requestUpdate()},190)}_cancelEditorSwapAnimations(){for(const t of this._editorSwapAnimations)t.cancel();this._editorSwapAnimations=[],this.renderRoot.querySelector(".editorchrome")?.classList.remove("resizing")}_animateEditorSwap(t){this.updateComplete.then(()=>{if(!this.isConnected||"swap"!==this._navMotion||window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return;const e=this.renderRoot.querySelector(".editorchrome"),i=e?.querySelector(".editorchrome-inner");if(!e||!i)return;this._cancelEditorSwapAnimations();const s=i.getBoundingClientRect().height;e.classList.add("resizing");const o={duration:190,easing:"cubic-bezier(0.2, 0.7, 0.2, 1)"},n=[];if(t>0&&s>0&&Math.abs(t-s)>.5){const i=e.animate([{height:`${t}px`},{height:`${s}px`}],o);i.id="hp-editor-height-swap",n.push(i)}const r=i.animate([{opacity:.42,transform:"translateY(5px) scale(0.995)"},{opacity:1,transform:"translateY(0) scale(1)"}],o);r.id="hp-editor-content-swap",n.push(r),this._editorSwapAnimations=n,Promise.allSettled(n.map(t=>t.finished)).then(()=>{this._editorSwapAnimations===n&&(this._editorSwapAnimations=[],e.classList.remove("resizing"))})})}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate())}_pickSpace(t){if(t===this._space)return;const e=this._model.map(t=>t.id),i=e.indexOf(this._space),s=e.indexOf(t);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(t,i>=0&&s0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const t=this._model.map(t=>t.id),e=t.indexOf(this._space);this._slideTo(t[(e+1)%t.length],"left"),this._showKioskDots()}}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}_hashSpace(){const t=/(?:^|[#&])space=([^&]+)/.exec(window.location.hash||"");return t?decodeURIComponent(t[1]):""}connectedCallback(){document.addEventListener("visibilitychange",this._vacVisHandler),super.connectedCallback(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>ji(this._serverCfg)),this._config?.kiosk&&Number(this._config?.cycle)>0&&(clearInterval(this._cycleTimer),this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle))),window.addEventListener("hashchange",this._onHashChange),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0));const t=Date.now();"visible"===document.visibilityState&&Va&&(t-Va>=Wa&&(Ka=t),Va=0),(this._warmLongReturn||t-Ka<=15e3)&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._resumeRaf&&(cancelAnimationFrame(this._resumeRaf),this._resumeRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._activityRt.values())clearTimeout(t.timer);window.removeEventListener("keydown",this._keyHandler),clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._navMotionTimer),this._cancelEditorSwapAnimations(),this._slideTimer=void 0,this._navMotionTimer=void 0,this._slide="",this._navMotion="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._resumeSettling=!1,super.disconnectedCallback()}_onKey(t){if("Escape"===t.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void t.stopPropagation();if("Escape"===t.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void(this._infoCard=null);if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return void(this._alignDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}const e=(t.composedPath?.()||[t.target]).some(t=>t?.matches?.('input, textarea, select, [contenteditable="true"]')),i=t.ctrlKey||t.metaKey,s=t.key.toLowerCase(),o=/^[a-z]$/.test(s),n="z"===s||!o&&"KeyZ"===t.code,r="y"===s||!o&&"KeyY"===t.code,a=i&&(n&&t.shiftKey||r),l=i&&n&&!t.shiftKey;if("decor"===this._mode){if((l||a)&&e)return;return a?(t.preventDefault(),void this._redoGeometry()):l?(t.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||e?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():this._furnPalette?this._furnPalette=null:this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(t.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!l&&!a||!e)){if(("Delete"===t.key||"Backspace"===t.key)&&this._physicalSel&&!e)return t.preventDefault(),void this._deletePhysicalSelection();if(a)return t.preventDefault(),void this._redoGeometry();if(l)return t.preventDefault(),this._rszDrag?void this._rszCancelDrag():"draw"!==this._tool&&"partition"!==this._tool||!this._path.length?"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry():void(this._activeDraftId&&this._path.length>1?this._undoGeometry():this._undoPoint());if("Escape"===t.key)return this._physicalDrag||this._physicalRotate?(t.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(t.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length||"partition"===this._tool&&this._path.length?(t.preventDefault(),void this._undoPoint()):this._physicalSel?(t.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():void(this._rszSel?this._rszSel=null:this._tool="draw")):"split"===this._tool?(t.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(t.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(t.preventDefault(),void(this._wallDialog=null)):"openwall"===this._tool||"closewall"===this._tool?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"partition"!==this._tool&&"column"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const t=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const e=this._curSpaceCfg,i=(e.room_drafts||[]).findIndex(t=>t.id===this._activeDraftId);return void(i>=0&&(this._path.length<2?(e.room_drafts.splice(i,1),e.room_drafts.length||delete e.room_drafts,this._activeDraftId=null):e.room_drafts[i]={id:this._activeDraftId,points:this._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentCms.map(t=>({cm:t}))},this._recordGeometry(this._t("history.draft_segment_delete"),t),this._saveConfig()))}this._path=this._path.slice(0,-1)}}static getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(t){for(const t of Ya.values())for(const e of t)clearTimeout(e.evict);Ya.clear(),Xa=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of Ya.values())for(const o of s)t++,o.dlg&&(e++,i.push(o.dlg.kind));return{keys:Ya.size,slots:t,dlgs:e,drafts:i}}setConfig(t){this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...t},this._config.kiosk&&(this._booting=!1,this._bootFading=!1),t.default_floor&&(this._space=t.default_floor);try{this._zoomBySpace=JSON.parse(localStorage.getItem(el)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(sl)||"null");this._kioskScale={icon:Ki(t?.icon),font:Ki(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(tl)||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._layout=e.layout||{},this._serverStorage=!0;const i=this._hashSpace(),s=this._savedNav();i&&this._model.find(t=>t.id===i)?(this._space=i,this._hashApplied=!0):s?.space&&this._model.find(t=>t.id===s.space)?(this._space=s.space,this._navApplied=!0):t.default_floor?this._space=t.default_floor:this._model.find(t=>t.id===this._space)||(this._space=this._model[0]?.id||this._space),s?.mode&&"view"!==s.mode&&!t.kiosk&&(this._canEdit?this._mode=s.mode:this._pendingNavMode=s.mode)}}catch{}"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const t=Za(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=Ya.get(t);if(!s||!s.length)return;const o=s.find(t=>t.owner===this._warmGen);if(o)return this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Wa,clearTimeout(o.evict),o.evict=0,o.freed=0,o.live=!0,this._warmSlot=o,void(this._warmKey=t);const{slot:n,sure:r}=((t,e,i,s)=>{const o=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let n=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=o(i);t<=0||(t>r?(n=i,r=t,a=1):t===r&&a++)}return!n||a>1?{slot:n||l,sure:!1}:{slot:n,sure:!0}})(s,this._warmGen,e,i);n&&(this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=Wa,this._booting=!1,this._bootFading=!1,this._hdrH=n.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)),this._warmKey=t,r?(clearTimeout(n.evict),n.evict=0,n.owner=this._warmGen,n.place=e?new WeakRef(e):null,n.idx=i,n.live=!0,this._warmSlot=n,this._warmVp=n.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:n.hdrH,stageH:n.stageH,vp:null,dlg:null,freed:0,evict:0},s.push(this._warmSlot),this._warmTrim(s)))}_warmIdx(t){const e=t?.children;if(!e)return-1;for(let t=0;t4;){const e=t.findIndex(t=>!t.live);if(e<0)break;clearTimeout(t[e].evict),t.splice(e,1)}}_warmScheduleEvict(t,e){if(clearTimeout(t.evict),!t.dlg)return;const i=t.freed,s=t.owner;t.evict=window.setTimeout(()=>{if(t.evict=0,t.freed!==i||t.owner!==s)return;t.dlg=null;const o=Ya.get(e);if(!t.live&&o&&o.length>1){const e=o.indexOf(t);e>=0&&o.splice(e,1)}},Xa+250)}_warmAdoptViewport(t){const e=this._warmVp;e&&(!this._hashApplied&&this._model.find(t=>t.id===e.space)?(this._space=e.space,this._navApplied=!0,this._mode="view"!==e.mode&&this._canEdit&&!t.kiosk?e.mode:"view",this._pendingNavMode="view"===e.mode||this._canEdit||t.kiosk?null:e.mode,this._zoom=e.zoom,this._view=e.view?{...e.view}:null,this._viewModeSnap=e.snap?{...e.snap}:null,this._tool=e.tool,this._decorTool=e.decorTool,this._showHidden=e.showHidden,this._showFar!==e.showFar&&(this._showFar=e.showFar,this._frame=null),this._selId=e.selId,this._rszSel=e.rszSel,this._decorSel=e.decorSel,this._warmVpArmed=!0):this._warmVp=null)}_warmPatch(t,e=!1){if(this._config?.kiosk)return;const i=Za(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!e)return;const t=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,place:t?new WeakRef(t):null,idx:this._warmIdx(t),live:!0,hdrH:this._hdrH,stageH:0,vp:null,dlg:null,freed:0,evict:0};const s=Ya.get(i)||[];for(s.push(this._warmSlot),Ya.set(i,s),this._warmTrim(s);Ya.size>8;){const t=Ya.keys().next().value;if(void 0===t||t===i)break;for(const e of Ya.get(t)||[])clearTimeout(e.evict);Ya.delete(t)}}Object.assign(this._warmSlot,t)}}_warmViewportState(){return{space:this._space,mode:this._mode,zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._rszSel,decorSel:this._decorSel}}_warmDialogState(){const t=(t,e)=>({kind:t,space:this._space,mode:this._mode,data:e});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog?null:this._openingInfo?t("openingInfo",this._openingInfo.id):this._infoCard?t("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:t("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:t("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:t("marker",this._markerDialog):this._openingDialog?t("opening",this._openingDialog):this._backdropDialog?t("backdrop",this._backdropDialog):this._decorShapeDialog?t("decorShape",this._decorShapeDialog):this._decorTextDialog?t("decorText",this._decorTextDialog):this._roomDialog?t("room",{editId:this._roomEditId,fill:this._roomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:t("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._resumeSettling||this._config?.kiosk)return;const t={vp:this._warmViewportState()};if(this._warmRevivePending||(t.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const e=this.parentNode;t.place=e?new WeakRef(e):null,t.idx=this._warmIdx(e)}this._warmPatch(t)}_warmReviveDialog(){this._warmRevivePending=!1;const t=this._warmSlot;if(this._warmReviveTimer=void 0,!t||!t.dlg)return;const e=t.dlg,i=t.freed;if(t.dlg=null,t.freed=0,clearTimeout(t.evict),t.evict=0,i&&!(Date.now()-i>Xa)&&e.space===this._space&&e.mode===this._mode){switch(e.kind){case"space":this._spaceDialog={...e.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...e.data,busy:!1};break;case"settings":this._settingsDialog={...e.data,busy:!1};break;case"rules":this._rulesDialog={...e.data,busy:!1};break;case"opening":this._openingDialog={...e.data};break;case"backdrop":this._backdropDialog={...e.data};break;case"decorShape":this._decorShapeDialog={...e.data};break;case"decorText":{this._decorTextDialog={...e.data};const t=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:t,end:t};break}case"room":{const t=e.data;this._roomEditId=t.editId,this._roomFill=t.fill,this._roomTempSrc=t.tempSrc,this._roomHumSrc=t.humSrc,this._roomSrcOpen=t.srcOpen,this._roomSrcFilter=t.srcFilter,this._roomNameScale=t.nameScale,this._roomLabelScale=t.labelScale,this._areaSel=t.areaSel,this._nameSel=t.nameSel,this._pendingSplit=t.pendingSplit,this._path=t.path,this._roomDialog=!0;break}case"info":{const t=this._devices.find(t=>t.id===e.data);t&&(this._infoCard=t);break}case"openingInfo":{const t=(this._curSpaceCfg?.openings||[]).find(t=>t.id===e.data);t&&(this._openingInfo=t);break}}this.requestUpdate()}}_cacheSnapshot(){if(this._serverCfg)try{localStorage.setItem(tl,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,layout:this._layout}))}catch{}}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const t=this._serverCfg?.spaces||[];let e=t.length+":";for(const i of t){e+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const t of i.rooms||[]){const i=t.poly?.[0],s=t.poly?.[t.poly.length-1];e+=(t.poly?.length||0)+"."+(t.id||"")+"."+(t.open_to||[]).join("+")+"."+(t.area||"")+"."+JSON.stringify(t.settings||0)+"."+(t.x??"")+","+(t.y??"")+","+(t.w??"")+","+(t.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(s?s[0]+"/"+s[1]:"")+";"}}return e}get _model(){if(!this._serverCfg)return[];const t=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===t)return this._modelCache.model;const e=this._buildModel();return this._modelCache={key:t,model:e},e}_buildModel(){if(!this._serverCfg)return[];const t=this._renderCfg;return rn(t).map((e,i)=>{const s=t.spaces[i]?.plan_url;return e.bg&&s?{...e,bg:{...e.bg,href:s}}:e})}_spaceModel(t){const e=this._model;return e.find(e=>e.id===(t??this._space))||e[0]}get _areaToSpace(){const t={};for(const e of this._model)for(const i of e.rooms)i.area&&(t[i.area]={space:e.id,room:i});return t}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const t=this._serverCfg,e=function(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,iconRules:r}=t,a=!1!==o.group_lights,l=oa(s),h=ia(e,a).filter(t=>!na(e,t.eid,l)),c=new Set(h.map(t=>t.area)),d=Or(e),p=new Set(s.map(t=>t.binding)),u=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(p.has("device:"+t.id))continue;const o=d[t.id]||[],l=Fr(e,t,o);let h=n.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;if(!h&&a&&c.has(s)){const i=(t.name_by_user||t.name||"").trim();"mdi:lightbulb"===sa(e,i,t.model,o,r)&&(h=!0)}h&&u.push("device:"+t.id)}return u}({hass:this.hass,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!e.length&&t.settings?.filter_seeded)return;t.markers=t.markers||[];const i=[];for(const s of e){const e="h"+s.slice(s.indexOf(":")+1);t.markers.push({id:e,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...t.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(t=>!i.includes(t))),t.settings=s,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const t=this._settings.icon_rules;if(!t||!Array.isArray(t)||!t.length)return;const e=JSON.stringify(t);return e!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=e,this._rulesCompiled=gt(t)),this._rulesCompiled}get _fillColors(){return Di(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):_t}willUpdate(t){t.has("_serverCfg")&&this._cfgEpoch++,this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick())}updated(){this._skyRelease(),this._warmSnapshot(),this._dtMeasure();const t=this._stageEl;t&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(t)),t&&this._booting&&!this._bootTimer&&this._bootWatch();const e=this.renderRoot.querySelector(".hdr");if(e&&t&&!this._roHdr){const i=()=>{const e=this.renderRoot.querySelector("ha-card");if(!e)return;const i=t.getBoundingClientRect().top-e.getBoundingClientRect().top,s=Math.min(Math.max(e.getBoundingClientRect().top,0),120),o=Math.round(i+s);o>=0&&o!==this._hdrH&&(this._hdrH=o),o>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:o,stageH:t.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(e),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(t&&!this._view&&this._refitView(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const t=function(t){const e=t?.floors;if(!e||"object"!=typeof e)return[];const i=[];for(const t of Object.values(e))t&&t.floor_id&&i.push({id:t.floor_id,name:t.name||t.floor_id,level:t.level??null});return i.sort((t,e)=>{const i=t.level??1e9,s=e.level??1e9;return i!==s?i-s:t.name.localeCompare(e.name)}),i}(this.hass);t.length?this._importDialog={floors:t.map(t=>({...t,checked:!0}))}:this._openSpaceDialog("create")}}async _loadFromServer(){this._loading=!0,this._loadTries++;try{const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._loadOk=!0,this._serverStorage=!0,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._canOptimizeUndo=!(!t?.can_optimize_undo&&!e?.can_optimize_undo),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;this._geometryHistory.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=i&&Array.isArray(i.spaces)?i:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._layout=e?.layout||{},this._layoutRev=e?.rev??0,this._unsubCfg||(this._unsubCfg=await this.hass.connection.subscribeEvents(t=>{const e=Number(t?.data?.rev??-1);e!==this._cfgRev&&this._reloadConfigOnly(!1,e)},"houseplan_config_updated")),this.hass.callWS({type:"houseplan/trail/get"}).then(t=>{this._vacSrvTrails=t?.trails||{},this.requestUpdate()}).catch(()=>{}),this._unsubTrail||(this._unsubTrail=await this.hass.connection.subscribeEvents(async()=>{try{const t=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=t?.trails||{},this.requestUpdate()}catch{}},"houseplan_trail_updated")),this._unsubLayout||(this._unsubLayout=await this.hass.connection.subscribeEvents(t=>this._onLayoutEvent(Number(t?.data?.rev??-1)),"houseplan_layout_updated"));const s=this._hashSpace(),o=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):o?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===o.space)?(this._space=o.space,this._navApplied=!0):this._norm&&!this._model.find(t=>t.id===this._space)&&(this._space=this._model[0]?.id||this._space),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:this._restoreZoom()}catch(t){if(this._serverCfg)this._scheduleLoadRetry();else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(Qa)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}async _reloadConfigOnly(t=!1,e){if(!t){if(void 0!==e&&e<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,e),400))}try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._geometryHistory.clear(),this._clearGeometryGesture(),this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._canOptimizeUndo=!!t?.can_optimize_undo,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null),this._cacheSnapshot(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(t){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(t)}))}}_scheduleLoadRetry(){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),t.addEventListener?.("ready",this._onConnReady),this._connHooked=t)}_display(t){return this._signer.display(this.hass,t)}_resign(){this._signer.resign(this.hass,ji(this._serverCfg))}_onLayoutEvent(t){t<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{t<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(t){const e=t?.rev;"number"==typeof e&&e>this._layoutRev&&(this._layoutRev=e)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;const t=new Map;for(const e of this._dirtyPos)this._layout[e]&&t.set(e,this._layout[e]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[e,i]of this._sentPos)t.set(e,i);try{const e=await this.hass.callWS({type:"houseplan/layout/get"}),i={...e?.layout||{}};for(const[e,s]of t)i[e]=s;this._layout=i,this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!!e?.can_optimize_undo,this._cacheSnapshot(),this.requestUpdate()}catch{}}_maybeRebuildDevices(){const t=this.hass;if(!t?.devices||!t?.entities||!t?.areas)return;const e=Object.keys(t.devices).length+":"+Object.keys(t.entities).length+":"+Object.keys(t.areas).length+":"+(this._norm?"n":"l")+":"+pa(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=aa({hass:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:t=>this._t(t),iconRules:this._iconRules}),this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime())}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const t=this._devices.filter(t=>!t.marker&&!t.virtual).map(t=>t.id).sort(),e=t.join(",");if(e===this._newSyncKey)return;this._newSyncKey=e;const i=this._settings,{fresh:s,known:o}=function(t,e){if(!Array.isArray(e))return{fresh:[],known:[...t]};const i=new Set(e),s=t.filter(t=>!i.has(t));return{fresh:s,known:s.length?[...e,...s]:e}}(t,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const t=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:o,new_device_ids:t}},this._saveConfig()}}get _newIds(){const t=this._settings.new_device_ids;return new Set(Array.isArray(t)?t:[])}_ackNewDevice(t){if(!this._newIds.has(t)||!this._serverCfg)return;const e=this._settings;this._serverCfg={...this._serverCfg,settings:{...e,new_device_ids:(e.new_device_ids||[]).filter(e=>e!==t)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(t){if(!t)return null;const e=[];for(const i of this._devices){if(i.area!==t||i.virtual)continue;const s=Zr(this.hass,i.entities);null!=s&&e.push(s)}return Qe(e)}_roomBounds(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}_defaultPositions(){const t={},e=this._config?.icon_size??2.5;for(const i of this._model){const s=e/100*Cn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const o=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!o.length)continue;const n=this._roomBounds(e),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(o.length*a/Math.max(l,1)))),c=Math.ceil(o.length/h),d=a/h,p=l/Math.max(c,1),u=o.map((t,e)=>({x:n.x+r+d*(e%h+.5),y:n.y+r+p*(Math.floor(e/h)+.5)}));ei(u,n,s,.5*r),o.forEach((e,i)=>t[e.id]=un(u[i]))}}return t}_pos(t){const e=this._spaceModel(t.space),i=this._layout[t.id];if(i)if(this._norm){if(i.s===t.space)return{x:i.x*ol,y:i.y*ol}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:un(Mn(e))}_savePos(t,e,i){if(this._norm){const s=this._gridPitch,o=Math.round(e/s)*s,n=Math.round(i/s)*s,r=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:mn(o/ol),y:mn(n/ol),...r?{k:r}:{}}}}else this._layout={...this._layout,[t.id]:{x:Math.round(e),y:Math.round(i)}};this._dirtyPos.add(t.id),this._persistLayout()}_coverIndicator(t){return"cover"===t.tapAction?ci(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_displayOf(t){const e=t.marker?.display;return"ripple"===e?"icon_ripple":e||"badge"}_visualSamples(t){let e=[];const i=this._coverIndicator(t),s=Yr(this.hass,[t]);if(i)e.push(Er(this.hass,i));else if(s.length)e.push(...s.map(t=>Er(this.hass,t.eid)));else{const i=Ur(this.hass,t.entities),s=i.length?i:t.primary?[t.primary]:[];e=function(t,e,i){const s=e.map(e=>Er(t,e)),o=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&o.length>1&&Ar(t,e[0])?s.map(t=>"unavailable"===t.availability?t:"off"===t.state?{...t,availability:"unavailable",status:"neutral",activity:"none",edge:"none"}:{...t,status:"neutral",activity:"none",edge:"none"}):s}(this.hass,s,t.entities)}for(const i of t.entities||[]){const t=Er(this.hass,i);"alarm"!==t.status||e.some(t=>t.eid===i)||e.push(t)}return e}_deviceVisual(t){if(t.hidden)return{availability:"available",status:"neutral",activity:"none"};const e=this._visualSamples(t),i=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return $r;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(e);if("alarm"===i.status)return i;if(!this._config?.live_states)return{availability:"available",status:"neutral",activity:"none"};if("unavailable"===i.availability)return i;const s=this._activityRt.get(t.id),o=this._activitySourceKey(e);return s?.sources===o&&s.flashTs&&s.flashKind&&Date.now()-s.flashTst?.device_id===e).map(([t])=>t)}_bindingHasClimate(t){return this._bindingEntities(t).some(t=>t.startsWith("climate."))}_bindingCoverEntity(t){return ci(this._bindingEntities(t))}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!hi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&ta(this.hass,t.primary)?ea(this.hass,t.entities):null}_openMoreInfo(t){t?al(this,"hass-more-info",{entityId:t}):this._showToast(this._t("toast.no_entity"))}_ctxDevice(t,e){"view"===this._mode&&(t.preventDefault(),t.stopPropagation(),e.primary?this._openMoreInfo(e.primary):this._infoCard=e)}_clickDevice(t,e){if(t.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(e);const i=this._coverIndicator(e),s=this._actEntity(e),o=s?s.split(".")[0]:null,n=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=Yr(this.hass,[e]).filter(t=>"controls"===t.via).map(t=>t.eid);if("toggle"===e.tapAction&&r.length){const t=(a=r.map(t=>this.hass.states[t]?.state),a.some(t=>"on"===t)?"turn_off":"turn_on");return void n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant",t,{entity_id:r}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}var a;const l=function(t,e,i,s){const o=t||e||("light"===i?"toggle":"info");return"more-info"===o?"more-info":"run"===o?"run"===t?"run":"info":"cover"===o?"cover"!==t||"cover"!==i||hi.has(String(s||""))?"info":"cover":"toggle"!==o||!i||li.has(i)?"info":"toggle"===t?"toggle":ai.has(i)?"cover"===i&&hi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,o,s?this.hass.states[s]?.attributes?.device_class:null);if("run"===l){const t=e.marker?.tap_target||"",i=function(t){const e=String(t||"").split(".")[0];return"automation"===e?{domain:"automation",service:"trigger"}:"script"===e?{domain:"script",service:"turn_on"}:"scene"===e?{domain:"scene",service:"turn_on"}:null}(t),s=this.hass.states[t];if(!i||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||t;return void n(this._t("confirm.tap_run",{name:o}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>{this._stampActivity(e.id,"event",this._activitySourceKey(this._visualSamples(e))),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}if("cover"===l&&i){const t=function(t){const e=String(t||"");return"closed"===e?"open_cover":"open"===e?"close_cover":"opening"===e||"closing"===e?"stop_cover":"toggle"}(this.hass.states[i]?.state);return void n(this._t("confirm.tap_cover",{name:e.name}),()=>{this.hass.callService("cover",t,{entity_id:i}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}"toggle"===l&&e.primary?n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant","toggle",{entity_id:e.primary}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}):"more-info"===l&&e.primary?this._openMoreInfo(e.primary):this._infoCard=e}_t(t,e){return ua(pa(this.hass,this._config?.language),t,e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(t){const e=[];for(const i of this._devices){if(i.space!==t.id||i.hidden)continue;const s=this._pos(i);e.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(t.id===this._space){for(const t of this._openingsR){const i=Number(t.angle)*Math.PI/180,s=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2,n=vn([[t.rx-s,t.ry-o],[t.rx+s,t.ry+o]]);n&&e.push(n)}const i=this._decorH;for(const t of this._decorList){const s=vn("line"===t.kind?[[t.x1*ol,t.y1*i],[t.x2*ol,t.y2*i]]:"text"===t.kind?[[t.x*ol,t.y*i]]:js({x:t.x*ol,y:t.y*i,w:t.w*ol,h:t.h*i,angle:t.angle}));s&&e.push(s)}for(const i of this._physicalBodiesR(t)){const t=vn(i);t&&e.push(t)}}return bn(t,e)}_frameOf(){const t=this._spaceModel(),e=this._frame,i="view"!==this._mode;if(e&&e.id===t.id&&this._bdDrag)return e;if(e&&e.id===t.id&&e.model===t&&e.layout===this._layout&&e.devs===this._devices&&e.far===this._showFar&&e.grow===i)return e;const s=xn(this._contentItems(t));let o=s.all||Sn(t),n=this._showFar?o:s.core||Sn(t);return e&&e.id===t.id&&i&&e.grow&&(n=rl(e.rect,n),o=rl(e.all,o)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:n,all:o,outliers:s.outliers},this._frame}_baseVb(){const t=this._frameOf().rect;return[t.x,t.y,t.w,t.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -3098,7 +3117,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow style="left:${s.toFixed(1)}%;top:${o.toFixed(1)}%" @click=${t=>{t.stopPropagation(),this._fitAll()}}> - `}_stageAspect(){const t=this._stageEl,e=this._baseVb();return t&&t.clientHeight?t.clientWidth/t.clientHeight:e[2]/e[3]}_viewOr(t){return this._view&&this._view.w?this._view:ti(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),o=i?.clientWidth||1,n=i?.clientHeight||1;return[s.x+t/o*s.w,s.y+e/n*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const o=1*Math.max(e,s),n=i-o,r=i+s-e+o;return Math.max(Math.min(n,r),Math.min(Math.max(n,r),t))};return{w:t.w,h:t.h,x:i(t.x,t.w,e.x,e.w),y:i(t.y,t.h,e.y,e.h)}}_applyView(t,e,i){const s=this._baseVb(),o=ti(s,this._stageAspect()),n=Math.min(hl.ZOOM_MAX,Math.max(hl.ZOOM_MIN,t)),r=o.w/n,a=o.h/n,l=this._viewOr(s),h=e??l.x+l.w/2,c=i??l.y+l.h/2;this._zoom=n,this._view=this._clampView({x:h-r/2,y:c-a/2,w:r,h:a},o)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const t=()=>{if(!this._booting)return;const e=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=e);const s=e-this._bootStart;s>=1200||s>=700&&i>0&&e-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(t,100)};this._bootTimer=window.setTimeout(t,100)}_bootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1;const t=this._stageEl?.clientHeight??0;!this._config?.kiosk&&t>0&&this._warmPatch({hdrH:this._hdrH,stageH:t,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._kiosk||"view"!==this._mode||this._booting||this._resumeSettling||(this._resumeSettling=!0,this._resumeStarted=performance.now(),this._resumeLastSize="",this._resumeLastChange=this._resumeStarted,this._viewportInvalidAt=0,this.requestUpdate(),this._resumeRaf&&cancelAnimationFrame(this._resumeRaf),this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick()))}_resumeSettleTick(){if(this._resumeRaf=0,!this._resumeSettling||!this.isConnected)return;if(this._kiosk||"view"!==this._mode)return this._resumeSettling=!1,void this.requestUpdate();const t=performance.now(),e=this._stageEl,i=!!e&&e.clientWidth>0&&e.clientHeight>0;if(i){const i=`${e.clientWidth}x${e.clientHeight}`;i!==this._resumeLastSize&&(this._resumeLastSize=i,this._resumeLastChange=t)}const s=t-this._resumeStarted;if(i&&s>=220&&t-this._resumeLastChange>=80||s>=750){const t=this._view;return this._resumeSettling=!1,i&&this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),void this.requestUpdate()}this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick())}_refitView(){const t=this._stageEl;if(!t||"visible"!==document.visibilityState||t.clientWidth<=0||t.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));if(this._viewportInvalidAt){const t=Date.now()-this._viewportInvalidAt;if(this._viewportInvalidAt=0,t>=Ua&&!this._resumeSettling&&(this._beginResumeSettle(),this._resumeSettling))return}if(this._resumeSettling)return;const e=this._view;this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const o=ti(this._baseVb(),this._stageAspect()),n=Math.min(hl.ZOOM_MAX,Math.max(hl.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),h=o.w/n,c=o.h/n;this._zoom=n,this._view=this._clampView({x:l[0]-t/r*h,y:l[1]-e/a*c,w:h,h:c},o)}_onWheel(t){const e=this._stageEl;if(!e)return;t.preventDefault();const i=e.getBoundingClientRect(),s=t.deltaY<0?1.15:1/1.15;this._zoomAt(t.clientX-i.left,t.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(t){const e=this._stageEl;e&&(this._zoomAt(e.clientWidth/2,e.clientHeight/2,this._zoom*(t>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const t=this._baseVb();this._zoom=1,this._view=ti(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(tl,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const t=this._zoomBySpace[this._space]||1;this._zoom=t;const e=this._stageEl;if(e&&e.clientHeight){const e=this._baseVb();return this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const e=this._baseVb();this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),this.requestUpdate()})}_stagePointerDown(t){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:t.clientX,y:t.clientY,id:t.pointerId},t.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&t.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&t.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(t))return;this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:t.clientX,sy:t.clientY,vx:e.x,vy:e.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y);this._pinchStart={dist:e,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(t){if(this._physicalRotate?.pid!==t.pointerId)if(this._physicalDrag?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId){if(this._decorDraft?.pid===t.pointerId){const e=this._decorDraft;let i=this._decorSnap(this._svgPoint(t),t.pointerType);if(t.shiftKey&&("rect"===e.kind||"ellipse"===e.kind)){const t=i[0]-e.a[0],s=i[1]-e.a[1],o=Math.max(Math.abs(t),Math.abs(s));i=this._snap([e.a[0]+(t<0?-o:o),e.a[1]+(s<0?-o:o)])}return void(this._decorDraft={...e,b:i})}if(this._decorMove?.pid!==t.pointerId)if(this._pointers.has(t.pointerId)){if(this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(t),this._pinchStart&&this._pointers.size>=2){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),s=(t[0].x+t[1].x)/2-i.left,o=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,o,this._pinchStart.zoom*e),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const e=t.clientX-this._panStart.sx,i=t.clientY-this._panStart.sy;Math.abs(e)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer)),null===this._panLock&&Math.abs(e)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(e)>1.5*Math.abs(i)?"swipe":"pan");const s=this._stageEl;if("pan"===this._panLock&&s){const t=this._baseVb(),o=this._viewOr(t),n=ti(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*o.w,y:this._panStart.vy-i/(s.clientHeight||1)*o.h,w:o.w,h:o.h},n)}}}else this._markupMove(t);else this._decorMoveUpdate(t)}else this._bdMove(t);else this._dtMove(t);else this._physicalMove(t);else this._physicalRotateMove(t)}_stagePointerUp(t){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const e=this._swipeStart;if(this._swipeStart=null,e&&e.id===t.pointerId){const i=t.clientX-e.x,s=t.clientY-e.y;if(Math.abs(i)+Math.abs(s)<8){const t=Date.now();t-this._lastTap<350&&this._resetZoom(),this._lastTap=t}const o="pan"===this._panLock?null:function(t,e,i,s,o,n=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);o&&(this._slideTo(o,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid!==t.pointerId)if(this._physicalRotate?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId){if(this._decorMove?.pid===t.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0))}else this._decorCommitDraft();else this._bdUp();else this._dtUp();else this._physicalRotateUp(t);else this._physicalUp(t)}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),rl(window,"location-changed",{replace:!1}))}_pointerDown(t,e){if("plan"===this._mode)return;if("view"===this._mode)return this._holdFired=!1,clearTimeout(this._holdTimer),void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._infoCard=e},600));t.preventDefault();const i=this._pos(e);this._drag={id:e.id,sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},ll(t),this._tip=null}_pointerMove(t,e){if(!this._drag||this._drag.id!==e.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),o=i.getBoundingClientRect(),n=this._viewOr(s),r=(t.clientX-this._drag.sx)/o.width*n.w,a=(t.clientY-this._drag.sy)/o.height*n.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=_n(this._drag.ox+r),h=_n(this._drag.oy+a);this._savePos(e,l,h)}_pointerUp(t,e){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==e.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=e.id,window.setTimeout(()=>this._drag=null,0))}_showToast(t){this._toast=t,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}get _noHover(){return hl._noHoverMq||hl._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(hl._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,o){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:o})}get _gridPitch(){return cn}get _cellCm(){const t=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(t)&&t>0?t:5}_fmtLen(t,e){const i=function(t,e,i,s){return Math.hypot(e[0]-t[0],e[1]-t[1])/i*s}(t,e,this._gridPitch,this._cellCm);return Ce(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const t=this._rszPreview;return t&&t.space===this._space?t.sp:this._serverCfg?.spaces.find(t=>t.id===this._space)}get _renderCfg(){const t=this._rszPreview;return t&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(e=>e.id===t.space?t.sp:e)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,sl}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Te(t?.rooms||[]).map(t=>[t[0]*sl,t[1]*e,t[2]*sl,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(el)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(el,JSON.stringify({space:this._space,mode:this._mode}))}catch{}}_setMode(t){if(this._kiosk&&"view"!==t)return;if(this._mode===t)return;if(this._bootSoftCancel(),("plan"===t||"decor"===t)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));const e=this._mode,i="view"!==e&&"view"!==t,s=i&&this.renderRoot.querySelector(".editorchrome-inner")?.getBoundingClientRect().height||0;i||this._cancelEditorSwapAnimations(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const o=!this._spaceModel().bg&&"view"===t!=("view"===this._mode);if("view"===this._mode&&"view"!==t){const t=this._view;this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?t.x+t.w/2:void 0,cy:t?t.y+t.h/2:void 0}}if("plan"===e&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=t,this._editorChromeMode="view"===t?e:t,this._startNavMotion("view"===e?"enter":"view"===t?"exit":"swap"),i&&this._animateEditorSwap(s),o&&(this._zoom=1,this._view=null),"view"===t){const t=this._viewModeSnap;this._viewModeSnap=null,t&&t.space===this._space?(this._zoom=t.zoom,this._view=null,requestAnimationFrame(()=>{this._stageEl&&"view"===this._mode&&this._space===t.space&&(this._applyView(t.zoom,t.cx,t.cy),this._saveZoom(),this.requestUpdate())})):t&&this._restoreZoom()}this._path=[],this._cursorPt=null,this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=Xs(Ys,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?Xs(Ys,this._imperial):this._drawWallField}get _drawWallCm(){const t=Wa(this._drawWallFieldValue);if(null==t||t<=0)return null;const e=this._imperial?2.54*t:t,i="column"===this._tool?Lo:100;return e>=1&&e<=i?e:null}get _drawWallMaxCm(){return"column"===this._tool?Lo:100}_showPhysicalRange(t=this._drawWallMaxCm){this._showToast(this._t("toast.physical_range",{max:Xs(t,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_limitReached(t){const e=this._curSpaceCfg;if(!e)return!0;const i="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=2e3:(e.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this._gridPitch;return[_n(Me(t[0],e)),_n(Me(t[1],e))]}_snapDrawPoint(t,e=!1){const i=this._path[this._path.length-1],s=e&&i?function(t,e,i,s=1/0){if(!(i>0&&t?.every(Number.isFinite)&&e?.every(Number.isFinite)))return[e[0],e[1]];const o=e[0]-t[0],n=e[1]-t[1];if(Math.abs(o)+Math.abs(n)<=1e-12)return[t[0],t[1]];const r=Math.PI/4;let a=Math.atan2(n,o);a<0&&(a+=2*Math.PI);const l=Math.floor(a/r+.5)%8,[h,c]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=h*h+c*c;let p=Math.max(0,Math.round((o*h+n*c)/(i*d)));if(Number.isFinite(s)&&s>=0){const e=(t,e)=>{if(!e)return 1/0;const o=e>0?s-t:t+s;return Math.max(0,Math.floor((o+1e-9*i)/i))};p=Math.min(p,e(t[0],h),e(t[1],c))}return[t[0]+h*p*i,t[1]+c*p*i]}(i,t,this._gridPitch,ln):t;return this._snap(s)}_samePt(t,e){return Ee(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this._serverCfg?.spaces||[]){delete t.segments;const e=new Set,i=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),s=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=an);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&i(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&s(t.id)).map(t=>({...t,cm:Math.max(1,Math.min(100,Number(t.cm)||15))})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&i(t.id)&&o(t.center)&&s(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qo(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Ho(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&i(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&s(t.id)).map(t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],i=[];for(let s=1;st.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Kn(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=po(t.walls,t.rooms||[],dn,1,e),t.walls.length||delete t.walls}}}get _cfgWriting(){return this._writesPending>0}_writeConfig(){this._writesPending++,this._writeChain=this._writeChain.catch(()=>{}).then(async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const t=await this.hass.callWS({type:"houseplan/config/set",config:this._serverCfg,expected_rev:this._cfgRev});this._cfgRev=t?.rev??this._cfgRev+1});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshot(t=this._space){const e=this._serverCfg?.spaces.find(e=>e.id===t);if(!e)return null;const i=t=>JSON.parse(JSON.stringify(t)),s={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==e[t]&&(s[t]=e[t]);return{spaceId:t,rooms:i(e.rooms||[]),...Array.isArray(e.openings)?{openings:i(e.openings)}:{},...Array.isArray(e.walls)?{walls:i(e.walls)}:{},...Array.isArray(e.open_spans)?{open_spans:i(e.open_spans)}:{},...Array.isArray(e.room_drafts)?{room_drafts:i(e.room_drafts)}:{},...Array.isArray(e.partitions)?{partitions:i(e.partitions)}:{},...Array.isArray(e.wall_columns)?{wall_columns:i(e.wall_columns)}:{},...Array.isArray(e.decor)?{decor:i(e.decor)}:{},plan_transform:s}}_recordGeometry(t,e){if(!e)return;const i=this._geometrySnapshot(e.spaceId);i&&JSON.stringify(e)!==JSON.stringify(i)&&(this._geometryHistory.push({name:t,before:e,after:i}),this.requestUpdate())}_clearGeometryGesture(){this._path=[],this._cursorPt=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._openWallAnchor=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===t.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid!==t.pointerId&&this._physicalRotate?.pid!==t.pointerId)return this._decorDraft?.pid===t.pointerId?(this._decorDraft=null,void this.requestUpdate()):void(this._decorMove?.pid!==t.pointerId&&this._dtDrag?.pid!==t.pointerId&&this._bdDrag?.pid!==t.pointerId?(this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null)):this._cancelDecorGesture());this._cancelPhysicalGesture()}_applyGeometryState(t){const e=this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(!e)return!1;const i=t=>JSON.parse(JSON.stringify(t));e.rooms=i(t.rooms),void 0!==t.openings?e.openings=i(t.openings):delete e.openings,void 0!==t.walls?e.walls=i(t.walls):delete e.walls,void 0!==t.open_spans?e.open_spans=i(t.open_spans):delete e.open_spans,void 0!==t.room_drafts?e.room_drafts=i(t.room_drafts):delete e.room_drafts,void 0!==t.partitions?e.partitions=i(t.partitions):delete e.partitions,void 0!==t.wall_columns?e.wall_columns=i(t.wall_columns):delete e.wall_columns,void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];return Object.assign(e,i(t.plan_transform||{})),this._clearGeometryGesture(),this._space!==t.spaceId&&(this._space=t.spaceId,this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&qe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&Ve(t,i)})}_pointInRoom(t,e){return e.poly?Ie(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!cs(t))return!0;const e=t.length,i=.001;for(let s=0;st?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(e.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._rszDrag||e.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...this._spaceModel().rooms].reverse().find(t=>this._pointInRoom(i,t));return void(this._rszSel=t?.id||null)}if("delroom"===this._tool)return void this._deleteRoomClick(i);if("opening"===this._tool)return void this._openingClick(i);if("merge"===this._tool)return void this._mergeClick(i);if("wallthick"===this._tool)return void this._wallThickClick(i);if("openwall"===this._tool)return void this._openWallClick(i);if("closewall"===this._tool)return void this._closeWallClick(i);if("split"===this._tool)return void this._splitClick(i);if("partition"===this._tool)return void this._partitionClick(i,t.shiftKey);if("column"===this._tool)return void this._columnClick(i);const s=this._snapDrawPoint(i,t.shiftKey);if(t.ctrlKey||t.metaKey)return t.preventDefault(),void this._closeRoomContour(!0);const o=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length){const t=this._draftEndAt(s);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(t=>[...t]):t.draft.points.map(t=>[...t]),void(this._draftSegmentCms=t.reverse?[...t.draft.segments].reverse().map(t=>t.cm):t.draft.segments.map(t=>t.cm))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[s]))}const n=this._path[this._path.length-1];if(this._samePt(s,n))return;if(o)return void this._closeRoomContour();const r=this._draftEndAt(s,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(null==this._drawWallCm)return void this._showPhysicalRange(100);if(this._path.length>=500)return void this._showToast(this._t("toast.physical_limit"));const a=this._curSpaceCfg;!this._activeDraftId&&(a?.room_drafts||[]).length>=200||this._draftSegmentCount(a)>=2e3?this._showToast(this._t("toast.physical_limit")):(this._path=[...this._path,s],this._persistActiveDraftSegment())}_draftEndAt(t,e){const i=this._viewOr(this._baseVb()),s=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?i.w/this._stageEl.clientWidth*12:0);for(const i of this._spaceModel().room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const o=i.points[0],n=i.points[i.points.length-1];if(Math.hypot(t[0]-n[0],t[1]-n[1])<=s)return{draft:i,reverse:!1};if(Math.hypot(t[0]-o[0],t[1]-o[1])<=s)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this._curSpaceCfg;if(!e||!this._path.length)return;const i=Array.isArray(e.room_drafts)?e.room_drafts:[],s=this._activeDraftId?i.find(t=>t.id===this._activeDraftId):null,o=i.find(e=>e.id===t.draft.id);if(!o)return;const n=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),r=t.reverse?(o.segments||[]).map(t=>({...t})):[...o.segments||[]].reverse().map(t=>({...t})),a=this._path[this._path.length-1],l=this._samePt(a,n[0]),h=l?null:this._drawWallCm;if(!l&&null==h)return void this._showPhysicalRange(100);const c=this._draftSegmentCms.map((t,e)=>({...s?.segments?.[e]||{},cm:t})),d=[...this._path.map(t=>[...t]),...l?n.slice(1):n],p=[...c,...null==h?[]:[{cm:h}],...r],u=d.length>=4&&this._samePt(d[0],d[d.length-1]),_=u?d.slice(0,-1):d,m=u?p.slice(0,-1):p;if(_.length>500)return void this._showToast(this._t("toast.physical_limit"));const g=(s?.segments?.length||0)+(o.segments?.length||0);if(this._draftSegmentCount(e)-g+m.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(u){if(this._contourSelfIntersects(_)||Ye(_)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(_);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const f=this._geometrySnapshot(),v=this._activeDraftId||t.draft.id,y={...s||o,id:v,points:_.map(t=>[t[0]/sl,t[1]/sl]),segments:m};e.room_drafts=i.filter(e=>e.id!==t.draft.id&&(!this._activeDraftId||e.id!==this._activeDraftId)),e.room_drafts.push(y),this._activeDraftId=v,this._resumeDraftBySpace[this._space]=v,this._draftSegmentCms=m.map(t=>Number(t.cm)),this._path=_,this._physicalSel=null,this._recordGeometry(this._t("history.draft_merge"),f),this._saveConfig(),u&&(this._closingWallCm=Number(p[p.length-1]?.cm)||Ys,this._path=[..._,[..._[0]]],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0)}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const t=this._drawWallCm;if(null==t)return;this._draftSegmentCms=[...this._draftSegmentCms,t];const e=this._geometrySnapshot(),i=this._curSpaceCfg;i.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const s=i.room_drafts.findIndex(t=>t.id===this._activeDraftId),o={...s>=0?i.room_drafts[s]:{},id:this._activeDraftId,points:this._path.map(t=>[t[0]/sl,t[1]/sl]),segments:this._draftSegmentCms.map((t,e)=>({...s>=0?i.room_drafts[s]?.segments?.[e]:{},cm:t}))};s>=0?i.room_drafts[s]=o:i.room_drafts.push(o),this._recordGeometry(this._t("history.draft_segment"),e),this._saveConfig()}_partitionClick(t,e){const i=this._snapDrawPoint(t,e);if(!this._path.length)return void(this._path=[i]);const s=this._path[0];if(this._samePt(s,i))return;const o=this._drawWallCm;if(null==o)return void this._showPhysicalRange(100);if(!this._curSpaceCfg||this._limitReached("partition"))return;const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.partitions||=[];const a="partition-"+Date.now().toString(36);r.partitions.push({id:a,a:[s[0]/sl,s[1]/sl],b:[i[0]/sl,i[1]/sl],cm:o}),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._recordGeometry(this._t("history.partition_add"),n),this._saveConfig()}_columnClick(t){const e=this._snap(t),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Lo);if(!this._curSpaceCfg||this._limitReached("column"))return;const s={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qo(i),angle:0},o=(this._spaceModel().wall_columns||[]).find(t=>Xo(t,s,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...s,center:[e[0]/sl,e[1]/sl]}),this._recordGeometry(this._t("history.column_add"),n),this._saveConfig()}_openPhysicalDialog(t,e,i){const s=this._spaceModel();if("partition"===t){const i=s.partitions.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===t){const i=s.wall_columns.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),shape:i.shape,angle:String("square"===i.shape?Ho(i.angle):0)})}else{const o=s.room_drafts.find(t=>t.id===e),n=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,i||0));o?.segments[n]&&(this._physicalDialog={kind:t,id:e,segment:n,cm:Xs(o.segments[n].cm,this._imperial),length:this._fmtLen(o.points[n],o.points[n+1])})}}_physicalDown(t,e,i){t.stopPropagation(),ll(t);const s=this._spaceModel(),o=this._svgPoint(t),n=[];for(const t of[...s.wall_columns].reverse())Zo(o,jo(t,this._cellCm,this._gridPitch))&&n.push({kind:"column",id:t.id});for(const t of[...s.partitions].reverse()){const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);e&&Zo(o,e)&&n.push({kind:"partition",id:t.id})}n.some(t=>t.kind===e&&t.id===i)||n.unshift({kind:e,id:i});const r=n.map(t=>`${t.kind}:${t.id}`).sort().join("|"),a=performance.now(),l=this._physicalPickCycle,h=n.length>1&&l?.signature===r&&a-l.at>380&&a-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%n.length:Math.max(0,n.findIndex(t=>t.kind===e&&t.id===i));this._physicalPickCycle={signature:r,index:h,x:t.clientX,y:t.clientY,at:a},e=n[h].kind,i=n[h].id;const c="partition"===e?s.partitions.find(t=>t.id===i):s.wall_columns.find(t=>t.id===i);c&&(this._physicalSel={kind:e,id:i},this._physicalDrag={pid:t.pointerId,kind:e,id:i,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,i){const s="partition"===t?[e.a,e.b]:[e.center],o=s.map(t=>t[0]),n=s.map(t=>t[1]);return[Math.max(-an*sl-Math.min(...o),Math.min(an*sl-Math.max(...o),i[0])),Math.max(-an*sl-Math.min(...n),Math.min(an*sl-Math.max(...n),i[1]))]}_physicalMove(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s="partition"===e.kind?e.base.a:e.base.center,o=this._snap([s[0]+i[0]-e.start[0],s[1]+i[1]-e.start[1]]),n=this._clampPhysicalDelta(e.kind,e.base,[o[0]-s[0],o[1]-s[1]]);this._physicalDrag={...e,delta:n,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);if(!this._curSpaceCfg)return;const i=this._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;t&&(t.a=[(s.a[0]+e.delta[0])/sl,(s.a[1]+e.delta[1])/sl],t.b=[(s.b[0]+e.delta[0])/sl,(s.b[1]+e.delta[1])/sl])}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),s=e.base,o={...s,center:[s.center[0]+e.delta[0],s.center[1]+e.delta[1]]};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));t&&(t.center=[(s.center[0]+e.delta[0])/sl,(s.center[1]+e.delta[1])/sl])}this._recordGeometry(this._t("history.physical_move"),e.before),this._saveConfig()}_registerPhysicalTap(t,e,i){const s=performance.now(),o=this._physicalLastTap?.kind===t&&this._physicalLastTap.id===e&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:t,id:e,segment:i,at:s},o&&(this._physicalLastTap=null,this._openPhysicalDialog(t,e,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape)return;t.preventDefault(),t.stopPropagation(),ll(t);const i=this._svgPoint(t);this._physicalSel={kind:"column",id:e.id},this._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,baseAngle:Ho(e.angle),angle:Ho(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const i=this._svgPoint(t),s=180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,o=e.baseAngle+s-e.startAngle,n=Ho(t.shiftKey?o:5*Math.round(o/5));this._physicalRotate={...e,angle:n,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;if(t.preventDefault(),t.stopPropagation(),this._physicalRotate=null,!e.moved||!this._curSpaceCfg)return;const i=this._spaceModel().wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const s={...i,angle:e.angle};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,s,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);o&&(o.angle=e.angle,this._recordGeometry(this._t("history.physical_edit"),e.before),this._saveConfig())}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Pe(e):null;i&&t.push({id:e.id,poly:i})}return t}_rszOpenings(){return this._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen}))}_rszOpts(){return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch}}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const o=JSON.parse(i.snap),n={...s,rooms:o.rooms,openings:o.openings,walls:o.walls,open_spans:o.open_spans};Array.isArray(o.open_spans)&&o.open_spans.length||delete n.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=n.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/sl,t[1]/r]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[t,i]of Object.entries(e)){const e=(n.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/sl,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=n.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const o=s.poly.map(t=>[t[0]*sl,t[1]*r]);if(e.poly.length===o.length)for(let t=0;tt.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const o=[...s.poly[i]],n=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:o,b:n,n:ns(s.poly,i)}}(s,e,i);o&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:o,rooms:s,openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszCornerDown(t,e,i,s){"resize"!==this._tool||this._rszDrag||(t.stopPropagation(),t.preventDefault(),ll(t),this._rszDrag={kind:"scale",pid:t.pointerId,roomId:e,fixed:s,span0:Math.hypot(i[0]-s[0],i[1]-s[1])||1,rooms:this._rszRooms(),openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszMove(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t);if("edge"===e.kind){const t=e.plan,s=(i[0]-t.a[0])*t.n[0]+(i[1]-t.a[1])*t.n[1],o=this._snap([t.a[0]+t.n[0]*s,t.a[1]+t.n[1]*s]);let n=(o[0]-t.a[0])*t.n[0]+(o[1]-t.a[1])*t.n[1];if(n=function(t,e,i,s,o,n){if(!Number.isFinite(s)||Math.abs(s)<1e-9)return 0;const r=Math.sign(s);let a=Math.abs(s);const l=Math.max(o,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(gs(t,e,i,s,n))return s}return 0}(e.rooms,e.openings,t,n,this._gridPitch,this._rszOpts()),n===e.d&&e.moved)return;e.d=n,e.moved=!0;const r=us(e.rooms,e.openings,t,n,this._rszOpts().eps);e.changed=Object.keys(r.polys),this._rszApplyPreview(r.polys,r.openings),this._rszLive=this._rszEdgeLabels(r,t)}else{const t=e.fixed,s=this._snap(i);let o=Math.hypot(s[0]-t[0],s[1]-t[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,o,n){if(!Number.isFinite(o)||o<=0)return 1;if(vs(t,e,i,s,o,n))return o;let r=1,a=o;for(let o=0;o<28;o++){const o=(r+a)/2;vs(t,e,i,s,o,n)?r=o:a=o}return r}(e.rooms,e.openings,e.roomId,t,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const n=e.rooms.find(t=>t.id===e.roomId),r=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),a=fs(n,e.openings,r,t,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:a.poly},a.openings),this._rszLive=this._rszScaleLabels(a.poly)}this.requestUpdate()}_rszUp(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._rszPreview;this._rszDrag=null,this._rszLive=null,this._rszPreview=null;if(!(e.moved&&("edge"===e.kind?Math.abs(e.d)>1e-9:Math.abs(e.k-1)>1e-9))||!i)return this._cfgEpoch++,void this.requestUpdate();const s=JSON.parse(e.snap),o=this._curSpaceCfg;if(o){o.rooms=i.sp.rooms,o.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?o.walls=i.sp.walls:delete o.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?o.open_spans=i.sp.open_spans:delete o.open_spans;for(const t of e.changed){const e=o.rooms.find(e=>e.id===t);e?.poly&&(e.poly=hs(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(o.walls)&&o.walls.length&&(o.walls=po(o.walls,o.rooms||[],dn,1,this._cfgOpenCuts()),o.walls.length||delete o.walls)}this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._recordGeometry(this._t("history.resize_room"),s),this._saveConfig(),this.requestUpdate()}_rszCancelDrag(){this._rszDrag&&(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._cfgEpoch++,this.requestUpdate())}_rszPointerCancel(t){const e=this._rszDrag;e&&e.pid===t.pointerId&&(t.stopPropagation(),this._rszCancelDrag())}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],o=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,n=o.length,r=e.edge,a=(r+1)%n;for(const[t,e]of[[o[(r-1+n)%n],o[r]],[o[r],o[a]],[o[a],o[(a+1)%n]]])s.push({x:(t[0]+e[0])/2,y:(t[1]+e[1])/2,text:this._fmtLen(t,e)});const l="mi"===this.hass?.config?.unit_system?.length,h=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],c=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs),p=this._physicalBodiesR();for(const e of h){const o=t.polys[e]||i.rooms.find(t=>t.id===e).poly,n=c.length&&Ao(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,c,d,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||o,r=We(n),a=p.length?Ko(Vo(n,p))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(n,this._gridPitch,this._cellCm);s.push({x:r[0],y:r[1],text:bs(a,l),area:!0})}return s}_rszScaleLabels(t){const e="mi"===this.hass?.config?.unit_system?.length,i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.max(...i)-Math.min(...i),n=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Ao([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||t,l=We(a),h=this._physicalBodiesR(),c=h.length?Ko(Vo(a,h))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(a,this._gridPitch,this._cellCm);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[o,0])} × ${this._fmtLen([0,0],[n,0])}`},{x:l[0],y:l[1],text:bs(c,e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),o=`M ${s(-.7*i)} 0 H ${s(.7*i)} M 0 ${s(-.22*i)} V ${s(-i)} M ${s(-.32*i)} ${s(-.6*i)} L 0 ${s(-i)} L ${s(.32*i)} ${s(-.6*i)} M 0 ${s(.22*i)} V ${s(i)} M ${s(-.32*i)} ${s(.6*i)} L 0 ${s(i)} L ${s(.32*i)} ${s(.6*i)}`,n=[],r=this._rszRooms();for(const t of r)for(let i=0;i`}_stageAspect(){const t=this._stageEl,e=this._baseVb();return t&&t.clientHeight?t.clientWidth/t.clientHeight:e[2]/e[3]}_viewOr(t){return this._view&&this._view.w?this._view:ti(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),o=i?.clientWidth||1,n=i?.clientHeight||1;return[s.x+t/o*s.w,s.y+e/n*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const o=1*Math.max(e,s),n=i-o,r=i+s-e+o;return Math.max(Math.min(n,r),Math.min(Math.max(n,r),t))};return{w:t.w,h:t.h,x:i(t.x,t.w,e.x,e.w),y:i(t.y,t.h,e.y,e.h)}}_applyView(t,e,i){const s=this._baseVb(),o=ti(s,this._stageAspect()),n=Math.min(cl.ZOOM_MAX,Math.max(cl.ZOOM_MIN,t)),r=o.w/n,a=o.h/n,l=this._viewOr(s),h=e??l.x+l.w/2,c=i??l.y+l.h/2;this._zoom=n,this._view=this._clampView({x:h-r/2,y:c-a/2,w:r,h:a},o)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const t=()=>{if(!this._booting)return;const e=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=e);const s=e-this._bootStart;s>=1200||s>=700&&i>0&&e-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(t,100)};this._bootTimer=window.setTimeout(t,100)}_bootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1;const t=this._stageEl?.clientHeight??0;!this._config?.kiosk&&t>0&&this._warmPatch({hdrH:this._hdrH,stageH:t,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._kiosk||"view"!==this._mode||this._booting||this._resumeSettling||(this._resumeSettling=!0,this._resumeStarted=performance.now(),this._resumeLastSize="",this._resumeLastChange=this._resumeStarted,this._viewportInvalidAt=0,this.requestUpdate(),this._resumeRaf&&cancelAnimationFrame(this._resumeRaf),this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick()))}_resumeSettleTick(){if(this._resumeRaf=0,!this._resumeSettling||!this.isConnected)return;if(this._kiosk||"view"!==this._mode)return this._resumeSettling=!1,void this.requestUpdate();const t=performance.now(),e=this._stageEl,i=!!e&&e.clientWidth>0&&e.clientHeight>0;if(i){const i=`${e.clientWidth}x${e.clientHeight}`;i!==this._resumeLastSize&&(this._resumeLastSize=i,this._resumeLastChange=t)}const s=t-this._resumeStarted;if(i&&s>=220&&t-this._resumeLastChange>=80||s>=750){const t=this._view;return this._resumeSettling=!1,i&&this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),void this.requestUpdate()}this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick())}_refitView(){const t=this._stageEl;if(!t||"visible"!==document.visibilityState||t.clientWidth<=0||t.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));if(this._viewportInvalidAt){const t=Date.now()-this._viewportInvalidAt;if(this._viewportInvalidAt=0,t>=Wa&&!this._resumeSettling&&(this._beginResumeSettle(),this._resumeSettling))return}if(this._resumeSettling)return;const e=this._view;this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const o=ti(this._baseVb(),this._stageAspect()),n=Math.min(cl.ZOOM_MAX,Math.max(cl.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),h=o.w/n,c=o.h/n;this._zoom=n,this._view=this._clampView({x:l[0]-t/r*h,y:l[1]-e/a*c,w:h,h:c},o)}_onWheel(t){const e=this._stageEl;if(!e)return;t.preventDefault();const i=e.getBoundingClientRect(),s=t.deltaY<0?1.15:1/1.15;this._zoomAt(t.clientX-i.left,t.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(t){const e=this._stageEl;e&&(this._zoomAt(e.clientWidth/2,e.clientHeight/2,this._zoom*(t>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const t=this._baseVb();this._zoom=1,this._view=ti(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(el,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const t=this._zoomBySpace[this._space]||1;this._zoom=t;const e=this._stageEl;if(e&&e.clientHeight){const e=this._baseVb();return this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const e=this._baseVb();this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),this.requestUpdate()})}_stagePointerDown(t){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:t.clientX,y:t.clientY,id:t.pointerId},t.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&t.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&t.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(t))return;this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:t.clientX,sy:t.clientY,vx:e.x,vy:e.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y);this._pinchStart={dist:e,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(t){if(this._physicalRotate?.pid!==t.pointerId)if(this._physicalDrag?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId){if(this._decorDraft?.pid===t.pointerId){const e=this._decorDraft;let i=this._decorSnap(this._svgPoint(t),t.pointerType);if(t.shiftKey&&("rect"===e.kind||"ellipse"===e.kind)){const t=i[0]-e.a[0],s=i[1]-e.a[1],o=Math.max(Math.abs(t),Math.abs(s));i=this._snap([e.a[0]+(t<0?-o:o),e.a[1]+(s<0?-o:o)])}return void(this._decorDraft={...e,b:i})}if(this._decorMove?.pid!==t.pointerId)if(this._pointers.has(t.pointerId)){if(this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(t),this._pinchStart&&this._pointers.size>=2){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),s=(t[0].x+t[1].x)/2-i.left,o=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,o,this._pinchStart.zoom*e),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const e=t.clientX-this._panStart.sx,i=t.clientY-this._panStart.sy;Math.abs(e)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer)),null===this._panLock&&Math.abs(e)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(e)>1.5*Math.abs(i)?"swipe":"pan");const s=this._stageEl;if("pan"===this._panLock&&s){const t=this._baseVb(),o=this._viewOr(t),n=ti(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*o.w,y:this._panStart.vy-i/(s.clientHeight||1)*o.h,w:o.w,h:o.h},n)}}}else this._markupMove(t);else this._decorMoveUpdate(t)}else this._bdMove(t);else this._dtMove(t);else this._physicalMove(t);else this._physicalRotateMove(t)}_stagePointerUp(t){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const e=this._swipeStart;if(this._swipeStart=null,e&&e.id===t.pointerId){const i=t.clientX-e.x,s=t.clientY-e.y;if(Math.abs(i)+Math.abs(s)<8){const t=Date.now();t-this._lastTap<350&&this._resetZoom(),this._lastTap=t}const o="pan"===this._panLock?null:function(t,e,i,s,o,n=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);o&&(this._slideTo(o,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid!==t.pointerId)if(this._physicalRotate?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId){if(this._decorMove?.pid===t.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0))}else this._decorCommitDraft();else this._bdUp();else this._dtUp();else this._physicalRotateUp(t);else this._physicalUp(t)}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),al(window,"location-changed",{replace:!1}))}_pointerDown(t,e){if("plan"===this._mode)return;if("view"===this._mode)return this._holdFired=!1,clearTimeout(this._holdTimer),void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._infoCard=e},600));t.preventDefault();const i=this._pos(e);this._drag={id:e.id,sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},hl(t),this._tip=null}_pointerMove(t,e){if(!this._drag||this._drag.id!==e.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),o=i.getBoundingClientRect(),n=this._viewOr(s),r=(t.clientX-this._drag.sx)/o.width*n.w,a=(t.clientY-this._drag.sy)/o.height*n.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=_n(this._drag.ox+r),h=_n(this._drag.oy+a);this._savePos(e,l,h)}_pointerUp(t,e){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==e.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=e.id,window.setTimeout(()=>this._drag=null,0))}_showToast(t){this._toast=t,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}get _noHover(){return cl._noHoverMq||cl._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(cl._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,o){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:o})}get _gridPitch(){return cn}get _cellCm(){const t=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(t)&&t>0?t:5}_fmtLen(t,e){const i=function(t,e,i,s){return Math.hypot(e[0]-t[0],e[1]-t[1])/i*s}(t,e,this._gridPitch,this._cellCm);return Ce(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const t=this._rszPreview;return t&&t.space===this._space?t.sp:this._serverCfg?.spaces.find(t=>t.id===this._space)}get _renderCfg(){const t=this._rszPreview;return t&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(e=>e.id===t.space?t.sp:e)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,ol}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Te(t?.rooms||[]).map(t=>[t[0]*ol,t[1]*e,t[2]*ol,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(il)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(il,JSON.stringify({space:this._space,mode:this._mode}))}catch{}}_setMode(t){if(this._kiosk&&"view"!==t)return;if(this._mode===t)return;if(this._bootSoftCancel(),("plan"===t||"decor"===t)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));const e=this._mode,i="view"!==e&&"view"!==t,s=i&&this.renderRoot.querySelector(".editorchrome-inner")?.getBoundingClientRect().height||0;i||this._cancelEditorSwapAnimations(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const o=!this._spaceModel().bg&&"view"===t!=("view"===this._mode);if("view"===this._mode&&"view"!==t){const t=this._view;this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?t.x+t.w/2:void 0,cy:t?t.y+t.h/2:void 0}}if("plan"===e&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=t,this._editorChromeMode="view"===t?e:t,this._startNavMotion("view"===e?"enter":"view"===t?"exit":"swap"),i&&this._animateEditorSwap(s),o&&(this._zoom=1,this._view=null),"view"===t){const t=this._viewModeSnap;this._viewModeSnap=null,t&&t.space===this._space?(this._zoom=t.zoom,this._view=null,requestAnimationFrame(()=>{this._stageEl&&"view"===this._mode&&this._space===t.space&&(this._applyView(t.zoom,t.cx,t.cy),this._saveZoom(),this.requestUpdate())})):t&&this._restoreZoom()}this._path=[],this._cursorPt=null,this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=Xs(Ys,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?Xs(Ys,this._imperial):this._drawWallField}get _drawWallCm(){const t=Ba(this._drawWallFieldValue);if(null==t||t<=0)return null;const e=this._imperial?2.54*t:t,i="column"===this._tool?Lo:100;return e>=1&&e<=i?e:null}get _drawWallMaxCm(){return"column"===this._tool?Lo:100}_showPhysicalRange(t=this._drawWallMaxCm){this._showToast(this._t("toast.physical_range",{max:Xs(t,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_limitReached(t){const e=this._curSpaceCfg;if(!e)return!0;const i="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=2e3:(e.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this._gridPitch;return[_n(Me(t[0],e)),_n(Me(t[1],e))]}_snapDrawPoint(t,e=!1){const i=this._path[this._path.length-1],s=e&&i?function(t,e,i,s=1/0){if(!(i>0&&t?.every(Number.isFinite)&&e?.every(Number.isFinite)))return[e[0],e[1]];const o=e[0]-t[0],n=e[1]-t[1];if(Math.abs(o)+Math.abs(n)<=1e-12)return[t[0],t[1]];const r=Math.PI/4;let a=Math.atan2(n,o);a<0&&(a+=2*Math.PI);const l=Math.floor(a/r+.5)%8,[h,c]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=h*h+c*c;let p=Math.max(0,Math.round((o*h+n*c)/(i*d)));if(Number.isFinite(s)&&s>=0){const e=(t,e)=>{if(!e)return 1/0;const o=e>0?s-t:t+s;return Math.max(0,Math.floor((o+1e-9*i)/i))};p=Math.min(p,e(t[0],h),e(t[1],c))}return[t[0]+h*p*i,t[1]+c*p*i]}(i,t,this._gridPitch,ln):t;return this._snap(s)}_samePt(t,e){return Ee(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this._serverCfg?.spaces||[]){delete t.segments;const e=new Set,i=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),s=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=an);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&i(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&s(t.id)).map(t=>({...t,cm:Math.max(1,Math.min(100,Number(t.cm)||15))})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&i(t.id)&&o(t.center)&&s(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qo(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Ho(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&i(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&s(t.id)).map(t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],i=[];for(let s=1;st.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Kn(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=po(t.walls,t.rooms||[],dn,1,e),t.walls.length||delete t.walls}}}get _cfgWriting(){return this._writesPending>0}_writeConfig(){this._writesPending++,this._writeChain=this._writeChain.catch(()=>{}).then(async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const t=await this.hass.callWS({type:"houseplan/config/set",config:this._serverCfg,expected_rev:this._cfgRev});this._cfgRev=t?.rev??this._cfgRev+1});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshot(t=this._space){const e=this._serverCfg?.spaces.find(e=>e.id===t);if(!e)return null;const i=t=>JSON.parse(JSON.stringify(t)),s={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==e[t]&&(s[t]=e[t]);return{spaceId:t,rooms:i(e.rooms||[]),...Array.isArray(e.openings)?{openings:i(e.openings)}:{},...Array.isArray(e.walls)?{walls:i(e.walls)}:{},...Array.isArray(e.open_spans)?{open_spans:i(e.open_spans)}:{},...Array.isArray(e.room_drafts)?{room_drafts:i(e.room_drafts)}:{},...Array.isArray(e.partitions)?{partitions:i(e.partitions)}:{},...Array.isArray(e.wall_columns)?{wall_columns:i(e.wall_columns)}:{},...Array.isArray(e.decor)?{decor:i(e.decor)}:{},plan_transform:s}}_recordGeometry(t,e){if(!e)return;const i=this._geometrySnapshot(e.spaceId);i&&JSON.stringify(e)!==JSON.stringify(i)&&(this._geometryHistory.push({name:t,before:e,after:i}),this.requestUpdate())}_clearGeometryGesture(){this._path=[],this._cursorPt=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._openWallAnchor=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===t.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid!==t.pointerId&&this._physicalRotate?.pid!==t.pointerId)return this._decorDraft?.pid===t.pointerId?(this._decorDraft=null,void this.requestUpdate()):void(this._decorMove?.pid!==t.pointerId&&this._dtDrag?.pid!==t.pointerId&&this._bdDrag?.pid!==t.pointerId?(this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null)):this._cancelDecorGesture());this._cancelPhysicalGesture()}_applyGeometryState(t){const e=this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(!e)return!1;const i=t=>JSON.parse(JSON.stringify(t));e.rooms=i(t.rooms),void 0!==t.openings?e.openings=i(t.openings):delete e.openings,void 0!==t.walls?e.walls=i(t.walls):delete e.walls,void 0!==t.open_spans?e.open_spans=i(t.open_spans):delete e.open_spans,void 0!==t.room_drafts?e.room_drafts=i(t.room_drafts):delete e.room_drafts,void 0!==t.partitions?e.partitions=i(t.partitions):delete e.partitions,void 0!==t.wall_columns?e.wall_columns=i(t.wall_columns):delete e.wall_columns,void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];return Object.assign(e,i(t.plan_transform||{})),this._clearGeometryGesture(),this._space!==t.spaceId&&(this._space=t.spaceId,this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&qe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&Ve(t,i)})}_pointInRoom(t,e){return e.poly?Ie(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!cs(t))return!0;const e=t.length,i=.001;for(let s=0;st?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(e.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._rszDrag||e.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...this._spaceModel().rooms].reverse().find(t=>this._pointInRoom(i,t));return void(this._rszSel=t?.id||null)}if("delroom"===this._tool)return void this._deleteRoomClick(i);if("opening"===this._tool)return void this._openingClick(i);if("merge"===this._tool)return void this._mergeClick(i);if("wallthick"===this._tool)return void this._wallThickClick(i);if("openwall"===this._tool)return void this._openWallClick(i);if("closewall"===this._tool)return void this._closeWallClick(i);if("split"===this._tool)return void this._splitClick(i);if("partition"===this._tool)return void this._partitionClick(i,t.shiftKey);if("column"===this._tool)return void this._columnClick(i);const s=this._snapDrawPoint(i,t.shiftKey);if(t.ctrlKey||t.metaKey)return t.preventDefault(),void this._closeRoomContour(!0);const o=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length){const t=this._draftEndAt(s);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(t=>[...t]):t.draft.points.map(t=>[...t]),void(this._draftSegmentCms=t.reverse?[...t.draft.segments].reverse().map(t=>t.cm):t.draft.segments.map(t=>t.cm))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[s]))}const n=this._path[this._path.length-1];if(this._samePt(s,n))return;if(o)return void this._closeRoomContour();const r=this._draftEndAt(s,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(null==this._drawWallCm)return void this._showPhysicalRange(100);if(this._path.length>=500)return void this._showToast(this._t("toast.physical_limit"));const a=this._curSpaceCfg;!this._activeDraftId&&(a?.room_drafts||[]).length>=200||this._draftSegmentCount(a)>=2e3?this._showToast(this._t("toast.physical_limit")):(this._path=[...this._path,s],this._persistActiveDraftSegment())}_draftEndAt(t,e){const i=this._viewOr(this._baseVb()),s=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?i.w/this._stageEl.clientWidth*12:0);for(const i of this._spaceModel().room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const o=i.points[0],n=i.points[i.points.length-1];if(Math.hypot(t[0]-n[0],t[1]-n[1])<=s)return{draft:i,reverse:!1};if(Math.hypot(t[0]-o[0],t[1]-o[1])<=s)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this._curSpaceCfg;if(!e||!this._path.length)return;const i=Array.isArray(e.room_drafts)?e.room_drafts:[],s=this._activeDraftId?i.find(t=>t.id===this._activeDraftId):null,o=i.find(e=>e.id===t.draft.id);if(!o)return;const n=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),r=t.reverse?(o.segments||[]).map(t=>({...t})):[...o.segments||[]].reverse().map(t=>({...t})),a=this._path[this._path.length-1],l=this._samePt(a,n[0]),h=l?null:this._drawWallCm;if(!l&&null==h)return void this._showPhysicalRange(100);const c=this._draftSegmentCms.map((t,e)=>({...s?.segments?.[e]||{},cm:t})),d=[...this._path.map(t=>[...t]),...l?n.slice(1):n],p=[...c,...null==h?[]:[{cm:h}],...r],u=d.length>=4&&this._samePt(d[0],d[d.length-1]),_=u?d.slice(0,-1):d,m=u?p.slice(0,-1):p;if(_.length>500)return void this._showToast(this._t("toast.physical_limit"));const g=(s?.segments?.length||0)+(o.segments?.length||0);if(this._draftSegmentCount(e)-g+m.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(u){if(this._contourSelfIntersects(_)||Ye(_)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(_);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const f=this._geometrySnapshot(),v=this._activeDraftId||t.draft.id,y={...s||o,id:v,points:_.map(t=>[t[0]/ol,t[1]/ol]),segments:m};e.room_drafts=i.filter(e=>e.id!==t.draft.id&&(!this._activeDraftId||e.id!==this._activeDraftId)),e.room_drafts.push(y),this._activeDraftId=v,this._resumeDraftBySpace[this._space]=v,this._draftSegmentCms=m.map(t=>Number(t.cm)),this._path=_,this._physicalSel=null,this._recordGeometry(this._t("history.draft_merge"),f),this._saveConfig(),u&&(this._closingWallCm=Number(p[p.length-1]?.cm)||Ys,this._path=[..._,[..._[0]]],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0)}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const t=this._drawWallCm;if(null==t)return;this._draftSegmentCms=[...this._draftSegmentCms,t];const e=this._geometrySnapshot(),i=this._curSpaceCfg;i.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const s=i.room_drafts.findIndex(t=>t.id===this._activeDraftId),o={...s>=0?i.room_drafts[s]:{},id:this._activeDraftId,points:this._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentCms.map((t,e)=>({...s>=0?i.room_drafts[s]?.segments?.[e]:{},cm:t}))};s>=0?i.room_drafts[s]=o:i.room_drafts.push(o),this._recordGeometry(this._t("history.draft_segment"),e),this._saveConfig()}_partitionClick(t,e){const i=this._snapDrawPoint(t,e);if(!this._path.length)return void(this._path=[i]);const s=this._path[0];if(this._samePt(s,i))return;const o=this._drawWallCm;if(null==o)return void this._showPhysicalRange(100);if(!this._curSpaceCfg||this._limitReached("partition"))return;const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.partitions||=[];const a="partition-"+Date.now().toString(36);r.partitions.push({id:a,a:[s[0]/ol,s[1]/ol],b:[i[0]/ol,i[1]/ol],cm:o}),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._recordGeometry(this._t("history.partition_add"),n),this._saveConfig()}_columnClick(t){const e=this._snap(t),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Lo);if(!this._curSpaceCfg||this._limitReached("column"))return;const s={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qo(i),angle:0},o=(this._spaceModel().wall_columns||[]).find(t=>Xo(t,s,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...s,center:[e[0]/ol,e[1]/ol]}),this._recordGeometry(this._t("history.column_add"),n),this._saveConfig()}_openPhysicalDialog(t,e,i){const s=this._spaceModel();if("partition"===t){const i=s.partitions.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===t){const i=s.wall_columns.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),shape:i.shape,angle:String("square"===i.shape?Ho(i.angle):0)})}else{const o=s.room_drafts.find(t=>t.id===e),n=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,i||0));o?.segments[n]&&(this._physicalDialog={kind:t,id:e,segment:n,cm:Xs(o.segments[n].cm,this._imperial),length:this._fmtLen(o.points[n],o.points[n+1])})}}_physicalDown(t,e,i){t.stopPropagation(),hl(t);const s=this._spaceModel(),o=this._svgPoint(t),n=[];for(const t of[...s.wall_columns].reverse())Zo(o,jo(t,this._cellCm,this._gridPitch))&&n.push({kind:"column",id:t.id});for(const t of[...s.partitions].reverse()){const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);e&&Zo(o,e)&&n.push({kind:"partition",id:t.id})}n.some(t=>t.kind===e&&t.id===i)||n.unshift({kind:e,id:i});const r=n.map(t=>`${t.kind}:${t.id}`).sort().join("|"),a=performance.now(),l=this._physicalPickCycle,h=n.length>1&&l?.signature===r&&a-l.at>380&&a-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%n.length:Math.max(0,n.findIndex(t=>t.kind===e&&t.id===i));this._physicalPickCycle={signature:r,index:h,x:t.clientX,y:t.clientY,at:a},e=n[h].kind,i=n[h].id;const c="partition"===e?s.partitions.find(t=>t.id===i):s.wall_columns.find(t=>t.id===i);c&&(this._physicalSel={kind:e,id:i},this._physicalDrag={pid:t.pointerId,kind:e,id:i,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,i){const s="partition"===t?[e.a,e.b]:[e.center],o=s.map(t=>t[0]),n=s.map(t=>t[1]);return[Math.max(-an*ol-Math.min(...o),Math.min(an*ol-Math.max(...o),i[0])),Math.max(-an*ol-Math.min(...n),Math.min(an*ol-Math.max(...n),i[1]))]}_physicalMove(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s="partition"===e.kind?e.base.a:e.base.center,o=this._snap([s[0]+i[0]-e.start[0],s[1]+i[1]-e.start[1]]),n=this._clampPhysicalDelta(e.kind,e.base,[o[0]-s[0],o[1]-s[1]]);this._physicalDrag={...e,delta:n,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);if(!this._curSpaceCfg)return;const i=this._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;t&&(t.a=[(s.a[0]+e.delta[0])/ol,(s.a[1]+e.delta[1])/ol],t.b=[(s.b[0]+e.delta[0])/ol,(s.b[1]+e.delta[1])/ol])}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),s=e.base,o={...s,center:[s.center[0]+e.delta[0],s.center[1]+e.delta[1]]};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));t&&(t.center=[(s.center[0]+e.delta[0])/ol,(s.center[1]+e.delta[1])/ol])}this._recordGeometry(this._t("history.physical_move"),e.before),this._saveConfig()}_registerPhysicalTap(t,e,i){const s=performance.now(),o=this._physicalLastTap?.kind===t&&this._physicalLastTap.id===e&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:t,id:e,segment:i,at:s},o&&(this._physicalLastTap=null,this._openPhysicalDialog(t,e,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape)return;t.preventDefault(),t.stopPropagation(),hl(t);const i=this._svgPoint(t);this._physicalSel={kind:"column",id:e.id},this._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,baseAngle:Ho(e.angle),angle:Ho(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const i=this._svgPoint(t),s=180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,o=e.baseAngle+s-e.startAngle,n=Ho(t.shiftKey?o:5*Math.round(o/5));this._physicalRotate={...e,angle:n,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;if(t.preventDefault(),t.stopPropagation(),this._physicalRotate=null,!e.moved||!this._curSpaceCfg)return;const i=this._spaceModel().wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const s={...i,angle:e.angle};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,s,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);o&&(o.angle=e.angle,this._recordGeometry(this._t("history.physical_edit"),e.before),this._saveConfig())}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Pe(e):null;i&&t.push({id:e.id,poly:i})}return t}_rszOpenings(){return this._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen}))}_rszOpts(){return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch}}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const o=JSON.parse(i.snap),n={...s,rooms:o.rooms,openings:o.openings,walls:o.walls,open_spans:o.open_spans};Array.isArray(o.open_spans)&&o.open_spans.length||delete n.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=n.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/ol,t[1]/r]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[t,i]of Object.entries(e)){const e=(n.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/ol,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=n.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const o=s.poly.map(t=>[t[0]*ol,t[1]*r]);if(e.poly.length===o.length)for(let t=0;tt.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const o=[...s.poly[i]],n=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:o,b:n,n:ns(s.poly,i)}}(s,e,i);o&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:o,rooms:s,openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszCornerDown(t,e,i,s){"resize"!==this._tool||this._rszDrag||(t.stopPropagation(),t.preventDefault(),hl(t),this._rszDrag={kind:"scale",pid:t.pointerId,roomId:e,fixed:s,span0:Math.hypot(i[0]-s[0],i[1]-s[1])||1,rooms:this._rszRooms(),openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszMove(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t);if("edge"===e.kind){const t=e.plan,s=(i[0]-t.a[0])*t.n[0]+(i[1]-t.a[1])*t.n[1],o=this._snap([t.a[0]+t.n[0]*s,t.a[1]+t.n[1]*s]);let n=(o[0]-t.a[0])*t.n[0]+(o[1]-t.a[1])*t.n[1];if(n=function(t,e,i,s,o,n){if(!Number.isFinite(s)||Math.abs(s)<1e-9)return 0;const r=Math.sign(s);let a=Math.abs(s);const l=Math.max(o,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(gs(t,e,i,s,n))return s}return 0}(e.rooms,e.openings,t,n,this._gridPitch,this._rszOpts()),n===e.d&&e.moved)return;e.d=n,e.moved=!0;const r=us(e.rooms,e.openings,t,n,this._rszOpts().eps);e.changed=Object.keys(r.polys),this._rszApplyPreview(r.polys,r.openings),this._rszLive=this._rszEdgeLabels(r,t)}else{const t=e.fixed,s=this._snap(i);let o=Math.hypot(s[0]-t[0],s[1]-t[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,o,n){if(!Number.isFinite(o)||o<=0)return 1;if(vs(t,e,i,s,o,n))return o;let r=1,a=o;for(let o=0;o<28;o++){const o=(r+a)/2;vs(t,e,i,s,o,n)?r=o:a=o}return r}(e.rooms,e.openings,e.roomId,t,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const n=e.rooms.find(t=>t.id===e.roomId),r=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),a=fs(n,e.openings,r,t,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:a.poly},a.openings),this._rszLive=this._rszScaleLabels(a.poly)}this.requestUpdate()}_rszUp(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._rszPreview;this._rszDrag=null,this._rszLive=null,this._rszPreview=null;if(!(e.moved&&("edge"===e.kind?Math.abs(e.d)>1e-9:Math.abs(e.k-1)>1e-9))||!i)return this._cfgEpoch++,void this.requestUpdate();const s=JSON.parse(e.snap),o=this._curSpaceCfg;if(o){o.rooms=i.sp.rooms,o.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?o.walls=i.sp.walls:delete o.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?o.open_spans=i.sp.open_spans:delete o.open_spans;for(const t of e.changed){const e=o.rooms.find(e=>e.id===t);e?.poly&&(e.poly=hs(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(o.walls)&&o.walls.length&&(o.walls=po(o.walls,o.rooms||[],dn,1,this._cfgOpenCuts()),o.walls.length||delete o.walls)}this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._recordGeometry(this._t("history.resize_room"),s),this._saveConfig(),this.requestUpdate()}_rszCancelDrag(){this._rszDrag&&(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._cfgEpoch++,this.requestUpdate())}_rszPointerCancel(t){const e=this._rszDrag;e&&e.pid===t.pointerId&&(t.stopPropagation(),this._rszCancelDrag())}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],o=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,n=o.length,r=e.edge,a=(r+1)%n;for(const[t,e]of[[o[(r-1+n)%n],o[r]],[o[r],o[a]],[o[a],o[(a+1)%n]]])s.push({x:(t[0]+e[0])/2,y:(t[1]+e[1])/2,text:this._fmtLen(t,e)});const l="mi"===this.hass?.config?.unit_system?.length,h=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],c=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs),p=this._physicalBodiesR();for(const e of h){const o=t.polys[e]||i.rooms.find(t=>t.id===e).poly,n=c.length&&Ao(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,c,d,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||o,r=We(n),a=p.length?Ko(Vo(n,p))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(n,this._gridPitch,this._cellCm);s.push({x:r[0],y:r[1],text:bs(a,l),area:!0})}return s}_rszScaleLabels(t){const e="mi"===this.hass?.config?.unit_system?.length,i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.max(...i)-Math.min(...i),n=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Ao([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||t,l=We(a),h=this._physicalBodiesR(),c=h.length?Ko(Vo(a,h))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(a,this._gridPitch,this._cellCm);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[o,0])} × ${this._fmtLen([0,0],[n,0])}`},{x:l[0],y:l[1],text:bs(c,e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),o=`M ${s(-.7*i)} 0 H ${s(.7*i)} M 0 ${s(-.22*i)} V ${s(-i)} M ${s(-.32*i)} ${s(-.6*i)} L 0 ${s(-i)} L ${s(.32*i)} ${s(-.6*i)} M 0 ${s(.22*i)} V ${s(i)} M ${s(-.32*i)} ${s(.6*i)} L 0 ${s(i)} L ${s(.32*i)} ${s(.6*i)}`,n=[],r=this._rszRooms();for(const t of r)for(let i=0;ithis._rszEdgeDown(e,t.id,i)} @pointermove=${t=>this._rszMove(t)} @pointerup=${t=>this._rszUp(t)} @@ -3108,7 +3127,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointermove=${t=>this._rszMove(t)} @pointerup=${t=>this._rszUp(t)} @pointercancel=${t=>this._rszPointerCancel(t)} - @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),n.push(B``)}return B`${n}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*sl,ry:t.y*e,rlen:t.length*sl}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return sl}_decorResolvedStyle(t){return function(t,e,i,s=Os){const o="rect"===t?.kind||"ellipse"===t?.kind,n=t,r=/^#[0-9a-f]{6}$/i.test(String(n?.color||""))?String(n.color):s.color;return{color:r,opacity:Fs(n?.opacity,s.opacity),widthCm:Ws(t,e,i,s.widthCm),fill:!!o&&!0===n?.fill,fillColor:/^#[0-9a-f]{6}$/i.test(String(n?.fill_color||""))?String(n.fill_color):n?.fill?r:s.fillColor,fillOpacity:o&&n?.fill?Fs(n?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Os)}_decorWidthUnits(t){return((t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return Hs(o,e,i);const n=Number(t?.width);return Number.isFinite(n)&&n>0?n:Hs(s,e,i)})(t,this._cellCm,this._gridPitch,Os.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Us(yi*bi(t),this._cellCm,this._gridPitch)}return Us(yi,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return yi;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Hs(e,this._cellCm,this._gridPitch):yi*bi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(La,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(an*this._cellCm,e)):.1}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*sl,y:t.y*this._decorH,w:t.w*sl,h:t.h*this._decorH,angle:Ls(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*sl,e.y1*this._decorH],i=[e.x2*sl,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*sl,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Gs(t))}for(const t of this._spaceModel().rooms){const e=Pe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,o=this._viewOr(this._baseVb()),n="touch"===e||"pen"===e?14:8,r=s?o.w/Math.max(1,s.clientWidth)*n:this._gridPitch;return function(t,e,i,s){const o=s(t);let n=null;for(const o of e.points){const e=s(o),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!n||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},ll(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:mn(e[0]/sl),y:mn(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:0,sizeCm:Us(yi,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,o=e.y+e.h/2,n=-Ls(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-o,l=[s+r*Math.cos(n)-a*Math.sin(n),o+r*Math.sin(n)+a*Math.cos(n)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const o=Math.abs(i[0]-e[0]),n=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(o,n)>=s:o>=s&&n>=s}(t.kind,t.a,t.b,e))return;const i=sl,s=this._decorH,o=this._decorStyle,n=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=mn;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Bs(o,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),n=a(Math.min(t.a[1],t.b[1])/s),h=Math.abs(t.b[0]-t.a[0])/i,c=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:n,w:h,h:c,...Bs(o,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),n),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},ll(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,o=("line"===s.kind?s.x1:s.x)*sl,n=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([o+(i[0]-e.start[0]),n+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-o)/sl,l=(r[1]-n)/this._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=an;a=Math.max(-_-c,Math.min(_-c-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){if("decor"!==this._mode||"select"!==this._decorTool)return;if(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,"text"===e.kind)return void this._decorOpenText(e);if(!["line","rect","ellipse","furniture"].includes(e.kind))return;const i=this._decorResolvedStyle(e),s="line"===e.kind?e:null,o=this._decorBoxOf(e);this._decorShapeDialog={id:e.id,kind:e.kind,color:i.color,opacity:i.opacity,widthCm:i.widthCm,angle:Ls(s?Yi([s.x1*sl,s.y1*this._decorH],[s.x2*sl,s.y2*this._decorH]):e.angle),...s?{lengthCm:Us(Math.hypot((s.x2-s.x1)*sl,(s.y2-s.y1)*this._decorH),this._cellCm,this._gridPitch)}:{},...o?{sizeWCm:Us(o.w,this._cellCm,this._gridPitch),sizeHCm:Us(o.h,this._cellCm,this._gridPitch)}:{},..."furniture"===e.kind?{symbol:e.symbol}:{},..."rect"===e.kind||"ellipse"===e.kind?{fill:i.fill,fillColor:i.fillColor,fillOpacity:i.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1])),s=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||s?"":mi(t.entity,o),r=!(!String(t.entity??"").trim()||i||n&&!s);if(n&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+n+e.slice(t+2):`${e}${e?" ":""}${n}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:t.color||this._decorStyle.color,opacity:Fs(t.opacity,this._decorStyle.opacity),angle:Ls(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=mi(e.pickerEntity,t);if(!i)return;const s=e.text,o=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),n=Math.max(o,Math.min(s.length,this._decorTextSelection.end));if(s.length-(n-o)+i.length>200)return;const r=s.slice(0,o)+i+s.slice(n),a=o+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,o={color:t.color,opacity:Fs(t.opacity),size_cm:Number(Math.max(.1,Math.min(La,t.sizeCm)).toFixed(4)),...Ls(t.angle)?{angle:Ls(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:n,...r}=i;return{...r,text:e,...o}}const{entity:s,attr:n,unit:r,...a}=i,{angle:l,size:h,scale:c,...d}=a;return{...d,text:e,...o}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...o}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:Fs(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Fs(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,o=Bs(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*sl,s=(e.y1+e.y2)/2*this._decorH,n=Hs(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Ls(t.angle)*Math.PI/180,a=Math.cos(r)*n/2,l=Math.sin(r)*n/2,h=this._snap([i-a,s-l]),c=this._snap([i+a,s+l]),{width:d,...p}=e;return{...p,...o,x1:mn(h[0]/sl),y1:mn(h[1]/this._decorH),x2:mn(c[0]/sl),y2:mn(c[1]/this._decorH)}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*sl,s=e.h*this._decorH,n=Math.max(this._gridPitch,Me(Hs(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Me(Hs(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*sl+i/2,l=e.y*this._decorH+s/2,h=Ls(t.angle),c=qs({x:a-n/2,y:l-r/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...o,x:mn(c[0]/sl),y:mn(c[1]/this._decorH),w:n/sl,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...h?{angle:h}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*sl,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*sl,(t.y+t.h/2)*this._decorH]:[t.x*sl,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const o={...s};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(La,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const o=this._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[n,r]=this._dtPivot(o),a=this._svgPoint(t),l=this._decorBoxOf(o);this._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:n,ay:r,r0:Math.hypot(a[0]-n,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-n)/Math.PI,textSizeCm0:"text"===o.kind?this._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},ll(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),o=mn(s[0]/sl),n=mn(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(o-a)>1e-9||Math.abs(n-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:n}:{x2:o,y2:n})}if("scale"===e.kind&&e.orig){const s=Vs(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),o=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(La,e.textSizeCm0*(t/e.r0))),o=Math.abs(s-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let o=e.angle0+(s-e.a0);t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360);const n=Math.abs(o-e.angle0)>1e-6;(n||e.moved)&&(e.moved||=n,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*sl,s=t.y1*this._decorH,o=t.x2*sl,n=t.y2*this._decorH;e={id:t.id,x:Math.min(i,o),y:Math.min(s,n),w:Math.abs(o-i),h:Math.abs(n-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*sl,y:t.y*this._decorH,w:t.w*sl,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=En(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const o=sl,n=this._decorH,r=qn(On(i.w,this._cellCm,this._gridPitch,o)),a=qn(On(i.h,this._cellCm,this._gridPitch,o)),l=this._geometrySnapshot(),h=this._decorSnap(t);let c=h[0],d=h[1],p=0;const u=e?null:Bn(c,d,a*n,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(c=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:mn(c/o-r/2),y:mn(d/n-a/2),w:r,h:a,...Bs(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const o=sl,n=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*o+(r[0]-e.start[0]),l=(i.y+i.h/2)*n+(r[1]-e.start[1]);let h,c,d=Number(i.angle)||0;const p=t.shiftKey?null:Bn(a,l,i.h*n,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)h=p.cx/o-i.w/2,c=p.cy/n-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*o,l-i.h/2*n],t.pointerType,e.id);h=s[0]/o,c=s[1]/n}h=mn(h),c=mn(c),(Math.abs(h-i.x)>1e-9||Math.abs(c-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:h,y:c};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=sl,o=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const n=qs(e,i.angle,t=>this._snap(t));return{...i,x:mn(n[0]/s),y:mn(n[1]/o),w:Math.max(this._gridPitch/s,Math.min(2*an,e.w/s)),h:Math.max(this._gridPitch/o,Math.min(2*an,e.h/o))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=sl,s=this._decorH,o=e.w*i,n=e.h*s,r=function(t,e,i,s,o){const n=t+i/2,r=e+s/2,a=(Number(o)||0)*Math.PI/180,l=Math.cos(a),h=Math.sin(a),c=(t,e)=>{const i=t-n,s=e-r;return[n+i*l-s*h,r+i*h+s*l]};return[c(t,e),c(t+i,e),c(t+i,e+s),c(t,e+s)]}(e.x*i,e.y*s,o,n,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),h=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[o,0])},{x:h[0],y:h[1],text:this._fmtLen([0,0],[0,n])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}> + @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),n.push(B``)}return B`${n}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*ol,ry:t.y*e,rlen:t.length*ol}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return ol}_decorResolvedStyle(t){return function(t,e,i,s=Os){const o="rect"===t?.kind||"ellipse"===t?.kind,n=t,r=/^#[0-9a-f]{6}$/i.test(String(n?.color||""))?String(n.color):s.color;return{color:r,opacity:Fs(n?.opacity,s.opacity),widthCm:Ws(t,e,i,s.widthCm),fill:!!o&&!0===n?.fill,fillColor:/^#[0-9a-f]{6}$/i.test(String(n?.fill_color||""))?String(n.fill_color):n?.fill?r:s.fillColor,fillOpacity:o&&n?.fill?Fs(n?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Os)}_decorWidthUnits(t){return((t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return Hs(o,e,i);const n=Number(t?.width);return Number.isFinite(n)&&n>0?n:Hs(s,e,i)})(t,this._cellCm,this._gridPitch,Os.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Us(yi*bi(t),this._cellCm,this._gridPitch)}return Us(yi,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return yi;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Hs(e,this._cellCm,this._gridPitch):yi*bi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(qa,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(an*this._cellCm,e)):.1}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*ol,y:t.y*this._decorH,w:t.w*ol,h:t.h*this._decorH,angle:Ls(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*ol,e.y1*this._decorH],i=[e.x2*ol,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*ol,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Gs(t))}for(const t of this._spaceModel().rooms){const e=Pe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,o=this._viewOr(this._baseVb()),n="touch"===e||"pen"===e?14:8,r=s?o.w/Math.max(1,s.clientWidth)*n:this._gridPitch;return function(t,e,i,s){const o=s(t);let n=null;for(const o of e.points){const e=s(o),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!n||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},hl(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:mn(e[0]/ol),y:mn(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:0,sizeCm:Us(yi,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,o=e.y+e.h/2,n=-Ls(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-o,l=[s+r*Math.cos(n)-a*Math.sin(n),o+r*Math.sin(n)+a*Math.cos(n)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const o=Math.abs(i[0]-e[0]),n=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(o,n)>=s:o>=s&&n>=s}(t.kind,t.a,t.b,e))return;const i=ol,s=this._decorH,o=this._decorStyle,n=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=mn;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Bs(o,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),n=a(Math.min(t.a[1],t.b[1])/s),h=Math.abs(t.b[0]-t.a[0])/i,c=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:n,w:h,h:c,...Bs(o,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),n),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},hl(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,o=("line"===s.kind?s.x1:s.x)*ol,n=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([o+(i[0]-e.start[0]),n+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-o)/ol,l=(r[1]-n)/this._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=an;a=Math.max(-_-c,Math.min(_-c-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){if("decor"!==this._mode||"select"!==this._decorTool)return;if(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,"text"===e.kind)return void this._decorOpenText(e);if(!["line","rect","ellipse","furniture"].includes(e.kind))return;const i=this._decorResolvedStyle(e),s="line"===e.kind?e:null,o=this._decorBoxOf(e);this._decorShapeDialog={id:e.id,kind:e.kind,color:i.color,opacity:i.opacity,widthCm:i.widthCm,angle:Ls(s?Yi([s.x1*ol,s.y1*this._decorH],[s.x2*ol,s.y2*this._decorH]):e.angle),...s?{lengthCm:Us(Math.hypot((s.x2-s.x1)*ol,(s.y2-s.y1)*this._decorH),this._cellCm,this._gridPitch)}:{},...o?{sizeWCm:Us(o.w,this._cellCm,this._gridPitch),sizeHCm:Us(o.h,this._cellCm,this._gridPitch)}:{},..."furniture"===e.kind?{symbol:e.symbol}:{},..."rect"===e.kind||"ellipse"===e.kind?{fill:i.fill,fillColor:i.fillColor,fillOpacity:i.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1])),s=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||s?"":mi(t.entity,o),r=!(!String(t.entity??"").trim()||i||n&&!s);if(n&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+n+e.slice(t+2):`${e}${e?" ":""}${n}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:t.color||this._decorStyle.color,opacity:Fs(t.opacity,this._decorStyle.opacity),angle:Ls(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=mi(e.pickerEntity,t);if(!i)return;const s=e.text,o=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),n=Math.max(o,Math.min(s.length,this._decorTextSelection.end));if(s.length-(n-o)+i.length>200)return;const r=s.slice(0,o)+i+s.slice(n),a=o+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,o={color:t.color,opacity:Fs(t.opacity),size_cm:Number(Math.max(.1,Math.min(qa,t.sizeCm)).toFixed(4)),...Ls(t.angle)?{angle:Ls(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:n,...r}=i;return{...r,text:e,...o}}const{entity:s,attr:n,unit:r,...a}=i,{angle:l,size:h,scale:c,...d}=a;return{...d,text:e,...o}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...o}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:Fs(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Fs(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,o=Bs(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*ol,s=(e.y1+e.y2)/2*this._decorH,n=Hs(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Ls(t.angle)*Math.PI/180,a=Math.cos(r)*n/2,l=Math.sin(r)*n/2,h=this._snap([i-a,s-l]),c=this._snap([i+a,s+l]),{width:d,...p}=e;return{...p,...o,x1:mn(h[0]/ol),y1:mn(h[1]/this._decorH),x2:mn(c[0]/ol),y2:mn(c[1]/this._decorH)}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*ol,s=e.h*this._decorH,n=Math.max(this._gridPitch,Me(Hs(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Me(Hs(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*ol+i/2,l=e.y*this._decorH+s/2,h=Ls(t.angle),c=qs({x:a-n/2,y:l-r/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...o,x:mn(c[0]/ol),y:mn(c[1]/this._decorH),w:n/ol,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...h?{angle:h}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*ol,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*ol,(t.y+t.h/2)*this._decorH]:[t.x*ol,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const o={...s};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(qa,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const o=this._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[n,r]=this._dtPivot(o),a=this._svgPoint(t),l=this._decorBoxOf(o);this._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:n,ay:r,r0:Math.hypot(a[0]-n,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-n)/Math.PI,textSizeCm0:"text"===o.kind?this._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},hl(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),o=mn(s[0]/ol),n=mn(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(o-a)>1e-9||Math.abs(n-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:n}:{x2:o,y2:n})}if("scale"===e.kind&&e.orig){const s=Vs(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),o=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(qa,e.textSizeCm0*(t/e.r0))),o=Math.abs(s-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let o=e.angle0+(s-e.a0);t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360);const n=Math.abs(o-e.angle0)>1e-6;(n||e.moved)&&(e.moved||=n,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*ol,s=t.y1*this._decorH,o=t.x2*ol,n=t.y2*this._decorH;e={id:t.id,x:Math.min(i,o),y:Math.min(s,n),w:Math.abs(o-i),h:Math.abs(n-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*ol,y:t.y*this._decorH,w:t.w*ol,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=En(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const o=ol,n=this._decorH,r=qn(On(i.w,this._cellCm,this._gridPitch,o)),a=qn(On(i.h,this._cellCm,this._gridPitch,o)),l=this._geometrySnapshot(),h=this._decorSnap(t);let c=h[0],d=h[1],p=0;const u=e?null:Bn(c,d,a*n,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(c=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:mn(c/o-r/2),y:mn(d/n-a/2),w:r,h:a,...Bs(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const o=ol,n=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*o+(r[0]-e.start[0]),l=(i.y+i.h/2)*n+(r[1]-e.start[1]);let h,c,d=Number(i.angle)||0;const p=t.shiftKey?null:Bn(a,l,i.h*n,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)h=p.cx/o-i.w/2,c=p.cy/n-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*o,l-i.h/2*n],t.pointerType,e.id);h=s[0]/o,c=s[1]/n}h=mn(h),c=mn(c),(Math.abs(h-i.x)>1e-9||Math.abs(c-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:h,y:c};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=ol,o=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const n=qs(e,i.angle,t=>this._snap(t));return{...i,x:mn(n[0]/s),y:mn(n[1]/o),w:Math.max(this._gridPitch/s,Math.min(2*an,e.w/s)),h:Math.max(this._gridPitch/o,Math.min(2*an,e.h/o))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=ol,s=this._decorH,o=e.w*i,n=e.h*s,r=function(t,e,i,s,o){const n=t+i/2,r=e+s/2,a=(Number(o)||0)*Math.PI/180,l=Math.cos(a),h=Math.sin(a),c=(t,e)=>{const i=t-n,s=e-r;return[n+i*l-s*h,r+i*h+s*l]};return[c(t,e),c(t+i,e),c(t+i,e+s),c(t,e+s)]}(e.x*i,e.y*s,o,n,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),h=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[o,0])},{x:h[0],y:h[1],text:this._fmtLen([0,0],[0,n])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}>
${this._t("furn.title")} @@ -3143,7 +3162,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @input=${e=>this._furnPalette={...t,h:this._furnFieldToCm(Number(e.target.value))}} /> ${this._t("furn.place_hint")}
`:W`
${this._t("furn.pick_hint")}
`} -
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...tn(t.plan_aspect,sl)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?on(t,sl):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),o=Number(t?.plan_scale_x),n=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(o)&&o>0?o:r,sy:Number.isFinite(n)&&n>0?n:r,angle:Ls(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Us(e.w,this._cellCm,this._gridPitch),heightCm:Us(e.h,this._cellCm,this._gridPitch),angle:Ls(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=this._geometrySnapshot(),o=Math.min(e.w*sn,Math.max(e.w*en,Me(Hs(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),n=Math.min(e.h*sn,Math.max(e.h*en,Me(Hs(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=qs({x:i.x+i.w/2-o/2,y:i.y+i.h/2-n/2},t.angle,t=>this._snap(t));this._bdApply((r[0]-e.x)/sl,(r[1]-e.y)/sl,o/e.w,n/e.h,t.angle),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),s),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,o){const n=this._curSpaceCfg;if(!n)return;n.plan_x=Number(mn(t).toFixed(6)),n.plan_y=Number(mn(e).toFixed(6)),delete n.plan_scale,n.plan_scale_x=Number(Math.min(sn,Math.max(en,i)).toFixed(6)),n.plan_scale_y=Number(Math.min(sn,Math.max(en,s)).toFixed(6));const r=Ls(o);r?n.plan_angle=Number(r.toFixed(2)):delete n.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,o=this._bdRect;if(!s||!o)return!1;const n=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?o.x:o.x+o.w,h=a>0?o.y:o.y+o.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:s,p0:this._bdParams,fx:l,fy:h,sgx:r,sgy:a,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},ll(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,n=this._snap([t+(i[0]-e.sx),o+(i[1]-e.sy)]),r=Math.abs(n[0]-t)>1e-9||Math.abs(n[1]-o)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((n[0]-s.x)/sl,(n[1]-s.y)/sl,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,n=180*Math.atan2(e.sy-o,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-o,i[0]-s)/Math.PI-n);t.shiftKey||(r=5*Math.round(r/5)),r=Ls(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const o=Vs(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*en),n=o.w/Math.max(1e-9,s.w),r=o.h/Math.max(1e-9,s.h),a=Math.abs(n-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=qs(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/sl,(l[1]-s.y)/sl,n,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return G;const i=.02*Math.max(t.w,t.h),s=i/4,o=e.x+e.w/2,n=e.y+e.h/2,r=Ls(e.angle),a=2.2*i;return B` +
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...tn(t.plan_aspect,ol)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?on(t,ol):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),o=Number(t?.plan_scale_x),n=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(o)&&o>0?o:r,sy:Number.isFinite(n)&&n>0?n:r,angle:Ls(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Us(e.w,this._cellCm,this._gridPitch),heightCm:Us(e.h,this._cellCm,this._gridPitch),angle:Ls(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=this._geometrySnapshot(),o=Math.min(e.w*sn,Math.max(e.w*en,Me(Hs(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),n=Math.min(e.h*sn,Math.max(e.h*en,Me(Hs(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=qs({x:i.x+i.w/2-o/2,y:i.y+i.h/2-n/2},t.angle,t=>this._snap(t));this._bdApply((r[0]-e.x)/ol,(r[1]-e.y)/ol,o/e.w,n/e.h,t.angle),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),s),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,o){const n=this._curSpaceCfg;if(!n)return;n.plan_x=Number(mn(t).toFixed(6)),n.plan_y=Number(mn(e).toFixed(6)),delete n.plan_scale,n.plan_scale_x=Number(Math.min(sn,Math.max(en,i)).toFixed(6)),n.plan_scale_y=Number(Math.min(sn,Math.max(en,s)).toFixed(6));const r=Ls(o);r?n.plan_angle=Number(r.toFixed(2)):delete n.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,o=this._bdRect;if(!s||!o)return!1;const n=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?o.x:o.x+o.w,h=a>0?o.y:o.y+o.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:s,p0:this._bdParams,fx:l,fy:h,sgx:r,sgy:a,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},hl(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,n=this._snap([t+(i[0]-e.sx),o+(i[1]-e.sy)]),r=Math.abs(n[0]-t)>1e-9||Math.abs(n[1]-o)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((n[0]-s.x)/ol,(n[1]-s.y)/ol,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,n=180*Math.atan2(e.sy-o,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-o,i[0]-s)/Math.PI-n);t.shiftKey||(r=5*Math.round(r/5)),r=Ls(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const o=Vs(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*en),n=o.w/Math.max(1e-9,s.w),r=o.h/Math.max(1e-9,s.h),a=Math.abs(n-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=qs(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/ol,(l[1]-s.y)/ol,n,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return G;const i=.02*Math.max(t.w,t.h),s=i/4,o=e.x+e.w/2,n=e.y+e.h/2,r=Ls(e.angle),a=2.2*i;return B` {e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} - `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return G;const s=.018*Math.max(t.w,t.h),o=s/4;if("line"===e.kind){const t=[e.x1*sl,e.y1*this._decorH],i=[e.x2*sl,e.y2*this._decorH];return B` + `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return G;const s=.018*Math.max(t.w,t.h),o=s/4;if("line"===e.kind){const t=[e.x1*ol,e.y1*this._decorH],i=[e.x2*ol,e.y2*this._decorH];return B` ${[t,i].map((t,e)=>B`this._dtStart(t,"scale",void 0,e)}> @@ -3168,30 +3187,42 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow cx="${t<0?i.x:i.x+i.w}" cy="${e<0?i.y:i.y+i.h}" r="${s.toFixed(1)}" @pointerdown=${i=>this._dtStart(i,"scale",[t,e])}>`)} - `}_renderDecorLayer(){const t=sl,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const o="dshape"+(i&&this._decorSel===s.id?" dsel":""),n=this._decorResolvedStyle(s),r=this._decorWidthUnits(s),a=t=>this._decorShapeDown(t,s),l=t=>this._decorShapeDbl(t,s);if("line"===s.kind)return B``;if("rect"===s.kind){const i=(s.x+s.w/2)*t,h=(s.y+s.h/2)*e,c=Ls(s.angle);return B``}if("ellipse"===s.kind){const i=(s.x+s.w/2)*t,h=(s.y+s.h/2)*e,c=Ls(s.angle);return B``}if("furniture"===s.kind){const i=s.w*t,h=s.h*e,c=Un(s.symbol,i,h);if(!c)return G;const d=Number(s.angle)||0,p=s.x*t+i/2,u=s.y*e+h/2,_=`${d?`rotate(${d} ${p} ${u}) `:""}translate(${s.x*t} ${s.y*e})`;return B``}_renderDecorLayer(){const t=ol,e=this._decorH,i="decor"===this._mode,s=i&&"erase"===this._decorTool,o=this._decorList.map(o=>{const n="dshape"+(i&&this._decorSel===o.id?" dsel":""),r=this._decorResolvedStyle(o),a=this._decorWidthUnits(o),l=t=>this._decorShapeDown(t,o),h=t=>this._decorShapeDbl(t,o);if("line"===o.kind)return B` + ${s?B``:G}`;if("rect"===o.kind){const i=(o.x+o.w/2)*t,c=(o.y+o.h/2)*e,d=Ls(o.angle);return B` + ${s?B``:G}`}if("ellipse"===o.kind){const i=(o.x+o.w/2)*t,c=(o.y+o.h/2)*e,d=Ls(o.angle);return B` + ${s?B``:G}`}if("furniture"===o.kind){const i=o.w*t,c=o.h*e,d=Un(o.symbol,i,c);if(!d)return G;const p=Number(o.angle)||0,u=o.x*t+i/2,_=o.y*e+c/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${o.x*t} ${o.y*e})`;return B``}if("text"===s.kind){const i=this._decorTextUnits(s),r=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=_i(e);return o?(n=!0,s(o.entity||"")?vi(i,o):pi):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf(ui);return t>=0?o.slice(0,t)+pi+o.slice(t+2):o?`${o} ${pi}`:pi}const l=vi(i,e),h=o.indexOf(ui);return h>=0?o.slice(0,h)+l+o.slice(h+2):o?`${o} ${l}`:l}(s.text,s,this.hass,t=>this._planEntityAvailable(t))),h=s.x*t,c=s.y*e,d=Number(s.angle)||0,p=c-(r.length-1)*i*1.2/2;return B`${r.map((t,e)=>B`${t}`)}`}return G});let o=G;const n=this._decorDraft;if(n){const t=this._decorStyle,e=Hs(t.widthCm,this._cellCm,this._gridPitch);if("line"===n.kind)o=B``;else{const i=Math.min(n.a[0],n.b[0]),s=Math.min(n.a[1],n.b[1]),r=Math.abs(n.b[0]-n.a[0]),a=Math.abs(n.b[1]-n.a[1]);o="rect"===n.kind?B``:B``}}return B`${s}${o}`}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._decorSel?this._decorList.find(t=>t.id===this._decorSel):null,i="rect"===this._decorTool||"ellipse"===this._decorTool||"rect"===e?.kind||"ellipse"===e?.kind,s=this._geometryHistory.undoName,o=this._geometryHistory.redoName;return W`
+ @pointerdown=${l} @dblclick=${h}> + ${s?B``:G}`}if("text"===o.kind){const i=this._decorTextUnits(o),s=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=_i(e);return o?(n=!0,s(o.entity||"")?vi(i,o):pi):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf(ui);return t>=0?o.slice(0,t)+pi+o.slice(t+2):o?`${o} ${pi}`:pi}const l=vi(i,e),h=o.indexOf(ui);return h>=0?o.slice(0,h)+l+o.slice(h+2):o?`${o} ${l}`:l}(o.text,o,this.hass,t=>this._planEntityAvailable(t))),a=o.x*t,c=o.y*e,d=Number(o.angle)||0,p=c-(s.length-1)*i*1.2/2;return B`${s.map((t,e)=>B`${t}`)}`}return G});let n=G;const r=this._decorDraft;if(r){const t=this._decorStyle,e=Hs(t.widthCm,this._cellCm,this._gridPitch);if("line"===r.kind)n=B``;else{const i=Math.min(r.a[0],r.b[0]),s=Math.min(r.a[1],r.b[1]),o=Math.abs(r.b[0]-r.a[0]),a=Math.abs(r.b[1]-r.a[1]);n="rect"===r.kind?B``:B``}}return B`${o}${n}`}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._decorSel?this._decorList.find(t=>t.id===this._decorSel):null,i="rect"===this._decorTool||"ellipse"===this._decorTool||"rect"===e?.kind||"ellipse"===e?.kind,s=this._geometryHistory.undoName,o=this._geometryHistory.redoName;return W`
${t.map(([t,e,i])=>W`
-
`:W``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=Re(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,o){for(const n of s)if(!(Zi([t,e],n)>o)&&uo([n[0],n[1]],[n[2],n[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:o.x,y:o.y,angle:o.angle},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{ll(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=Re(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,o=s?.openings?.find(t=>t.id===e.id);if(!o)return;const n=this._opRuler(i,o.length*sl);this._opMeasure=n.measure;const r=n.x/sl,a=n.y/this._spaceH;o.x===r&&o.y===a&&o.angle===i.angle||(this._opDrag.dirty=!0),o.x=r,o.y=a,o.angle=i.angle,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let o=t.x,n=t.y,r=Ae([o,n],t.angle,e,i,s);if(r&&r.centered&&(o!==r.wallCenter[0]||n!==r.wallCenter[1]))[o,n]=r.wallCenter,r=Ae([o,n],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[h,c]=r.wallB,d=h-a,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-a)*d+(n-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=a+_/u*d,n=l+_/u*p,r=Ae([o,n],t.angle,e,i,s)||r}}if(!r)return{x:o,y:n,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ce(t/this._gridPitch*this._cellCm,a)});return{x:o,y:n,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,o={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/sl,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/sl,contact:t.contact||null,lock:"door"===t.type&&t.lock||null,invert:t.invert||void 0,flip_h:t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const n=e.openings.findIndex(t=>t.id===o.id);n>=0?e.openings[n]=o:e.openings.push(o),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],o=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||o)&&t.push([e,s?.attributes?.friendly_name||e,o?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 o=e.find(t=>t.id===this._mergeSel),n=o?Pe(o):null,r=Pe(i),a=n&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ke(Je(t),Je(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}(n,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,o="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===o);r?(r.poly=t.poly.map(t=>[t[0]/sl,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==n),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?Pe(i):null;if(!i||!s)return void(this._splitSel=null);const o=.02*this._gridPitch,n=6*this._gridPitch,r=Fe(t,s),a=r&&function(t,e,i){let s=null,o=1/0;for(let n=0;n=o)continue;o=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*c,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?a:null,h=!!l&&Le(l,s,o),c=this._splitSel.pts;if(!c.length)return h?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const e=this._snap(t);return qe(e,s,o)?void(this._splitSel={...this._splitSel,pts:[...c,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],o=e[e.length-1];if(Ee(s,o,i))return null;const n=Ze(t,s,i),r=Ze(t,o,i);if(n<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!qe(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!==n);e++)a=(a+1)%t.length;return r.push(o),Xe(r,i)};let h,c;if(n===r){const r=Xe([...e],i);if(r.length<3||Ye(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}h=Xe(a,i),c=r}else h=Xe([...l(s,n,o,r),...[...a].reverse()],i),c=Xe([...l(o,r,s,n),...a],i);return h.length<3||c.length<3||Ye(h)<=i||Ye(c)<=i||Math.abs(Ye(h)+Ye(c)-Ye(t))>Math.max(i,1e-6*Ye(t))?null:[h,c]}(s,[...c,l],o);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=Ye(p)>=Ye(u)?p:u,m=_===p?u:p;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;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"openwall"===this._tool||"closewall"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const e=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!e&&!i)return;const s=this._svgPoint(t);this._cursorPt=e?this._snapDrawPoint(s,t.shiftKey):this._snap(s)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),o=Re(t,this._spaceModel().rooms,e);if(!o)return null;const n=this._opRuler(o,s);return{x:n.x,y:n.y,angle:n.angle,rlen:s,measure:n.measure}}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){this._areaSel&&this._commitRoom()}_saveRoomNoArea(){this._nameSel.trim()&&(this._areaSel="",this._commitRoom())}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit;let o;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]/sl,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,o=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;o=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:o.map(t=>[t[0]/sl,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!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(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,o,n=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Mo(e,i,n,o,r,t);if(!a)return t?t.slice():[];const l=Co(t,a,o,r);let h=t?t.slice():[];for(let t=0;t0||(h=mo(h,e,i,s,o,r))}return h}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,sl);for(const t of Po(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=o.findIndex((t,e)=>{const i=o[(e+1)%o.length];return Zi(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(n=mo(n,t.a,t.b,e[a]||i,this._wallKeyPitch,sl))}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 a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=sl,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/sl,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const h=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:h,added:l}):this._t("toast.room_saved_no_area",{n:h}))}_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._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_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){t&&this._ackNewDevice(t.id),this._norm?this._markerDialog=t?{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.hass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:"ripple"===t.marker?.display?"icon_ripple":t.marker?.display||"badge",rippleColor: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:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:Br(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",isLight:!0===t.marker?.is_light||jr(t.marker?.binding,t.marker?.controls,t.entities),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}:{name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",isLight:!1,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)}:this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of di)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&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.hass,e=sa(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 o=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&o.add(t.name.trim()+"|"+(t.area||""));const n=[];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)&&o.has(r+"|"+(e.area_id||""))||n.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[o,a]of Object.entries(t.entities)){const l="entity:"+o;if(s.has(l))continue;if(oa(t,o,e)&&!i.has(l))continue;const h=r.has(a.platform),c="group"===a.platform;if(!h&&!c)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[o];n.push({value:l,label:a.name||d?.attributes?.friendly_name||o,sub:o.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const o=new Set(n.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||o.has(l)||a.hidden&&!i.has(l))continue;if(oa(t,r,e)&&!i.has(l))continue;const h=t.states[r],c=a.name||h?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";n.push({value:l,label:c,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?n.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):n;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=>Go(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),o=(t.wall_columns||[]).map(t=>jo(t,this._cellCm,this._gridPitch)),n=[...i,...s,...o];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:o,all:n},n}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,o=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=Ba(this._cleanFloorCache,o);if(t.hit)return t.value}const n=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...n),Math.min(...r),Math.max(...n),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]}),h=l.length?Vo(e,l):null,c={floor:e,geom:h,path:h?Wo(h):"",area:Ko(h||[[[...e,e[0]]]])};return this._rszPreview||ja(this._cleanFloorCache,o,c,600),c}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";if("device"===e){const t=this.hass.devices?.[i];if(!t)return"mdi:help-circle";const e=Object.entries(this.hass.entities||{}).filter(([,t])=>t?.device_id===i).map(([t])=>t);return e.some(t=>t.startsWith("lock."))?"mdi:lock":ia(this.hass,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=this.hass.entities?.[i],e=this.hass.states?.[i],s=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":ia(this.hass,s,"",[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)}}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",o=[];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}`}:{}}),n=await i.json().catch(()=>({}));if(!i.ok||n.error){const t={too_large:this._t("err.too_large",{mb:n.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[n.error]||n.error||"HTTP "+i.status)}o.push({name:n.name||t.name,url:n.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}o.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...o]},this._showToast(this._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=function(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}(t.room);let o=s?.space||null,n=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||o||(o=this._space),i=function(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=l.length>0,c=Br(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,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,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:c.length?c:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Wa(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=n,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==o||u.area!==n||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const o="/files/"+e+"/",n="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(o))return t;const e=t.url.split(o)[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(o+i)[0]+n+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(o).join(n)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=o||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!h&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(h||!this._layout[i]||m){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):n?t.rooms.find(t=>t.area===n):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(o||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const k=new Set(l);a&&a!==i&&k.add(a),k.delete(i);for(const t of k)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).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 o=s.markers,n="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!n)return;const r=function(t,e,i,s){const o=new Set([e]),n=t.filter(t=>{const n=t.id===e||!s&&t.binding===i;return n&&o.add(t.id),!n});return{markers:s?n:[...n,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:o}}(s.markers,e.id,n,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){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._infoCard=null),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=o),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/sl,y:i/sl}}_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=xi(i);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:s.fill,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Rs({},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:ki,roomOpacity:$i,fillMode:"glow",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 o=new Uint8Array(await i.arrayBuffer());let n="";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``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=Re(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,o){for(const n of s)if(!(Zi([t,e],n)>o)&&uo([n[0],n[1]],[n[2],n[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:o.x,y:o.y,angle:o.angle},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{hl(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=Re(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,o=s?.openings?.find(t=>t.id===e.id);if(!o)return;const n=this._opRuler(i,o.length*ol);this._opMeasure=n.measure;const r=n.x/ol,a=n.y/this._spaceH;o.x===r&&o.y===a&&o.angle===i.angle||(this._opDrag.dirty=!0),o.x=r,o.y=a,o.angle=i.angle,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let o=t.x,n=t.y,r=Ae([o,n],t.angle,e,i,s);if(r&&r.centered&&(o!==r.wallCenter[0]||n!==r.wallCenter[1]))[o,n]=r.wallCenter,r=Ae([o,n],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[h,c]=r.wallB,d=h-a,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-a)*d+(n-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=a+_/u*d,n=l+_/u*p,r=Ae([o,n],t.angle,e,i,s)||r}}if(!r)return{x:o,y:n,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ce(t/this._gridPitch*this._cellCm,a)});return{x:o,y:n,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,o={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/ol,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/ol,contact:t.contact||null,lock:"door"===t.type&&t.lock||null,invert:t.invert||void 0,flip_h:t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const n=e.openings.findIndex(t=>t.id===o.id);n>=0?e.openings[n]=o:e.openings.push(o),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],o=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||o)&&t.push([e,s?.attributes?.friendly_name||e,o?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 o=e.find(t=>t.id===this._mergeSel),n=o?Pe(o):null,r=Pe(i),a=n&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=$e(Je(t),Je(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}(n,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,o="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===o);r?(r.poly=t.poly.map(t=>[t[0]/ol,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==n),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?Pe(i):null;if(!i||!s)return void(this._splitSel=null);const o=.02*this._gridPitch,n=6*this._gridPitch,r=Fe(t,s),a=r&&function(t,e,i){let s=null,o=1/0;for(let n=0;n=o)continue;o=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*c,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?a:null,h=!!l&&Le(l,s,o),c=this._splitSel.pts;if(!c.length)return h?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const e=this._snap(t);return qe(e,s,o)?void(this._splitSel={...this._splitSel,pts:[...c,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],o=e[e.length-1];if(Ee(s,o,i))return null;const n=Ze(t,s,i),r=Ze(t,o,i);if(n<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!qe(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!==n);e++)a=(a+1)%t.length;return r.push(o),Xe(r,i)};let h,c;if(n===r){const r=Xe([...e],i);if(r.length<3||Ye(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}h=Xe(a,i),c=r}else h=Xe([...l(s,n,o,r),...[...a].reverse()],i),c=Xe([...l(o,r,s,n),...a],i);return h.length<3||c.length<3||Ye(h)<=i||Ye(c)<=i||Math.abs(Ye(h)+Ye(c)-Ye(t))>Math.max(i,1e-6*Ye(t))?null:[h,c]}(s,[...c,l],o);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=Ye(p)>=Ye(u)?p:u,m=_===p?u:p;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;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"openwall"===this._tool||"closewall"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const e=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!e&&!i)return;const s=this._svgPoint(t);this._cursorPt=e?this._snapDrawPoint(s,t.shiftKey):this._snap(s)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),o=Re(t,this._spaceModel().rooms,e);if(!o)return null;const n=this._opRuler(o,s);return{x:n.x,y:n.y,angle:n.angle,rlen:s,measure:n.measure}}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;let o;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]/ol,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,o=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;o=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:o.map(t=>[t[0]/ol,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!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(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,o,n=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Mo(e,i,n,o,r,t);if(!a)return t?t.slice():[];const l=Co(t,a,o,r);let h=t?t.slice():[];for(let t=0;t0||(h=mo(h,e,i,s,o,r))}return h}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,ol);for(const t of Po(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=o.findIndex((t,e)=>{const i=o[(e+1)%o.length];return Zi(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(n=mo(n,t.a,t.b,e[a]||i,this._wallKeyPitch,ol))}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 a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=ol,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/ol,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const h=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:h,added:l}):this._t("toast.room_saved_no_area",{n:h}))}_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._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_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){t&&this._ackNewDevice(t.id),this._norm?this._markerDialog=t?{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.hass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:"ripple"===t.marker?.display?"icon_ripple":t.marker?.display||"badge",rippleColor: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:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:jr(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",isLight:!0===t.marker?.is_light||Gr(t.marker?.binding,t.marker?.controls,t.entities),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}:{name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",isLight:!1,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)}:this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of di)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&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.hass,e=oa(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 o=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&o.add(t.name.trim()+"|"+(t.area||""));const n=[];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)&&o.has(r+"|"+(e.area_id||""))||n.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[o,a]of Object.entries(t.entities)){const l="entity:"+o;if(s.has(l))continue;if(na(t,o,e)&&!i.has(l))continue;const h=r.has(a.platform),c="group"===a.platform;if(!h&&!c)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[o];n.push({value:l,label:a.name||d?.attributes?.friendly_name||o,sub:o.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const o=new Set(n.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||o.has(l)||a.hidden&&!i.has(l))continue;if(na(t,r,e)&&!i.has(l))continue;const h=t.states[r],c=a.name||h?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";n.push({value:l,label:c,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?n.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):n;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=>Go(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),o=(t.wall_columns||[]).map(t=>jo(t,this._cellCm,this._gridPitch)),n=[...i,...s,...o];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:o,all:n},n}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,o=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=ja(this._cleanFloorCache,o);if(t.hit)return t.value}const n=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...n),Math.min(...r),Math.max(...n),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]}),h=l.length?Vo(e,l):null,c={floor:e,geom:h,path:h?Wo(h):"",area:Ko(h||[[[...e,e[0]]]])};return this._rszPreview||Ga(this._cleanFloorCache,o,c,600),c}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";if("device"===e){const t=this.hass.devices?.[i];if(!t)return"mdi:help-circle";const e=Object.entries(this.hass.entities||{}).filter(([,t])=>t?.device_id===i).map(([t])=>t);return e.some(t=>t.startsWith("lock."))?"mdi:lock":sa(this.hass,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=this.hass.entities?.[i],e=this.hass.states?.[i],s=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":sa(this.hass,s,"",[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)}}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",o=[];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}`}:{}}),n=await i.json().catch(()=>({}));if(!i.ok||n.error){const t={too_large:this._t("err.too_large",{mb:n.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[n.error]||n.error||"HTTP "+i.status)}o.push({name:n.name||t.name,url:n.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}o.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...o]},this._showToast(this._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=function(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}(t.room);let o=s?.space||null,n=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||o||(o=this._space),i=function(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=l.length>0,c=jr(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,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,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:c.length?c:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Ba(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=n,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==o||u.area!==n||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const o="/files/"+e+"/",n="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(o))return t;const e=t.url.split(o)[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(o+i)[0]+n+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(o).join(n)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=o||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!h&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(h||!this._layout[i]||m){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):n?t.rooms.find(t=>t.area===n):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(o||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const $=new Set(l);a&&a!==i&&$.add(a),$.delete(i);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(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).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 o=s.markers,n="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!n)return;const r=function(t,e,i,s){const o=new Set([e]),n=t.filter(t=>{const n=t.id===e||!s&&t.binding===i;return n&&o.add(t.id),!n});return{markers:s?n:[...n,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:o}}(s.markers,e.id,n,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){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._infoCard=null),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=o),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/ol,y:i/ol}}_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=xi(i);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:s.fill,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Rs({},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:$i,roomOpacity:ki,fillMode:"glow",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 o=new Uint8Array(await i.arrayBuffer());let n="";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`
@@ -3417,7 +3448,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`)} -
`}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!o&&this._aspectJob&&(o=await this._aspectJob||null);const n=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},n.spaces.push(r);else{if(r=n.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=o),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(qa,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:ki,roomOpacity:$i,fillMode:"glow",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})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return W``}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!o&&this._aspectJob&&(o=await this._aspectJob||null);const n=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},n.spaces.push(r);else{if(r=n.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=o),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(Ha,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:$i,roomOpacity:ki,fillMode:"glow",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})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return W`this._importDialog=null}>
${this._t("import.hint")}
@@ -3437,7 +3468,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._t("import.start",{n:e})}
-
`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays,weather_entity:(t.weatherEntity||"").trim()||void 0}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Rs(this._sunGlobal(),this._sunSpace())}_effBgMode(){return As(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Ns(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Is(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Is(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const o=Es(this._sunGlobal()),n=function(t){if(!t)return 1;const e=zs[String(t).toLowerCase()];return void 0===e?1:e}(o?this.hass?.states?.[o]?.state:null),r=function(t=1){return.3*ks(t)}(n);if(r<=0)return this._sunFadeReset(),e;if(a=s.elevation,Number(a)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var a;const l=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const e=t.rooms.map(t=>({id:t.id||"",poly:Pe(t)})).filter(t=>!!t.id&&!!t.poly),o=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),n=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},h={};if(n.length){for(const i of e){const e=Ao(t.rooms,i.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,sl);e&&(a[i.id]=e)}for(const e of o){const i=Fo(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},n,this._wallKeyPitch,this._cellCm,this._gridPitch,sl);i.cm>0&&(h[e.id]=Qs(i.cm,this._cellCm,this._gridPitch))}}let c=Ds(e,o,s.azimuth,s.elevation,i,n.length?a:void 0,n.length?h:void 0);const d=this._physicalBodiesR(t);d.length&&(c=c.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.slice(0,-1).concat(o.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(d,t.dir,t.len),i=t.polys.map(t=>Vo(t,e));return{...t,paths:i.map(Wo).filter(Boolean),polys:i.flatMap(Yo)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:l,rays:c,rims:c.map(t=>function(t,e=1e-4){const[i,s]=t.dir,o=-s,n=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((c[0]-a[0])*o+(c[1]-a[1])*n)>e)continue;const d=(h[0]-a[0])*i+(h[1]-a[1])*s,p=(c[0]-a[0])*i+(c[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const h=[];for(const t of l){const i=h[h.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):h.push([t[0],t[1]])}for(const[t,e]of h)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const h=this._sunRaysCache.rays,c=this._sunRaysCache.rims;if(!h.length)return e;const d=(p=$s(s.elevation).warmth,Pi("#ffe9c2","#ff9a45",ks(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*ks(t)}(n),m=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` +
`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays,weather_entity:(t.weatherEntity||"").trim()||void 0}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Rs(this._sunGlobal(),this._sunSpace())}_effBgMode(){return As(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Ns(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Is(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Is(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const o=Es(this._sunGlobal()),n=function(t){if(!t)return 1;const e=zs[String(t).toLowerCase()];return void 0===e?1:e}(o?this.hass?.states?.[o]?.state:null),r=function(t=1){return.3*$s(t)}(n);if(r<=0)return this._sunFadeReset(),e;if(a=s.elevation,Number(a)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var a;const l=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const e=t.rooms.map(t=>({id:t.id||"",poly:Pe(t)})).filter(t=>!!t.id&&!!t.poly),o=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),n=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},h={};if(n.length){for(const i of e){const e=Ao(t.rooms,i.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,ol);e&&(a[i.id]=e)}for(const e of o){const i=Fo(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},n,this._wallKeyPitch,this._cellCm,this._gridPitch,ol);i.cm>0&&(h[e.id]=Qs(i.cm,this._cellCm,this._gridPitch))}}let c=Ds(e,o,s.azimuth,s.elevation,i,n.length?a:void 0,n.length?h:void 0);const d=this._physicalBodiesR(t);d.length&&(c=c.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.slice(0,-1).concat(o.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(d,t.dir,t.len),i=t.polys.map(t=>Vo(t,e));return{...t,paths:i.map(Wo).filter(Boolean),polys:i.flatMap(Yo)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:l,rays:c,rims:c.map(t=>function(t,e=1e-4){const[i,s]=t.dir,o=-s,n=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((c[0]-a[0])*o+(c[1]-a[1])*n)>e)continue;const d=(h[0]-a[0])*i+(h[1]-a[1])*s,p=(c[0]-a[0])*i+(c[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const h=[];for(const t of l){const i=h[h.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):h.push([t[0],t[1]])}for(const[t,e]of h)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const h=this._sunRaysCache.rays,c=this._sunRaysCache.rims;if(!h.length)return e;const d=(p=ks(s.elevation).warmth,Pi("#ffe9c2","#ff9a45",$s(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*$s(t)}(n),m=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` ${h.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,o=i+t.normal[0]*t.depth,n=s+t.normal[1]*t.depth;return B` ${u.map(([t,e])=>B`${this._t("gs.north_letter")} ${null===t?"—":t+"°"} - `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return $s(Ps(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Si(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Mi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const o=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(o)&&o>0&&300!==Math.round(o)?s.glow_radius_cm=Math.round(o):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays;const n=(t.weatherEntity||"").trim();n?s.weather_entity=n:delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?W``:W``}_rangeInput(t,e,i,s,o){const n=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?W``:W``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return W`
+ `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return ks(Ps(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Si(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Mi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const o=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(o)&&o>0&&300!==Math.round(o)?s.glow_radius_cm=Math.round(o):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays;const n=(t.weatherEntity||"").trim();n?s.weather_entity=n:delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?W``:W``}_rangeInput(t,e,i,s,o){const n=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?W``:W``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return W`
${this._t(e)} this._setFillColor(t,{c:e.target.value})} /> ${this._rangeInput(0,100,1,Math.round(100*i.a),e=>this._setFillColor(t,{a:e/100}))} ${Math.round(100*i.a)}% -
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t){const e=this._fillColors,i=this._glowRadiusCm/this._cellCm*this._gridPitch,s=this._gridPitch,o=t.rooms.map(t=>({r:t,poly:Pe(t)})).filter(t=>!!t.poly),n=this._openingsR.filter(t=>"door"===t.type),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._physicalBodiesR(t),h=new Map;if(r.length)for(const e of n){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,o=Math.sin(i)*e.rlen/2,n=To(t.rooms,r,a,[e.rx-s,e.ry-o,e.rx+s,e.ry+o],this._wallKeyPitch,this._cellCm,this._gridPitch,sl);n>0&&h.set(e.id,Qs(n,this._cellCm,this._gridPitch))}const c=new Map;for(const e of Kr(this.hass,this._devices.filter(e=>e.space===t.id)))e.on&&e.device.id&&!c.has(e.device.id)&&c.set(e.device.id,e.eid);const d=[];for(const p of this._devices){if(p.space!==t.id)continue;const u=c.get(p.id);if(!u)continue;const _=Ei(this.hass.states[u],e.glow_light.c);if(!_)continue;const m=Number(p.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:i,f=this._pos(p),v=[...o].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?Ba(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=l.length?Jo(l,[f.x,f.y],g):[],i=v.r.id?qi(v.r.id,t.rooms):new Set([v.r.id]),c=o.filter(t=>t.r.id&&i.has(t.r.id)),d=c.length?c:[v],p=d.map(i=>{const s=r.length&&i.r.id&&Ao(t.rooms,i.r.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||i.poly;return e.length?Wo(Vo(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=o.filter(t=>!d.includes(t)).map(t=>t.poly);for(const t of n){const i=d.some(e=>{const i=Fe([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!i)continue;const o=t.angle*Math.PI/180,n=Math.cos(o)*t.rlen/2,r=Math.sin(o)*t.rlen/2;if(!Oi([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*s))continue;const a=Ii([f.x,f.y],[t.rx-n,t.ry-r],[t.rx+n,t.ry+r],g,170,h.get(t.id)||0);a&&p.push(e.length?Wo(Vo(a,e)):"M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,ja(this._glowClipCache,b,y,256)}d.push({pos:f,c:_.c,alpha:e.glow_light.a*_.bri,clip:y,r:g})}return d.length?B` +
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t){const e=this._fillColors,i=this._glowRadiusCm/this._cellCm*this._gridPitch,s=this._gridPitch,o=t.rooms.map(t=>({r:t,poly:Pe(t)})).filter(t=>!!t.poly),n=this._openingsR.filter(t=>"door"===t.type),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._physicalBodiesR(t),h=new Map;if(r.length)for(const e of n){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,o=Math.sin(i)*e.rlen/2,n=To(t.rooms,r,a,[e.rx-s,e.ry-o,e.rx+s,e.ry+o],this._wallKeyPitch,this._cellCm,this._gridPitch,ol);n>0&&h.set(e.id,Qs(n,this._cellCm,this._gridPitch))}const c=new Map;for(const e of Yr(this.hass,this._devices.filter(e=>e.space===t.id)))e.on&&e.device.id&&!c.has(e.device.id)&&c.set(e.device.id,e.eid);const d=[];for(const p of this._devices){if(p.space!==t.id)continue;const u=c.get(p.id);if(!u)continue;const _=Ei(this.hass.states[u],e.glow_light.c);if(!_)continue;const m=Number(p.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:i,f=this._pos(p),v=[...o].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?ja(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=l.length?Jo(l,[f.x,f.y],g):[],i=v.r.id?qi(v.r.id,t.rooms):new Set([v.r.id]),c=o.filter(t=>t.r.id&&i.has(t.r.id)),d=c.length?c:[v],p=d.map(i=>{const s=r.length&&i.r.id&&Ao(t.rooms,i.r.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||i.poly;return e.length?Wo(Vo(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=o.filter(t=>!d.includes(t)).map(t=>t.poly);for(const t of n){const i=d.some(e=>{const i=Fe([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!i)continue;const o=t.angle*Math.PI/180,n=Math.cos(o)*t.rlen/2,r=Math.sin(o)*t.rlen/2;if(!Oi([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*s))continue;const a=Ii([f.x,f.y],[t.rx-n,t.ry-r],[t.rx+n,t.ry+r],g,170,h.get(t.id)||0);a&&p.push(e.length?Wo(Vo(a,e)):"M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,Ga(this._glowClipCache,b,y,256)}d.push({pos:f,c:_.c,alpha:e.glow_light.a*_.bri,clip:y,r:g})}return d.length?B` ${d.map((t,e)=>B` @@ -3533,7 +3564,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._t("gs.glow_radius")} {const e=Wa(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> + @input=${t=>{const e=Ba(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")}
@@ -3594,7 +3625,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:G} -
${this._t("gs.about_version",{v:Fa})}
+
${this._t("gs.about_version",{v:La})}
${this._t("gs.about_github")} @@ -3651,7 +3682,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t.busy?"…":this._t("btn.save")} - `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(il,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const t=this._kioskScale,e=(e,i)=>W` + `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(sl,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const t=this._kioskScale,e=(e,i)=>W`
${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))} ${Math.round(100*t[e])}% @@ -3682,7 +3713,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._spaceDialog?this._renderSpaceDialog():G} ${this._importDialog?this._renderImportDialog():G} ${this._toast?W`
${this._toast}
`:G} - `;const e=this._spaceModel(),i=e.vb,s="devices"===this._mode&&this._showAll,o=this._devices.filter(t=>t.space===e.id&&(!t.hidden||s)),n=xi(this._curSpaceCfg),r=n.showLqi??this._config.show_signal??!0,a=this._config.icon_size??2.5,l=a>8?2.5:a,h=this._viewOr(i),c=this._editing?"":this._stageBg(n),d=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow(),p=d?$s(Ps(d.elevation)).planDim:0,u=this._opMeasureView,_=this._decorMeasure,m=this._bdLive,g=this._furnLive,f="view"===this._mode?this._editorChromeMode:this._mode;return W` + `;const e=this._spaceModel(),i=e.vb,s="devices"===this._mode&&this._showAll,o=this._devices.filter(t=>t.space===e.id&&(!t.hidden||s)),n=xi(this._curSpaceCfg),r=n.showLqi??this._config.show_signal??!0,a=this._config.icon_size??2.5,l=a>8?2.5:a,h=this._viewOr(i),c=this._editing?"":this._stageBg(n),d=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow(),p=d?ks(Ps(d.elevation)).planDim:0,u=this._opMeasureView,_=this._decorMeasure,m=this._bdLive,g=this._furnLive,f="view"===this._mode?this._editorChromeMode:this._mode;return W`
@@ -3761,14 +3792,14 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow preserveAspectRatio="none" />`:G} ${""} ${n.hideDecor&&"decor"!==this._mode?G:this._renderDecorLayer()} - ${(()=>{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Pe(t)),i.get(t));return e.rooms.filter(t=>t.area||"view"===this._mode||this._markup||n.showBorders).map(i=>{let o="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Vi(n.fill,i);if(!this._markup&&(n.showBorders||"none"!==l)){o+=" styled";const t=[];t.push(`--room-stroke:${n.color}`,`--room-stroke-op:${n.showBorders?n.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?zi("temp",null,"none",this._roomTemp(i),n.tempMin,n.tempMax,this._fillColors):"light"===l?zi("light",null,Yr(Kr(this.hass,this._devices,i)),null,n.tempMin,n.tempMax,this._fillColors):i.area?zi(l,"lqi"===l?this._roomLqi(i.area):null,"none",null,n.tempMin,n.tempMax,this._fillColors):null;e?(o+=" filled",t.push(`--room-fill:${e.c}`,`--room-fill-op:${e.a.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),a=t.join(";")}let h;const c=t=>{"view"===this._mode&&(void 0===h&&(h=this._roomArea(i)),this._showTip(t,i.name||this._t("room.unnamed"),h?this._t("tip.area",{value:h}):"",r?this._roomLqi(i.area):null,this._roomTemp(i)))},d=!e.bg&&!n.showNames&&!this._markup,p=this._roomCenter(i),u=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),_=i.id&&!u?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],m=u?[]:this._thickWallCuts(),g=_.concat(m);g.length&&(o+=" noedge");const f=s(i),v=this._spaceWalls,y=v.length&&i.id&&f&&Ao(e.rooms,i.id,v,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||f,b=y?Ke(y,(w=i,e.rooms.filter(t=>t!==w).map(s).filter(Boolean))):[];var w;const k=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",$=y?this._cleanFloor(i,y,e).path:"",x=i.id||G,S=i.area||G,M=$&&y?B`{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Pe(t)),i.get(t));return e.rooms.filter(t=>t.area||"view"===this._mode||this._markup||n.showBorders).map(i=>{let o="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Vi(n.fill,i);if(!this._markup&&(n.showBorders||"none"!==l)){o+=" styled";const t=[];t.push(`--room-stroke:${n.color}`,`--room-stroke-op:${n.showBorders?n.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?zi("temp",null,"none",this._roomTemp(i),n.tempMin,n.tempMax,this._fillColors):"light"===l?zi("light",null,Jr(Yr(this.hass,this._devices,i)),null,n.tempMin,n.tempMax,this._fillColors):i.area?zi(l,"lqi"===l?this._roomLqi(i.area):null,"none",null,n.tempMin,n.tempMax,this._fillColors):null;e?(o+=" filled",t.push(`--room-fill:${e.c}`,`--room-fill-op:${e.a.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),a=t.join(";")}let h;const c=t=>{"view"===this._mode&&(void 0===h&&(h=this._roomArea(i)),this._showTip(t,i.name||this._t("room.unnamed"),h?this._t("tip.area",{value:h}):"",r?this._roomLqi(i.area):null,this._roomTemp(i)))},d=!e.bg&&!n.showNames&&!this._markup,p=this._roomCenter(i),u=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),_=i.id&&!u?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],m=u?[]:this._thickWallCuts(),g=_.concat(m);g.length&&(o+=" noedge");const f=s(i),v=this._spaceWalls,y=v.length&&i.id&&f&&Ao(e.rooms,i.id,v,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||f,b=y?Ke(y,(w=i,e.rooms.filter(t=>t!==w).map(s).filter(Boolean))):[];var w;const $=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",k=y?this._cleanFloor(i,y,e).path:"",x=i.id||G,S=i.area||G,M=k&&y?B`this._hoverRoom={space:e.id,room:i}} @mousemove=${c} @mouseleave=${()=>{this._tip=null,this._hoverRoom=null}}>`:b.length&&y?B`this._hoverRoom={space:e.id,room:i}} @mousemove=${c} @mouseleave=${()=>{this._tip=null,this._hoverRoom=null}}>`:y&&y!==f?B``:G} ${this._toast?W`
${this._toast}
`:G} - `}_vacSource(t){const e=t.marker?.vacuum;if(!1===e?.live)return null;if(e?.source&&this.hass?.states[e.source])return e.source;for(const e of t.entities||[])if(ur(this.hass?.states[e]))return e;return null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return t.map(t=>t.eid).sort().join("\n")}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s={sources:i||"",last:{},flashTs:0,flashKind:null,timer:0,gen:0},this._activityRt.set(t,s)),null!=i&&(s.sources=i),s.flashTs&&Date.now()-s.flashTsthis.requestUpdate(),3360))}_syncActivityRuntime(){if(!this.hass)return;const t=new Set;for(const e of this._devices){if(e.hidden)continue;t.add(e.id);const i=this._visualSamples(e),s=this._activitySourceKey(i);let o=this._activityRt.get(e.id);if(o){if(o.sources!==s){clearTimeout(o.timer),o.sources=s,o.last={},o.flashTs=0,o.flashKind=null;for(const t of i)o.last[t.eid]=t.state}}else{o={sources:s,last:{},flashTs:0,flashKind:null,timer:0,gen:0};for(const t of i)o.last[t.eid]=t.state;this._activityRt.set(e.id,o)}}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e))}_activityTick(){if(this.hass){this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;const e=this._visualSamples(t),i=this._activitySourceKey(e),s=this._activityRt.get(t.id);if(!s||s.sources!==i)continue;"transition"===s.flashKind&&e.some(t=>"transition"===t.activity)&&(clearTimeout(s.timer),s.flashTs=0,s.flashKind=null);let o=null;for(const t of e){const e=Er(s.last[t.eid],t);("event"===e||!o&&e)&&(o=e),s.last[t.eid]=t.state}o&&this._stampActivity(t.id,o,i)}}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=fr(this.hass.states[i||""]?.state),o=pr(this.hass.states[e]?.attributes);let n=this._vacRt.get(t.id);n||(n={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,n)),s&&!n.moving&&(n.trail=[],n.lastPos=null);const r="never"!==wr(t.marker?.vacuum)&&!o?.path;!s&&n.moving&&(n.endedTs=Date.now(),r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=null),n.moving=s;const a=o?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==n.lastKey){const e=Date.now();n.jump=n.lastTs>0&&e-n.lastTs>1e4,n.lastKey=t,n.lastTs=e,r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return G;const i=e.marker?.vacuum||{},s=this._vacSource(e),o=s?pr(this.hass?.states[s]?.attributes):null,n=!!(o&&o.rooms.length>=3),r=o?.pos?wi(this._t("vac.status_found"),{name:s||""}):this._t("vac.status_none"),a=Object.keys(i.calibration||{}),l=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._saveConfig(),this.requestUpdate())};return W` + `}_vacSource(t){const e=t.marker?.vacuum;if(!1===e?.live)return null;if(e?.source&&this.hass?.states[e.source])return e.source;for(const e of t.entities||[])if(ur(this.hass?.states[e]))return e;return null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return t.map(t=>t.eid).sort().join("\n")}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s={sources:i||"",last:{},flashTs:0,flashKind:null,timer:0,gen:0},this._activityRt.set(t,s)),null!=i&&(s.sources=i),s.flashTs&&Date.now()-s.flashTsthis.requestUpdate(),3360))}_syncActivityRuntime(){if(!this.hass)return;const t=new Set;for(const e of this._devices){if(e.hidden)continue;t.add(e.id);const i=this._visualSamples(e),s=this._activitySourceKey(i);let o=this._activityRt.get(e.id);if(o){if(o.sources!==s){clearTimeout(o.timer),o.sources=s,o.last={},o.flashTs=0,o.flashKind=null;for(const t of i)o.last[t.eid]=t.state}}else{o={sources:s,last:{},flashTs:0,flashKind:null,timer:0,gen:0};for(const t of i)o.last[t.eid]=t.state;this._activityRt.set(e.id,o)}}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e))}_activityTick(){if(this.hass){this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;const e=this._visualSamples(t),i=this._activitySourceKey(e),s=this._activityRt.get(t.id);if(!s||s.sources!==i)continue;"transition"===s.flashKind&&e.some(t=>"transition"===t.activity)&&(clearTimeout(s.timer),s.flashTs=0,s.flashKind=null);let o=null;for(const t of e){const e=Ir(s.last[t.eid],t);("event"===e||!o&&e)&&(o=e),s.last[t.eid]=t.state}o&&this._stampActivity(t.id,o,i)}}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=fr(this.hass.states[i||""]?.state),o=pr(this.hass.states[e]?.attributes);let n=this._vacRt.get(t.id);n||(n={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,n)),s&&!n.moving&&(n.trail=[],n.lastPos=null);const r="never"!==wr(t.marker?.vacuum)&&!o?.path;!s&&n.moving&&(n.endedTs=Date.now(),r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=null),n.moving=s;const a=o?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==n.lastKey){const e=Date.now();n.jump=n.lastTs>0&&e-n.lastTs>1e4,n.lastKey=t,n.lastTs=e,r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return G;const i=e.marker?.vacuum||{},s=this._vacSource(e),o=s?pr(this.hass?.states[s]?.attributes):null,n=!!(o&&o.rooms.length>=3),r=o?.pos?wi(this._t("vac.status_found"),{name:s||""}):this._t("vac.status_none"),a=Object.keys(i.calibration||{}),l=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._saveConfig(),this.requestUpdate())};return W`
${r}
@@ -3910,7 +3941,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?W`
${wi(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:G} `:G} -
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return o=e.mapId,n=s,"default"!==o?o:null!=n?String(n):"default";var o,n}_vacSaveMatrix(t,e,i,s){const o=this._devices.find(e=>e.id===t),n=o?this._vacEnsureMarker(o):this._serverCfg?.markers?.find(e=>e.id===t);if(!n)return!1;const r={...n.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).map(t=>({r:t,poly:Pe(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=We(e);return{name:t.name,cx:i[0],cy:i[1]}}),n=mr(i.rooms,o);n?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix)&&(n.residual>50&&this._showToast(wi(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._showToast(wi(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),o=t.marker?.vacuum?.calibration?.[s],n=this._spaceModel(t.space),r=n?.vb||[0,0,sl,sl],a=o&&6===o.length&&function(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}(o)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1,h={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},c=yr(h),[d,p]=hr(c,(o+n)/2,(r+a)/2);return h.ox=e[0]+e[2]/2-d,h.oy=e[1]+e[3]/2-p,h}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,yr(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=pr(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),o={...e.p,...t};this._vacFit={...e,p:br(o,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,o=s?.clientWidth||1,n=s?.clientHeight||1;return[e/o*t.w,i/n*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[o,n]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+o,oy:s.p0.oy+n}};else{const t=pr(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=yr(s.p0),[a,l]=hr(r,e[0],e[1]),[h,c]=hr(r,s.fx,s.fy),d=Math.hypot(a-h,l-c)||1,[p,u]=[2*a-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*n-c)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:br(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return G;const i=pr(this.hass?.states[e.source]?.attributes);if(!i)return G;const s=yr(e.p),o=[],n=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>hr(s,t,e));e.forEach(([t,e])=>{n.push(t),r.push(e)});const[i,a]=hr(s,t.cx,t.cy);o.push(B` +
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return o=e.mapId,n=s,"default"!==o?o:null!=n?String(n):"default";var o,n}_vacSaveMatrix(t,e,i,s){const o=this._devices.find(e=>e.id===t),n=o?this._vacEnsureMarker(o):this._serverCfg?.markers?.find(e=>e.id===t);if(!n)return!1;const r={...n.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).map(t=>({r:t,poly:Pe(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=We(e);return{name:t.name,cx:i[0],cy:i[1]}}),n=mr(i.rooms,o);n?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix)&&(n.residual>50&&this._showToast(wi(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._showToast(wi(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),o=t.marker?.vacuum?.calibration?.[s],n=this._spaceModel(t.space),r=n?.vb||[0,0,ol,ol],a=o&&6===o.length&&function(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}(o)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1,h={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},c=yr(h),[d,p]=hr(c,(o+n)/2,(r+a)/2);return h.ox=e[0]+e[2]/2-d,h.oy=e[1]+e[3]/2-p,h}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,yr(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=pr(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),o={...e.p,...t};this._vacFit={...e,p:br(o,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,o=s?.clientWidth||1,n=s?.clientHeight||1;return[e/o*t.w,i/n*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[o,n]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+o,oy:s.p0.oy+n}};else{const t=pr(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=yr(s.p0),[a,l]=hr(r,e[0],e[1]),[h,c]=hr(r,s.fx,s.fy),d=Math.hypot(a-h,l-c)||1,[p,u]=[2*a-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*n-c)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:br(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return G;const i=pr(this.hass?.states[e.source]?.attributes);if(!i)return G;const s=yr(e.p),o=[],n=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>hr(s,t,e));e.forEach(([t,e])=>{n.push(t),r.push(e)});const[i,a]=hr(s,t.cx,t.cy);o.push(B` ${t.name}`)}let a=G;if(i.pos){const[e,o]=hr(s,i.pos.x,i.pos.y);a=B``}const l=[];if(n.length){const e=(()=>{const t=s[0]*s[4]-s[1]*s[3];return(e,i)=>[(s[4]*(e-s[2])-s[1]*(i-s[5]))/t,(-s[3]*(e-s[2])+s[0]*(i-s[5]))/t]})(),i=Math.min(...n),o=Math.max(...n),a=Math.min(...r),h=Math.max(...r),c=.022*t.w,d=c/4;for(const[t,s,n,r]of[[i,a,o,h],[o,a,i,h],[o,h,i,a],[i,h,o,a]]){const i=e(n,r);l.push(B` `)}}return W`
`)}return this._vacLastView=e,o.length&&!this._vacRaf&&this._vacRafLoop(),o.length||n.length?W` ${n.length?B`${n}`:G} - ${o}`:G}_renderDevice(t,e,i=!0){const s=this._pos(t),o=(s.x-e.x)/e.w*100,n=(s.y-e.y)/e.h*100,r=this._deviceVisual(t),a=t.hidden?"":this._stateClass(t,r),l=t.hidden?null:this._liveTemp(t),h=t.hidden?null:this._liveHum(t),c=!i||t.virtual||t.hidden?null:Jr(this.hass,t.entities),d=t.marker,p=this._displayOf(t),u="icon_ripple"===p&&!t.hidden&&this._config?.live_states&&"alarm"!==r.status?r.activity:"none",_=this._actEntity(t),m=_?this.hass.states[_]:void 0,g="value"!==p||t.hidden||null!=l||null!=h||!m||isNaN(parseFloat(m.state))?null:gi(this.hass,_),f="value"!==p||t.hidden?null:null!=l?l+"°":null!=h?h+"%":g?g.formatted?fi(g,m.attributes?.unit_of_measurement):parseFloat(m.state)+(m.attributes?.unit_of_measurement?" "+m.attributes.unit_of_measurement:""):null,v=_?_.split(".")[0]:null,y=this._config?.live_states&&!t.hidden?Ni(t.icon,v,m?.attributes?.device_class,m?.state,!!d?.icon):t.icon,b=Kr(this.hass,[t]),w=this._config?.live_states&&!t.hidden&&b.map(t=>function(t){if(!t||"on"!==t.state)return null;const e=t.attributes?.rgb_color;return Array.isArray(e)&&e.length>=3&&e.every(t=>Number.isFinite(t))?`rgb(${e[0]}, ${e[1]}, ${e[2]})`:null}(this.hass.states[t.eid])).find(t=>t)||null,k=Number(d?.size)>0?Number(d.size):1,$=Number(d?.angle)||0,x=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${o}%`,`top:${n}%`];return 1!==k&&S.push(`--dev-scale:${k}`),"icon_ripple"===p&&(S.push(`--ripple-scale:${x}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):w&&S.push(`--ripple-color:${w}`)),W`
function(t){if(!t||"on"!==t.state)return null;const e=t.attributes?.rgb_color;return Array.isArray(e)&&e.length>=3&&e.every(t=>Number.isFinite(t))?`rgb(${e[0]}, ${e[1]}, ${e[2]})`:null}(this.hass.states[t.eid])).find(t=>t)||null,$=Number(d?.size)>0?Number(d.size):1,k=Number(d?.angle)||0,x=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${o}%`,`top:${n}%`];return 1!==$&&S.push(`--dev-scale:${$}`),"icon_ripple"===p&&(S.push(`--ripple-scale:${x}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):w&&S.push(`--ripple-color:${w}`)),W`
${"none"!==u?W``:G} ${this._newIds.has(t.id)?W``:G} - ${null!=f?W`${f}`:W``} + ${null!=f?W`${f}`:W``} ${null!=l&&null==f?W`${l}°`:G} ${null!=h&&null==f?W`${h}%`:G} ${null!=c?W`${c}`:G} -
`}_roomArea(t){const e=Pe(t);if(!e)return null;const i=this._spaceWalls,s=i.length&&t.id&&Ao(this._spaceModel().rooms,t.id,i,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||e,o=this._cleanFloor(t,s),n=this._cellCm/this._gridPitch;return bs(o.area*n*n/1e4,"mi"===this.hass?.config?.unit_system?.length)}_roomTemp(t){const e=t.settings?.temp_source;return e?aa(this.hass,e,"temp",this._markers):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?aa(this.hass,e,"hum",this._markers):t.area?this._climate().get(t.area)?.hum??null:null}_climate(){const t=this._serverCfg?.markers,e=this._climateCache;if(e&&e.h===this.hass&&e.r===this._iconRules&&e.mk===t)return e.m;const i=function(t,e,i){const s=new Map;if(!t?.entities)return s;const o=sa(i),n=new Set;for(const t of i||[]){if(t?.removed||!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&n.add(t.binding.slice(e+1))}const r=new Map;for(const[e,i]of Object.entries(t.entities)){if(i.device_id&&o.devices.has(i.device_id)||!i.device_id&&o.entities.has(e))continue;const s=i.device_id?t.devices?.[i.device_id]:null,a=i.area_id||s?.area_id||null;if(!a)continue;if(i.entity_category)continue;if(!(n.size>0&&e.startsWith("climate.")&&(n.has(e)||i.device_id&&n.has(i.device_id)))){if(_t.has(i.platform))continue;if(la.test(e))continue}let l=r.get(a);l||(l=new Map,r.set(a,l));const h=i.device_id||e;let c=l.get(h);if(!c){const o=t.states?.[e];c={name:(s?s.name_by_user||s.name:i.name||o?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},l.set(h,c)}c.ents.push(e)}for(const[i,o]of r){const r=[],a=[];for(const[i,s]of o){const o=ia(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===o||"mdi:air-filter"===o;if(l){const e=Zr(t,s.ents);null!=e&&r.push(e)}if(n.size>0&&(n.has(i)||s.ents.some(t=>n.has(t)))){const e=Xr(t,s.ents);null!=e&&r.push(e)}if(l||"mdi:water-percent"===o){const e=ta(t,s.ents);null!=e&&a.push(e)}}(r.length||a.length)&&s.set(i,{temp:r.length?Math.round(r.reduce((t,e)=>t+e,0)/r.length*10)/10:null,hum:a.length?Math.round(a.reduce((t,e)=>t+e,0)/a.length):null})}return s}(this.hass,this._iconRules,t);return this._climateCache={h:this.hass,r:this._iconRules,mk:t,m:i},i}_resetRoomDialogFields(){this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1}_openRoomEdit(t){t.id&&(this._roomEditId=t.id,this._nameSel=t.name||"",this._areaSel=t.area||"",this._roomFill=t.settings?.fill_mode||"",this._roomTempSrc=t.settings?.temp_source||"",this._roomHumSrc=t.settings?.hum_source||"",this._roomNameScale=Ki(t.settings?.name_scale),this._roomLabelScale=Ki(t.settings?.label_scale),this._roomSrcOpen=null,this._roomSrcFilter="",this._roomDialog=!0)}_roomSettingsFromDialog(){const t={};return this._roomFill&&(t.fill_mode=this._roomFill),this._roomTempSrc&&(t.temp_source=this._roomTempSrc),this._roomHumSrc&&(t.hum_source=this._roomHumSrc),1!==this._roomNameScale&&(t.name_scale=this._roomNameScale),1!==this._roomLabelScale&&(t.label_scale=this._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this._curSpaceCfg,e=t?.rooms.find(t=>t.id===this._roomEditId);if(!e)return this._roomDialog=!1,void(this._roomEditId=null);e.name=this._nameSel.trim()||e.name,e.area=this._areaSel||null;const i=this._roomSettingsFromDialog();i?e.settings=i:delete e.settings,this._saveConfig(),this._roomDialog=!1,this._roomEditId=null,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate(),this._showToast(this._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.hass,e=sa(this._markers),i=this._roomSrcFilter.trim().toLowerCase(),s=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();i&&!t.toLowerCase().includes(i)||s.push({value:"device:"+o.id,label:t,sub:o.model||this._t("marker.sub_device")})}for(const[o,n]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||n.hidden)continue;if(oa(t,o,e))continue;const r=n.name||t.states[o]?.attributes?.friendly_name||o;i&&!(r+" "+o).toLowerCase().includes(i)||s.push({value:"entity:"+o,label:r,sub:o})}return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),i=t.slice(0,e),s=t.slice(e+1);return"device"===i?this.hass.devices[s]?.name_by_user||this.hass.devices[s]?.name||s:this.hass.entities[s]?.name||this.hass.states[s]?.attributes?.friendly_name||s}_labelPos(t,e){const i=this._layout["rl_"+(t.id||"")];if(i&&i.s===e)return{x:i.x*sl,y:i.y*sl};const s=this._snap(this._roomCenter(t));return{x:s[0],y:s[1]}}_labelDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=this._labelPos(e,i);this._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:s.x,oy:s.y,moved:!1},ll(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.id||"");if(!this._drag||this._drag.id!==s)return;const o=this._stageEl;if(!o)return;const n=this._spaceModel(i).vb,r=o.getBoundingClientRect(),a=this._viewOr(n),l=(t.clientX-this._drag.sx)/r.width*a.w,h=(t.clientY-this._drag.sy)/r.height*a.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0);const c=_n(this._drag.ox+l),d=_n(this._drag.oy+h);this._savePos({id:s,space:i},c,d)}_labelUp(t){const e="rl_"+(t.id||"");if(!this._drag||this._drag.id!==e)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&window.setTimeout(()=>this._drag=null,0)}_labelScale(t){const e=this._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=t.target.closest(".roomlabel");if(!s)return;const o=s.getBoundingClientRect(),n=o.left+o.width/2,r=o.top+o.height/2,a=Math.max(8,Math.hypot(t.clientX-n,t.clientY-r));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:n,cy:r,d0:a},ll(t)}_rlResizeMove(t){const e=this._rlResize;if(!e)return;t.stopPropagation();const i=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),s=Math.min(3,Math.max(.5,e.k0*(i/e.d0))),o=this._layout[e.id];if(o)this._layout={...this._layout,[e.id]:{...o,k:s}};else{const t=e.id.slice(3),i=this._spaceModel(e.space).rooms.find(e=>e.id===t);if(!i)return;const o=this._labelPos(i,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:o.x/sl,y:o.y/sl,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return G;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=We(t.poly),this._gearPtCache.set(t.poly,s))):null!=t.x&&null!=t.y&&(s=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!s)return G;const o=(s[0]-i.x)/i.w*100,n=(s[1]-i.y)/i.h*100;return W``}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return G;const o=this._labelPos(t,e.id),n=(o.x-i.x)/i.w*100,r=(o.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),h=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&h.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&h.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&h.push(W`${e}`)}if(s.labelLight){const e=(c=Kr(this.hass,this._devices,t)).length?{on:c.filter(t=>t.on).length,total:c.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});h.push(W`${t}`)}}}var c;return W`
`}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return G;const o=this._labelPos(t,e.id),n=(o.x-i.x)/i.w*100,r=(o.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),h=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&h.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&h.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&h.push(W`${e}`)}if(s.labelLight){const e=(c=Yr(this.hass,this._devices,t)).length?{on:c.filter(t=>t.on).length,total:c.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});h.push(W`${t}`)}}}var c;return W`
this._labelDown(i,t,e.id)} @@ -3974,12 +4005,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointerup=${()=>this._rlResizeUp()} @pointercancel=${()=>this._rlResizeUp()}>`):G}
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"!==this._tool&&"partition"!==this._tool||!this._path.length||this._contourClosed?"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:this._path[this._path.length-1]:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,o=(i[1]-t.y)/t.h*100,n=Yi(e,i),r=Math.round(10*n)/10,a=Ji(n);return W`
- ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,o]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-o)<1e-6)return null;const n=(e+s)/2,r=(i+o)/2;if("line"===t.kind){const e=Yi(t.a,t.b);return{x:n,y:r,on45:Ji(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,o]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(o-i)/2]);return{x:n,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(o-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:n,y:r,on45:!1,text:`${a} × ${l} · ${bs(Us(Math.abs(s-e),this._cellCm,this._gridPitch)*Us(Math.abs(o-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=sl,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Pe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return B``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,o=null;for(const n of e)if(!(Math.abs(n[0]-t[0])<1e-6&&Math.abs(n[1]-t[1])<1e-6)){if(Math.abs(n[0]-t[0])<=i){const e=Math.abs(n[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!o||e + ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,o]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-o)<1e-6)return null;const n=(e+s)/2,r=(i+o)/2;if("line"===t.kind){const e=Yi(t.a,t.b);return{x:n,y:r,on45:Ji(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,o]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(o-i)/2]);return{x:n,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(o-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:n,y:r,on45:!1,text:`${a} × ${l} · ${bs(Us(Math.abs(s-e),this._cellCm,this._gridPitch)*Us(Math.abs(o-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=ol,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Pe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return B``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,o=null;for(const n of e)if(!(Math.abs(n[0]-t[0])<1e-6&&Math.abs(n[1]-t[1])<1e-6)){if(Math.abs(n[0]-t[0])<=i){const e=Math.abs(n[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!o||e ${i.map(e=>{const[i,n,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return B` `})} `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return B``}_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._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ne(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&!oa(this.hass,t,sa(this._markers))}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,o=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,n=this._openingAmt(t),r=n>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a=s.length?Fo(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,sl):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,c=t.flip_v?-1:1;let d,p=0,u=0;if(a.cm>0&&(a.ox||a.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);p=a.ox*i-a.oy*s,u=a.ox*s+a.oy*i,u*=c,p*=h}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``}_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._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ne(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&!na(this.hass,t,oa(this._markers))}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,o=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,n=this._openingAmt(t),r=n>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a=s.length?Fo(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,ol):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,c=t.flip_v?-1:1;let d,p=0,u=0;if(a.cm>0&&(a.ox||a.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);p=a.ox*i-a.oy*s,u=a.ox*s+a.oy*i,u*=c,p*=h}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``:G;d=B` ${this._t("opening.length_label")} {const i=Wa(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> + @input=${e=>{const i=Ba(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> ${e(this._contactCandidates(),t.contact,e=>this._openingDialog={...t,contact:e})} @@ -4309,7 +4340,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @click=${()=>this._setMode("view")}> -
`}_cardEntities(t){const e=this.hass,i=[],s=new Set,o=t=>{if(!t||s.has(t)||!e.states[t])return;const o=e.entities[t];if("config"===o?.entity_category||"diagnostic"===o?.entity_category)return;s.add(t);const n=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(n)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(n)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(n)&&i.push({eid:t,kind:"value"})};for(const i of Kr(e,[t]))o(i.eid);t.primary&&o(t.primary);for(const e of t.entities)o(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?gi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Fi);return W``}_cardEntities(t){const e=this.hass,i=[],s=new Set,o=t=>{if(!t||s.has(t)||!e.states[t])return;const o=e.entities[t];if("config"===o?.entity_category||"diagnostic"===o?.entity_category)return;s.add(t);const n=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(n)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(n)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(n)&&i.push({eid:t,kind:"value"})};for(const i of Yr(e,[t]))o(i.eid);t.primary&&o(t.primary);for(const e of t.entities)o(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?gi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Fi);return W`this._infoCard=null}>
${(()=>{const e=this._cardEntities(t);return e.length?W`
@@ -4344,8 +4375,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t.primary?W``:G} - - +
`}_renderMarkerDialog(){const t=this._markerDialog,e="virtual"===t.bindingMode,i=this._bindingCandidates(),s=this._bindingEntities(t.binding),o=(()=>{if(e)return null;const s=i.find(e=>e.value===t.binding);if(s)return s.label;const[o,n]=t.binding.split(":");return"device"===o?this.hass.devices[n]?.name_by_user||this.hass.devices[n]?.name||n:this.hass.states[n]?.attributes?.friendly_name||n})();return W`
@@ -4385,7 +4415,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @input=${e=>this._markerDialog={...t,bindingFilter:e.target.value}} />
${i.map(e=>W`
this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:Wr(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> + @click=${()=>this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:Br(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> ${e.label}${e.sub}
`)} ${i.length?G:W`
${this._t("marker.nothing_found")}
`} @@ -4438,7 +4468,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .value=${t.controlsFilter} @input=${e=>this._markerDialog={...t,controlsFilter:e.target.value}} /> ${t.controlsFilter.trim()?W`
- ${Object.keys(this.hass.states).filter(e=>Wr(t.binding,[e],s).length>0&&!t.controls.includes(e)).filter(e=>{const i=t.controlsFilter.trim().toLowerCase(),s=String(this.hass.states[e]?.attributes?.friendly_name||"");return e.toLowerCase().includes(i)||s.toLowerCase().includes(i)}).slice(0,8).map(e=>W`
`)}
-
`:G}`:G}`}_renderRoomDialog(){const t=!!this._roomEditId,e=[...this._freeAreas];if(t&&this._areaSel&&!e.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&e.unshift(t)}return W``:G}`:G}`}_renderRoomDialog(){const t=!!this._roomEditId,e=!!this._areaSel||!!this._nameSel.trim(),i=[...this._freeAreas];if(t&&this._areaSel&&!i.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&i.unshift(t)}return W`
@@ -4763,7 +4793,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @@ -4794,16 +4824,11 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t?W``:W` - ${this._pendingSplit?G:W``:W`${this._pendingSplit?G:W``} - `}
-
`}}hl.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},hl.ZOOM_MAX=8,hl.ZOOM_MIN=1/3,hl._touchSeen=!1,hl._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,hl.styles=_a,customElements.get("houseplan-card")||customElements.define("houseplan-card",hl),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${Fa} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); +
`}}cl.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},cl.ZOOM_MAX=8,cl.ZOOM_MIN=1/3,cl._touchSeen=!1,cl._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,cl.styles=ma,customElements.get("houseplan-card")||customElements.define("houseplan-card",cl),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${La} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/custom_components/houseplan/manifest.json b/custom_components/houseplan/manifest.json index f340cae6..90b97ecd 100755 --- a/custom_components/houseplan/manifest.json +++ b/custom_components/houseplan/manifest.json @@ -16,5 +16,5 @@ "issue_tracker": "https://github.com/Matysh/houseplan-card/issues", "requirements": [], "single_config_entry": true, - "version": "1.60.2-beta.1" + "version": "1.60.2-beta.2" } diff --git a/demo/smoke_card_controls.mjs b/demo/smoke_card_controls.mjs index 5099ca4b..728e3e9d 100644 --- a/demo/smoke_card_controls.mjs +++ b/demo/smoke_card_controls.mjs @@ -15,6 +15,7 @@ const res = await page.evaluate(async () => { out.hasDev = !!dev; c._infoCard = dev; await c.updateComplete; await new Promise((resolve) => requestAnimationFrame(resolve)); + out.infoUsesWideShell = sr().querySelector('hp-dialog')?.hasAttribute('wide') === true; const footer = sr().querySelector('.infofooter'); const footerBox = footer?.getBoundingClientRect(); const footerButtons = [...(footer?.querySelectorAll('button') || [])]; diff --git a/demo/smoke_decor.mjs b/demo/smoke_decor.mjs index 1b0e769f..3c54da07 100644 --- a/demo/smoke_decor.mjs +++ b/demo/smoke_decor.mjs @@ -206,6 +206,31 @@ const res = await page.evaluate(async () => { out.objectDialogSavesStyle = editedProbe?.color === '#123456' && editedProbe?.width_cm === 6.5 && editedProbe?.width === undefined && c._decorShapeDialog === null; + // Erase gets a constant screen-space target around hairlines. Exercise the + // browser's actual SVG hit-test 6 px away from a sub-pixel painted stroke; + // dispatching directly to the proxy would only prove that its listener exists. + c._replaceDecor('dcprobe', { width_cm: 0.1, width: undefined }); + c._decorTool = 'erase'; await c.updateComplete; + const paintedProbe = sr().querySelector('.decorlayer line.dshape:not(.derasehit)'); + const eraseHit = sr().querySelector('.decorlayer line.derasehit'); + const matrix = paintedProbe?.getScreenCTM(); + let picked = null; + if (paintedProbe && eraseHit && matrix) { + const p1 = new DOMPoint(+paintedProbe.getAttribute('x1'), +paintedProbe.getAttribute('y1')).matrixTransform(matrix); + const p2 = new DOMPoint(+paintedProbe.getAttribute('x2'), +paintedProbe.getAttribute('y2')).matrixTransform(matrix); + const dx = p2.x - p1.x, dy = p2.y - p1.y, len = Math.hypot(dx, dy) || 1; + picked = sr().elementFromPoint((p1.x + p2.x) / 2 - (dy / len) * 6, + (p1.y + p2.y) / 2 + (dx / len) * 6); + } + out.eraseHairlineHasWideHitTarget = picked === eraseHit + && getComputedStyle(eraseHit).strokeWidth === '16px' + && getComputedStyle(eraseHit).pointerEvents === 'stroke'; + picked?.dispatchEvent(new PointerEvent('pointerdown', { + bubbles: true, composed: true, cancelable: true, pointerId: 45, button: 0, isPrimary: true, + })); + await c.updateComplete; + out.eraseWideHitOpensConfirmation = c._decorEraseConfirm?.id === 'dcprobe'; + c._decorEraseConfirm = null; c._decorSel = null; c._curSpaceCfg.decor = decorBefore; // сцена как была до пробы c._decorTool = 'select'; await c.updateComplete; diff --git a/demo/smoke_editor_tabs.mjs b/demo/smoke_editor_tabs.mjs index a33faeaa..7782186d 100644 --- a/demo/smoke_editor_tabs.mjs +++ b/demo/smoke_editor_tabs.mjs @@ -96,6 +96,8 @@ const res = await page.evaluate(async () => { // the same gesture must refuse a draft with fewer than two existing edges. const savedPath = c._path; const savedRoomDialog = c._roomDialog; + const savedNameSel = c._nameSel; + const savedAreaSel = c._areaSel; const ctrlClick = () => { let prevented = false; c._markupClick({ @@ -121,8 +123,17 @@ const res = await page.evaluate(async () => { && roomDialog.hasAttribute('wide'); out.roomDialogHasNoHorizontalScroll = !!roomBody && roomBody.scrollWidth <= roomBody.clientWidth + 1; + c._nameSel = 'No-area room'; + c._areaSel = ''; + c.requestUpdate(); + await c.updateComplete; + const noAreaSave = sr().querySelector('hp-dialog.roomdialog .room-save'); + out.noAreaUsesRegularSave = !!noAreaSave && !noAreaSave.disabled + && sr().querySelectorAll('hp-dialog.roomdialog .room-save').length === 1; c._path = savedPath; c._roomDialog = savedRoomDialog; + c._nameSel = savedNameSel; + c._areaSel = savedAreaSel; return out; }); // значения зафиксированы прогоном на v1.43.1 и сверены с кодом (audit T1) diff --git a/demo/smoke_merge_split.mjs b/demo/smoke_merge_split.mjs index 1495cf83..959d8aa2 100644 --- a/demo/smoke_merge_split.mjs +++ b/demo/smoke_merge_split.mjs @@ -61,7 +61,7 @@ out.cancelWhole = (await S()).rooms.length===5; // 4 базовых + rg await page.evaluate((p)=>window.__card._splitClick(p), await R(0.28,0.28)); await page.evaluate((p)=>window.__card._splitClick(p), await R(0.25,0.0625)); await page.evaluate((p)=>window.__card._splitClick(p), await R(0.25,0.5)); -await page.evaluate(()=>{const c=window.__card; c._nameSel='Cabinet'; c._saveRoomNoArea();}); +await page.evaluate(()=>{const c=window.__card; c._nameSel='Cabinet'; c._areaSel=''; c._saveRoom();}); s = await S(); out.splitRooms = s.rooms.length===6; // 4 базовых + rg разрезанная надвое out.bigKeepsLiving = s.rooms.some(r=>r.id==='r1' && r.area==='living_room'); diff --git a/demo/srv/assets/houseplan-card.js b/demo/srv/assets/houseplan-card.js index 81aa68d4..5c7347e0 100755 --- a/demo/srv/assets/houseplan-card.js +++ b/demo/srv/assets/houseplan-card.js @@ -1,4 +1,4 @@ -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 o=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 n=(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 o(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 o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:h,getOwnPropertyNames:c,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.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"),u.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:o}=h(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(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=[...c(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"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),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 o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),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),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(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}),(u.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,P=`<${D}>`,z=document,T=()=>z.createComment(""),R=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,N="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,O=/>/g,F=RegExp(`>|${N}(?:([^\\s"'>=/]+)(${N}*=${N}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,q=/"/g,H=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=U(1),B=U(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,h=-1):void 0===l[1]?h=-2:(h=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?q:L):r===q||r===L?r=F:r===I||r===O?r=E:(r=F,o=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+P:h>=0?(s.push(a),i.slice(0,h)+M+i.slice(h)+C+d):i+C+(-2===h?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,h]=J(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;iA(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&&R(this._$AH)?this._$AA.nextSibling.data=t:this.T(z.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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(T()),this.O(T()),this,this.options)):i=e[s],i._$AI(o),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 o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!R(t)||t!==this._$AH&&t!==j,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(T(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ht=at.litElementPolyfillSupport;ht?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ct=new WeakMap;let dt=0;class pt extends lt{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._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.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)return t.preventDefault(),t.stopImmediatePropagation(),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(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.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=ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ct.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._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ct.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ct.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ct.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.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(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=W` +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 o=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 n=(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 o(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 o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:h,getOwnPropertyNames:c,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.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"),u.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:o}=h(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(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=[...c(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"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),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 o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),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),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(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}),(u.reactiveElementVersions??=[]).push("2.1.2");const $=globalThis,k=t=>t,x=$.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,z=document,T=()=>z.createComment(""),R=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,N="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,O=/>/g,F=RegExp(`>|${N}(?:([^\\s"'>=/]+)(${N}*=${N}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,q=/"/g,H=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=U(1),B=U(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,h=-1):void 0===l[1]?h=-2:(h=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?q:L):r===q||r===L?r=F:r===I||r===O?r=E:(r=F,o=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+P:h>=0?(s.push(a),i.slice(0,h)+M+i.slice(h)+C+d):i+C+(-2===h?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,h]=J(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;iA(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&&R(this._$AH)?this._$AA.nextSibling.data=t:this.T(z.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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(T()),this.O(T()),this,this.options)):i=e[s],i._$AI(o),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 o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!R(t)||t!==this._$AH&&t!==j,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(T(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ht=at.litElementPolyfillSupport;ht?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ct=new WeakMap;let dt=0;class pt extends lt{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._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.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)return t.preventDefault(),t.stopImmediatePropagation(),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(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.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=ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ct.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._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ct.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ct.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ct.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.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(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=W` ${this.icon?W``:G} ${this.title} `;return this._useHaDialog?W`0||t===e?e:e-1}function Nt(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sh^i?1:-1;for(a=(l=o.length)<(h=n.length)?l:h,r=0;rn[r]^i?1:-1;return l==h?0:l>h^i?1:-1}function It(t,e,i,s){if(ti||t!==xt(t))throw Error(St+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Ot(t){var e=t.c.length-1;return At(t.e/Dt)==e&&t.c[e]%2!=0}function Ft(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Lt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!kt.test(d=String(t)))return o(p,d,h);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(It(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,m);if(d=String(t),h="number"==typeof t){if(0*t!=0)return o(p,d,h,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Mt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,c=d.length;la){a=c;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),h,e)}h=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(c=d.length;48===d.charCodeAt(--c););if(d=d.slice(l,++c)){if(c-=l,h&&M.DEBUG&&c>15&&(t>Pt||t!==xt(t)))throw Error(Mt+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Ft(l,r):Lt(l,r,"0");else if(n=(t=z(new M(t),e,i)).e,a=(l=Nt(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Lt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Dt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Dt,r=e,l=d[h=0],c=xt(l/p[o-r-1]%10);else if((h=$t((n+1)/Dt))>=d.length){if(!s)break t;for(;d.length<=h;d.push(0));l=c=0,o=1,r=(n%=Dt)-Dt+1}else{for(l=a=d[h],o=1;a>=10;a/=10,o++);c=(r=(n%=Dt)-Dt+o)<0?0:xt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[h+1]||(r<0?l:l%p[o-r-1]),s=i<4?(c||s)&&(0==i||i==(t.s<0?3:2)):c>5||5==c&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[h-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Dt-e%Dt)%Dt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=h,a=1,h--):(d.length=h+1,a=p[Dt-n],d[h]=r>0?xt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==h){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Ct&&(d[0]=1));break}if(d[h]+=a,d[h]!=Ct)break;d[h--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Ft(e,i):Lt(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(St+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(It(i=t[e],0,Rt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(It(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(It(i[0],-Rt,0,e),It(i[1],0,Rt,e),g=i[0],f=i[1]):(It(i,-Rt,Rt,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)It(i[0],-Rt,-1,e),It(i[1],1,Rt,e),v=i[0],y=i[1];else{if(It(i,-Rt,Rt,e),!i)throw Error(St+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(St+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(St+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(It(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(It(i=t[e],0,Rt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(St+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(St+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:$,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Rt&&o<=Rt&&o===xt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Dt)<1&&(e+=Dt),String(s[0]).length==e){for(e=0;e=Ct||i!==xt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(St+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return xt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],h=new M(u);if(null==t?t=_:It(t,0,Rt),o=$t(t/Dt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(St+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,h,c,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),k=d,f.c=e(Lt(Nt(u.c),u.e,"0"),10,n,t),f.e=f.c.length),c=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--c:(u.c=g,u.e=c,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,c=u.e),v=g[h=c+y+1],d=n/2,p=p||h<0||null!=g[h+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[h-1]||b==(u.s<0?8:7)),h<1||!g[0])s=p?Lt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=h,p)for(--n;++g[--h]>n;)g[h]=0,h||(++c,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=Lt(s,c,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,h=e%Tt,c=e/Tt|0;for(t=t.slice();l--;)a=((o=h*(n=t[l]%Tt)+(s=c*n+(r=t[l]/Tt|0)*h)%Tt*Tt+a)/i|0)+(s/Tt|0)+c*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,h,c,d,p,u,_,m,g,f,v,y,b,w,k,$,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(h=s.e-o.e)+1,a||(a=Ct,h=At(s.e/Dt)-At(o.e/Dt),S=S/Dt|0),c=0;D[c]==(C[c]||0);c++);if(D[c]>(C[c]||0)&&h--,S<0)g.push(1),d=!0;else{for(w=C.length,$=D.length,c=0,S+=2,(p=xt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=a/2&&k++;do{if(p=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*a+(f[1]||0)),(p=xt(y/k))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,$<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,c++);z(m,n+(m.e=c+h*Dt-1)+1,r,d)}else m.e=h,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,h=/^\.([^.]+)$/,c=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(c.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(h,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(St+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return Et(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return It(t,0,Rt),null==e?e=m:It(e,0,8),z(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-At(this.e/Dt))*Dt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,h,c=this;if((t=new M(t)).c&&!t.isInteger())throw Error(St+"Exponent not an integer: "+T(t));if(null!=e&&(e=new M(e)),r=t.e>14,!c.c||!c.c[0]||1==c.c[0]&&!c.e&&1==c.c.length||!t.c||!t.c[0])return h=new M(Math.pow(+T(c),r?t.s*(2-Ot(t)):+T(t))),e?h.mod(e):h;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&c.isInteger()&&e.isInteger())&&(c=c.mod(e))}else{if(t.e>9&&(c.e>0||c.e<-1||(0==c.e?c.c[0]>1||r&&c.c[1]>=24e7:c.c[0]<8e13||r&&c.c[0]<=9999975e7)))return n=c.s<0&&Ot(t)?-0:0,c.e>-1&&(n=1/n),new M(a?1/n:n);k&&(n=$t(k/Dt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Ot(t)):l=(o=Math.abs(+T(t)))%2,h=new M(u);;){if(l){if(!(h=h.times(c)).c)break;n?h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}if(o){if(0===(o=xt(o/2)))break;l=o%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Ot(t);else{if(0===(o=+T(t)))break;l=o%2}c=c.times(c),n?c.c&&c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}return s?h:(a&&(h=u.div(h)),e?h.mod(e):n?z(h,k,m,void 0):h)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:It(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Et(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Et(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Et(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&At(this.e/Dt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Et(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Et(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Dt,h=t.e/Dt,c=r.c,d=t.c;if(!l||!h){if(!c||!d)return c?(t.s=-e,t):new M(d?r:NaN);if(!c[0]||!d[0])return d[0]?(t.s=-e,t):new M(c[0]?r:3==m?-0:0)}if(l=At(l),h=At(h),c=c.slice(),a=l-h){for((n=a<0)?(a=-a,o=c):(h=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=c.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;c[i++]=0);for(e=Ct-1;s>a;){if(c[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((h=p*(h=v[--r]%g)+(a=u*h+(c=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*c,_[n--]=h%m;_[n]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Dt,r=t.e/Dt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=At(n),r=At(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Ct|0,a[e]=Ct===a[e]?0:a[e]%Ct;return o&&(a=[o].concat(a),++r),P(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return It(t,1,Rt),null==e?e=m:It(e,0,8),z(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Dt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return It(t,-9007199254740991,Pt),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,h=r.e,c=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+T(r)))||l==1/0?(((e=Nt(a)).length+h)%2==0&&(e+="0"),l=Math.sqrt(+e),h=At((h+1)/2)-(h<0||h%2),s=new M(e=l==1/0?"5e"+h:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+h)):s=new M(l+""),s.c[0])for((l=(h=s.e)+c)<3&&(l=0);;)if(n=s,s=d.times(n.plus(i(r,n,c,1))),Nt(n.c).slice(0,l)===(e=Nt(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,c=u.substr(0,n);n<_;n+=a)c+=h+u.substr(n,a);l>0&&(c+=h+u.slice(n)),p&&(c="-"+c)}s=d?c+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):c}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,h,c,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(St+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+T(l));if(!f)return new M(g);for(e=new M(u),c=s=new M(u),o=h=new M(u),_=Nt(f),r=e.e=_.length-g.e-1,e.c[0]=zt[(a=r%Dt)<0?Dt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:c:l,a=y,y=1/0,l=new M(_),h.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,c=h.plus(d.times(n=c)),h=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),h=h.plus(n.times(c)),s=s.plus(n.times(o)),h.s=c.s=g.s,p=i(c,o,r*=2,m).minus(g).abs().comparedTo(i(h,s,r,m).minus(g).abs())<1?[c,o]:[h,s],y=a,p},p.toNumber=function(){return+T(this)},p.toPrecision=function(t,e){return null!=t&&It(t,1,Rt),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Ft(Nt(i.c),n):Lt(Nt(i.c),n,"0"):10===t&&S?e=Lt(Nt((i=z(new M(i),_+n+1,m)).c),i.e,"0"):(It(t,2,x.length,"Base"),e=s(Lt(Nt(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return T(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Ht=class{key;left=null;right=null;constructor(t){this.key=t}},Ut=class extends Ht{constructor(t){super(t)}},Wt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Bt=class t extends Wt{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new Ut(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Ut(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Ut(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new Vt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Gt(this.wrap())}[Symbol.toStringTag]="[object Set]"},jt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Gt=class extends jt{getValue(t){return t.key}},Vt=class extends jt{getValue(t){return[t.key,t.key]}},Kt=t=>()=>t,Yt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Kt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Jt(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):Kt(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var Zt=t=>t,Xt=t=>{if(t){const e=new Bt(Yt(t)),i=new Bt(Yt(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new qt(0),y:new qt(0)}),o}return Zt},Qt=t=>({set:t=>{te=Qt(t)},reset:()=>Qt(t),compare:Yt(t),snap:Xt(t),orient:Jt(t)}),te=Qt(),ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),ie=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},se=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),oe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ne=t=>oe(t,t).sqrt(),re=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return se(o,s).div(ne(o)).div(ne(s))},ae=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return oe(o,s).div(ne(o)).div(ne(s))},le=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},he=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},ce=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:ve.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:re(this.point,t.point,s.point),cosine:ae(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},de=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},pe=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(h))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(h))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),c=h.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&c.isLessThan(d))return 1;if(t.isLessThan(e)&&c.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(h)?-1:l.isGreaterThan(h)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++fe,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ce.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ce(o,!0),h=new ce(n,!1);return new t(l,h,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return te.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=ie(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ee(e,r)&&0===this.comparePoint(r),h=ee(i,o)&&0===t.comparePoint(o),c=ee(e,a)&&0===this.comparePoint(a),d=ee(i,n)&&0===t.comparePoint(n);if(h&&l)return d&&!c?n:!d&&c?a:null;if(h)return c&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&c)return null;if(d)return n;if(c)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return he(i,s,t.x);if(s.x.isZero())return he(t,e,i.x);if(e.y.isZero())return le(i,s,t.y);if(s.y.isZero())return le(t,e,i.y);const o=se(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=se(n,e).div(o),a=se(n,s).div(o),l=t.x.plus(a.times(e.x)),h=i.x.plus(r.times(s.x)),c=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(h).div(2),y:c.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ee(s,p)?te.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ce(e,!0),n=new ce(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ce.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ce.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ye=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=te.snap({x:new qt(t[0][0]),y:new qt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;ege.run("union",t,e),$e=(t,...e)=>ge.run("intersection",t,e),xe=(t,...e)=>ge.run("difference",t,e);function Se(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Me(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function Ce(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function De(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function ze(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function Te(t){const e=[],i=new Set;for(const s of t||[]){const t=Pe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*h,y:r+l*c,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function Ae(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,h=n;for(const e of Te(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],c=Math.min(o,r),d=Math.max(o,r),p=c>0?c:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(c+d)/2;return{wallA:m(c),wallB:m(d),sideA:u,sideB:_,midA:m((c-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ne(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"door"===t?1:0;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function Ee(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Oe(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function Fe(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function We(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let h=0,c=0,d=0;for(let e=0;e1e-9?[c/(3*h),d/(3*h)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ie([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=h,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Be(t)||t[0]}function Be(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(qe(s,t,e))return s;for(let s=0;sGe(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ge(e,t,i)))}function Ye(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ze(t,e,i){for(let s=0;s1&&Ee(i[0],i[i.length-1],e)&&i.pop(),i}function Qe(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function ti(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function ei(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const di=["automation","script","scene"];const pi="—",ui="{}";function _i(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function mi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=_i(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function gi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function fi(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function vi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return pi;const n=(e?.attr||"").trim(),r=gi(t,i,n||null);if(null===r)return pi;return fi(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const yi=20;function bi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function wi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const ki="#55606c",$i=.55;function xi(t){const e=t?.settings||{},i=!t?.plan_url;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:"string"==typeof e.room_color&&/^#[0-9a-f]{6}$/i.test(e.room_color)?e.room_color:ki,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):$i,fill:["lqi","light","temp","glow"].includes(e.fill_mode)?e.fill_mode:"none",tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:"string"==typeof e.bg_color&&/^#[0-9a-f]{6}$/i.test(e.bg_color)?e.bg_color:null,hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Si(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return"string"==typeof i&&/^#[0-9a-f]{6}$/i.test(i)?i:""}const Mi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}},Ci=/^#[0-9a-f]{6}$/i;function Di(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Mi)){const s=Mi[t],o=i[t];e[t]={c:o&&"string"==typeof o.c&&Ci.test(o.c)?o.c:s.c,a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function Pi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function zi(t,e,i,s,o,n,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:Pi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}const Ti={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},Ri=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function Ai(t){for(const e of[...Object.values(Ti),...Ri])if(t===e[0]||t===e[1])return e;return null}function Ni(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Ai(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=Ti[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Ai(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Ei(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),o=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,n=i.rgb_color;if(Array.isArray(n)&&n.length>=3&&n.every(t=>Number.isFinite(t)))return{c:`rgb(${n[0]}, ${n[1]}, ${n[2]})`,bri:o};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:o}}return{c:e,bri:o}}function Ii(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,h;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),c=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*c>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+c*_*u],g=[i[0]+a*_*u,i[1]+c*_*u],f=[e[0]-a*_*u,e[1]-c*_*u],v=[i[0]-a*_*u,i[1]-c*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),k=b(g),$=b(f),x=b(v),S=Math.max(Math.min(w,k),Math.min($,x)),M=Math.min(Math.max(w,k),Math.max($,x));if(!(M-S>1e-9))return null;l=S,h=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),h=Math.atan2(i[1]-t[1],i[0]-t[0])-l;h>Math.PI;)h-=2*Math.PI;for(;h<-Math.PI;)h+=2*Math.PI}const c=o*Math.PI/180;if(Math.abs(h)>c){const t=l+h/2;h=c*Math.sign(h),l=t-h/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+h*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Oi(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>qe(l,t,1e-9))}function Fi(t){return t.startsWith("light.")||t.startsWith("switch.")}function Li(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*c+(o[1]-n[1])*d,_=(r[0]-n[0])*c+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(h,Math.max(u,_));g-m>i&&s.push([n[0]+c*m,n[1]+d*m,n[0]+c*g,n[1]+d*g])}}return s}function qi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function Hi(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*h+(s[1]-t[1])*c,a=(s[2]-t[0])*h+(s[3]-t[1])*c,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+h*p,t[1]+c*p,t[0]+h*e,t[1]+c*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+h*p,t[1]+c*p,n[0],n[1]])}return s}function Ui(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=Gi(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function Gi(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function Vi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Ki(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Yi(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ji(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Zi(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const Xi=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Qi=(t,e)=>[t[0]-e[0],t[1]-e[1]],ts=(t,e)=>[t[0]+e[0],t[1]+e[1]],es=(t,e)=>t[0]*e[0]+t[1]*e[1],is=t=>Math.hypot(t[0],t[1]);function ss(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function rs(t,e,i,s){const o=s||ns(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function as(t,e,i,s){const o=[],n=Qi(i,e),r=is(n);if(rs||c>s)continue;const d=es(Qi(n,e),a),p=es(Qi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function ls(t,e,i,s,o){const n=as(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>os(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=es(Qi(e,h),u),y=es(Qi(i,h),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wis(Qi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=is(Qi(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function cs(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],h=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],c=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=h(e);o<=c||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ps(t,e,i){if(Ve(t,e,i))return!0;if(Ge(t,e,i)||Ge(e,t,i))return!1;let s=0;try{const i=$e([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of i)t?.[0]&&(s+=Ye(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function us(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=rs(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[ts(i.a,n),ts(i.b,n)]];continue}const t=as(e.poly,i.a,i.b,o);if(!t.length)continue;const a=ls(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[ts(t,n),ts(e,n)]))}for(const t of e)os([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function _s(t,e,i){for(const s of e)for(let e=0;ei)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-i&&h+t.length/2<=a+i)return!0}return!1}function ms(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!cs(o))return!1;const l=ss(s.poly),c=ss(o);if(Math.abs(c)t.find(t=>t.id===e).poly),d=t.map(h);for(const t of ms(e,c,2*r)){const e=a.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function fs(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const h=t.filter(t=>t.id!==i).map(t=>t.poly),c=fs(l,e,h,s,o,2*a),d=c.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ms(e,[l.poly],2*a)){const e=c.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ys(t,e,i){const s=i/e;return Ye(t)*s*s/1e4}function bs(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const ws=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],ks=t=>Math.min(1,Math.max(0,t));function $s(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=ws[ws.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Ie(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Ss(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Ms(t,e,i,s){return[[t[0],t[1]],[e[0],e[1]],[e[0]+i[0]*s,e[1]+i[1]*s],[t[0]+i[0]*s,t[1]+i[1]*s]]}function Cs(t,e){try{const i=$e([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]),s=[];for(const t of i){const e=t?.[0];!Array.isArray(e)||e.length<4||s.push(e.slice(0,e.length-1).map(t=>[t[0],t[1]]))}return s}catch{return[]}}function Ds(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],h=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),c=[];for(const i of e){if(!(i.length>0))continue;const e=xs(i,t);if(!e||!Ss(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=h*i.length,$=Cs(Ms(b,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];c.push({openingId:i.id,roomId:e.roomId,polys:$,a:b,b:w,dir:l,len:k,normal:_,depth:k*x})}return c}function Ps(t){return Math.round(10*(Number(t)||0))/10}const zs={clear:1,sunny:1,"clear-night":1,windy:1,exceptional:1,partlycloudy:.7,"windy-variant":.7,cloudy:.4,overcast:.25,fog:.25,rainy:0,pouring:0,snowy:0,"snowy-rainy":0,hail:0,lightning:0,"lightning-rainy":0};const Ts=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Rs(t,e){const i=Ts(e?.north_deg);return null!==i?i:Ts(t?.north_deg)}function As(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Ns(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Es(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Is(t){const e=t?.states?.["sun.sun"]?.attributes,i=Number(e?.azimuth),s=Number(e?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Os={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Fs=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ls=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function qs(t,e,i){return Ls(e)?[t.x,t.y]:i([t.x,t.y])}const Hs=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Us=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Ws=(t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Us(n,e,i):s};function Bs(t,e){return{color:t.color,opacity:Fs(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Fs(t.fillOpacity,.25)}:{}}}function js(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ls(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Gs(t){const e=js(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Vs(t,e,i,s,o,n,r,a){const l=Ls(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*h+g*d,v=_+m*c+g*p,y=s-f,b=o-v;let w=(y*h+b*c)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),$=null;if(n){const e=w/Math.max(t.w,a),i=k/Math.max(t.h,a);$=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*$,k=t.h*$}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round(($??1)/o)*o);w=t.w*r,k=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,k=w*(t.h/Math.max(t.w,a))):(k=Math.round(k/r)*r,w=k*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,k=Math.round(k/r)*r;w=Math.max(a,w),k=Math.max(a,k);return{x:f+(e>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*d-w/2,y:v+(e>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*p-k/2,w:w,h:k,angle:Ls(t.angle)||void 0}}const Ks=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Ys=15;function Js(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Zs(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Xs(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Qs(t,e,i){const s=Number(e)>0?Number(e):5;return Zs(t)/s*i}function to(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function eo(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function io(t,e,i){const s=to((t[0]+e[0])/2,i),o=to((t[1]+e[1])/2,i),[n,r]=eo(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,h=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(h)},${o.toFixed(h)}@${l.toFixed(4)}`}function so(t,e,i,s){return 1===s?io(t,e,i):io([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function oo(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function no(t,e,i,s,o){const n=o>0?o:1;return{key:so(t,e,s,n),cm:Zs(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function ro(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function ao(t,e){const[i,s]=eo(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function lo(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function ho(t,e,i,s,o=1){if(!t?.length)return null;const n=so(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,h=(e[1]+i[1])/2,c=ao(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of ro(t,a))if(lo(e.ang,c)&&Math.hypot(e.x-l,e.y-h)<=d)return e.w;return null}function co(t,e,i,s,o=1){const n=ho(t,e,i,s,o);return n&&n.cm>0?Zs(n.cm):0}function po(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Te(e);for(const t of r)n.add(so([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=oo(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],h=n[1]-i[1],c=Math.hypot(a,h);if(c<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return lo(ao(i,n),ao(e,s))&&Ro(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ro(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!lo(ao(i,n),ao(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*h-(t[1]-i[1])*a)/c;if(o(e)>l||o(s)>l)return!1;const r=c*c,d=((e[0]-i[0])*a+(e[1]-i[1])*h)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*h)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/c});return!d})(t))&&t.cm>=1&&t.cm<=100)}function uo(t,e,i,s=8){const[o,n]=eo(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function _o(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,h=[];for(const o of t){let t="",c=null;const d=oo(o,r);if(d)for(let o=0;oa||Ro(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[h[0]+(p[0]-h[0])*e,h[1]+(p[1]-h[1])*e]};c=[g(d[0]),g(d[1])],t=so(c[0],c[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=ro([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ro(n.x,n.y,l[0],l[1],h[0],h[1])>a)continue;const g=c[0]+(d[0]-c[0])*Math.max(0,Math.min(1,m)),f=c[1]+(d[1]-c[1])*Math.max(0,Math.min(1,m)),[v,y]=eo(c,d),b=Math.max(s*r,1e-6);t=so([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),h.push(c?no(c[0],c[1],o.cm,s,r):{...o,key:t,cm:Zs(o.cm)}))}return h}function mo(t,e,i,s,o,n=1){const r=so(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,no(e,i,s,o,n)]}function go(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Mo(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Io(i,t),o=$o(i,t);if(s&&o)return`${xo(s)} ${xo(So(o))}`}}let s="";for(let i=0;i.05)&&Ro(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function yo(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function bo(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const h=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([h[0]+.001*a,h[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function wo(t,e){const i=t[0]*e[1]-t[1]*e[0],s=t[0]*e[0]+t[1]*e[1];return Math.abs(i)<1e-9&&s>0}function ko(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function $o(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]+p*c,e[1]+u*c],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=ko(k,b,$,w),S=Math.max(c,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}c>0&&s.push([t[o][0]+p*c,t[o][1]+u*c]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),c>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function xo(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Pe(r);if(!a||a.length<3)return null;const l=yo(s,o),h=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Pe(i);if(t)for(const e of Li(a,t,l))h.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])h.push([t[0],t[1]],[t[2],t[3]]);for(const t of n||[]){const e=oo(t,o);e&&h.push(e[0],e[1])}const c=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:c,parent:d,orig:a}}function Co(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=Zs(h.cm),r.add(h.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,h=Math.max(.5*i,1e-9)*l,c=ro(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=ao(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of c){if(r.has(t.w.key))continue;if(!lo(t.ang,a))continue;const e=oo(t.w,l);let i=!1,n=0;if(e){if(!lo(ao(e[0],e[1]),a))continue;if(Ro(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>h||Ro(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>h)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ro(t.x,t.y,s[0],s[1],o[0],o[1])>h)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Pe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Pe(s);if(t)for(const e of Li(o,t,i))n.push(e)}return n}(t,e,yo(o,a)),c=function(t,e,i,s,o){const n=yo(s,o),r=[];for(let a=0;aRo(c,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,h,s,o,a),d=Co(i,l,o,a),p=d.map((t,e)=>c[e]&&t>0?Qs(t,n,r)/2:0);return{...l,kinds:c,cms:d,offsets:p}}function Po(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(h)for(let t=0;t0&&!l.has(h.key)&&(l.add(h.key),a.push(h));const h=[];for(const a of t||[]){if(!a?.id)continue;const l=Do(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&h.push({a:[c[0],c[1]],b:[d[0],d[1]],key:so(c,d,s,r),cm:o,len:p}),t=n+1}}}h.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const c=[],d=new Set,p=new Set,u=4*yo(s,r);for(const t of h){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&lo(ao(e.a,e.b),ao(t.a,t.b))&&Ro(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ro(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)));return c}function To(t,e,i,s,o,n,r,a=1){const l=yo(o,a),h=(s[0]+s[2])/2,c=(s[1]+s[3])/2,d=ao([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Po(t,e,i,o,n,r,a)){if(!lo(ao(s.a,s.b),d))continue;const t=Ro(h,c,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),h=Pe(l);if(!h||h.length<3)return null;if(!i?.length)return h.map(t=>[t[0],t[1]]);const c=Do(t,e,i,s,o,n,r,a);return c&&c.offsets.some(t=>t>0)&&$o(c.poly,c.offsets)||h.map(t=>[t[0],t[1]])}function No(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Eo(t,e,i,s=[],o,n,r,a=1,l=[]){if(!e?.length&&!l.length)return null;const h=[];let c=0;for(const s of t||[]){if(!s?.id)continue;const l=Do(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(c=Math.max(c,2*t));const d=Io(l.poly,l.offsets),p=$o(l.poly,l.offsets);d&&h.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));c=Math.max(c,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*yo(s,r),l=new Map;for(const a of Po(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const h=[...l.values()];if(h.length<2)return[];const c=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])c.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||c.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of c){const e=h.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),c,u,p]:[t.slice(),p,u,c])}}return d}(t,e,i,o,n,r,a);try{const t=t=>{const e=No(t.outset);return t.inset?xe(e,No(t.inset)):e};let e=h.length?t(h[0]):null;for(let i=1;i0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),n=Math.sin(i),r=-n,l=s,h=t.length/2,d=1.25*Math.max(c,o*a),p=[[t.x-s*h-r*d,t.y-n*h-l*d],[t.x+s*h-r*d,t.y+n*h-l*d],[t.x+s*h+r*d,t.y+n*h+l*d],[t.x-s*h+r*d,t.y-n*h+l*d]];e&&(e=xe(e,No(p)))}for(const t of l)t.length<3||(e=e?ke(e,No(t)):[No(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+xo(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:c,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(!h||h.poly.length<3||!h.offsets.some(t=>t>0))continue;const c=Io(h.poly,h.offsets),d=$o(h.poly,h.offsets);if(!c||!d)continue;const p=`${xo(c)} ${xo(So(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=so(h.poly[t],h.poly[(t+1)%h.poly.length],s,r),_=h.kinds[t]||"outer",m=h.cms[t],g=Qs(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),h=l.map(t=>xo(t)).join(" ");return s.length||h?{d:[s.map(t=>t.d).join(" "),h].filter(Boolean).join(" "),depthUnits:c,fillRule:"nonzero"}:null}}function Io(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);So(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-h*r],n=[e[0]-c*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=ko(k,b,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-h*r]),a>0&&s.push([t[o][0]-c*a,t[o][1]-d*a])}return s.length>=3?s:null}function Oo(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const h=Pe(l);if(h&&h.length>=3){const c=Do(t,l.id,e,i,s,o,n,r),d=c&&c.offsets.some(t=>t>0)?Io(c.poly,c.offsets):null,p=d||h;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Fo(t,e,i,s,o,n,r=1){let a=null;for(const i of t||[]){const t=Pe(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=co(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const h=Qs(l,o,n),c=Pe(a.room),[d,p]=bo(c,a.edge),u=e.flip_v?-1:1,_=h/2;return{ox:d*_*u,oy:p*_*u,cm:l}}const Lo=150;function qo(t){return Number.isFinite(t)?Math.max(1,Math.min(Lo,t)):1}function Ho(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Uo=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Wo(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Bo(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=Qs(i,s,o)/2,h=-r/a*l,c=n/a*l;return[[t[0]+h,t[1]+c],[e[0]+h,e[1]+c],[e[0]-h,e[1]-c],[t[0]-h,t[1]-c]]}function jo(t,e,i){const s=Number(e)>0?Number(e):5,o=qo(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Ho(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*h-e*c,r+t*c+e*h])}function Go(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Uo(t));return e.length?ke(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return xe(Uo(t),i)}catch{}let i=[Uo(t)];for(const t of e)if(!(t.length<3))try{i=xe(i,Uo(t))}catch{}return i}function Ko(t){let e=0;for(const i of t||[])if(i?.length){e+=Ye(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jo(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zo(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const h=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+h*r),e[1]-(i[1]+h*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*c+h*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+h/p*g],v=[e[0]+c/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function Zo(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function Xo(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(qo(t.cm)-qo(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Ho(t.angle)-Ho(e.angle));return Math.min(s,90-s)<=1e-6}const Qo=1e3;function tn(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const en=.01,sn=100;function on(t,e=1e3){const i=tn(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(sn,Math.max(en,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(sn,Math.max(en,n)):o,l=Number.isFinite(r)&&r>0?Math.min(sn,Math.max(en,r)):o,h=Number(t?.plan_x),c=Number(t?.plan_y),d=Ls(t?.plan_angle);return{x:i.x+(Number.isFinite(h)?mn(h):0)*e,y:i.y+(Number.isFinite(c)?mn(c):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function nn(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function rn(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qo,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Qo,i[1]*e,i[2]*Qo,i[3]*e],bg:t.plan_url?{href:Gi(t.plan_url),...on(t,Qo)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...nn(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Qo:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qo:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qo,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qo,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qo,t.a[1]*e],b:[t.b[0]*Qo,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*Qo,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Ho(t.angle)}}))}}):[]}const an=5e3,ln=an*Qo,hn=240,cn=Qo/hn,dn=1/hn;function pn(t){if(!Number.isFinite(t))return t;const e=Math.round(t*hn/Qo)*Qo/hn;return Math.abs(e-t)<=1e-9*cn?t:e}function un(t){return{x:pn(t.x),y:pn(t.y)}}function _n(t){return Number.isFinite(t)?Math.min(ln,Math.max(-ln,t)):0}function mn(t){return Number.isFinite(t)?Math.min(an,Math.max(-an,t)):0}const gn=1/3,fn=200;function vn(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function yn(t){return t.poly&&t.poly.length?vn(t.poly):null==t.x||null==t.y?null:vn([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function bn(t,e){const i=[];for(const e of t.rooms||[]){const t=yn(e);t&&i.push(t)}if(t.bg){const e=vn(js(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=vn([t]);e&&i.push(e)}else i.push(t);return i}const wn=t=>{if(!t.length)return 0;const e=[...t].sort((t,e)=>t-e),i=e.length>>1;return e.length%2?e[i]:(e[i-1]+e[i])/2},kn=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function $n(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=fn),n<30&&(s=s+n/2-100,n=fn);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function xn(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ln&&Math.abs(t.maxX)<=ln&&Math.abs(t.minY)<=ln&&Math.abs(t.maxY)<=ln);if(!n.length)return{core:null,all:null,outliers:0};const r=kn(n);if(n.length<4){const t=$n(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),h=wn(a),c=wn(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-h),Math.abs(l[e]-c))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*gn?n.filter((t,e)=>!u[e]):n;return{core:$n(kn(m)||r,i),all:$n(r,i),outliers:m===n?0:_}}function Sn(t,e,i=.05){const s=xn(bn(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,Qo,Qo];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Mn(t){const e=Sn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Cn(t){const e=[];for(const i of t.rooms||[]){const t=yn(i);t&&e.push(t)}const i=xn(e,{pad:0}).core;return i?Math.max(Qo,Math.min(ln,Math.max(i.w,i.h))):Qo}function Dn(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Cn(e)*n/o}const Pn=[1,2,5,10,20,50,100,200,500,1e3];function zn(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Tn(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)]}const Rn=["furniture","appliance","sanitary","other"],An=[{id:"sofa",group:"furniture",w:220,h:90,g:[["r",0,0,1,1],["l",.09,.26,.91,.26],["l",.09,.26,.09,1],["l",.91,.26,.91,1],["l",.5,.26,.5,1]]},{id:"armchair",group:"furniture",w:90,h:85,g:[["r",0,0,1,1],["l",.14,.28,.86,.28],["l",.14,.28,.14,1],["l",.86,.28,.86,1]]},{id:"coffee_table",group:"furniture",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",w:120,h:60,g:[["r",0,0,1,1],["r",.63,.07,.31,.86],["l",.63,.5,.94,.5]]},{id:"bed_double",group:"furniture",w:160,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.06,.1,.4,.15],["r",.54,.1,.4,.15],["l",0,.33,1,.33]]},{id:"bed_single",group:"furniture",w:90,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.15,.1,.7,.15],["l",0,.33,1,.33]]},{id:"nightstand",group:"furniture",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["e",.29,.31,.15,.15],["e",.71,.31,.15,.15],["e",.29,.71,.15,.15],["e",.71,.71,.15,.15]]},{id:"dishwasher",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["r",.1,.12,.8,.76],["e",.5,.5,.27,.27],["e",.5,.5,.13,.13]]},{id:"washer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["e",.5,.57,.14,.14]]},{id:"dryer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["p",.36,.5,.5,.64,.64,.5]]},{id:"tv",group:"appliance",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",w:90,h:25,g:[["r",0,0,1,1],["l",.05,.55,.95,.55],["l",.05,.79,.95,.79]]},{id:"water_heater",group:"appliance",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",w:90,h:90,g:[["r",0,0,1,1],["l",0,0,1,1],["l",1,0,0,1],["e",.5,.5,.08,.08]]},{id:"sink",group:"sanitary",w:60,h:45,g:[["r",0,0,1,1],["e",.5,.6,.34,.3],["e",.5,.15,.07,.07]]},{id:"kitchen_sink",group:"sanitary",w:80,h:60,g:[["r",0,0,1,1],["r",.06,.24,.44,.64],["r",.54,.24,.4,.64],["e",.5,.12,.06,.06]]},{id:"bidet",group:"sanitary",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",w:100,h:280,g:[["r",0,0,1,1],["l",0,.111,1,.111],["l",0,.222,1,.222],["l",0,.333,1,.333],["l",0,.444,1,.444],["l",0,.556,1,.556],["l",0,.667,1,.667],["l",0,.778,1,.778],["l",0,.889,1,.889],["l",.5,.93,.5,.06],["p",.38,.16,.5,.06,.62,.16]]},{id:"fireplace",group:"other",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",w:40,h:40,g:[["e",.5,.5,.22,.22],["l",.5,.28,.5,.02],["l",.5,.72,.5,.98],["l",.28,.5,.02,.5],["l",.72,.5,.98,.5],["l",.34,.34,.13,.13],["l",.66,.66,.87,.87],["l",.66,.34,.87,.13],["l",.34,.66,.13,.87]]},{id:"rug",group:"other",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],Nn=new Map(An.map(t=>[t.id,t]));function En(t){return t&&Nn.get(t)||null}function In(t){return An.filter(e=>e.group===t)}function On(t,e,i=cn,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Fn=5e-4,Ln=an;function qn(t){return Number.isFinite(t)?Math.max(Fn,Math.min(Ln,t)):Fn}const Hn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Un(t,e,i){const s=En(t);if(!(s&&e>0&&i>0))return"";const o=t=>Hn(t*e),n=t=>Hn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,h]=t;r.push(`M${o(s-l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Bn(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,h,c]=o,d=h-s,p=c-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Wn(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function jn(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Gn(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Vn(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Kn(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Vn(t.a)||!Vn(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function Yn(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Jn(t,e){return Yn(t,e).q}function Zn(t,e,i,s,o){const n=Yn(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+c*g,h+d*g]}function Xn(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt.seg)}(e,s);let n=null;for(const r of e||[]){if(!r?.id)continue;const e=Pe(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of o)if(Zi([(l[0]+h[0])/2,(l[1]+h[1])/2],t)<2*s){p=!0;break}p||(!n||dt?.id),r=Kn(e);if(r.length)return rr(r,t,i,s).map(t=>Gn(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tjn([t[0],t[1]],[t[2],t[3]],e))}function ir(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tZi(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function sr(t,e,i){let s=null;for(const o of e){const e=Zi(t,o);e<=i&&(!s||e.05)continue;const d=co(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-c[0],p[1]-c[1]);u0?l:n}(t,e,i,s,o,n);return mo(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function nr(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(Zi(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Zi([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])Zi(e,t)<=2*i&&s.push(e);return s}function rr(t,e,i,s){const o=Kn(t);if(!o.length)return[];const n=Xn(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=Gn(t,i),o=e[0],l=e[1],h=e[2]-o,c=e[3]-l,d=Math.hypot(h,c);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,h=(t[2]-o)*p+(t[3]-l)*u,c=Math.max(0,Math.min(r,h)),m=Math.min(d,Math.max(r,h));if(m-ct.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=h&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=h);c||(c={pair:t,origin:[e[0],e[1]],ux:n,uy:r,ranges:[]},l.push(c));const d=(e[0]-c.origin[0])*c.ux+(e[1]-c.origin[1])*c.uy,p=(e[2]-c.origin[0])*c.ux+(e[3]-c.origin[1])*c.uy;c.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const c=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&c.push(jn(e,o,i))}}return c}function ar(t,e,i,s){const o=Kn(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=Gn(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,h=a;for(let t=0;t{const e=Yn(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=c[0]-n[0],r=c[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(Yn([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),h=d(a);break}}n.push(jn(l,h,s))}return n}class lr{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=Gi(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{const e=Number(t);return Number.isFinite(e)?e:null};function pr(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dr(e.x)&&null!=dr(e.y)?{x:dr(e.x),y:dr(e.y),a:dr(e.a??e.angle??e.theta)}:null;let s=null;const o=t.path?.points??t.path;if(Array.isArray(o)&&o.length){s=[];for(const t of o){const e=dr(Array.isArray(t)?t[0]:t?.x),i=dr(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const n=[],r=t.rooms,a=Array.isArray(r)?r.map((t,e)=>[String(t?.id??e),t]):r&&"object"==typeof r?Object.entries(r):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim();let s=dr(e.cx??e.center?.x),o=dr(e.cy??e.center?.y);if(null==s||null==o){const t=dr(e.x0),i=dr(e.y0),n=dr(e.x1),r=dr(e.y1);null!=t&&null!=i&&null!=n&&null!=r&&(s=(t+n)/2,o=(i+r)/2)}if(null!=s&&null!=o||(s=dr(e.x),o=dr(e.y)),i&&null!=s&&null!=o){const r={id:t,name:i,cx:s,cy:o},a=dr(e.x0),l=dr(e.y0),h=dr(e.x1),c=dr(e.y1);null!=a&&null!=l&&null!=h&&null!=c&&(r.x0=Math.min(a,h),r.y0=Math.min(l,c),r.x1=Math.max(a,h),r.y1=Math.max(l,c)),n.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s?{pos:i,path:s,rooms:n,mapId:l}:null}function ur(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const _r=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function mr(t,e){const i=new Map(e.map(t=>[_r(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(_r(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,h=0,c=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,h+=m,c+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,h]=u,c=e*(n*h-r*l)-i*(o*h-r*a)+s*(o*l-n*a);if(!Number.isFinite(c)||Math.abs(c)<1e-9)return null;const d=[(n*h-r*l)/c,(s*l-i*h)/c,(i*r-s*n)/c,(r*a-o*h)/c,(e*h-s*a)/c,(s*o-e*r)/c,(o*l-n*a)/c,(i*a-e*l)/c,(e*n-i*o)/c];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,h]),g=_([c,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:cr(n,s)}:null}function gr(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,h]=t[n],c=l-r,d=h-a,p=Math.hypot(c,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function fr(t){return"cleaning"===t||"returning"===t||"on"===t}const vr={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function yr(t){const[e,i]=vr[t.rot]||[1,0],s=t.mir?-1:1;return[t.s*e*s,-t.s*i,t.ox,t.s*i*s,t.s*e,t.oy]}function br(t,e,i,s){const[o,n]=hr(yr(e),i,s),r=yr({...t,ox:0,oy:0}),[a,l]=hr(r,i,s);return{...t,ox:o-a,oy:n-l}}function wr(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const kr={availability:"available",status:"alarm",activity:"none"},$r=new Set(["motion","vibration","sound"]),xr=new Set(["occupancy","presence"]),Sr=new Set(["door","window","garage_door","opening"]),Mr=new Set(["running","power"]),Cr=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Dr=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Pr=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),zr=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Tr=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Rr=t=>String(t??"").trim().toLowerCase();function Ar(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Rr(t?.[e]);if(Dr.has(i)||Pr.has(i))return i}return""}function Nr(t,e){const i=t?.states?.[e],s=i?Rr(i.state):"__missing__",o=String(e||"").split(".")[0],n=Rr(i?.attributes?.device_class),r={eid:e,state:s,availability:Tr(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Xi.has(e))}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return $r.has(n)?{...r,edge:"rising"}:xr.has(n)?{...r,activity:"on"===s?"presence":"none"}:Sr.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:Mr.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=Ar(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Rr):[],o=!Pr.has(s)&&(zr.has(s)||Dr.has(s)||e.includes(s));return(t?Dr.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=Ar(i.attributes);return Dr.has(a)||Dr.has(s)&&!Pr.has(s)?{...r,status:"working",activity:"running"}:r}function Er(t,e){if(!(t=>!!t&&!Tr(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}function Ir(t){const e={};for(const[i,s]of Object.entries(t.entities))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}function Or(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Fr(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const Lr=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],qr=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Hr(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of Lr){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return qr(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Rr(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return $r.has(i)||xr.has(i)||Sr.has(i)||Mr.has(i)||"moving"===i||Cr.has(i)}(t,e.eid));if(n.length)return qr(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length)return qr(r).map(t=>t.eid);const a=[];for(const t of wt)a.push(...qr(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...qr(o.filter(t=>!wt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function Ur(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>Fr(t,e.eid));if(i)return i.eid}return Hr(t,e)[0]}function Wr(t,e,i=[]){return[...new Set(Br(t,e,i))].filter(t=>Fi(t))}function Br(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function jr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).some(t=>t.startsWith("switch.")&&s.has(t))}function Gr(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Vr(t,e){const i=Wr(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light||jr(e.marker?.binding,e.marker?.controls,e.entities)){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Fi),s=t.primary&&Fi(t.primary)?t.primary:null;return(e&&Fi(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||Hr(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function Kr(t,e,i){const s=[],o=new Set;for(const n of e){if(n.hidden||null!=i&&!Gr(n,i))continue;const e=Vr(t,n);for(const{eid:i,via:r}of e)i&&!o.has(i)&&(o.add(i),s.push({eid:i,device:n,via:r,on:"on"===t.states[i]?.state}))}return s}function Yr(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function Jr(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return Qe(i)}function Zr(t,e){for(const i of e){if(!Fr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function Xr(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function Qr(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ta(t,e){for(const i of e){if(!Qr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function ea(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function ia(t,e,i,s,o){const n=bt(e,i,o);if(n!==yt)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=vt[e];if(t)return t}return null}(r)??yt}function sa(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function oa(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function na(t,e,i,s){const o=Wr(e.binding,e.controls,t.entities).filter(t=>!oa(i,t,s));t.marker=e,t.controls=o,e.hidden&&(t.hidden=!0),e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function ra(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,showAll:r,firstSpaceId:a,loc:l,iconRules:h}=t,c=!1!==o.group_lights,d=sa(s),p=ea(e,c).filter(t=>!oa(e,t.eid,d)),u=new Set(p.map(t=>t.area)),_=Ir(e),m=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||m.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),f={},v=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(m.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!o.filter_seeded)continue;const d=_[t.id]||[],p=Or(e,t,d),y=!o.filter_seeded;if(y&&!r){if(n.has(p))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===p&&t.via_device_id)continue}const b=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=b+"|"+s;let k=ia(e,b,t.model,d,h);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),y&&!r&&c&&"mdi:lightbulb"===k&&u.has(s))continue;f[w]=(f[w]||0)+1;const $=f[w]>1?b+" "+f[w]:b,x={id:t.id,name:$,model:t.model||"",area:s,space:i[s],icon:k,entities:d,bindingKind:"device",bindingRef:t.id,pdfs:[]};x.primary=Ur(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=Zr(e,d)),x.primary&&Qr(e,x.primary)&&(x.hum=ta(e,d)),v.push(x)}for(const t of p)i[t.area]&&(m.has("entity:"+t.eid)||v.push({id:"lg_"+t.eid,name:t.name,model:l("device.light_group"),area:t.area,space:i[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of s){if(t.removed)continue;if(t.hidden&&!o.filter_seeded)continue;const[s,n]=t.binding.split(":");if("device"===s){const s=e.devices[n],o=t.area||s?.area_id||"",r=o&&i[o]||t.space||a,c=s&&_[s.id]||[];let p=s?ia(e,s.name_by_user||s.name||"",s.model,c,h):"mdi:help-circle";c.some(t=>t.startsWith("lock."))&&(p="mdi:lock");const u={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:o,space:r,icon:p,entities:c,bindingKind:"device",bindingRef:n};u.primary=Ur(e,c,p),"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Zr(e,c)),u.primary&&Qr(e,u.primary)&&(u.hum=ta(e,c)),u.primary&&Qr(e,u.primary)&&(u.hum=ta(e,c)),na(u,t,e,d),v.push(u)}else if("entity"===s){if(oa(e,n,d))continue;const s=e.entities[n],o=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=o&&i[o]||t.space||a,l=e.states[n],c=s?.name||l?.attributes?.friendly_name||n;let p=ia(e,c,"",[n],h);n.startsWith("lock.")&&(p="mdi:lock");const u={id:t.id,name:c,model:"",area:o,space:r,icon:p,entities:[n],primary:n,bindingKind:"entity",bindingRef:n};"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Zr(e,[n])),Qr(e,n)&&(u.hum=ta(e,[n])),na(u,t,e,d),v.push(u)}else{const s=t.area||"",o=t.space||s&&i[s]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:s,space:o,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};na(n,t,e,d),v.push(n)}}return v}function aa(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=sa(i);return"device"===o?r.devices.has(n):"entity"===o&&oa(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?Zr(t,e):ta(t,e)}return null}const la=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");var ha={"btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","btn.no_area":"No area","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden devices (ghosted, this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_closewall":"Physical wall: click a dashed virtual stretch","title.no_area_room":"Decorative room without an HA area (e.g. a hallway)","title.choose_area":"Select a Home Assistant area","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.closewall":"Physical wall","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary stretch","history.close_boundary":"Close boundary opening","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit door or window opening","history.move_opening":"Move door or window opening","history.delete_opening":"Delete door or window opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Doors & windows: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Door / window","opening.door":"Door","opening.window":"Window","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon","display.icon_ripple":"Icon + activity","marker.display_hint":"A yellow plate means actual work. Activity adds events, presence and movement; alarms are always visible.","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide doors and windows","space.hide_openings_tip":"The symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value instead of an icon","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Show hidden","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors and windows, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","fill.glow":"Light sources (dark house, glowing lamps)","gs.glow_group":"Light-sources fill","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius (light-sources fill)","marker.glow_radius_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.openwall":"Virtual wall","title.markup_openwall":"Virtual wall — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Crosshair cursor.","toast.openwall_pick":"Click a wall shared by two rooms","toast.openwall_shared_only":"Only a shared wall between two rooms can be opened","toast.openwall_short":"Stretch too short — pick a farther point","toast.openwall_opened_span":"Virtual stretch opened","toast.openwall_closed_span":"Virtual stretch closed","toast.closewall_pick":"Click a dashed virtual stretch","toast.closewall_use_tool":"Use “Physical wall” for this stretch","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Makes the icon glow in the “Light sources” fill even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Show hidden" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.status_found":"Position source found: {name}","vac.status_none":"The integration reports no coordinates — the robot will only be shown at its base","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_res_warn":"Matched {rooms} rooms but the fit is rough — verify and refine via “Fit manually” if needed","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the sun (day/night)","gs.bg_daynight_hint":"The stage follows the sun: neutral day, warm golden hour, deep night. Needs the compass below.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the sun features stay off.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Point the arrow at north (1° steps, 15° with Shift) or type the degrees — until then the sun features stay off.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.weather":"Weather entity","gs.weather_ph":"weather.home (optional)","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug"};const ca={en:ha,ru:{"btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","btn.no_area":"Без зоны","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые устройства (полупрозрачными, только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_closewall":"Физическая стена: кликните по пунктирному виртуальному участку","title.no_area_room":"Декоративная комната без привязки к зоне (например, холл)","title.choose_area":"Выберите зону Home Assistant","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.closewall":"Физическая стена","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие участка границы","history.close_boundary":"Закрытие проёма в границе","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение дверного или оконного проёма","history.move_opening":"Перемещение дверного или оконного проёма","history.delete_opening":"Удаление дверного или оконного проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Двери и окна: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Дверь / окно","opening.door":"Дверь","opening.window":"Окно","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок","display.icon_ripple":"Значок + активность","marker.display_hint":"Жёлтая подложка означает фактическую работу. Активность добавляет события, присутствие и движение; тревога видна всегда.","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить (свет/розетки)","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери и окна не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Показать скрытые","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери и окна, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","fill.glow":"Свет по источникам (тёмный дом, пятна света)","gs.glow_group":"Заливка «Свет по источникам»","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения (заливка «Свет по источникам»)","marker.glow_radius_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.openwall":"Виртуальная стена","title.markup_openwall":"Виртуальная стена — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает виртуальный участок. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","toast.closewall_pick":"Кликните по пунктирному виртуальному участку","toast.closewall_use_tool":"Для этого участка выберите «Физическая стена»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Использовать датчик температуры устройства","marker.use_climate_temp_tip":"Кондиционеры и термостаты знают температуру комнаты (current_temperature). Показывает её плашкой у значка и учитывает в средней температуре комнаты — как обычный термометр.","marker.is_light":"Это устройство — источник света","marker.is_light_tip":"Даёт ореол в заливке «Свет по источникам» даже без light-сущности — для умного выключателя с обычными светильниками. Ореол следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Показать скрытые» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.status_found":"Источник координат найден: {name}","vac.status_none":"Интеграция не отдаёт координаты — робот будет показан только на базе","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_res_warn":"Совпало комнат: {rooms}, но привязка грубовата — проверьте и при необходимости откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за солнцем (день/ночь)","gs.bg_daynight_hint":"Фон следует за солнцем: нейтральный день, тёплый золотой час, глубокая ночь. Нужен компас ниже.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — солнечные функции выключены.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Направьте стрелку на север (шаг 1°, с Shift 15°) или введите градусы — до этого солнечные функции молчат.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.weather":"Сущность погоды","gs.weather_ph":"weather.home (необязательно)","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр"}};function da(t,e){if(e&&e in ca)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function pa(t,e,i){return wi(ca[t][e]??ha[e]??e,i)}class ua extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return da(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[],e=this._lang;return[{name:"title",selector:{text:{}}},t.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:t}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:[{value:"",label:pa(e,"editor.lang_auto")},{value:"en",label:pa(e,"editor.lang_en")},{value:"ru",label:pa(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:pa(t,"editor.title"),default_floor:pa(t,"editor.default_floor"),language:pa(t,"editor.language"),icon_size:pa(t,"editor.icon_size"),show_temperature:pa(t,"editor.show_temperature"),live_states:pa(t,"editor.live_states"),show_signal:pa(t,"editor.show_signal"),kiosk:pa(t,"editor.kiosk"),cycle:pa(t,"editor.cycle")};return W`0||t===e?e:e-1}function Nt(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sh^i?1:-1;for(a=(l=o.length)<(h=n.length)?l:h,r=0;rn[r]^i?1:-1;return l==h?0:l>h^i?1:-1}function It(t,e,i,s){if(ti||t!==xt(t))throw Error(St+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Ot(t){var e=t.c.length-1;return At(t.e/Dt)==e&&t.c[e]%2!=0}function Ft(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Lt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!$t.test(d=String(t)))return o(p,d,h);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(It(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,m);if(d=String(t),h="number"==typeof t){if(0*t!=0)return o(p,d,h,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Mt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,c=d.length;la){a=c;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),h,e)}h=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(c=d.length;48===d.charCodeAt(--c););if(d=d.slice(l,++c)){if(c-=l,h&&M.DEBUG&&c>15&&(t>Pt||t!==xt(t)))throw Error(Mt+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Ft(l,r):Lt(l,r,"0");else if(n=(t=z(new M(t),e,i)).e,a=(l=Nt(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Lt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Dt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Dt,r=e,l=d[h=0],c=xt(l/p[o-r-1]%10);else if((h=kt((n+1)/Dt))>=d.length){if(!s)break t;for(;d.length<=h;d.push(0));l=c=0,o=1,r=(n%=Dt)-Dt+1}else{for(l=a=d[h],o=1;a>=10;a/=10,o++);c=(r=(n%=Dt)-Dt+o)<0?0:xt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[h+1]||(r<0?l:l%p[o-r-1]),s=i<4?(c||s)&&(0==i||i==(t.s<0?3:2)):c>5||5==c&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[h-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Dt-e%Dt)%Dt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=h,a=1,h--):(d.length=h+1,a=p[Dt-n],d[h]=r>0?xt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==h){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Ct&&(d[0]=1));break}if(d[h]+=a,d[h]!=Ct)break;d[h--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Ft(e,i):Lt(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(St+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(It(i=t[e],0,Rt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(It(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(It(i[0],-Rt,0,e),It(i[1],0,Rt,e),g=i[0],f=i[1]):(It(i,-Rt,Rt,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)It(i[0],-Rt,-1,e),It(i[1],1,Rt,e),v=i[0],y=i[1];else{if(It(i,-Rt,Rt,e),!i)throw Error(St+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(St+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(St+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(It(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(It(i=t[e],0,Rt,e),$=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(St+e+" not an object: "+i);k=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(St+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:$,FORMAT:k,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Rt&&o<=Rt&&o===xt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Dt)<1&&(e+=Dt),String(s[0]).length==e){for(e=0;e=Ct||i!==xt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(St+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return xt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],h=new M(u);if(null==t?t=_:It(t,0,Rt),o=kt(t/Dt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(St+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,h,c,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=$,$=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),$=d,f.c=e(Lt(Nt(u.c),u.e,"0"),10,n,t),f.e=f.c.length),c=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--c:(u.c=g,u.e=c,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,c=u.e),v=g[h=c+y+1],d=n/2,p=p||h<0||null!=g[h+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[h-1]||b==(u.s<0?8:7)),h<1||!g[0])s=p?Lt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=h,p)for(--n;++g[--h]>n;)g[h]=0,h||(++c,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=Lt(s,c,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,h=e%Tt,c=e/Tt|0;for(t=t.slice();l--;)a=((o=h*(n=t[l]%Tt)+(s=c*n+(r=t[l]/Tt|0)*h)%Tt*Tt+a)/i|0)+(s/Tt|0)+c*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,h,c,d,p,u,_,m,g,f,v,y,b,w,$,k,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(h=s.e-o.e)+1,a||(a=Ct,h=At(s.e/Dt)-At(o.e/Dt),S=S/Dt|0),c=0;D[c]==(C[c]||0);c++);if(D[c]>(C[c]||0)&&h--,S<0)g.push(1),d=!0;else{for(w=C.length,k=D.length,c=0,S+=2,(p=xt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),k=D.length,w=C.length),b=k,v=(f=C.slice(0,k)).length;v=a/2&&$++;do{if(p=0,(l=e(D,f,k,v))<0){if(y=f[0],k!=v&&(y=y*a+(f[1]||0)),(p=xt(y/$))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,k<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,c++);z(m,n+(m.e=c+h*Dt-1)+1,r,d)}else m.e=h,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,h=/^\.([^.]+)$/,c=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(c.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(h,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(St+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return Et(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return It(t,0,Rt),null==e?e=m:It(e,0,8),z(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-At(this.e/Dt))*Dt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,h,c=this;if((t=new M(t)).c&&!t.isInteger())throw Error(St+"Exponent not an integer: "+T(t));if(null!=e&&(e=new M(e)),r=t.e>14,!c.c||!c.c[0]||1==c.c[0]&&!c.e&&1==c.c.length||!t.c||!t.c[0])return h=new M(Math.pow(+T(c),r?t.s*(2-Ot(t)):+T(t))),e?h.mod(e):h;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&c.isInteger()&&e.isInteger())&&(c=c.mod(e))}else{if(t.e>9&&(c.e>0||c.e<-1||(0==c.e?c.c[0]>1||r&&c.c[1]>=24e7:c.c[0]<8e13||r&&c.c[0]<=9999975e7)))return n=c.s<0&&Ot(t)?-0:0,c.e>-1&&(n=1/n),new M(a?1/n:n);$&&(n=kt($/Dt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Ot(t)):l=(o=Math.abs(+T(t)))%2,h=new M(u);;){if(l){if(!(h=h.times(c)).c)break;n?h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}if(o){if(0===(o=xt(o/2)))break;l=o%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Ot(t);else{if(0===(o=+T(t)))break;l=o%2}c=c.times(c),n?c.c&&c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}return s?h:(a&&(h=u.div(h)),e?h.mod(e):n?z(h,$,m,void 0):h)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:It(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Et(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Et(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Et(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&At(this.e/Dt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Et(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Et(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Dt,h=t.e/Dt,c=r.c,d=t.c;if(!l||!h){if(!c||!d)return c?(t.s=-e,t):new M(d?r:NaN);if(!c[0]||!d[0])return d[0]?(t.s=-e,t):new M(c[0]?r:3==m?-0:0)}if(l=At(l),h=At(h),c=c.slice(),a=l-h){for((n=a<0)?(a=-a,o=c):(h=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=c.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;c[i++]=0);for(e=Ct-1;s>a;){if(c[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((h=p*(h=v[--r]%g)+(a=u*h+(c=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*c,_[n--]=h%m;_[n]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Dt,r=t.e/Dt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=At(n),r=At(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Ct|0,a[e]=Ct===a[e]?0:a[e]%Ct;return o&&(a=[o].concat(a),++r),P(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return It(t,1,Rt),null==e?e=m:It(e,0,8),z(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Dt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return It(t,-9007199254740991,Pt),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,h=r.e,c=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+T(r)))||l==1/0?(((e=Nt(a)).length+h)%2==0&&(e+="0"),l=Math.sqrt(+e),h=At((h+1)/2)-(h<0||h%2),s=new M(e=l==1/0?"5e"+h:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+h)):s=new M(l+""),s.c[0])for((l=(h=s.e)+c)<3&&(l=0);;)if(n=s,s=d.times(n.plus(i(r,n,c,1))),Nt(n.c).slice(0,l)===(e=Nt(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,c=u.substr(0,n);n<_;n+=a)c+=h+u.substr(n,a);l>0&&(c+=h+u.slice(n)),p&&(c="-"+c)}s=d?c+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):c}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,h,c,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(St+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+T(l));if(!f)return new M(g);for(e=new M(u),c=s=new M(u),o=h=new M(u),_=Nt(f),r=e.e=_.length-g.e-1,e.c[0]=zt[(a=r%Dt)<0?Dt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:c:l,a=y,y=1/0,l=new M(_),h.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,c=h.plus(d.times(n=c)),h=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),h=h.plus(n.times(c)),s=s.plus(n.times(o)),h.s=c.s=g.s,p=i(c,o,r*=2,m).minus(g).abs().comparedTo(i(h,s,r,m).minus(g).abs())<1?[c,o]:[h,s],y=a,p},p.toNumber=function(){return+T(this)},p.toPrecision=function(t,e){return null!=t&&It(t,1,Rt),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Ft(Nt(i.c),n):Lt(Nt(i.c),n,"0"):10===t&&S?e=Lt(Nt((i=z(new M(i),_+n+1,m)).c),i.e,"0"):(It(t,2,x.length,"Base"),e=s(Lt(Nt(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return T(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Ht=class{key;left=null;right=null;constructor(t){this.key=t}},Ut=class extends Ht{constructor(t){super(t)}},Wt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Bt=class t extends Wt{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new Ut(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Ut(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Ut(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new Vt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Gt(this.wrap())}[Symbol.toStringTag]="[object Set]"},jt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Gt=class extends jt{getValue(t){return t.key}},Vt=class extends jt{getValue(t){return[t.key,t.key]}},Kt=t=>()=>t,Yt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Kt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Jt(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):Kt(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var Zt=t=>t,Xt=t=>{if(t){const e=new Bt(Yt(t)),i=new Bt(Yt(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new qt(0),y:new qt(0)}),o}return Zt},Qt=t=>({set:t=>{te=Qt(t)},reset:()=>Qt(t),compare:Yt(t),snap:Xt(t),orient:Jt(t)}),te=Qt(),ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),ie=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},se=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),oe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ne=t=>oe(t,t).sqrt(),re=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return se(o,s).div(ne(o)).div(ne(s))},ae=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return oe(o,s).div(ne(o)).div(ne(s))},le=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},he=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},ce=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:ve.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:re(this.point,t.point,s.point),cosine:ae(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},de=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},pe=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(h))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(h))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),c=h.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&c.isLessThan(d))return 1;if(t.isLessThan(e)&&c.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(h)?-1:l.isGreaterThan(h)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++fe,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ce.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ce(o,!0),h=new ce(n,!1);return new t(l,h,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return te.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=ie(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ee(e,r)&&0===this.comparePoint(r),h=ee(i,o)&&0===t.comparePoint(o),c=ee(e,a)&&0===this.comparePoint(a),d=ee(i,n)&&0===t.comparePoint(n);if(h&&l)return d&&!c?n:!d&&c?a:null;if(h)return c&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&c)return null;if(d)return n;if(c)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return he(i,s,t.x);if(s.x.isZero())return he(t,e,i.x);if(e.y.isZero())return le(i,s,t.y);if(s.y.isZero())return le(t,e,i.y);const o=se(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=se(n,e).div(o),a=se(n,s).div(o),l=t.x.plus(a.times(e.x)),h=i.x.plus(r.times(s.x)),c=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(h).div(2),y:c.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ee(s,p)?te.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ce(e,!0),n=new ce(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ce.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ce.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ye=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=te.snap({x:new qt(t[0][0]),y:new qt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;ege.run("union",t,e),ke=(t,...e)=>ge.run("intersection",t,e),xe=(t,...e)=>ge.run("difference",t,e);function Se(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Me(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function Ce(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function De(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function ze(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function Te(t){const e=[],i=new Set;for(const s of t||[]){const t=Pe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*h,y:r+l*c,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function Ae(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,h=n;for(const e of Te(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],c=Math.min(o,r),d=Math.max(o,r),p=c>0?c:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(c+d)/2;return{wallA:m(c),wallB:m(d),sideA:u,sideB:_,midA:m((c-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ne(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"door"===t?1:0;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function Ee(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Oe(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function Fe(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function We(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let h=0,c=0,d=0;for(let e=0;e1e-9?[c/(3*h),d/(3*h)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ie([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=h,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Be(t)||t[0]}function Be(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(qe(s,t,e))return s;for(let s=0;sGe(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ge(e,t,i)))}function Ye(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ze(t,e,i){for(let s=0;s1&&Ee(i[0],i[i.length-1],e)&&i.pop(),i}function Qe(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function ti(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function ei(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const di=["automation","script","scene"];const pi="—",ui="{}";function _i(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function mi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=_i(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function gi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function fi(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function vi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return pi;const n=(e?.attr||"").trim(),r=gi(t,i,n||null);if(null===r)return pi;return fi(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const yi=20;function bi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function wi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const $i="#55606c",ki=.55;function xi(t){const e=t?.settings||{},i=!t?.plan_url;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:"string"==typeof e.room_color&&/^#[0-9a-f]{6}$/i.test(e.room_color)?e.room_color:$i,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):ki,fill:["lqi","light","temp","glow"].includes(e.fill_mode)?e.fill_mode:"none",tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:"string"==typeof e.bg_color&&/^#[0-9a-f]{6}$/i.test(e.bg_color)?e.bg_color:null,hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Si(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return"string"==typeof i&&/^#[0-9a-f]{6}$/i.test(i)?i:""}const Mi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}},Ci=/^#[0-9a-f]{6}$/i;function Di(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Mi)){const s=Mi[t],o=i[t];e[t]={c:o&&"string"==typeof o.c&&Ci.test(o.c)?o.c:s.c,a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function Pi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function zi(t,e,i,s,o,n,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:Pi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}const Ti={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},Ri=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function Ai(t){for(const e of[...Object.values(Ti),...Ri])if(t===e[0]||t===e[1])return e;return null}function Ni(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Ai(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=Ti[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Ai(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Ei(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),o=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,n=i.rgb_color;if(Array.isArray(n)&&n.length>=3&&n.every(t=>Number.isFinite(t)))return{c:`rgb(${n[0]}, ${n[1]}, ${n[2]})`,bri:o};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:o}}return{c:e,bri:o}}function Ii(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,h;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),c=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*c>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+c*_*u],g=[i[0]+a*_*u,i[1]+c*_*u],f=[e[0]-a*_*u,e[1]-c*_*u],v=[i[0]-a*_*u,i[1]-c*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),$=b(g),k=b(f),x=b(v),S=Math.max(Math.min(w,$),Math.min(k,x)),M=Math.min(Math.max(w,$),Math.max(k,x));if(!(M-S>1e-9))return null;l=S,h=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),h=Math.atan2(i[1]-t[1],i[0]-t[0])-l;h>Math.PI;)h-=2*Math.PI;for(;h<-Math.PI;)h+=2*Math.PI}const c=o*Math.PI/180;if(Math.abs(h)>c){const t=l+h/2;h=c*Math.sign(h),l=t-h/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+h*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Oi(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>qe(l,t,1e-9))}function Fi(t){return t.startsWith("light.")||t.startsWith("switch.")}function Li(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*c+(o[1]-n[1])*d,_=(r[0]-n[0])*c+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(h,Math.max(u,_));g-m>i&&s.push([n[0]+c*m,n[1]+d*m,n[0]+c*g,n[1]+d*g])}}return s}function qi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function Hi(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*h+(s[1]-t[1])*c,a=(s[2]-t[0])*h+(s[3]-t[1])*c,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+h*p,t[1]+c*p,t[0]+h*e,t[1]+c*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+h*p,t[1]+c*p,n[0],n[1]])}return s}function Ui(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=Gi(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function Gi(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function Vi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Ki(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Yi(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ji(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Zi(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const Xi=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Qi=(t,e)=>[t[0]-e[0],t[1]-e[1]],ts=(t,e)=>[t[0]+e[0],t[1]+e[1]],es=(t,e)=>t[0]*e[0]+t[1]*e[1],is=t=>Math.hypot(t[0],t[1]);function ss(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function rs(t,e,i,s){const o=s||ns(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function as(t,e,i,s){const o=[],n=Qi(i,e),r=is(n);if(rs||c>s)continue;const d=es(Qi(n,e),a),p=es(Qi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function ls(t,e,i,s,o){const n=as(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>os(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=es(Qi(e,h),u),y=es(Qi(i,h),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wis(Qi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=is(Qi(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function cs(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],h=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],c=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=h(e);o<=c||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ps(t,e,i){if(Ve(t,e,i))return!0;if(Ge(t,e,i)||Ge(e,t,i))return!1;let s=0;try{const i=ke([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of i)t?.[0]&&(s+=Ye(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function us(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=rs(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[ts(i.a,n),ts(i.b,n)]];continue}const t=as(e.poly,i.a,i.b,o);if(!t.length)continue;const a=ls(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[ts(t,n),ts(e,n)]))}for(const t of e)os([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function _s(t,e,i){for(const s of e)for(let e=0;ei)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-i&&h+t.length/2<=a+i)return!0}return!1}function ms(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!cs(o))return!1;const l=ss(s.poly),c=ss(o);if(Math.abs(c)t.find(t=>t.id===e).poly),d=t.map(h);for(const t of ms(e,c,2*r)){const e=a.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function fs(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const h=t.filter(t=>t.id!==i).map(t=>t.poly),c=fs(l,e,h,s,o,2*a),d=c.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ms(e,[l.poly],2*a)){const e=c.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ys(t,e,i){const s=i/e;return Ye(t)*s*s/1e4}function bs(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const ws=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],$s=t=>Math.min(1,Math.max(0,t));function ks(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=ws[ws.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Ie(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Ss(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Ms(t,e,i,s){return[[t[0],t[1]],[e[0],e[1]],[e[0]+i[0]*s,e[1]+i[1]*s],[t[0]+i[0]*s,t[1]+i[1]*s]]}function Cs(t,e){try{const i=ke([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]),s=[];for(const t of i){const e=t?.[0];!Array.isArray(e)||e.length<4||s.push(e.slice(0,e.length-1).map(t=>[t[0],t[1]]))}return s}catch{return[]}}function Ds(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],h=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),c=[];for(const i of e){if(!(i.length>0))continue;const e=xs(i,t);if(!e||!Ss(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],$=h*i.length,k=Cs(Ms(b,w,l,$),d);if(!k.length)continue;const x=l[0]*_[0]+l[1]*_[1];c.push({openingId:i.id,roomId:e.roomId,polys:k,a:b,b:w,dir:l,len:$,normal:_,depth:$*x})}return c}function Ps(t){return Math.round(10*(Number(t)||0))/10}const zs={clear:1,sunny:1,"clear-night":1,windy:1,exceptional:1,partlycloudy:.7,"windy-variant":.7,cloudy:.4,overcast:.25,fog:.25,rainy:0,pouring:0,snowy:0,"snowy-rainy":0,hail:0,lightning:0,"lightning-rainy":0};const Ts=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Rs(t,e){const i=Ts(e?.north_deg);return null!==i?i:Ts(t?.north_deg)}function As(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Ns(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Es(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Is(t){const e=t?.states?.["sun.sun"]?.attributes,i=Number(e?.azimuth),s=Number(e?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Os={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Fs=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ls=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function qs(t,e,i){return Ls(e)?[t.x,t.y]:i([t.x,t.y])}const Hs=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Us=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Ws=(t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Us(n,e,i):s};function Bs(t,e){return{color:t.color,opacity:Fs(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Fs(t.fillOpacity,.25)}:{}}}function js(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ls(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Gs(t){const e=js(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Vs(t,e,i,s,o,n,r,a){const l=Ls(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*h+g*d,v=_+m*c+g*p,y=s-f,b=o-v;let w=(y*h+b*c)*(e>0?1:-1),$=(y*d+b*p)*(i>0?1:-1),k=null;if(n){const e=w/Math.max(t.w,a),i=$/Math.max(t.h,a);k=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*k,$=t.h*k}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round((k??1)/o)*o);w=t.w*r,$=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,$=w*(t.h/Math.max(t.w,a))):($=Math.round($/r)*r,w=$*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,$=Math.round($/r)*r;w=Math.max(a,w),$=Math.max(a,$);return{x:f+(e>0?w/2:-w/2)*h+(i>0?$/2:-$/2)*d-w/2,y:v+(e>0?w/2:-w/2)*c+(i>0?$/2:-$/2)*p-$/2,w:w,h:$,angle:Ls(t.angle)||void 0}}const Ks=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Ys=15;function Js(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Zs(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Xs(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Qs(t,e,i){const s=Number(e)>0?Number(e):5;return Zs(t)/s*i}function to(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function eo(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function io(t,e,i){const s=to((t[0]+e[0])/2,i),o=to((t[1]+e[1])/2,i),[n,r]=eo(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,h=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(h)},${o.toFixed(h)}@${l.toFixed(4)}`}function so(t,e,i,s){return 1===s?io(t,e,i):io([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function oo(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function no(t,e,i,s,o){const n=o>0?o:1;return{key:so(t,e,s,n),cm:Zs(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function ro(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function ao(t,e){const[i,s]=eo(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function lo(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function ho(t,e,i,s,o=1){if(!t?.length)return null;const n=so(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,h=(e[1]+i[1])/2,c=ao(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of ro(t,a))if(lo(e.ang,c)&&Math.hypot(e.x-l,e.y-h)<=d)return e.w;return null}function co(t,e,i,s,o=1){const n=ho(t,e,i,s,o);return n&&n.cm>0?Zs(n.cm):0}function po(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Te(e);for(const t of r)n.add(so([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=oo(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],h=n[1]-i[1],c=Math.hypot(a,h);if(c<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return lo(ao(i,n),ao(e,s))&&Ro(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ro(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!lo(ao(i,n),ao(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*h-(t[1]-i[1])*a)/c;if(o(e)>l||o(s)>l)return!1;const r=c*c,d=((e[0]-i[0])*a+(e[1]-i[1])*h)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*h)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/c});return!d})(t))&&t.cm>=1&&t.cm<=100)}function uo(t,e,i,s=8){const[o,n]=eo(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function _o(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,h=[];for(const o of t){let t="",c=null;const d=oo(o,r);if(d)for(let o=0;oa||Ro(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[h[0]+(p[0]-h[0])*e,h[1]+(p[1]-h[1])*e]};c=[g(d[0]),g(d[1])],t=so(c[0],c[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=ro([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ro(n.x,n.y,l[0],l[1],h[0],h[1])>a)continue;const g=c[0]+(d[0]-c[0])*Math.max(0,Math.min(1,m)),f=c[1]+(d[1]-c[1])*Math.max(0,Math.min(1,m)),[v,y]=eo(c,d),b=Math.max(s*r,1e-6);t=so([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),h.push(c?no(c[0],c[1],o.cm,s,r):{...o,key:t,cm:Zs(o.cm)}))}return h}function mo(t,e,i,s,o,n=1){const r=so(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,no(e,i,s,o,n)]}function go(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Mo(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Io(i,t),o=ko(i,t);if(s&&o)return`${xo(s)} ${xo(So(o))}`}}let s="";for(let i=0;i.05)&&Ro(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function yo(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function bo(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const h=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([h[0]+.001*a,h[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function wo(t,e){const i=t[0]*e[1]-t[1]*e[0],s=t[0]*e[0]+t[1]*e[1];return Math.abs(i)<1e-9&&s>0}function $o(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function ko(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]+p*c,e[1]+u*c],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=$o($,b,k,w),S=Math.max(c,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}c>0&&s.push([t[o][0]+p*c,t[o][1]+u*c]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),c>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function xo(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Pe(r);if(!a||a.length<3)return null;const l=yo(s,o),h=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Pe(i);if(t)for(const e of Li(a,t,l))h.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])h.push([t[0],t[1]],[t[2],t[3]]);for(const t of n||[]){const e=oo(t,o);e&&h.push(e[0],e[1])}const c=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:c,parent:d,orig:a}}function Co(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=Zs(h.cm),r.add(h.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,h=Math.max(.5*i,1e-9)*l,c=ro(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=ao(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of c){if(r.has(t.w.key))continue;if(!lo(t.ang,a))continue;const e=oo(t.w,l);let i=!1,n=0;if(e){if(!lo(ao(e[0],e[1]),a))continue;if(Ro(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>h||Ro(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>h)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ro(t.x,t.y,s[0],s[1],o[0],o[1])>h)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Pe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Pe(s);if(t)for(const e of Li(o,t,i))n.push(e)}return n}(t,e,yo(o,a)),c=function(t,e,i,s,o){const n=yo(s,o),r=[];for(let a=0;aRo(c,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,h,s,o,a),d=Co(i,l,o,a),p=d.map((t,e)=>c[e]&&t>0?Qs(t,n,r)/2:0);return{...l,kinds:c,cms:d,offsets:p}}function Po(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(h)for(let t=0;t0&&!l.has(h.key)&&(l.add(h.key),a.push(h));const h=[];for(const a of t||[]){if(!a?.id)continue;const l=Do(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&h.push({a:[c[0],c[1]],b:[d[0],d[1]],key:so(c,d,s,r),cm:o,len:p}),t=n+1}}}h.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const c=[],d=new Set,p=new Set,u=4*yo(s,r);for(const t of h){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&lo(ao(e.a,e.b),ao(t.a,t.b))&&Ro(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ro(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)));return c}function To(t,e,i,s,o,n,r,a=1){const l=yo(o,a),h=(s[0]+s[2])/2,c=(s[1]+s[3])/2,d=ao([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Po(t,e,i,o,n,r,a)){if(!lo(ao(s.a,s.b),d))continue;const t=Ro(h,c,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),h=Pe(l);if(!h||h.length<3)return null;if(!i?.length)return h.map(t=>[t[0],t[1]]);const c=Do(t,e,i,s,o,n,r,a);return c&&c.offsets.some(t=>t>0)&&ko(c.poly,c.offsets)||h.map(t=>[t[0],t[1]])}function No(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Eo(t,e,i,s=[],o,n,r,a=1,l=[]){if(!e?.length&&!l.length)return null;const h=[];let c=0;for(const s of t||[]){if(!s?.id)continue;const l=Do(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(c=Math.max(c,2*t));const d=Io(l.poly,l.offsets),p=ko(l.poly,l.offsets);d&&h.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));c=Math.max(c,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*yo(s,r),l=new Map;for(const a of Po(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const h=[...l.values()];if(h.length<2)return[];const c=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])c.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||c.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of c){const e=h.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),c,u,p]:[t.slice(),p,u,c])}}return d}(t,e,i,o,n,r,a);try{const t=t=>{const e=No(t.outset);return t.inset?xe(e,No(t.inset)):e};let e=h.length?t(h[0]):null;for(let i=1;i0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),n=Math.sin(i),r=-n,l=s,h=t.length/2,d=1.25*Math.max(c,o*a),p=[[t.x-s*h-r*d,t.y-n*h-l*d],[t.x+s*h-r*d,t.y+n*h-l*d],[t.x+s*h+r*d,t.y+n*h+l*d],[t.x-s*h+r*d,t.y-n*h+l*d]];e&&(e=xe(e,No(p)))}for(const t of l)t.length<3||(e=e?$e(e,No(t)):[No(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+xo(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:c,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(!h||h.poly.length<3||!h.offsets.some(t=>t>0))continue;const c=Io(h.poly,h.offsets),d=ko(h.poly,h.offsets);if(!c||!d)continue;const p=`${xo(c)} ${xo(So(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=so(h.poly[t],h.poly[(t+1)%h.poly.length],s,r),_=h.kinds[t]||"outer",m=h.cms[t],g=Qs(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),h=l.map(t=>xo(t)).join(" ");return s.length||h?{d:[s.map(t=>t.d).join(" "),h].filter(Boolean).join(" "),depthUnits:c,fillRule:"nonzero"}:null}}function Io(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);So(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-h*r],n=[e[0]-c*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=$o($,b,k,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-h*r]),a>0&&s.push([t[o][0]-c*a,t[o][1]-d*a])}return s.length>=3?s:null}function Oo(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const h=Pe(l);if(h&&h.length>=3){const c=Do(t,l.id,e,i,s,o,n,r),d=c&&c.offsets.some(t=>t>0)?Io(c.poly,c.offsets):null,p=d||h;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Fo(t,e,i,s,o,n,r=1){let a=null;for(const i of t||[]){const t=Pe(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=co(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const h=Qs(l,o,n),c=Pe(a.room),[d,p]=bo(c,a.edge),u=e.flip_v?-1:1,_=h/2;return{ox:d*_*u,oy:p*_*u,cm:l}}const Lo=150;function qo(t){return Number.isFinite(t)?Math.max(1,Math.min(Lo,t)):1}function Ho(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Uo=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Wo(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Bo(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=Qs(i,s,o)/2,h=-r/a*l,c=n/a*l;return[[t[0]+h,t[1]+c],[e[0]+h,e[1]+c],[e[0]-h,e[1]-c],[t[0]-h,t[1]-c]]}function jo(t,e,i){const s=Number(e)>0?Number(e):5,o=qo(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Ho(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*h-e*c,r+t*c+e*h])}function Go(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Uo(t));return e.length?$e(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return xe(Uo(t),i)}catch{}let i=[Uo(t)];for(const t of e)if(!(t.length<3))try{i=xe(i,Uo(t))}catch{}return i}function Ko(t){let e=0;for(const i of t||[])if(i?.length){e+=Ye(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jo(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zo(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const h=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+h*r),e[1]-(i[1]+h*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*c+h*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+h/p*g],v=[e[0]+c/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function Zo(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function Xo(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(qo(t.cm)-qo(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Ho(t.angle)-Ho(e.angle));return Math.min(s,90-s)<=1e-6}const Qo=1e3;function tn(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const en=.01,sn=100;function on(t,e=1e3){const i=tn(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(sn,Math.max(en,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(sn,Math.max(en,n)):o,l=Number.isFinite(r)&&r>0?Math.min(sn,Math.max(en,r)):o,h=Number(t?.plan_x),c=Number(t?.plan_y),d=Ls(t?.plan_angle);return{x:i.x+(Number.isFinite(h)?mn(h):0)*e,y:i.y+(Number.isFinite(c)?mn(c):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function nn(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function rn(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qo,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Qo,i[1]*e,i[2]*Qo,i[3]*e],bg:t.plan_url?{href:Gi(t.plan_url),...on(t,Qo)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...nn(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Qo:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qo:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qo,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qo,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qo,t.a[1]*e],b:[t.b[0]*Qo,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*Qo,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Ho(t.angle)}}))}}):[]}const an=5e3,ln=an*Qo,hn=240,cn=Qo/hn,dn=1/hn;function pn(t){if(!Number.isFinite(t))return t;const e=Math.round(t*hn/Qo)*Qo/hn;return Math.abs(e-t)<=1e-9*cn?t:e}function un(t){return{x:pn(t.x),y:pn(t.y)}}function _n(t){return Number.isFinite(t)?Math.min(ln,Math.max(-ln,t)):0}function mn(t){return Number.isFinite(t)?Math.min(an,Math.max(-an,t)):0}const gn=1/3,fn=200;function vn(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function yn(t){return t.poly&&t.poly.length?vn(t.poly):null==t.x||null==t.y?null:vn([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function bn(t,e){const i=[];for(const e of t.rooms||[]){const t=yn(e);t&&i.push(t)}if(t.bg){const e=vn(js(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=vn([t]);e&&i.push(e)}else i.push(t);return i}const wn=t=>{if(!t.length)return 0;const e=[...t].sort((t,e)=>t-e),i=e.length>>1;return e.length%2?e[i]:(e[i-1]+e[i])/2},$n=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function kn(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=fn),n<30&&(s=s+n/2-100,n=fn);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function xn(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ln&&Math.abs(t.maxX)<=ln&&Math.abs(t.minY)<=ln&&Math.abs(t.maxY)<=ln);if(!n.length)return{core:null,all:null,outliers:0};const r=$n(n);if(n.length<4){const t=kn(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),h=wn(a),c=wn(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-h),Math.abs(l[e]-c))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*gn?n.filter((t,e)=>!u[e]):n;return{core:kn($n(m)||r,i),all:kn(r,i),outliers:m===n?0:_}}function Sn(t,e,i=.05){const s=xn(bn(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,Qo,Qo];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Mn(t){const e=Sn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Cn(t){const e=[];for(const i of t.rooms||[]){const t=yn(i);t&&e.push(t)}const i=xn(e,{pad:0}).core;return i?Math.max(Qo,Math.min(ln,Math.max(i.w,i.h))):Qo}function Dn(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Cn(e)*n/o}const Pn=[1,2,5,10,20,50,100,200,500,1e3];function zn(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Tn(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)]}const Rn=["furniture","appliance","sanitary","other"],An=[{id:"sofa",group:"furniture",w:220,h:90,g:[["r",0,0,1,1],["l",.09,.26,.91,.26],["l",.09,.26,.09,1],["l",.91,.26,.91,1],["l",.5,.26,.5,1]]},{id:"armchair",group:"furniture",w:90,h:85,g:[["r",0,0,1,1],["l",.14,.28,.86,.28],["l",.14,.28,.14,1],["l",.86,.28,.86,1]]},{id:"coffee_table",group:"furniture",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",w:120,h:60,g:[["r",0,0,1,1],["r",.63,.07,.31,.86],["l",.63,.5,.94,.5]]},{id:"bed_double",group:"furniture",w:160,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.06,.1,.4,.15],["r",.54,.1,.4,.15],["l",0,.33,1,.33]]},{id:"bed_single",group:"furniture",w:90,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.15,.1,.7,.15],["l",0,.33,1,.33]]},{id:"nightstand",group:"furniture",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["e",.29,.31,.15,.15],["e",.71,.31,.15,.15],["e",.29,.71,.15,.15],["e",.71,.71,.15,.15]]},{id:"dishwasher",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["r",.1,.12,.8,.76],["e",.5,.5,.27,.27],["e",.5,.5,.13,.13]]},{id:"washer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["e",.5,.57,.14,.14]]},{id:"dryer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["p",.36,.5,.5,.64,.64,.5]]},{id:"tv",group:"appliance",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",w:90,h:25,g:[["r",0,0,1,1],["l",.05,.55,.95,.55],["l",.05,.79,.95,.79]]},{id:"water_heater",group:"appliance",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",w:90,h:90,g:[["r",0,0,1,1],["l",0,0,1,1],["l",1,0,0,1],["e",.5,.5,.08,.08]]},{id:"sink",group:"sanitary",w:60,h:45,g:[["r",0,0,1,1],["e",.5,.6,.34,.3],["e",.5,.15,.07,.07]]},{id:"kitchen_sink",group:"sanitary",w:80,h:60,g:[["r",0,0,1,1],["r",.06,.24,.44,.64],["r",.54,.24,.4,.64],["e",.5,.12,.06,.06]]},{id:"bidet",group:"sanitary",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",w:100,h:280,g:[["r",0,0,1,1],["l",0,.111,1,.111],["l",0,.222,1,.222],["l",0,.333,1,.333],["l",0,.444,1,.444],["l",0,.556,1,.556],["l",0,.667,1,.667],["l",0,.778,1,.778],["l",0,.889,1,.889],["l",.5,.93,.5,.06],["p",.38,.16,.5,.06,.62,.16]]},{id:"fireplace",group:"other",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",w:40,h:40,g:[["e",.5,.5,.22,.22],["l",.5,.28,.5,.02],["l",.5,.72,.5,.98],["l",.28,.5,.02,.5],["l",.72,.5,.98,.5],["l",.34,.34,.13,.13],["l",.66,.66,.87,.87],["l",.66,.34,.87,.13],["l",.34,.66,.13,.87]]},{id:"rug",group:"other",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],Nn=new Map(An.map(t=>[t.id,t]));function En(t){return t&&Nn.get(t)||null}function In(t){return An.filter(e=>e.group===t)}function On(t,e,i=cn,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Fn=5e-4,Ln=an;function qn(t){return Number.isFinite(t)?Math.max(Fn,Math.min(Ln,t)):Fn}const Hn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Un(t,e,i){const s=En(t);if(!(s&&e>0&&i>0))return"";const o=t=>Hn(t*e),n=t=>Hn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,h]=t;r.push(`M${o(s-l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Bn(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,h,c]=o,d=h-s,p=c-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Wn(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function jn(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Gn(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Vn(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Kn(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Vn(t.a)||!Vn(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function Yn(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Jn(t,e){return Yn(t,e).q}function Zn(t,e,i,s,o){const n=Yn(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+c*g,h+d*g]}function Xn(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt.seg)}(e,s);let n=null;for(const r of e||[]){if(!r?.id)continue;const e=Pe(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of o)if(Zi([(l[0]+h[0])/2,(l[1]+h[1])/2],t)<2*s){p=!0;break}p||(!n||dt?.id),r=Kn(e);if(r.length)return rr(r,t,i,s).map(t=>Gn(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tjn([t[0],t[1]],[t[2],t[3]],e))}function ir(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tZi(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function sr(t,e,i){let s=null;for(const o of e){const e=Zi(t,o);e<=i&&(!s||e.05)continue;const d=co(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-c[0],p[1]-c[1]);u0?l:n}(t,e,i,s,o,n);return mo(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function nr(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(Zi(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Zi([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])Zi(e,t)<=2*i&&s.push(e);return s}function rr(t,e,i,s){const o=Kn(t);if(!o.length)return[];const n=Xn(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=Gn(t,i),o=e[0],l=e[1],h=e[2]-o,c=e[3]-l,d=Math.hypot(h,c);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,h=(t[2]-o)*p+(t[3]-l)*u,c=Math.max(0,Math.min(r,h)),m=Math.min(d,Math.max(r,h));if(m-ct.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=h&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=h);c||(c={pair:t,origin:[e[0],e[1]],ux:n,uy:r,ranges:[]},l.push(c));const d=(e[0]-c.origin[0])*c.ux+(e[1]-c.origin[1])*c.uy,p=(e[2]-c.origin[0])*c.ux+(e[3]-c.origin[1])*c.uy;c.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const c=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&c.push(jn(e,o,i))}}return c}function ar(t,e,i,s){const o=Kn(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=Gn(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,h=a;for(let t=0;t{const e=Yn(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=c[0]-n[0],r=c[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(Yn([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),h=d(a);break}}n.push(jn(l,h,s))}return n}class lr{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=Gi(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{const e=Number(t);return Number.isFinite(e)?e:null};function pr(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dr(e.x)&&null!=dr(e.y)?{x:dr(e.x),y:dr(e.y),a:dr(e.a??e.angle??e.theta)}:null;let s=null;const o=t.path?.points??t.path;if(Array.isArray(o)&&o.length){s=[];for(const t of o){const e=dr(Array.isArray(t)?t[0]:t?.x),i=dr(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const n=[],r=t.rooms,a=Array.isArray(r)?r.map((t,e)=>[String(t?.id??e),t]):r&&"object"==typeof r?Object.entries(r):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim();let s=dr(e.cx??e.center?.x),o=dr(e.cy??e.center?.y);if(null==s||null==o){const t=dr(e.x0),i=dr(e.y0),n=dr(e.x1),r=dr(e.y1);null!=t&&null!=i&&null!=n&&null!=r&&(s=(t+n)/2,o=(i+r)/2)}if(null!=s&&null!=o||(s=dr(e.x),o=dr(e.y)),i&&null!=s&&null!=o){const r={id:t,name:i,cx:s,cy:o},a=dr(e.x0),l=dr(e.y0),h=dr(e.x1),c=dr(e.y1);null!=a&&null!=l&&null!=h&&null!=c&&(r.x0=Math.min(a,h),r.y0=Math.min(l,c),r.x1=Math.max(a,h),r.y1=Math.max(l,c)),n.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s?{pos:i,path:s,rooms:n,mapId:l}:null}function ur(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const _r=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function mr(t,e){const i=new Map(e.map(t=>[_r(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(_r(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,h=0,c=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,h+=m,c+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,h]=u,c=e*(n*h-r*l)-i*(o*h-r*a)+s*(o*l-n*a);if(!Number.isFinite(c)||Math.abs(c)<1e-9)return null;const d=[(n*h-r*l)/c,(s*l-i*h)/c,(i*r-s*n)/c,(r*a-o*h)/c,(e*h-s*a)/c,(s*o-e*r)/c,(o*l-n*a)/c,(i*a-e*l)/c,(e*n-i*o)/c];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,h]),g=_([c,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:cr(n,s)}:null}function gr(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,h]=t[n],c=l-r,d=h-a,p=Math.hypot(c,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function fr(t){return"cleaning"===t||"returning"===t||"on"===t}const vr={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function yr(t){const[e,i]=vr[t.rot]||[1,0],s=t.mir?-1:1;return[t.s*e*s,-t.s*i,t.ox,t.s*i*s,t.s*e,t.oy]}function br(t,e,i,s){const[o,n]=hr(yr(e),i,s),r=yr({...t,ox:0,oy:0}),[a,l]=hr(r,i,s);return{...t,ox:o-a,oy:n-l}}function wr(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const $r={availability:"available",status:"alarm",activity:"none"},kr=new Set(["motion","vibration","sound"]),xr=new Set(["occupancy","presence"]),Sr=new Set(["door","window","garage_door","opening"]),Mr=new Set(["running","power"]),Cr=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Dr=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Pr=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),zr=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Tr=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Rr=t=>String(t??"").trim().toLowerCase();function Ar(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],o=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(o))return!0;if([i.translation_key,i.original_name,i.name].map(Rr).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const n=Rr(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(n)||/(?:^|[\s._-])питание$/.test(n)}function Nr(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Rr(t?.[e]);if(Dr.has(i)||Pr.has(i))return i}return""}function Er(t,e){const i=t?.states?.[e],s=i?Rr(i.state):"__missing__",o=String(e||"").split(".")[0],n=Rr(i?.attributes?.device_class),r={eid:e,state:s,availability:Tr(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Xi.has(e))}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return kr.has(n)?{...r,edge:"rising"}:xr.has(n)?{...r,activity:"on"===s?"presence":"none"}:Sr.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:Mr.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=Nr(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Rr):[],o=!Pr.has(s)&&(zr.has(s)||Dr.has(s)||e.includes(s));return(t?Dr.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=Nr(i.attributes);return Dr.has(a)||Dr.has(s)&&!Pr.has(s)?{...r,status:"working",activity:"running"}:r}function Ir(t,e){if(!(t=>!!t&&!Tr(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}function Or(t){const e={};for(const[i,s]of Object.entries(t.entities))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}function Fr(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Lr(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const qr=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Hr=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Ur(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of qr){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Hr(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Rr(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return kr.has(i)||xr.has(i)||Sr.has(i)||Mr.has(i)||"moving"===i||Cr.has(i)}(t,e.eid));if(n.length)return Hr(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length){const e=Hr(r);return[(e.find(e=>Ar(t,e.eid))||e[0]).eid]}const a=[];for(const t of wt)a.push(...Hr(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...Hr(o.filter(t=>!wt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function Wr(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>Lr(t,e.eid));if(i)return i.eid}return Ur(t,e)[0]}function Br(t,e,i=[]){return[...new Set(jr(t,e,i))].filter(t=>Fi(t))}function jr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function Gr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).some(t=>t.startsWith("switch.")&&s.has(t))}function Vr(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Kr(t,e){const i=Br(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light||Gr(e.marker?.binding,e.marker?.controls,e.entities)){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Fi),s=t.primary&&Fi(t.primary)?t.primary:null;return(e&&Fi(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||Ur(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function Yr(t,e,i){const s=[],o=new Set;for(const n of e){if(n.hidden||null!=i&&!Vr(n,i))continue;const e=Kr(t,n);for(const{eid:i,via:r}of e)i&&!o.has(i)&&(o.add(i),s.push({eid:i,device:n,via:r,on:"on"===t.states[i]?.state}))}return s}function Jr(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function Zr(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return Qe(i)}function Xr(t,e){for(const i of e){if(!Lr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function Qr(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function ta(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ea(t,e){for(const i of e){if(!ta(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function ia(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function sa(t,e,i,s,o){const n=bt(e,i,o);if(n!==yt)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=vt[e];if(t)return t}return null}(r)??yt}function oa(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function na(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function ra(t,e,i,s){const o=Br(e.binding,e.controls,t.entities).filter(t=>!na(i,t,s));t.marker=e,t.controls=o,e.hidden&&(t.hidden=!0),e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function aa(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,showAll:r,firstSpaceId:a,loc:l,iconRules:h}=t,c=!1!==o.group_lights,d=oa(s),p=ia(e,c).filter(t=>!na(e,t.eid,d)),u=new Set(p.map(t=>t.area)),_=Or(e),m=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||m.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),f={},v=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(m.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!o.filter_seeded)continue;const d=_[t.id]||[],p=Fr(e,t,d),y=!o.filter_seeded;if(y&&!r){if(n.has(p))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===p&&t.via_device_id)continue}const b=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=b+"|"+s;let $=sa(e,b,t.model,d,h);if(d.some(t=>t.startsWith("lock."))&&($="mdi:lock"),y&&!r&&c&&"mdi:lightbulb"===$&&u.has(s))continue;f[w]=(f[w]||0)+1;const k=f[w]>1?b+" "+f[w]:b,x={id:t.id,name:k,model:t.model||"",area:s,space:i[s],icon:$,entities:d,bindingKind:"device",bindingRef:t.id,pdfs:[]};x.primary=Wr(e,d,$),"mdi:thermometer"!==$&&"mdi:air-filter"!==$||(x.temp=Xr(e,d)),x.primary&&ta(e,x.primary)&&(x.hum=ea(e,d)),v.push(x)}for(const t of p)i[t.area]&&(m.has("entity:"+t.eid)||v.push({id:"lg_"+t.eid,name:t.name,model:l("device.light_group"),area:t.area,space:i[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of s){if(t.removed)continue;if(t.hidden&&!o.filter_seeded)continue;const[s,n]=t.binding.split(":");if("device"===s){const s=e.devices[n],o=t.area||s?.area_id||"",r=o&&i[o]||t.space||a,c=s&&_[s.id]||[];let p=s?sa(e,s.name_by_user||s.name||"",s.model,c,h):"mdi:help-circle";c.some(t=>t.startsWith("lock."))&&(p="mdi:lock");const u={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:o,space:r,icon:p,entities:c,bindingKind:"device",bindingRef:n};u.primary=Wr(e,c,p),"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Xr(e,c)),u.primary&&ta(e,u.primary)&&(u.hum=ea(e,c)),u.primary&&ta(e,u.primary)&&(u.hum=ea(e,c)),ra(u,t,e,d),v.push(u)}else if("entity"===s){if(na(e,n,d))continue;const s=e.entities[n],o=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=o&&i[o]||t.space||a,l=e.states[n],c=s?.name||l?.attributes?.friendly_name||n;let p=sa(e,c,"",[n],h);n.startsWith("lock.")&&(p="mdi:lock");const u={id:t.id,name:c,model:"",area:o,space:r,icon:p,entities:[n],primary:n,bindingKind:"entity",bindingRef:n};"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Xr(e,[n])),ta(e,n)&&(u.hum=ea(e,[n])),ra(u,t,e,d),v.push(u)}else{const s=t.area||"",o=t.space||s&&i[s]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:s,space:o,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};ra(n,t,e,d),v.push(n)}}return v}function la(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=oa(i);return"device"===o?r.devices.has(n):"entity"===o&&na(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?Xr(t,e):ea(t,e)}return null}const ha=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");var ca={"btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden devices (ghosted, this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_closewall":"Physical wall: click a dashed virtual stretch","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.closewall":"Physical wall","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary stretch","history.close_boundary":"Close boundary opening","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit door or window opening","history.move_opening":"Move door or window opening","history.delete_opening":"Delete door or window opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Doors & windows: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Door / window","opening.door":"Door","opening.window":"Window","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon","display.icon_ripple":"Icon + activity","marker.display_hint":"A yellow plate means actual work. Activity adds events, presence and movement; alarms are always visible.","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide doors and windows","space.hide_openings_tip":"The symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value instead of an icon","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Show hidden","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors and windows, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","fill.glow":"Light sources (dark house, glowing lamps)","gs.glow_group":"Light-sources fill","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius (light-sources fill)","marker.glow_radius_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.openwall":"Virtual wall","title.markup_openwall":"Virtual wall — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Crosshair cursor.","toast.openwall_pick":"Click a wall shared by two rooms","toast.openwall_shared_only":"Only a shared wall between two rooms can be opened","toast.openwall_short":"Stretch too short — pick a farther point","toast.openwall_opened_span":"Virtual stretch opened","toast.openwall_closed_span":"Virtual stretch closed","toast.closewall_pick":"Click a dashed virtual stretch","toast.closewall_use_tool":"Use “Physical wall” for this stretch","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Makes the icon glow in the “Light sources” fill even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Show hidden" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.status_found":"Position source found: {name}","vac.status_none":"The integration reports no coordinates — the robot will only be shown at its base","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_res_warn":"Matched {rooms} rooms but the fit is rough — verify and refine via “Fit manually” if needed","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the sun (day/night)","gs.bg_daynight_hint":"The stage follows the sun: neutral day, warm golden hour, deep night. Needs the compass below.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the sun features stay off.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Point the arrow at north (1° steps, 15° with Shift) or type the degrees — until then the sun features stay off.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.weather":"Weather entity","gs.weather_ph":"weather.home (optional)","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug"};const da={en:ca,ru:{"btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые устройства (полупрозрачными, только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_closewall":"Физическая стена: кликните по пунктирному виртуальному участку","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.closewall":"Физическая стена","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие участка границы","history.close_boundary":"Закрытие проёма в границе","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение дверного или оконного проёма","history.move_opening":"Перемещение дверного или оконного проёма","history.delete_opening":"Удаление дверного или оконного проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Двери и окна: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Дверь / окно","opening.door":"Дверь","opening.window":"Окно","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок","display.icon_ripple":"Значок + активность","marker.display_hint":"Жёлтая подложка означает фактическую работу. Активность добавляет события, присутствие и движение; тревога видна всегда.","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить (свет/розетки)","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери и окна не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Показать скрытые","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери и окна, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","fill.glow":"Свет по источникам (тёмный дом, пятна света)","gs.glow_group":"Заливка «Свет по источникам»","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения (заливка «Свет по источникам»)","marker.glow_radius_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.openwall":"Виртуальная стена","title.markup_openwall":"Виртуальная стена — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает виртуальный участок. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","toast.closewall_pick":"Кликните по пунктирному виртуальному участку","toast.closewall_use_tool":"Для этого участка выберите «Физическая стена»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Использовать датчик температуры устройства","marker.use_climate_temp_tip":"Кондиционеры и термостаты знают температуру комнаты (current_temperature). Показывает её плашкой у значка и учитывает в средней температуре комнаты — как обычный термометр.","marker.is_light":"Это устройство — источник света","marker.is_light_tip":"Даёт ореол в заливке «Свет по источникам» даже без light-сущности — для умного выключателя с обычными светильниками. Ореол следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Показать скрытые» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.status_found":"Источник координат найден: {name}","vac.status_none":"Интеграция не отдаёт координаты — робот будет показан только на базе","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_res_warn":"Совпало комнат: {rooms}, но привязка грубовата — проверьте и при необходимости откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за солнцем (день/ночь)","gs.bg_daynight_hint":"Фон следует за солнцем: нейтральный день, тёплый золотой час, глубокая ночь. Нужен компас ниже.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — солнечные функции выключены.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Направьте стрелку на север (шаг 1°, с Shift 15°) или введите градусы — до этого солнечные функции молчат.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.weather":"Сущность погоды","gs.weather_ph":"weather.home (необязательно)","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр"}};function pa(t,e){if(e&&e in da)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function ua(t,e,i){return wi(da[t][e]??ca[e]??e,i)}class _a extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[],e=this._lang;return[{name:"title",selector:{text:{}}},t.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:t}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:[{value:"",label:ua(e,"editor.lang_auto")},{value:"en",label:ua(e,"editor.lang_en")},{value:"ru",label:ua(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:ua(t,"editor.title"),default_floor:ua(t,"editor.default_floor"),language:ua(t,"editor.language"),icon_size:ua(t,"editor.icon_size"),show_temperature:ua(t,"editor.show_temperature"),live_states:ua(t,"editor.live_states"),show_signal:ua(t,"editor.show_signal"),kiosk:ua(t,"editor.kiosk"),cycle:ua(t,"editor.cycle")};return W`e[t.name]||t.name} @value-changed=${this._valueChanged} - >`}_valueChanged(t){const e={...this._config,...t.detail.value},i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ua.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",ua);const _a=n` + >`}_valueChanged(t){const e={...this._config,...t.detail.value},i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}_a.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",_a);const ma=n` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -1297,6 +1297,21 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .stage.mode-decor.dtool-text .decorlayer .dshape.dtext { cursor: text; } + /* Erasing a hairline must not require pixel-perfect aim. The duplicate + geometry is invisible and exists only while Erase is active. A + non-scaling stroke keeps the target comfortably wide at every zoom. */ + .decorlayer .derasehit { + fill: none; + stroke: transparent; + stroke-width: 16px; + stroke-linecap: round; + stroke-linejoin: round; + vector-effect: non-scaling-stroke; + pointer-events: none; + } + .stage.mode-decor.dtool-erase .decorlayer .derasehit { + pointer-events: stroke; + } .decorlayer .dsel { filter: drop-shadow(0 0 3px var(--hp-accent)); } @@ -2820,17 +2835,21 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .editorchrome-inner.nav-enter, .editorchrome-inner.nav-exit { animation: none; } } - /* Device info can have Edit + Open in HA + Close. A small HA dialog is - narrower than those three Russian-labelled actions; without wrapping, - HA's end-aligned footer pushed the first button outside the surface. */ + /* Device info can have Edit + Open in HA + Close. It uses a wide dialog; + wrapping remains as a phone fallback, but without a flex spacer (which + used to strand Edit alone on the first line). */ hp-dialog .row.infofooter { align-items: center; + justify-content: flex-start; flex-wrap: wrap; gap: var(--sp-3); } hp-dialog .row.infofooter .btn { flex-shrink: 0; } + hp-dialog .row.infofooter .infofooter-close { + margin-left: auto; + } @media (max-width: 480px) { hp-dialog .row.infofooter { padding: var(--sp-4) var(--sp-5); @@ -2966,16 +2985,16 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow z-index: 120; max-width: 90vw; } - `;function ma(t){const e=rn(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=xi(t.cfg.spaces.find(e=>e.id===t.spaceId)),o=t.iconSize??2.5,n=o>8?2.5:o,r={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(r[t.area]=e.id);const a=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):_t,l=gt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:mt),h=ra({hass:t.hass,areaToSpace:r,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:a,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>pa(t.lang,e),iconRules:l}),c=h.filter(e=>e.space===t.spaceId),d=c.filter(t=>!t.hidden),p=function(t,e,i){const s={},o=i/100*Cn(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=zn(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),c=a/h,d=l/Math.max(Math.ceil(e.length/h),1),p=e.map((t,e)=>({x:n.x+r+c*(e%h+.5),y:n.y+r+d*(Math.floor(e/h)+.5)}));ei(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=un(p[e]))}return s}(c,i,n),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qo,e=i.y*Qo;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},m=Array.isArray(_.walls)?_.walls:[],g=Number(_.cell_cm)>0?Number(_.cell_cm):5,f=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Bo(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...Go(o,e,i));for(const o of t.wall_columns||[])s.push(jo(o,e,i));return s}(i,g,cn);for(const t of f){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&u.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const v=Sn(i,u),y=[v.x,v.y,v.w,v.h],b=i.rooms.filter(t=>t.area||s.showBorders||"light"===Vi(s.fill,t)).map(e=>{let o="room "+(i.bg?"overlay":"yard"),n="";const r=Vi(s.fill,e);if(s.showBorders||"none"!==r){o+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a="light"===r?zi("light",null,Yr(Kr(t.hass,c,e)),null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):e.area?zi(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Jr(t,o.entities);null!=e&&s.push(e)}return Qe(s)}(t.hass,c,e.area):null,"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=Zr(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,c,e.area):null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):null;a?(o+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!s.showNames,l=Tn(e),h=e.id||G,d=e.area||G,p=e.poly?B`e.id===t.spaceId);if(!i)return null;const s=xi(t.cfg.spaces.find(e=>e.id===t.spaceId)),o=t.iconSize??2.5,n=o>8?2.5:o,r={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(r[t.area]=e.id);const a=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):_t,l=gt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:mt),h=aa({hass:t.hass,areaToSpace:r,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:a,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>ua(t.lang,e),iconRules:l}),c=h.filter(e=>e.space===t.spaceId),d=c.filter(t=>!t.hidden),p=function(t,e,i){const s={},o=i/100*Cn(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=zn(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),c=a/h,d=l/Math.max(Math.ceil(e.length/h),1),p=e.map((t,e)=>({x:n.x+r+c*(e%h+.5),y:n.y+r+d*(Math.floor(e/h)+.5)}));ei(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=un(p[e]))}return s}(c,i,n),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qo,e=i.y*Qo;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},m=Array.isArray(_.walls)?_.walls:[],g=Number(_.cell_cm)>0?Number(_.cell_cm):5,f=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Bo(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...Go(o,e,i));for(const o of t.wall_columns||[])s.push(jo(o,e,i));return s}(i,g,cn);for(const t of f){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&u.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const v=Sn(i,u),y=[v.x,v.y,v.w,v.h],b=i.rooms.filter(t=>t.area||s.showBorders||"light"===Vi(s.fill,t)).map(e=>{let o="room "+(i.bg?"overlay":"yard"),n="";const r=Vi(s.fill,e);if(s.showBorders||"none"!==r){o+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a="light"===r?zi("light",null,Jr(Yr(t.hass,c,e)),null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):e.area?zi(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Zr(t,o.entities);null!=e&&s.push(e)}return Qe(s)}(t.hass,c,e.area):null,"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=Xr(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,c,e.area):null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):null;a?(o+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!s.showNames,l=Tn(e),h=e.id||G,d=e.area||G,p=e.poly?B``:B``;return B`${p}${a?B`${e.name}`:G}`}),w=d.map(e=>{const s=function(t,e,i,s,o){const n=e[t.id];return n&&n.s===t.space?{x:n.x*Qo,y:n.y*Qo}:s[t.id]?s[t.id]:un(Mn(o))}(e,t.layout,t.cfg,p,i),o=(s.x-y[0])/y[2]*100,n=(s.y-y[1])/y[3]*100,r=Number(e.marker?.size)>0?Number(e.marker.size):1,a=Number(e.marker?.angle)||0,l=[`left:${o}%`,`top:${n}%`];return 1!==r&&l.push(`--dev-scale:${r}`),W`
-
`}),k=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const o=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Qo,y:s.y*Qo};const o=Tn(t);return un({x:o[0],y:o[1]})}(e,i.id,t.layout,t.cfg),n=(o.x-y[0])/y[2]*100,r=(o.y-y[1])/y[3]*100,a=Math.min(1,s.opacity+.25);return W`
`}),$=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const o=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Qo,y:s.y*Qo};const o=Tn(t);return un({x:o[0],y:o[1]})}(e,i.id,t.layout,t.cfg),n=(o.x-y[0])/y[2]*100,r=(o.y-y[1])/y[3]*100,a=Math.min(1,s.opacity+.25);return W`
${e.name}
`}):[],$=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",x=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let S="";if("daynight"===As(t.cfg?.settings,x)&&null!==Rs(t.cfg?.settings,x)){const e=Is(t.hass);e&&(S=$s(e.elevation).bg)}const M=S||Si(t.cfg?.settings,s),C=m.length?Oo(i.rooms,m,[],dn,g,cn,Qo):ze(i.rooms),D=Di(t.cfg.settings),P=(m.length||f.length)&&s.showBorders?Eo(i.rooms,m,[],[],dn,g,cn,Qo,f):null,z=t.stageWidth&&y[2]?t.stageWidth/y[2]:1,T=!!P&&Js(P.depthUnits,z),R=s.color||"#607d8b";return W` + style="left:${n}%;top:${r}%;color:${s.color};opacity:${a}">${e.name}
`}):[],k=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",x=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let S="";if("daynight"===As(t.cfg?.settings,x)&&null!==Rs(t.cfg?.settings,x)){const e=Is(t.hass);e&&(S=ks(e.elevation).bg)}const M=S||Si(t.cfg?.settings,s),C=m.length?Oo(i.rooms,m,[],dn,g,cn,Qo):ze(i.rooms),D=Di(t.cfg.settings),P=(m.length||f.length)&&s.showBorders?Eo(i.rooms,m,[],[],dn,g,cn,Qo,f):null,z=t.stageWidth&&y[2]?t.stageWidth/y[2]:1,T=!!P&&Js(P.depthUnits,z),R=s.color||"#607d8b";return W`
${P?B` @@ -2985,7 +3004,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:G} ${C.map(t=>"poly"in t?B``:B``)} - ${$?B``:G} ${b} @@ -2999,15 +3018,15 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:G} ${""} -
${w}${k}
+
${w}${$}
- `}let ga=null,fa=null,va=!1;const ya=new Set;function ba(){if(ga)return ga;try{const t=JSON.parse(localStorage.getItem("houseplan_card_cfg_v1")||"null");if(t&&t.config&&Array.isArray(t.config.spaces))return{config:t.config,rev:t.rev||0,layout:t.layout||{}}}catch{}return null}function wa(t){return ga?Promise.resolve(ga):fa||(fa=async function(t){const[e,i]=await Promise.all([t.callWS({type:"houseplan/config/get"}),t.callWS({type:"houseplan/layout/get"})]);if(ga={config:e?.config??null,rev:e?.rev??0,layout:i?.layout??{}},!va&&t.connection?.subscribeEvents){va=!0;const e=()=>{ga=null,ya.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{va=!1}}return ga}(t).finally(()=>{fa=null}),fa)}class ka extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return da(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[];return[t.length?{name:"space",selector:{select:{mode:"dropdown",options:t}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={space:pa(t,"editor.space"),title:pa(t,"editor.title"),show_button:pa(t,"editor.show_button"),button_label:pa(t,"editor.button_label"),button_target:pa(t,"editor.button_target"),icon_size:pa(t,"editor.icon_size")};return W`{fa=null,ba.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{ya=!1}}return fa}(t).finally(()=>{va=null}),va)}class ka extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[];return[t.length?{name:"space",selector:{select:{mode:"dropdown",options:t}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={space:ua(t,"editor.space"),title:ua(t,"editor.title"),show_button:ua(t,"editor.show_button"),button_label:ua(t,"editor.button_label"),button_target:ua(t,"editor.button_target"),icon_size:ua(t,"editor.icon_size")};return W`e[t.name]||t.name} @value-changed=${this._valueChanged} - >`}_valueChanged(t){const e={...this._config||{},...t.detail.value};delete e.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ka.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",ka);const $a=t=>{history.pushState(null,"",t),((t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class xa extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new lr(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");$a(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=ba();return{type:"custom:houseplan-space-card",space:rn(e?.config||null)[0]?.id||"",show_button:!0}}setConfig(t){if(!t||!t.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",...t},this._snap=this._snap||ba()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},ya.add(t),()=>ya.delete(t)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._unsub?.(),this._unsub=void 0,this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._signer.dispose(),super.disconnectedCallback()}willUpdate(t){!this.hass||this._loading||this._snap&&!t.has("hass")||this._snap&&this._loadedOnce||this._load()}updated(){const t=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(t===this._observedStage)return;if(this._stageObserver?.disconnect(),this._observedStage=t,!t)return void(this._stageObserver=void 0);const e=()=>{const e=t.clientWidth;e>0&&Math.abs(e-this._stageWidth)>.5&&(this._stageWidth=e,this.requestUpdate())};this._stageObserver=new ResizeObserver(e),this._stageObserver.observe(t),e()}async _load(){if(this.hass&&!this._loading){this._loading=!0;try{const t=await wa(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return da(this.hass,this._config?.language)}getCardSize(){const t=rn(this._snap?.config||null).find(t=>t.id===this._config?.space);if(t){const e=t.vb[3]/t.vb[2];return Math.max(3,Math.round(8*e))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(t){return W`
${t}
`}_referenced(){return ji(this._snap?.config)}render(){if(!this._config)return G;const t=this._snap?.config;if(!t)return W`
${pa(this._lang,"space_card.loading")}
`;const e=this._config.space,i=ma({hass:this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:e,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:t=>this._signer.display(this.hass,t)});if(!i)return this._errorCard(pa(this._lang,"space_card.not_found",{id:e}));const s=rn(t).find(t=>t.id===e),o=void 0!==this._config.title?this._config.title:s?.title||"",n=!1!==this._config.show_button,r=this._config.button_label||pa(this._lang,"space_card.button");return W` + >
`}_valueChanged(t){const e={...this._config||{},...t.detail.value};delete e.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ka.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",ka);const xa=t=>{history.pushState(null,"",t),((t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class Sa extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new lr(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");xa(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=wa();return{type:"custom:houseplan-space-card",space:rn(e?.config||null)[0]?.id||"",show_button:!0}}setConfig(t){if(!t||!t.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",...t},this._snap=this._snap||wa()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},ba.add(t),()=>ba.delete(t)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._unsub?.(),this._unsub=void 0,this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._signer.dispose(),super.disconnectedCallback()}willUpdate(t){!this.hass||this._loading||this._snap&&!t.has("hass")||this._snap&&this._loadedOnce||this._load()}updated(){const t=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(t===this._observedStage)return;if(this._stageObserver?.disconnect(),this._observedStage=t,!t)return void(this._stageObserver=void 0);const e=()=>{const e=t.clientWidth;e>0&&Math.abs(e-this._stageWidth)>.5&&(this._stageWidth=e,this.requestUpdate())};this._stageObserver=new ResizeObserver(e),this._stageObserver.observe(t),e()}async _load(){if(this.hass&&!this._loading){this._loading=!0;try{const t=await $a(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return pa(this.hass,this._config?.language)}getCardSize(){const t=rn(this._snap?.config||null).find(t=>t.id===this._config?.space);if(t){const e=t.vb[3]/t.vb[2];return Math.max(3,Math.round(8*e))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(t){return W`
${t}
`}_referenced(){return ji(this._snap?.config)}render(){if(!this._config)return G;const t=this._snap?.config;if(!t)return W`
${ua(this._lang,"space_card.loading")}
`;const e=this._config.space,i=ga({hass:this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:e,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:t=>this._signer.display(this.hass,t)});if(!i)return this._errorCard(ua(this._lang,"space_card.not_found",{id:e}));const s=rn(t).find(t=>t.id===e),o=void 0!==this._config.title?this._config.title:s?.title||"",n=!1!==this._config.show_button,r=this._config.button_label||ua(this._lang,"space_card.button");return W` ${o?W`
${o}
`:G} ${i} @@ -3015,7 +3034,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:G}
- `}}xa.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},xa.styles=[_a,n` + `}}Sa.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},Sa.styles=[ma,n` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -3090,7 +3109,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",xa),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-space-card"===t.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only static schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Sa=4.166666666666667e-9;function Ma(t){if(!Number.isFinite(t))return t;const e=Math.round(t/dn)*dn;return Math.abs(e-t)<=Sa?t:e}const Ca=(t,e,i,s)=>Math.hypot(i-t,s-e),Da=(t,e,i,s,o,n,r,a)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+r-(t+i))),Math.max(Math.abs(n-e),Math.abs(n+a-(e+s)))),Pa=(t,e,i,s,o,n,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,h=Math.cos(i*l)*a,c=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(Ca(t+h,e+c,o+d,n+p),Ca(t-h,e-c,o-d,n-p)),_=Math.max(Ca(t+h,e+c,o-d,n-p),Ca(t-h,e-c,o+d,n+p));return Math.min(u,_)},za=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const Ta=t=>JSON.parse(JSON.stringify(t)),Ra=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Aa=(t,e,i)=>Math.min(i,Math.max(e,t)),Na=t=>rn({spaces:[t]})[0],Ea=t=>{const e=[];for(const i of t||[]){const t=Pe(i);if(t?.length)for(let i=0;i{let e=0;for(const i of t.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",e++),Array.isArray(i.controls)){jr(i.binding,i.controls)&&!0!==i.is_light&&(i.is_light=!0,e++);const t="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=t?i.controls.filter(e=>e!==t):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,e++)}const t=i.vacuum;t&&Ra(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const i of t.spaces||[]){if(Ra(i,"segments")&&(delete i.segments,e++),Ra(i,"plan_scale")){const t=Number(i.plan_scale);Number.isFinite(t)&&t>=en&&t<=sn&&(Ra(i,"plan_scale_x")||(i.plan_scale_x=t),Ra(i,"plan_scale_y")||(i.plan_scale_y=t),delete i.plan_scale,e++)}const t=Number(i.cell_cm);Ra(i,"cell_cm")&&(!Number.isFinite(t)||t<.1||t>1e3)&&(i.cell_cm=Number.isFinite(t)&&t>0?Aa(t,.1,1e3):5,e++);const s=Number(i.cell_cm),o=Number.isFinite(s)&&s>0?s:5;for(const t of i.wall_columns||[])if("circle"===t.shape)Ra(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Ra(t,"angle")){const i=((Number(t.angle)||0)%90+90)%90;t.angle!==i&&(t.angle=i,e++)}for(const t of i.decor||[]){if(Ra(t,"width")){const i=Number(t.width);(Ra(t,"width_cm")||Number.isFinite(i))&&(Ra(t,"width_cm")||(t.width_cm=Number(Aa(i/cn*o,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Ra(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Ra(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Aa(yi*bi(t)/cn*o,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Ra(t,"scale")||Ra(t,"size"))&&(delete t.scale,delete t.size,e++);let i=String(t.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1]));if(!(Ra(t,"entity")||Ra(t,"attr")||Ra(t,"unit")))continue;const n=String(t.unit??"").trim(),r="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||n?"":mi(t.entity,r);if(s||a){if(a){const e=i.indexOf("{}");i=e>=0?i.slice(0,e)+a+i.slice(e+2):`${i}${i?" ":""}${a}`,t.text=i}delete t.entity,delete t.attr,delete t.unit,e++}}}return e})(i);for(const t of i.spaces||[]){const e=Na(t);if(!e)continue;if(!Kn(t.open_spans).length){const i=tr(e.rooms,null,Qo,.02*cn,!0);i.length&&(t.open_spans=er(i,Qo),r++)}}const a=Ta(i.spaces||[]),l=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let o=0,n=0,r=0,a=0,l="",h=0,c=0;const d={};let p=5;for(const t of i){const e=za(t);null!=t?.id&&(d[String(t.id)]=e),e>p&&(p=e)}const u=(t,e,i,s=!1)=>{if(!(t>Sa||s))return;o++,t>r&&(r=t);const n=t*hn*e;n>a&&(a=n,l=i)};for(const t of i){const e=za(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){n++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[Ma(e[0]),Ma(e[1])];return t=Math.max(t,Ca(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,o=s.w||0,n=s.h||0,r=Ma(e+o),a=Ma(i+n),l=Ma(e),h=Ma(i),c=Math.max(dn,r-l),d=Math.max(dn,a-h);t=Da(e,i,o,n,l,h,c,d),s.x=l,s.y=h,s.w=c,s.h=d}u(t,e,i)}for(const s of t.room_drafts||[]){n++;let t=0;const o=(s.points||[]).map(e=>{const i=[Ma(e[0]),Ma(e[1])];return t=Math.max(t,Ca(e[0],e[1],i[0],i[1])),i}),r=o.length?[o[0]]:[],a=[];for(let t=0;t+1t.points?.length>=2),c+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const s of t.partitions||[]){n++;const t=[Ma(s.a[0]),Ma(s.a[1])],o=[Ma(s.b[0]),Ma(s.b[1])];let r=Math.max(Ca(s.a[0],s.a[1],t[0],t[1]),Ca(s.b[0],s.b[1],o[0],o[1]));Ca(t[0],t[1],o[0],o[1])>Sa?(s.a=t,s.b=o):r=0,u(r,e,i)}for(const s of t.wall_columns||[]){n++;const t=[Ma(s.center[0]),Ma(s.center[1])],o=Ca(s.center[0],s.center[1],t[0],t[1]);s.center=t,u(o,e,i)}for(const s of t.decor||[]){n++;let t=0;if("line"===s.kind){const e=[Ma(s.x1),Ma(s.y1)],i=[Ma(s.x2),Ma(s.y2)];t=Math.max(Ca(s.x1,s.y1,e[0],e[1]),Ca(s.x2,s.y2,i[0],i[1])),s.x1=e[0],s.y1=e[1],s.x2=i[0],s.y2=i[1]}else{const e=Ma(s.x),i=Ma(s.y);if(null!=s.w&&null!=s.h){const o=Ma(s.x+s.w),n=Ma(s.y+s.h),r=Math.max(dn,o-e),a=Math.max(dn,n-i);t=Da(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=Ca(s.x,s.y,e,i);s.x=e,s.y=i}u(t,e,i)}for(const s of t.openings||[]){n++;const o=Re([s.x,s.y],t.rooms||[],.025,{step:dn,length:Number(s.length)||0});if(!o)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===o.angle),l=Pa(s.x,s.y,Number.isFinite(r)?r:o.angle,Number(s.length)||0,o.x,o.y,o.angle);s.x=o.x,s.y=o.y,s.angle=o.angle,a&&h++,u(l,e,i,a)}}for(const[t,e]of Object.entries(s)){if(!e||"object"!=typeof e)continue;const i=e;if("number"!=typeof i.x||"number"!=typeof i.y)continue;n++;const o=Ma(i.x),r=Ma(i.y),a=Ca(i.x,i.y,o,r);s[t]={...i,x:o,y:r};const l="string"==typeof i.s?i.s:"";u(a,d[l]??p,l)}return{spaces:i,layout:s,report:{moved:o,total:n,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:h,removedDrafts:c},changed:o>0}}(i.spaces||[],e||{});i.spaces=l.spaces;const h={...l.report};let c=0,d=0,p=0;for(let t=0;t[t.id,t.open_to||[]]),walls:e.walls||[]}),n=Na(e),r=Na(s);if(!n||!r)continue;const l=Ea(n.rooms),u=Ea(r.rooms),_=.02*cn;let m=ar(Kn(e.open_spans),l,u,Qo);const g=m.length;m=rr(m,r.rooms,Qo,_),m=m.map(t=>{const e=Gn(t,Qo),i=[e[0],e[1]],o=[e[2],e[3]],n=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=e[2]-e[0],a=e[3]-e[1],l=Math.hypot(r,a)||1,c=u.map(([t,e])=>[t[0],t[1],e[0],e[1]]).filter(t=>{const e=t[2]-t[0],s=t[3]-t[1],h=Math.hypot(e,s)||1;return Math.abs(r*s-a*e)/(l*h)<=1e-6&&Yn(n,t).d<=4*_&&Yn(i,t).d<=4*_&&Yn(o,t).d<=4*_}).sort((t,e)=>Math.hypot(e[2]-e[0],e[3]-e[1])-Math.hypot(t[2]-t[0],t[3]-t[1]))[0];if(h.total++,!c)return t;const d=[[c[0],c[1]],[c[2],c[3]]],p=Zn(i,c,d,cn,2*_),m=Zn(o,c,d,cn,2*_);if(Math.hypot(m[0]-p[0],m[1]-p[1])<.5*cn)return t;const g=Math.max(Math.hypot(p[0]-i[0],p[1]-i[1]),Math.hypot(m[0]-o[0],m[1]-o[1]));if(g>1e-6*cn){h.moved++;const t=g/Qo;t>h.maxShift&&(h.maxShift=t);const e=g/cn*(Number(s.cell_cm)>0?Number(s.cell_cm):5);e>h.maxShiftCm&&(h.maxShiftCm=e,h.maxSpace=String(s.id||""))}return jn(p,m,Qo)}),m=rr(m,r.rooms,Qo,_),d+=Math.max(0,g-m.length);const f=m.map(t=>Gn(t,Qo));m.length?s.open_spans=m:delete s.open_spans,ir(s.rooms||[],r.rooms,f,_);const v=Array.isArray(e.walls)?e.walls.length:0;let y=_o(e.walls,l,u,dn,Qo);y=zo(r.rooms,y,f,dn,Number(s.cell_cm)>0?Number(s.cell_cm):5,cn,Qo),y=po(y,s.rooms||[],dn,1,f.map(t=>[t[0]/Qo,t[1]/Qo,t[2]/Qo,t[3]/Qo])),c+=Math.max(0,v-y.length),y.length?s.walls=y:delete s.walls;const b=JSON.stringify({spans:s.open_spans||[],links:(s.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:s.walls||[]});b!==o&&p++}const u=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o;n<5&&u&&(i.model_version=5);const _=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o,m=Number.isInteger(Number(i.model_version))?Number(i.model_version):n;return{config:i,layout:l.layout,report:{...h,modelFrom:n,modelTo:m,migrated:r,canonicalized:p,wallsMerged:c,spansMerged:d},changed:_}}class Oa{constructor(t=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(t))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(t){this._undo.push(t),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const t=this._undo.pop()??null;return t&&this._redo.push(t),t}redo(){const t=this._redo.pop()??null;return t&&this._undo.push(t),t}clear(){this._undo=[],this._redo=[]}}const Fa="1.60.2-beta.1",La=2e3,qa=1e3,Ha=1500,Ua=15e3,Wa=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const i=Number(e);return Number.isFinite(i)?i:null},Ba=(t,e)=>{if(!t.has(e))return{hit:!1};const i=t.get(e);return t.delete(e),t.set(e,i),{hit:!0,value:i}},ja=(t,e,i,s)=>{for(t.delete(e),t.set(e,i);t.size>s;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}};let Ga="undefined"!=typeof document&&"hidden"===document.visibilityState?Date.now():0,Va=0;const Ka=new Map;let Ya=0;const Ja=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Za=1e4;const Xa="houseplan_card_layout_v1",Qa="houseplan_card_cfg_v1",tl="houseplan_card_zoom_v1",el="houseplan_card_nav_v1",il="houseplan_card_kiosk_v1",sl=1e3,ol=3300,nl=(t,e)=>{const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return{x:i,y:s,w:Math.max(t.x+t.w,e.x+e.w)-i,h:Math.max(t.y+t.h,e.y+e.h)-s}},rl=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},al=(t,e)=>{let i,s=null;const o=(...o)=>{clearTimeout(i),s=o,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return o.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},o.pending=()=>void 0!==i,o},ll=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class hl extends lt{constructor(){super(...arguments),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._unsubCfg=null,this._unsubLayout=null,this._layoutRev=0,this._canOptimizeUndo=!1,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Os},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._navMotion="",this._editorSwapAnimations=[],this._editorChromeMode="plan",this._tool="draw",this._geometryHistory=new Oa(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._cleanFloorCache=new Map,this._glowClipCache=new Map,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._path=[],this._cursorPt=null,this._mergeSel=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._openWallAnchor=null,this._splitSel=null,this._pendingSplit=null,this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._alignDialog=null,this._settingsDialog=null,this._sunRaysCache=null,this._skyElev=null,this._skySnap=!1,this._skySnapRaf=0,this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=t=>this._onKey(t),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++Ya,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{if("hidden"!==document.visibilityState){if("visible"===document.visibilityState){const t=Date.now();Ga&&t-Ga>=Ua&&(Va=t),Ga=0,this._vacJumpOnce=!0,this._skyElev=null,this._tip=null,this._hoverRoom=null,t-Va<=15e3&&this._beginResumeSettle(),this.requestUpdate()}}else Ga||(Ga=Date.now())},this._resumeSettling=!1,this._resumeRaf=0,this._resumeStarted=0,this._resumeLastSize="",this._resumeLastChange=0,this._viewportInvalidAt=0,this._vacFit=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._lastTap=0,this._onHashChange=()=>{const t=this._hashSpace();t&&this._model.find(e=>e.id===t)&&t!==this._space&&(this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._selId=null,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=null,this._decorSnapCache=null,this._showHidden=!1,this._connHooked=null,this._onConnReady=()=>{this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._loading||(this._loadOk&&this._unsubCfg?this._reloadConfigOnly():this._loadFromServer())},this._signer=new lr(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=al(()=>{if(this._serverStorage){const t=[...this._dirtyPos];this._dirtyPos.clear();for(const e of t){const t=this._layout[e];t&&(this._sentPos.set(e,t),this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t}).then(t=>this._noteLayoutRev(t)).catch(t=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))).finally(()=>{this._sentPos.get(e)===t&&this._sentPos.delete(e)}))}this._cacheSnapshot()}else localStorage.setItem(Xa,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.undo();t&&(this._applyGeometryState(t.before)?this._showToast(this._t("history.undone",{name:t.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.redo();t&&(this._applyGeometryState(t.after)?this._showToast(this._t("history.redone",{name:t.name})):this._geometryHistory.clear())},this._saveConfigDebounced=al(()=>{this._serverCfg&&this._writeConfig().catch(t=>{"conflict"===t?.code?(this._showToast(this._t("toast.conflict")),this._cancelPath(),this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(t)}))})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=Wa(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Lo:100);const s=this._imperial?2.54*i:i,o="column"===t.kind?Lo:100;if(!Number.isFinite(s)||s<1||s>o)return void this._showPhysicalRange(o);if("column"===t.kind){const e=this._spaceModel().wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Wa(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:s}:{id:t.id,shape:"square",center:e.center,cm:s,angle:i};if(this._spaceModel().wall_columns.some(e=>e.id!==t.id&&Xo(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const n=this._geometrySnapshot();if("partition"===t.kind){const i=(e.partitions||[]).find(e=>e.id===t.id);i&&(i.cm=s)}else if("column"===t.kind){const i=(e.wall_columns||[]).find(e=>e.id===t.id);i&&(i.cm=qo(s),i.shape="circle"===t.shape?"circle":"square","square"===i.shape?i.angle=Wa(t.angle||"0"):delete i.angle)}else{const i=(e.room_drafts||[]).find(e=>e.id===t.id);i?.segments?.[t.segment||0]&&(i.segments[t.segment||0].cm=s)}this._recordGeometry(this._t("history.physical_edit"),n),this._physicalDialog=null,this._saveConfig()},this._deletePhysicalSelection=()=>{const t=this._physicalSel,e=this._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot(),s="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[s]=(e[s]||[]).filter(e=>e.id!==t.id),e[s].length||delete e[s],this._activeDraftId===t.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftWhole=()=>{const t="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,e=this._curSpaceCfg;if(!t||!e||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===t&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftSegment=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(i<0)return;const s=e.room_drafts[i],o=Math.max(0,Math.min(s.segments.length-1,t.segment||0)),n=[],r=s.points.slice(0,o+1),a=s.points.slice(o+1);if(r.length>=2&&n.push({id:s.id,points:r,segments:s.segments.slice(0,o)}),a.length>=2&&n.push({id:n.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:a,segments:s.segments.slice(o+1)}),2===n.length&&e.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(i,1,...n),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=n.length?{kind:"draft",id:n[0].id}:null,this._recordGeometry(this._t("history.draft_segment_delete"),l),this._saveConfig()},this._keepClosedAsPartitions=()=>{if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const t=this._curSpaceCfg,e=this._path.slice(0,-1);if((t.partitions||[]).length+e.length>2e3)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys];t.partitions||=[];const o=Date.now().toString(36);for(let i=0;it.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._recordGeometry(this._t("history.contour_to_partitions"),i),this._saveConfig(),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this._spaceDialog;if(t)if(t.pickSaved)this._spaceDialog={...t,pickSaved:!1};else{this._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.hass.callWS({type:"houseplan/plans/list"}),e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(t)}))}}},this._aspectJob=null,this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const t=this._glowRadiusCm,e=this._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:e,bgColor:Si(this._settings,{bgColor:null})||null,northDeg:Rs(this._settings,{}),bgMode:As(this._settings,{}),sunRays:Ns(this._settings,{}),weatherEntity:Es(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=Ia(this._serverCfg,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),o=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,config:e.config,layout:e.layout,cm:i,where:o,changed:e.changed,busy:!1}},this._optimizeUndoBusy=!1,this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:mt).map(t=>({...t}));this._rulesDialog={rules:e,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}_startNavMotion(t){if(clearTimeout(this._navMotionTimer),this._navMotionTimer=void 0,window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return this._cancelEditorSwapAnimations(),void(this._navMotion="");this._navMotion=t,this._navMotionTimer=window.setTimeout(()=>{this._navMotionTimer=void 0,this._navMotion="",this.requestUpdate()},190)}_cancelEditorSwapAnimations(){for(const t of this._editorSwapAnimations)t.cancel();this._editorSwapAnimations=[],this.renderRoot.querySelector(".editorchrome")?.classList.remove("resizing")}_animateEditorSwap(t){this.updateComplete.then(()=>{if(!this.isConnected||"swap"!==this._navMotion||window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return;const e=this.renderRoot.querySelector(".editorchrome"),i=e?.querySelector(".editorchrome-inner");if(!e||!i)return;this._cancelEditorSwapAnimations();const s=i.getBoundingClientRect().height;e.classList.add("resizing");const o={duration:190,easing:"cubic-bezier(0.2, 0.7, 0.2, 1)"},n=[];if(t>0&&s>0&&Math.abs(t-s)>.5){const i=e.animate([{height:`${t}px`},{height:`${s}px`}],o);i.id="hp-editor-height-swap",n.push(i)}const r=i.animate([{opacity:.42,transform:"translateY(5px) scale(0.995)"},{opacity:1,transform:"translateY(0) scale(1)"}],o);r.id="hp-editor-content-swap",n.push(r),this._editorSwapAnimations=n,Promise.allSettled(n.map(t=>t.finished)).then(()=>{this._editorSwapAnimations===n&&(this._editorSwapAnimations=[],e.classList.remove("resizing"))})})}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate())}_pickSpace(t){if(t===this._space)return;const e=this._model.map(t=>t.id),i=e.indexOf(this._space),s=e.indexOf(t);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(t,i>=0&&s0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const t=this._model.map(t=>t.id),e=t.indexOf(this._space);this._slideTo(t[(e+1)%t.length],"left"),this._showKioskDots()}}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}_hashSpace(){const t=/(?:^|[#&])space=([^&]+)/.exec(window.location.hash||"");return t?decodeURIComponent(t[1]):""}connectedCallback(){document.addEventListener("visibilitychange",this._vacVisHandler),super.connectedCallback(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>ji(this._serverCfg)),this._config?.kiosk&&Number(this._config?.cycle)>0&&(clearInterval(this._cycleTimer),this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle))),window.addEventListener("hashchange",this._onHashChange),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0));const t=Date.now();"visible"===document.visibilityState&&Ga&&(t-Ga>=Ua&&(Va=t),Ga=0),(this._warmLongReturn||t-Va<=15e3)&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._resumeRaf&&(cancelAnimationFrame(this._resumeRaf),this._resumeRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._activityRt.values())clearTimeout(t.timer);window.removeEventListener("keydown",this._keyHandler),clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._navMotionTimer),this._cancelEditorSwapAnimations(),this._slideTimer=void 0,this._navMotionTimer=void 0,this._slide="",this._navMotion="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._resumeSettling=!1,super.disconnectedCallback()}_onKey(t){if("Escape"===t.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void t.stopPropagation();if("Escape"===t.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void(this._infoCard=null);if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return void(this._alignDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}const e=(t.composedPath?.()||[t.target]).some(t=>t?.matches?.('input, textarea, select, [contenteditable="true"]')),i=t.ctrlKey||t.metaKey,s=t.key.toLowerCase(),o=/^[a-z]$/.test(s),n="z"===s||!o&&"KeyZ"===t.code,r="y"===s||!o&&"KeyY"===t.code,a=i&&(n&&t.shiftKey||r),l=i&&n&&!t.shiftKey;if("decor"===this._mode){if((l||a)&&e)return;return a?(t.preventDefault(),void this._redoGeometry()):l?(t.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||e?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():this._furnPalette?this._furnPalette=null:this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(t.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!l&&!a||!e)){if(("Delete"===t.key||"Backspace"===t.key)&&this._physicalSel&&!e)return t.preventDefault(),void this._deletePhysicalSelection();if(a)return t.preventDefault(),void this._redoGeometry();if(l)return t.preventDefault(),this._rszDrag?void this._rszCancelDrag():"draw"!==this._tool&&"partition"!==this._tool||!this._path.length?"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry():void(this._activeDraftId&&this._path.length>1?this._undoGeometry():this._undoPoint());if("Escape"===t.key)return this._physicalDrag||this._physicalRotate?(t.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(t.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length||"partition"===this._tool&&this._path.length?(t.preventDefault(),void this._undoPoint()):this._physicalSel?(t.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():void(this._rszSel?this._rszSel=null:this._tool="draw")):"split"===this._tool?(t.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(t.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(t.preventDefault(),void(this._wallDialog=null)):"openwall"===this._tool||"closewall"===this._tool?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"partition"!==this._tool&&"column"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const t=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const e=this._curSpaceCfg,i=(e.room_drafts||[]).findIndex(t=>t.id===this._activeDraftId);return void(i>=0&&(this._path.length<2?(e.room_drafts.splice(i,1),e.room_drafts.length||delete e.room_drafts,this._activeDraftId=null):e.room_drafts[i]={id:this._activeDraftId,points:this._path.map(t=>[t[0]/sl,t[1]/sl]),segments:this._draftSegmentCms.map(t=>({cm:t}))},this._recordGeometry(this._t("history.draft_segment_delete"),t),this._saveConfig()))}this._path=this._path.slice(0,-1)}}static getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(t){for(const t of Ka.values())for(const e of t)clearTimeout(e.evict);Ka.clear(),Za=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of Ka.values())for(const o of s)t++,o.dlg&&(e++,i.push(o.dlg.kind));return{keys:Ka.size,slots:t,dlgs:e,drafts:i}}setConfig(t){this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...t},this._config.kiosk&&(this._booting=!1,this._bootFading=!1),t.default_floor&&(this._space=t.default_floor);try{this._zoomBySpace=JSON.parse(localStorage.getItem(tl)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(il)||"null");this._kioskScale={icon:Ki(t?.icon),font:Ki(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Qa)||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._layout=e.layout||{},this._serverStorage=!0;const i=this._hashSpace(),s=this._savedNav();i&&this._model.find(t=>t.id===i)?(this._space=i,this._hashApplied=!0):s?.space&&this._model.find(t=>t.id===s.space)?(this._space=s.space,this._navApplied=!0):t.default_floor?this._space=t.default_floor:this._model.find(t=>t.id===this._space)||(this._space=this._model[0]?.id||this._space),s?.mode&&"view"!==s.mode&&!t.kiosk&&(this._canEdit?this._mode=s.mode:this._pendingNavMode=s.mode)}}catch{}"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const t=Ja(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=Ka.get(t);if(!s||!s.length)return;const o=s.find(t=>t.owner===this._warmGen);if(o)return this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Ua,clearTimeout(o.evict),o.evict=0,o.freed=0,o.live=!0,this._warmSlot=o,void(this._warmKey=t);const{slot:n,sure:r}=((t,e,i,s)=>{const o=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let n=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=o(i);t<=0||(t>r?(n=i,r=t,a=1):t===r&&a++)}return!n||a>1?{slot:n||l,sure:!1}:{slot:n,sure:!0}})(s,this._warmGen,e,i);n&&(this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=Ua,this._booting=!1,this._bootFading=!1,this._hdrH=n.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)),this._warmKey=t,r?(clearTimeout(n.evict),n.evict=0,n.owner=this._warmGen,n.place=e?new WeakRef(e):null,n.idx=i,n.live=!0,this._warmSlot=n,this._warmVp=n.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:n.hdrH,stageH:n.stageH,vp:null,dlg:null,freed:0,evict:0},s.push(this._warmSlot),this._warmTrim(s)))}_warmIdx(t){const e=t?.children;if(!e)return-1;for(let t=0;t4;){const e=t.findIndex(t=>!t.live);if(e<0)break;clearTimeout(t[e].evict),t.splice(e,1)}}_warmScheduleEvict(t,e){if(clearTimeout(t.evict),!t.dlg)return;const i=t.freed,s=t.owner;t.evict=window.setTimeout(()=>{if(t.evict=0,t.freed!==i||t.owner!==s)return;t.dlg=null;const o=Ka.get(e);if(!t.live&&o&&o.length>1){const e=o.indexOf(t);e>=0&&o.splice(e,1)}},Za+250)}_warmAdoptViewport(t){const e=this._warmVp;e&&(!this._hashApplied&&this._model.find(t=>t.id===e.space)?(this._space=e.space,this._navApplied=!0,this._mode="view"!==e.mode&&this._canEdit&&!t.kiosk?e.mode:"view",this._pendingNavMode="view"===e.mode||this._canEdit||t.kiosk?null:e.mode,this._zoom=e.zoom,this._view=e.view?{...e.view}:null,this._viewModeSnap=e.snap?{...e.snap}:null,this._tool=e.tool,this._decorTool=e.decorTool,this._showHidden=e.showHidden,this._showFar!==e.showFar&&(this._showFar=e.showFar,this._frame=null),this._selId=e.selId,this._rszSel=e.rszSel,this._decorSel=e.decorSel,this._warmVpArmed=!0):this._warmVp=null)}_warmPatch(t,e=!1){if(this._config?.kiosk)return;const i=Ja(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!e)return;const t=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,place:t?new WeakRef(t):null,idx:this._warmIdx(t),live:!0,hdrH:this._hdrH,stageH:0,vp:null,dlg:null,freed:0,evict:0};const s=Ka.get(i)||[];for(s.push(this._warmSlot),Ka.set(i,s),this._warmTrim(s);Ka.size>8;){const t=Ka.keys().next().value;if(void 0===t||t===i)break;for(const e of Ka.get(t)||[])clearTimeout(e.evict);Ka.delete(t)}}Object.assign(this._warmSlot,t)}}_warmViewportState(){return{space:this._space,mode:this._mode,zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._rszSel,decorSel:this._decorSel}}_warmDialogState(){const t=(t,e)=>({kind:t,space:this._space,mode:this._mode,data:e});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog?null:this._openingInfo?t("openingInfo",this._openingInfo.id):this._infoCard?t("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:t("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:t("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:t("marker",this._markerDialog):this._openingDialog?t("opening",this._openingDialog):this._backdropDialog?t("backdrop",this._backdropDialog):this._decorShapeDialog?t("decorShape",this._decorShapeDialog):this._decorTextDialog?t("decorText",this._decorTextDialog):this._roomDialog?t("room",{editId:this._roomEditId,fill:this._roomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:t("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._resumeSettling||this._config?.kiosk)return;const t={vp:this._warmViewportState()};if(this._warmRevivePending||(t.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const e=this.parentNode;t.place=e?new WeakRef(e):null,t.idx=this._warmIdx(e)}this._warmPatch(t)}_warmReviveDialog(){this._warmRevivePending=!1;const t=this._warmSlot;if(this._warmReviveTimer=void 0,!t||!t.dlg)return;const e=t.dlg,i=t.freed;if(t.dlg=null,t.freed=0,clearTimeout(t.evict),t.evict=0,i&&!(Date.now()-i>Za)&&e.space===this._space&&e.mode===this._mode){switch(e.kind){case"space":this._spaceDialog={...e.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...e.data,busy:!1};break;case"settings":this._settingsDialog={...e.data,busy:!1};break;case"rules":this._rulesDialog={...e.data,busy:!1};break;case"opening":this._openingDialog={...e.data};break;case"backdrop":this._backdropDialog={...e.data};break;case"decorShape":this._decorShapeDialog={...e.data};break;case"decorText":{this._decorTextDialog={...e.data};const t=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:t,end:t};break}case"room":{const t=e.data;this._roomEditId=t.editId,this._roomFill=t.fill,this._roomTempSrc=t.tempSrc,this._roomHumSrc=t.humSrc,this._roomSrcOpen=t.srcOpen,this._roomSrcFilter=t.srcFilter,this._roomNameScale=t.nameScale,this._roomLabelScale=t.labelScale,this._areaSel=t.areaSel,this._nameSel=t.nameSel,this._pendingSplit=t.pendingSplit,this._path=t.path,this._roomDialog=!0;break}case"info":{const t=this._devices.find(t=>t.id===e.data);t&&(this._infoCard=t);break}case"openingInfo":{const t=(this._curSpaceCfg?.openings||[]).find(t=>t.id===e.data);t&&(this._openingInfo=t);break}}this.requestUpdate()}}_cacheSnapshot(){if(this._serverCfg)try{localStorage.setItem(Qa,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,layout:this._layout}))}catch{}}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const t=this._serverCfg?.spaces||[];let e=t.length+":";for(const i of t){e+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const t of i.rooms||[]){const i=t.poly?.[0],s=t.poly?.[t.poly.length-1];e+=(t.poly?.length||0)+"."+(t.id||"")+"."+(t.open_to||[]).join("+")+"."+(t.area||"")+"."+JSON.stringify(t.settings||0)+"."+(t.x??"")+","+(t.y??"")+","+(t.w??"")+","+(t.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(s?s[0]+"/"+s[1]:"")+";"}}return e}get _model(){if(!this._serverCfg)return[];const t=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===t)return this._modelCache.model;const e=this._buildModel();return this._modelCache={key:t,model:e},e}_buildModel(){if(!this._serverCfg)return[];const t=this._renderCfg;return rn(t).map((e,i)=>{const s=t.spaces[i]?.plan_url;return e.bg&&s?{...e,bg:{...e.bg,href:s}}:e})}_spaceModel(t){const e=this._model;return e.find(e=>e.id===(t??this._space))||e[0]}get _areaToSpace(){const t={};for(const e of this._model)for(const i of e.rooms)i.area&&(t[i.area]={space:e.id,room:i});return t}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const t=this._serverCfg,e=function(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,iconRules:r}=t,a=!1!==o.group_lights,l=sa(s),h=ea(e,a).filter(t=>!oa(e,t.eid,l)),c=new Set(h.map(t=>t.area)),d=Ir(e),p=new Set(s.map(t=>t.binding)),u=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(p.has("device:"+t.id))continue;const o=d[t.id]||[],l=Or(e,t,o);let h=n.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;if(!h&&a&&c.has(s)){const i=(t.name_by_user||t.name||"").trim();"mdi:lightbulb"===ia(e,i,t.model,o,r)&&(h=!0)}h&&u.push("device:"+t.id)}return u}({hass:this.hass,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!e.length&&t.settings?.filter_seeded)return;t.markers=t.markers||[];const i=[];for(const s of e){const e="h"+s.slice(s.indexOf(":")+1);t.markers.push({id:e,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...t.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(t=>!i.includes(t))),t.settings=s,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const t=this._settings.icon_rules;if(!t||!Array.isArray(t)||!t.length)return;const e=JSON.stringify(t);return e!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=e,this._rulesCompiled=gt(t)),this._rulesCompiled}get _fillColors(){return Di(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):_t}willUpdate(t){t.has("_serverCfg")&&this._cfgEpoch++,this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick())}updated(){this._skyRelease(),this._warmSnapshot(),this._dtMeasure();const t=this._stageEl;t&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(t)),t&&this._booting&&!this._bootTimer&&this._bootWatch();const e=this.renderRoot.querySelector(".hdr");if(e&&t&&!this._roHdr){const i=()=>{const e=this.renderRoot.querySelector("ha-card");if(!e)return;const i=t.getBoundingClientRect().top-e.getBoundingClientRect().top,s=Math.min(Math.max(e.getBoundingClientRect().top,0),120),o=Math.round(i+s);o>=0&&o!==this._hdrH&&(this._hdrH=o),o>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:o,stageH:t.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(e),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(t&&!this._view&&this._refitView(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const t=function(t){const e=t?.floors;if(!e||"object"!=typeof e)return[];const i=[];for(const t of Object.values(e))t&&t.floor_id&&i.push({id:t.floor_id,name:t.name||t.floor_id,level:t.level??null});return i.sort((t,e)=>{const i=t.level??1e9,s=e.level??1e9;return i!==s?i-s:t.name.localeCompare(e.name)}),i}(this.hass);t.length?this._importDialog={floors:t.map(t=>({...t,checked:!0}))}:this._openSpaceDialog("create")}}async _loadFromServer(){this._loading=!0,this._loadTries++;try{const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._loadOk=!0,this._serverStorage=!0,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._canOptimizeUndo=!(!t?.can_optimize_undo&&!e?.can_optimize_undo),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;this._geometryHistory.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=i&&Array.isArray(i.spaces)?i:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._layout=e?.layout||{},this._layoutRev=e?.rev??0,this._unsubCfg||(this._unsubCfg=await this.hass.connection.subscribeEvents(t=>{const e=Number(t?.data?.rev??-1);e!==this._cfgRev&&this._reloadConfigOnly(!1,e)},"houseplan_config_updated")),this.hass.callWS({type:"houseplan/trail/get"}).then(t=>{this._vacSrvTrails=t?.trails||{},this.requestUpdate()}).catch(()=>{}),this._unsubTrail||(this._unsubTrail=await this.hass.connection.subscribeEvents(async()=>{try{const t=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=t?.trails||{},this.requestUpdate()}catch{}},"houseplan_trail_updated")),this._unsubLayout||(this._unsubLayout=await this.hass.connection.subscribeEvents(t=>this._onLayoutEvent(Number(t?.data?.rev??-1)),"houseplan_layout_updated"));const s=this._hashSpace(),o=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):o?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===o.space)?(this._space=o.space,this._navApplied=!0):this._norm&&!this._model.find(t=>t.id===this._space)&&(this._space=this._model[0]?.id||this._space),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:this._restoreZoom()}catch(t){if(this._serverCfg)this._scheduleLoadRetry();else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(Xa)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}async _reloadConfigOnly(t=!1,e){if(!t){if(void 0!==e&&e<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,e),400))}try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._geometryHistory.clear(),this._clearGeometryGesture(),this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._canOptimizeUndo=!!t?.can_optimize_undo,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null),this._cacheSnapshot(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(t){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(t)}))}}_scheduleLoadRetry(){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),t.addEventListener?.("ready",this._onConnReady),this._connHooked=t)}_display(t){return this._signer.display(this.hass,t)}_resign(){this._signer.resign(this.hass,ji(this._serverCfg))}_onLayoutEvent(t){t<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{t<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(t){const e=t?.rev;"number"==typeof e&&e>this._layoutRev&&(this._layoutRev=e)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;const t=new Map;for(const e of this._dirtyPos)this._layout[e]&&t.set(e,this._layout[e]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[e,i]of this._sentPos)t.set(e,i);try{const e=await this.hass.callWS({type:"houseplan/layout/get"}),i={...e?.layout||{}};for(const[e,s]of t)i[e]=s;this._layout=i,this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!!e?.can_optimize_undo,this._cacheSnapshot(),this.requestUpdate()}catch{}}_maybeRebuildDevices(){const t=this.hass;if(!t?.devices||!t?.entities||!t?.areas)return;const e=Object.keys(t.devices).length+":"+Object.keys(t.entities).length+":"+Object.keys(t.areas).length+":"+(this._norm?"n":"l")+":"+da(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=ra({hass:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:t=>this._t(t),iconRules:this._iconRules}),this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime())}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const t=this._devices.filter(t=>!t.marker&&!t.virtual).map(t=>t.id).sort(),e=t.join(",");if(e===this._newSyncKey)return;this._newSyncKey=e;const i=this._settings,{fresh:s,known:o}=function(t,e){if(!Array.isArray(e))return{fresh:[],known:[...t]};const i=new Set(e),s=t.filter(t=>!i.has(t));return{fresh:s,known:s.length?[...e,...s]:e}}(t,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const t=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:o,new_device_ids:t}},this._saveConfig()}}get _newIds(){const t=this._settings.new_device_ids;return new Set(Array.isArray(t)?t:[])}_ackNewDevice(t){if(!this._newIds.has(t)||!this._serverCfg)return;const e=this._settings;this._serverCfg={...this._serverCfg,settings:{...e,new_device_ids:(e.new_device_ids||[]).filter(e=>e!==t)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(t){if(!t)return null;const e=[];for(const i of this._devices){if(i.area!==t||i.virtual)continue;const s=Jr(this.hass,i.entities);null!=s&&e.push(s)}return Qe(e)}_roomBounds(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}_defaultPositions(){const t={},e=this._config?.icon_size??2.5;for(const i of this._model){const s=e/100*Cn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const o=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!o.length)continue;const n=this._roomBounds(e),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(o.length*a/Math.max(l,1)))),c=Math.ceil(o.length/h),d=a/h,p=l/Math.max(c,1),u=o.map((t,e)=>({x:n.x+r+d*(e%h+.5),y:n.y+r+p*(Math.floor(e/h)+.5)}));ei(u,n,s,.5*r),o.forEach((e,i)=>t[e.id]=un(u[i]))}}return t}_pos(t){const e=this._spaceModel(t.space),i=this._layout[t.id];if(i)if(this._norm){if(i.s===t.space)return{x:i.x*sl,y:i.y*sl}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:un(Mn(e))}_savePos(t,e,i){if(this._norm){const s=this._gridPitch,o=Math.round(e/s)*s,n=Math.round(i/s)*s,r=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:mn(o/sl),y:mn(n/sl),...r?{k:r}:{}}}}else this._layout={...this._layout,[t.id]:{x:Math.round(e),y:Math.round(i)}};this._dirtyPos.add(t.id),this._persistLayout()}_coverIndicator(t){return"cover"===t.tapAction?ci(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_displayOf(t){const e=t.marker?.display;return"ripple"===e?"icon_ripple":e||"badge"}_visualSamples(t){const e=[],i=this._coverIndicator(t),s=Kr(this.hass,[t]);if(i)e.push(i);else if(s.length)e.push(...s.map(t=>t.eid));else{const i=Hr(this.hass,t.entities);i.length?e.push(...i):t.primary&&e.push(t.primary)}for(const i of t.entities||[]){"alarm"!==Nr(this.hass,i).status||e.includes(i)||e.push(i)}return e.map(t=>Nr(this.hass,t))}_deviceVisual(t){if(t.hidden)return{availability:"available",status:"neutral",activity:"none"};const e=this._visualSamples(t),i=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return kr;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(e);if("alarm"===i.status)return i;if(!this._config?.live_states)return{availability:"available",status:"neutral",activity:"none"};if("unavailable"===i.availability)return i;const s=this._activityRt.get(t.id),o=this._activitySourceKey(e);return s?.sources===o&&s.flashTs&&s.flashKind&&Date.now()-s.flashTst?.device_id===e).map(([t])=>t)}_bindingHasClimate(t){return this._bindingEntities(t).some(t=>t.startsWith("climate."))}_bindingCoverEntity(t){return ci(this._bindingEntities(t))}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!hi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Qr(this.hass,t.primary)?ta(this.hass,t.entities):null}_openMoreInfo(t){t?rl(this,"hass-more-info",{entityId:t}):this._showToast(this._t("toast.no_entity"))}_ctxDevice(t,e){"view"===this._mode&&(t.preventDefault(),t.stopPropagation(),e.primary?this._openMoreInfo(e.primary):this._infoCard=e)}_clickDevice(t,e){if(t.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(e);const i=this._coverIndicator(e),s=this._actEntity(e),o=s?s.split(".")[0]:null,n=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=Kr(this.hass,[e]).filter(t=>"controls"===t.via).map(t=>t.eid);if("toggle"===e.tapAction&&r.length){const t=(a=r.map(t=>this.hass.states[t]?.state),a.some(t=>"on"===t)?"turn_off":"turn_on");return void n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant",t,{entity_id:r}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}var a;const l=function(t,e,i,s){const o=t||e||("light"===i?"toggle":"info");return"more-info"===o?"more-info":"run"===o?"run"===t?"run":"info":"cover"===o?"cover"!==t||"cover"!==i||hi.has(String(s||""))?"info":"cover":"toggle"!==o||!i||li.has(i)?"info":"toggle"===t?"toggle":ai.has(i)?"cover"===i&&hi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,o,s?this.hass.states[s]?.attributes?.device_class:null);if("run"===l){const t=e.marker?.tap_target||"",i=function(t){const e=String(t||"").split(".")[0];return"automation"===e?{domain:"automation",service:"trigger"}:"script"===e?{domain:"script",service:"turn_on"}:"scene"===e?{domain:"scene",service:"turn_on"}:null}(t),s=this.hass.states[t];if(!i||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||t;return void n(this._t("confirm.tap_run",{name:o}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>{this._stampActivity(e.id,"event",this._activitySourceKey(this._visualSamples(e))),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}if("cover"===l&&i){const t=function(t){const e=String(t||"");return"closed"===e?"open_cover":"open"===e?"close_cover":"opening"===e||"closing"===e?"stop_cover":"toggle"}(this.hass.states[i]?.state);return void n(this._t("confirm.tap_cover",{name:e.name}),()=>{this.hass.callService("cover",t,{entity_id:i}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}"toggle"===l&&e.primary?n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant","toggle",{entity_id:e.primary}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}):"more-info"===l&&e.primary?this._openMoreInfo(e.primary):this._infoCard=e}_t(t,e){return pa(da(this.hass,this._config?.language),t,e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(t){const e=[];for(const i of this._devices){if(i.space!==t.id||i.hidden)continue;const s=this._pos(i);e.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(t.id===this._space){for(const t of this._openingsR){const i=Number(t.angle)*Math.PI/180,s=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2,n=vn([[t.rx-s,t.ry-o],[t.rx+s,t.ry+o]]);n&&e.push(n)}const i=this._decorH;for(const t of this._decorList){const s=vn("line"===t.kind?[[t.x1*sl,t.y1*i],[t.x2*sl,t.y2*i]]:"text"===t.kind?[[t.x*sl,t.y*i]]:js({x:t.x*sl,y:t.y*i,w:t.w*sl,h:t.h*i,angle:t.angle}));s&&e.push(s)}for(const i of this._physicalBodiesR(t)){const t=vn(i);t&&e.push(t)}}return bn(t,e)}_frameOf(){const t=this._spaceModel(),e=this._frame,i="view"!==this._mode;if(e&&e.id===t.id&&this._bdDrag)return e;if(e&&e.id===t.id&&e.model===t&&e.layout===this._layout&&e.devs===this._devices&&e.far===this._showFar&&e.grow===i)return e;const s=xn(this._contentItems(t));let o=s.all||Sn(t),n=this._showFar?o:s.core||Sn(t);return e&&e.id===t.id&&i&&e.grow&&(n=nl(e.rect,n),o=nl(e.all,o)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:n,all:o,outliers:s.outliers},this._frame}_baseVb(){const t=this._frameOf().rect;return[t.x,t.y,t.w,t.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
+ `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",Sa),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-space-card"===t.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only static schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Ma=4.166666666666667e-9;function Ca(t){if(!Number.isFinite(t))return t;const e=Math.round(t/dn)*dn;return Math.abs(e-t)<=Ma?t:e}const Da=(t,e,i,s)=>Math.hypot(i-t,s-e),Pa=(t,e,i,s,o,n,r,a)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+r-(t+i))),Math.max(Math.abs(n-e),Math.abs(n+a-(e+s)))),za=(t,e,i,s,o,n,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,h=Math.cos(i*l)*a,c=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(Da(t+h,e+c,o+d,n+p),Da(t-h,e-c,o-d,n-p)),_=Math.max(Da(t+h,e+c,o-d,n-p),Da(t-h,e-c,o+d,n+p));return Math.min(u,_)},Ta=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const Ra=t=>JSON.parse(JSON.stringify(t)),Aa=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Na=(t,e,i)=>Math.min(i,Math.max(e,t)),Ea=t=>rn({spaces:[t]})[0],Ia=t=>{const e=[];for(const i of t||[]){const t=Pe(i);if(t?.length)for(let i=0;i{let e=0;for(const i of t.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",e++),Array.isArray(i.controls)){Gr(i.binding,i.controls)&&!0!==i.is_light&&(i.is_light=!0,e++);const t="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=t?i.controls.filter(e=>e!==t):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,e++)}const t=i.vacuum;t&&Aa(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const i of t.spaces||[]){if(Aa(i,"segments")&&(delete i.segments,e++),Aa(i,"plan_scale")){const t=Number(i.plan_scale);Number.isFinite(t)&&t>=en&&t<=sn&&(Aa(i,"plan_scale_x")||(i.plan_scale_x=t),Aa(i,"plan_scale_y")||(i.plan_scale_y=t),delete i.plan_scale,e++)}const t=Number(i.cell_cm);Aa(i,"cell_cm")&&(!Number.isFinite(t)||t<.1||t>1e3)&&(i.cell_cm=Number.isFinite(t)&&t>0?Na(t,.1,1e3):5,e++);const s=Number(i.cell_cm),o=Number.isFinite(s)&&s>0?s:5;for(const t of i.wall_columns||[])if("circle"===t.shape)Aa(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Aa(t,"angle")){const i=((Number(t.angle)||0)%90+90)%90;t.angle!==i&&(t.angle=i,e++)}for(const t of i.decor||[]){if(Aa(t,"width")){const i=Number(t.width);(Aa(t,"width_cm")||Number.isFinite(i))&&(Aa(t,"width_cm")||(t.width_cm=Number(Na(i/cn*o,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Aa(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Aa(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Na(yi*bi(t)/cn*o,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Aa(t,"scale")||Aa(t,"size"))&&(delete t.scale,delete t.size,e++);let i=String(t.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1]));if(!(Aa(t,"entity")||Aa(t,"attr")||Aa(t,"unit")))continue;const n=String(t.unit??"").trim(),r="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||n?"":mi(t.entity,r);if(s||a){if(a){const e=i.indexOf("{}");i=e>=0?i.slice(0,e)+a+i.slice(e+2):`${i}${i?" ":""}${a}`,t.text=i}delete t.entity,delete t.attr,delete t.unit,e++}}}return e})(i);for(const t of i.spaces||[]){const e=Ea(t);if(!e)continue;if(!Kn(t.open_spans).length){const i=tr(e.rooms,null,Qo,.02*cn,!0);i.length&&(t.open_spans=er(i,Qo),r++)}}const a=Ra(i.spaces||[]),l=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let o=0,n=0,r=0,a=0,l="",h=0,c=0;const d={};let p=5;for(const t of i){const e=Ta(t);null!=t?.id&&(d[String(t.id)]=e),e>p&&(p=e)}const u=(t,e,i,s=!1)=>{if(!(t>Ma||s))return;o++,t>r&&(r=t);const n=t*hn*e;n>a&&(a=n,l=i)};for(const t of i){const e=Ta(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){n++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[Ca(e[0]),Ca(e[1])];return t=Math.max(t,Da(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,o=s.w||0,n=s.h||0,r=Ca(e+o),a=Ca(i+n),l=Ca(e),h=Ca(i),c=Math.max(dn,r-l),d=Math.max(dn,a-h);t=Pa(e,i,o,n,l,h,c,d),s.x=l,s.y=h,s.w=c,s.h=d}u(t,e,i)}for(const s of t.room_drafts||[]){n++;let t=0;const o=(s.points||[]).map(e=>{const i=[Ca(e[0]),Ca(e[1])];return t=Math.max(t,Da(e[0],e[1],i[0],i[1])),i}),r=o.length?[o[0]]:[],a=[];for(let t=0;t+1t.points?.length>=2),c+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const s of t.partitions||[]){n++;const t=[Ca(s.a[0]),Ca(s.a[1])],o=[Ca(s.b[0]),Ca(s.b[1])];let r=Math.max(Da(s.a[0],s.a[1],t[0],t[1]),Da(s.b[0],s.b[1],o[0],o[1]));Da(t[0],t[1],o[0],o[1])>Ma?(s.a=t,s.b=o):r=0,u(r,e,i)}for(const s of t.wall_columns||[]){n++;const t=[Ca(s.center[0]),Ca(s.center[1])],o=Da(s.center[0],s.center[1],t[0],t[1]);s.center=t,u(o,e,i)}for(const s of t.decor||[]){n++;let t=0;if("line"===s.kind){const e=[Ca(s.x1),Ca(s.y1)],i=[Ca(s.x2),Ca(s.y2)];t=Math.max(Da(s.x1,s.y1,e[0],e[1]),Da(s.x2,s.y2,i[0],i[1])),s.x1=e[0],s.y1=e[1],s.x2=i[0],s.y2=i[1]}else{const e=Ca(s.x),i=Ca(s.y);if(null!=s.w&&null!=s.h){const o=Ca(s.x+s.w),n=Ca(s.y+s.h),r=Math.max(dn,o-e),a=Math.max(dn,n-i);t=Pa(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=Da(s.x,s.y,e,i);s.x=e,s.y=i}u(t,e,i)}for(const s of t.openings||[]){n++;const o=Re([s.x,s.y],t.rooms||[],.025,{step:dn,length:Number(s.length)||0});if(!o)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===o.angle),l=za(s.x,s.y,Number.isFinite(r)?r:o.angle,Number(s.length)||0,o.x,o.y,o.angle);s.x=o.x,s.y=o.y,s.angle=o.angle,a&&h++,u(l,e,i,a)}}for(const[t,e]of Object.entries(s)){if(!e||"object"!=typeof e)continue;const i=e;if("number"!=typeof i.x||"number"!=typeof i.y)continue;n++;const o=Ca(i.x),r=Ca(i.y),a=Da(i.x,i.y,o,r);s[t]={...i,x:o,y:r};const l="string"==typeof i.s?i.s:"";u(a,d[l]??p,l)}return{spaces:i,layout:s,report:{moved:o,total:n,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:h,removedDrafts:c},changed:o>0}}(i.spaces||[],e||{});i.spaces=l.spaces;const h={...l.report};let c=0,d=0,p=0;for(let t=0;t[t.id,t.open_to||[]]),walls:e.walls||[]}),n=Ea(e),r=Ea(s);if(!n||!r)continue;const l=Ia(n.rooms),u=Ia(r.rooms),_=.02*cn;let m=ar(Kn(e.open_spans),l,u,Qo);const g=m.length;m=rr(m,r.rooms,Qo,_),m=m.map(t=>{const e=Gn(t,Qo),i=[e[0],e[1]],o=[e[2],e[3]],n=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=e[2]-e[0],a=e[3]-e[1],l=Math.hypot(r,a)||1,c=u.map(([t,e])=>[t[0],t[1],e[0],e[1]]).filter(t=>{const e=t[2]-t[0],s=t[3]-t[1],h=Math.hypot(e,s)||1;return Math.abs(r*s-a*e)/(l*h)<=1e-6&&Yn(n,t).d<=4*_&&Yn(i,t).d<=4*_&&Yn(o,t).d<=4*_}).sort((t,e)=>Math.hypot(e[2]-e[0],e[3]-e[1])-Math.hypot(t[2]-t[0],t[3]-t[1]))[0];if(h.total++,!c)return t;const d=[[c[0],c[1]],[c[2],c[3]]],p=Zn(i,c,d,cn,2*_),m=Zn(o,c,d,cn,2*_);if(Math.hypot(m[0]-p[0],m[1]-p[1])<.5*cn)return t;const g=Math.max(Math.hypot(p[0]-i[0],p[1]-i[1]),Math.hypot(m[0]-o[0],m[1]-o[1]));if(g>1e-6*cn){h.moved++;const t=g/Qo;t>h.maxShift&&(h.maxShift=t);const e=g/cn*(Number(s.cell_cm)>0?Number(s.cell_cm):5);e>h.maxShiftCm&&(h.maxShiftCm=e,h.maxSpace=String(s.id||""))}return jn(p,m,Qo)}),m=rr(m,r.rooms,Qo,_),d+=Math.max(0,g-m.length);const f=m.map(t=>Gn(t,Qo));m.length?s.open_spans=m:delete s.open_spans,ir(s.rooms||[],r.rooms,f,_);const v=Array.isArray(e.walls)?e.walls.length:0;let y=_o(e.walls,l,u,dn,Qo);y=zo(r.rooms,y,f,dn,Number(s.cell_cm)>0?Number(s.cell_cm):5,cn,Qo),y=po(y,s.rooms||[],dn,1,f.map(t=>[t[0]/Qo,t[1]/Qo,t[2]/Qo,t[3]/Qo])),c+=Math.max(0,v-y.length),y.length?s.walls=y:delete s.walls;const b=JSON.stringify({spans:s.open_spans||[],links:(s.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:s.walls||[]});b!==o&&p++}const u=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o;n<5&&u&&(i.model_version=5);const _=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o,m=Number.isInteger(Number(i.model_version))?Number(i.model_version):n;return{config:i,layout:l.layout,report:{...h,modelFrom:n,modelTo:m,migrated:r,canonicalized:p,wallsMerged:c,spansMerged:d},changed:_}}class Fa{constructor(t=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(t))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(t){this._undo.push(t),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const t=this._undo.pop()??null;return t&&this._redo.push(t),t}redo(){const t=this._redo.pop()??null;return t&&this._undo.push(t),t}clear(){this._undo=[],this._redo=[]}}const La="1.60.2-beta.2",qa=2e3,Ha=1e3,Ua=1500,Wa=15e3,Ba=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const i=Number(e);return Number.isFinite(i)?i:null},ja=(t,e)=>{if(!t.has(e))return{hit:!1};const i=t.get(e);return t.delete(e),t.set(e,i),{hit:!0,value:i}},Ga=(t,e,i,s)=>{for(t.delete(e),t.set(e,i);t.size>s;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}};let Va="undefined"!=typeof document&&"hidden"===document.visibilityState?Date.now():0,Ka=0;const Ya=new Map;let Ja=0;const Za=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Xa=1e4;const Qa="houseplan_card_layout_v1",tl="houseplan_card_cfg_v1",el="houseplan_card_zoom_v1",il="houseplan_card_nav_v1",sl="houseplan_card_kiosk_v1",ol=1e3,nl=3300,rl=(t,e)=>{const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return{x:i,y:s,w:Math.max(t.x+t.w,e.x+e.w)-i,h:Math.max(t.y+t.h,e.y+e.h)-s}},al=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},ll=(t,e)=>{let i,s=null;const o=(...o)=>{clearTimeout(i),s=o,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return o.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},o.pending=()=>void 0!==i,o},hl=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class cl extends lt{constructor(){super(...arguments),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._unsubCfg=null,this._unsubLayout=null,this._layoutRev=0,this._canOptimizeUndo=!1,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Os},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._navMotion="",this._editorSwapAnimations=[],this._editorChromeMode="plan",this._tool="draw",this._geometryHistory=new Fa(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._cleanFloorCache=new Map,this._glowClipCache=new Map,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._path=[],this._cursorPt=null,this._mergeSel=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._openWallAnchor=null,this._splitSel=null,this._pendingSplit=null,this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._alignDialog=null,this._settingsDialog=null,this._sunRaysCache=null,this._skyElev=null,this._skySnap=!1,this._skySnapRaf=0,this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=t=>this._onKey(t),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++Ja,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{if("hidden"!==document.visibilityState){if("visible"===document.visibilityState){const t=Date.now();Va&&t-Va>=Wa&&(Ka=t),Va=0,this._vacJumpOnce=!0,this._skyElev=null,this._tip=null,this._hoverRoom=null,t-Ka<=15e3&&this._beginResumeSettle(),this.requestUpdate()}}else Va||(Va=Date.now())},this._resumeSettling=!1,this._resumeRaf=0,this._resumeStarted=0,this._resumeLastSize="",this._resumeLastChange=0,this._viewportInvalidAt=0,this._vacFit=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._lastTap=0,this._onHashChange=()=>{const t=this._hashSpace();t&&this._model.find(e=>e.id===t)&&t!==this._space&&(this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._selId=null,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=null,this._decorSnapCache=null,this._showHidden=!1,this._connHooked=null,this._onConnReady=()=>{this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._loading||(this._loadOk&&this._unsubCfg?this._reloadConfigOnly():this._loadFromServer())},this._signer=new lr(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=ll(()=>{if(this._serverStorage){const t=[...this._dirtyPos];this._dirtyPos.clear();for(const e of t){const t=this._layout[e];t&&(this._sentPos.set(e,t),this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t}).then(t=>this._noteLayoutRev(t)).catch(t=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))).finally(()=>{this._sentPos.get(e)===t&&this._sentPos.delete(e)}))}this._cacheSnapshot()}else localStorage.setItem(Qa,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.undo();t&&(this._applyGeometryState(t.before)?this._showToast(this._t("history.undone",{name:t.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.redo();t&&(this._applyGeometryState(t.after)?this._showToast(this._t("history.redone",{name:t.name})):this._geometryHistory.clear())},this._saveConfigDebounced=ll(()=>{this._serverCfg&&this._writeConfig().catch(t=>{"conflict"===t?.code?(this._showToast(this._t("toast.conflict")),this._cancelPath(),this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(t)}))})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=Ba(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Lo:100);const s=this._imperial?2.54*i:i,o="column"===t.kind?Lo:100;if(!Number.isFinite(s)||s<1||s>o)return void this._showPhysicalRange(o);if("column"===t.kind){const e=this._spaceModel().wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Ba(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:s}:{id:t.id,shape:"square",center:e.center,cm:s,angle:i};if(this._spaceModel().wall_columns.some(e=>e.id!==t.id&&Xo(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const n=this._geometrySnapshot();if("partition"===t.kind){const i=(e.partitions||[]).find(e=>e.id===t.id);i&&(i.cm=s)}else if("column"===t.kind){const i=(e.wall_columns||[]).find(e=>e.id===t.id);i&&(i.cm=qo(s),i.shape="circle"===t.shape?"circle":"square","square"===i.shape?i.angle=Ba(t.angle||"0"):delete i.angle)}else{const i=(e.room_drafts||[]).find(e=>e.id===t.id);i?.segments?.[t.segment||0]&&(i.segments[t.segment||0].cm=s)}this._recordGeometry(this._t("history.physical_edit"),n),this._physicalDialog=null,this._saveConfig()},this._deletePhysicalSelection=()=>{const t=this._physicalSel,e=this._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot(),s="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[s]=(e[s]||[]).filter(e=>e.id!==t.id),e[s].length||delete e[s],this._activeDraftId===t.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftWhole=()=>{const t="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,e=this._curSpaceCfg;if(!t||!e||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===t&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftSegment=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(i<0)return;const s=e.room_drafts[i],o=Math.max(0,Math.min(s.segments.length-1,t.segment||0)),n=[],r=s.points.slice(0,o+1),a=s.points.slice(o+1);if(r.length>=2&&n.push({id:s.id,points:r,segments:s.segments.slice(0,o)}),a.length>=2&&n.push({id:n.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:a,segments:s.segments.slice(o+1)}),2===n.length&&e.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(i,1,...n),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=n.length?{kind:"draft",id:n[0].id}:null,this._recordGeometry(this._t("history.draft_segment_delete"),l),this._saveConfig()},this._keepClosedAsPartitions=()=>{if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const t=this._curSpaceCfg,e=this._path.slice(0,-1);if((t.partitions||[]).length+e.length>2e3)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys];t.partitions||=[];const o=Date.now().toString(36);for(let i=0;it.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._recordGeometry(this._t("history.contour_to_partitions"),i),this._saveConfig(),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this._spaceDialog;if(t)if(t.pickSaved)this._spaceDialog={...t,pickSaved:!1};else{this._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.hass.callWS({type:"houseplan/plans/list"}),e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(t)}))}}},this._aspectJob=null,this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const t=this._glowRadiusCm,e=this._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:e,bgColor:Si(this._settings,{bgColor:null})||null,northDeg:Rs(this._settings,{}),bgMode:As(this._settings,{}),sunRays:Ns(this._settings,{}),weatherEntity:Es(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=Oa(this._serverCfg,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),o=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,config:e.config,layout:e.layout,cm:i,where:o,changed:e.changed,busy:!1}},this._optimizeUndoBusy=!1,this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:mt).map(t=>({...t}));this._rulesDialog={rules:e,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}_startNavMotion(t){if(clearTimeout(this._navMotionTimer),this._navMotionTimer=void 0,window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return this._cancelEditorSwapAnimations(),void(this._navMotion="");this._navMotion=t,this._navMotionTimer=window.setTimeout(()=>{this._navMotionTimer=void 0,this._navMotion="",this.requestUpdate()},190)}_cancelEditorSwapAnimations(){for(const t of this._editorSwapAnimations)t.cancel();this._editorSwapAnimations=[],this.renderRoot.querySelector(".editorchrome")?.classList.remove("resizing")}_animateEditorSwap(t){this.updateComplete.then(()=>{if(!this.isConnected||"swap"!==this._navMotion||window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return;const e=this.renderRoot.querySelector(".editorchrome"),i=e?.querySelector(".editorchrome-inner");if(!e||!i)return;this._cancelEditorSwapAnimations();const s=i.getBoundingClientRect().height;e.classList.add("resizing");const o={duration:190,easing:"cubic-bezier(0.2, 0.7, 0.2, 1)"},n=[];if(t>0&&s>0&&Math.abs(t-s)>.5){const i=e.animate([{height:`${t}px`},{height:`${s}px`}],o);i.id="hp-editor-height-swap",n.push(i)}const r=i.animate([{opacity:.42,transform:"translateY(5px) scale(0.995)"},{opacity:1,transform:"translateY(0) scale(1)"}],o);r.id="hp-editor-content-swap",n.push(r),this._editorSwapAnimations=n,Promise.allSettled(n.map(t=>t.finished)).then(()=>{this._editorSwapAnimations===n&&(this._editorSwapAnimations=[],e.classList.remove("resizing"))})})}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate())}_pickSpace(t){if(t===this._space)return;const e=this._model.map(t=>t.id),i=e.indexOf(this._space),s=e.indexOf(t);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(t,i>=0&&s0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const t=this._model.map(t=>t.id),e=t.indexOf(this._space);this._slideTo(t[(e+1)%t.length],"left"),this._showKioskDots()}}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}_hashSpace(){const t=/(?:^|[#&])space=([^&]+)/.exec(window.location.hash||"");return t?decodeURIComponent(t[1]):""}connectedCallback(){document.addEventListener("visibilitychange",this._vacVisHandler),super.connectedCallback(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>ji(this._serverCfg)),this._config?.kiosk&&Number(this._config?.cycle)>0&&(clearInterval(this._cycleTimer),this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle))),window.addEventListener("hashchange",this._onHashChange),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0));const t=Date.now();"visible"===document.visibilityState&&Va&&(t-Va>=Wa&&(Ka=t),Va=0),(this._warmLongReturn||t-Ka<=15e3)&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._resumeRaf&&(cancelAnimationFrame(this._resumeRaf),this._resumeRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._activityRt.values())clearTimeout(t.timer);window.removeEventListener("keydown",this._keyHandler),clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._navMotionTimer),this._cancelEditorSwapAnimations(),this._slideTimer=void 0,this._navMotionTimer=void 0,this._slide="",this._navMotion="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._resumeSettling=!1,super.disconnectedCallback()}_onKey(t){if("Escape"===t.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void t.stopPropagation();if("Escape"===t.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void(this._infoCard=null);if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return void(this._alignDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}const e=(t.composedPath?.()||[t.target]).some(t=>t?.matches?.('input, textarea, select, [contenteditable="true"]')),i=t.ctrlKey||t.metaKey,s=t.key.toLowerCase(),o=/^[a-z]$/.test(s),n="z"===s||!o&&"KeyZ"===t.code,r="y"===s||!o&&"KeyY"===t.code,a=i&&(n&&t.shiftKey||r),l=i&&n&&!t.shiftKey;if("decor"===this._mode){if((l||a)&&e)return;return a?(t.preventDefault(),void this._redoGeometry()):l?(t.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||e?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():this._furnPalette?this._furnPalette=null:this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(t.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!l&&!a||!e)){if(("Delete"===t.key||"Backspace"===t.key)&&this._physicalSel&&!e)return t.preventDefault(),void this._deletePhysicalSelection();if(a)return t.preventDefault(),void this._redoGeometry();if(l)return t.preventDefault(),this._rszDrag?void this._rszCancelDrag():"draw"!==this._tool&&"partition"!==this._tool||!this._path.length?"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry():void(this._activeDraftId&&this._path.length>1?this._undoGeometry():this._undoPoint());if("Escape"===t.key)return this._physicalDrag||this._physicalRotate?(t.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(t.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length||"partition"===this._tool&&this._path.length?(t.preventDefault(),void this._undoPoint()):this._physicalSel?(t.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():void(this._rszSel?this._rszSel=null:this._tool="draw")):"split"===this._tool?(t.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(t.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(t.preventDefault(),void(this._wallDialog=null)):"openwall"===this._tool||"closewall"===this._tool?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"partition"!==this._tool&&"column"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const t=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const e=this._curSpaceCfg,i=(e.room_drafts||[]).findIndex(t=>t.id===this._activeDraftId);return void(i>=0&&(this._path.length<2?(e.room_drafts.splice(i,1),e.room_drafts.length||delete e.room_drafts,this._activeDraftId=null):e.room_drafts[i]={id:this._activeDraftId,points:this._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentCms.map(t=>({cm:t}))},this._recordGeometry(this._t("history.draft_segment_delete"),t),this._saveConfig()))}this._path=this._path.slice(0,-1)}}static getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(t){for(const t of Ya.values())for(const e of t)clearTimeout(e.evict);Ya.clear(),Xa=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of Ya.values())for(const o of s)t++,o.dlg&&(e++,i.push(o.dlg.kind));return{keys:Ya.size,slots:t,dlgs:e,drafts:i}}setConfig(t){this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...t},this._config.kiosk&&(this._booting=!1,this._bootFading=!1),t.default_floor&&(this._space=t.default_floor);try{this._zoomBySpace=JSON.parse(localStorage.getItem(el)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(sl)||"null");this._kioskScale={icon:Ki(t?.icon),font:Ki(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(tl)||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._layout=e.layout||{},this._serverStorage=!0;const i=this._hashSpace(),s=this._savedNav();i&&this._model.find(t=>t.id===i)?(this._space=i,this._hashApplied=!0):s?.space&&this._model.find(t=>t.id===s.space)?(this._space=s.space,this._navApplied=!0):t.default_floor?this._space=t.default_floor:this._model.find(t=>t.id===this._space)||(this._space=this._model[0]?.id||this._space),s?.mode&&"view"!==s.mode&&!t.kiosk&&(this._canEdit?this._mode=s.mode:this._pendingNavMode=s.mode)}}catch{}"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const t=Za(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=Ya.get(t);if(!s||!s.length)return;const o=s.find(t=>t.owner===this._warmGen);if(o)return this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Wa,clearTimeout(o.evict),o.evict=0,o.freed=0,o.live=!0,this._warmSlot=o,void(this._warmKey=t);const{slot:n,sure:r}=((t,e,i,s)=>{const o=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let n=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=o(i);t<=0||(t>r?(n=i,r=t,a=1):t===r&&a++)}return!n||a>1?{slot:n||l,sure:!1}:{slot:n,sure:!0}})(s,this._warmGen,e,i);n&&(this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=Wa,this._booting=!1,this._bootFading=!1,this._hdrH=n.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)),this._warmKey=t,r?(clearTimeout(n.evict),n.evict=0,n.owner=this._warmGen,n.place=e?new WeakRef(e):null,n.idx=i,n.live=!0,this._warmSlot=n,this._warmVp=n.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:n.hdrH,stageH:n.stageH,vp:null,dlg:null,freed:0,evict:0},s.push(this._warmSlot),this._warmTrim(s)))}_warmIdx(t){const e=t?.children;if(!e)return-1;for(let t=0;t4;){const e=t.findIndex(t=>!t.live);if(e<0)break;clearTimeout(t[e].evict),t.splice(e,1)}}_warmScheduleEvict(t,e){if(clearTimeout(t.evict),!t.dlg)return;const i=t.freed,s=t.owner;t.evict=window.setTimeout(()=>{if(t.evict=0,t.freed!==i||t.owner!==s)return;t.dlg=null;const o=Ya.get(e);if(!t.live&&o&&o.length>1){const e=o.indexOf(t);e>=0&&o.splice(e,1)}},Xa+250)}_warmAdoptViewport(t){const e=this._warmVp;e&&(!this._hashApplied&&this._model.find(t=>t.id===e.space)?(this._space=e.space,this._navApplied=!0,this._mode="view"!==e.mode&&this._canEdit&&!t.kiosk?e.mode:"view",this._pendingNavMode="view"===e.mode||this._canEdit||t.kiosk?null:e.mode,this._zoom=e.zoom,this._view=e.view?{...e.view}:null,this._viewModeSnap=e.snap?{...e.snap}:null,this._tool=e.tool,this._decorTool=e.decorTool,this._showHidden=e.showHidden,this._showFar!==e.showFar&&(this._showFar=e.showFar,this._frame=null),this._selId=e.selId,this._rszSel=e.rszSel,this._decorSel=e.decorSel,this._warmVpArmed=!0):this._warmVp=null)}_warmPatch(t,e=!1){if(this._config?.kiosk)return;const i=Za(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!e)return;const t=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,place:t?new WeakRef(t):null,idx:this._warmIdx(t),live:!0,hdrH:this._hdrH,stageH:0,vp:null,dlg:null,freed:0,evict:0};const s=Ya.get(i)||[];for(s.push(this._warmSlot),Ya.set(i,s),this._warmTrim(s);Ya.size>8;){const t=Ya.keys().next().value;if(void 0===t||t===i)break;for(const e of Ya.get(t)||[])clearTimeout(e.evict);Ya.delete(t)}}Object.assign(this._warmSlot,t)}}_warmViewportState(){return{space:this._space,mode:this._mode,zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._rszSel,decorSel:this._decorSel}}_warmDialogState(){const t=(t,e)=>({kind:t,space:this._space,mode:this._mode,data:e});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog?null:this._openingInfo?t("openingInfo",this._openingInfo.id):this._infoCard?t("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:t("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:t("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:t("marker",this._markerDialog):this._openingDialog?t("opening",this._openingDialog):this._backdropDialog?t("backdrop",this._backdropDialog):this._decorShapeDialog?t("decorShape",this._decorShapeDialog):this._decorTextDialog?t("decorText",this._decorTextDialog):this._roomDialog?t("room",{editId:this._roomEditId,fill:this._roomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:t("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._resumeSettling||this._config?.kiosk)return;const t={vp:this._warmViewportState()};if(this._warmRevivePending||(t.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const e=this.parentNode;t.place=e?new WeakRef(e):null,t.idx=this._warmIdx(e)}this._warmPatch(t)}_warmReviveDialog(){this._warmRevivePending=!1;const t=this._warmSlot;if(this._warmReviveTimer=void 0,!t||!t.dlg)return;const e=t.dlg,i=t.freed;if(t.dlg=null,t.freed=0,clearTimeout(t.evict),t.evict=0,i&&!(Date.now()-i>Xa)&&e.space===this._space&&e.mode===this._mode){switch(e.kind){case"space":this._spaceDialog={...e.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...e.data,busy:!1};break;case"settings":this._settingsDialog={...e.data,busy:!1};break;case"rules":this._rulesDialog={...e.data,busy:!1};break;case"opening":this._openingDialog={...e.data};break;case"backdrop":this._backdropDialog={...e.data};break;case"decorShape":this._decorShapeDialog={...e.data};break;case"decorText":{this._decorTextDialog={...e.data};const t=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:t,end:t};break}case"room":{const t=e.data;this._roomEditId=t.editId,this._roomFill=t.fill,this._roomTempSrc=t.tempSrc,this._roomHumSrc=t.humSrc,this._roomSrcOpen=t.srcOpen,this._roomSrcFilter=t.srcFilter,this._roomNameScale=t.nameScale,this._roomLabelScale=t.labelScale,this._areaSel=t.areaSel,this._nameSel=t.nameSel,this._pendingSplit=t.pendingSplit,this._path=t.path,this._roomDialog=!0;break}case"info":{const t=this._devices.find(t=>t.id===e.data);t&&(this._infoCard=t);break}case"openingInfo":{const t=(this._curSpaceCfg?.openings||[]).find(t=>t.id===e.data);t&&(this._openingInfo=t);break}}this.requestUpdate()}}_cacheSnapshot(){if(this._serverCfg)try{localStorage.setItem(tl,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,layout:this._layout}))}catch{}}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const t=this._serverCfg?.spaces||[];let e=t.length+":";for(const i of t){e+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const t of i.rooms||[]){const i=t.poly?.[0],s=t.poly?.[t.poly.length-1];e+=(t.poly?.length||0)+"."+(t.id||"")+"."+(t.open_to||[]).join("+")+"."+(t.area||"")+"."+JSON.stringify(t.settings||0)+"."+(t.x??"")+","+(t.y??"")+","+(t.w??"")+","+(t.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(s?s[0]+"/"+s[1]:"")+";"}}return e}get _model(){if(!this._serverCfg)return[];const t=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===t)return this._modelCache.model;const e=this._buildModel();return this._modelCache={key:t,model:e},e}_buildModel(){if(!this._serverCfg)return[];const t=this._renderCfg;return rn(t).map((e,i)=>{const s=t.spaces[i]?.plan_url;return e.bg&&s?{...e,bg:{...e.bg,href:s}}:e})}_spaceModel(t){const e=this._model;return e.find(e=>e.id===(t??this._space))||e[0]}get _areaToSpace(){const t={};for(const e of this._model)for(const i of e.rooms)i.area&&(t[i.area]={space:e.id,room:i});return t}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const t=this._serverCfg,e=function(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,iconRules:r}=t,a=!1!==o.group_lights,l=oa(s),h=ia(e,a).filter(t=>!na(e,t.eid,l)),c=new Set(h.map(t=>t.area)),d=Or(e),p=new Set(s.map(t=>t.binding)),u=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(p.has("device:"+t.id))continue;const o=d[t.id]||[],l=Fr(e,t,o);let h=n.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;if(!h&&a&&c.has(s)){const i=(t.name_by_user||t.name||"").trim();"mdi:lightbulb"===sa(e,i,t.model,o,r)&&(h=!0)}h&&u.push("device:"+t.id)}return u}({hass:this.hass,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!e.length&&t.settings?.filter_seeded)return;t.markers=t.markers||[];const i=[];for(const s of e){const e="h"+s.slice(s.indexOf(":")+1);t.markers.push({id:e,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...t.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(t=>!i.includes(t))),t.settings=s,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const t=this._settings.icon_rules;if(!t||!Array.isArray(t)||!t.length)return;const e=JSON.stringify(t);return e!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=e,this._rulesCompiled=gt(t)),this._rulesCompiled}get _fillColors(){return Di(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):_t}willUpdate(t){t.has("_serverCfg")&&this._cfgEpoch++,this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick())}updated(){this._skyRelease(),this._warmSnapshot(),this._dtMeasure();const t=this._stageEl;t&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(t)),t&&this._booting&&!this._bootTimer&&this._bootWatch();const e=this.renderRoot.querySelector(".hdr");if(e&&t&&!this._roHdr){const i=()=>{const e=this.renderRoot.querySelector("ha-card");if(!e)return;const i=t.getBoundingClientRect().top-e.getBoundingClientRect().top,s=Math.min(Math.max(e.getBoundingClientRect().top,0),120),o=Math.round(i+s);o>=0&&o!==this._hdrH&&(this._hdrH=o),o>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:o,stageH:t.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(e),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(t&&!this._view&&this._refitView(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const t=function(t){const e=t?.floors;if(!e||"object"!=typeof e)return[];const i=[];for(const t of Object.values(e))t&&t.floor_id&&i.push({id:t.floor_id,name:t.name||t.floor_id,level:t.level??null});return i.sort((t,e)=>{const i=t.level??1e9,s=e.level??1e9;return i!==s?i-s:t.name.localeCompare(e.name)}),i}(this.hass);t.length?this._importDialog={floors:t.map(t=>({...t,checked:!0}))}:this._openSpaceDialog("create")}}async _loadFromServer(){this._loading=!0,this._loadTries++;try{const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._loadOk=!0,this._serverStorage=!0,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._canOptimizeUndo=!(!t?.can_optimize_undo&&!e?.can_optimize_undo),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;this._geometryHistory.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=i&&Array.isArray(i.spaces)?i:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._layout=e?.layout||{},this._layoutRev=e?.rev??0,this._unsubCfg||(this._unsubCfg=await this.hass.connection.subscribeEvents(t=>{const e=Number(t?.data?.rev??-1);e!==this._cfgRev&&this._reloadConfigOnly(!1,e)},"houseplan_config_updated")),this.hass.callWS({type:"houseplan/trail/get"}).then(t=>{this._vacSrvTrails=t?.trails||{},this.requestUpdate()}).catch(()=>{}),this._unsubTrail||(this._unsubTrail=await this.hass.connection.subscribeEvents(async()=>{try{const t=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=t?.trails||{},this.requestUpdate()}catch{}},"houseplan_trail_updated")),this._unsubLayout||(this._unsubLayout=await this.hass.connection.subscribeEvents(t=>this._onLayoutEvent(Number(t?.data?.rev??-1)),"houseplan_layout_updated"));const s=this._hashSpace(),o=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):o?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===o.space)?(this._space=o.space,this._navApplied=!0):this._norm&&!this._model.find(t=>t.id===this._space)&&(this._space=this._model[0]?.id||this._space),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:this._restoreZoom()}catch(t){if(this._serverCfg)this._scheduleLoadRetry();else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(Qa)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}async _reloadConfigOnly(t=!1,e){if(!t){if(void 0!==e&&e<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,e),400))}try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._geometryHistory.clear(),this._clearGeometryGesture(),this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._canOptimizeUndo=!!t?.can_optimize_undo,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null),this._cacheSnapshot(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(t){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(t)}))}}_scheduleLoadRetry(){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),t.addEventListener?.("ready",this._onConnReady),this._connHooked=t)}_display(t){return this._signer.display(this.hass,t)}_resign(){this._signer.resign(this.hass,ji(this._serverCfg))}_onLayoutEvent(t){t<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{t<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(t){const e=t?.rev;"number"==typeof e&&e>this._layoutRev&&(this._layoutRev=e)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;const t=new Map;for(const e of this._dirtyPos)this._layout[e]&&t.set(e,this._layout[e]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[e,i]of this._sentPos)t.set(e,i);try{const e=await this.hass.callWS({type:"houseplan/layout/get"}),i={...e?.layout||{}};for(const[e,s]of t)i[e]=s;this._layout=i,this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!!e?.can_optimize_undo,this._cacheSnapshot(),this.requestUpdate()}catch{}}_maybeRebuildDevices(){const t=this.hass;if(!t?.devices||!t?.entities||!t?.areas)return;const e=Object.keys(t.devices).length+":"+Object.keys(t.entities).length+":"+Object.keys(t.areas).length+":"+(this._norm?"n":"l")+":"+pa(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=aa({hass:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:t=>this._t(t),iconRules:this._iconRules}),this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime())}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const t=this._devices.filter(t=>!t.marker&&!t.virtual).map(t=>t.id).sort(),e=t.join(",");if(e===this._newSyncKey)return;this._newSyncKey=e;const i=this._settings,{fresh:s,known:o}=function(t,e){if(!Array.isArray(e))return{fresh:[],known:[...t]};const i=new Set(e),s=t.filter(t=>!i.has(t));return{fresh:s,known:s.length?[...e,...s]:e}}(t,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const t=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:o,new_device_ids:t}},this._saveConfig()}}get _newIds(){const t=this._settings.new_device_ids;return new Set(Array.isArray(t)?t:[])}_ackNewDevice(t){if(!this._newIds.has(t)||!this._serverCfg)return;const e=this._settings;this._serverCfg={...this._serverCfg,settings:{...e,new_device_ids:(e.new_device_ids||[]).filter(e=>e!==t)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(t){if(!t)return null;const e=[];for(const i of this._devices){if(i.area!==t||i.virtual)continue;const s=Zr(this.hass,i.entities);null!=s&&e.push(s)}return Qe(e)}_roomBounds(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}_defaultPositions(){const t={},e=this._config?.icon_size??2.5;for(const i of this._model){const s=e/100*Cn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const o=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!o.length)continue;const n=this._roomBounds(e),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(o.length*a/Math.max(l,1)))),c=Math.ceil(o.length/h),d=a/h,p=l/Math.max(c,1),u=o.map((t,e)=>({x:n.x+r+d*(e%h+.5),y:n.y+r+p*(Math.floor(e/h)+.5)}));ei(u,n,s,.5*r),o.forEach((e,i)=>t[e.id]=un(u[i]))}}return t}_pos(t){const e=this._spaceModel(t.space),i=this._layout[t.id];if(i)if(this._norm){if(i.s===t.space)return{x:i.x*ol,y:i.y*ol}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:un(Mn(e))}_savePos(t,e,i){if(this._norm){const s=this._gridPitch,o=Math.round(e/s)*s,n=Math.round(i/s)*s,r=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:mn(o/ol),y:mn(n/ol),...r?{k:r}:{}}}}else this._layout={...this._layout,[t.id]:{x:Math.round(e),y:Math.round(i)}};this._dirtyPos.add(t.id),this._persistLayout()}_coverIndicator(t){return"cover"===t.tapAction?ci(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_displayOf(t){const e=t.marker?.display;return"ripple"===e?"icon_ripple":e||"badge"}_visualSamples(t){let e=[];const i=this._coverIndicator(t),s=Yr(this.hass,[t]);if(i)e.push(Er(this.hass,i));else if(s.length)e.push(...s.map(t=>Er(this.hass,t.eid)));else{const i=Ur(this.hass,t.entities),s=i.length?i:t.primary?[t.primary]:[];e=function(t,e,i){const s=e.map(e=>Er(t,e)),o=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&o.length>1&&Ar(t,e[0])?s.map(t=>"unavailable"===t.availability?t:"off"===t.state?{...t,availability:"unavailable",status:"neutral",activity:"none",edge:"none"}:{...t,status:"neutral",activity:"none",edge:"none"}):s}(this.hass,s,t.entities)}for(const i of t.entities||[]){const t=Er(this.hass,i);"alarm"!==t.status||e.some(t=>t.eid===i)||e.push(t)}return e}_deviceVisual(t){if(t.hidden)return{availability:"available",status:"neutral",activity:"none"};const e=this._visualSamples(t),i=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return $r;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(e);if("alarm"===i.status)return i;if(!this._config?.live_states)return{availability:"available",status:"neutral",activity:"none"};if("unavailable"===i.availability)return i;const s=this._activityRt.get(t.id),o=this._activitySourceKey(e);return s?.sources===o&&s.flashTs&&s.flashKind&&Date.now()-s.flashTst?.device_id===e).map(([t])=>t)}_bindingHasClimate(t){return this._bindingEntities(t).some(t=>t.startsWith("climate."))}_bindingCoverEntity(t){return ci(this._bindingEntities(t))}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!hi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&ta(this.hass,t.primary)?ea(this.hass,t.entities):null}_openMoreInfo(t){t?al(this,"hass-more-info",{entityId:t}):this._showToast(this._t("toast.no_entity"))}_ctxDevice(t,e){"view"===this._mode&&(t.preventDefault(),t.stopPropagation(),e.primary?this._openMoreInfo(e.primary):this._infoCard=e)}_clickDevice(t,e){if(t.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(e);const i=this._coverIndicator(e),s=this._actEntity(e),o=s?s.split(".")[0]:null,n=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=Yr(this.hass,[e]).filter(t=>"controls"===t.via).map(t=>t.eid);if("toggle"===e.tapAction&&r.length){const t=(a=r.map(t=>this.hass.states[t]?.state),a.some(t=>"on"===t)?"turn_off":"turn_on");return void n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant",t,{entity_id:r}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}var a;const l=function(t,e,i,s){const o=t||e||("light"===i?"toggle":"info");return"more-info"===o?"more-info":"run"===o?"run"===t?"run":"info":"cover"===o?"cover"!==t||"cover"!==i||hi.has(String(s||""))?"info":"cover":"toggle"!==o||!i||li.has(i)?"info":"toggle"===t?"toggle":ai.has(i)?"cover"===i&&hi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,o,s?this.hass.states[s]?.attributes?.device_class:null);if("run"===l){const t=e.marker?.tap_target||"",i=function(t){const e=String(t||"").split(".")[0];return"automation"===e?{domain:"automation",service:"trigger"}:"script"===e?{domain:"script",service:"turn_on"}:"scene"===e?{domain:"scene",service:"turn_on"}:null}(t),s=this.hass.states[t];if(!i||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||t;return void n(this._t("confirm.tap_run",{name:o}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>{this._stampActivity(e.id,"event",this._activitySourceKey(this._visualSamples(e))),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}if("cover"===l&&i){const t=function(t){const e=String(t||"");return"closed"===e?"open_cover":"open"===e?"close_cover":"opening"===e||"closing"===e?"stop_cover":"toggle"}(this.hass.states[i]?.state);return void n(this._t("confirm.tap_cover",{name:e.name}),()=>{this.hass.callService("cover",t,{entity_id:i}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}"toggle"===l&&e.primary?n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant","toggle",{entity_id:e.primary}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}):"more-info"===l&&e.primary?this._openMoreInfo(e.primary):this._infoCard=e}_t(t,e){return ua(pa(this.hass,this._config?.language),t,e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(t){const e=[];for(const i of this._devices){if(i.space!==t.id||i.hidden)continue;const s=this._pos(i);e.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(t.id===this._space){for(const t of this._openingsR){const i=Number(t.angle)*Math.PI/180,s=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2,n=vn([[t.rx-s,t.ry-o],[t.rx+s,t.ry+o]]);n&&e.push(n)}const i=this._decorH;for(const t of this._decorList){const s=vn("line"===t.kind?[[t.x1*ol,t.y1*i],[t.x2*ol,t.y2*i]]:"text"===t.kind?[[t.x*ol,t.y*i]]:js({x:t.x*ol,y:t.y*i,w:t.w*ol,h:t.h*i,angle:t.angle}));s&&e.push(s)}for(const i of this._physicalBodiesR(t)){const t=vn(i);t&&e.push(t)}}return bn(t,e)}_frameOf(){const t=this._spaceModel(),e=this._frame,i="view"!==this._mode;if(e&&e.id===t.id&&this._bdDrag)return e;if(e&&e.id===t.id&&e.model===t&&e.layout===this._layout&&e.devs===this._devices&&e.far===this._showFar&&e.grow===i)return e;const s=xn(this._contentItems(t));let o=s.all||Sn(t),n=this._showFar?o:s.core||Sn(t);return e&&e.id===t.id&&i&&e.grow&&(n=rl(e.rect,n),o=rl(e.all,o)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:n,all:o,outliers:s.outliers},this._frame}_baseVb(){const t=this._frameOf().rect;return[t.x,t.y,t.w,t.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -3098,7 +3117,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow style="left:${s.toFixed(1)}%;top:${o.toFixed(1)}%" @click=${t=>{t.stopPropagation(),this._fitAll()}}> - `}_stageAspect(){const t=this._stageEl,e=this._baseVb();return t&&t.clientHeight?t.clientWidth/t.clientHeight:e[2]/e[3]}_viewOr(t){return this._view&&this._view.w?this._view:ti(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),o=i?.clientWidth||1,n=i?.clientHeight||1;return[s.x+t/o*s.w,s.y+e/n*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const o=1*Math.max(e,s),n=i-o,r=i+s-e+o;return Math.max(Math.min(n,r),Math.min(Math.max(n,r),t))};return{w:t.w,h:t.h,x:i(t.x,t.w,e.x,e.w),y:i(t.y,t.h,e.y,e.h)}}_applyView(t,e,i){const s=this._baseVb(),o=ti(s,this._stageAspect()),n=Math.min(hl.ZOOM_MAX,Math.max(hl.ZOOM_MIN,t)),r=o.w/n,a=o.h/n,l=this._viewOr(s),h=e??l.x+l.w/2,c=i??l.y+l.h/2;this._zoom=n,this._view=this._clampView({x:h-r/2,y:c-a/2,w:r,h:a},o)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const t=()=>{if(!this._booting)return;const e=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=e);const s=e-this._bootStart;s>=1200||s>=700&&i>0&&e-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(t,100)};this._bootTimer=window.setTimeout(t,100)}_bootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1;const t=this._stageEl?.clientHeight??0;!this._config?.kiosk&&t>0&&this._warmPatch({hdrH:this._hdrH,stageH:t,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._kiosk||"view"!==this._mode||this._booting||this._resumeSettling||(this._resumeSettling=!0,this._resumeStarted=performance.now(),this._resumeLastSize="",this._resumeLastChange=this._resumeStarted,this._viewportInvalidAt=0,this.requestUpdate(),this._resumeRaf&&cancelAnimationFrame(this._resumeRaf),this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick()))}_resumeSettleTick(){if(this._resumeRaf=0,!this._resumeSettling||!this.isConnected)return;if(this._kiosk||"view"!==this._mode)return this._resumeSettling=!1,void this.requestUpdate();const t=performance.now(),e=this._stageEl,i=!!e&&e.clientWidth>0&&e.clientHeight>0;if(i){const i=`${e.clientWidth}x${e.clientHeight}`;i!==this._resumeLastSize&&(this._resumeLastSize=i,this._resumeLastChange=t)}const s=t-this._resumeStarted;if(i&&s>=220&&t-this._resumeLastChange>=80||s>=750){const t=this._view;return this._resumeSettling=!1,i&&this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),void this.requestUpdate()}this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick())}_refitView(){const t=this._stageEl;if(!t||"visible"!==document.visibilityState||t.clientWidth<=0||t.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));if(this._viewportInvalidAt){const t=Date.now()-this._viewportInvalidAt;if(this._viewportInvalidAt=0,t>=Ua&&!this._resumeSettling&&(this._beginResumeSettle(),this._resumeSettling))return}if(this._resumeSettling)return;const e=this._view;this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const o=ti(this._baseVb(),this._stageAspect()),n=Math.min(hl.ZOOM_MAX,Math.max(hl.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),h=o.w/n,c=o.h/n;this._zoom=n,this._view=this._clampView({x:l[0]-t/r*h,y:l[1]-e/a*c,w:h,h:c},o)}_onWheel(t){const e=this._stageEl;if(!e)return;t.preventDefault();const i=e.getBoundingClientRect(),s=t.deltaY<0?1.15:1/1.15;this._zoomAt(t.clientX-i.left,t.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(t){const e=this._stageEl;e&&(this._zoomAt(e.clientWidth/2,e.clientHeight/2,this._zoom*(t>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const t=this._baseVb();this._zoom=1,this._view=ti(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(tl,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const t=this._zoomBySpace[this._space]||1;this._zoom=t;const e=this._stageEl;if(e&&e.clientHeight){const e=this._baseVb();return this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const e=this._baseVb();this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),this.requestUpdate()})}_stagePointerDown(t){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:t.clientX,y:t.clientY,id:t.pointerId},t.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&t.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&t.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(t))return;this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:t.clientX,sy:t.clientY,vx:e.x,vy:e.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y);this._pinchStart={dist:e,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(t){if(this._physicalRotate?.pid!==t.pointerId)if(this._physicalDrag?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId){if(this._decorDraft?.pid===t.pointerId){const e=this._decorDraft;let i=this._decorSnap(this._svgPoint(t),t.pointerType);if(t.shiftKey&&("rect"===e.kind||"ellipse"===e.kind)){const t=i[0]-e.a[0],s=i[1]-e.a[1],o=Math.max(Math.abs(t),Math.abs(s));i=this._snap([e.a[0]+(t<0?-o:o),e.a[1]+(s<0?-o:o)])}return void(this._decorDraft={...e,b:i})}if(this._decorMove?.pid!==t.pointerId)if(this._pointers.has(t.pointerId)){if(this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(t),this._pinchStart&&this._pointers.size>=2){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),s=(t[0].x+t[1].x)/2-i.left,o=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,o,this._pinchStart.zoom*e),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const e=t.clientX-this._panStart.sx,i=t.clientY-this._panStart.sy;Math.abs(e)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer)),null===this._panLock&&Math.abs(e)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(e)>1.5*Math.abs(i)?"swipe":"pan");const s=this._stageEl;if("pan"===this._panLock&&s){const t=this._baseVb(),o=this._viewOr(t),n=ti(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*o.w,y:this._panStart.vy-i/(s.clientHeight||1)*o.h,w:o.w,h:o.h},n)}}}else this._markupMove(t);else this._decorMoveUpdate(t)}else this._bdMove(t);else this._dtMove(t);else this._physicalMove(t);else this._physicalRotateMove(t)}_stagePointerUp(t){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const e=this._swipeStart;if(this._swipeStart=null,e&&e.id===t.pointerId){const i=t.clientX-e.x,s=t.clientY-e.y;if(Math.abs(i)+Math.abs(s)<8){const t=Date.now();t-this._lastTap<350&&this._resetZoom(),this._lastTap=t}const o="pan"===this._panLock?null:function(t,e,i,s,o,n=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);o&&(this._slideTo(o,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid!==t.pointerId)if(this._physicalRotate?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId){if(this._decorMove?.pid===t.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0))}else this._decorCommitDraft();else this._bdUp();else this._dtUp();else this._physicalRotateUp(t);else this._physicalUp(t)}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),rl(window,"location-changed",{replace:!1}))}_pointerDown(t,e){if("plan"===this._mode)return;if("view"===this._mode)return this._holdFired=!1,clearTimeout(this._holdTimer),void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._infoCard=e},600));t.preventDefault();const i=this._pos(e);this._drag={id:e.id,sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},ll(t),this._tip=null}_pointerMove(t,e){if(!this._drag||this._drag.id!==e.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),o=i.getBoundingClientRect(),n=this._viewOr(s),r=(t.clientX-this._drag.sx)/o.width*n.w,a=(t.clientY-this._drag.sy)/o.height*n.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=_n(this._drag.ox+r),h=_n(this._drag.oy+a);this._savePos(e,l,h)}_pointerUp(t,e){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==e.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=e.id,window.setTimeout(()=>this._drag=null,0))}_showToast(t){this._toast=t,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}get _noHover(){return hl._noHoverMq||hl._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(hl._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,o){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:o})}get _gridPitch(){return cn}get _cellCm(){const t=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(t)&&t>0?t:5}_fmtLen(t,e){const i=function(t,e,i,s){return Math.hypot(e[0]-t[0],e[1]-t[1])/i*s}(t,e,this._gridPitch,this._cellCm);return Ce(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const t=this._rszPreview;return t&&t.space===this._space?t.sp:this._serverCfg?.spaces.find(t=>t.id===this._space)}get _renderCfg(){const t=this._rszPreview;return t&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(e=>e.id===t.space?t.sp:e)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,sl}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Te(t?.rooms||[]).map(t=>[t[0]*sl,t[1]*e,t[2]*sl,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(el)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(el,JSON.stringify({space:this._space,mode:this._mode}))}catch{}}_setMode(t){if(this._kiosk&&"view"!==t)return;if(this._mode===t)return;if(this._bootSoftCancel(),("plan"===t||"decor"===t)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));const e=this._mode,i="view"!==e&&"view"!==t,s=i&&this.renderRoot.querySelector(".editorchrome-inner")?.getBoundingClientRect().height||0;i||this._cancelEditorSwapAnimations(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const o=!this._spaceModel().bg&&"view"===t!=("view"===this._mode);if("view"===this._mode&&"view"!==t){const t=this._view;this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?t.x+t.w/2:void 0,cy:t?t.y+t.h/2:void 0}}if("plan"===e&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=t,this._editorChromeMode="view"===t?e:t,this._startNavMotion("view"===e?"enter":"view"===t?"exit":"swap"),i&&this._animateEditorSwap(s),o&&(this._zoom=1,this._view=null),"view"===t){const t=this._viewModeSnap;this._viewModeSnap=null,t&&t.space===this._space?(this._zoom=t.zoom,this._view=null,requestAnimationFrame(()=>{this._stageEl&&"view"===this._mode&&this._space===t.space&&(this._applyView(t.zoom,t.cx,t.cy),this._saveZoom(),this.requestUpdate())})):t&&this._restoreZoom()}this._path=[],this._cursorPt=null,this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=Xs(Ys,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?Xs(Ys,this._imperial):this._drawWallField}get _drawWallCm(){const t=Wa(this._drawWallFieldValue);if(null==t||t<=0)return null;const e=this._imperial?2.54*t:t,i="column"===this._tool?Lo:100;return e>=1&&e<=i?e:null}get _drawWallMaxCm(){return"column"===this._tool?Lo:100}_showPhysicalRange(t=this._drawWallMaxCm){this._showToast(this._t("toast.physical_range",{max:Xs(t,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_limitReached(t){const e=this._curSpaceCfg;if(!e)return!0;const i="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=2e3:(e.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this._gridPitch;return[_n(Me(t[0],e)),_n(Me(t[1],e))]}_snapDrawPoint(t,e=!1){const i=this._path[this._path.length-1],s=e&&i?function(t,e,i,s=1/0){if(!(i>0&&t?.every(Number.isFinite)&&e?.every(Number.isFinite)))return[e[0],e[1]];const o=e[0]-t[0],n=e[1]-t[1];if(Math.abs(o)+Math.abs(n)<=1e-12)return[t[0],t[1]];const r=Math.PI/4;let a=Math.atan2(n,o);a<0&&(a+=2*Math.PI);const l=Math.floor(a/r+.5)%8,[h,c]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=h*h+c*c;let p=Math.max(0,Math.round((o*h+n*c)/(i*d)));if(Number.isFinite(s)&&s>=0){const e=(t,e)=>{if(!e)return 1/0;const o=e>0?s-t:t+s;return Math.max(0,Math.floor((o+1e-9*i)/i))};p=Math.min(p,e(t[0],h),e(t[1],c))}return[t[0]+h*p*i,t[1]+c*p*i]}(i,t,this._gridPitch,ln):t;return this._snap(s)}_samePt(t,e){return Ee(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this._serverCfg?.spaces||[]){delete t.segments;const e=new Set,i=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),s=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=an);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&i(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&s(t.id)).map(t=>({...t,cm:Math.max(1,Math.min(100,Number(t.cm)||15))})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&i(t.id)&&o(t.center)&&s(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qo(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Ho(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&i(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&s(t.id)).map(t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],i=[];for(let s=1;st.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Kn(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=po(t.walls,t.rooms||[],dn,1,e),t.walls.length||delete t.walls}}}get _cfgWriting(){return this._writesPending>0}_writeConfig(){this._writesPending++,this._writeChain=this._writeChain.catch(()=>{}).then(async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const t=await this.hass.callWS({type:"houseplan/config/set",config:this._serverCfg,expected_rev:this._cfgRev});this._cfgRev=t?.rev??this._cfgRev+1});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshot(t=this._space){const e=this._serverCfg?.spaces.find(e=>e.id===t);if(!e)return null;const i=t=>JSON.parse(JSON.stringify(t)),s={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==e[t]&&(s[t]=e[t]);return{spaceId:t,rooms:i(e.rooms||[]),...Array.isArray(e.openings)?{openings:i(e.openings)}:{},...Array.isArray(e.walls)?{walls:i(e.walls)}:{},...Array.isArray(e.open_spans)?{open_spans:i(e.open_spans)}:{},...Array.isArray(e.room_drafts)?{room_drafts:i(e.room_drafts)}:{},...Array.isArray(e.partitions)?{partitions:i(e.partitions)}:{},...Array.isArray(e.wall_columns)?{wall_columns:i(e.wall_columns)}:{},...Array.isArray(e.decor)?{decor:i(e.decor)}:{},plan_transform:s}}_recordGeometry(t,e){if(!e)return;const i=this._geometrySnapshot(e.spaceId);i&&JSON.stringify(e)!==JSON.stringify(i)&&(this._geometryHistory.push({name:t,before:e,after:i}),this.requestUpdate())}_clearGeometryGesture(){this._path=[],this._cursorPt=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._openWallAnchor=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===t.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid!==t.pointerId&&this._physicalRotate?.pid!==t.pointerId)return this._decorDraft?.pid===t.pointerId?(this._decorDraft=null,void this.requestUpdate()):void(this._decorMove?.pid!==t.pointerId&&this._dtDrag?.pid!==t.pointerId&&this._bdDrag?.pid!==t.pointerId?(this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null)):this._cancelDecorGesture());this._cancelPhysicalGesture()}_applyGeometryState(t){const e=this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(!e)return!1;const i=t=>JSON.parse(JSON.stringify(t));e.rooms=i(t.rooms),void 0!==t.openings?e.openings=i(t.openings):delete e.openings,void 0!==t.walls?e.walls=i(t.walls):delete e.walls,void 0!==t.open_spans?e.open_spans=i(t.open_spans):delete e.open_spans,void 0!==t.room_drafts?e.room_drafts=i(t.room_drafts):delete e.room_drafts,void 0!==t.partitions?e.partitions=i(t.partitions):delete e.partitions,void 0!==t.wall_columns?e.wall_columns=i(t.wall_columns):delete e.wall_columns,void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];return Object.assign(e,i(t.plan_transform||{})),this._clearGeometryGesture(),this._space!==t.spaceId&&(this._space=t.spaceId,this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&qe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&Ve(t,i)})}_pointInRoom(t,e){return e.poly?Ie(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!cs(t))return!0;const e=t.length,i=.001;for(let s=0;st?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(e.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._rszDrag||e.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...this._spaceModel().rooms].reverse().find(t=>this._pointInRoom(i,t));return void(this._rszSel=t?.id||null)}if("delroom"===this._tool)return void this._deleteRoomClick(i);if("opening"===this._tool)return void this._openingClick(i);if("merge"===this._tool)return void this._mergeClick(i);if("wallthick"===this._tool)return void this._wallThickClick(i);if("openwall"===this._tool)return void this._openWallClick(i);if("closewall"===this._tool)return void this._closeWallClick(i);if("split"===this._tool)return void this._splitClick(i);if("partition"===this._tool)return void this._partitionClick(i,t.shiftKey);if("column"===this._tool)return void this._columnClick(i);const s=this._snapDrawPoint(i,t.shiftKey);if(t.ctrlKey||t.metaKey)return t.preventDefault(),void this._closeRoomContour(!0);const o=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length){const t=this._draftEndAt(s);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(t=>[...t]):t.draft.points.map(t=>[...t]),void(this._draftSegmentCms=t.reverse?[...t.draft.segments].reverse().map(t=>t.cm):t.draft.segments.map(t=>t.cm))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[s]))}const n=this._path[this._path.length-1];if(this._samePt(s,n))return;if(o)return void this._closeRoomContour();const r=this._draftEndAt(s,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(null==this._drawWallCm)return void this._showPhysicalRange(100);if(this._path.length>=500)return void this._showToast(this._t("toast.physical_limit"));const a=this._curSpaceCfg;!this._activeDraftId&&(a?.room_drafts||[]).length>=200||this._draftSegmentCount(a)>=2e3?this._showToast(this._t("toast.physical_limit")):(this._path=[...this._path,s],this._persistActiveDraftSegment())}_draftEndAt(t,e){const i=this._viewOr(this._baseVb()),s=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?i.w/this._stageEl.clientWidth*12:0);for(const i of this._spaceModel().room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const o=i.points[0],n=i.points[i.points.length-1];if(Math.hypot(t[0]-n[0],t[1]-n[1])<=s)return{draft:i,reverse:!1};if(Math.hypot(t[0]-o[0],t[1]-o[1])<=s)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this._curSpaceCfg;if(!e||!this._path.length)return;const i=Array.isArray(e.room_drafts)?e.room_drafts:[],s=this._activeDraftId?i.find(t=>t.id===this._activeDraftId):null,o=i.find(e=>e.id===t.draft.id);if(!o)return;const n=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),r=t.reverse?(o.segments||[]).map(t=>({...t})):[...o.segments||[]].reverse().map(t=>({...t})),a=this._path[this._path.length-1],l=this._samePt(a,n[0]),h=l?null:this._drawWallCm;if(!l&&null==h)return void this._showPhysicalRange(100);const c=this._draftSegmentCms.map((t,e)=>({...s?.segments?.[e]||{},cm:t})),d=[...this._path.map(t=>[...t]),...l?n.slice(1):n],p=[...c,...null==h?[]:[{cm:h}],...r],u=d.length>=4&&this._samePt(d[0],d[d.length-1]),_=u?d.slice(0,-1):d,m=u?p.slice(0,-1):p;if(_.length>500)return void this._showToast(this._t("toast.physical_limit"));const g=(s?.segments?.length||0)+(o.segments?.length||0);if(this._draftSegmentCount(e)-g+m.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(u){if(this._contourSelfIntersects(_)||Ye(_)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(_);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const f=this._geometrySnapshot(),v=this._activeDraftId||t.draft.id,y={...s||o,id:v,points:_.map(t=>[t[0]/sl,t[1]/sl]),segments:m};e.room_drafts=i.filter(e=>e.id!==t.draft.id&&(!this._activeDraftId||e.id!==this._activeDraftId)),e.room_drafts.push(y),this._activeDraftId=v,this._resumeDraftBySpace[this._space]=v,this._draftSegmentCms=m.map(t=>Number(t.cm)),this._path=_,this._physicalSel=null,this._recordGeometry(this._t("history.draft_merge"),f),this._saveConfig(),u&&(this._closingWallCm=Number(p[p.length-1]?.cm)||Ys,this._path=[..._,[..._[0]]],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0)}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const t=this._drawWallCm;if(null==t)return;this._draftSegmentCms=[...this._draftSegmentCms,t];const e=this._geometrySnapshot(),i=this._curSpaceCfg;i.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const s=i.room_drafts.findIndex(t=>t.id===this._activeDraftId),o={...s>=0?i.room_drafts[s]:{},id:this._activeDraftId,points:this._path.map(t=>[t[0]/sl,t[1]/sl]),segments:this._draftSegmentCms.map((t,e)=>({...s>=0?i.room_drafts[s]?.segments?.[e]:{},cm:t}))};s>=0?i.room_drafts[s]=o:i.room_drafts.push(o),this._recordGeometry(this._t("history.draft_segment"),e),this._saveConfig()}_partitionClick(t,e){const i=this._snapDrawPoint(t,e);if(!this._path.length)return void(this._path=[i]);const s=this._path[0];if(this._samePt(s,i))return;const o=this._drawWallCm;if(null==o)return void this._showPhysicalRange(100);if(!this._curSpaceCfg||this._limitReached("partition"))return;const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.partitions||=[];const a="partition-"+Date.now().toString(36);r.partitions.push({id:a,a:[s[0]/sl,s[1]/sl],b:[i[0]/sl,i[1]/sl],cm:o}),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._recordGeometry(this._t("history.partition_add"),n),this._saveConfig()}_columnClick(t){const e=this._snap(t),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Lo);if(!this._curSpaceCfg||this._limitReached("column"))return;const s={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qo(i),angle:0},o=(this._spaceModel().wall_columns||[]).find(t=>Xo(t,s,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...s,center:[e[0]/sl,e[1]/sl]}),this._recordGeometry(this._t("history.column_add"),n),this._saveConfig()}_openPhysicalDialog(t,e,i){const s=this._spaceModel();if("partition"===t){const i=s.partitions.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===t){const i=s.wall_columns.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),shape:i.shape,angle:String("square"===i.shape?Ho(i.angle):0)})}else{const o=s.room_drafts.find(t=>t.id===e),n=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,i||0));o?.segments[n]&&(this._physicalDialog={kind:t,id:e,segment:n,cm:Xs(o.segments[n].cm,this._imperial),length:this._fmtLen(o.points[n],o.points[n+1])})}}_physicalDown(t,e,i){t.stopPropagation(),ll(t);const s=this._spaceModel(),o=this._svgPoint(t),n=[];for(const t of[...s.wall_columns].reverse())Zo(o,jo(t,this._cellCm,this._gridPitch))&&n.push({kind:"column",id:t.id});for(const t of[...s.partitions].reverse()){const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);e&&Zo(o,e)&&n.push({kind:"partition",id:t.id})}n.some(t=>t.kind===e&&t.id===i)||n.unshift({kind:e,id:i});const r=n.map(t=>`${t.kind}:${t.id}`).sort().join("|"),a=performance.now(),l=this._physicalPickCycle,h=n.length>1&&l?.signature===r&&a-l.at>380&&a-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%n.length:Math.max(0,n.findIndex(t=>t.kind===e&&t.id===i));this._physicalPickCycle={signature:r,index:h,x:t.clientX,y:t.clientY,at:a},e=n[h].kind,i=n[h].id;const c="partition"===e?s.partitions.find(t=>t.id===i):s.wall_columns.find(t=>t.id===i);c&&(this._physicalSel={kind:e,id:i},this._physicalDrag={pid:t.pointerId,kind:e,id:i,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,i){const s="partition"===t?[e.a,e.b]:[e.center],o=s.map(t=>t[0]),n=s.map(t=>t[1]);return[Math.max(-an*sl-Math.min(...o),Math.min(an*sl-Math.max(...o),i[0])),Math.max(-an*sl-Math.min(...n),Math.min(an*sl-Math.max(...n),i[1]))]}_physicalMove(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s="partition"===e.kind?e.base.a:e.base.center,o=this._snap([s[0]+i[0]-e.start[0],s[1]+i[1]-e.start[1]]),n=this._clampPhysicalDelta(e.kind,e.base,[o[0]-s[0],o[1]-s[1]]);this._physicalDrag={...e,delta:n,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);if(!this._curSpaceCfg)return;const i=this._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;t&&(t.a=[(s.a[0]+e.delta[0])/sl,(s.a[1]+e.delta[1])/sl],t.b=[(s.b[0]+e.delta[0])/sl,(s.b[1]+e.delta[1])/sl])}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),s=e.base,o={...s,center:[s.center[0]+e.delta[0],s.center[1]+e.delta[1]]};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));t&&(t.center=[(s.center[0]+e.delta[0])/sl,(s.center[1]+e.delta[1])/sl])}this._recordGeometry(this._t("history.physical_move"),e.before),this._saveConfig()}_registerPhysicalTap(t,e,i){const s=performance.now(),o=this._physicalLastTap?.kind===t&&this._physicalLastTap.id===e&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:t,id:e,segment:i,at:s},o&&(this._physicalLastTap=null,this._openPhysicalDialog(t,e,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape)return;t.preventDefault(),t.stopPropagation(),ll(t);const i=this._svgPoint(t);this._physicalSel={kind:"column",id:e.id},this._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,baseAngle:Ho(e.angle),angle:Ho(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const i=this._svgPoint(t),s=180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,o=e.baseAngle+s-e.startAngle,n=Ho(t.shiftKey?o:5*Math.round(o/5));this._physicalRotate={...e,angle:n,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;if(t.preventDefault(),t.stopPropagation(),this._physicalRotate=null,!e.moved||!this._curSpaceCfg)return;const i=this._spaceModel().wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const s={...i,angle:e.angle};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,s,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);o&&(o.angle=e.angle,this._recordGeometry(this._t("history.physical_edit"),e.before),this._saveConfig())}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Pe(e):null;i&&t.push({id:e.id,poly:i})}return t}_rszOpenings(){return this._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen}))}_rszOpts(){return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch}}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const o=JSON.parse(i.snap),n={...s,rooms:o.rooms,openings:o.openings,walls:o.walls,open_spans:o.open_spans};Array.isArray(o.open_spans)&&o.open_spans.length||delete n.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=n.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/sl,t[1]/r]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[t,i]of Object.entries(e)){const e=(n.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/sl,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=n.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const o=s.poly.map(t=>[t[0]*sl,t[1]*r]);if(e.poly.length===o.length)for(let t=0;tt.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const o=[...s.poly[i]],n=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:o,b:n,n:ns(s.poly,i)}}(s,e,i);o&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:o,rooms:s,openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszCornerDown(t,e,i,s){"resize"!==this._tool||this._rszDrag||(t.stopPropagation(),t.preventDefault(),ll(t),this._rszDrag={kind:"scale",pid:t.pointerId,roomId:e,fixed:s,span0:Math.hypot(i[0]-s[0],i[1]-s[1])||1,rooms:this._rszRooms(),openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszMove(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t);if("edge"===e.kind){const t=e.plan,s=(i[0]-t.a[0])*t.n[0]+(i[1]-t.a[1])*t.n[1],o=this._snap([t.a[0]+t.n[0]*s,t.a[1]+t.n[1]*s]);let n=(o[0]-t.a[0])*t.n[0]+(o[1]-t.a[1])*t.n[1];if(n=function(t,e,i,s,o,n){if(!Number.isFinite(s)||Math.abs(s)<1e-9)return 0;const r=Math.sign(s);let a=Math.abs(s);const l=Math.max(o,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(gs(t,e,i,s,n))return s}return 0}(e.rooms,e.openings,t,n,this._gridPitch,this._rszOpts()),n===e.d&&e.moved)return;e.d=n,e.moved=!0;const r=us(e.rooms,e.openings,t,n,this._rszOpts().eps);e.changed=Object.keys(r.polys),this._rszApplyPreview(r.polys,r.openings),this._rszLive=this._rszEdgeLabels(r,t)}else{const t=e.fixed,s=this._snap(i);let o=Math.hypot(s[0]-t[0],s[1]-t[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,o,n){if(!Number.isFinite(o)||o<=0)return 1;if(vs(t,e,i,s,o,n))return o;let r=1,a=o;for(let o=0;o<28;o++){const o=(r+a)/2;vs(t,e,i,s,o,n)?r=o:a=o}return r}(e.rooms,e.openings,e.roomId,t,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const n=e.rooms.find(t=>t.id===e.roomId),r=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),a=fs(n,e.openings,r,t,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:a.poly},a.openings),this._rszLive=this._rszScaleLabels(a.poly)}this.requestUpdate()}_rszUp(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._rszPreview;this._rszDrag=null,this._rszLive=null,this._rszPreview=null;if(!(e.moved&&("edge"===e.kind?Math.abs(e.d)>1e-9:Math.abs(e.k-1)>1e-9))||!i)return this._cfgEpoch++,void this.requestUpdate();const s=JSON.parse(e.snap),o=this._curSpaceCfg;if(o){o.rooms=i.sp.rooms,o.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?o.walls=i.sp.walls:delete o.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?o.open_spans=i.sp.open_spans:delete o.open_spans;for(const t of e.changed){const e=o.rooms.find(e=>e.id===t);e?.poly&&(e.poly=hs(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(o.walls)&&o.walls.length&&(o.walls=po(o.walls,o.rooms||[],dn,1,this._cfgOpenCuts()),o.walls.length||delete o.walls)}this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._recordGeometry(this._t("history.resize_room"),s),this._saveConfig(),this.requestUpdate()}_rszCancelDrag(){this._rszDrag&&(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._cfgEpoch++,this.requestUpdate())}_rszPointerCancel(t){const e=this._rszDrag;e&&e.pid===t.pointerId&&(t.stopPropagation(),this._rszCancelDrag())}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],o=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,n=o.length,r=e.edge,a=(r+1)%n;for(const[t,e]of[[o[(r-1+n)%n],o[r]],[o[r],o[a]],[o[a],o[(a+1)%n]]])s.push({x:(t[0]+e[0])/2,y:(t[1]+e[1])/2,text:this._fmtLen(t,e)});const l="mi"===this.hass?.config?.unit_system?.length,h=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],c=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs),p=this._physicalBodiesR();for(const e of h){const o=t.polys[e]||i.rooms.find(t=>t.id===e).poly,n=c.length&&Ao(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,c,d,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||o,r=We(n),a=p.length?Ko(Vo(n,p))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(n,this._gridPitch,this._cellCm);s.push({x:r[0],y:r[1],text:bs(a,l),area:!0})}return s}_rszScaleLabels(t){const e="mi"===this.hass?.config?.unit_system?.length,i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.max(...i)-Math.min(...i),n=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Ao([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||t,l=We(a),h=this._physicalBodiesR(),c=h.length?Ko(Vo(a,h))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(a,this._gridPitch,this._cellCm);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[o,0])} × ${this._fmtLen([0,0],[n,0])}`},{x:l[0],y:l[1],text:bs(c,e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),o=`M ${s(-.7*i)} 0 H ${s(.7*i)} M 0 ${s(-.22*i)} V ${s(-i)} M ${s(-.32*i)} ${s(-.6*i)} L 0 ${s(-i)} L ${s(.32*i)} ${s(-.6*i)} M 0 ${s(.22*i)} V ${s(i)} M ${s(-.32*i)} ${s(.6*i)} L 0 ${s(i)} L ${s(.32*i)} ${s(.6*i)}`,n=[],r=this._rszRooms();for(const t of r)for(let i=0;i`}_stageAspect(){const t=this._stageEl,e=this._baseVb();return t&&t.clientHeight?t.clientWidth/t.clientHeight:e[2]/e[3]}_viewOr(t){return this._view&&this._view.w?this._view:ti(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),o=i?.clientWidth||1,n=i?.clientHeight||1;return[s.x+t/o*s.w,s.y+e/n*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const o=1*Math.max(e,s),n=i-o,r=i+s-e+o;return Math.max(Math.min(n,r),Math.min(Math.max(n,r),t))};return{w:t.w,h:t.h,x:i(t.x,t.w,e.x,e.w),y:i(t.y,t.h,e.y,e.h)}}_applyView(t,e,i){const s=this._baseVb(),o=ti(s,this._stageAspect()),n=Math.min(cl.ZOOM_MAX,Math.max(cl.ZOOM_MIN,t)),r=o.w/n,a=o.h/n,l=this._viewOr(s),h=e??l.x+l.w/2,c=i??l.y+l.h/2;this._zoom=n,this._view=this._clampView({x:h-r/2,y:c-a/2,w:r,h:a},o)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const t=()=>{if(!this._booting)return;const e=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=e);const s=e-this._bootStart;s>=1200||s>=700&&i>0&&e-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(t,100)};this._bootTimer=window.setTimeout(t,100)}_bootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1;const t=this._stageEl?.clientHeight??0;!this._config?.kiosk&&t>0&&this._warmPatch({hdrH:this._hdrH,stageH:t,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._kiosk||"view"!==this._mode||this._booting||this._resumeSettling||(this._resumeSettling=!0,this._resumeStarted=performance.now(),this._resumeLastSize="",this._resumeLastChange=this._resumeStarted,this._viewportInvalidAt=0,this.requestUpdate(),this._resumeRaf&&cancelAnimationFrame(this._resumeRaf),this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick()))}_resumeSettleTick(){if(this._resumeRaf=0,!this._resumeSettling||!this.isConnected)return;if(this._kiosk||"view"!==this._mode)return this._resumeSettling=!1,void this.requestUpdate();const t=performance.now(),e=this._stageEl,i=!!e&&e.clientWidth>0&&e.clientHeight>0;if(i){const i=`${e.clientWidth}x${e.clientHeight}`;i!==this._resumeLastSize&&(this._resumeLastSize=i,this._resumeLastChange=t)}const s=t-this._resumeStarted;if(i&&s>=220&&t-this._resumeLastChange>=80||s>=750){const t=this._view;return this._resumeSettling=!1,i&&this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),void this.requestUpdate()}this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick())}_refitView(){const t=this._stageEl;if(!t||"visible"!==document.visibilityState||t.clientWidth<=0||t.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));if(this._viewportInvalidAt){const t=Date.now()-this._viewportInvalidAt;if(this._viewportInvalidAt=0,t>=Wa&&!this._resumeSettling&&(this._beginResumeSettle(),this._resumeSettling))return}if(this._resumeSettling)return;const e=this._view;this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const o=ti(this._baseVb(),this._stageAspect()),n=Math.min(cl.ZOOM_MAX,Math.max(cl.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),h=o.w/n,c=o.h/n;this._zoom=n,this._view=this._clampView({x:l[0]-t/r*h,y:l[1]-e/a*c,w:h,h:c},o)}_onWheel(t){const e=this._stageEl;if(!e)return;t.preventDefault();const i=e.getBoundingClientRect(),s=t.deltaY<0?1.15:1/1.15;this._zoomAt(t.clientX-i.left,t.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(t){const e=this._stageEl;e&&(this._zoomAt(e.clientWidth/2,e.clientHeight/2,this._zoom*(t>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const t=this._baseVb();this._zoom=1,this._view=ti(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(el,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const t=this._zoomBySpace[this._space]||1;this._zoom=t;const e=this._stageEl;if(e&&e.clientHeight){const e=this._baseVb();return this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const e=this._baseVb();this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),this.requestUpdate()})}_stagePointerDown(t){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:t.clientX,y:t.clientY,id:t.pointerId},t.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&t.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&t.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(t))return;this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:t.clientX,sy:t.clientY,vx:e.x,vy:e.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y);this._pinchStart={dist:e,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(t){if(this._physicalRotate?.pid!==t.pointerId)if(this._physicalDrag?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId){if(this._decorDraft?.pid===t.pointerId){const e=this._decorDraft;let i=this._decorSnap(this._svgPoint(t),t.pointerType);if(t.shiftKey&&("rect"===e.kind||"ellipse"===e.kind)){const t=i[0]-e.a[0],s=i[1]-e.a[1],o=Math.max(Math.abs(t),Math.abs(s));i=this._snap([e.a[0]+(t<0?-o:o),e.a[1]+(s<0?-o:o)])}return void(this._decorDraft={...e,b:i})}if(this._decorMove?.pid!==t.pointerId)if(this._pointers.has(t.pointerId)){if(this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(t),this._pinchStart&&this._pointers.size>=2){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),s=(t[0].x+t[1].x)/2-i.left,o=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,o,this._pinchStart.zoom*e),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const e=t.clientX-this._panStart.sx,i=t.clientY-this._panStart.sy;Math.abs(e)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer)),null===this._panLock&&Math.abs(e)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(e)>1.5*Math.abs(i)?"swipe":"pan");const s=this._stageEl;if("pan"===this._panLock&&s){const t=this._baseVb(),o=this._viewOr(t),n=ti(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*o.w,y:this._panStart.vy-i/(s.clientHeight||1)*o.h,w:o.w,h:o.h},n)}}}else this._markupMove(t);else this._decorMoveUpdate(t)}else this._bdMove(t);else this._dtMove(t);else this._physicalMove(t);else this._physicalRotateMove(t)}_stagePointerUp(t){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const e=this._swipeStart;if(this._swipeStart=null,e&&e.id===t.pointerId){const i=t.clientX-e.x,s=t.clientY-e.y;if(Math.abs(i)+Math.abs(s)<8){const t=Date.now();t-this._lastTap<350&&this._resetZoom(),this._lastTap=t}const o="pan"===this._panLock?null:function(t,e,i,s,o,n=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);o&&(this._slideTo(o,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid!==t.pointerId)if(this._physicalRotate?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId){if(this._decorMove?.pid===t.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0))}else this._decorCommitDraft();else this._bdUp();else this._dtUp();else this._physicalRotateUp(t);else this._physicalUp(t)}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),al(window,"location-changed",{replace:!1}))}_pointerDown(t,e){if("plan"===this._mode)return;if("view"===this._mode)return this._holdFired=!1,clearTimeout(this._holdTimer),void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._infoCard=e},600));t.preventDefault();const i=this._pos(e);this._drag={id:e.id,sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},hl(t),this._tip=null}_pointerMove(t,e){if(!this._drag||this._drag.id!==e.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),o=i.getBoundingClientRect(),n=this._viewOr(s),r=(t.clientX-this._drag.sx)/o.width*n.w,a=(t.clientY-this._drag.sy)/o.height*n.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=_n(this._drag.ox+r),h=_n(this._drag.oy+a);this._savePos(e,l,h)}_pointerUp(t,e){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==e.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=e.id,window.setTimeout(()=>this._drag=null,0))}_showToast(t){this._toast=t,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}get _noHover(){return cl._noHoverMq||cl._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(cl._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,o){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:o})}get _gridPitch(){return cn}get _cellCm(){const t=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(t)&&t>0?t:5}_fmtLen(t,e){const i=function(t,e,i,s){return Math.hypot(e[0]-t[0],e[1]-t[1])/i*s}(t,e,this._gridPitch,this._cellCm);return Ce(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const t=this._rszPreview;return t&&t.space===this._space?t.sp:this._serverCfg?.spaces.find(t=>t.id===this._space)}get _renderCfg(){const t=this._rszPreview;return t&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(e=>e.id===t.space?t.sp:e)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,ol}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Te(t?.rooms||[]).map(t=>[t[0]*ol,t[1]*e,t[2]*ol,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(il)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(il,JSON.stringify({space:this._space,mode:this._mode}))}catch{}}_setMode(t){if(this._kiosk&&"view"!==t)return;if(this._mode===t)return;if(this._bootSoftCancel(),("plan"===t||"decor"===t)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));const e=this._mode,i="view"!==e&&"view"!==t,s=i&&this.renderRoot.querySelector(".editorchrome-inner")?.getBoundingClientRect().height||0;i||this._cancelEditorSwapAnimations(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const o=!this._spaceModel().bg&&"view"===t!=("view"===this._mode);if("view"===this._mode&&"view"!==t){const t=this._view;this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?t.x+t.w/2:void 0,cy:t?t.y+t.h/2:void 0}}if("plan"===e&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=t,this._editorChromeMode="view"===t?e:t,this._startNavMotion("view"===e?"enter":"view"===t?"exit":"swap"),i&&this._animateEditorSwap(s),o&&(this._zoom=1,this._view=null),"view"===t){const t=this._viewModeSnap;this._viewModeSnap=null,t&&t.space===this._space?(this._zoom=t.zoom,this._view=null,requestAnimationFrame(()=>{this._stageEl&&"view"===this._mode&&this._space===t.space&&(this._applyView(t.zoom,t.cx,t.cy),this._saveZoom(),this.requestUpdate())})):t&&this._restoreZoom()}this._path=[],this._cursorPt=null,this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=Xs(Ys,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?Xs(Ys,this._imperial):this._drawWallField}get _drawWallCm(){const t=Ba(this._drawWallFieldValue);if(null==t||t<=0)return null;const e=this._imperial?2.54*t:t,i="column"===this._tool?Lo:100;return e>=1&&e<=i?e:null}get _drawWallMaxCm(){return"column"===this._tool?Lo:100}_showPhysicalRange(t=this._drawWallMaxCm){this._showToast(this._t("toast.physical_range",{max:Xs(t,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_limitReached(t){const e=this._curSpaceCfg;if(!e)return!0;const i="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=2e3:(e.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this._gridPitch;return[_n(Me(t[0],e)),_n(Me(t[1],e))]}_snapDrawPoint(t,e=!1){const i=this._path[this._path.length-1],s=e&&i?function(t,e,i,s=1/0){if(!(i>0&&t?.every(Number.isFinite)&&e?.every(Number.isFinite)))return[e[0],e[1]];const o=e[0]-t[0],n=e[1]-t[1];if(Math.abs(o)+Math.abs(n)<=1e-12)return[t[0],t[1]];const r=Math.PI/4;let a=Math.atan2(n,o);a<0&&(a+=2*Math.PI);const l=Math.floor(a/r+.5)%8,[h,c]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=h*h+c*c;let p=Math.max(0,Math.round((o*h+n*c)/(i*d)));if(Number.isFinite(s)&&s>=0){const e=(t,e)=>{if(!e)return 1/0;const o=e>0?s-t:t+s;return Math.max(0,Math.floor((o+1e-9*i)/i))};p=Math.min(p,e(t[0],h),e(t[1],c))}return[t[0]+h*p*i,t[1]+c*p*i]}(i,t,this._gridPitch,ln):t;return this._snap(s)}_samePt(t,e){return Ee(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this._serverCfg?.spaces||[]){delete t.segments;const e=new Set,i=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),s=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=an);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&i(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&s(t.id)).map(t=>({...t,cm:Math.max(1,Math.min(100,Number(t.cm)||15))})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&i(t.id)&&o(t.center)&&s(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qo(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Ho(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&i(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&s(t.id)).map(t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],i=[];for(let s=1;st.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Kn(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=po(t.walls,t.rooms||[],dn,1,e),t.walls.length||delete t.walls}}}get _cfgWriting(){return this._writesPending>0}_writeConfig(){this._writesPending++,this._writeChain=this._writeChain.catch(()=>{}).then(async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const t=await this.hass.callWS({type:"houseplan/config/set",config:this._serverCfg,expected_rev:this._cfgRev});this._cfgRev=t?.rev??this._cfgRev+1});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshot(t=this._space){const e=this._serverCfg?.spaces.find(e=>e.id===t);if(!e)return null;const i=t=>JSON.parse(JSON.stringify(t)),s={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==e[t]&&(s[t]=e[t]);return{spaceId:t,rooms:i(e.rooms||[]),...Array.isArray(e.openings)?{openings:i(e.openings)}:{},...Array.isArray(e.walls)?{walls:i(e.walls)}:{},...Array.isArray(e.open_spans)?{open_spans:i(e.open_spans)}:{},...Array.isArray(e.room_drafts)?{room_drafts:i(e.room_drafts)}:{},...Array.isArray(e.partitions)?{partitions:i(e.partitions)}:{},...Array.isArray(e.wall_columns)?{wall_columns:i(e.wall_columns)}:{},...Array.isArray(e.decor)?{decor:i(e.decor)}:{},plan_transform:s}}_recordGeometry(t,e){if(!e)return;const i=this._geometrySnapshot(e.spaceId);i&&JSON.stringify(e)!==JSON.stringify(i)&&(this._geometryHistory.push({name:t,before:e,after:i}),this.requestUpdate())}_clearGeometryGesture(){this._path=[],this._cursorPt=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._openWallAnchor=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===t.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid!==t.pointerId&&this._physicalRotate?.pid!==t.pointerId)return this._decorDraft?.pid===t.pointerId?(this._decorDraft=null,void this.requestUpdate()):void(this._decorMove?.pid!==t.pointerId&&this._dtDrag?.pid!==t.pointerId&&this._bdDrag?.pid!==t.pointerId?(this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null)):this._cancelDecorGesture());this._cancelPhysicalGesture()}_applyGeometryState(t){const e=this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(!e)return!1;const i=t=>JSON.parse(JSON.stringify(t));e.rooms=i(t.rooms),void 0!==t.openings?e.openings=i(t.openings):delete e.openings,void 0!==t.walls?e.walls=i(t.walls):delete e.walls,void 0!==t.open_spans?e.open_spans=i(t.open_spans):delete e.open_spans,void 0!==t.room_drafts?e.room_drafts=i(t.room_drafts):delete e.room_drafts,void 0!==t.partitions?e.partitions=i(t.partitions):delete e.partitions,void 0!==t.wall_columns?e.wall_columns=i(t.wall_columns):delete e.wall_columns,void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];return Object.assign(e,i(t.plan_transform||{})),this._clearGeometryGesture(),this._space!==t.spaceId&&(this._space=t.spaceId,this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&qe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&Ve(t,i)})}_pointInRoom(t,e){return e.poly?Ie(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!cs(t))return!0;const e=t.length,i=.001;for(let s=0;st?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(e.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._rszDrag||e.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...this._spaceModel().rooms].reverse().find(t=>this._pointInRoom(i,t));return void(this._rszSel=t?.id||null)}if("delroom"===this._tool)return void this._deleteRoomClick(i);if("opening"===this._tool)return void this._openingClick(i);if("merge"===this._tool)return void this._mergeClick(i);if("wallthick"===this._tool)return void this._wallThickClick(i);if("openwall"===this._tool)return void this._openWallClick(i);if("closewall"===this._tool)return void this._closeWallClick(i);if("split"===this._tool)return void this._splitClick(i);if("partition"===this._tool)return void this._partitionClick(i,t.shiftKey);if("column"===this._tool)return void this._columnClick(i);const s=this._snapDrawPoint(i,t.shiftKey);if(t.ctrlKey||t.metaKey)return t.preventDefault(),void this._closeRoomContour(!0);const o=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length){const t=this._draftEndAt(s);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(t=>[...t]):t.draft.points.map(t=>[...t]),void(this._draftSegmentCms=t.reverse?[...t.draft.segments].reverse().map(t=>t.cm):t.draft.segments.map(t=>t.cm))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[s]))}const n=this._path[this._path.length-1];if(this._samePt(s,n))return;if(o)return void this._closeRoomContour();const r=this._draftEndAt(s,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(null==this._drawWallCm)return void this._showPhysicalRange(100);if(this._path.length>=500)return void this._showToast(this._t("toast.physical_limit"));const a=this._curSpaceCfg;!this._activeDraftId&&(a?.room_drafts||[]).length>=200||this._draftSegmentCount(a)>=2e3?this._showToast(this._t("toast.physical_limit")):(this._path=[...this._path,s],this._persistActiveDraftSegment())}_draftEndAt(t,e){const i=this._viewOr(this._baseVb()),s=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?i.w/this._stageEl.clientWidth*12:0);for(const i of this._spaceModel().room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const o=i.points[0],n=i.points[i.points.length-1];if(Math.hypot(t[0]-n[0],t[1]-n[1])<=s)return{draft:i,reverse:!1};if(Math.hypot(t[0]-o[0],t[1]-o[1])<=s)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this._curSpaceCfg;if(!e||!this._path.length)return;const i=Array.isArray(e.room_drafts)?e.room_drafts:[],s=this._activeDraftId?i.find(t=>t.id===this._activeDraftId):null,o=i.find(e=>e.id===t.draft.id);if(!o)return;const n=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),r=t.reverse?(o.segments||[]).map(t=>({...t})):[...o.segments||[]].reverse().map(t=>({...t})),a=this._path[this._path.length-1],l=this._samePt(a,n[0]),h=l?null:this._drawWallCm;if(!l&&null==h)return void this._showPhysicalRange(100);const c=this._draftSegmentCms.map((t,e)=>({...s?.segments?.[e]||{},cm:t})),d=[...this._path.map(t=>[...t]),...l?n.slice(1):n],p=[...c,...null==h?[]:[{cm:h}],...r],u=d.length>=4&&this._samePt(d[0],d[d.length-1]),_=u?d.slice(0,-1):d,m=u?p.slice(0,-1):p;if(_.length>500)return void this._showToast(this._t("toast.physical_limit"));const g=(s?.segments?.length||0)+(o.segments?.length||0);if(this._draftSegmentCount(e)-g+m.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(u){if(this._contourSelfIntersects(_)||Ye(_)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(_);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const f=this._geometrySnapshot(),v=this._activeDraftId||t.draft.id,y={...s||o,id:v,points:_.map(t=>[t[0]/ol,t[1]/ol]),segments:m};e.room_drafts=i.filter(e=>e.id!==t.draft.id&&(!this._activeDraftId||e.id!==this._activeDraftId)),e.room_drafts.push(y),this._activeDraftId=v,this._resumeDraftBySpace[this._space]=v,this._draftSegmentCms=m.map(t=>Number(t.cm)),this._path=_,this._physicalSel=null,this._recordGeometry(this._t("history.draft_merge"),f),this._saveConfig(),u&&(this._closingWallCm=Number(p[p.length-1]?.cm)||Ys,this._path=[..._,[..._[0]]],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0)}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const t=this._drawWallCm;if(null==t)return;this._draftSegmentCms=[...this._draftSegmentCms,t];const e=this._geometrySnapshot(),i=this._curSpaceCfg;i.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const s=i.room_drafts.findIndex(t=>t.id===this._activeDraftId),o={...s>=0?i.room_drafts[s]:{},id:this._activeDraftId,points:this._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentCms.map((t,e)=>({...s>=0?i.room_drafts[s]?.segments?.[e]:{},cm:t}))};s>=0?i.room_drafts[s]=o:i.room_drafts.push(o),this._recordGeometry(this._t("history.draft_segment"),e),this._saveConfig()}_partitionClick(t,e){const i=this._snapDrawPoint(t,e);if(!this._path.length)return void(this._path=[i]);const s=this._path[0];if(this._samePt(s,i))return;const o=this._drawWallCm;if(null==o)return void this._showPhysicalRange(100);if(!this._curSpaceCfg||this._limitReached("partition"))return;const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.partitions||=[];const a="partition-"+Date.now().toString(36);r.partitions.push({id:a,a:[s[0]/ol,s[1]/ol],b:[i[0]/ol,i[1]/ol],cm:o}),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._recordGeometry(this._t("history.partition_add"),n),this._saveConfig()}_columnClick(t){const e=this._snap(t),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Lo);if(!this._curSpaceCfg||this._limitReached("column"))return;const s={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qo(i),angle:0},o=(this._spaceModel().wall_columns||[]).find(t=>Xo(t,s,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...s,center:[e[0]/ol,e[1]/ol]}),this._recordGeometry(this._t("history.column_add"),n),this._saveConfig()}_openPhysicalDialog(t,e,i){const s=this._spaceModel();if("partition"===t){const i=s.partitions.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===t){const i=s.wall_columns.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),shape:i.shape,angle:String("square"===i.shape?Ho(i.angle):0)})}else{const o=s.room_drafts.find(t=>t.id===e),n=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,i||0));o?.segments[n]&&(this._physicalDialog={kind:t,id:e,segment:n,cm:Xs(o.segments[n].cm,this._imperial),length:this._fmtLen(o.points[n],o.points[n+1])})}}_physicalDown(t,e,i){t.stopPropagation(),hl(t);const s=this._spaceModel(),o=this._svgPoint(t),n=[];for(const t of[...s.wall_columns].reverse())Zo(o,jo(t,this._cellCm,this._gridPitch))&&n.push({kind:"column",id:t.id});for(const t of[...s.partitions].reverse()){const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);e&&Zo(o,e)&&n.push({kind:"partition",id:t.id})}n.some(t=>t.kind===e&&t.id===i)||n.unshift({kind:e,id:i});const r=n.map(t=>`${t.kind}:${t.id}`).sort().join("|"),a=performance.now(),l=this._physicalPickCycle,h=n.length>1&&l?.signature===r&&a-l.at>380&&a-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%n.length:Math.max(0,n.findIndex(t=>t.kind===e&&t.id===i));this._physicalPickCycle={signature:r,index:h,x:t.clientX,y:t.clientY,at:a},e=n[h].kind,i=n[h].id;const c="partition"===e?s.partitions.find(t=>t.id===i):s.wall_columns.find(t=>t.id===i);c&&(this._physicalSel={kind:e,id:i},this._physicalDrag={pid:t.pointerId,kind:e,id:i,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,i){const s="partition"===t?[e.a,e.b]:[e.center],o=s.map(t=>t[0]),n=s.map(t=>t[1]);return[Math.max(-an*ol-Math.min(...o),Math.min(an*ol-Math.max(...o),i[0])),Math.max(-an*ol-Math.min(...n),Math.min(an*ol-Math.max(...n),i[1]))]}_physicalMove(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s="partition"===e.kind?e.base.a:e.base.center,o=this._snap([s[0]+i[0]-e.start[0],s[1]+i[1]-e.start[1]]),n=this._clampPhysicalDelta(e.kind,e.base,[o[0]-s[0],o[1]-s[1]]);this._physicalDrag={...e,delta:n,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);if(!this._curSpaceCfg)return;const i=this._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;t&&(t.a=[(s.a[0]+e.delta[0])/ol,(s.a[1]+e.delta[1])/ol],t.b=[(s.b[0]+e.delta[0])/ol,(s.b[1]+e.delta[1])/ol])}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),s=e.base,o={...s,center:[s.center[0]+e.delta[0],s.center[1]+e.delta[1]]};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));t&&(t.center=[(s.center[0]+e.delta[0])/ol,(s.center[1]+e.delta[1])/ol])}this._recordGeometry(this._t("history.physical_move"),e.before),this._saveConfig()}_registerPhysicalTap(t,e,i){const s=performance.now(),o=this._physicalLastTap?.kind===t&&this._physicalLastTap.id===e&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:t,id:e,segment:i,at:s},o&&(this._physicalLastTap=null,this._openPhysicalDialog(t,e,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape)return;t.preventDefault(),t.stopPropagation(),hl(t);const i=this._svgPoint(t);this._physicalSel={kind:"column",id:e.id},this._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,baseAngle:Ho(e.angle),angle:Ho(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const i=this._svgPoint(t),s=180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,o=e.baseAngle+s-e.startAngle,n=Ho(t.shiftKey?o:5*Math.round(o/5));this._physicalRotate={...e,angle:n,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;if(t.preventDefault(),t.stopPropagation(),this._physicalRotate=null,!e.moved||!this._curSpaceCfg)return;const i=this._spaceModel().wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const s={...i,angle:e.angle};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,s,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);o&&(o.angle=e.angle,this._recordGeometry(this._t("history.physical_edit"),e.before),this._saveConfig())}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Pe(e):null;i&&t.push({id:e.id,poly:i})}return t}_rszOpenings(){return this._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen}))}_rszOpts(){return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch}}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const o=JSON.parse(i.snap),n={...s,rooms:o.rooms,openings:o.openings,walls:o.walls,open_spans:o.open_spans};Array.isArray(o.open_spans)&&o.open_spans.length||delete n.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=n.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/ol,t[1]/r]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[t,i]of Object.entries(e)){const e=(n.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/ol,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=n.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const o=s.poly.map(t=>[t[0]*ol,t[1]*r]);if(e.poly.length===o.length)for(let t=0;tt.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const o=[...s.poly[i]],n=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:o,b:n,n:ns(s.poly,i)}}(s,e,i);o&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:o,rooms:s,openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszCornerDown(t,e,i,s){"resize"!==this._tool||this._rszDrag||(t.stopPropagation(),t.preventDefault(),hl(t),this._rszDrag={kind:"scale",pid:t.pointerId,roomId:e,fixed:s,span0:Math.hypot(i[0]-s[0],i[1]-s[1])||1,rooms:this._rszRooms(),openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszMove(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t);if("edge"===e.kind){const t=e.plan,s=(i[0]-t.a[0])*t.n[0]+(i[1]-t.a[1])*t.n[1],o=this._snap([t.a[0]+t.n[0]*s,t.a[1]+t.n[1]*s]);let n=(o[0]-t.a[0])*t.n[0]+(o[1]-t.a[1])*t.n[1];if(n=function(t,e,i,s,o,n){if(!Number.isFinite(s)||Math.abs(s)<1e-9)return 0;const r=Math.sign(s);let a=Math.abs(s);const l=Math.max(o,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(gs(t,e,i,s,n))return s}return 0}(e.rooms,e.openings,t,n,this._gridPitch,this._rszOpts()),n===e.d&&e.moved)return;e.d=n,e.moved=!0;const r=us(e.rooms,e.openings,t,n,this._rszOpts().eps);e.changed=Object.keys(r.polys),this._rszApplyPreview(r.polys,r.openings),this._rszLive=this._rszEdgeLabels(r,t)}else{const t=e.fixed,s=this._snap(i);let o=Math.hypot(s[0]-t[0],s[1]-t[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,o,n){if(!Number.isFinite(o)||o<=0)return 1;if(vs(t,e,i,s,o,n))return o;let r=1,a=o;for(let o=0;o<28;o++){const o=(r+a)/2;vs(t,e,i,s,o,n)?r=o:a=o}return r}(e.rooms,e.openings,e.roomId,t,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const n=e.rooms.find(t=>t.id===e.roomId),r=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),a=fs(n,e.openings,r,t,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:a.poly},a.openings),this._rszLive=this._rszScaleLabels(a.poly)}this.requestUpdate()}_rszUp(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._rszPreview;this._rszDrag=null,this._rszLive=null,this._rszPreview=null;if(!(e.moved&&("edge"===e.kind?Math.abs(e.d)>1e-9:Math.abs(e.k-1)>1e-9))||!i)return this._cfgEpoch++,void this.requestUpdate();const s=JSON.parse(e.snap),o=this._curSpaceCfg;if(o){o.rooms=i.sp.rooms,o.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?o.walls=i.sp.walls:delete o.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?o.open_spans=i.sp.open_spans:delete o.open_spans;for(const t of e.changed){const e=o.rooms.find(e=>e.id===t);e?.poly&&(e.poly=hs(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(o.walls)&&o.walls.length&&(o.walls=po(o.walls,o.rooms||[],dn,1,this._cfgOpenCuts()),o.walls.length||delete o.walls)}this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._recordGeometry(this._t("history.resize_room"),s),this._saveConfig(),this.requestUpdate()}_rszCancelDrag(){this._rszDrag&&(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._cfgEpoch++,this.requestUpdate())}_rszPointerCancel(t){const e=this._rszDrag;e&&e.pid===t.pointerId&&(t.stopPropagation(),this._rszCancelDrag())}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],o=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,n=o.length,r=e.edge,a=(r+1)%n;for(const[t,e]of[[o[(r-1+n)%n],o[r]],[o[r],o[a]],[o[a],o[(a+1)%n]]])s.push({x:(t[0]+e[0])/2,y:(t[1]+e[1])/2,text:this._fmtLen(t,e)});const l="mi"===this.hass?.config?.unit_system?.length,h=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],c=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs),p=this._physicalBodiesR();for(const e of h){const o=t.polys[e]||i.rooms.find(t=>t.id===e).poly,n=c.length&&Ao(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,c,d,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||o,r=We(n),a=p.length?Ko(Vo(n,p))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(n,this._gridPitch,this._cellCm);s.push({x:r[0],y:r[1],text:bs(a,l),area:!0})}return s}_rszScaleLabels(t){const e="mi"===this.hass?.config?.unit_system?.length,i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.max(...i)-Math.min(...i),n=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Ao([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||t,l=We(a),h=this._physicalBodiesR(),c=h.length?Ko(Vo(a,h))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(a,this._gridPitch,this._cellCm);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[o,0])} × ${this._fmtLen([0,0],[n,0])}`},{x:l[0],y:l[1],text:bs(c,e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),o=`M ${s(-.7*i)} 0 H ${s(.7*i)} M 0 ${s(-.22*i)} V ${s(-i)} M ${s(-.32*i)} ${s(-.6*i)} L 0 ${s(-i)} L ${s(.32*i)} ${s(-.6*i)} M 0 ${s(.22*i)} V ${s(i)} M ${s(-.32*i)} ${s(.6*i)} L 0 ${s(i)} L ${s(.32*i)} ${s(.6*i)}`,n=[],r=this._rszRooms();for(const t of r)for(let i=0;ithis._rszEdgeDown(e,t.id,i)} @pointermove=${t=>this._rszMove(t)} @pointerup=${t=>this._rszUp(t)} @@ -3108,7 +3127,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointermove=${t=>this._rszMove(t)} @pointerup=${t=>this._rszUp(t)} @pointercancel=${t=>this._rszPointerCancel(t)} - @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),n.push(B``)}return B`${n}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*sl,ry:t.y*e,rlen:t.length*sl}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return sl}_decorResolvedStyle(t){return function(t,e,i,s=Os){const o="rect"===t?.kind||"ellipse"===t?.kind,n=t,r=/^#[0-9a-f]{6}$/i.test(String(n?.color||""))?String(n.color):s.color;return{color:r,opacity:Fs(n?.opacity,s.opacity),widthCm:Ws(t,e,i,s.widthCm),fill:!!o&&!0===n?.fill,fillColor:/^#[0-9a-f]{6}$/i.test(String(n?.fill_color||""))?String(n.fill_color):n?.fill?r:s.fillColor,fillOpacity:o&&n?.fill?Fs(n?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Os)}_decorWidthUnits(t){return((t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return Hs(o,e,i);const n=Number(t?.width);return Number.isFinite(n)&&n>0?n:Hs(s,e,i)})(t,this._cellCm,this._gridPitch,Os.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Us(yi*bi(t),this._cellCm,this._gridPitch)}return Us(yi,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return yi;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Hs(e,this._cellCm,this._gridPitch):yi*bi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(La,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(an*this._cellCm,e)):.1}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*sl,y:t.y*this._decorH,w:t.w*sl,h:t.h*this._decorH,angle:Ls(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*sl,e.y1*this._decorH],i=[e.x2*sl,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*sl,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Gs(t))}for(const t of this._spaceModel().rooms){const e=Pe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,o=this._viewOr(this._baseVb()),n="touch"===e||"pen"===e?14:8,r=s?o.w/Math.max(1,s.clientWidth)*n:this._gridPitch;return function(t,e,i,s){const o=s(t);let n=null;for(const o of e.points){const e=s(o),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!n||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},ll(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:mn(e[0]/sl),y:mn(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:0,sizeCm:Us(yi,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,o=e.y+e.h/2,n=-Ls(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-o,l=[s+r*Math.cos(n)-a*Math.sin(n),o+r*Math.sin(n)+a*Math.cos(n)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const o=Math.abs(i[0]-e[0]),n=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(o,n)>=s:o>=s&&n>=s}(t.kind,t.a,t.b,e))return;const i=sl,s=this._decorH,o=this._decorStyle,n=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=mn;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Bs(o,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),n=a(Math.min(t.a[1],t.b[1])/s),h=Math.abs(t.b[0]-t.a[0])/i,c=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:n,w:h,h:c,...Bs(o,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),n),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},ll(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,o=("line"===s.kind?s.x1:s.x)*sl,n=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([o+(i[0]-e.start[0]),n+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-o)/sl,l=(r[1]-n)/this._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=an;a=Math.max(-_-c,Math.min(_-c-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){if("decor"!==this._mode||"select"!==this._decorTool)return;if(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,"text"===e.kind)return void this._decorOpenText(e);if(!["line","rect","ellipse","furniture"].includes(e.kind))return;const i=this._decorResolvedStyle(e),s="line"===e.kind?e:null,o=this._decorBoxOf(e);this._decorShapeDialog={id:e.id,kind:e.kind,color:i.color,opacity:i.opacity,widthCm:i.widthCm,angle:Ls(s?Yi([s.x1*sl,s.y1*this._decorH],[s.x2*sl,s.y2*this._decorH]):e.angle),...s?{lengthCm:Us(Math.hypot((s.x2-s.x1)*sl,(s.y2-s.y1)*this._decorH),this._cellCm,this._gridPitch)}:{},...o?{sizeWCm:Us(o.w,this._cellCm,this._gridPitch),sizeHCm:Us(o.h,this._cellCm,this._gridPitch)}:{},..."furniture"===e.kind?{symbol:e.symbol}:{},..."rect"===e.kind||"ellipse"===e.kind?{fill:i.fill,fillColor:i.fillColor,fillOpacity:i.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1])),s=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||s?"":mi(t.entity,o),r=!(!String(t.entity??"").trim()||i||n&&!s);if(n&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+n+e.slice(t+2):`${e}${e?" ":""}${n}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:t.color||this._decorStyle.color,opacity:Fs(t.opacity,this._decorStyle.opacity),angle:Ls(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=mi(e.pickerEntity,t);if(!i)return;const s=e.text,o=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),n=Math.max(o,Math.min(s.length,this._decorTextSelection.end));if(s.length-(n-o)+i.length>200)return;const r=s.slice(0,o)+i+s.slice(n),a=o+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,o={color:t.color,opacity:Fs(t.opacity),size_cm:Number(Math.max(.1,Math.min(La,t.sizeCm)).toFixed(4)),...Ls(t.angle)?{angle:Ls(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:n,...r}=i;return{...r,text:e,...o}}const{entity:s,attr:n,unit:r,...a}=i,{angle:l,size:h,scale:c,...d}=a;return{...d,text:e,...o}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...o}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:Fs(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Fs(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,o=Bs(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*sl,s=(e.y1+e.y2)/2*this._decorH,n=Hs(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Ls(t.angle)*Math.PI/180,a=Math.cos(r)*n/2,l=Math.sin(r)*n/2,h=this._snap([i-a,s-l]),c=this._snap([i+a,s+l]),{width:d,...p}=e;return{...p,...o,x1:mn(h[0]/sl),y1:mn(h[1]/this._decorH),x2:mn(c[0]/sl),y2:mn(c[1]/this._decorH)}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*sl,s=e.h*this._decorH,n=Math.max(this._gridPitch,Me(Hs(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Me(Hs(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*sl+i/2,l=e.y*this._decorH+s/2,h=Ls(t.angle),c=qs({x:a-n/2,y:l-r/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...o,x:mn(c[0]/sl),y:mn(c[1]/this._decorH),w:n/sl,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...h?{angle:h}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*sl,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*sl,(t.y+t.h/2)*this._decorH]:[t.x*sl,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const o={...s};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(La,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const o=this._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[n,r]=this._dtPivot(o),a=this._svgPoint(t),l=this._decorBoxOf(o);this._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:n,ay:r,r0:Math.hypot(a[0]-n,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-n)/Math.PI,textSizeCm0:"text"===o.kind?this._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},ll(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),o=mn(s[0]/sl),n=mn(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(o-a)>1e-9||Math.abs(n-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:n}:{x2:o,y2:n})}if("scale"===e.kind&&e.orig){const s=Vs(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),o=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(La,e.textSizeCm0*(t/e.r0))),o=Math.abs(s-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let o=e.angle0+(s-e.a0);t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360);const n=Math.abs(o-e.angle0)>1e-6;(n||e.moved)&&(e.moved||=n,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*sl,s=t.y1*this._decorH,o=t.x2*sl,n=t.y2*this._decorH;e={id:t.id,x:Math.min(i,o),y:Math.min(s,n),w:Math.abs(o-i),h:Math.abs(n-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*sl,y:t.y*this._decorH,w:t.w*sl,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=En(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const o=sl,n=this._decorH,r=qn(On(i.w,this._cellCm,this._gridPitch,o)),a=qn(On(i.h,this._cellCm,this._gridPitch,o)),l=this._geometrySnapshot(),h=this._decorSnap(t);let c=h[0],d=h[1],p=0;const u=e?null:Bn(c,d,a*n,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(c=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:mn(c/o-r/2),y:mn(d/n-a/2),w:r,h:a,...Bs(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const o=sl,n=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*o+(r[0]-e.start[0]),l=(i.y+i.h/2)*n+(r[1]-e.start[1]);let h,c,d=Number(i.angle)||0;const p=t.shiftKey?null:Bn(a,l,i.h*n,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)h=p.cx/o-i.w/2,c=p.cy/n-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*o,l-i.h/2*n],t.pointerType,e.id);h=s[0]/o,c=s[1]/n}h=mn(h),c=mn(c),(Math.abs(h-i.x)>1e-9||Math.abs(c-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:h,y:c};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=sl,o=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const n=qs(e,i.angle,t=>this._snap(t));return{...i,x:mn(n[0]/s),y:mn(n[1]/o),w:Math.max(this._gridPitch/s,Math.min(2*an,e.w/s)),h:Math.max(this._gridPitch/o,Math.min(2*an,e.h/o))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=sl,s=this._decorH,o=e.w*i,n=e.h*s,r=function(t,e,i,s,o){const n=t+i/2,r=e+s/2,a=(Number(o)||0)*Math.PI/180,l=Math.cos(a),h=Math.sin(a),c=(t,e)=>{const i=t-n,s=e-r;return[n+i*l-s*h,r+i*h+s*l]};return[c(t,e),c(t+i,e),c(t+i,e+s),c(t,e+s)]}(e.x*i,e.y*s,o,n,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),h=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[o,0])},{x:h[0],y:h[1],text:this._fmtLen([0,0],[0,n])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}> + @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),n.push(B``)}return B`${n}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*ol,ry:t.y*e,rlen:t.length*ol}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return ol}_decorResolvedStyle(t){return function(t,e,i,s=Os){const o="rect"===t?.kind||"ellipse"===t?.kind,n=t,r=/^#[0-9a-f]{6}$/i.test(String(n?.color||""))?String(n.color):s.color;return{color:r,opacity:Fs(n?.opacity,s.opacity),widthCm:Ws(t,e,i,s.widthCm),fill:!!o&&!0===n?.fill,fillColor:/^#[0-9a-f]{6}$/i.test(String(n?.fill_color||""))?String(n.fill_color):n?.fill?r:s.fillColor,fillOpacity:o&&n?.fill?Fs(n?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Os)}_decorWidthUnits(t){return((t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return Hs(o,e,i);const n=Number(t?.width);return Number.isFinite(n)&&n>0?n:Hs(s,e,i)})(t,this._cellCm,this._gridPitch,Os.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Us(yi*bi(t),this._cellCm,this._gridPitch)}return Us(yi,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return yi;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Hs(e,this._cellCm,this._gridPitch):yi*bi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(qa,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(an*this._cellCm,e)):.1}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*ol,y:t.y*this._decorH,w:t.w*ol,h:t.h*this._decorH,angle:Ls(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*ol,e.y1*this._decorH],i=[e.x2*ol,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*ol,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Gs(t))}for(const t of this._spaceModel().rooms){const e=Pe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,o=this._viewOr(this._baseVb()),n="touch"===e||"pen"===e?14:8,r=s?o.w/Math.max(1,s.clientWidth)*n:this._gridPitch;return function(t,e,i,s){const o=s(t);let n=null;for(const o of e.points){const e=s(o),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!n||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},hl(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:mn(e[0]/ol),y:mn(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:0,sizeCm:Us(yi,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,o=e.y+e.h/2,n=-Ls(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-o,l=[s+r*Math.cos(n)-a*Math.sin(n),o+r*Math.sin(n)+a*Math.cos(n)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const o=Math.abs(i[0]-e[0]),n=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(o,n)>=s:o>=s&&n>=s}(t.kind,t.a,t.b,e))return;const i=ol,s=this._decorH,o=this._decorStyle,n=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=mn;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Bs(o,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),n=a(Math.min(t.a[1],t.b[1])/s),h=Math.abs(t.b[0]-t.a[0])/i,c=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:n,w:h,h:c,...Bs(o,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),n),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},hl(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,o=("line"===s.kind?s.x1:s.x)*ol,n=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([o+(i[0]-e.start[0]),n+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-o)/ol,l=(r[1]-n)/this._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=an;a=Math.max(-_-c,Math.min(_-c-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){if("decor"!==this._mode||"select"!==this._decorTool)return;if(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,"text"===e.kind)return void this._decorOpenText(e);if(!["line","rect","ellipse","furniture"].includes(e.kind))return;const i=this._decorResolvedStyle(e),s="line"===e.kind?e:null,o=this._decorBoxOf(e);this._decorShapeDialog={id:e.id,kind:e.kind,color:i.color,opacity:i.opacity,widthCm:i.widthCm,angle:Ls(s?Yi([s.x1*ol,s.y1*this._decorH],[s.x2*ol,s.y2*this._decorH]):e.angle),...s?{lengthCm:Us(Math.hypot((s.x2-s.x1)*ol,(s.y2-s.y1)*this._decorH),this._cellCm,this._gridPitch)}:{},...o?{sizeWCm:Us(o.w,this._cellCm,this._gridPitch),sizeHCm:Us(o.h,this._cellCm,this._gridPitch)}:{},..."furniture"===e.kind?{symbol:e.symbol}:{},..."rect"===e.kind||"ellipse"===e.kind?{fill:i.fill,fillColor:i.fillColor,fillOpacity:i.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1])),s=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||s?"":mi(t.entity,o),r=!(!String(t.entity??"").trim()||i||n&&!s);if(n&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+n+e.slice(t+2):`${e}${e?" ":""}${n}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:t.color||this._decorStyle.color,opacity:Fs(t.opacity,this._decorStyle.opacity),angle:Ls(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=mi(e.pickerEntity,t);if(!i)return;const s=e.text,o=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),n=Math.max(o,Math.min(s.length,this._decorTextSelection.end));if(s.length-(n-o)+i.length>200)return;const r=s.slice(0,o)+i+s.slice(n),a=o+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,o={color:t.color,opacity:Fs(t.opacity),size_cm:Number(Math.max(.1,Math.min(qa,t.sizeCm)).toFixed(4)),...Ls(t.angle)?{angle:Ls(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:n,...r}=i;return{...r,text:e,...o}}const{entity:s,attr:n,unit:r,...a}=i,{angle:l,size:h,scale:c,...d}=a;return{...d,text:e,...o}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...o}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:Fs(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Fs(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,o=Bs(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*ol,s=(e.y1+e.y2)/2*this._decorH,n=Hs(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Ls(t.angle)*Math.PI/180,a=Math.cos(r)*n/2,l=Math.sin(r)*n/2,h=this._snap([i-a,s-l]),c=this._snap([i+a,s+l]),{width:d,...p}=e;return{...p,...o,x1:mn(h[0]/ol),y1:mn(h[1]/this._decorH),x2:mn(c[0]/ol),y2:mn(c[1]/this._decorH)}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*ol,s=e.h*this._decorH,n=Math.max(this._gridPitch,Me(Hs(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Me(Hs(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*ol+i/2,l=e.y*this._decorH+s/2,h=Ls(t.angle),c=qs({x:a-n/2,y:l-r/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...o,x:mn(c[0]/ol),y:mn(c[1]/this._decorH),w:n/ol,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...h?{angle:h}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*ol,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*ol,(t.y+t.h/2)*this._decorH]:[t.x*ol,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const o={...s};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(qa,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const o=this._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[n,r]=this._dtPivot(o),a=this._svgPoint(t),l=this._decorBoxOf(o);this._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:n,ay:r,r0:Math.hypot(a[0]-n,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-n)/Math.PI,textSizeCm0:"text"===o.kind?this._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},hl(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),o=mn(s[0]/ol),n=mn(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(o-a)>1e-9||Math.abs(n-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:n}:{x2:o,y2:n})}if("scale"===e.kind&&e.orig){const s=Vs(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),o=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(qa,e.textSizeCm0*(t/e.r0))),o=Math.abs(s-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let o=e.angle0+(s-e.a0);t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360);const n=Math.abs(o-e.angle0)>1e-6;(n||e.moved)&&(e.moved||=n,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*ol,s=t.y1*this._decorH,o=t.x2*ol,n=t.y2*this._decorH;e={id:t.id,x:Math.min(i,o),y:Math.min(s,n),w:Math.abs(o-i),h:Math.abs(n-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*ol,y:t.y*this._decorH,w:t.w*ol,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=En(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const o=ol,n=this._decorH,r=qn(On(i.w,this._cellCm,this._gridPitch,o)),a=qn(On(i.h,this._cellCm,this._gridPitch,o)),l=this._geometrySnapshot(),h=this._decorSnap(t);let c=h[0],d=h[1],p=0;const u=e?null:Bn(c,d,a*n,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(c=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:mn(c/o-r/2),y:mn(d/n-a/2),w:r,h:a,...Bs(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const o=ol,n=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*o+(r[0]-e.start[0]),l=(i.y+i.h/2)*n+(r[1]-e.start[1]);let h,c,d=Number(i.angle)||0;const p=t.shiftKey?null:Bn(a,l,i.h*n,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)h=p.cx/o-i.w/2,c=p.cy/n-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*o,l-i.h/2*n],t.pointerType,e.id);h=s[0]/o,c=s[1]/n}h=mn(h),c=mn(c),(Math.abs(h-i.x)>1e-9||Math.abs(c-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:h,y:c};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=ol,o=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const n=qs(e,i.angle,t=>this._snap(t));return{...i,x:mn(n[0]/s),y:mn(n[1]/o),w:Math.max(this._gridPitch/s,Math.min(2*an,e.w/s)),h:Math.max(this._gridPitch/o,Math.min(2*an,e.h/o))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=ol,s=this._decorH,o=e.w*i,n=e.h*s,r=function(t,e,i,s,o){const n=t+i/2,r=e+s/2,a=(Number(o)||0)*Math.PI/180,l=Math.cos(a),h=Math.sin(a),c=(t,e)=>{const i=t-n,s=e-r;return[n+i*l-s*h,r+i*h+s*l]};return[c(t,e),c(t+i,e),c(t+i,e+s),c(t,e+s)]}(e.x*i,e.y*s,o,n,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),h=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[o,0])},{x:h[0],y:h[1],text:this._fmtLen([0,0],[0,n])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}>
${this._t("furn.title")} @@ -3143,7 +3162,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @input=${e=>this._furnPalette={...t,h:this._furnFieldToCm(Number(e.target.value))}} /> ${this._t("furn.place_hint")}
`:W`
${this._t("furn.pick_hint")}
`} -
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...tn(t.plan_aspect,sl)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?on(t,sl):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),o=Number(t?.plan_scale_x),n=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(o)&&o>0?o:r,sy:Number.isFinite(n)&&n>0?n:r,angle:Ls(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Us(e.w,this._cellCm,this._gridPitch),heightCm:Us(e.h,this._cellCm,this._gridPitch),angle:Ls(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=this._geometrySnapshot(),o=Math.min(e.w*sn,Math.max(e.w*en,Me(Hs(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),n=Math.min(e.h*sn,Math.max(e.h*en,Me(Hs(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=qs({x:i.x+i.w/2-o/2,y:i.y+i.h/2-n/2},t.angle,t=>this._snap(t));this._bdApply((r[0]-e.x)/sl,(r[1]-e.y)/sl,o/e.w,n/e.h,t.angle),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),s),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,o){const n=this._curSpaceCfg;if(!n)return;n.plan_x=Number(mn(t).toFixed(6)),n.plan_y=Number(mn(e).toFixed(6)),delete n.plan_scale,n.plan_scale_x=Number(Math.min(sn,Math.max(en,i)).toFixed(6)),n.plan_scale_y=Number(Math.min(sn,Math.max(en,s)).toFixed(6));const r=Ls(o);r?n.plan_angle=Number(r.toFixed(2)):delete n.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,o=this._bdRect;if(!s||!o)return!1;const n=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?o.x:o.x+o.w,h=a>0?o.y:o.y+o.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:s,p0:this._bdParams,fx:l,fy:h,sgx:r,sgy:a,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},ll(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,n=this._snap([t+(i[0]-e.sx),o+(i[1]-e.sy)]),r=Math.abs(n[0]-t)>1e-9||Math.abs(n[1]-o)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((n[0]-s.x)/sl,(n[1]-s.y)/sl,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,n=180*Math.atan2(e.sy-o,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-o,i[0]-s)/Math.PI-n);t.shiftKey||(r=5*Math.round(r/5)),r=Ls(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const o=Vs(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*en),n=o.w/Math.max(1e-9,s.w),r=o.h/Math.max(1e-9,s.h),a=Math.abs(n-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=qs(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/sl,(l[1]-s.y)/sl,n,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return G;const i=.02*Math.max(t.w,t.h),s=i/4,o=e.x+e.w/2,n=e.y+e.h/2,r=Ls(e.angle),a=2.2*i;return B` +
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...tn(t.plan_aspect,ol)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?on(t,ol):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),o=Number(t?.plan_scale_x),n=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(o)&&o>0?o:r,sy:Number.isFinite(n)&&n>0?n:r,angle:Ls(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Us(e.w,this._cellCm,this._gridPitch),heightCm:Us(e.h,this._cellCm,this._gridPitch),angle:Ls(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=this._geometrySnapshot(),o=Math.min(e.w*sn,Math.max(e.w*en,Me(Hs(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),n=Math.min(e.h*sn,Math.max(e.h*en,Me(Hs(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=qs({x:i.x+i.w/2-o/2,y:i.y+i.h/2-n/2},t.angle,t=>this._snap(t));this._bdApply((r[0]-e.x)/ol,(r[1]-e.y)/ol,o/e.w,n/e.h,t.angle),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),s),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,o){const n=this._curSpaceCfg;if(!n)return;n.plan_x=Number(mn(t).toFixed(6)),n.plan_y=Number(mn(e).toFixed(6)),delete n.plan_scale,n.plan_scale_x=Number(Math.min(sn,Math.max(en,i)).toFixed(6)),n.plan_scale_y=Number(Math.min(sn,Math.max(en,s)).toFixed(6));const r=Ls(o);r?n.plan_angle=Number(r.toFixed(2)):delete n.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,o=this._bdRect;if(!s||!o)return!1;const n=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?o.x:o.x+o.w,h=a>0?o.y:o.y+o.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:s,p0:this._bdParams,fx:l,fy:h,sgx:r,sgy:a,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},hl(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,n=this._snap([t+(i[0]-e.sx),o+(i[1]-e.sy)]),r=Math.abs(n[0]-t)>1e-9||Math.abs(n[1]-o)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((n[0]-s.x)/ol,(n[1]-s.y)/ol,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,n=180*Math.atan2(e.sy-o,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-o,i[0]-s)/Math.PI-n);t.shiftKey||(r=5*Math.round(r/5)),r=Ls(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const o=Vs(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*en),n=o.w/Math.max(1e-9,s.w),r=o.h/Math.max(1e-9,s.h),a=Math.abs(n-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=qs(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/ol,(l[1]-s.y)/ol,n,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return G;const i=.02*Math.max(t.w,t.h),s=i/4,o=e.x+e.w/2,n=e.y+e.h/2,r=Ls(e.angle),a=2.2*i;return B` {e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} - `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return G;const s=.018*Math.max(t.w,t.h),o=s/4;if("line"===e.kind){const t=[e.x1*sl,e.y1*this._decorH],i=[e.x2*sl,e.y2*this._decorH];return B` + `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return G;const s=.018*Math.max(t.w,t.h),o=s/4;if("line"===e.kind){const t=[e.x1*ol,e.y1*this._decorH],i=[e.x2*ol,e.y2*this._decorH];return B` ${[t,i].map((t,e)=>B`this._dtStart(t,"scale",void 0,e)}> @@ -3168,30 +3187,42 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow cx="${t<0?i.x:i.x+i.w}" cy="${e<0?i.y:i.y+i.h}" r="${s.toFixed(1)}" @pointerdown=${i=>this._dtStart(i,"scale",[t,e])}>`)} - `}_renderDecorLayer(){const t=sl,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const o="dshape"+(i&&this._decorSel===s.id?" dsel":""),n=this._decorResolvedStyle(s),r=this._decorWidthUnits(s),a=t=>this._decorShapeDown(t,s),l=t=>this._decorShapeDbl(t,s);if("line"===s.kind)return B``;if("rect"===s.kind){const i=(s.x+s.w/2)*t,h=(s.y+s.h/2)*e,c=Ls(s.angle);return B``}if("ellipse"===s.kind){const i=(s.x+s.w/2)*t,h=(s.y+s.h/2)*e,c=Ls(s.angle);return B``}if("furniture"===s.kind){const i=s.w*t,h=s.h*e,c=Un(s.symbol,i,h);if(!c)return G;const d=Number(s.angle)||0,p=s.x*t+i/2,u=s.y*e+h/2,_=`${d?`rotate(${d} ${p} ${u}) `:""}translate(${s.x*t} ${s.y*e})`;return B``}_renderDecorLayer(){const t=ol,e=this._decorH,i="decor"===this._mode,s=i&&"erase"===this._decorTool,o=this._decorList.map(o=>{const n="dshape"+(i&&this._decorSel===o.id?" dsel":""),r=this._decorResolvedStyle(o),a=this._decorWidthUnits(o),l=t=>this._decorShapeDown(t,o),h=t=>this._decorShapeDbl(t,o);if("line"===o.kind)return B` + ${s?B``:G}`;if("rect"===o.kind){const i=(o.x+o.w/2)*t,c=(o.y+o.h/2)*e,d=Ls(o.angle);return B` + ${s?B``:G}`}if("ellipse"===o.kind){const i=(o.x+o.w/2)*t,c=(o.y+o.h/2)*e,d=Ls(o.angle);return B` + ${s?B``:G}`}if("furniture"===o.kind){const i=o.w*t,c=o.h*e,d=Un(o.symbol,i,c);if(!d)return G;const p=Number(o.angle)||0,u=o.x*t+i/2,_=o.y*e+c/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${o.x*t} ${o.y*e})`;return B``}if("text"===s.kind){const i=this._decorTextUnits(s),r=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=_i(e);return o?(n=!0,s(o.entity||"")?vi(i,o):pi):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf(ui);return t>=0?o.slice(0,t)+pi+o.slice(t+2):o?`${o} ${pi}`:pi}const l=vi(i,e),h=o.indexOf(ui);return h>=0?o.slice(0,h)+l+o.slice(h+2):o?`${o} ${l}`:l}(s.text,s,this.hass,t=>this._planEntityAvailable(t))),h=s.x*t,c=s.y*e,d=Number(s.angle)||0,p=c-(r.length-1)*i*1.2/2;return B`${r.map((t,e)=>B`${t}`)}`}return G});let o=G;const n=this._decorDraft;if(n){const t=this._decorStyle,e=Hs(t.widthCm,this._cellCm,this._gridPitch);if("line"===n.kind)o=B``;else{const i=Math.min(n.a[0],n.b[0]),s=Math.min(n.a[1],n.b[1]),r=Math.abs(n.b[0]-n.a[0]),a=Math.abs(n.b[1]-n.a[1]);o="rect"===n.kind?B``:B``}}return B`${s}${o}`}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._decorSel?this._decorList.find(t=>t.id===this._decorSel):null,i="rect"===this._decorTool||"ellipse"===this._decorTool||"rect"===e?.kind||"ellipse"===e?.kind,s=this._geometryHistory.undoName,o=this._geometryHistory.redoName;return W`
+ @pointerdown=${l} @dblclick=${h}> + ${s?B``:G}`}if("text"===o.kind){const i=this._decorTextUnits(o),s=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=_i(e);return o?(n=!0,s(o.entity||"")?vi(i,o):pi):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf(ui);return t>=0?o.slice(0,t)+pi+o.slice(t+2):o?`${o} ${pi}`:pi}const l=vi(i,e),h=o.indexOf(ui);return h>=0?o.slice(0,h)+l+o.slice(h+2):o?`${o} ${l}`:l}(o.text,o,this.hass,t=>this._planEntityAvailable(t))),a=o.x*t,c=o.y*e,d=Number(o.angle)||0,p=c-(s.length-1)*i*1.2/2;return B`${s.map((t,e)=>B`${t}`)}`}return G});let n=G;const r=this._decorDraft;if(r){const t=this._decorStyle,e=Hs(t.widthCm,this._cellCm,this._gridPitch);if("line"===r.kind)n=B``;else{const i=Math.min(r.a[0],r.b[0]),s=Math.min(r.a[1],r.b[1]),o=Math.abs(r.b[0]-r.a[0]),a=Math.abs(r.b[1]-r.a[1]);n="rect"===r.kind?B``:B``}}return B`${o}${n}`}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._decorSel?this._decorList.find(t=>t.id===this._decorSel):null,i="rect"===this._decorTool||"ellipse"===this._decorTool||"rect"===e?.kind||"ellipse"===e?.kind,s=this._geometryHistory.undoName,o=this._geometryHistory.redoName;return W`
${t.map(([t,e,i])=>W`
-
`:W``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=Re(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,o){for(const n of s)if(!(Zi([t,e],n)>o)&&uo([n[0],n[1]],[n[2],n[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:o.x,y:o.y,angle:o.angle},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{ll(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=Re(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,o=s?.openings?.find(t=>t.id===e.id);if(!o)return;const n=this._opRuler(i,o.length*sl);this._opMeasure=n.measure;const r=n.x/sl,a=n.y/this._spaceH;o.x===r&&o.y===a&&o.angle===i.angle||(this._opDrag.dirty=!0),o.x=r,o.y=a,o.angle=i.angle,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let o=t.x,n=t.y,r=Ae([o,n],t.angle,e,i,s);if(r&&r.centered&&(o!==r.wallCenter[0]||n!==r.wallCenter[1]))[o,n]=r.wallCenter,r=Ae([o,n],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[h,c]=r.wallB,d=h-a,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-a)*d+(n-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=a+_/u*d,n=l+_/u*p,r=Ae([o,n],t.angle,e,i,s)||r}}if(!r)return{x:o,y:n,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ce(t/this._gridPitch*this._cellCm,a)});return{x:o,y:n,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,o={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/sl,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/sl,contact:t.contact||null,lock:"door"===t.type&&t.lock||null,invert:t.invert||void 0,flip_h:t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const n=e.openings.findIndex(t=>t.id===o.id);n>=0?e.openings[n]=o:e.openings.push(o),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],o=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||o)&&t.push([e,s?.attributes?.friendly_name||e,o?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 o=e.find(t=>t.id===this._mergeSel),n=o?Pe(o):null,r=Pe(i),a=n&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ke(Je(t),Je(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}(n,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,o="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===o);r?(r.poly=t.poly.map(t=>[t[0]/sl,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==n),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?Pe(i):null;if(!i||!s)return void(this._splitSel=null);const o=.02*this._gridPitch,n=6*this._gridPitch,r=Fe(t,s),a=r&&function(t,e,i){let s=null,o=1/0;for(let n=0;n=o)continue;o=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*c,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?a:null,h=!!l&&Le(l,s,o),c=this._splitSel.pts;if(!c.length)return h?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const e=this._snap(t);return qe(e,s,o)?void(this._splitSel={...this._splitSel,pts:[...c,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],o=e[e.length-1];if(Ee(s,o,i))return null;const n=Ze(t,s,i),r=Ze(t,o,i);if(n<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!qe(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!==n);e++)a=(a+1)%t.length;return r.push(o),Xe(r,i)};let h,c;if(n===r){const r=Xe([...e],i);if(r.length<3||Ye(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}h=Xe(a,i),c=r}else h=Xe([...l(s,n,o,r),...[...a].reverse()],i),c=Xe([...l(o,r,s,n),...a],i);return h.length<3||c.length<3||Ye(h)<=i||Ye(c)<=i||Math.abs(Ye(h)+Ye(c)-Ye(t))>Math.max(i,1e-6*Ye(t))?null:[h,c]}(s,[...c,l],o);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=Ye(p)>=Ye(u)?p:u,m=_===p?u:p;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;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"openwall"===this._tool||"closewall"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const e=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!e&&!i)return;const s=this._svgPoint(t);this._cursorPt=e?this._snapDrawPoint(s,t.shiftKey):this._snap(s)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),o=Re(t,this._spaceModel().rooms,e);if(!o)return null;const n=this._opRuler(o,s);return{x:n.x,y:n.y,angle:n.angle,rlen:s,measure:n.measure}}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){this._areaSel&&this._commitRoom()}_saveRoomNoArea(){this._nameSel.trim()&&(this._areaSel="",this._commitRoom())}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit;let o;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]/sl,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,o=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;o=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:o.map(t=>[t[0]/sl,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!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(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,o,n=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Mo(e,i,n,o,r,t);if(!a)return t?t.slice():[];const l=Co(t,a,o,r);let h=t?t.slice():[];for(let t=0;t0||(h=mo(h,e,i,s,o,r))}return h}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,sl);for(const t of Po(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=o.findIndex((t,e)=>{const i=o[(e+1)%o.length];return Zi(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(n=mo(n,t.a,t.b,e[a]||i,this._wallKeyPitch,sl))}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 a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=sl,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/sl,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const h=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:h,added:l}):this._t("toast.room_saved_no_area",{n:h}))}_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._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_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){t&&this._ackNewDevice(t.id),this._norm?this._markerDialog=t?{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.hass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:"ripple"===t.marker?.display?"icon_ripple":t.marker?.display||"badge",rippleColor: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:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:Br(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",isLight:!0===t.marker?.is_light||jr(t.marker?.binding,t.marker?.controls,t.entities),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}:{name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",isLight:!1,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)}:this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of di)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&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.hass,e=sa(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 o=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&o.add(t.name.trim()+"|"+(t.area||""));const n=[];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)&&o.has(r+"|"+(e.area_id||""))||n.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[o,a]of Object.entries(t.entities)){const l="entity:"+o;if(s.has(l))continue;if(oa(t,o,e)&&!i.has(l))continue;const h=r.has(a.platform),c="group"===a.platform;if(!h&&!c)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[o];n.push({value:l,label:a.name||d?.attributes?.friendly_name||o,sub:o.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const o=new Set(n.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||o.has(l)||a.hidden&&!i.has(l))continue;if(oa(t,r,e)&&!i.has(l))continue;const h=t.states[r],c=a.name||h?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";n.push({value:l,label:c,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?n.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):n;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=>Go(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),o=(t.wall_columns||[]).map(t=>jo(t,this._cellCm,this._gridPitch)),n=[...i,...s,...o];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:o,all:n},n}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,o=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=Ba(this._cleanFloorCache,o);if(t.hit)return t.value}const n=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...n),Math.min(...r),Math.max(...n),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]}),h=l.length?Vo(e,l):null,c={floor:e,geom:h,path:h?Wo(h):"",area:Ko(h||[[[...e,e[0]]]])};return this._rszPreview||ja(this._cleanFloorCache,o,c,600),c}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";if("device"===e){const t=this.hass.devices?.[i];if(!t)return"mdi:help-circle";const e=Object.entries(this.hass.entities||{}).filter(([,t])=>t?.device_id===i).map(([t])=>t);return e.some(t=>t.startsWith("lock."))?"mdi:lock":ia(this.hass,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=this.hass.entities?.[i],e=this.hass.states?.[i],s=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":ia(this.hass,s,"",[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)}}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",o=[];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}`}:{}}),n=await i.json().catch(()=>({}));if(!i.ok||n.error){const t={too_large:this._t("err.too_large",{mb:n.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[n.error]||n.error||"HTTP "+i.status)}o.push({name:n.name||t.name,url:n.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}o.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...o]},this._showToast(this._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=function(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}(t.room);let o=s?.space||null,n=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||o||(o=this._space),i=function(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=l.length>0,c=Br(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,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,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:c.length?c:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Wa(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=n,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==o||u.area!==n||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const o="/files/"+e+"/",n="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(o))return t;const e=t.url.split(o)[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(o+i)[0]+n+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(o).join(n)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=o||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!h&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(h||!this._layout[i]||m){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):n?t.rooms.find(t=>t.area===n):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(o||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const k=new Set(l);a&&a!==i&&k.add(a),k.delete(i);for(const t of k)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).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 o=s.markers,n="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!n)return;const r=function(t,e,i,s){const o=new Set([e]),n=t.filter(t=>{const n=t.id===e||!s&&t.binding===i;return n&&o.add(t.id),!n});return{markers:s?n:[...n,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:o}}(s.markers,e.id,n,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){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._infoCard=null),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=o),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/sl,y:i/sl}}_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=xi(i);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:s.fill,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Rs({},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:ki,roomOpacity:$i,fillMode:"glow",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 o=new Uint8Array(await i.arrayBuffer());let n="";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``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=Re(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,o){for(const n of s)if(!(Zi([t,e],n)>o)&&uo([n[0],n[1]],[n[2],n[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:o.x,y:o.y,angle:o.angle},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{hl(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=Re(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,o=s?.openings?.find(t=>t.id===e.id);if(!o)return;const n=this._opRuler(i,o.length*ol);this._opMeasure=n.measure;const r=n.x/ol,a=n.y/this._spaceH;o.x===r&&o.y===a&&o.angle===i.angle||(this._opDrag.dirty=!0),o.x=r,o.y=a,o.angle=i.angle,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let o=t.x,n=t.y,r=Ae([o,n],t.angle,e,i,s);if(r&&r.centered&&(o!==r.wallCenter[0]||n!==r.wallCenter[1]))[o,n]=r.wallCenter,r=Ae([o,n],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[h,c]=r.wallB,d=h-a,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-a)*d+(n-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=a+_/u*d,n=l+_/u*p,r=Ae([o,n],t.angle,e,i,s)||r}}if(!r)return{x:o,y:n,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ce(t/this._gridPitch*this._cellCm,a)});return{x:o,y:n,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,o={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/ol,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/ol,contact:t.contact||null,lock:"door"===t.type&&t.lock||null,invert:t.invert||void 0,flip_h:t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const n=e.openings.findIndex(t=>t.id===o.id);n>=0?e.openings[n]=o:e.openings.push(o),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],o=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||o)&&t.push([e,s?.attributes?.friendly_name||e,o?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 o=e.find(t=>t.id===this._mergeSel),n=o?Pe(o):null,r=Pe(i),a=n&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=$e(Je(t),Je(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}(n,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,o="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===o);r?(r.poly=t.poly.map(t=>[t[0]/ol,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==n),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?Pe(i):null;if(!i||!s)return void(this._splitSel=null);const o=.02*this._gridPitch,n=6*this._gridPitch,r=Fe(t,s),a=r&&function(t,e,i){let s=null,o=1/0;for(let n=0;n=o)continue;o=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*c,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?a:null,h=!!l&&Le(l,s,o),c=this._splitSel.pts;if(!c.length)return h?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const e=this._snap(t);return qe(e,s,o)?void(this._splitSel={...this._splitSel,pts:[...c,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],o=e[e.length-1];if(Ee(s,o,i))return null;const n=Ze(t,s,i),r=Ze(t,o,i);if(n<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!qe(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!==n);e++)a=(a+1)%t.length;return r.push(o),Xe(r,i)};let h,c;if(n===r){const r=Xe([...e],i);if(r.length<3||Ye(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}h=Xe(a,i),c=r}else h=Xe([...l(s,n,o,r),...[...a].reverse()],i),c=Xe([...l(o,r,s,n),...a],i);return h.length<3||c.length<3||Ye(h)<=i||Ye(c)<=i||Math.abs(Ye(h)+Ye(c)-Ye(t))>Math.max(i,1e-6*Ye(t))?null:[h,c]}(s,[...c,l],o);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=Ye(p)>=Ye(u)?p:u,m=_===p?u:p;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;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"openwall"===this._tool||"closewall"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const e=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!e&&!i)return;const s=this._svgPoint(t);this._cursorPt=e?this._snapDrawPoint(s,t.shiftKey):this._snap(s)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),o=Re(t,this._spaceModel().rooms,e);if(!o)return null;const n=this._opRuler(o,s);return{x:n.x,y:n.y,angle:n.angle,rlen:s,measure:n.measure}}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;let o;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]/ol,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,o=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;o=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:o.map(t=>[t[0]/ol,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!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(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,o,n=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Mo(e,i,n,o,r,t);if(!a)return t?t.slice():[];const l=Co(t,a,o,r);let h=t?t.slice():[];for(let t=0;t0||(h=mo(h,e,i,s,o,r))}return h}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,ol);for(const t of Po(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=o.findIndex((t,e)=>{const i=o[(e+1)%o.length];return Zi(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(n=mo(n,t.a,t.b,e[a]||i,this._wallKeyPitch,ol))}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 a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=ol,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/ol,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const h=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:h,added:l}):this._t("toast.room_saved_no_area",{n:h}))}_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._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_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){t&&this._ackNewDevice(t.id),this._norm?this._markerDialog=t?{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.hass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:"ripple"===t.marker?.display?"icon_ripple":t.marker?.display||"badge",rippleColor: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:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:jr(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",isLight:!0===t.marker?.is_light||Gr(t.marker?.binding,t.marker?.controls,t.entities),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}:{name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",isLight:!1,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)}:this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of di)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&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.hass,e=oa(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 o=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&o.add(t.name.trim()+"|"+(t.area||""));const n=[];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)&&o.has(r+"|"+(e.area_id||""))||n.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[o,a]of Object.entries(t.entities)){const l="entity:"+o;if(s.has(l))continue;if(na(t,o,e)&&!i.has(l))continue;const h=r.has(a.platform),c="group"===a.platform;if(!h&&!c)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[o];n.push({value:l,label:a.name||d?.attributes?.friendly_name||o,sub:o.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const o=new Set(n.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||o.has(l)||a.hidden&&!i.has(l))continue;if(na(t,r,e)&&!i.has(l))continue;const h=t.states[r],c=a.name||h?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";n.push({value:l,label:c,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?n.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):n;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=>Go(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),o=(t.wall_columns||[]).map(t=>jo(t,this._cellCm,this._gridPitch)),n=[...i,...s,...o];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:o,all:n},n}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,o=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=ja(this._cleanFloorCache,o);if(t.hit)return t.value}const n=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...n),Math.min(...r),Math.max(...n),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]}),h=l.length?Vo(e,l):null,c={floor:e,geom:h,path:h?Wo(h):"",area:Ko(h||[[[...e,e[0]]]])};return this._rszPreview||Ga(this._cleanFloorCache,o,c,600),c}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";if("device"===e){const t=this.hass.devices?.[i];if(!t)return"mdi:help-circle";const e=Object.entries(this.hass.entities||{}).filter(([,t])=>t?.device_id===i).map(([t])=>t);return e.some(t=>t.startsWith("lock."))?"mdi:lock":sa(this.hass,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=this.hass.entities?.[i],e=this.hass.states?.[i],s=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":sa(this.hass,s,"",[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)}}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",o=[];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}`}:{}}),n=await i.json().catch(()=>({}));if(!i.ok||n.error){const t={too_large:this._t("err.too_large",{mb:n.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[n.error]||n.error||"HTTP "+i.status)}o.push({name:n.name||t.name,url:n.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}o.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...o]},this._showToast(this._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=function(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}(t.room);let o=s?.space||null,n=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||o||(o=this._space),i=function(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=l.length>0,c=jr(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,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,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:c.length?c:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Ba(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=n,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==o||u.area!==n||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const o="/files/"+e+"/",n="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(o))return t;const e=t.url.split(o)[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(o+i)[0]+n+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(o).join(n)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=o||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!h&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(h||!this._layout[i]||m){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):n?t.rooms.find(t=>t.area===n):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(o||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const $=new Set(l);a&&a!==i&&$.add(a),$.delete(i);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(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).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 o=s.markers,n="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!n)return;const r=function(t,e,i,s){const o=new Set([e]),n=t.filter(t=>{const n=t.id===e||!s&&t.binding===i;return n&&o.add(t.id),!n});return{markers:s?n:[...n,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:o}}(s.markers,e.id,n,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){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._infoCard=null),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=o),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/ol,y:i/ol}}_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=xi(i);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:s.fill,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Rs({},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:$i,roomOpacity:ki,fillMode:"glow",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 o=new Uint8Array(await i.arrayBuffer());let n="";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`
@@ -3417,7 +3448,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`)} -
`}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!o&&this._aspectJob&&(o=await this._aspectJob||null);const n=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},n.spaces.push(r);else{if(r=n.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=o),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(qa,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:ki,roomOpacity:$i,fillMode:"glow",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})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return W``}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!o&&this._aspectJob&&(o=await this._aspectJob||null);const n=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},n.spaces.push(r);else{if(r=n.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=o),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(Ha,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:$i,roomOpacity:ki,fillMode:"glow",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})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return W`this._importDialog=null}>
${this._t("import.hint")}
@@ -3437,7 +3468,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._t("import.start",{n:e})}
-
`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays,weather_entity:(t.weatherEntity||"").trim()||void 0}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Rs(this._sunGlobal(),this._sunSpace())}_effBgMode(){return As(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Ns(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Is(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Is(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const o=Es(this._sunGlobal()),n=function(t){if(!t)return 1;const e=zs[String(t).toLowerCase()];return void 0===e?1:e}(o?this.hass?.states?.[o]?.state:null),r=function(t=1){return.3*ks(t)}(n);if(r<=0)return this._sunFadeReset(),e;if(a=s.elevation,Number(a)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var a;const l=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const e=t.rooms.map(t=>({id:t.id||"",poly:Pe(t)})).filter(t=>!!t.id&&!!t.poly),o=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),n=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},h={};if(n.length){for(const i of e){const e=Ao(t.rooms,i.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,sl);e&&(a[i.id]=e)}for(const e of o){const i=Fo(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},n,this._wallKeyPitch,this._cellCm,this._gridPitch,sl);i.cm>0&&(h[e.id]=Qs(i.cm,this._cellCm,this._gridPitch))}}let c=Ds(e,o,s.azimuth,s.elevation,i,n.length?a:void 0,n.length?h:void 0);const d=this._physicalBodiesR(t);d.length&&(c=c.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.slice(0,-1).concat(o.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(d,t.dir,t.len),i=t.polys.map(t=>Vo(t,e));return{...t,paths:i.map(Wo).filter(Boolean),polys:i.flatMap(Yo)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:l,rays:c,rims:c.map(t=>function(t,e=1e-4){const[i,s]=t.dir,o=-s,n=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((c[0]-a[0])*o+(c[1]-a[1])*n)>e)continue;const d=(h[0]-a[0])*i+(h[1]-a[1])*s,p=(c[0]-a[0])*i+(c[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const h=[];for(const t of l){const i=h[h.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):h.push([t[0],t[1]])}for(const[t,e]of h)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const h=this._sunRaysCache.rays,c=this._sunRaysCache.rims;if(!h.length)return e;const d=(p=$s(s.elevation).warmth,Pi("#ffe9c2","#ff9a45",ks(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*ks(t)}(n),m=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` +
`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays,weather_entity:(t.weatherEntity||"").trim()||void 0}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Rs(this._sunGlobal(),this._sunSpace())}_effBgMode(){return As(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Ns(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Is(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Is(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const o=Es(this._sunGlobal()),n=function(t){if(!t)return 1;const e=zs[String(t).toLowerCase()];return void 0===e?1:e}(o?this.hass?.states?.[o]?.state:null),r=function(t=1){return.3*$s(t)}(n);if(r<=0)return this._sunFadeReset(),e;if(a=s.elevation,Number(a)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var a;const l=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const e=t.rooms.map(t=>({id:t.id||"",poly:Pe(t)})).filter(t=>!!t.id&&!!t.poly),o=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),n=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},h={};if(n.length){for(const i of e){const e=Ao(t.rooms,i.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,ol);e&&(a[i.id]=e)}for(const e of o){const i=Fo(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},n,this._wallKeyPitch,this._cellCm,this._gridPitch,ol);i.cm>0&&(h[e.id]=Qs(i.cm,this._cellCm,this._gridPitch))}}let c=Ds(e,o,s.azimuth,s.elevation,i,n.length?a:void 0,n.length?h:void 0);const d=this._physicalBodiesR(t);d.length&&(c=c.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.slice(0,-1).concat(o.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(d,t.dir,t.len),i=t.polys.map(t=>Vo(t,e));return{...t,paths:i.map(Wo).filter(Boolean),polys:i.flatMap(Yo)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:l,rays:c,rims:c.map(t=>function(t,e=1e-4){const[i,s]=t.dir,o=-s,n=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((c[0]-a[0])*o+(c[1]-a[1])*n)>e)continue;const d=(h[0]-a[0])*i+(h[1]-a[1])*s,p=(c[0]-a[0])*i+(c[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const h=[];for(const t of l){const i=h[h.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):h.push([t[0],t[1]])}for(const[t,e]of h)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const h=this._sunRaysCache.rays,c=this._sunRaysCache.rims;if(!h.length)return e;const d=(p=ks(s.elevation).warmth,Pi("#ffe9c2","#ff9a45",$s(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*$s(t)}(n),m=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` ${h.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,o=i+t.normal[0]*t.depth,n=s+t.normal[1]*t.depth;return B` ${u.map(([t,e])=>B`${this._t("gs.north_letter")} ${null===t?"—":t+"°"} - `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return $s(Ps(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Si(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Mi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const o=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(o)&&o>0&&300!==Math.round(o)?s.glow_radius_cm=Math.round(o):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays;const n=(t.weatherEntity||"").trim();n?s.weather_entity=n:delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?W``:W``}_rangeInput(t,e,i,s,o){const n=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?W``:W``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return W`
+ `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return ks(Ps(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Si(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Mi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const o=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(o)&&o>0&&300!==Math.round(o)?s.glow_radius_cm=Math.round(o):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays;const n=(t.weatherEntity||"").trim();n?s.weather_entity=n:delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?W``:W``}_rangeInput(t,e,i,s,o){const n=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?W``:W``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return W`
${this._t(e)} this._setFillColor(t,{c:e.target.value})} /> ${this._rangeInput(0,100,1,Math.round(100*i.a),e=>this._setFillColor(t,{a:e/100}))} ${Math.round(100*i.a)}% -
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t){const e=this._fillColors,i=this._glowRadiusCm/this._cellCm*this._gridPitch,s=this._gridPitch,o=t.rooms.map(t=>({r:t,poly:Pe(t)})).filter(t=>!!t.poly),n=this._openingsR.filter(t=>"door"===t.type),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._physicalBodiesR(t),h=new Map;if(r.length)for(const e of n){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,o=Math.sin(i)*e.rlen/2,n=To(t.rooms,r,a,[e.rx-s,e.ry-o,e.rx+s,e.ry+o],this._wallKeyPitch,this._cellCm,this._gridPitch,sl);n>0&&h.set(e.id,Qs(n,this._cellCm,this._gridPitch))}const c=new Map;for(const e of Kr(this.hass,this._devices.filter(e=>e.space===t.id)))e.on&&e.device.id&&!c.has(e.device.id)&&c.set(e.device.id,e.eid);const d=[];for(const p of this._devices){if(p.space!==t.id)continue;const u=c.get(p.id);if(!u)continue;const _=Ei(this.hass.states[u],e.glow_light.c);if(!_)continue;const m=Number(p.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:i,f=this._pos(p),v=[...o].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?Ba(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=l.length?Jo(l,[f.x,f.y],g):[],i=v.r.id?qi(v.r.id,t.rooms):new Set([v.r.id]),c=o.filter(t=>t.r.id&&i.has(t.r.id)),d=c.length?c:[v],p=d.map(i=>{const s=r.length&&i.r.id&&Ao(t.rooms,i.r.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||i.poly;return e.length?Wo(Vo(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=o.filter(t=>!d.includes(t)).map(t=>t.poly);for(const t of n){const i=d.some(e=>{const i=Fe([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!i)continue;const o=t.angle*Math.PI/180,n=Math.cos(o)*t.rlen/2,r=Math.sin(o)*t.rlen/2;if(!Oi([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*s))continue;const a=Ii([f.x,f.y],[t.rx-n,t.ry-r],[t.rx+n,t.ry+r],g,170,h.get(t.id)||0);a&&p.push(e.length?Wo(Vo(a,e)):"M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,ja(this._glowClipCache,b,y,256)}d.push({pos:f,c:_.c,alpha:e.glow_light.a*_.bri,clip:y,r:g})}return d.length?B` +
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t){const e=this._fillColors,i=this._glowRadiusCm/this._cellCm*this._gridPitch,s=this._gridPitch,o=t.rooms.map(t=>({r:t,poly:Pe(t)})).filter(t=>!!t.poly),n=this._openingsR.filter(t=>"door"===t.type),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._physicalBodiesR(t),h=new Map;if(r.length)for(const e of n){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,o=Math.sin(i)*e.rlen/2,n=To(t.rooms,r,a,[e.rx-s,e.ry-o,e.rx+s,e.ry+o],this._wallKeyPitch,this._cellCm,this._gridPitch,ol);n>0&&h.set(e.id,Qs(n,this._cellCm,this._gridPitch))}const c=new Map;for(const e of Yr(this.hass,this._devices.filter(e=>e.space===t.id)))e.on&&e.device.id&&!c.has(e.device.id)&&c.set(e.device.id,e.eid);const d=[];for(const p of this._devices){if(p.space!==t.id)continue;const u=c.get(p.id);if(!u)continue;const _=Ei(this.hass.states[u],e.glow_light.c);if(!_)continue;const m=Number(p.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:i,f=this._pos(p),v=[...o].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?ja(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=l.length?Jo(l,[f.x,f.y],g):[],i=v.r.id?qi(v.r.id,t.rooms):new Set([v.r.id]),c=o.filter(t=>t.r.id&&i.has(t.r.id)),d=c.length?c:[v],p=d.map(i=>{const s=r.length&&i.r.id&&Ao(t.rooms,i.r.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||i.poly;return e.length?Wo(Vo(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=o.filter(t=>!d.includes(t)).map(t=>t.poly);for(const t of n){const i=d.some(e=>{const i=Fe([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!i)continue;const o=t.angle*Math.PI/180,n=Math.cos(o)*t.rlen/2,r=Math.sin(o)*t.rlen/2;if(!Oi([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*s))continue;const a=Ii([f.x,f.y],[t.rx-n,t.ry-r],[t.rx+n,t.ry+r],g,170,h.get(t.id)||0);a&&p.push(e.length?Wo(Vo(a,e)):"M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,Ga(this._glowClipCache,b,y,256)}d.push({pos:f,c:_.c,alpha:e.glow_light.a*_.bri,clip:y,r:g})}return d.length?B` ${d.map((t,e)=>B` @@ -3533,7 +3564,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._t("gs.glow_radius")} {const e=Wa(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> + @input=${t=>{const e=Ba(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")}
@@ -3594,7 +3625,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:G} -
${this._t("gs.about_version",{v:Fa})}
+
${this._t("gs.about_version",{v:La})}
${this._t("gs.about_github")} @@ -3651,7 +3682,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t.busy?"…":this._t("btn.save")}
- `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(il,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const t=this._kioskScale,e=(e,i)=>W` + `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(sl,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const t=this._kioskScale,e=(e,i)=>W`
${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))} ${Math.round(100*t[e])}% @@ -3682,7 +3713,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._spaceDialog?this._renderSpaceDialog():G} ${this._importDialog?this._renderImportDialog():G} ${this._toast?W`
${this._toast}
`:G} - `;const e=this._spaceModel(),i=e.vb,s="devices"===this._mode&&this._showAll,o=this._devices.filter(t=>t.space===e.id&&(!t.hidden||s)),n=xi(this._curSpaceCfg),r=n.showLqi??this._config.show_signal??!0,a=this._config.icon_size??2.5,l=a>8?2.5:a,h=this._viewOr(i),c=this._editing?"":this._stageBg(n),d=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow(),p=d?$s(Ps(d.elevation)).planDim:0,u=this._opMeasureView,_=this._decorMeasure,m=this._bdLive,g=this._furnLive,f="view"===this._mode?this._editorChromeMode:this._mode;return W` + `;const e=this._spaceModel(),i=e.vb,s="devices"===this._mode&&this._showAll,o=this._devices.filter(t=>t.space===e.id&&(!t.hidden||s)),n=xi(this._curSpaceCfg),r=n.showLqi??this._config.show_signal??!0,a=this._config.icon_size??2.5,l=a>8?2.5:a,h=this._viewOr(i),c=this._editing?"":this._stageBg(n),d=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow(),p=d?ks(Ps(d.elevation)).planDim:0,u=this._opMeasureView,_=this._decorMeasure,m=this._bdLive,g=this._furnLive,f="view"===this._mode?this._editorChromeMode:this._mode;return W`
@@ -3761,14 +3792,14 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow preserveAspectRatio="none" />`:G} ${""} ${n.hideDecor&&"decor"!==this._mode?G:this._renderDecorLayer()} - ${(()=>{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Pe(t)),i.get(t));return e.rooms.filter(t=>t.area||"view"===this._mode||this._markup||n.showBorders).map(i=>{let o="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Vi(n.fill,i);if(!this._markup&&(n.showBorders||"none"!==l)){o+=" styled";const t=[];t.push(`--room-stroke:${n.color}`,`--room-stroke-op:${n.showBorders?n.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?zi("temp",null,"none",this._roomTemp(i),n.tempMin,n.tempMax,this._fillColors):"light"===l?zi("light",null,Yr(Kr(this.hass,this._devices,i)),null,n.tempMin,n.tempMax,this._fillColors):i.area?zi(l,"lqi"===l?this._roomLqi(i.area):null,"none",null,n.tempMin,n.tempMax,this._fillColors):null;e?(o+=" filled",t.push(`--room-fill:${e.c}`,`--room-fill-op:${e.a.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),a=t.join(";")}let h;const c=t=>{"view"===this._mode&&(void 0===h&&(h=this._roomArea(i)),this._showTip(t,i.name||this._t("room.unnamed"),h?this._t("tip.area",{value:h}):"",r?this._roomLqi(i.area):null,this._roomTemp(i)))},d=!e.bg&&!n.showNames&&!this._markup,p=this._roomCenter(i),u=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),_=i.id&&!u?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],m=u?[]:this._thickWallCuts(),g=_.concat(m);g.length&&(o+=" noedge");const f=s(i),v=this._spaceWalls,y=v.length&&i.id&&f&&Ao(e.rooms,i.id,v,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||f,b=y?Ke(y,(w=i,e.rooms.filter(t=>t!==w).map(s).filter(Boolean))):[];var w;const k=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",$=y?this._cleanFloor(i,y,e).path:"",x=i.id||G,S=i.area||G,M=$&&y?B`{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Pe(t)),i.get(t));return e.rooms.filter(t=>t.area||"view"===this._mode||this._markup||n.showBorders).map(i=>{let o="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Vi(n.fill,i);if(!this._markup&&(n.showBorders||"none"!==l)){o+=" styled";const t=[];t.push(`--room-stroke:${n.color}`,`--room-stroke-op:${n.showBorders?n.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?zi("temp",null,"none",this._roomTemp(i),n.tempMin,n.tempMax,this._fillColors):"light"===l?zi("light",null,Jr(Yr(this.hass,this._devices,i)),null,n.tempMin,n.tempMax,this._fillColors):i.area?zi(l,"lqi"===l?this._roomLqi(i.area):null,"none",null,n.tempMin,n.tempMax,this._fillColors):null;e?(o+=" filled",t.push(`--room-fill:${e.c}`,`--room-fill-op:${e.a.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),a=t.join(";")}let h;const c=t=>{"view"===this._mode&&(void 0===h&&(h=this._roomArea(i)),this._showTip(t,i.name||this._t("room.unnamed"),h?this._t("tip.area",{value:h}):"",r?this._roomLqi(i.area):null,this._roomTemp(i)))},d=!e.bg&&!n.showNames&&!this._markup,p=this._roomCenter(i),u=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),_=i.id&&!u?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],m=u?[]:this._thickWallCuts(),g=_.concat(m);g.length&&(o+=" noedge");const f=s(i),v=this._spaceWalls,y=v.length&&i.id&&f&&Ao(e.rooms,i.id,v,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||f,b=y?Ke(y,(w=i,e.rooms.filter(t=>t!==w).map(s).filter(Boolean))):[];var w;const $=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",k=y?this._cleanFloor(i,y,e).path:"",x=i.id||G,S=i.area||G,M=k&&y?B`this._hoverRoom={space:e.id,room:i}} @mousemove=${c} @mouseleave=${()=>{this._tip=null,this._hoverRoom=null}}>`:b.length&&y?B`this._hoverRoom={space:e.id,room:i}} @mousemove=${c} @mouseleave=${()=>{this._tip=null,this._hoverRoom=null}}>`:y&&y!==f?B``:G} ${this._toast?W`
${this._toast}
`:G} - `}_vacSource(t){const e=t.marker?.vacuum;if(!1===e?.live)return null;if(e?.source&&this.hass?.states[e.source])return e.source;for(const e of t.entities||[])if(ur(this.hass?.states[e]))return e;return null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return t.map(t=>t.eid).sort().join("\n")}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s={sources:i||"",last:{},flashTs:0,flashKind:null,timer:0,gen:0},this._activityRt.set(t,s)),null!=i&&(s.sources=i),s.flashTs&&Date.now()-s.flashTsthis.requestUpdate(),3360))}_syncActivityRuntime(){if(!this.hass)return;const t=new Set;for(const e of this._devices){if(e.hidden)continue;t.add(e.id);const i=this._visualSamples(e),s=this._activitySourceKey(i);let o=this._activityRt.get(e.id);if(o){if(o.sources!==s){clearTimeout(o.timer),o.sources=s,o.last={},o.flashTs=0,o.flashKind=null;for(const t of i)o.last[t.eid]=t.state}}else{o={sources:s,last:{},flashTs:0,flashKind:null,timer:0,gen:0};for(const t of i)o.last[t.eid]=t.state;this._activityRt.set(e.id,o)}}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e))}_activityTick(){if(this.hass){this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;const e=this._visualSamples(t),i=this._activitySourceKey(e),s=this._activityRt.get(t.id);if(!s||s.sources!==i)continue;"transition"===s.flashKind&&e.some(t=>"transition"===t.activity)&&(clearTimeout(s.timer),s.flashTs=0,s.flashKind=null);let o=null;for(const t of e){const e=Er(s.last[t.eid],t);("event"===e||!o&&e)&&(o=e),s.last[t.eid]=t.state}o&&this._stampActivity(t.id,o,i)}}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=fr(this.hass.states[i||""]?.state),o=pr(this.hass.states[e]?.attributes);let n=this._vacRt.get(t.id);n||(n={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,n)),s&&!n.moving&&(n.trail=[],n.lastPos=null);const r="never"!==wr(t.marker?.vacuum)&&!o?.path;!s&&n.moving&&(n.endedTs=Date.now(),r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=null),n.moving=s;const a=o?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==n.lastKey){const e=Date.now();n.jump=n.lastTs>0&&e-n.lastTs>1e4,n.lastKey=t,n.lastTs=e,r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return G;const i=e.marker?.vacuum||{},s=this._vacSource(e),o=s?pr(this.hass?.states[s]?.attributes):null,n=!!(o&&o.rooms.length>=3),r=o?.pos?wi(this._t("vac.status_found"),{name:s||""}):this._t("vac.status_none"),a=Object.keys(i.calibration||{}),l=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._saveConfig(),this.requestUpdate())};return W` + `}_vacSource(t){const e=t.marker?.vacuum;if(!1===e?.live)return null;if(e?.source&&this.hass?.states[e.source])return e.source;for(const e of t.entities||[])if(ur(this.hass?.states[e]))return e;return null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return t.map(t=>t.eid).sort().join("\n")}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s={sources:i||"",last:{},flashTs:0,flashKind:null,timer:0,gen:0},this._activityRt.set(t,s)),null!=i&&(s.sources=i),s.flashTs&&Date.now()-s.flashTsthis.requestUpdate(),3360))}_syncActivityRuntime(){if(!this.hass)return;const t=new Set;for(const e of this._devices){if(e.hidden)continue;t.add(e.id);const i=this._visualSamples(e),s=this._activitySourceKey(i);let o=this._activityRt.get(e.id);if(o){if(o.sources!==s){clearTimeout(o.timer),o.sources=s,o.last={},o.flashTs=0,o.flashKind=null;for(const t of i)o.last[t.eid]=t.state}}else{o={sources:s,last:{},flashTs:0,flashKind:null,timer:0,gen:0};for(const t of i)o.last[t.eid]=t.state;this._activityRt.set(e.id,o)}}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e))}_activityTick(){if(this.hass){this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;const e=this._visualSamples(t),i=this._activitySourceKey(e),s=this._activityRt.get(t.id);if(!s||s.sources!==i)continue;"transition"===s.flashKind&&e.some(t=>"transition"===t.activity)&&(clearTimeout(s.timer),s.flashTs=0,s.flashKind=null);let o=null;for(const t of e){const e=Ir(s.last[t.eid],t);("event"===e||!o&&e)&&(o=e),s.last[t.eid]=t.state}o&&this._stampActivity(t.id,o,i)}}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=fr(this.hass.states[i||""]?.state),o=pr(this.hass.states[e]?.attributes);let n=this._vacRt.get(t.id);n||(n={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,n)),s&&!n.moving&&(n.trail=[],n.lastPos=null);const r="never"!==wr(t.marker?.vacuum)&&!o?.path;!s&&n.moving&&(n.endedTs=Date.now(),r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=null),n.moving=s;const a=o?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==n.lastKey){const e=Date.now();n.jump=n.lastTs>0&&e-n.lastTs>1e4,n.lastKey=t,n.lastTs=e,r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return G;const i=e.marker?.vacuum||{},s=this._vacSource(e),o=s?pr(this.hass?.states[s]?.attributes):null,n=!!(o&&o.rooms.length>=3),r=o?.pos?wi(this._t("vac.status_found"),{name:s||""}):this._t("vac.status_none"),a=Object.keys(i.calibration||{}),l=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._saveConfig(),this.requestUpdate())};return W`
${r}
@@ -3910,7 +3941,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?W`
${wi(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:G} `:G} -
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return o=e.mapId,n=s,"default"!==o?o:null!=n?String(n):"default";var o,n}_vacSaveMatrix(t,e,i,s){const o=this._devices.find(e=>e.id===t),n=o?this._vacEnsureMarker(o):this._serverCfg?.markers?.find(e=>e.id===t);if(!n)return!1;const r={...n.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).map(t=>({r:t,poly:Pe(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=We(e);return{name:t.name,cx:i[0],cy:i[1]}}),n=mr(i.rooms,o);n?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix)&&(n.residual>50&&this._showToast(wi(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._showToast(wi(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),o=t.marker?.vacuum?.calibration?.[s],n=this._spaceModel(t.space),r=n?.vb||[0,0,sl,sl],a=o&&6===o.length&&function(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}(o)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1,h={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},c=yr(h),[d,p]=hr(c,(o+n)/2,(r+a)/2);return h.ox=e[0]+e[2]/2-d,h.oy=e[1]+e[3]/2-p,h}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,yr(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=pr(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),o={...e.p,...t};this._vacFit={...e,p:br(o,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,o=s?.clientWidth||1,n=s?.clientHeight||1;return[e/o*t.w,i/n*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[o,n]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+o,oy:s.p0.oy+n}};else{const t=pr(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=yr(s.p0),[a,l]=hr(r,e[0],e[1]),[h,c]=hr(r,s.fx,s.fy),d=Math.hypot(a-h,l-c)||1,[p,u]=[2*a-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*n-c)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:br(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return G;const i=pr(this.hass?.states[e.source]?.attributes);if(!i)return G;const s=yr(e.p),o=[],n=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>hr(s,t,e));e.forEach(([t,e])=>{n.push(t),r.push(e)});const[i,a]=hr(s,t.cx,t.cy);o.push(B` +
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return o=e.mapId,n=s,"default"!==o?o:null!=n?String(n):"default";var o,n}_vacSaveMatrix(t,e,i,s){const o=this._devices.find(e=>e.id===t),n=o?this._vacEnsureMarker(o):this._serverCfg?.markers?.find(e=>e.id===t);if(!n)return!1;const r={...n.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).map(t=>({r:t,poly:Pe(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=We(e);return{name:t.name,cx:i[0],cy:i[1]}}),n=mr(i.rooms,o);n?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix)&&(n.residual>50&&this._showToast(wi(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._showToast(wi(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),o=t.marker?.vacuum?.calibration?.[s],n=this._spaceModel(t.space),r=n?.vb||[0,0,ol,ol],a=o&&6===o.length&&function(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}(o)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1,h={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},c=yr(h),[d,p]=hr(c,(o+n)/2,(r+a)/2);return h.ox=e[0]+e[2]/2-d,h.oy=e[1]+e[3]/2-p,h}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,yr(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=pr(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),o={...e.p,...t};this._vacFit={...e,p:br(o,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,o=s?.clientWidth||1,n=s?.clientHeight||1;return[e/o*t.w,i/n*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[o,n]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+o,oy:s.p0.oy+n}};else{const t=pr(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=yr(s.p0),[a,l]=hr(r,e[0],e[1]),[h,c]=hr(r,s.fx,s.fy),d=Math.hypot(a-h,l-c)||1,[p,u]=[2*a-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*n-c)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:br(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return G;const i=pr(this.hass?.states[e.source]?.attributes);if(!i)return G;const s=yr(e.p),o=[],n=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>hr(s,t,e));e.forEach(([t,e])=>{n.push(t),r.push(e)});const[i,a]=hr(s,t.cx,t.cy);o.push(B` ${t.name}`)}let a=G;if(i.pos){const[e,o]=hr(s,i.pos.x,i.pos.y);a=B``}const l=[];if(n.length){const e=(()=>{const t=s[0]*s[4]-s[1]*s[3];return(e,i)=>[(s[4]*(e-s[2])-s[1]*(i-s[5]))/t,(-s[3]*(e-s[2])+s[0]*(i-s[5]))/t]})(),i=Math.min(...n),o=Math.max(...n),a=Math.min(...r),h=Math.max(...r),c=.022*t.w,d=c/4;for(const[t,s,n,r]of[[i,a,o,h],[o,a,i,h],[o,h,i,a],[i,h,o,a]]){const i=e(n,r);l.push(B` `)}}return W`
`)}return this._vacLastView=e,o.length&&!this._vacRaf&&this._vacRafLoop(),o.length||n.length?W` ${n.length?B`${n}`:G} - ${o}`:G}_renderDevice(t,e,i=!0){const s=this._pos(t),o=(s.x-e.x)/e.w*100,n=(s.y-e.y)/e.h*100,r=this._deviceVisual(t),a=t.hidden?"":this._stateClass(t,r),l=t.hidden?null:this._liveTemp(t),h=t.hidden?null:this._liveHum(t),c=!i||t.virtual||t.hidden?null:Jr(this.hass,t.entities),d=t.marker,p=this._displayOf(t),u="icon_ripple"===p&&!t.hidden&&this._config?.live_states&&"alarm"!==r.status?r.activity:"none",_=this._actEntity(t),m=_?this.hass.states[_]:void 0,g="value"!==p||t.hidden||null!=l||null!=h||!m||isNaN(parseFloat(m.state))?null:gi(this.hass,_),f="value"!==p||t.hidden?null:null!=l?l+"°":null!=h?h+"%":g?g.formatted?fi(g,m.attributes?.unit_of_measurement):parseFloat(m.state)+(m.attributes?.unit_of_measurement?" "+m.attributes.unit_of_measurement:""):null,v=_?_.split(".")[0]:null,y=this._config?.live_states&&!t.hidden?Ni(t.icon,v,m?.attributes?.device_class,m?.state,!!d?.icon):t.icon,b=Kr(this.hass,[t]),w=this._config?.live_states&&!t.hidden&&b.map(t=>function(t){if(!t||"on"!==t.state)return null;const e=t.attributes?.rgb_color;return Array.isArray(e)&&e.length>=3&&e.every(t=>Number.isFinite(t))?`rgb(${e[0]}, ${e[1]}, ${e[2]})`:null}(this.hass.states[t.eid])).find(t=>t)||null,k=Number(d?.size)>0?Number(d.size):1,$=Number(d?.angle)||0,x=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${o}%`,`top:${n}%`];return 1!==k&&S.push(`--dev-scale:${k}`),"icon_ripple"===p&&(S.push(`--ripple-scale:${x}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):w&&S.push(`--ripple-color:${w}`)),W`
function(t){if(!t||"on"!==t.state)return null;const e=t.attributes?.rgb_color;return Array.isArray(e)&&e.length>=3&&e.every(t=>Number.isFinite(t))?`rgb(${e[0]}, ${e[1]}, ${e[2]})`:null}(this.hass.states[t.eid])).find(t=>t)||null,$=Number(d?.size)>0?Number(d.size):1,k=Number(d?.angle)||0,x=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${o}%`,`top:${n}%`];return 1!==$&&S.push(`--dev-scale:${$}`),"icon_ripple"===p&&(S.push(`--ripple-scale:${x}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):w&&S.push(`--ripple-color:${w}`)),W`
${"none"!==u?W``:G} ${this._newIds.has(t.id)?W``:G} - ${null!=f?W`${f}`:W``} + ${null!=f?W`${f}`:W``} ${null!=l&&null==f?W`${l}°`:G} ${null!=h&&null==f?W`${h}%`:G} ${null!=c?W`${c}`:G} -
`}_roomArea(t){const e=Pe(t);if(!e)return null;const i=this._spaceWalls,s=i.length&&t.id&&Ao(this._spaceModel().rooms,t.id,i,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||e,o=this._cleanFloor(t,s),n=this._cellCm/this._gridPitch;return bs(o.area*n*n/1e4,"mi"===this.hass?.config?.unit_system?.length)}_roomTemp(t){const e=t.settings?.temp_source;return e?aa(this.hass,e,"temp",this._markers):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?aa(this.hass,e,"hum",this._markers):t.area?this._climate().get(t.area)?.hum??null:null}_climate(){const t=this._serverCfg?.markers,e=this._climateCache;if(e&&e.h===this.hass&&e.r===this._iconRules&&e.mk===t)return e.m;const i=function(t,e,i){const s=new Map;if(!t?.entities)return s;const o=sa(i),n=new Set;for(const t of i||[]){if(t?.removed||!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&n.add(t.binding.slice(e+1))}const r=new Map;for(const[e,i]of Object.entries(t.entities)){if(i.device_id&&o.devices.has(i.device_id)||!i.device_id&&o.entities.has(e))continue;const s=i.device_id?t.devices?.[i.device_id]:null,a=i.area_id||s?.area_id||null;if(!a)continue;if(i.entity_category)continue;if(!(n.size>0&&e.startsWith("climate.")&&(n.has(e)||i.device_id&&n.has(i.device_id)))){if(_t.has(i.platform))continue;if(la.test(e))continue}let l=r.get(a);l||(l=new Map,r.set(a,l));const h=i.device_id||e;let c=l.get(h);if(!c){const o=t.states?.[e];c={name:(s?s.name_by_user||s.name:i.name||o?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},l.set(h,c)}c.ents.push(e)}for(const[i,o]of r){const r=[],a=[];for(const[i,s]of o){const o=ia(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===o||"mdi:air-filter"===o;if(l){const e=Zr(t,s.ents);null!=e&&r.push(e)}if(n.size>0&&(n.has(i)||s.ents.some(t=>n.has(t)))){const e=Xr(t,s.ents);null!=e&&r.push(e)}if(l||"mdi:water-percent"===o){const e=ta(t,s.ents);null!=e&&a.push(e)}}(r.length||a.length)&&s.set(i,{temp:r.length?Math.round(r.reduce((t,e)=>t+e,0)/r.length*10)/10:null,hum:a.length?Math.round(a.reduce((t,e)=>t+e,0)/a.length):null})}return s}(this.hass,this._iconRules,t);return this._climateCache={h:this.hass,r:this._iconRules,mk:t,m:i},i}_resetRoomDialogFields(){this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1}_openRoomEdit(t){t.id&&(this._roomEditId=t.id,this._nameSel=t.name||"",this._areaSel=t.area||"",this._roomFill=t.settings?.fill_mode||"",this._roomTempSrc=t.settings?.temp_source||"",this._roomHumSrc=t.settings?.hum_source||"",this._roomNameScale=Ki(t.settings?.name_scale),this._roomLabelScale=Ki(t.settings?.label_scale),this._roomSrcOpen=null,this._roomSrcFilter="",this._roomDialog=!0)}_roomSettingsFromDialog(){const t={};return this._roomFill&&(t.fill_mode=this._roomFill),this._roomTempSrc&&(t.temp_source=this._roomTempSrc),this._roomHumSrc&&(t.hum_source=this._roomHumSrc),1!==this._roomNameScale&&(t.name_scale=this._roomNameScale),1!==this._roomLabelScale&&(t.label_scale=this._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this._curSpaceCfg,e=t?.rooms.find(t=>t.id===this._roomEditId);if(!e)return this._roomDialog=!1,void(this._roomEditId=null);e.name=this._nameSel.trim()||e.name,e.area=this._areaSel||null;const i=this._roomSettingsFromDialog();i?e.settings=i:delete e.settings,this._saveConfig(),this._roomDialog=!1,this._roomEditId=null,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate(),this._showToast(this._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.hass,e=sa(this._markers),i=this._roomSrcFilter.trim().toLowerCase(),s=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();i&&!t.toLowerCase().includes(i)||s.push({value:"device:"+o.id,label:t,sub:o.model||this._t("marker.sub_device")})}for(const[o,n]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||n.hidden)continue;if(oa(t,o,e))continue;const r=n.name||t.states[o]?.attributes?.friendly_name||o;i&&!(r+" "+o).toLowerCase().includes(i)||s.push({value:"entity:"+o,label:r,sub:o})}return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),i=t.slice(0,e),s=t.slice(e+1);return"device"===i?this.hass.devices[s]?.name_by_user||this.hass.devices[s]?.name||s:this.hass.entities[s]?.name||this.hass.states[s]?.attributes?.friendly_name||s}_labelPos(t,e){const i=this._layout["rl_"+(t.id||"")];if(i&&i.s===e)return{x:i.x*sl,y:i.y*sl};const s=this._snap(this._roomCenter(t));return{x:s[0],y:s[1]}}_labelDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=this._labelPos(e,i);this._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:s.x,oy:s.y,moved:!1},ll(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.id||"");if(!this._drag||this._drag.id!==s)return;const o=this._stageEl;if(!o)return;const n=this._spaceModel(i).vb,r=o.getBoundingClientRect(),a=this._viewOr(n),l=(t.clientX-this._drag.sx)/r.width*a.w,h=(t.clientY-this._drag.sy)/r.height*a.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0);const c=_n(this._drag.ox+l),d=_n(this._drag.oy+h);this._savePos({id:s,space:i},c,d)}_labelUp(t){const e="rl_"+(t.id||"");if(!this._drag||this._drag.id!==e)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&window.setTimeout(()=>this._drag=null,0)}_labelScale(t){const e=this._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=t.target.closest(".roomlabel");if(!s)return;const o=s.getBoundingClientRect(),n=o.left+o.width/2,r=o.top+o.height/2,a=Math.max(8,Math.hypot(t.clientX-n,t.clientY-r));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:n,cy:r,d0:a},ll(t)}_rlResizeMove(t){const e=this._rlResize;if(!e)return;t.stopPropagation();const i=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),s=Math.min(3,Math.max(.5,e.k0*(i/e.d0))),o=this._layout[e.id];if(o)this._layout={...this._layout,[e.id]:{...o,k:s}};else{const t=e.id.slice(3),i=this._spaceModel(e.space).rooms.find(e=>e.id===t);if(!i)return;const o=this._labelPos(i,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:o.x/sl,y:o.y/sl,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return G;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=We(t.poly),this._gearPtCache.set(t.poly,s))):null!=t.x&&null!=t.y&&(s=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!s)return G;const o=(s[0]-i.x)/i.w*100,n=(s[1]-i.y)/i.h*100;return W``}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return G;const o=this._labelPos(t,e.id),n=(o.x-i.x)/i.w*100,r=(o.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),h=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&h.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&h.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&h.push(W`${e}`)}if(s.labelLight){const e=(c=Kr(this.hass,this._devices,t)).length?{on:c.filter(t=>t.on).length,total:c.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});h.push(W`${t}`)}}}var c;return W`
`}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return G;const o=this._labelPos(t,e.id),n=(o.x-i.x)/i.w*100,r=(o.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),h=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&h.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&h.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&h.push(W`${e}`)}if(s.labelLight){const e=(c=Yr(this.hass,this._devices,t)).length?{on:c.filter(t=>t.on).length,total:c.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});h.push(W`${t}`)}}}var c;return W`
this._labelDown(i,t,e.id)} @@ -3974,12 +4005,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointerup=${()=>this._rlResizeUp()} @pointercancel=${()=>this._rlResizeUp()}>`):G}
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"!==this._tool&&"partition"!==this._tool||!this._path.length||this._contourClosed?"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:this._path[this._path.length-1]:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,o=(i[1]-t.y)/t.h*100,n=Yi(e,i),r=Math.round(10*n)/10,a=Ji(n);return W`
- ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,o]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-o)<1e-6)return null;const n=(e+s)/2,r=(i+o)/2;if("line"===t.kind){const e=Yi(t.a,t.b);return{x:n,y:r,on45:Ji(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,o]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(o-i)/2]);return{x:n,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(o-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:n,y:r,on45:!1,text:`${a} × ${l} · ${bs(Us(Math.abs(s-e),this._cellCm,this._gridPitch)*Us(Math.abs(o-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=sl,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Pe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return B``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,o=null;for(const n of e)if(!(Math.abs(n[0]-t[0])<1e-6&&Math.abs(n[1]-t[1])<1e-6)){if(Math.abs(n[0]-t[0])<=i){const e=Math.abs(n[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!o||e + ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,o]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-o)<1e-6)return null;const n=(e+s)/2,r=(i+o)/2;if("line"===t.kind){const e=Yi(t.a,t.b);return{x:n,y:r,on45:Ji(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,o]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(o-i)/2]);return{x:n,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(o-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:n,y:r,on45:!1,text:`${a} × ${l} · ${bs(Us(Math.abs(s-e),this._cellCm,this._gridPitch)*Us(Math.abs(o-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=ol,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Pe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return B``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,o=null;for(const n of e)if(!(Math.abs(n[0]-t[0])<1e-6&&Math.abs(n[1]-t[1])<1e-6)){if(Math.abs(n[0]-t[0])<=i){const e=Math.abs(n[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!o||e ${i.map(e=>{const[i,n,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return B` `})} `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return B``}_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._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ne(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&!oa(this.hass,t,sa(this._markers))}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,o=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,n=this._openingAmt(t),r=n>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a=s.length?Fo(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,sl):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,c=t.flip_v?-1:1;let d,p=0,u=0;if(a.cm>0&&(a.ox||a.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);p=a.ox*i-a.oy*s,u=a.ox*s+a.oy*i,u*=c,p*=h}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``}_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._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ne(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&!na(this.hass,t,oa(this._markers))}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,o=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,n=this._openingAmt(t),r=n>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a=s.length?Fo(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,ol):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,c=t.flip_v?-1:1;let d,p=0,u=0;if(a.cm>0&&(a.ox||a.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);p=a.ox*i-a.oy*s,u=a.ox*s+a.oy*i,u*=c,p*=h}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``:G;d=B` ${this._t("opening.length_label")} {const i=Wa(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> + @input=${e=>{const i=Ba(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> ${e(this._contactCandidates(),t.contact,e=>this._openingDialog={...t,contact:e})} @@ -4309,7 +4340,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @click=${()=>this._setMode("view")}> -
`}_cardEntities(t){const e=this.hass,i=[],s=new Set,o=t=>{if(!t||s.has(t)||!e.states[t])return;const o=e.entities[t];if("config"===o?.entity_category||"diagnostic"===o?.entity_category)return;s.add(t);const n=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(n)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(n)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(n)&&i.push({eid:t,kind:"value"})};for(const i of Kr(e,[t]))o(i.eid);t.primary&&o(t.primary);for(const e of t.entities)o(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?gi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Fi);return W``}_cardEntities(t){const e=this.hass,i=[],s=new Set,o=t=>{if(!t||s.has(t)||!e.states[t])return;const o=e.entities[t];if("config"===o?.entity_category||"diagnostic"===o?.entity_category)return;s.add(t);const n=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(n)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(n)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(n)&&i.push({eid:t,kind:"value"})};for(const i of Yr(e,[t]))o(i.eid);t.primary&&o(t.primary);for(const e of t.entities)o(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?gi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Fi);return W`this._infoCard=null}>
${(()=>{const e=this._cardEntities(t);return e.length?W`
@@ -4344,8 +4375,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t.primary?W``:G} - - +
`}_renderMarkerDialog(){const t=this._markerDialog,e="virtual"===t.bindingMode,i=this._bindingCandidates(),s=this._bindingEntities(t.binding),o=(()=>{if(e)return null;const s=i.find(e=>e.value===t.binding);if(s)return s.label;const[o,n]=t.binding.split(":");return"device"===o?this.hass.devices[n]?.name_by_user||this.hass.devices[n]?.name||n:this.hass.states[n]?.attributes?.friendly_name||n})();return W`
@@ -4385,7 +4415,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @input=${e=>this._markerDialog={...t,bindingFilter:e.target.value}} />
${i.map(e=>W`
this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:Wr(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> + @click=${()=>this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:Br(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> ${e.label}${e.sub}
`)} ${i.length?G:W`
${this._t("marker.nothing_found")}
`} @@ -4438,7 +4468,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .value=${t.controlsFilter} @input=${e=>this._markerDialog={...t,controlsFilter:e.target.value}} /> ${t.controlsFilter.trim()?W`
- ${Object.keys(this.hass.states).filter(e=>Wr(t.binding,[e],s).length>0&&!t.controls.includes(e)).filter(e=>{const i=t.controlsFilter.trim().toLowerCase(),s=String(this.hass.states[e]?.attributes?.friendly_name||"");return e.toLowerCase().includes(i)||s.toLowerCase().includes(i)}).slice(0,8).map(e=>W`
`)}
-
`:G}`:G}`}_renderRoomDialog(){const t=!!this._roomEditId,e=[...this._freeAreas];if(t&&this._areaSel&&!e.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&e.unshift(t)}return W``:G}`:G}`}_renderRoomDialog(){const t=!!this._roomEditId,e=!!this._areaSel||!!this._nameSel.trim(),i=[...this._freeAreas];if(t&&this._areaSel&&!i.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&i.unshift(t)}return W`
@@ -4763,7 +4793,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @@ -4794,16 +4824,11 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t?W``:W` - ${this._pendingSplit?G:W``:W`${this._pendingSplit?G:W``} - `}
-
`}}hl.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},hl.ZOOM_MAX=8,hl.ZOOM_MIN=1/3,hl._touchSeen=!1,hl._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,hl.styles=_a,customElements.get("houseplan-card")||customElements.define("houseplan-card",hl),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${Fa} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); +
`}}cl.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},cl.ZOOM_MAX=8,cl.ZOOM_MIN=1/3,cl._touchSeen=!1,cl._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,cl.styles=ma,customElements.get("houseplan-card")||customElements.define("houseplan-card",cl),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${La} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js index 81aa68d4..5c7347e0 100755 --- a/dist/houseplan-card.js +++ b/dist/houseplan-card.js @@ -1,4 +1,4 @@ -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 o=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 n=(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 o(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 o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:h,getOwnPropertyNames:c,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.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"),u.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:o}=h(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(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=[...c(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"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),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 o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),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),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(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}),(u.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,P=`<${D}>`,z=document,T=()=>z.createComment(""),R=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,N="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,O=/>/g,F=RegExp(`>|${N}(?:([^\\s"'>=/]+)(${N}*=${N}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,q=/"/g,H=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=U(1),B=U(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,h=-1):void 0===l[1]?h=-2:(h=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?q:L):r===q||r===L?r=F:r===I||r===O?r=E:(r=F,o=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+P:h>=0?(s.push(a),i.slice(0,h)+M+i.slice(h)+C+d):i+C+(-2===h?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,h]=J(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;iA(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&&R(this._$AH)?this._$AA.nextSibling.data=t:this.T(z.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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(T()),this.O(T()),this,this.options)):i=e[s],i._$AI(o),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 o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!R(t)||t!==this._$AH&&t!==j,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(T(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ht=at.litElementPolyfillSupport;ht?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ct=new WeakMap;let dt=0;class pt extends lt{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._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.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)return t.preventDefault(),t.stopImmediatePropagation(),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(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.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=ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ct.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._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ct.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ct.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ct.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.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(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=W` +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 o=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 n=(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 o(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 o("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:a,defineProperty:l,getOwnPropertyDescriptor:h,getOwnPropertyNames:c,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.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"),u.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:o}=h(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const n=s?.call(this);o?.call(this,e),this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(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=[...c(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"),o=t.litNonce;void 0!==o&&s.setAttribute("nonce",o),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 o=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==o?this.removeAttribute(s):this.setAttribute(s,o),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),o="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const n=o.fromAttribute(e,t.type);this[s]=n??this._$Ej?.get(s)??n,this._$Em=null}}requestUpdate(t,e,i,s=!1,o){if(void 0!==t){const n=this.constructor;if(!1===s&&(o=this[t]),i??=n.getPropertyOptions(t),!((i.hasChanged??y)(o,e)||i.useDefault&&i.reflect&&o===this._$Ej?.get(t)&&!this.hasAttribute(n._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,n??e??this[t]),!0!==o||void 0!==n)||(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}),(u.reactiveElementVersions??=[]).push("2.1.2");const $=globalThis,k=t=>t,x=$.trustedTypes,S=x?x.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,z=document,T=()=>z.createComment(""),R=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,N="[ \t\n\f\r]",E=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,I=/-->/g,O=/>/g,F=RegExp(`>|${N}(?:([^\\s"'>=/]+)(${N}*=${N}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),L=/'/g,q=/"/g,H=/^(?:script|style|textarea|title)$/i,U=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=U(1),B=U(2),j=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=z.createTreeWalker(z,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let o,n=2===e?"":3===e?"":"",r=E;for(let e=0;e"===l[0]?(r=o??E,h=-1):void 0===l[1]?h=-2:(h=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?F:'"'===l[3]?q:L):r===q||r===L?r=F:r===I||r===O?r=E:(r=F,o=void 0);const d=r===F&&t[e+1].startsWith("/>")?" ":"";n+=r===E?i+P:h>=0?(s.push(a),i.slice(0,h)+M+i.slice(h)+C+d):i+C+(-2===h?e:d)}return[Y(t,n+(t[i]||"")+(2===e?"":3===e?"":"")),s]};class Z{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const r=t.length-1,a=this.parts,[l,h]=J(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;iA(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&&R(this._$AH)?this._$AA.nextSibling.data=t:this.T(z.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){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const o of t)s===e.length?e.push(i=new tt(this.O(T()),this.O(T()),this,this.options)):i=e[s],i._$AI(o),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 o=this.strings;let n=!1;if(void 0===o)t=X(this,t,e,0),n=!R(t)||t!==this._$AH&&t!==j,n&&(this._$AH=t);else{const s=t;let r,a;for(t=o[0],r=0;r{const s=i?.renderBefore??e;let o=s._$litPart$;if(void 0===o){const t=i?.renderBefore??null;s._$litPart$=o=new tt(e.insertBefore(T(),t),t,void 0,i??{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return j}}lt._$litElement$=!0,lt.finalized=!0,at.litElementHydrateSupport?.({LitElement:lt});const ht=at.litElementPolyfillSupport;ht?.({LitElement:lt}),(at.litElementVersions??=[]).push("4.2.2");const ct=new WeakMap;let dt=0;class pt extends lt{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._titleId="hp-dialog-title-"+ ++dt,this._focusInitial=()=>{const t=this.querySelector("[autofocus]")||this._focusableElements()[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");t?.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)return t.preventDefault(),t.stopImmediatePropagation(),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(),o=i[0],n=i[i.length-1];!t.shiftKey||s!==o&&i.includes(s)?t.shiftKey||s!==n||(t.preventDefault(),o.focus()):(t.preventDefault(),n.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=ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),ct.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._focusRoot,e=this._opener;this._opener=null,this._focusRoot=null;const i=t?ct.get(t):void 0;i?.dialogs.delete(this),super.disconnectedCallback(),t&&i&&requestAnimationFrame(()=>{const i=ct.get(t);if(!i)return;if(!i.dialogs.size)return i.opener?.isConnected&&i.opener.focus({preventScroll:!0}),void ct.delete(t);const s=e?.closest("hp-dialog");e?.isConnected&&s&&i.dialogs.has(s)&&e.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(",");return Array.from(this.querySelectorAll(t)).filter(t=>{const e=getComputedStyle(t);return!t.hidden&&"none"!==e.display&&"hidden"!==e.visibility})}render(){const t=W` ${this.icon?W``:G} ${this.title} `;return this._useHaDialog?W`0||t===e?e:e-1}function Nt(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sh^i?1:-1;for(a=(l=o.length)<(h=n.length)?l:h,r=0;rn[r]^i?1:-1;return l==h?0:l>h^i?1:-1}function It(t,e,i,s){if(ti||t!==xt(t))throw Error(St+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Ot(t){var e=t.c.length-1;return At(t.e/Dt)==e&&t.c[e]%2!=0}function Ft(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Lt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!kt.test(d=String(t)))return o(p,d,h);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(It(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,m);if(d=String(t),h="number"==typeof t){if(0*t!=0)return o(p,d,h,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Mt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,c=d.length;la){a=c;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),h,e)}h=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(c=d.length;48===d.charCodeAt(--c););if(d=d.slice(l,++c)){if(c-=l,h&&M.DEBUG&&c>15&&(t>Pt||t!==xt(t)))throw Error(Mt+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Ft(l,r):Lt(l,r,"0");else if(n=(t=z(new M(t),e,i)).e,a=(l=Nt(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Lt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Dt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Dt,r=e,l=d[h=0],c=xt(l/p[o-r-1]%10);else if((h=$t((n+1)/Dt))>=d.length){if(!s)break t;for(;d.length<=h;d.push(0));l=c=0,o=1,r=(n%=Dt)-Dt+1}else{for(l=a=d[h],o=1;a>=10;a/=10,o++);c=(r=(n%=Dt)-Dt+o)<0?0:xt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[h+1]||(r<0?l:l%p[o-r-1]),s=i<4?(c||s)&&(0==i||i==(t.s<0?3:2)):c>5||5==c&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[h-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Dt-e%Dt)%Dt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=h,a=1,h--):(d.length=h+1,a=p[Dt-n],d[h]=r>0?xt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==h){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Ct&&(d[0]=1));break}if(d[h]+=a,d[h]!=Ct)break;d[h--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Ft(e,i):Lt(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(St+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(It(i=t[e],0,Rt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(It(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(It(i[0],-Rt,0,e),It(i[1],0,Rt,e),g=i[0],f=i[1]):(It(i,-Rt,Rt,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)It(i[0],-Rt,-1,e),It(i[1],1,Rt,e),v=i[0],y=i[1];else{if(It(i,-Rt,Rt,e),!i)throw Error(St+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(St+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(St+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(It(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(It(i=t[e],0,Rt,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(St+e+" not an object: "+i);$=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(St+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:$,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Rt&&o<=Rt&&o===xt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Dt)<1&&(e+=Dt),String(s[0]).length==e){for(e=0;e=Ct||i!==xt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(St+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return xt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],h=new M(u);if(null==t?t=_:It(t,0,Rt),o=$t(t/Dt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(St+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,h,c,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),k=d,f.c=e(Lt(Nt(u.c),u.e,"0"),10,n,t),f.e=f.c.length),c=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--c:(u.c=g,u.e=c,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,c=u.e),v=g[h=c+y+1],d=n/2,p=p||h<0||null!=g[h+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[h-1]||b==(u.s<0?8:7)),h<1||!g[0])s=p?Lt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=h,p)for(--n;++g[--h]>n;)g[h]=0,h||(++c,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=Lt(s,c,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,h=e%Tt,c=e/Tt|0;for(t=t.slice();l--;)a=((o=h*(n=t[l]%Tt)+(s=c*n+(r=t[l]/Tt|0)*h)%Tt*Tt+a)/i|0)+(s/Tt|0)+c*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,h,c,d,p,u,_,m,g,f,v,y,b,w,k,$,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(h=s.e-o.e)+1,a||(a=Ct,h=At(s.e/Dt)-At(o.e/Dt),S=S/Dt|0),c=0;D[c]==(C[c]||0);c++);if(D[c]>(C[c]||0)&&h--,S<0)g.push(1),d=!0;else{for(w=C.length,$=D.length,c=0,S+=2,(p=xt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),$=D.length,w=C.length),b=$,v=(f=C.slice(0,$)).length;v<$;f[v++]=0);x=D.slice(),x=[0].concat(x),k=D[0],D[1]>=a/2&&k++;do{if(p=0,(l=e(D,f,$,v))<0){if(y=f[0],$!=v&&(y=y*a+(f[1]||0)),(p=xt(y/k))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,$<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,c++);z(m,n+(m.e=c+h*Dt-1)+1,r,d)}else m.e=h,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,h=/^\.([^.]+)$/,c=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(c.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(h,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(St+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return Et(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return It(t,0,Rt),null==e?e=m:It(e,0,8),z(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-At(this.e/Dt))*Dt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,h,c=this;if((t=new M(t)).c&&!t.isInteger())throw Error(St+"Exponent not an integer: "+T(t));if(null!=e&&(e=new M(e)),r=t.e>14,!c.c||!c.c[0]||1==c.c[0]&&!c.e&&1==c.c.length||!t.c||!t.c[0])return h=new M(Math.pow(+T(c),r?t.s*(2-Ot(t)):+T(t))),e?h.mod(e):h;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&c.isInteger()&&e.isInteger())&&(c=c.mod(e))}else{if(t.e>9&&(c.e>0||c.e<-1||(0==c.e?c.c[0]>1||r&&c.c[1]>=24e7:c.c[0]<8e13||r&&c.c[0]<=9999975e7)))return n=c.s<0&&Ot(t)?-0:0,c.e>-1&&(n=1/n),new M(a?1/n:n);k&&(n=$t(k/Dt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Ot(t)):l=(o=Math.abs(+T(t)))%2,h=new M(u);;){if(l){if(!(h=h.times(c)).c)break;n?h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}if(o){if(0===(o=xt(o/2)))break;l=o%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Ot(t);else{if(0===(o=+T(t)))break;l=o%2}c=c.times(c),n?c.c&&c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}return s?h:(a&&(h=u.div(h)),e?h.mod(e):n?z(h,k,m,void 0):h)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:It(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Et(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Et(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Et(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&At(this.e/Dt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Et(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Et(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Dt,h=t.e/Dt,c=r.c,d=t.c;if(!l||!h){if(!c||!d)return c?(t.s=-e,t):new M(d?r:NaN);if(!c[0]||!d[0])return d[0]?(t.s=-e,t):new M(c[0]?r:3==m?-0:0)}if(l=At(l),h=At(h),c=c.slice(),a=l-h){for((n=a<0)?(a=-a,o=c):(h=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=c.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;c[i++]=0);for(e=Ct-1;s>a;){if(c[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((h=p*(h=v[--r]%g)+(a=u*h+(c=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*c,_[n--]=h%m;_[n]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Dt,r=t.e/Dt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=At(n),r=At(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Ct|0,a[e]=Ct===a[e]?0:a[e]%Ct;return o&&(a=[o].concat(a),++r),P(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return It(t,1,Rt),null==e?e=m:It(e,0,8),z(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Dt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return It(t,-9007199254740991,Pt),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,h=r.e,c=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+T(r)))||l==1/0?(((e=Nt(a)).length+h)%2==0&&(e+="0"),l=Math.sqrt(+e),h=At((h+1)/2)-(h<0||h%2),s=new M(e=l==1/0?"5e"+h:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+h)):s=new M(l+""),s.c[0])for((l=(h=s.e)+c)<3&&(l=0);;)if(n=s,s=d.times(n.plus(i(r,n,c,1))),Nt(n.c).slice(0,l)===(e=Nt(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,c=u.substr(0,n);n<_;n+=a)c+=h+u.substr(n,a);l>0&&(c+=h+u.slice(n)),p&&(c="-"+c)}s=d?c+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):c}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,h,c,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(St+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+T(l));if(!f)return new M(g);for(e=new M(u),c=s=new M(u),o=h=new M(u),_=Nt(f),r=e.e=_.length-g.e-1,e.c[0]=zt[(a=r%Dt)<0?Dt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:c:l,a=y,y=1/0,l=new M(_),h.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,c=h.plus(d.times(n=c)),h=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),h=h.plus(n.times(c)),s=s.plus(n.times(o)),h.s=c.s=g.s,p=i(c,o,r*=2,m).minus(g).abs().comparedTo(i(h,s,r,m).minus(g).abs())<1?[c,o]:[h,s],y=a,p},p.toNumber=function(){return+T(this)},p.toPrecision=function(t,e){return null!=t&&It(t,1,Rt),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Ft(Nt(i.c),n):Lt(Nt(i.c),n,"0"):10===t&&S?e=Lt(Nt((i=z(new M(i),_+n+1,m)).c),i.e,"0"):(It(t,2,x.length,"Base"),e=s(Lt(Nt(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return T(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Ht=class{key;left=null;right=null;constructor(t){this.key=t}},Ut=class extends Ht{constructor(t){super(t)}},Wt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Bt=class t extends Wt{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new Ut(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Ut(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Ut(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new Vt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Gt(this.wrap())}[Symbol.toStringTag]="[object Set]"},jt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Gt=class extends jt{getValue(t){return t.key}},Vt=class extends jt{getValue(t){return[t.key,t.key]}},Kt=t=>()=>t,Yt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Kt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Jt(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):Kt(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var Zt=t=>t,Xt=t=>{if(t){const e=new Bt(Yt(t)),i=new Bt(Yt(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new qt(0),y:new qt(0)}),o}return Zt},Qt=t=>({set:t=>{te=Qt(t)},reset:()=>Qt(t),compare:Yt(t),snap:Xt(t),orient:Jt(t)}),te=Qt(),ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),ie=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},se=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),oe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ne=t=>oe(t,t).sqrt(),re=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return se(o,s).div(ne(o)).div(ne(s))},ae=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return oe(o,s).div(ne(o)).div(ne(s))},le=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},he=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},ce=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:ve.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:re(this.point,t.point,s.point),cosine:ae(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},de=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},pe=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(h))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(h))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),c=h.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&c.isLessThan(d))return 1;if(t.isLessThan(e)&&c.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(h)?-1:l.isGreaterThan(h)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++fe,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ce.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ce(o,!0),h=new ce(n,!1);return new t(l,h,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return te.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=ie(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ee(e,r)&&0===this.comparePoint(r),h=ee(i,o)&&0===t.comparePoint(o),c=ee(e,a)&&0===this.comparePoint(a),d=ee(i,n)&&0===t.comparePoint(n);if(h&&l)return d&&!c?n:!d&&c?a:null;if(h)return c&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&c)return null;if(d)return n;if(c)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return he(i,s,t.x);if(s.x.isZero())return he(t,e,i.x);if(e.y.isZero())return le(i,s,t.y);if(s.y.isZero())return le(t,e,i.y);const o=se(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=se(n,e).div(o),a=se(n,s).div(o),l=t.x.plus(a.times(e.x)),h=i.x.plus(r.times(s.x)),c=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(h).div(2),y:c.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ee(s,p)?te.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ce(e,!0),n=new ce(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ce.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ce.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ye=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=te.snap({x:new qt(t[0][0]),y:new qt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;ege.run("union",t,e),$e=(t,...e)=>ge.run("intersection",t,e),xe=(t,...e)=>ge.run("difference",t,e);function Se(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Me(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function Ce(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function De(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function ze(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function Te(t){const e=[],i=new Set;for(const s of t||[]){const t=Pe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*h,y:r+l*c,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function Ae(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,h=n;for(const e of Te(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],c=Math.min(o,r),d=Math.max(o,r),p=c>0?c:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(c+d)/2;return{wallA:m(c),wallB:m(d),sideA:u,sideB:_,midA:m((c-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ne(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"door"===t?1:0;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function Ee(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Oe(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function Fe(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function We(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let h=0,c=0,d=0;for(let e=0;e1e-9?[c/(3*h),d/(3*h)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ie([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=h,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Be(t)||t[0]}function Be(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(qe(s,t,e))return s;for(let s=0;sGe(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ge(e,t,i)))}function Ye(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ze(t,e,i){for(let s=0;s1&&Ee(i[0],i[i.length-1],e)&&i.pop(),i}function Qe(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function ti(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function ei(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const di=["automation","script","scene"];const pi="—",ui="{}";function _i(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function mi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=_i(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function gi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function fi(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function vi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return pi;const n=(e?.attr||"").trim(),r=gi(t,i,n||null);if(null===r)return pi;return fi(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const yi=20;function bi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function wi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const ki="#55606c",$i=.55;function xi(t){const e=t?.settings||{},i=!t?.plan_url;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:"string"==typeof e.room_color&&/^#[0-9a-f]{6}$/i.test(e.room_color)?e.room_color:ki,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):$i,fill:["lqi","light","temp","glow"].includes(e.fill_mode)?e.fill_mode:"none",tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:"string"==typeof e.bg_color&&/^#[0-9a-f]{6}$/i.test(e.bg_color)?e.bg_color:null,hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Si(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return"string"==typeof i&&/^#[0-9a-f]{6}$/i.test(i)?i:""}const Mi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}},Ci=/^#[0-9a-f]{6}$/i;function Di(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Mi)){const s=Mi[t],o=i[t];e[t]={c:o&&"string"==typeof o.c&&Ci.test(o.c)?o.c:s.c,a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function Pi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function zi(t,e,i,s,o,n,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:Pi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}const Ti={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},Ri=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function Ai(t){for(const e of[...Object.values(Ti),...Ri])if(t===e[0]||t===e[1])return e;return null}function Ni(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Ai(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=Ti[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Ai(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Ei(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),o=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,n=i.rgb_color;if(Array.isArray(n)&&n.length>=3&&n.every(t=>Number.isFinite(t)))return{c:`rgb(${n[0]}, ${n[1]}, ${n[2]})`,bri:o};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:o}}return{c:e,bri:o}}function Ii(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,h;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),c=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*c>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+c*_*u],g=[i[0]+a*_*u,i[1]+c*_*u],f=[e[0]-a*_*u,e[1]-c*_*u],v=[i[0]-a*_*u,i[1]-c*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),k=b(g),$=b(f),x=b(v),S=Math.max(Math.min(w,k),Math.min($,x)),M=Math.min(Math.max(w,k),Math.max($,x));if(!(M-S>1e-9))return null;l=S,h=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),h=Math.atan2(i[1]-t[1],i[0]-t[0])-l;h>Math.PI;)h-=2*Math.PI;for(;h<-Math.PI;)h+=2*Math.PI}const c=o*Math.PI/180;if(Math.abs(h)>c){const t=l+h/2;h=c*Math.sign(h),l=t-h/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+h*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Oi(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>qe(l,t,1e-9))}function Fi(t){return t.startsWith("light.")||t.startsWith("switch.")}function Li(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*c+(o[1]-n[1])*d,_=(r[0]-n[0])*c+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(h,Math.max(u,_));g-m>i&&s.push([n[0]+c*m,n[1]+d*m,n[0]+c*g,n[1]+d*g])}}return s}function qi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function Hi(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*h+(s[1]-t[1])*c,a=(s[2]-t[0])*h+(s[3]-t[1])*c,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+h*p,t[1]+c*p,t[0]+h*e,t[1]+c*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+h*p,t[1]+c*p,n[0],n[1]])}return s}function Ui(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=Gi(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function Gi(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function Vi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Ki(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Yi(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ji(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Zi(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const Xi=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Qi=(t,e)=>[t[0]-e[0],t[1]-e[1]],ts=(t,e)=>[t[0]+e[0],t[1]+e[1]],es=(t,e)=>t[0]*e[0]+t[1]*e[1],is=t=>Math.hypot(t[0],t[1]);function ss(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function rs(t,e,i,s){const o=s||ns(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function as(t,e,i,s){const o=[],n=Qi(i,e),r=is(n);if(rs||c>s)continue;const d=es(Qi(n,e),a),p=es(Qi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function ls(t,e,i,s,o){const n=as(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>os(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=es(Qi(e,h),u),y=es(Qi(i,h),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wis(Qi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=is(Qi(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function cs(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],h=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],c=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=h(e);o<=c||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ps(t,e,i){if(Ve(t,e,i))return!0;if(Ge(t,e,i)||Ge(e,t,i))return!1;let s=0;try{const i=$e([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of i)t?.[0]&&(s+=Ye(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function us(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=rs(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[ts(i.a,n),ts(i.b,n)]];continue}const t=as(e.poly,i.a,i.b,o);if(!t.length)continue;const a=ls(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[ts(t,n),ts(e,n)]))}for(const t of e)os([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function _s(t,e,i){for(const s of e)for(let e=0;ei)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-i&&h+t.length/2<=a+i)return!0}return!1}function ms(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!cs(o))return!1;const l=ss(s.poly),c=ss(o);if(Math.abs(c)t.find(t=>t.id===e).poly),d=t.map(h);for(const t of ms(e,c,2*r)){const e=a.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function fs(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const h=t.filter(t=>t.id!==i).map(t=>t.poly),c=fs(l,e,h,s,o,2*a),d=c.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ms(e,[l.poly],2*a)){const e=c.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ys(t,e,i){const s=i/e;return Ye(t)*s*s/1e4}function bs(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const ws=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],ks=t=>Math.min(1,Math.max(0,t));function $s(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=ws[ws.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Ie(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Ss(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Ms(t,e,i,s){return[[t[0],t[1]],[e[0],e[1]],[e[0]+i[0]*s,e[1]+i[1]*s],[t[0]+i[0]*s,t[1]+i[1]*s]]}function Cs(t,e){try{const i=$e([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]),s=[];for(const t of i){const e=t?.[0];!Array.isArray(e)||e.length<4||s.push(e.slice(0,e.length-1).map(t=>[t[0],t[1]]))}return s}catch{return[]}}function Ds(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],h=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),c=[];for(const i of e){if(!(i.length>0))continue;const e=xs(i,t);if(!e||!Ss(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],k=h*i.length,$=Cs(Ms(b,w,l,k),d);if(!$.length)continue;const x=l[0]*_[0]+l[1]*_[1];c.push({openingId:i.id,roomId:e.roomId,polys:$,a:b,b:w,dir:l,len:k,normal:_,depth:k*x})}return c}function Ps(t){return Math.round(10*(Number(t)||0))/10}const zs={clear:1,sunny:1,"clear-night":1,windy:1,exceptional:1,partlycloudy:.7,"windy-variant":.7,cloudy:.4,overcast:.25,fog:.25,rainy:0,pouring:0,snowy:0,"snowy-rainy":0,hail:0,lightning:0,"lightning-rainy":0};const Ts=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Rs(t,e){const i=Ts(e?.north_deg);return null!==i?i:Ts(t?.north_deg)}function As(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Ns(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Es(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Is(t){const e=t?.states?.["sun.sun"]?.attributes,i=Number(e?.azimuth),s=Number(e?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Os={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Fs=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ls=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function qs(t,e,i){return Ls(e)?[t.x,t.y]:i([t.x,t.y])}const Hs=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Us=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Ws=(t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Us(n,e,i):s};function Bs(t,e){return{color:t.color,opacity:Fs(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Fs(t.fillOpacity,.25)}:{}}}function js(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ls(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Gs(t){const e=js(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Vs(t,e,i,s,o,n,r,a){const l=Ls(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*h+g*d,v=_+m*c+g*p,y=s-f,b=o-v;let w=(y*h+b*c)*(e>0?1:-1),k=(y*d+b*p)*(i>0?1:-1),$=null;if(n){const e=w/Math.max(t.w,a),i=k/Math.max(t.h,a);$=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*$,k=t.h*$}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round(($??1)/o)*o);w=t.w*r,k=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,k=w*(t.h/Math.max(t.w,a))):(k=Math.round(k/r)*r,w=k*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,k=Math.round(k/r)*r;w=Math.max(a,w),k=Math.max(a,k);return{x:f+(e>0?w/2:-w/2)*h+(i>0?k/2:-k/2)*d-w/2,y:v+(e>0?w/2:-w/2)*c+(i>0?k/2:-k/2)*p-k/2,w:w,h:k,angle:Ls(t.angle)||void 0}}const Ks=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Ys=15;function Js(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Zs(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Xs(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Qs(t,e,i){const s=Number(e)>0?Number(e):5;return Zs(t)/s*i}function to(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function eo(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function io(t,e,i){const s=to((t[0]+e[0])/2,i),o=to((t[1]+e[1])/2,i),[n,r]=eo(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,h=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(h)},${o.toFixed(h)}@${l.toFixed(4)}`}function so(t,e,i,s){return 1===s?io(t,e,i):io([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function oo(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function no(t,e,i,s,o){const n=o>0?o:1;return{key:so(t,e,s,n),cm:Zs(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function ro(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function ao(t,e){const[i,s]=eo(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function lo(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function ho(t,e,i,s,o=1){if(!t?.length)return null;const n=so(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,h=(e[1]+i[1])/2,c=ao(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of ro(t,a))if(lo(e.ang,c)&&Math.hypot(e.x-l,e.y-h)<=d)return e.w;return null}function co(t,e,i,s,o=1){const n=ho(t,e,i,s,o);return n&&n.cm>0?Zs(n.cm):0}function po(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Te(e);for(const t of r)n.add(so([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=oo(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],h=n[1]-i[1],c=Math.hypot(a,h);if(c<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return lo(ao(i,n),ao(e,s))&&Ro(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ro(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!lo(ao(i,n),ao(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*h-(t[1]-i[1])*a)/c;if(o(e)>l||o(s)>l)return!1;const r=c*c,d=((e[0]-i[0])*a+(e[1]-i[1])*h)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*h)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/c});return!d})(t))&&t.cm>=1&&t.cm<=100)}function uo(t,e,i,s=8){const[o,n]=eo(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function _o(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,h=[];for(const o of t){let t="",c=null;const d=oo(o,r);if(d)for(let o=0;oa||Ro(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[h[0]+(p[0]-h[0])*e,h[1]+(p[1]-h[1])*e]};c=[g(d[0]),g(d[1])],t=so(c[0],c[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=ro([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ro(n.x,n.y,l[0],l[1],h[0],h[1])>a)continue;const g=c[0]+(d[0]-c[0])*Math.max(0,Math.min(1,m)),f=c[1]+(d[1]-c[1])*Math.max(0,Math.min(1,m)),[v,y]=eo(c,d),b=Math.max(s*r,1e-6);t=so([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),h.push(c?no(c[0],c[1],o.cm,s,r):{...o,key:t,cm:Zs(o.cm)}))}return h}function mo(t,e,i,s,o,n=1){const r=so(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,no(e,i,s,o,n)]}function go(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Mo(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Io(i,t),o=$o(i,t);if(s&&o)return`${xo(s)} ${xo(So(o))}`}}let s="";for(let i=0;i.05)&&Ro(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function yo(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function bo(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const h=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([h[0]+.001*a,h[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function wo(t,e){const i=t[0]*e[1]-t[1]*e[0],s=t[0]*e[0]+t[1]*e[1];return Math.abs(i)<1e-9&&s>0}function ko(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function $o(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]+p*c,e[1]+u*c],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=ko(k,b,$,w),S=Math.max(c,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}c>0&&s.push([t[o][0]+p*c,t[o][1]+u*c]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),c>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function xo(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Pe(r);if(!a||a.length<3)return null;const l=yo(s,o),h=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Pe(i);if(t)for(const e of Li(a,t,l))h.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])h.push([t[0],t[1]],[t[2],t[3]]);for(const t of n||[]){const e=oo(t,o);e&&h.push(e[0],e[1])}const c=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:c,parent:d,orig:a}}function Co(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=Zs(h.cm),r.add(h.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,h=Math.max(.5*i,1e-9)*l,c=ro(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=ao(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of c){if(r.has(t.w.key))continue;if(!lo(t.ang,a))continue;const e=oo(t.w,l);let i=!1,n=0;if(e){if(!lo(ao(e[0],e[1]),a))continue;if(Ro(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>h||Ro(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>h)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ro(t.x,t.y,s[0],s[1],o[0],o[1])>h)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Pe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Pe(s);if(t)for(const e of Li(o,t,i))n.push(e)}return n}(t,e,yo(o,a)),c=function(t,e,i,s,o){const n=yo(s,o),r=[];for(let a=0;aRo(c,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,h,s,o,a),d=Co(i,l,o,a),p=d.map((t,e)=>c[e]&&t>0?Qs(t,n,r)/2:0);return{...l,kinds:c,cms:d,offsets:p}}function Po(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(h)for(let t=0;t0&&!l.has(h.key)&&(l.add(h.key),a.push(h));const h=[];for(const a of t||[]){if(!a?.id)continue;const l=Do(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&h.push({a:[c[0],c[1]],b:[d[0],d[1]],key:so(c,d,s,r),cm:o,len:p}),t=n+1}}}h.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const c=[],d=new Set,p=new Set,u=4*yo(s,r);for(const t of h){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&lo(ao(e.a,e.b),ao(t.a,t.b))&&Ro(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ro(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)));return c}function To(t,e,i,s,o,n,r,a=1){const l=yo(o,a),h=(s[0]+s[2])/2,c=(s[1]+s[3])/2,d=ao([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Po(t,e,i,o,n,r,a)){if(!lo(ao(s.a,s.b),d))continue;const t=Ro(h,c,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),h=Pe(l);if(!h||h.length<3)return null;if(!i?.length)return h.map(t=>[t[0],t[1]]);const c=Do(t,e,i,s,o,n,r,a);return c&&c.offsets.some(t=>t>0)&&$o(c.poly,c.offsets)||h.map(t=>[t[0],t[1]])}function No(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Eo(t,e,i,s=[],o,n,r,a=1,l=[]){if(!e?.length&&!l.length)return null;const h=[];let c=0;for(const s of t||[]){if(!s?.id)continue;const l=Do(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(c=Math.max(c,2*t));const d=Io(l.poly,l.offsets),p=$o(l.poly,l.offsets);d&&h.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));c=Math.max(c,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*yo(s,r),l=new Map;for(const a of Po(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const h=[...l.values()];if(h.length<2)return[];const c=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])c.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||c.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of c){const e=h.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),c,u,p]:[t.slice(),p,u,c])}}return d}(t,e,i,o,n,r,a);try{const t=t=>{const e=No(t.outset);return t.inset?xe(e,No(t.inset)):e};let e=h.length?t(h[0]):null;for(let i=1;i0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),n=Math.sin(i),r=-n,l=s,h=t.length/2,d=1.25*Math.max(c,o*a),p=[[t.x-s*h-r*d,t.y-n*h-l*d],[t.x+s*h-r*d,t.y+n*h-l*d],[t.x+s*h+r*d,t.y+n*h+l*d],[t.x-s*h+r*d,t.y-n*h+l*d]];e&&(e=xe(e,No(p)))}for(const t of l)t.length<3||(e=e?ke(e,No(t)):[No(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+xo(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:c,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(!h||h.poly.length<3||!h.offsets.some(t=>t>0))continue;const c=Io(h.poly,h.offsets),d=$o(h.poly,h.offsets);if(!c||!d)continue;const p=`${xo(c)} ${xo(So(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=so(h.poly[t],h.poly[(t+1)%h.poly.length],s,r),_=h.kinds[t]||"outer",m=h.cms[t],g=Qs(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),h=l.map(t=>xo(t)).join(" ");return s.length||h?{d:[s.map(t=>t.d).join(" "),h].filter(Boolean).join(" "),depthUnits:c,fillRule:"nonzero"}:null}}function Io(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);So(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-h*r],n=[e[0]-c*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=ko(k,b,$,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-h*r]),a>0&&s.push([t[o][0]-c*a,t[o][1]-d*a])}return s.length>=3?s:null}function Oo(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const h=Pe(l);if(h&&h.length>=3){const c=Do(t,l.id,e,i,s,o,n,r),d=c&&c.offsets.some(t=>t>0)?Io(c.poly,c.offsets):null,p=d||h;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Fo(t,e,i,s,o,n,r=1){let a=null;for(const i of t||[]){const t=Pe(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=co(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const h=Qs(l,o,n),c=Pe(a.room),[d,p]=bo(c,a.edge),u=e.flip_v?-1:1,_=h/2;return{ox:d*_*u,oy:p*_*u,cm:l}}const Lo=150;function qo(t){return Number.isFinite(t)?Math.max(1,Math.min(Lo,t)):1}function Ho(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Uo=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Wo(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Bo(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=Qs(i,s,o)/2,h=-r/a*l,c=n/a*l;return[[t[0]+h,t[1]+c],[e[0]+h,e[1]+c],[e[0]-h,e[1]-c],[t[0]-h,t[1]-c]]}function jo(t,e,i){const s=Number(e)>0?Number(e):5,o=qo(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Ho(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*h-e*c,r+t*c+e*h])}function Go(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Uo(t));return e.length?ke(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return xe(Uo(t),i)}catch{}let i=[Uo(t)];for(const t of e)if(!(t.length<3))try{i=xe(i,Uo(t))}catch{}return i}function Ko(t){let e=0;for(const i of t||[])if(i?.length){e+=Ye(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jo(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zo(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const h=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+h*r),e[1]-(i[1]+h*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*c+h*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+h/p*g],v=[e[0]+c/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function Zo(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function Xo(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(qo(t.cm)-qo(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Ho(t.angle)-Ho(e.angle));return Math.min(s,90-s)<=1e-6}const Qo=1e3;function tn(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const en=.01,sn=100;function on(t,e=1e3){const i=tn(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(sn,Math.max(en,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(sn,Math.max(en,n)):o,l=Number.isFinite(r)&&r>0?Math.min(sn,Math.max(en,r)):o,h=Number(t?.plan_x),c=Number(t?.plan_y),d=Ls(t?.plan_angle);return{x:i.x+(Number.isFinite(h)?mn(h):0)*e,y:i.y+(Number.isFinite(c)?mn(c):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function nn(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function rn(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qo,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Qo,i[1]*e,i[2]*Qo,i[3]*e],bg:t.plan_url?{href:Gi(t.plan_url),...on(t,Qo)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...nn(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Qo:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qo:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qo,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qo,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qo,t.a[1]*e],b:[t.b[0]*Qo,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*Qo,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Ho(t.angle)}}))}}):[]}const an=5e3,ln=an*Qo,hn=240,cn=Qo/hn,dn=1/hn;function pn(t){if(!Number.isFinite(t))return t;const e=Math.round(t*hn/Qo)*Qo/hn;return Math.abs(e-t)<=1e-9*cn?t:e}function un(t){return{x:pn(t.x),y:pn(t.y)}}function _n(t){return Number.isFinite(t)?Math.min(ln,Math.max(-ln,t)):0}function mn(t){return Number.isFinite(t)?Math.min(an,Math.max(-an,t)):0}const gn=1/3,fn=200;function vn(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function yn(t){return t.poly&&t.poly.length?vn(t.poly):null==t.x||null==t.y?null:vn([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function bn(t,e){const i=[];for(const e of t.rooms||[]){const t=yn(e);t&&i.push(t)}if(t.bg){const e=vn(js(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=vn([t]);e&&i.push(e)}else i.push(t);return i}const wn=t=>{if(!t.length)return 0;const e=[...t].sort((t,e)=>t-e),i=e.length>>1;return e.length%2?e[i]:(e[i-1]+e[i])/2},kn=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function $n(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=fn),n<30&&(s=s+n/2-100,n=fn);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function xn(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ln&&Math.abs(t.maxX)<=ln&&Math.abs(t.minY)<=ln&&Math.abs(t.maxY)<=ln);if(!n.length)return{core:null,all:null,outliers:0};const r=kn(n);if(n.length<4){const t=$n(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),h=wn(a),c=wn(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-h),Math.abs(l[e]-c))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*gn?n.filter((t,e)=>!u[e]):n;return{core:$n(kn(m)||r,i),all:$n(r,i),outliers:m===n?0:_}}function Sn(t,e,i=.05){const s=xn(bn(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,Qo,Qo];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Mn(t){const e=Sn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Cn(t){const e=[];for(const i of t.rooms||[]){const t=yn(i);t&&e.push(t)}const i=xn(e,{pad:0}).core;return i?Math.max(Qo,Math.min(ln,Math.max(i.w,i.h))):Qo}function Dn(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Cn(e)*n/o}const Pn=[1,2,5,10,20,50,100,200,500,1e3];function zn(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Tn(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)]}const Rn=["furniture","appliance","sanitary","other"],An=[{id:"sofa",group:"furniture",w:220,h:90,g:[["r",0,0,1,1],["l",.09,.26,.91,.26],["l",.09,.26,.09,1],["l",.91,.26,.91,1],["l",.5,.26,.5,1]]},{id:"armchair",group:"furniture",w:90,h:85,g:[["r",0,0,1,1],["l",.14,.28,.86,.28],["l",.14,.28,.14,1],["l",.86,.28,.86,1]]},{id:"coffee_table",group:"furniture",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",w:120,h:60,g:[["r",0,0,1,1],["r",.63,.07,.31,.86],["l",.63,.5,.94,.5]]},{id:"bed_double",group:"furniture",w:160,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.06,.1,.4,.15],["r",.54,.1,.4,.15],["l",0,.33,1,.33]]},{id:"bed_single",group:"furniture",w:90,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.15,.1,.7,.15],["l",0,.33,1,.33]]},{id:"nightstand",group:"furniture",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["e",.29,.31,.15,.15],["e",.71,.31,.15,.15],["e",.29,.71,.15,.15],["e",.71,.71,.15,.15]]},{id:"dishwasher",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["r",.1,.12,.8,.76],["e",.5,.5,.27,.27],["e",.5,.5,.13,.13]]},{id:"washer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["e",.5,.57,.14,.14]]},{id:"dryer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["p",.36,.5,.5,.64,.64,.5]]},{id:"tv",group:"appliance",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",w:90,h:25,g:[["r",0,0,1,1],["l",.05,.55,.95,.55],["l",.05,.79,.95,.79]]},{id:"water_heater",group:"appliance",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",w:90,h:90,g:[["r",0,0,1,1],["l",0,0,1,1],["l",1,0,0,1],["e",.5,.5,.08,.08]]},{id:"sink",group:"sanitary",w:60,h:45,g:[["r",0,0,1,1],["e",.5,.6,.34,.3],["e",.5,.15,.07,.07]]},{id:"kitchen_sink",group:"sanitary",w:80,h:60,g:[["r",0,0,1,1],["r",.06,.24,.44,.64],["r",.54,.24,.4,.64],["e",.5,.12,.06,.06]]},{id:"bidet",group:"sanitary",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",w:100,h:280,g:[["r",0,0,1,1],["l",0,.111,1,.111],["l",0,.222,1,.222],["l",0,.333,1,.333],["l",0,.444,1,.444],["l",0,.556,1,.556],["l",0,.667,1,.667],["l",0,.778,1,.778],["l",0,.889,1,.889],["l",.5,.93,.5,.06],["p",.38,.16,.5,.06,.62,.16]]},{id:"fireplace",group:"other",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",w:40,h:40,g:[["e",.5,.5,.22,.22],["l",.5,.28,.5,.02],["l",.5,.72,.5,.98],["l",.28,.5,.02,.5],["l",.72,.5,.98,.5],["l",.34,.34,.13,.13],["l",.66,.66,.87,.87],["l",.66,.34,.87,.13],["l",.34,.66,.13,.87]]},{id:"rug",group:"other",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],Nn=new Map(An.map(t=>[t.id,t]));function En(t){return t&&Nn.get(t)||null}function In(t){return An.filter(e=>e.group===t)}function On(t,e,i=cn,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Fn=5e-4,Ln=an;function qn(t){return Number.isFinite(t)?Math.max(Fn,Math.min(Ln,t)):Fn}const Hn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Un(t,e,i){const s=En(t);if(!(s&&e>0&&i>0))return"";const o=t=>Hn(t*e),n=t=>Hn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,h]=t;r.push(`M${o(s-l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Bn(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,h,c]=o,d=h-s,p=c-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Wn(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function jn(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Gn(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Vn(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Kn(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Vn(t.a)||!Vn(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function Yn(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Jn(t,e){return Yn(t,e).q}function Zn(t,e,i,s,o){const n=Yn(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+c*g,h+d*g]}function Xn(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt.seg)}(e,s);let n=null;for(const r of e||[]){if(!r?.id)continue;const e=Pe(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of o)if(Zi([(l[0]+h[0])/2,(l[1]+h[1])/2],t)<2*s){p=!0;break}p||(!n||dt?.id),r=Kn(e);if(r.length)return rr(r,t,i,s).map(t=>Gn(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tjn([t[0],t[1]],[t[2],t[3]],e))}function ir(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tZi(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function sr(t,e,i){let s=null;for(const o of e){const e=Zi(t,o);e<=i&&(!s||e.05)continue;const d=co(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-c[0],p[1]-c[1]);u0?l:n}(t,e,i,s,o,n);return mo(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function nr(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(Zi(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Zi([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])Zi(e,t)<=2*i&&s.push(e);return s}function rr(t,e,i,s){const o=Kn(t);if(!o.length)return[];const n=Xn(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=Gn(t,i),o=e[0],l=e[1],h=e[2]-o,c=e[3]-l,d=Math.hypot(h,c);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,h=(t[2]-o)*p+(t[3]-l)*u,c=Math.max(0,Math.min(r,h)),m=Math.min(d,Math.max(r,h));if(m-ct.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=h&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=h);c||(c={pair:t,origin:[e[0],e[1]],ux:n,uy:r,ranges:[]},l.push(c));const d=(e[0]-c.origin[0])*c.ux+(e[1]-c.origin[1])*c.uy,p=(e[2]-c.origin[0])*c.ux+(e[3]-c.origin[1])*c.uy;c.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const c=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&c.push(jn(e,o,i))}}return c}function ar(t,e,i,s){const o=Kn(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=Gn(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,h=a;for(let t=0;t{const e=Yn(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=c[0]-n[0],r=c[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(Yn([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),h=d(a);break}}n.push(jn(l,h,s))}return n}class lr{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=Gi(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{const e=Number(t);return Number.isFinite(e)?e:null};function pr(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dr(e.x)&&null!=dr(e.y)?{x:dr(e.x),y:dr(e.y),a:dr(e.a??e.angle??e.theta)}:null;let s=null;const o=t.path?.points??t.path;if(Array.isArray(o)&&o.length){s=[];for(const t of o){const e=dr(Array.isArray(t)?t[0]:t?.x),i=dr(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const n=[],r=t.rooms,a=Array.isArray(r)?r.map((t,e)=>[String(t?.id??e),t]):r&&"object"==typeof r?Object.entries(r):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim();let s=dr(e.cx??e.center?.x),o=dr(e.cy??e.center?.y);if(null==s||null==o){const t=dr(e.x0),i=dr(e.y0),n=dr(e.x1),r=dr(e.y1);null!=t&&null!=i&&null!=n&&null!=r&&(s=(t+n)/2,o=(i+r)/2)}if(null!=s&&null!=o||(s=dr(e.x),o=dr(e.y)),i&&null!=s&&null!=o){const r={id:t,name:i,cx:s,cy:o},a=dr(e.x0),l=dr(e.y0),h=dr(e.x1),c=dr(e.y1);null!=a&&null!=l&&null!=h&&null!=c&&(r.x0=Math.min(a,h),r.y0=Math.min(l,c),r.x1=Math.max(a,h),r.y1=Math.max(l,c)),n.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s?{pos:i,path:s,rooms:n,mapId:l}:null}function ur(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const _r=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function mr(t,e){const i=new Map(e.map(t=>[_r(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(_r(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,h=0,c=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,h+=m,c+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,h]=u,c=e*(n*h-r*l)-i*(o*h-r*a)+s*(o*l-n*a);if(!Number.isFinite(c)||Math.abs(c)<1e-9)return null;const d=[(n*h-r*l)/c,(s*l-i*h)/c,(i*r-s*n)/c,(r*a-o*h)/c,(e*h-s*a)/c,(s*o-e*r)/c,(o*l-n*a)/c,(i*a-e*l)/c,(e*n-i*o)/c];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,h]),g=_([c,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:cr(n,s)}:null}function gr(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,h]=t[n],c=l-r,d=h-a,p=Math.hypot(c,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function fr(t){return"cleaning"===t||"returning"===t||"on"===t}const vr={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function yr(t){const[e,i]=vr[t.rot]||[1,0],s=t.mir?-1:1;return[t.s*e*s,-t.s*i,t.ox,t.s*i*s,t.s*e,t.oy]}function br(t,e,i,s){const[o,n]=hr(yr(e),i,s),r=yr({...t,ox:0,oy:0}),[a,l]=hr(r,i,s);return{...t,ox:o-a,oy:n-l}}function wr(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const kr={availability:"available",status:"alarm",activity:"none"},$r=new Set(["motion","vibration","sound"]),xr=new Set(["occupancy","presence"]),Sr=new Set(["door","window","garage_door","opening"]),Mr=new Set(["running","power"]),Cr=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Dr=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Pr=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),zr=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Tr=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Rr=t=>String(t??"").trim().toLowerCase();function Ar(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Rr(t?.[e]);if(Dr.has(i)||Pr.has(i))return i}return""}function Nr(t,e){const i=t?.states?.[e],s=i?Rr(i.state):"__missing__",o=String(e||"").split(".")[0],n=Rr(i?.attributes?.device_class),r={eid:e,state:s,availability:Tr(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Xi.has(e))}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return $r.has(n)?{...r,edge:"rising"}:xr.has(n)?{...r,activity:"on"===s?"presence":"none"}:Sr.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:Mr.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=Ar(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Rr):[],o=!Pr.has(s)&&(zr.has(s)||Dr.has(s)||e.includes(s));return(t?Dr.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=Ar(i.attributes);return Dr.has(a)||Dr.has(s)&&!Pr.has(s)?{...r,status:"working",activity:"running"}:r}function Er(t,e){if(!(t=>!!t&&!Tr(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}function Ir(t){const e={};for(const[i,s]of Object.entries(t.entities))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}function Or(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Fr(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const Lr=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],qr=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Hr(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of Lr){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return qr(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Rr(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return $r.has(i)||xr.has(i)||Sr.has(i)||Mr.has(i)||"moving"===i||Cr.has(i)}(t,e.eid));if(n.length)return qr(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length)return qr(r).map(t=>t.eid);const a=[];for(const t of wt)a.push(...qr(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...qr(o.filter(t=>!wt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function Ur(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>Fr(t,e.eid));if(i)return i.eid}return Hr(t,e)[0]}function Wr(t,e,i=[]){return[...new Set(Br(t,e,i))].filter(t=>Fi(t))}function Br(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function jr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).some(t=>t.startsWith("switch.")&&s.has(t))}function Gr(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Vr(t,e){const i=Wr(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light||jr(e.marker?.binding,e.marker?.controls,e.entities)){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Fi),s=t.primary&&Fi(t.primary)?t.primary:null;return(e&&Fi(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||Hr(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function Kr(t,e,i){const s=[],o=new Set;for(const n of e){if(n.hidden||null!=i&&!Gr(n,i))continue;const e=Vr(t,n);for(const{eid:i,via:r}of e)i&&!o.has(i)&&(o.add(i),s.push({eid:i,device:n,via:r,on:"on"===t.states[i]?.state}))}return s}function Yr(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function Jr(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return Qe(i)}function Zr(t,e){for(const i of e){if(!Fr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function Xr(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function Qr(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ta(t,e){for(const i of e){if(!Qr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function ea(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function ia(t,e,i,s,o){const n=bt(e,i,o);if(n!==yt)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=vt[e];if(t)return t}return null}(r)??yt}function sa(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function oa(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function na(t,e,i,s){const o=Wr(e.binding,e.controls,t.entities).filter(t=>!oa(i,t,s));t.marker=e,t.controls=o,e.hidden&&(t.hidden=!0),e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function ra(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,showAll:r,firstSpaceId:a,loc:l,iconRules:h}=t,c=!1!==o.group_lights,d=sa(s),p=ea(e,c).filter(t=>!oa(e,t.eid,d)),u=new Set(p.map(t=>t.area)),_=Ir(e),m=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||m.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),f={},v=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(m.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!o.filter_seeded)continue;const d=_[t.id]||[],p=Or(e,t,d),y=!o.filter_seeded;if(y&&!r){if(n.has(p))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===p&&t.via_device_id)continue}const b=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=b+"|"+s;let k=ia(e,b,t.model,d,h);if(d.some(t=>t.startsWith("lock."))&&(k="mdi:lock"),y&&!r&&c&&"mdi:lightbulb"===k&&u.has(s))continue;f[w]=(f[w]||0)+1;const $=f[w]>1?b+" "+f[w]:b,x={id:t.id,name:$,model:t.model||"",area:s,space:i[s],icon:k,entities:d,bindingKind:"device",bindingRef:t.id,pdfs:[]};x.primary=Ur(e,d,k),"mdi:thermometer"!==k&&"mdi:air-filter"!==k||(x.temp=Zr(e,d)),x.primary&&Qr(e,x.primary)&&(x.hum=ta(e,d)),v.push(x)}for(const t of p)i[t.area]&&(m.has("entity:"+t.eid)||v.push({id:"lg_"+t.eid,name:t.name,model:l("device.light_group"),area:t.area,space:i[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of s){if(t.removed)continue;if(t.hidden&&!o.filter_seeded)continue;const[s,n]=t.binding.split(":");if("device"===s){const s=e.devices[n],o=t.area||s?.area_id||"",r=o&&i[o]||t.space||a,c=s&&_[s.id]||[];let p=s?ia(e,s.name_by_user||s.name||"",s.model,c,h):"mdi:help-circle";c.some(t=>t.startsWith("lock."))&&(p="mdi:lock");const u={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:o,space:r,icon:p,entities:c,bindingKind:"device",bindingRef:n};u.primary=Ur(e,c,p),"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Zr(e,c)),u.primary&&Qr(e,u.primary)&&(u.hum=ta(e,c)),u.primary&&Qr(e,u.primary)&&(u.hum=ta(e,c)),na(u,t,e,d),v.push(u)}else if("entity"===s){if(oa(e,n,d))continue;const s=e.entities[n],o=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=o&&i[o]||t.space||a,l=e.states[n],c=s?.name||l?.attributes?.friendly_name||n;let p=ia(e,c,"",[n],h);n.startsWith("lock.")&&(p="mdi:lock");const u={id:t.id,name:c,model:"",area:o,space:r,icon:p,entities:[n],primary:n,bindingKind:"entity",bindingRef:n};"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Zr(e,[n])),Qr(e,n)&&(u.hum=ta(e,[n])),na(u,t,e,d),v.push(u)}else{const s=t.area||"",o=t.space||s&&i[s]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:s,space:o,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};na(n,t,e,d),v.push(n)}}return v}function aa(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=sa(i);return"device"===o?r.devices.has(n):"entity"===o&&oa(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?Zr(t,e):ta(t,e)}return null}const la=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");var ha={"btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","btn.no_area":"No area","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden devices (ghosted, this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_closewall":"Physical wall: click a dashed virtual stretch","title.no_area_room":"Decorative room without an HA area (e.g. a hallway)","title.choose_area":"Select a Home Assistant area","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.closewall":"Physical wall","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary stretch","history.close_boundary":"Close boundary opening","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit door or window opening","history.move_opening":"Move door or window opening","history.delete_opening":"Delete door or window opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Doors & windows: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Door / window","opening.door":"Door","opening.window":"Window","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon","display.icon_ripple":"Icon + activity","marker.display_hint":"A yellow plate means actual work. Activity adds events, presence and movement; alarms are always visible.","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide doors and windows","space.hide_openings_tip":"The symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value instead of an icon","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Show hidden","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors and windows, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","fill.glow":"Light sources (dark house, glowing lamps)","gs.glow_group":"Light-sources fill","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius (light-sources fill)","marker.glow_radius_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.openwall":"Virtual wall","title.markup_openwall":"Virtual wall — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Crosshair cursor.","toast.openwall_pick":"Click a wall shared by two rooms","toast.openwall_shared_only":"Only a shared wall between two rooms can be opened","toast.openwall_short":"Stretch too short — pick a farther point","toast.openwall_opened_span":"Virtual stretch opened","toast.openwall_closed_span":"Virtual stretch closed","toast.closewall_pick":"Click a dashed virtual stretch","toast.closewall_use_tool":"Use “Physical wall” for this stretch","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Makes the icon glow in the “Light sources” fill even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Show hidden" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.status_found":"Position source found: {name}","vac.status_none":"The integration reports no coordinates — the robot will only be shown at its base","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_res_warn":"Matched {rooms} rooms but the fit is rough — verify and refine via “Fit manually” if needed","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the sun (day/night)","gs.bg_daynight_hint":"The stage follows the sun: neutral day, warm golden hour, deep night. Needs the compass below.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the sun features stay off.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Point the arrow at north (1° steps, 15° with Shift) or type the degrees — until then the sun features stay off.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.weather":"Weather entity","gs.weather_ph":"weather.home (optional)","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug"};const ca={en:ha,ru:{"btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","btn.no_area":"Без зоны","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые устройства (полупрозрачными, только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_closewall":"Физическая стена: кликните по пунктирному виртуальному участку","title.no_area_room":"Декоративная комната без привязки к зоне (например, холл)","title.choose_area":"Выберите зону Home Assistant","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.closewall":"Физическая стена","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие участка границы","history.close_boundary":"Закрытие проёма в границе","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение дверного или оконного проёма","history.move_opening":"Перемещение дверного или оконного проёма","history.delete_opening":"Удаление дверного или оконного проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Двери и окна: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Дверь / окно","opening.door":"Дверь","opening.window":"Окно","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок","display.icon_ripple":"Значок + активность","marker.display_hint":"Жёлтая подложка означает фактическую работу. Активность добавляет события, присутствие и движение; тревога видна всегда.","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить (свет/розетки)","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери и окна не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Показать скрытые","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери и окна, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","fill.glow":"Свет по источникам (тёмный дом, пятна света)","gs.glow_group":"Заливка «Свет по источникам»","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения (заливка «Свет по источникам»)","marker.glow_radius_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.openwall":"Виртуальная стена","title.markup_openwall":"Виртуальная стена — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает виртуальный участок. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","toast.closewall_pick":"Кликните по пунктирному виртуальному участку","toast.closewall_use_tool":"Для этого участка выберите «Физическая стена»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Использовать датчик температуры устройства","marker.use_climate_temp_tip":"Кондиционеры и термостаты знают температуру комнаты (current_temperature). Показывает её плашкой у значка и учитывает в средней температуре комнаты — как обычный термометр.","marker.is_light":"Это устройство — источник света","marker.is_light_tip":"Даёт ореол в заливке «Свет по источникам» даже без light-сущности — для умного выключателя с обычными светильниками. Ореол следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Показать скрытые» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.status_found":"Источник координат найден: {name}","vac.status_none":"Интеграция не отдаёт координаты — робот будет показан только на базе","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_res_warn":"Совпало комнат: {rooms}, но привязка грубовата — проверьте и при необходимости откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за солнцем (день/ночь)","gs.bg_daynight_hint":"Фон следует за солнцем: нейтральный день, тёплый золотой час, глубокая ночь. Нужен компас ниже.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — солнечные функции выключены.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Направьте стрелку на север (шаг 1°, с Shift 15°) или введите градусы — до этого солнечные функции молчат.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.weather":"Сущность погоды","gs.weather_ph":"weather.home (необязательно)","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр"}};function da(t,e){if(e&&e in ca)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function pa(t,e,i){return wi(ca[t][e]??ha[e]??e,i)}class ua extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return da(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[],e=this._lang;return[{name:"title",selector:{text:{}}},t.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:t}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:[{value:"",label:pa(e,"editor.lang_auto")},{value:"en",label:pa(e,"editor.lang_en")},{value:"ru",label:pa(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:pa(t,"editor.title"),default_floor:pa(t,"editor.default_floor"),language:pa(t,"editor.language"),icon_size:pa(t,"editor.icon_size"),show_temperature:pa(t,"editor.show_temperature"),live_states:pa(t,"editor.live_states"),show_signal:pa(t,"editor.show_signal"),kiosk:pa(t,"editor.kiosk"),cycle:pa(t,"editor.cycle")};return W`0||t===e?e:e-1}function Nt(t){for(var e,i,s=1,o=t.length,n=t[0]+"";sh^i?1:-1;for(a=(l=o.length)<(h=n.length)?l:h,r=0;rn[r]^i?1:-1;return l==h?0:l>h^i?1:-1}function It(t,e,i,s){if(ti||t!==xt(t))throw Error(St+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function Ot(t){var e=t.c.length-1;return At(t.e/Dt)==e&&t.c[e]%2!=0}function Ft(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function Lt(t,e,i){var s,o;if(e<0){for(o=i+".";++e;o+=i);t=o+t}else if(++e>(s=t.length)){for(o=i,e-=s;--e;o+=i);t+=o}else ey?p.c=p.e=null:t.e=10;l/=10,a++);return void(a>y?p.c=p.e=null:(p.e=a,p.c=[t]))}d=String(t)}else{if(!$t.test(d=String(t)))return o(p,d,h);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(a=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(a<0&&(a=l),a+=+d.slice(l+1),d=d.substring(0,l)):a<0&&(a=d.length)}else{if(It(e,2,x.length,"Base"),10==e&&S)return z(p=new M(t),_+p.e+1,m);if(d=String(t),h="number"==typeof t){if(0*t!=0)return o(p,d,h,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(Mt+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=x.slice(0,e),a=l=0,c=d.length;la){a=c;continue}}else if(!r&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){r=!0,l=-1,a=0;continue}return o(p,String(t),h,e)}h=!1,(a=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):a=d.length}for(l=0;48===d.charCodeAt(l);l++);for(c=d.length;48===d.charCodeAt(--c););if(d=d.slice(l,++c)){if(c-=l,h&&M.DEBUG&&c>15&&(t>Pt||t!==xt(t)))throw Error(Mt+p.s*t);if((a=a-l-1)>y)p.c=p.e=null;else if(a=f)?Ft(l,r):Lt(l,r,"0");else if(n=(t=z(new M(t),e,i)).e,a=(l=Nt(t.c)).length,1==s||2==s&&(e<=n||n<=g)){for(;ar),l=Lt(l,n,"0"),n+1>a){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=n-a)>0)for(n+1==a&&(l+=".");e--;l+="0");return t.s<0&&o?"-"+l:l}function D(t,e){for(var i,s,o=1,n=new M(t[0]);o=10;o/=10,s++);return(i=s+i*Dt-1)>y?t.c=t.e=null:i=10;a/=10,o++);if((n=e-o)<0)n+=Dt,r=e,l=d[h=0],c=xt(l/p[o-r-1]%10);else if((h=kt((n+1)/Dt))>=d.length){if(!s)break t;for(;d.length<=h;d.push(0));l=c=0,o=1,r=(n%=Dt)-Dt+1}else{for(l=a=d[h],o=1;a>=10;a/=10,o++);c=(r=(n%=Dt)-Dt+o)<0?0:xt(l/p[o-r-1]%10)}if(s=s||e<0||null!=d[h+1]||(r<0?l:l%p[o-r-1]),s=i<4?(c||s)&&(0==i||i==(t.s<0?3:2)):c>5||5==c&&(4==i||s||6==i&&(n>0?r>0?l/p[o-r]:0:d[h-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(Dt-e%Dt)%Dt],t.e=-e||0):d[0]=t.e=0,t;if(0==n?(d.length=h,a=1,h--):(d.length=h+1,a=p[Dt-n],d[h]=r>0?xt(l/p[o-r]%p[r])*a:0),s)for(;;){if(0==h){for(n=1,r=d[0];r>=10;r/=10,n++);for(r=d[0]+=a,a=1;r>=10;r/=10,a++);n!=a&&(t.e++,d[0]==Ct&&(d[0]=1));break}if(d[h]+=a,d[h]!=Ct)break;d[h--]=0,a=1}for(n=d.length;0===d[--n];d.pop());}t.e>y?t.c=t.e=null:t.e=f?Ft(e,i):Lt(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(St+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(It(i=t[e],0,Rt,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(It(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(It(i[0],-Rt,0,e),It(i[1],0,Rt,e),g=i[0],f=i[1]):(It(i,-Rt,Rt,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)It(i[0],-Rt,-1,e),It(i[1],1,Rt,e),v=i[0],y=i[1];else{if(It(i,-Rt,Rt,e),!i)throw Error(St+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(St+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(St+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(It(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(It(i=t[e],0,Rt,e),$=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(St+e+" not an object: "+i);k=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(St+e+" invalid: "+i);S="0123456789"==i.slice(0,10),x=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:$,FORMAT:k,ALPHABET:x}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,o=t.e,n=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===n||-1===n)&&o>=-Rt&&o<=Rt&&o===xt(o)){if(0===s[0]){if(0===o&&1===s.length)return!0;break t}if((e=(o+1)%Dt)<1&&(e+=Dt),String(s[0]).length==e){for(e=0;e=Ct||i!==xt(i))break t;if(0!==i)return!0}}}else if(null===s&&null===o&&(null===n||1===n||-1===n))return!0;throw Error(St+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(n=9007199254740992,r=Math.random()*n&2097151?function(){return xt(Math.random()*n)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,o,n,a=0,l=[],h=new M(u);if(null==t?t=_:It(t,0,Rt),o=kt(t/Dt),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(o*=2));a>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[a]=i[0],e[a+1]=i[1]):(l.push(n%1e14),a+=2);a=o/2}else{if(!crypto.randomBytes)throw b=!1,Error(St+"crypto unavailable");for(e=crypto.randomBytes(o*=7);a=9e15?crypto.randomBytes(7).copy(e,a):(l.push(n%1e14),a+=7);a=o/7}if(!b)for(;a=10;n/=10,a++);ai-1&&(null==r[o+1]&&(r[o+1]=0),r[o+1]+=r[o]/i|0,r[o]%=i)}return r.reverse()}return function(s,o,n,r,a){var l,h,c,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=$,$=0,s=s.replace(".",""),u=(f=new M(o)).pow(s.length-v),$=d,f.c=e(Lt(Nt(u.c),u.e,"0"),10,n,t),f.e=f.c.length),c=d=(g=e(s,o,n,a?(l=x,t):(l=t,x))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--c:(u.c=g,u.e=c,u.s=r,g=(u=i(u,f,y,b,n)).c,p=u.r,c=u.e),v=g[h=c+y+1],d=n/2,p=p||h<0||null!=g[h+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[h-1]||b==(u.s<0?8:7)),h<1||!g[0])s=p?Lt(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=h,p)for(--n;++g[--h]>n;)g[h]=0,h||(++c,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=Lt(s,c,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,o,n,r,a=0,l=t.length,h=e%Tt,c=e/Tt|0;for(t=t.slice();l--;)a=((o=h*(n=t[l]%Tt)+(s=c*n+(r=t[l]/Tt|0)*h)%Tt*Tt+a)/i|0)+(s/Tt|0)+c*r,t[l]=o%i;return a&&(t=[a].concat(t)),t}function e(t,e,i,s){var o,n;if(i!=s)n=i>s?1:-1;else for(o=n=0;oe[o]?1:-1;break}return n}function i(t,e,i,s){for(var o=0;i--;)t[i]-=o,o=t[i]1;t.splice(0,1));}return function(s,o,n,r,a){var l,h,c,d,p,u,_,m,g,f,v,y,b,w,$,k,x,S=s.s==o.s?1:-1,C=s.c,D=o.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&o.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=n+(h=s.e-o.e)+1,a||(a=Ct,h=At(s.e/Dt)-At(o.e/Dt),S=S/Dt|0),c=0;D[c]==(C[c]||0);c++);if(D[c]>(C[c]||0)&&h--,S<0)g.push(1),d=!0;else{for(w=C.length,k=D.length,c=0,S+=2,(p=xt(a/(D[0]+1)))>1&&(D=t(D,p,a),C=t(C,p,a),k=D.length,w=C.length),b=k,v=(f=C.slice(0,k)).length;v=a/2&&$++;do{if(p=0,(l=e(D,f,k,v))<0){if(y=f[0],k!=v&&(y=y*a+(f[1]||0)),(p=xt(y/$))>1)for(p>=a&&(p=a-1),_=(u=t(D,p,a)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,k<_?x:D,_,a),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,c++);z(m,n+(m.e=c+h*Dt-1)+1,r,d)}else m.e=h,m.r=+d;return m}}(),a=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,h=/^\.([^.]+)$/,c=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,o=function(t,e,i,s){var o,n=i?e:e.replace(d,"");if(c.test(n))t.s=isNaN(n)?null:n<0?-1:1;else{if(!i&&(n=n.replace(a,function(t,e,i){return o="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=o?t:e}),s&&(o=s,n=n.replace(l,"$1").replace(h,"0.$1")),e!=n))return new M(n,o);if(M.DEBUG)throw Error(St+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return Et(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,o,n=this;if(null!=t)return It(t,0,Rt),null==e?e=m:It(e,0,8),z(new M(n),t+n.e+1,e);if(!(i=n.c))return null;if(s=((o=i.length-1)-At(this.e/Dt))*Dt,o=i[o])for(;o%10==0;o/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,o,n,r,a,l,h,c=this;if((t=new M(t)).c&&!t.isInteger())throw Error(St+"Exponent not an integer: "+T(t));if(null!=e&&(e=new M(e)),r=t.e>14,!c.c||!c.c[0]||1==c.c[0]&&!c.e&&1==c.c.length||!t.c||!t.c[0])return h=new M(Math.pow(+T(c),r?t.s*(2-Ot(t)):+T(t))),e?h.mod(e):h;if(a=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!a&&c.isInteger()&&e.isInteger())&&(c=c.mod(e))}else{if(t.e>9&&(c.e>0||c.e<-1||(0==c.e?c.c[0]>1||r&&c.c[1]>=24e7:c.c[0]<8e13||r&&c.c[0]<=9999975e7)))return n=c.s<0&&Ot(t)?-0:0,c.e>-1&&(n=1/n),new M(a?1/n:n);$&&(n=kt($/Dt+2))}for(r?(i=new M(.5),a&&(t.s=1),l=Ot(t)):l=(o=Math.abs(+T(t)))%2,h=new M(u);;){if(l){if(!(h=h.times(c)).c)break;n?h.c.length>n&&(h.c.length=n):s&&(h=h.mod(e))}if(o){if(0===(o=xt(o/2)))break;l=o%2}else if(z(t=t.times(i),t.e+1,1),t.e>14)l=Ot(t);else{if(0===(o=+T(t)))break;l=o%2}c=c.times(c),n?c.c&&c.c.length>n&&(c.c.length=n):s&&(c=c.mod(e))}return s?h:(a&&(h=u.div(h)),e?h.mod(e):n?z(h,$,m,void 0):h)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:It(t,0,8),z(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===Et(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return Et(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=Et(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&At(this.e/Dt)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return Et(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=Et(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,o,n,r=this,a=r.s;if(e=(t=new M(t,e)).s,!a||!e)return new M(NaN);if(a!=e)return t.s=-e,r.plus(t);var l=r.e/Dt,h=t.e/Dt,c=r.c,d=t.c;if(!l||!h){if(!c||!d)return c?(t.s=-e,t):new M(d?r:NaN);if(!c[0]||!d[0])return d[0]?(t.s=-e,t):new M(c[0]?r:3==m?-0:0)}if(l=At(l),h=At(h),c=c.slice(),a=l-h){for((n=a<0)?(a=-a,o=c):(h=l,o=d),o.reverse(),e=a;e--;o.push(0));o.reverse()}else for(s=(n=(a=c.length)<(e=d.length))?a:e,a=e=0;e0)for(;e--;c[i++]=0);for(e=Ct-1;s>a;){if(c[--s]=0;){for(i=0,p=y[o]%g,u=y[o]/g|0,n=o+(r=l);n>o;)i=((h=p*(h=v[--r]%g)+(a=u*h+(c=v[r]/g|0)*p)%g*g+_[n]+i)/m|0)+(a/g|0)+u*c,_[n--]=h%m;_[n]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,o=s.s;if(e=(t=new M(t,e)).s,!o||!e)return new M(NaN);if(o!=e)return t.s=-e,s.minus(t);var n=s.e/Dt,r=t.e/Dt,a=s.c,l=t.c;if(!n||!r){if(!a||!l)return new M(o/0);if(!a[0]||!l[0])return l[0]?t:new M(a[0]?s:0*o)}if(n=At(n),r=At(r),a=a.slice(),o=n-r){for(o>0?(r=n,i=l):(o=-o,i=a),i.reverse();o--;i.push(0));i.reverse()}for((o=a.length)-(e=l.length)<0&&(i=l,l=a,a=i,e=o),o=0;e;)o=(a[--e]=a[e]+l[e]+o)/Ct|0,a[e]=Ct===a[e]?0:a[e]%Ct;return o&&(a=[o].concat(a),++r),P(t,a,r)},p.precision=p.sd=function(t,e){var i,s,o,n=this;if(null!=t&&t!==!!t)return It(t,1,Rt),null==e?e=m:It(e,0,8),z(new M(n),t,e);if(!(i=n.c))return null;if(s=(o=i.length-1)*Dt+1,o=i[o]){for(;o%10==0;o/=10,s--);for(o=i[0];o>=10;o/=10,s++);}return t&&n.e+1>s&&(s=n.e+1),s},p.shiftedBy=function(t){return It(t,-9007199254740991,Pt),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,o,n,r=this,a=r.c,l=r.s,h=r.e,c=_+4,d=new M("0.5");if(1!==l||!a||!a[0])return new M(!l||l<0&&(!a||a[0])?NaN:a?r:1/0);if(0==(l=Math.sqrt(+T(r)))||l==1/0?(((e=Nt(a)).length+h)%2==0&&(e+="0"),l=Math.sqrt(+e),h=At((h+1)/2)-(h<0||h%2),s=new M(e=l==1/0?"5e"+h:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+h)):s=new M(l+""),s.c[0])for((l=(h=s.e)+c)<3&&(l=0);;)if(n=s,s=d.times(n.plus(i(r,n,c,1))),Nt(n.c).slice(0,l)===(e=Nt(s.c)).slice(0,l)){if(s.e0&&_>0){for(n=_%a||a,c=u.substr(0,n);n<_;n+=a)c+=h+u.substr(n,a);l>0&&(c+=h+u.slice(n)),p&&(c="-"+c)}s=d?c+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):c}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,o,n,r,a,l,h,c,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(St+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+T(l));if(!f)return new M(g);for(e=new M(u),c=s=new M(u),o=h=new M(u),_=Nt(f),r=e.e=_.length-g.e-1,e.c[0]=zt[(a=r%Dt)<0?Dt+a:a],t=!t||l.comparedTo(e)>0?r>0?e:c:l,a=y,y=1/0,l=new M(_),h.c[0]=0;d=i(l,e,0,1),1!=(n=s.plus(d.times(o))).comparedTo(t);)s=o,o=n,c=h.plus(d.times(n=c)),h=n,e=l.minus(d.times(n=e)),l=n;return n=i(t.minus(s),o,0,1),h=h.plus(n.times(c)),s=s.plus(n.times(o)),h.s=c.s=g.s,p=i(c,o,r*=2,m).minus(g).abs().comparedTo(i(h,s,r,m).minus(g).abs())<1?[c,o]:[h,s],y=a,p},p.toNumber=function(){return+T(this)},p.toPrecision=function(t,e){return null!=t&&It(t,1,Rt),C(this,t,e,2)},p.toString=function(t){var e,i=this,o=i.s,n=i.e;return null===n?o?(e="Infinity",o<0&&(e="-"+e)):e="NaN":(null==t?e=n<=g||n>=f?Ft(Nt(i.c),n):Lt(Nt(i.c),n,"0"):10===t&&S?e=Lt(Nt((i=z(new M(i),_+n+1,m)).c),i.e,"0"):(It(t,2,x.length,"Base"),e=s(Lt(Nt(i.c),n,"0"),10,t,o,!0)),o<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return T(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),Ht=class{key;left=null;right=null;constructor(t){this.key=t}},Ut=class extends Ht{constructor(t){super(t)}},Wt=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,o=null,n=null,r=e;const a=this.compare;let l;for(;;)if(l=a(r.key,t),l>0){let e=r.left;if(null==e)break;if(l=a(e.key,t),l>0&&(r.left=e.right,e.right=r,r=e,e=r.left,null==e))break;null==i?s=r:i.left=r,i=r,r=e}else{if(!(l<0))break;{let e=r.right;if(null==e)break;if(l=a(e.key,t),l<0&&(r.right=e.left,e.left=r,r=e,e=r.right,null==e))break;null==o?n=r:o.right=r,o=r,r=e}}return null!=o&&(o.right=r.left,r.left=n),null!=i&&(i.left=r.right,r.right=s),this.root!==r&&(this.root=r,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Bt=class t extends Wt{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new Ut(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new Ut(t.key);return function t(e,i){let s,o;do{if(s=e.left,o=e.right,null!=s){const e=new Ut(s.key);i.left=e,t(s,e)}if(null!=o){const t=new Ut(o.key);i.right=t,e=o,i=t}}while(null!=o)}(t,e),e}toSet(){return this.clone()}entries(){return new Vt(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Gt(this.wrap())}[Symbol.toStringTag]="[object Set]"},jt=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Gt=class extends jt{getValue(t){return t.key}},Vt=class extends jt{getValue(t){return[t.key,t.key]}},Kt=t=>()=>t,Yt=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Kt(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Jt(t){const e=t?(e,i,s,o,n)=>e.exponentiatedBy(2).isLessThanOrEqualTo(o.minus(i).exponentiatedBy(2).plus(n.minus(s).exponentiatedBy(2)).times(t)):Kt(!1);return(t,i,s)=>{const o=t.x,n=t.y,r=s.x,a=s.y,l=n.minus(a).times(i.x.minus(r)).minus(o.minus(r).times(i.y.minus(a)));return e(l,o,n,r,a)?0:l.comparedTo(0)}}var Zt=t=>t,Xt=t=>{if(t){const e=new Bt(Yt(t)),i=new Bt(Yt(t)),s=(t,e)=>e.addAndReturn(t),o=t=>({x:s(t.x,e),y:s(t.y,i)});return o({x:new qt(0),y:new qt(0)}),o}return Zt},Qt=t=>({set:t=>{te=Qt(t)},reset:()=>Qt(t),compare:Yt(t),snap:Xt(t),orient:Jt(t)}),te=Qt(),ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),ie=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},se=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),oe=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),ne=t=>oe(t,t).sqrt(),re=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return se(o,s).div(ne(o)).div(ne(s))},ae=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},o={x:i.x.minus(t.x),y:i.y.minus(t.y)};return oe(o,s).div(ne(o)).div(ne(s))},le=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},he=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},ce=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:ve.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:re(this.point,t.point,s.point),cosine:ae(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:o,cosine:n}=e.get(t),{sine:r,cosine:a}=e.get(s);return o.isGreaterThanOrEqualTo(0)&&r.isGreaterThanOrEqualTo(0)?n.isLessThan(a)?1:n.isGreaterThan(a)?-1:0:o.isLessThan(0)&&r.isLessThan(0)?n.isLessThan(a)?-1:n.isGreaterThan(a)?1:0:r.isLessThan(o)?-1:r.isGreaterThan(o)?1:0}}},de=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,o=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},pe=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&o){const t=s.getIntersection(o);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(r.isLessThan(a)&&r.isLessThan(h))return-1;if(r.isGreaterThan(a)&&r.isGreaterThan(h))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(r.isLessThan(a))return-1;if(r.isGreaterThan(a))return 1;if(o.isLessThan(n)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(o.isGreaterThan(n)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!o.eq(n)){const t=l.minus(r),e=o.minus(i),c=h.minus(a),d=n.minus(s);if(t.isGreaterThan(e)&&c.isLessThan(d))return 1;if(t.isLessThan(e)&&c.isGreaterThan(d))return-1}return o.isGreaterThan(n)?1:o.isLessThan(n)||l.isLessThan(h)?-1:l.isGreaterThan(h)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++fe,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let o,n,r;const a=ce.comparePoints(e,i);if(a<0)o=e,n=i,r=1;else{if(!(a>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);o=i,n=e,r=-1}const l=new ce(o,!0),h=new ce(n,!1);return new t(l,h,[s],[r])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return te.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=ie(e,i);if(null===s)return null;const o=this.leftSE.point,n=this.rightSE.point,r=t.leftSE.point,a=t.rightSE.point,l=ee(e,r)&&0===this.comparePoint(r),h=ee(i,o)&&0===t.comparePoint(o),c=ee(e,a)&&0===this.comparePoint(a),d=ee(i,n)&&0===t.comparePoint(n);if(h&&l)return d&&!c?n:!d&&c?a:null;if(h)return c&&o.x.eq(a.x)&&o.y.eq(a.y)?null:o;if(l)return d&&n.x.eq(r.x)&&n.y.eq(r.y)?null:r;if(d&&c)return null;if(d)return n;if(c)return a;const p=((t,e,i,s)=>{if(e.x.isZero())return he(i,s,t.x);if(s.x.isZero())return he(t,e,i.x);if(e.y.isZero())return le(i,s,t.y);if(s.y.isZero())return le(t,e,i.y);const o=se(e,s);if(o.isZero())return null;const n={x:i.x.minus(t.x),y:i.y.minus(t.y)},r=se(n,e).div(o),a=se(n,s).div(o),l=t.x.plus(a.times(e.x)),h=i.x.plus(r.times(s.x)),c=t.y.plus(a.times(e.y)),d=i.y.plus(r.times(s.y));return{x:l.plus(h).div(2),y:c.plus(d).div(2)}})(o,this.vector(),r,t.vector());return null===p?null:ee(s,p)?te.snap(p):null}split(e){const i=[],s=void 0!==e.events,o=new ce(e,!0),n=new ce(e,!1),r=this.rightSE;this.replaceRightSE(n),i.push(n),i.push(o);const a=new t(o,r,this.rings.slice(),this.windings.slice());return ce.comparePoints(a.leftSE.point,a.rightSE.point)>0&&a.swapEvents(),ce.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(o.checkForConsuming(),n.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ye=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=te.snap({x:new qt(t[0][0]),y:new qt(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let o=s;for(let e=1,i=t.length;ege.run("union",t,e),ke=(t,...e)=>ge.run("intersection",t,e),xe=(t,...e)=>ge.run("difference",t,e);function Se(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function Me(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function Ce(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function De(t,e,i=1){const s=t[0].toFixed(i),o=t[1].toFixed(i),n=e[0].toFixed(i),r=e[1].toFixed(i),a=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function ze(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function Te(t){const e=[],i=new Set;for(const s of t||[]){const t=Pe(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),n=Math.min(Math.max(s.length||0,0)/2,i/2);let a=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(a=i/2),a=Math.max(n,Math.min(i-n,a));const l=a/i;o={x:e+l*h,y:r+l*c,angle:t}}else o={x:u[0],y:u[1],angle:t}}}return o}function Ae(t,e,i,s,o,n=1){const r=e*Math.PI/180,a=[Math.cos(r),Math.sin(r)];let l=null,h=n;for(const e of Te(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(a[0]*(e[1]-t[1])-a[1]*(e[0]-t[0]));if(s(i[0])>n||s(i[1])>n)continue;const o=(i[0][0]-t[0])*a[0]+(i[0][1]-t[1])*a[1],r=(i[1][0]-t[0])*a[0]+(i[1][1]-t[1])*a[1],c=Math.min(o,r),d=Math.max(o,r),p=c>0?c:d<0?-d:0;p[t[0]+a[0]*e,t[1]+a[1]*e],g=(c+d)/2;return{wallA:m(c),wallB:m(d),sideA:u,sideB:_,midA:m((c-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=o}}function Ne(t,e,i=!1){if(null==e||"unavailable"===e||"unknown"===e)return"door"===t?1:0;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function Ee(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}function Oe(t,e,i){const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;let r=n?((t[0]-e[0])*s+(t[1]-e[1])*o)/n:0;return r=Math.max(0,Math.min(1,r)),Math.hypot(t[0]-(e[0]+r*s),t[1]-(e[1]+r*o))}function Fe(t,e){if(!e||e.length<2)return null;let i=null,s=1/0;for(let o=0;oo&&r<-o||n<-o&&r>o)&&(a>o&&l<-o||a<-o&&l>o)}function We(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1;let h=0,c=0,d=0;for(let e=0;e1e-9?[c/(3*h),d/(3*h)]:[(o+n)/2,(r+a)/2],u=(e,i)=>{const s=((e,i)=>{if(!Ie([e,i],t))return-1/0;let s=1/0;for(let o=0;om&&(m=h,_=[s,l])}if(_){const[t,i]=_,s=(n-o)/e,l=(a-r)/e;for(let e=-4;e<=4;e++)for(let o=-4;o<=4;o++){const n=t+s*e/4,r=i+l*o/4,a=u(n,r);a>m&&(m=a,_=[n,r])}}return _||Be(t)||t[0]}function Be(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(qe(s,t,e))return s;for(let s=0;sGe(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ge(e,t,i)))}function Ye(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ze(t,e,i){for(let s=0;s1&&Ee(i[0],i[i.length-1],e)&&i.pop(),i}function Qe(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function ti(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function ei(t,e,i,s){if(t.length<2)return;const o=e.x+s,n=e.x+e.w-s,r=e.y+s,a=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;st.startsWith("cover."))||null}const di=["automation","script","scene"];const pi="—",ui="{}";function _i(t){const e=String(t??"").trim();if(!e)return null;let i=e,s="";const o=e.indexOf(":");if(o>=0)i=e.slice(0,o).trim(),s=e.slice(o+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?o>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function mi(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),o=_i(s?`${i}:${s}`:i);return o?`{${o.entity}${o.attr?`:${o.attr}`:""}}`:""}function gi(t,e,i){const s=String(e??"").trim();if(!s)return null;const o=t?.states?.[s];if(!o)return null;const n=String(i??"").trim(),r=t=>t.slice(0,60);if(n){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(o.attributes?.[n]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,o,n);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const a=o.state;if(null==a||""===a)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,o);if("string"==typeof e&&""!==e)return{text:r(e),formatted:!0}}catch{}return{text:r(String(a)),formatted:!1}}function fi(t,e,i){const s=String(e??"").trim(),o=String(i??"").trim()||s;if(!o)return t.text;const n=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${n} ${o}`}function vi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],o=s?.state;if(!s||null==o||""===o||"unavailable"===o||"unknown"===o)return pi;const n=(e?.attr||"").trim(),r=gi(t,i,n||null);if(null===r)return pi;return fi(r,n?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}const yi=20;function bi(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function wi(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const $i="#55606c",ki=.55;function xi(t){const e=t?.settings||{},i=!t?.plan_url;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:"string"==typeof e.room_color&&/^#[0-9a-f]{6}$/i.test(e.room_color)?e.room_color:$i,opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):ki,fill:["lqi","light","temp","glow"].includes(e.fill_mode)?e.fill_mode:"none",tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:"string"==typeof e.bg_color&&/^#[0-9a-f]{6}$/i.test(e.bg_color)?e.bg_color:null,hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function Si(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return"string"==typeof i&&/^#[0-9a-f]{6}$/i.test(i)?i:""}const Mi={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}},Ci=/^#[0-9a-f]{6}$/i;function Di(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(Mi)){const s=Mi[t],o=i[t];e[t]={c:o&&"string"==typeof o.c&&Ci.test(o.c)?o.c:s.c,a:o&&"number"==typeof o.a?Math.min(1,Math.max(0,o.a)):s.a}}return e}function Pi(t,e,i){const s=Math.min(1,Math.max(0,i)),o=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),n=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),r=o.map((t,e)=>Math.round(t+(n[e]-t)*s));return"#"+r.map(t=>t.toString(16).padStart(2,"0")).join("")}function zi(t,e,i,s,o,n,r){if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:Pi(r.lqi_low.c,r.lqi_high.c,t),a:r.lqi_low.a+(r.lqi_high.a-r.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?r.light_none.a>0?r.light_none:null:"on"===i?r.light_on:r.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(o,n),e=Math.max(o,n);return se?r.temp_hot:r.temp_ok}return null}const Ti={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},Ri=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function Ai(t){for(const e of[...Object.values(Ti),...Ri])if(t===e[0]||t===e[1])return e;return null}function Ni(t,e,i,s,o){if(!s||"unavailable"===s||"unknown"===s)return t;if(o){const i="cover"===e?Ai(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=Ti[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const o=Ai(t);return o?"closed"===s?o[0]:o[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function Ei(t,e){if(!t||"on"!==t.state)return null;const i=t.attributes||{},s=Number(i.brightness),o=Number.isFinite(s)&&s>0?Math.max(.15,Math.min(1,s/255)):1,n=i.rgb_color;if(Array.isArray(n)&&n.length>=3&&n.every(t=>Number.isFinite(t)))return{c:`rgb(${n[0]}, ${n[1]}, ${n[2]})`,bri:o};const r=Number(i.color_temp_kelvin)||(Number(i.color_temp)>0?1e6/Number(i.color_temp):NaN);if(Number.isFinite(r)&&r>0){const[t,e,i]=function(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),o=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,n=t=>Math.round(Math.min(255,Math.max(0,t)));return[n(i),n(s),n(o)]}(r);return{c:`rgb(${t}, ${e}, ${i})`,bri:o}}return{c:e,bri:o}}function Ii(t,e,i,s,o=170,n=0){const r=Math.hypot(e[0]-t[0],e[1]-t[1]),a=Math.hypot(i[0]-t[0],i[1]-t[1]);if(r<1e-6||a<1e-6||Math.min(r,a)>=s)return null;let l,h;if(Number.isFinite(n)&&n>1e-6){const s=i[0]-e[0],o=i[1]-e[1],r=Math.hypot(s,o);if(r<1e-6)return null;const a=-(o/r),c=s/r,d=(e[0]+i[0])/2,p=(e[1]+i[1])/2,u=(t[0]-d)*a+(t[1]-p)*c>=0?1:-1,_=n/2,m=[e[0]+a*_*u,e[1]+c*_*u],g=[i[0]+a*_*u,i[1]+c*_*u],f=[e[0]-a*_*u,e[1]-c*_*u],v=[i[0]-a*_*u,i[1]-c*_*u],y=Math.atan2(p-t[1],d-t[0]),b=e=>{let i=Math.atan2(e[1]-t[1],e[0]-t[0]);for(;i-y>Math.PI;)i-=2*Math.PI;for(;i-y<-Math.PI;)i+=2*Math.PI;return i},w=b(m),$=b(g),k=b(f),x=b(v),S=Math.max(Math.min(w,$),Math.min(k,x)),M=Math.min(Math.max(w,$),Math.max(k,x));if(!(M-S>1e-9))return null;l=S,h=M-S}else{for(l=Math.atan2(e[1]-t[1],e[0]-t[0]),h=Math.atan2(i[1]-t[1],i[0]-t[0])-l;h>Math.PI;)h-=2*Math.PI;for(;h<-Math.PI;)h+=2*Math.PI}const c=o*Math.PI/180;if(Math.abs(h)>c){const t=l+h/2;h=c*Math.sign(h),l=t-h/2}const d=[[t[0],t[1]]];for(let e=0;e<=8;e++){const i=l+h*e/8;d.push([t[0]+Math.cos(i)*s,t[1]+Math.sin(i)*s])}return d}function Oi(t,e,i,s,o){const n=e*Math.PI/180,r=[-Math.sin(n),Math.cos(n)],a=(i[0]-t[0])*r[0]+(i[1]-t[1])*r[1]>0?-1:1,l=[t[0]+r[0]*o*a,t[1]+r[1]*o*a];return s.some(t=>qe(l,t,1e-9))}function Fi(t){return t.startsWith("light.")||t.startsWith("switch.")}function Li(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let o=0;op||l>p)continue;const u=(o[0]-n[0])*c+(o[1]-n[1])*d,_=(r[0]-n[0])*c+(r[1]-n[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(h,Math.max(u,_));g-m>i&&s.push([n[0]+c*m,n[1]+d*m,n[0]+c*g,n[1]+d*g])}}return s}function qi(t,e){const i=new Set([t]),s=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);let o=!0;for(;o;){o=!1;for(const t of e)if(t.id&&!i.has(t.id))for(const n of e)if(n.id&&i.has(n.id)&&s(t,n)){i.add(t.id),o=!0;break}}return i}function Hi(t,e,i=1e-6){const s=[];for(const o of t){const t=[o[0],o[1]],n=[o[2],o[3]],r=n[0]-t[0],a=n[1]-t[1],l=Math.hypot(r,a);if(ln||o>n)continue;const r=(s[0]-t[0])*h+(s[1]-t[1])*c,a=(s[2]-t[0])*h+(s[3]-t[1])*c,p=Math.max(0,Math.min(r,a)),u=Math.min(l,Math.max(r,a));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],n[0],n[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,o]of d)e-p>i&&s.push([t[0]+h*p,t[1]+c*p,t[0]+h*e,t[1]+c*e]),p=Math.max(p,o);l-p>i&&s.push([t[0]+h*p,t[1]+c*p,n[0],n[1]])}return s}function Ui(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=Gi(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function Gi(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function Vi(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i?i:t}function Ki(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Yi(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Ji(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Zi(t,e){const i=e[2]-e[0],s=e[3]-e[1],o=i*i+s*s;if(!o)return Math.hypot(t[0]-e[0],t[1]-e[1]);let n=((t[0]-e[0])*i+(t[1]-e[1])*s)/o;return n=Math.max(0,Math.min(1,n)),Math.hypot(t[0]-(e[0]+n*i),t[1]-(e[1]+n*s))}const Xi=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Qi=(t,e)=>[t[0]-e[0],t[1]-e[1]],ts=(t,e)=>[t[0]+e[0],t[1]+e[1]],es=(t,e)=>t[0]*e[0]+t[1]*e[1],is=t=>Math.hypot(t[0],t[1]);function ss(t){let e=0;for(let i=0;it[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r)+n&&(i=!i)}return i}([a[0]+r[0]*l,a[1]+r[1]*l],t)&&(r=[-r[0],-r[1]]),r}function rs(t,e,i,s){const o=s||ns(t,e),n=(e+1)%t.length;return t.map((t,s)=>s===e||s===n?[t[0]+o[0]*i,t[1]+o[1]*i]:[...t])}function as(t,e,i,s){const o=[],n=Qi(i,e),r=is(n);if(rs||c>s)continue;const d=es(Qi(n,e),a),p=es(Qi(l,e),a),u=Math.max(0,Math.min(d,p)),_=Math.min(r,Math.max(d,p));_-u>s&&o.push([[e[0]+a[0]*u,e[1]+a[1]*u],[e[0]+a[0]*_,e[1]+a[1]*_]])}return o}function ls(t,e,i,s,o){const n=as(t,e,i,o);if(!n.length)return null;const r=t=>n.some(([e,i])=>os(t,e,i)<=o),a=t.length,l=[];for(let n=0;no||f>o)continue;const v=es(Qi(e,h),u),y=es(Qi(i,h),u),b=Math.max(0,Math.min(v,y)),w=Math.min(p,Math.max(v,y));if(!(w-b<=o)){if(b>o&&bo&&wis(Qi(i,t[(s+1)%t.length]))>e);for(let t=0;t<2;t++)i=i.filter((t,s)=>{const o=i[(s-1+i.length)%i.length],n=i[(s+1)%i.length],r=(t[0]-o[0])*(n[1]-o[1])-(t[1]-o[1])*(n[0]-o[0]),a=is(Qi(n,o))||1;return Math.abs(r)/a>e});return i.length>=3?i:t}function cs(t){const e=t.length;if(e<3)return!1;for(let i=0;i(t[0]-o[0])*a[1]-(t[1]-o[1])*a[0],h=t=>(t[0]-o[0])*a[0]+(t[1]-o[1])*a[1],c=i,d=r-i;for(const e of t){const t=Math.abs(l(e));if(t<=i)continue;const o=h(e);o<=c||o>=d||tMath.abs(r+(t-p)/g*(a-r));s=Math.min(s,f(_),f(m))}}return s}function ps(t,e,i){if(Ve(t,e,i))return!0;if(Ge(t,e,i)||Ge(e,t,i))return!1;let s=0;try{const i=ke([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]);for(const t of i)t?.[0]&&(s+=Ye(t[0]))}catch{return!1}return s>Math.max(1e-7,i*i)}function us(t,e,i,s,o){const n=[i.n[0]*s,i.n[1]*s],r={polys:{},openings:{},movedSpans:{}};if(Math.abs(s)<1e-9)return r;for(const e of t){if(e.id===i.roomId){r.polys[e.id]=rs(e.poly,i.edge,s,i.n),r.movedSpans[e.id]=[[ts(i.a,n),ts(i.b,n)]];continue}const t=as(e.poly,i.a,i.b,o);if(!t.length)continue;const a=ls(e.poly,i.a,i.b,n,o);a&&(r.polys[e.id]=a,r.movedSpans[e.id]=t.map(([t,e])=>[ts(t,n),ts(e,n)]))}for(const t of e)os([t.x,t.y],i.a,i.b)<=o&&(r.openings[t.id]=[t.x+n[0],t.y+n[1]]);return r}function _s(t,e,i){for(const s of e)for(let e=0;ei)continue;const h=(t.x-o[0])*l[0]+(t.y-o[1])*l[1];if(h-t.length/2>=-i&&h+t.length/2<=a+i)return!0}return!1}function ms(t,e,i){return t.filter(t=>e.some(e=>{for(let s=0;sa.polys[t.id]||t.poly;for(const e of l){const s=t.find(t=>t.id===e),o=a.polys[e];if(!cs(o))return!1;const l=ss(s.poly),c=ss(o);if(Math.abs(c)t.find(t=>t.id===e).poly),d=t.map(h);for(const t of ms(e,c,2*r)){const e=a.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,d,2*r))return!1}return!0}function fs(t,e,i,s,o,n){const r=t=>[s[0]+(t[0]-s[0])*o,s[1]+(t[1]-s[1])*o],a={poly:t.poly.map(r),openings:{}};for(const s of e){let e=!1;for(let i=0;i{for(let e=0;et.id===i);if(!l)return!1;const h=t.filter(t=>t.id!==i).map(t=>t.poly),c=fs(l,e,h,s,o,2*a),d=c.poly,p=function(t){const e=function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<3)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.pop(),o.pop(),s.concat(o)}(t);if(e.length<3)return 0;let i=1/0;for(let t=0;tt.id===i?d:t.poly);for(const t of ms(e,[l.poly],2*a)){const e=c.openings[t.id];if(!_s(e?{...t,x:e[0],y:e[1]}:t,u,2*a))return!1}return!0}function ys(t,e,i){const s=i/e;return Ye(t)*s*s/1e4}function bs(t,e){return e?`${Math.round(10.7639*t)} ft²`:`${(Math.round(10*t)/10).toFixed(1)} m²`}const ws=[[-90,"#070c14"],[-12,"#070c14"],[-4,"#131a28"],[0,"#4a3527"],[10,"#e8ddcf"],[30,"#ffffff"],[90,"#ffffff"]],$s=t=>Math.min(1,Math.max(0,t));function ks(t){const e=Math.min(90,Math.max(-90,Number(t)||0));let i=ws[ws.length-1][1];for(let t=1;t{const n=[t.x+o[0]*i*s,t.y+o[1]*i*s];return e.find(t=>t.poly.length>=3&&Ie(n,t.poly))||null},r=n(1),a=n(-1);return r&&a?null:r||a?r?{normal:[-o[0],-o[1]],roomId:r.id}:{normal:o,roomId:a.id}:null}function Ss(t,e,i){return i>0&&t[0]*e[0]+t[1]*e[1]>.05}function Ms(t,e,i,s){return[[t[0],t[1]],[e[0],e[1]],[e[0]+i[0]*s,e[1]+i[1]*s],[t[0]+i[0]*s,t[1]+i[1]*s]]}function Cs(t,e){try{const i=ke([[...t.map(t=>[t[0],t[1]]),[t[0][0],t[0][1]]]],[[...e.map(t=>[t[0],t[1]]),[e[0][0],e[0][1]]]]),s=[];for(const t of i){const e=t?.[0];!Array.isArray(e)||e.length<4||s.push(e.slice(0,e.length-1).map(t=>[t[0],t[1]]))}return s}catch{return[]}}function Ds(t,e,i,s,o,n,r){if(!(s>0))return[];const a=function(t,e){const i=function(t,e){return function(t){const e=t%360;return e<0?e+360:e}(t-e)}(t,e)*Math.PI/180;return[Math.sin(i),-Math.cos(i)]}(i,o),l=[-a[0],-a[1]],h=function(t){const e=Math.min(90,Math.max(0,t));return.7*(.8+1.7*Math.pow(1-e/90,1.6))}(s),c=[];for(const i of e){if(!(i.length>0))continue;const e=xs(i,t);if(!e||!Ss(e.normal,a,s))continue;const o=t.find(t=>t.id===e.roomId);if(!o)continue;const d=n&&n[e.roomId]||o.poly,p=i.angle*Math.PI/180,u=i.length/2,_=[-e.normal[0],-e.normal[1]],m=Math.max(0,r?.[i.id]||0),g=i.x+_[0]*m/2,f=i.y+_[1]*m/2,v=Math.cos(p)*u,y=Math.sin(p)*u,b=[g-v,f-y],w=[g+v,f+y],$=h*i.length,k=Cs(Ms(b,w,l,$),d);if(!k.length)continue;const x=l[0]*_[0]+l[1]*_[1];c.push({openingId:i.id,roomId:e.roomId,polys:k,a:b,b:w,dir:l,len:$,normal:_,depth:$*x})}return c}function Ps(t){return Math.round(10*(Number(t)||0))/10}const zs={clear:1,sunny:1,"clear-night":1,windy:1,exceptional:1,partlycloudy:.7,"windy-variant":.7,cloudy:.4,overcast:.25,fog:.25,rainy:0,pouring:0,snowy:0,"snowy-rainy":0,hail:0,lightning:0,"lightning-rainy":0};const Ts=t=>"number"==typeof t&&Number.isInteger(t)&&t>=0&&t<=359?t:null;function Rs(t,e){const i=Ts(e?.north_deg);return null!==i?i:Ts(t?.north_deg)}function As(t,e){const i=t=>"static"===t||"daynight"===t?t:null;return i(e?.bg_mode)??i(t?.bg_mode)??"static"}function Ns(t,e){const i=e?.sun_rays;return"boolean"==typeof i?i:!0===t?.sun_rays}function Es(t){const e=t?.weather_entity;return"string"==typeof e&&e.trim()?e.trim():null}function Is(t){const e=t?.states?.["sun.sun"]?.attributes,i=Number(e?.azimuth),s=Number(e?.elevation);return Number.isFinite(i)&&Number.isFinite(s)?{azimuth:i,elevation:s}:null}const Os={color:"#607d8b",opacity:1,widthCm:3.6,fill:!1,fillColor:"#607d8b",fillOpacity:.25},Fs=(t,e=1)=>{const i=Number(t);return Number.isFinite(i)?Math.min(1,Math.max(0,i)):e},Ls=t=>{let e=Number(t);return Number.isFinite(e)?(e=(e%360+360)%360,e>180?e-360:e):0};function qs(t,e,i){return Ls(e)?[t.x,t.y]:i([t.x,t.y])}const Hs=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&s>0?s/o*i:0},Us=(t,e,i)=>{const s=Number(t),o=Number.isFinite(e)&&e>0?e:5;return Number.isFinite(s)&&i>0?s/i*o:0},Ws=(t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return o;const n=Number(t?.width);return Number.isFinite(n)&&n>0?Us(n,e,i):s};function Bs(t,e){return{color:t.color,opacity:Fs(t.opacity),width_cm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),...e?{fill:t.fill,fill_color:t.fillColor,fill_opacity:Fs(t.fillOpacity,.25)}:{}}}function js(t){const e=t.x+t.w/2,i=t.y+t.h/2,s=Ls(t.angle)*Math.PI/180,o=Math.cos(s),n=Math.sin(s),r=(t,s)=>{const r=t-e,a=s-i;return[e+r*o-a*n,i+r*n+a*o]};return[r(t.x,t.y),r(t.x+t.w,t.y),r(t.x+t.w,t.y+t.h),r(t.x,t.y+t.h)]}function Gs(t){const e=js(t),i=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],s=[(e[0][0]+e[2][0])/2,(e[0][1]+e[2][1])/2];return{points:[...e,i(e[0],e[1]),i(e[1],e[2]),i(e[2],e[3]),i(e[3],e[0]),s],segments:e.map((t,i)=>({a:t,b:e[(i+1)%4]}))}}function Vs(t,e,i,s,o,n,r,a){const l=Ls(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l),d=-Math.sin(l),p=Math.cos(l),u=t.x+t.w/2,_=t.y+t.h/2,m=e>0?-t.w/2:t.w/2,g=i>0?-t.h/2:t.h/2,f=u+m*h+g*d,v=_+m*c+g*p,y=s-f,b=o-v;let w=(y*h+b*c)*(e>0?1:-1),$=(y*d+b*p)*(i>0?1:-1),k=null;if(n){const e=w/Math.max(t.w,a),i=$/Math.max(t.h,a);k=Math.max(a/Math.max(t.w,a),a/Math.max(t.h,a),e,i),w=t.w*k,$=t.h*k}if(r>0)if(n){const e=Math.max(1,Math.round(t.w/r)),i=Math.max(1,Math.round(t.h/r));if(Math.abs(t.w-e*r)<1e-6&&Math.abs(t.h-i*r)<1e-6){const s=(t,e)=>{let i=Math.abs(t),s=Math.abs(e);for(;s;)[i,s]=[s,i%s];return Math.max(1,i)},o=1/s(e,i),n=Math.max(a/t.w,a/t.h),r=Math.max(n,Math.round((k??1)/o)*o);w=t.w*r,$=t.h*r}else{t.w>=t.h?(w=Math.round(w/r)*r,$=w*(t.h/Math.max(t.w,a))):($=Math.round($/r)*r,w=$*(t.w/Math.max(t.h,a)))}}else w=Math.round(w/r)*r,$=Math.round($/r)*r;w=Math.max(a,w),$=Math.max(a,$);return{x:f+(e>0?w/2:-w/2)*h+(i>0?$/2:-$/2)*d-w/2,y:v+(e>0?w/2:-w/2)*c+(i>0?$/2:-$/2)*p-$/2,w:w,h:$,angle:Ls(t.angle)||void 0}}const Ks=(t,e,i)=>{const s=i[0]-e[0],o=i[1]-e[1],n=s*s+o*o;if(n<1e-12)return[...e];const r=Math.max(0,Math.min(1,((t[0]-e[0])*s+(t[1]-e[1])*o)/n));return[e[0]+s*r,e[1]+o*r]};const Ys=15;function Js(t,e){return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&t*e<3}function Zs(t){return Number.isFinite(t)?Math.max(1,Math.min(100,t)):1}function Xs(t,e){return!Number.isFinite(t)||t<=0?"":String(e?Math.round(t/2.54*100)/100:Math.round(100*t)/100)}function Qs(t,e,i){const s=Number(e)>0?Number(e):5;return Zs(t)/s*i}function to(t,e){return e>0&&Number.isFinite(t)?Math.round(t/e)*e:t}function eo(t,e){let i=e[0]-t[0],s=e[1]-t[1];const o=Math.hypot(i,s);return o<1e-12?[1,0]:(i/=o,s/=o,(i<-1e-12||Math.abs(i)<=1e-12&&s<0)&&(i=-i,s=-s),[i,s])}function io(t,e,i){const s=to((t[0]+e[0])/2,i),o=to((t[1]+e[1])/2,i),[n,r]=eo(t,e);let a=Math.atan2(r,n);a<0&&(a+=Math.PI);const l=Math.round(1800*a)/1800,h=i>0&&i<.01?6:i<1?4:2;return`${s.toFixed(h)},${o.toFixed(h)}@${l.toFixed(4)}`}function so(t,e,i,s){return 1===s?io(t,e,i):io([t[0]/s,t[1]/s],[e[0]/s,e[1]/s],i)}function oo(t,e){if(!Array.isArray(t.a)||!Array.isArray(t.b)||t.a.length<2||t.b.length<2)return null;const i=[Number(t.a[0]),Number(t.a[1]),Number(t.b[0]),Number(t.b[1])];if(!i.every(Number.isFinite))return null;const s=e>0?e:1;return[[i[0]*s,i[1]*s],[i[2]*s,i[3]*s]]}function no(t,e,i,s,o){const n=o>0?o:1;return{key:so(t,e,s,n),cm:Zs(i),a:[t[0]/n,t[1]/n],b:[e[0]/n,e[1]/n]}}function ro(t,e){const i=e>0?e:1,s=[];for(const e of t){const t=e.key.lastIndexOf("@");if(t<0)continue;const[o,n]=e.key.slice(0,t).split(",").map(Number),r=Number(e.key.slice(t+1));[o,n,r].every(Number.isFinite)&&s.push({w:e,x:o*i,y:n*i,ang:r})}return s}function ao(t,e){const[i,s]=eo(t,e);let o=Math.atan2(s,i);return o<0&&(o+=Math.PI),o}function lo(t,e){let i=Math.abs(t-e);return i>Math.PI/2&&(i=Math.PI-i),i<.02}function ho(t,e,i,s,o=1){if(!t?.length)return null;const n=so(e,i,s,o),r=t.find(t=>t.key===n);if(r)return r;const a=o>0?o:1,l=(e[0]+i[0])/2,h=(e[1]+i[1])/2,c=ao(e,i),d=Math.max(.5*s,1e-9)*a;for(const e of ro(t,a))if(lo(e.ang,c)&&Math.hypot(e.x-l,e.y-h)<=d)return e.w;return null}function co(t,e,i,s,o=1){const n=ho(t,e,i,s,o);return n&&n.cm>0?Zs(n.cm):0}function po(t,e,i,s=1,o=[]){if(!t?.length)return[];const n=new Set,r=Te(e);for(const t of r)n.add(so([t[0],t[1]],[t[2],t[3]],i,s));const a=e||[],l=Math.max(i*s*.02,1e-9);for(let t=0;t(n.has(t.key)||(t=>{const e=oo(t,s);if(!e)return!1;const[i,n]=e,a=n[0]-i[0],h=n[1]-i[1],c=Math.hypot(a,h);if(c<=l)return!1;if(!r.some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];return lo(ao(i,n),ao(e,s))&&Ro(i[0],i[1],e[0],e[1],s[0],s[1])<=l&&Ro(n[0],n[1],e[0],e[1],s[0],s[1])<=l}))return!1;const d=(o||[]).some(t=>{const e=[t[0],t[1]],s=[t[2],t[3]];if(!lo(ao(i,n),ao(e,s)))return!1;const o=t=>Math.abs((t[0]-i[0])*h-(t[1]-i[1])*a)/c;if(o(e)>l||o(s)>l)return!1;const r=c*c,d=((e[0]-i[0])*a+(e[1]-i[1])*h)/r,p=((s[0]-i[0])*a+(s[1]-i[1])*h)/r;return Math.min(1,Math.max(d,p))-Math.max(0,Math.min(d,p))>l/c});return!d})(t))&&t.cm>=1&&t.cm<=100)}function uo(t,e,i,s=8){const[o,n]=eo(t,e);let r=Math.atan2(n,o);r<0&&(r+=Math.PI);let a=i*Math.PI/180%Math.PI;a<0&&(a+=Math.PI);let l=Math.abs(r-a);return l>Math.PI/2&&(l=Math.PI-l),l<=s*Math.PI/180}function _o(t,e,i,s,o=1){if(!t?.length)return[];if(e.length!==i.length)return t.slice();const n=new Map;for(let t=0;t0?o:1,a=Math.max(.5*s,1e-9)*r,l=new Set,h=[];for(const o of t){let t="",c=null;const d=oo(o,r);if(d)for(let o=0;oa||Ro(d[1][0],d[1][1],n[0],n[1],l[0],l[1])>a)continue;const u=l[0]-n[0],_=l[1]-n[1],m=u*u+_*_;if(m<1e-18)continue;const g=t=>{const e=Math.max(0,Math.min(1,((t[0]-n[0])*u+(t[1]-n[1])*_)/m));return[h[0]+(p[0]-h[0])*e,h[1]+(p[1]-h[1])*e]};c=[g(d[0]),g(d[1])],t=so(c[0],c[1],s,r);break}if(d||(t=n.get(o.key)||""),!t){const n=ro([o],r)[0];if(n)for(let o=0;o1.000001)continue;if(Ro(n.x,n.y,l[0],l[1],h[0],h[1])>a)continue;const g=c[0]+(d[0]-c[0])*Math.max(0,Math.min(1,m)),f=c[1]+(d[1]-c[1])*Math.max(0,Math.min(1,m)),[v,y]=eo(c,d),b=Math.max(s*r,1e-6);t=so([g-v*b,f-y*b],[g+v*b,f+y*b],s,r);break}}t||(t=o.key),l.has(t)||(l.add(t),h.push(c?no(c[0],c[1],o.cm,s,r):{...o,key:t,cm:Zs(o.cm)}))}return h}function mo(t,e,i,s,o,n=1){const r=so(e,i,o,n),a=(t||[]).filter(t=>t.key!==r);return null==s||s<1?a:[...a,no(e,i,s,o,n)]}function go(t,e,i,s,o,n=[],r=1){let a=t?t.slice():[];for(const t of function(t,e,i,s,o=1,n=[]){const r=Mo(t,e,i,s,o,n);if(!r)return[];const a=[];for(let t=0;t0)||!t||t.length<2)return"";if(i&&t.length>=3){let i=t;const s=t[t.length-1];if(t.length>=4&&Math.hypot(t[0][0]-s[0],t[0][1]-s[1])<1e-9&&(i=t.slice(0,-1)),i.length>=3){const t=i.map(()=>e),s=Io(i,t),o=ko(i,t);if(s&&o)return`${xo(s)} ${xo(So(o))}`}}let s="";for(let i=0;i.05)&&Ro(r,a,t[0],t[1],t[2],t[3])<=n)return!0}return!1}function yo(t,e){return Math.max(t*(e>0?e:1)*.04,1e-9)}function bo(t,e){const i=t[e],s=t[(e+1)%t.length],o=s[0]-i[0],n=s[1]-i[1],r=Math.hypot(o,n)||1;let a=-n/r,l=o/r;const h=[(i[0]+s[0])/2,(i[1]+s[1])/2];return function(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r+0)+n&&(i=!i)}return i}([h[0]+.001*a,h[1]+.001*l],t)||(a=-a,l=-l),[a,l]}function wo(t,e){const i=t[0]*e[1]-t[1]*e[0],s=t[0]*e[0]+t[1]*e[1];return Math.abs(i)<1e-9&&s>0}function $o(t,e,i,s){const o=e[0]*s[1]-e[1]*s[0];if(Math.abs(o)<1e-12)return null;const n=[i[0]-t[0],i[1]-t[1]],r=(n[0]*s[1]-n[1]*s[0])/o;return[t[0]+r*e[0],t[1]+r*e[1]]}function ko(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);const s=[];for(let o=0;o0||d>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]+p*c,e[1]+u*c],n=[e[0]+_*d,e[1]+m*d];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=$o($,b,k,w),S=Math.max(c,d,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}c>0&&s.push([t[o][0]+p*c,t[o][1]+u*c]),d>0&&s.push([t[o][0]+_*d,t[o][1]+m*d]),c>0||d>0||s.push([t[o][0],t[o][1]])}return s.length>=3?s:null}function xo(t,e=!0){if(!t.length)return"";let i=`M ${t[0][0]} ${t[0][1]}`;for(let e=1;et?.id===e),a=Pe(r);if(!a||a.length<3)return null;const l=yo(s,o),h=[];for(const i of t||[]){if(!i||i.id===e)continue;const t=Pe(i);if(t)for(const e of Li(a,t,l))h.push([e[0],e[1]],[e[2],e[3]])}for(const t of i||[])h.push([t[0],t[1]],[t[2],t[3]]);for(const t of n||[]){const e=oo(t,o);e&&h.push(e[0],e[1])}const c=[],d=[];for(let t=0;tl)continue;const r=((t[0]-e[0])*(i[0]-e[0])+(t[1]-e[1])*(i[1]-e[1]))/(s*s);r<=o||r>=1-o||(n.some(t=>Math.abs(t-r)*s<=2*l)||n.push(r))}n.sort((t,e)=>t-e);for(const s of n)c.push([e[0]+(i[0]-e[0])*s,e[1]+(i[1]-e[1])*s]),d.push(t)}return{poly:c,parent:d,orig:a}}function Co(t,e,i,s){const o=e.poly.length,n=new Array(o).fill(0);if(!t?.length)return n;const r=new Set,a=[];for(let l=0;l0?(n[l]=Zs(h.cm),r.add(h.key)):a.push(l)}if(!a.length)return n;const l=s>0?s:1,h=Math.max(.5*i,1e-9)*l,c=ro(t,l).filter(t=>t.w.cm>0),d=new Map;for(const t of a){const i=e.parent[t],s=d.get(i);s?s.push(t):d.set(i,[t])}for(const[t,i]of d){const s=e.orig[t],o=e.orig[(t+1)%e.orig.length],a=ao(s,o),d=(s[0]+o[0])/2,p=(s[1]+o[1])/2;let u=null;const _=Math.hypot(o[0]-s[0],o[1]-s[1]);for(const t of c){if(r.has(t.w.key))continue;if(!lo(t.ang,a))continue;const e=oo(t.w,l);let i=!1,n=0;if(e){if(!lo(ao(e[0],e[1]),a))continue;if(Ro(s[0],s[1],e[0][0],e[0][1],e[1][0],e[1][1])>h||Ro(o[0],o[1],e[0][0],e[0][1],e[1][0],e[1][1])>h)continue;i=!0,n=Math.max(0,Math.hypot(e[1][0]-e[0][0],e[1][1]-e[0][1])-_)}else{if(Ro(t.x,t.y,s[0],s[1],o[0],o[1])>h)continue;n=Math.hypot(t.x-d,t.y-p)}(!u||i&&!u.exact||i===u.exact&&nt?.id===e),o=Pe(s);if(!o)return[];const n=[];for(const s of t||[]){if(!s||s.id===e)continue;const t=Pe(s);if(t)for(const e of Li(o,t,i))n.push(e)}return n}(t,e,yo(o,a)),c=function(t,e,i,s,o){const n=yo(s,o),r=[];for(let a=0;aRo(c,d,t[0],t[1],t[2],t[3])<=n);r.push(p?"shared":"outer")}return r}(l.poly,h,s,o,a),d=Co(i,l,o,a),p=d.map((t,e)=>c[e]&&t>0?Qs(t,n,r)/2:0);return{...l,kinds:c,cms:d,offsets:p}}function Po(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(h)for(let t=0;t0&&!l.has(h.key)&&(l.add(h.key),a.push(h));const h=[];for(const a of t||[]){if(!a?.id)continue;const l=Do(t,a.id,e,i,s,o,n,r);if(l)for(let t=0;t0)||null===l.kinds[i]){t++;continue}let n=t;for(;n+10&&h.push({a:[c[0],c[1]],b:[d[0],d[1]],key:so(c,d,s,r),cm:o,len:p}),t=n+1}}}h.sort((t,e)=>e.len-t.len||t.key.localeCompare(e.key));const c=[],d=new Set,p=new Set,u=4*yo(s,r);for(const t of h){const e=a.filter(e=>!p.has(e.key)&&e.cm===t.cm&&lo(ao(e.a,e.b),ao(t.a,t.b))&&Ro(e.a[0],e.a[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u&&Ro(e.b[0],e.b[1],t.a[0],t.a[1],t.b[0],t.b[1])<=u);if(e.length){for(const t of e)p.add(t.key);d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)))}}for(const t of a)p.has(t.key)||d.has(t.key)||(d.add(t.key),c.push(no(t.a,t.b,t.cm,s,r)));return c}function To(t,e,i,s,o,n,r,a=1){const l=yo(o,a),h=(s[0]+s[2])/2,c=(s[1]+s[3])/2,d=ao([s[0],s[1]],[s[2],s[3]]);let p=null;for(const s of Po(t,e,i,o,n,r,a)){if(!lo(ao(s.a,s.b),d))continue;const t=Ro(h,c,s.a[0],s.a[1],s.b[0],s.b[1]);t>4*l||(!p||tt?.id===e),h=Pe(l);if(!h||h.length<3)return null;if(!i?.length)return h.map(t=>[t[0],t[1]]);const c=Do(t,e,i,s,o,n,r,a);return c&&c.offsets.some(t=>t>0)&&ko(c.poly,c.offsets)||h.map(t=>[t[0],t[1]])}function No(t){const e=t.map(t=>[t[0],t[1]]);return e.push([t[0][0],t[0][1]]),[e]}function Eo(t,e,i,s=[],o,n,r,a=1,l=[]){if(!e?.length&&!l.length)return null;const h=[];let c=0;for(const s of t||[]){if(!s?.id)continue;const l=Do(t,s.id,e,i,o,n,r,a);if(!l||l.poly.length<3||!l.offsets.some(t=>t>0))continue;for(const t of l.offsets)t>0&&(c=Math.max(c,2*t));const d=Io(l.poly,l.offsets),p=ko(l.poly,l.offsets);d&&h.push({outset:d,inset:p})}for(const t of l){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);if(e.length){const s=Math.min(Math.max(...e)-Math.min(...e),Math.max(...i)-Math.min(...i)),o=Math.min(...t.map((e,i)=>{const s=t[(i+1)%t.length];return Math.hypot(s[0]-e[0],s[1]-e[1])}));c=Math.max(c,t.length>16?s:o)}}const d=function(t,e,i,s,o,n,r){if(!e?.length||!i?.length)return[];const a=4*yo(s,r),l=new Map;for(const a of Po(t,e,i,s,o,n,r))!a.open&&a.half>0&&!l.has(a.key)&&l.set(a.key,a);const h=[...l.values()];if(h.length<2)return[];const c=[];for(const t of i)for(const e of[[t[0],t[1]],[t[2],t[3]]])c.some(t=>Math.hypot(t[0]-e[0],t[1]-e[1])<=a)||c.push(e);const d=[],p=(t,e)=>{let i=0,s=0;if(Math.hypot(t.a[0]-e[0],t.a[1]-e[1])<=a)i=t.b[0]-t.a[0],s=t.b[1]-t.a[1];else{if(!(Math.hypot(t.b[0]-e[0],t.b[1]-e[1])<=a))return null;i=t.a[0]-t.b[0],s=t.a[1]-t.b[1]}const o=Math.hypot(i,s);return o>a?[i/o,s/o]:null};for(const t of c){const e=h.map(e=>({iv:e,u:p(e,t)})).filter(t=>!!t.u);for(let i=0;i4*_||d.push(r>0?[t.slice(),c,u,p]:[t.slice(),p,u,c])}}return d}(t,e,i,o,n,r,a);try{const t=t=>{const e=No(t.outset);return t.inset?xe(e,No(t.inset)):e};let e=h.length?t(h[0]):null;for(let i=1;i0))continue;const i=t.angle*Math.PI/180,s=Math.cos(i),n=Math.sin(i),r=-n,l=s,h=t.length/2,d=1.25*Math.max(c,o*a),p=[[t.x-s*h-r*d,t.y-n*h-l*d],[t.x+s*h-r*d,t.y+n*h-l*d],[t.x+s*h+r*d,t.y+n*h+l*d],[t.x-s*h+r*d,t.y-n*h+l*d]];e&&(e=xe(e,No(p)))}for(const t of l)t.length<3||(e=e?$e(e,No(t)):[No(t)]);const i=function(t){if(!t)return"";let e="";for(const i of t)if(Array.isArray(i))for(const t of i){if(!Array.isArray(t)||t.length<4)continue;const i=t.slice(0,t.length-1);i.length<3||(e+=(e?" ":"")+xo(i.map(t=>[t[0],t[1]])))}return e}(e);return i?{d:i,depthUnits:c,fillRule:"evenodd"}:null}catch{const s=function(t,e,i,s,o,n,r=1){if(!e?.length)return[];const a=[];for(const l of t||[]){if(!l?.id)continue;const h=Do(t,l.id,e,i,s,o,n,r);if(!h||h.poly.length<3||!h.offsets.some(t=>t>0))continue;const c=Io(h.poly,h.offsets),d=ko(h.poly,h.offsets);if(!c||!d)continue;const p=`${xo(c)} ${xo(So(d))}`;let u="",_="outer",m=0,g=0;for(let t=0;t0){u=so(h.poly[t],h.poly[(t+1)%h.poly.length],s,r),_=h.kinds[t]||"outer",m=h.cms[t],g=Qs(m,o,n);break}a.push({d:p,key:u,kind:_,cm:m,depthUnits:g})}return a}(t,e,i,o,n,r,a),h=l.map(t=>xo(t)).join(" ");return s.length||h?{d:[s.map(t=>t.d).join(" "),h].filter(Boolean).join(" "),depthUnits:c,fillRule:"nonzero"}:null}}function Io(t,e){const i=t?.length||0;if(i<3||e.length!==i)return null;if(e.every(t=>!(t>0)))return t.map(t=>[t[0],t[1]]);So(t),e.slice().reverse();const s=[];for(let o=0;o0||a>0)){s.push([t[o][0],t[o][1]]);continue}if(wo(b,w)){const e=t[o],i=[e[0]-l*r,e[1]-h*r],n=[e[0]-c*a,e[1]-d*a];s.push(i),Math.hypot(n[0]-i[0],n[1]-i[1])>1e-9&&s.push(n);continue}const x=$o($,b,k,w),S=Math.max(r,a,1e-9);if(x){if(Math.hypot(x[0]-t[o][0],x[1]-t[o][1])<=4*S){s.push(x);continue}}r>0&&s.push([t[o][0]-l*r,t[o][1]-h*r]),a>0&&s.push([t[o][0]-c*a,t[o][1]-d*a])}return s.length>=3?s:null}function Oo(t,e,i,s,o,n,r=1){const a=[];for(const l of t||[]){const h=Pe(l);if(h&&h.length>=3){const c=Do(t,l.id,e,i,s,o,n,r),d=c&&c.offsets.some(t=>t>0)?Io(c.poly,c.offsets):null,p=d||h;a.push({poly:p.map(t=>t.join(",")).join(" ")})}else l&&null!=l.x&&null!=l.y&&null!=l.w&&null!=l.h&&a.push({rect:{x:l.x,y:l.y,w:l.w,h:l.h,rx:.03*Math.min(l.w,l.h)}})}return a}function Fo(t,e,i,s,o,n,r=1){let a=null;for(const i of t||[]){const t=Pe(i);if(t)for(let s=0;ss*r)return{ox:0,oy:0,cm:0};const l=co(i,a.a,a.b,s,r);if(!(l>0))return{ox:0,oy:0,cm:0};const h=Qs(l,o,n),c=Pe(a.room),[d,p]=bo(c,a.edge),u=e.flip_v?-1:1,_=h/2;return{ox:d*_*u,oy:p*_*u,cm:l}}const Lo=150;function qo(t){return Number.isFinite(t)?Math.max(1,Math.min(Lo,t)):1}function Ho(t){return((Number.isFinite(Number(t))?Number(t):0)%90+90)%90}const Uo=t=>{const e=t.map(t=>[t[0],t[1]]);return!e.length||e[0][0]===e[e.length-1][0]&&e[0][1]===e[e.length-1][1]||e.push([...e[0]]),[e]};function Wo(t){const e=[];for(const i of t||[])for(const t of i||[]){const i=(t||[]).filter(t=>Array.isArray(t)&&t.length>=2);i.length<4||e.push(`M ${i.slice(0,-1).map(t=>`${t[0]} ${t[1]}`).join(" L ")} Z`)}return e.join(" ")}function Bo(t,e,i,s,o){const n=e[0]-t[0],r=e[1]-t[1],a=Math.hypot(n,r);if(!(a>1e-9))return null;const l=Qs(i,s,o)/2,h=-r/a*l,c=n/a*l;return[[t[0]+h,t[1]+c],[e[0]+h,e[1]+c],[e[0]-h,e[1]-c],[t[0]-h,t[1]-c]]}function jo(t,e,i){const s=Number(e)>0?Number(e):5,o=qo(t.cm)/s*i,n=t.center[0],r=t.center[1];if("circle"===t.shape){const t=o/2;return Array.from({length:96},(e,i)=>{const s=i/96*Math.PI*2;return[n+Math.cos(s)*t,r+Math.sin(s)*t]})}const a=o/2,l=Ho(t.angle)*Math.PI/180,h=Math.cos(l),c=Math.sin(l);return[[-a,-a],[a,-a],[a,a],[-a,a]].map(([t,e])=>[n+t*h-e*c,r+t*c+e*h])}function Go(t,e,i){const s=[];for(let o=0;o+1t.length>=3).map(t=>Uo(t));return e.length?$e(e[0],...e.slice(1)):null}catch{return null}}(e);if(i)return xe(Uo(t),i)}catch{}let i=[Uo(t)];for(const t of e)if(!(t.length<3))try{i=xe(i,Uo(t))}catch{}return i}function Ko(t){let e=0;for(const i of t||[])if(i?.length){e+=Ye(i[0]||[]);for(let t=1;t=4&&e.push(t.slice(0,-1).map(t=>[t[0],t[1]]))}return e}function Jo(t,e,i){if(!(i>0))return[];const s=Math.max(1e-9,1e-9*i);if(t.some(t=>Zo(e,t)||(t=>t.some((i,o)=>{const n=t[(o+1)%t.length],r=n[0]-i[0],a=n[1]-i[1],l=r*r+a*a;if(!(l>0))return Math.hypot(e[0]-i[0],e[1]-i[1])<=s;const h=Math.max(0,Math.min(1,((e[0]-i[0])*r+(e[1]-i[1])*a)/l));return Math.hypot(e[0]-(i[0]+h*r),e[1]-(i[1]+h*a))<=s}))(t)))return[Array.from({length:128},(t,s)=>{const o=s/128*Math.PI*2;return[e[0]+Math.cos(o)*i*1.01,e[1]+Math.sin(o)*i*1.01]})];const o=[];for(const n of t)if(!(n.length<2)){o.push(n);for(let t=0;ts&&u>s))continue;const _=Math.max(-1,Math.min(1,(l*c+h*d)/(p*u))),m=Math.sqrt(Math.max(0,(1+_)/2)),g=Math.max(2*i,1.02*i/Math.max(m,.001)),f=[e[0]+l/p*g,e[1]+h/p*g],v=[e[0]+c/u*g,e[1]+d/u*g];o.push([r,a,v,f])}}return o}function Zo(t,e){let i=!1;for(let s=0,o=e.length-1;st[1]!=l>t[1]&&t[0]<(a-n)*(t[1]-r)/(l-r||1e-12)+n&&(i=!i)}return i}function Xo(t,e,i){if(Math.hypot(t.center[0]-e.center[0],t.center[1]-e.center[1])>i)return!1;if(Math.abs(qo(t.cm)-qo(e.cm))>1e-6)return!1;if(t.shape!==e.shape)return!0;if("circle"===t.shape||"circle"===e.shape)return!0;const s=Math.abs(Ho(t.angle)-Ho(e.angle));return Math.min(s,90-s)<=1e-6}const Qo=1e3;function tn(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}const en=.01,sn=100;function on(t,e=1e3){const i=tn(t?.plan_aspect,e),s=Number(t?.plan_scale),o=Number.isFinite(s)&&s>0?Math.min(sn,Math.max(en,s)):1,n=Number(t?.plan_scale_x),r=Number(t?.plan_scale_y),a=Number.isFinite(n)&&n>0?Math.min(sn,Math.max(en,n)):o,l=Number.isFinite(r)&&r>0?Math.min(sn,Math.max(en,r)):o,h=Number(t?.plan_x),c=Number(t?.plan_y),d=Ls(t?.plan_angle);return{x:i.x+(Number.isFinite(h)?mn(h):0)*e,y:i.y+(Number.isFinite(c)?mn(c):0)*e,w:i.w*a,h:i.h*l,...d?{angle:d}:{}}}function nn(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function rn(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Qo,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Qo,i[1]*e,i[2]*Qo,i[3]*e],bg:t.plan_url?{href:Gi(t.plan_url),...on(t,Qo)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...nn(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Qo:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Qo:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Qo,t[1]*e]):void 0}}),room_drafts:(t.room_drafts||[]).map(t=>({id:t.id,points:(t.points||[]).map(t=>[t[0]*Qo,t[1]*e]),segments:(t.segments||[]).map(t=>({cm:Number(t.cm)}))})),partitions:(t.partitions||[]).map(t=>({id:t.id,a:[t.a[0]*Qo,t.a[1]*e],b:[t.b[0]*Qo,t.b[1]*e],cm:Number(t.cm)})),wall_columns:(t.wall_columns||[]).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[t.center[0]*Qo,t.center[1]*e],cm:Number(t.cm),..."circle"===t.shape?{}:{angle:Ho(t.angle)}}))}}):[]}const an=5e3,ln=an*Qo,hn=240,cn=Qo/hn,dn=1/hn;function pn(t){if(!Number.isFinite(t))return t;const e=Math.round(t*hn/Qo)*Qo/hn;return Math.abs(e-t)<=1e-9*cn?t:e}function un(t){return{x:pn(t.x),y:pn(t.y)}}function _n(t){return Number.isFinite(t)?Math.min(ln,Math.max(-ln,t)):0}function mn(t){return Number.isFinite(t)?Math.min(an,Math.max(-an,t)):0}const gn=1/3,fn=200;function vn(t){let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t){const t=Number(n[0]),r=Number(n[1]);Number.isFinite(t)&&Number.isFinite(r)&&(ts&&(s=t),r>o&&(o=r))}return e>s?null:{minX:e,minY:i,maxX:s,maxY:o}}function yn(t){return t.poly&&t.poly.length?vn(t.poly):null==t.x||null==t.y?null:vn([[t.x,t.y],[t.x+(t.w||0),t.y+(t.h||0)]])}function bn(t,e){const i=[];for(const e of t.rooms||[]){const t=yn(e);t&&i.push(t)}if(t.bg){const e=vn(js(t.bg));e&&i.push(e)}for(const t of e||[])if(Array.isArray(t)){const e=vn([t]);e&&i.push(e)}else i.push(t);return i}const wn=t=>{if(!t.length)return 0;const e=[...t].sort((t,e)=>t-e),i=e.length>>1;return e.length%2?e[i]:(e[i-1]+e[i])/2},$n=t=>{let e=1/0,i=1/0,s=-1/0,o=-1/0;for(const n of t)n.minXs&&(s=n.maxX),n.maxY>o&&(o=n.maxY);return e>s||i>o?null:{x:e,y:i,w:s-e,h:o-i}};function kn(t,e){let{x:i,y:s,w:o,h:n}=t;o<30&&(i=i+o/2-100,o=fn),n<30&&(s=s+n/2-100,n=fn);const r=Math.max(o,n)*e;return{x:i-r,y:s-r,w:o+2*r,h:n+2*r}}function xn(t,e={}){const i=e.pad??.05,s=e.k??10,o=e.minSpread??50,n=t.filter(t=>Number.isFinite(t.minX)&&Number.isFinite(t.minY)&&Number.isFinite(t.maxX)&&Number.isFinite(t.maxY)&&Math.abs(t.minX)<=ln&&Math.abs(t.maxX)<=ln&&Math.abs(t.minY)<=ln&&Math.abs(t.maxY)<=ln);if(!n.length)return{core:null,all:null,outliers:0};const r=$n(n);if(n.length<4){const t=kn(r,i);return{core:t,all:t,outliers:0}}const a=n.map(t=>(t.minX+t.maxX)/2),l=n.map(t=>(t.minY+t.maxY)/2),h=wn(a),c=wn(l),d=n.map((t,e)=>Math.max(Math.abs(a[e]-h),Math.abs(l[e]-c))),p=Math.max(((t,e)=>t.length?t[Math.min(t.length-1,Math.max(0,Math.round(e*(t.length-1))))]:0)([...d].sort((t,e)=>t-e),.75),o),u=d.map(t=>t>s*p),_=u.filter(Boolean).length,m=_&&_<=n.length*gn?n.filter((t,e)=>!u[e]):n;return{core:kn($n(m)||r,i),all:kn(r,i),outliers:m===n?0:_}}function Sn(t,e,i=.05){const s=xn(bn(t,e),{pad:i});if(s.core)return s.core;const o=t.vb&&4===t.vb.length&&t.vb[2]>0&&t.vb[3]>0?t.vb:[0,0,Qo,Qo];return{x:o[0],y:o[1],w:o[2],h:o[3]}}function Mn(t){const e=Sn(t);return{x:e.x+e.w/2,y:e.y+e.h/2}}function Cn(t){const e=[];for(const i of t.rooms||[]){const t=yn(i);t&&e.push(t)}const i=xn(e,{pad:0}).core;return i?Math.max(Qo,Math.min(ln,Math.max(i.w,i.h))):Qo}function Dn(t,e,i,s=1){const o=Number(i),n=Number.isFinite(s)&&s>0?s:1;return!Number.isFinite(o)||o<=0?t*n:t*Cn(e)*n/o}const Pn=[1,2,5,10,20,50,100,200,500,1e3];function zn(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Tn(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)]}const Rn=["furniture","appliance","sanitary","other"],An=[{id:"sofa",group:"furniture",w:220,h:90,g:[["r",0,0,1,1],["l",.09,.26,.91,.26],["l",.09,.26,.09,1],["l",.91,.26,.91,1],["l",.5,.26,.5,1]]},{id:"armchair",group:"furniture",w:90,h:85,g:[["r",0,0,1,1],["l",.14,.28,.86,.28],["l",.14,.28,.14,1],["l",.86,.28,.86,1]]},{id:"coffee_table",group:"furniture",w:110,h:60,g:[["r",0,0,1,1],["r",.08,.14,.84,.72]]},{id:"table_dining",group:"furniture",w:140,h:80,g:[["r",0,0,1,1],["r",.06,.11,.88,.78]]},{id:"table_round",group:"furniture",w:120,h:120,g:[["e",.5,.5,.5,.5],["e",.5,.5,.41,.41]]},{id:"chair",group:"furniture",w:45,h:45,g:[["r",0,0,1,.18],["r",.06,.18,.88,.8]]},{id:"desk",group:"furniture",w:120,h:60,g:[["r",0,0,1,1],["r",.63,.07,.31,.86],["l",.63,.5,.94,.5]]},{id:"bed_double",group:"furniture",w:160,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.06,.1,.4,.15],["r",.54,.1,.4,.15],["l",0,.33,1,.33]]},{id:"bed_single",group:"furniture",w:90,h:200,g:[["r",0,0,1,1],["r",0,0,1,.07],["r",.15,.1,.7,.15],["l",0,.33,1,.33]]},{id:"nightstand",group:"furniture",w:45,h:40,g:[["r",0,0,1,1],["r",.12,.14,.76,.33],["r",.12,.53,.76,.33]]},{id:"wardrobe",group:"furniture",w:100,h:60,g:[["r",0,0,1,1],["l",0,.72,1,.72],["l",.5,.72,.5,1]]},{id:"bookshelf",group:"furniture",w:80,h:30,g:[["r",0,0,1,1],["l",.34,0,.34,1],["l",.67,0,.67,1]]},{id:"fridge",group:"appliance",w:60,h:65,g:[["r",0,0,1,1],["l",0,.36,1,.36],["l",.83,.44,.83,.64]]},{id:"stove",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["e",.29,.31,.15,.15],["e",.71,.31,.15,.15],["e",.29,.71,.15,.15],["e",.71,.71,.15,.15]]},{id:"dishwasher",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["r",.1,.12,.8,.76],["e",.5,.5,.27,.27],["e",.5,.5,.13,.13]]},{id:"washer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["e",.5,.57,.14,.14]]},{id:"dryer",group:"appliance",w:60,h:60,g:[["r",0,0,1,1],["l",.08,.17,.92,.17],["e",.5,.57,.3,.3],["p",.36,.5,.5,.64,.64,.5]]},{id:"tv",group:"appliance",w:120,h:30,g:[["r",0,0,1,.42],["l",.5,.42,.5,.72],["l",.3,.72,.7,.72]]},{id:"ac",group:"appliance",w:90,h:25,g:[["r",0,0,1,1],["l",.05,.55,.95,.55],["l",.05,.79,.95,.79]]},{id:"water_heater",group:"appliance",w:45,h:45,g:[["e",.5,.5,.5,.5],["e",.5,.5,.31,.31]]},{id:"toilet",group:"sanitary",w:40,h:70,g:[["r",.06,0,.88,.2],["e",.5,.58,.37,.35],["e",.5,.58,.22,.2]]},{id:"bathtub",group:"sanitary",w:170,h:75,g:[["r",0,0,1,1],["r",.05,.11,.77,.78],["e",.89,.5,.045,.1]]},{id:"shower",group:"sanitary",w:90,h:90,g:[["r",0,0,1,1],["l",0,0,1,1],["l",1,0,0,1],["e",.5,.5,.08,.08]]},{id:"sink",group:"sanitary",w:60,h:45,g:[["r",0,0,1,1],["e",.5,.6,.34,.3],["e",.5,.15,.07,.07]]},{id:"kitchen_sink",group:"sanitary",w:80,h:60,g:[["r",0,0,1,1],["r",.06,.24,.44,.64],["r",.54,.24,.4,.64],["e",.5,.12,.06,.06]]},{id:"bidet",group:"sanitary",w:40,h:55,g:[["e",.5,.5,.44,.5],["e",.5,.5,.26,.3]]},{id:"stairs",group:"other",w:100,h:280,g:[["r",0,0,1,1],["l",0,.111,1,.111],["l",0,.222,1,.222],["l",0,.333,1,.333],["l",0,.444,1,.444],["l",0,.556,1,.556],["l",0,.667,1,.667],["l",0,.778,1,.778],["l",0,.889,1,.889],["l",.5,.93,.5,.06],["p",.38,.16,.5,.06,.62,.16]]},{id:"fireplace",group:"other",w:120,h:40,g:[["r",0,0,1,1],["p",.22,1,.22,.42,.78,.42,.78,1]]},{id:"plant",group:"other",w:40,h:40,g:[["e",.5,.5,.22,.22],["l",.5,.28,.5,.02],["l",.5,.72,.5,.98],["l",.28,.5,.02,.5],["l",.72,.5,.98,.5],["l",.34,.34,.13,.13],["l",.66,.66,.87,.87],["l",.66,.34,.87,.13],["l",.34,.66,.13,.87]]},{id:"rug",group:"other",w:200,h:140,g:[["r",0,0,1,1],["r",.06,.09,.88,.82]]}],Nn=new Map(An.map(t=>[t.id,t]));function En(t){return t&&Nn.get(t)||null}function In(t){return An.filter(e=>e.group===t)}function On(t,e,i=cn,s=1e3){const o=Number(e)>0?Number(e):5;return(Number(t)||0)/o*i/s}const Fn=5e-4,Ln=an;function qn(t){return Number.isFinite(t)?Math.max(Fn,Math.min(Ln,t)):Fn}const Hn=t=>{const e=Math.round(1e3*t)/1e3;return Object.is(e,-0)?"0":String(e)};function Un(t,e,i){const s=En(t);if(!(s&&e>0&&i>0))return"";const o=t=>Hn(t*e),n=t=>Hn(t*i),r=[];for(const t of s.g)if("r"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}H${o(e+s)}V${n(i+a)}H${o(e)}Z`)}else if("l"===t[0]){const[,e,i,s,a]=t;r.push(`M${o(e)} ${n(i)}L${o(s)} ${n(a)}`)}else if("e"===t[0]){const[,s,a,l,h]=t;r.push(`M${o(s-l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s+l)} ${n(a)}A${Hn(l*e)} ${Hn(h*i)} 0 0 1 ${o(s-l)} ${n(a)}Z`)}else{const e=t.slice(1);if(e.length<4)continue;let i=`M${o(e[0])} ${n(e[1])}`;for(let t=2;t+1{let e=(t%360+360)%360;return e>180&&(e-=360),e};function Bn(t,e,i,s,o,n=0){let r=null,a=o;for(const o of s){const[s,l,h,c]=o,d=h-s,p=c-l,u=d*d+p*p;if(!u)continue;const _=Math.sqrt(u);let m=((t-s)*d+(e-l)*p)/u;m=Math.max(0,Math.min(1,m));let g=s+m*d,f=l+m*p;const v=Math.hypot(t-g,e-f);if(!(v0){const t=Math.round(m*_/n)*n;g=s+t/_*d,f=l+t/_*p}r={cx:g+y*(i/2),cy:f+b*(i/2),angle:Wn(180*Math.atan2(-y,b)/Math.PI),dist:v}}return r}function jn(t,e,i){const s=i;return{a:[t[0]/s,t[1]/s],b:[e[0]/s,e[1]/s]}}function Gn(t,e){const i=e;return[t.a[0]*i,t.a[1]*i,t.b[0]*i,t.b[1]*i]}function Vn(t){return Array.isArray(t)&&t.length>=2&&Number.isFinite(Number(t[0]))&&Number.isFinite(Number(t[1]))}function Kn(t){if(!Array.isArray(t))return[];const e=[];for(const i of t){if(!i||"object"!=typeof i)continue;const t=i;if(!Vn(t.a)||!Vn(t.b))continue;const s=[Number(t.a[0]),Number(t.a[1])],o=[Number(t.b[0]),Number(t.b[1])];Math.hypot(o[0]-s[0],o[1]-s[1])<.001||e.push({a:s,b:o})}return e}function Yn(t,e){const i=e[0],s=e[1],o=e[2]-i,n=e[3]-s,r=o*o+n*n;if(r<1e-18){return{q:[i,s],t:0,d:Math.hypot(t[0]-i,t[1]-s)}}let a=((t[0]-i)*o+(t[1]-s)*n)/r;a=Math.max(0,Math.min(1,a));const l=[i+a*o,s+a*n];return{q:l,t:a,d:Math.hypot(t[0]-l[0],t[1]-l[1])}}function Jn(t,e){return Yn(t,e).q}function Zn(t,e,i,s,o){const n=Yn(t,e).q;let r=n,a=1/0;for(const t of i){const e=Math.hypot(n[0]-t[0],n[1]-t[1]);e<=o&&e0?s:1,m=Math.round(u/_)*_,g=Math.max(0,Math.min(p,m))/p;return[l+c*g,h+d*g]}function Xn(t,e){const i=[],s=(t||[]).filter(t=>t?.id);for(let t=0;tt.seg)}(e,s);let n=null;for(const r of e||[]){if(!r?.id)continue;const e=Pe(r);if(e)for(let a=0;ai)continue;let p=!1;for(const t of o)if(Zi([(l[0]+h[0])/2,(l[1]+h[1])/2],t)<2*s){p=!0;break}p||(!n||dt?.id),r=Kn(e);if(r.length)return rr(r,t,i,s).map(t=>Gn(t,i));if(!o)return[];const a=[],l=(t,e)=>(t.open_to||[]).includes(e.id)||(e.open_to||[]).includes(t.id);for(let t=0;tjn([t[0],t[1]],[t[2],t[3]],e))}function ir(t,e,i,s){for(const e of t||[])e.open_to&&delete e.open_to;if(!i.length)return;const o=(e||[]).filter(t=>t?.id);new Map(o.map(t=>[t.id,t]));const n=new Map((t||[]).filter(t=>t?.id).map(t=>[t.id,t])),r=(t,e)=>{const i=n.get(t),s=n.get(e);i&&s&&((i.open_to||[]).includes(e)||(i.open_to=[...i.open_to||[],e]),(s.open_to||[]).includes(t)||(s.open_to=[...s.open_to||[],t]))};for(let t=0;tZi(i,t)<4*s)){r(o[t].id,o[e].id);break}}}for(const e of t||[])e.open_to&&!e.open_to.length&&delete e.open_to}function sr(t,e,i){let s=null;for(const o of e){const e=Zi(t,o);e<=i&&(!s||e.05)continue;const d=co(t,[e[0],e[1]],[e[2],e[3]],s,o);if(!(d>0))continue;const p=[(e[0]+e[2])/2,(e[1]+e[3])/2],u=Math.hypot(p[0]-c[0],p[1]-c[1]);u0?l:n}(t,e,i,s,o,n);return mo(t,[e[0],e[1]],[e[2],e[3]],r,s,o)}function nr(t,e,i){const s=[[t[0],t[1]],[t[2],t[3]]],o=[(t[0]+t[2])/2,(t[1]+t[3])/2];for(const n of e)if(!(Zi(o,n)>Math.hypot(t[2]-t[0],t[3]-t[1])&&Zi([n[0],n[1]],t)>i))for(const e of[[n[0],n[1]],[n[2],n[3]]])Zi(e,t)<=2*i&&s.push(e);return s}function rr(t,e,i,s){const o=Kn(t);if(!o.length)return[];const n=Xn(e,s);if(!n.length)return[];const r=[],a=Math.max(4*s,1e-6);for(const t of o){const e=Gn(t,i),o=e[0],l=e[1],h=e[2]-o,c=e[3]-l,d=Math.hypot(h,c);if(d4*s||n>4*s)continue;const r=(t[0]-o)*p+(t[1]-l)*u,h=(t[2]-o)*p+(t[3]-l)*u,c=Math.max(0,Math.min(r,h)),m=Math.min(d,Math.max(r,h));if(m-ct.lo-e.lo||t.hi-e.hi);const i=[];for(const t of e){const e=i[i.length-1];e&&t.lo<=e.hi+a?e.hi=Math.max(e.hi,t.hi):i.push({...t})}for(const e of i){const i=[o+p*e.lo,l+u*e.lo],s=[o+p*e.hi,l+u*e.hi];Math.hypot(s[0]-i[0],s[1]-i[1])i.pair===t&&Math.abs(i.ux*r-i.uy*n)<=1e-6&&Math.abs((e[0]-i.origin[0])*i.uy-(e[1]-i.origin[1])*i.ux)<=h&&Math.abs((e[2]-i.origin[0])*i.uy-(e[3]-i.origin[1])*i.ux)<=h);c||(c={pair:t,origin:[e[0],e[1]],ux:n,uy:r,ranges:[]},l.push(c));const d=(e[0]-c.origin[0])*c.ux+(e[1]-c.origin[1])*c.uy,p=(e[2]-c.origin[0])*c.ux+(e[3]-c.origin[1])*c.uy;c.ranges.push({lo:Math.min(d,p),hi:Math.max(d,p)})}const c=[];for(const t of l){t.ranges.sort((t,e)=>t.lo-e.lo||t.hi-e.hi);const e=[];for(const i of t.ranges){const t=e[e.length-1];t&&i.lo<=t.hi+a?t.hi=Math.max(t.hi,i.hi):e.push({...i})}for(const s of e){const e=[t.origin[0]+t.ux*s.lo,t.origin[1]+t.uy*s.lo],o=[t.origin[0]+t.ux*s.hi,t.origin[1]+t.uy*s.hi];Math.hypot(o[0]-e[0],o[1]-e[1])>=a&&c.push(jn(e,o,i))}}return c}function ar(t,e,i,s){const o=Kn(t);if(!o.length)return[];if(e.length!==i.length)return o;const n=[];for(const t of o){const o=Gn(t,s),r=[o[0],o[1]],a=[o[2],o[3]];let l=r,h=a;for(let t=0;t{const e=Yn(t,[s[0],s[1],o[0],o[1]]);if(e.d>.001)return t;const i=c[0]-n[0],r=c[1]-n[1];return[n[0]+i*e.t,n[1]+r*e.t]};if(Yn([(r[0]+a[0])/2,(r[1]+a[1])/2],[s[0],s[1],o[0],o[1]]).d<.01){l=d(r),h=d(a);break}}n.push(jn(l,h,s))}return n}class lr{constructor(t,e=()=>Date.now()){this.onUpdate=t,this.now=e,this.signed={},this.queued=new Set,this.inFlight=new Map,this.retry=new Map,this.disposed=!1}start(t,e){this.disposed=!1,this.stopTimer(),this.resignTimer=setInterval(()=>this.resign(t(),e()),288e5)}dispose(){this.disposed=!0,this.stopTimer(),clearTimeout(this.batchTimer),this.queued.clear(),this.inFlight.clear()}stopTimer(){void 0!==this.resignTimer&&clearInterval(this.resignTimer),this.resignTimer=void 0}display(t,e){const i=Gi(e);if(!i.startsWith("/api/houseplan/content/"))return i;const s=this.signed[i],o=s?this.now()-s.at:1/0;return o{const e=[...this.queued];this.queued.clear(),this.sign(t,e)},30))}sign(t,e){if(e.length&&t?.callWS)for(const i of function(t,e){const i=Math.max(1,Math.floor(e)),s=[];for(let e=0;e{if(this.disposed)return;const e=this.now(),s={...this.signed};let o=0;for(const n of i){const i=t?.urls?.[n];"string"==typeof i&&i?(s[n]={url:i,at:e},this.retry.delete(n),o++):this.backOff(n)}o&&(this.signed=s,this.onUpdate())}).catch(()=>{for(const t of i)this.backOff(t)}).finally(()=>{for(const t of i)this.inFlight.get(t)===e&&this.inFlight.delete(t)})}}backOff(t){const e=this.retry.get(t)?.delay||0,i=Math.min(6e4,e?2*e:2e3);this.retry.set(t,{notBefore:this.now()+i,delay:i})}resign(t,e){const i=this.now(),s={};for(const[t,o]of Object.entries(this.signed))e.has(t)&&i-o.at{const e=Number(t);return Number.isFinite(e)?e:null};function pr(t){if(!t)return null;const e=t.vacuum_position||t.robot_position||null,i=e&&null!=dr(e.x)&&null!=dr(e.y)?{x:dr(e.x),y:dr(e.y),a:dr(e.a??e.angle??e.theta)}:null;let s=null;const o=t.path?.points??t.path;if(Array.isArray(o)&&o.length){s=[];for(const t of o){const e=dr(Array.isArray(t)?t[0]:t?.x),i=dr(Array.isArray(t)?t[1]:t?.y);null!=e&&null!=i&&s.push([e,i])}s.length||(s=null)}const n=[],r=t.rooms,a=Array.isArray(r)?r.map((t,e)=>[String(t?.id??e),t]):r&&"object"==typeof r?Object.entries(r):[];for(const[t,e]of a){if(!e||"object"!=typeof e)continue;const i=String(e.name??e.label??"").trim();let s=dr(e.cx??e.center?.x),o=dr(e.cy??e.center?.y);if(null==s||null==o){const t=dr(e.x0),i=dr(e.y0),n=dr(e.x1),r=dr(e.y1);null!=t&&null!=i&&null!=n&&null!=r&&(s=(t+n)/2,o=(i+r)/2)}if(null!=s&&null!=o||(s=dr(e.x),o=dr(e.y)),i&&null!=s&&null!=o){const r={id:t,name:i,cx:s,cy:o},a=dr(e.x0),l=dr(e.y0),h=dr(e.x1),c=dr(e.y1);null!=a&&null!=l&&null!=h&&null!=c&&(r.x0=Math.min(a,h),r.y0=Math.min(l,c),r.x1=Math.max(a,h),r.y1=Math.max(l,c)),n.push(r)}}const l=function(t){return String(t.map_name??t.current_map??t.map_index??t.selected_map??"default")}(t);return i||n.length||s?{pos:i,path:s,rooms:n,mapId:l}:null}function ur(t){const e=t?.attributes;return!(!e||!e.vacuum_position&&!e.robot_position)}const _r=t=>t.toLowerCase().replace(/[\s_\-.,]+/g,"");function mr(t,e){const i=new Map(e.map(t=>[_r(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(_r(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=function(t){if(t.length<3)return null;let e=0,i=0,s=0,o=0,n=0,r=0,a=0,l=0,h=0,c=0,d=0,p=0;for(const[[u,_],[m,g]]of t){if(![u,_,m,g].every(Number.isFinite))return null;e+=u*u,i+=u*_,s+=u,o+=_*_,n+=_,r+=1,a+=u*m,l+=_*m,h+=m,c+=u*g,d+=_*g,p+=g}const u=[e,i,s,i,o,n,s,n,r],_=t=>{const[e,i,s,o,n,r,a,l,h]=u,c=e*(n*h-r*l)-i*(o*h-r*a)+s*(o*l-n*a);if(!Number.isFinite(c)||Math.abs(c)<1e-9)return null;const d=[(n*h-r*l)/c,(s*l-i*h)/c,(i*r-s*n)/c,(r*a-o*h)/c,(e*h-s*a)/c,(s*o-e*r)/c,(o*l-n*a)/c,(i*a-e*l)/c,(e*n-i*o)/c];return[d[0]*t[0]+d[1]*t[1]+d[2]*t[2],d[3]*t[0]+d[4]*t[1]+d[5]*t[2],d[6]*t[0]+d[7]*t[1]+d[8]*t[2]]},m=_([a,l,h]),g=_([c,d,p]);if(!m||!g)return null;const f=[m[0],m[1],m[2],g[0],g[1],g[2]];return f.every(Number.isFinite)?f:null}(s);return n?{matrix:n,matched:o,residual:cr(n,s)}:null}function gr(t,e,i){const s=t[t.length-1];if(s&&s[0]===e[0]&&s[1]===e[1])return t;if(t.push(e),t.length<=600)return t;let o=function(t,e){if(t.length<3)return t.slice();const i=new Uint8Array(t.length);i[0]=i[t.length-1]=1;const s=[[0,t.length-1]];for(;s.length;){const[o,n]=s.pop(),[r,a]=t[o],[l,h]=t[n],c=l-r,d=h-a,p=Math.hypot(c,d)||1e-9;let u=0,_=-1;for(let e=o+1;eu&&(u=i,_=e)}_>0&&u>e&&(i[_]=1,s.push([o,_],[_,n]))}const o=[];for(let e=0;e600&&(o=o.filter((t,e)=>e%2==0||e===o.length-1)),o}function fr(t){return"cleaning"===t||"returning"===t||"on"===t}const vr={0:[1,0],90:[0,1],180:[-1,0],270:[0,-1]};function yr(t){const[e,i]=vr[t.rot]||[1,0],s=t.mir?-1:1;return[t.s*e*s,-t.s*i,t.ox,t.s*i*s,t.s*e,t.oy]}function br(t,e,i,s){const[o,n]=hr(yr(e),i,s),r=yr({...t,ox:0,oy:0}),[a,l]=hr(r,i,s);return{...t,ox:o-a,oy:n-l}}function wr(t){const e=t?.trail_mode;return"never"===e||"cleaning"===e||"always"===e?e:!1===t?.trail?"never":"cleaning"}const $r={availability:"available",status:"alarm",activity:"none"},kr=new Set(["motion","vibration","sound"]),xr=new Set(["occupancy","presence"]),Sr=new Set(["door","window","garage_door","opening"]),Mr=new Set(["running","power"]),Cr=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Dr=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Pr=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping"]),zr=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Tr=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,Rr=t=>String(t??"").trim().toLowerCase();function Ar(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],o=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(o))return!0;if([i.translation_key,i.original_name,i.name].map(Rr).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const n=Rr(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(n)||/(?:^|[\s._-])питание$/.test(n)}function Nr(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=Rr(t?.[e]);if(Dr.has(i)||Pr.has(i))return i}return""}function Er(t,e){const i=t?.states?.[e],s=i?Rr(i.state):"__missing__",o=String(e||"").split(".")[0],n=Rr(i?.attributes?.device_class),r={eid:e,state:s,availability:Tr(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===r.availability)return r;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Xi.has(e))}(o,n,s))return{...r,status:"alarm"};if("binary_sensor"===o)return kr.has(n)?{...r,edge:"rising"}:xr.has(n)?{...r,activity:"on"===s?"presence":"none"}:Sr.has(n)?{...r,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===n?{...r,activity:"on"===s?"transition":"none"}:Mr.has(n)&&"on"===s?{...r,status:"working",activity:"running"}:r;if("cover"===o)return{...r,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===o)return{...r,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===o)return{...r,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===o){const t=Nr(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(Rr):[],o=!Pr.has(s)&&(zr.has(s)||Dr.has(s)||e.includes(s));return(t?Dr.has(t):o)?{...r,status:"working",activity:"running"}:r}if(["light","switch","fan","humidifier"].includes(o))return"on"===s?{...r,status:"working",activity:"running"}:r;if("media_player"===o)return"off"===s?{...r,availability:"unavailable"}:r;if("vacuum"===o)return"cleaning"===s?{...r,status:"working",activity:"running"}:"returning"===s?{...r,status:"working",activity:"transition"}:r;if("script"===o)return"on"===s?{...r,status:"working",activity:"running"}:r;if("automation"===o)return r;if("button"===o||"event"===o)return{...r,edge:"change"};const a=Nr(i.attributes);return Dr.has(a)||Dr.has(s)&&!Pr.has(s)?{...r,status:"working",activity:"running"}:r}function Ir(t,e){if(!(t=>!!t&&!Tr(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}function Or(t){const e={};for(const[i,s]of Object.entries(t.entities))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}function Fr(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Lr(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const qr=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Hr=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Ur(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),o=s.length?s:i;for(const t of qr){const e=o.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Hr(e).map(t=>t.eid)}const n=o.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=Rr(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return kr.has(i)||xr.has(i)||Sr.has(i)||Mr.has(i)||"moving"===i||Cr.has(i)}(t,e.eid));if(n.length)return Hr(n).map(t=>t.eid);const r=o.filter(t=>t.eid.startsWith("switch."));if(r.length){const e=Hr(r);return[(e.find(e=>Ar(t,e.eid))||e[0]).eid]}const a=[];for(const t of wt)a.push(...Hr(o.filter(e=>e.eid.startsWith(t+"."))));return a.push(...Hr(o.filter(t=>!wt.includes(t.eid.split(".")[0])))),a.map(t=>t.eid)}function Wr(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),o=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of o){const i=e.find(e=>Lr(t,e.eid));if(i)return i.eid}return Ur(t,e)[0]}function Br(t,e,i=[]){return[...new Set(jr(t,e,i))].filter(t=>Fi(t))}function jr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function Gr(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).some(t=>t.startsWith("switch.")&&s.has(t))}function Vr(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function Kr(t,e){const i=Br(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"}));if(!0===e.marker?.is_light||Gr(e.marker?.binding,e.marker?.controls,e.entities)){const t=function(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Fi),s=t.primary&&Fi(t.primary)?t.primary:null;return(e&&Fi(e)?e:null)||s||i[0]||null}(e);t&&!i.some(e=>e.eid===t)&&i.push({eid:t,via:"forced"})}if(i.length)return i;const s=e.primary||Ur(t,e.entities)[0];return s&&!s.startsWith("light.")?[]:e.entities.filter(t=>t.startsWith("light.")).map(t=>({eid:t,via:"light"}))}function Yr(t,e,i){const s=[],o=new Set;for(const n of e){if(n.hidden||null!=i&&!Vr(n,i))continue;const e=Kr(t,n);for(const{eid:i,via:r}of e)i&&!o.has(i)&&(o.add(i),s.push({eid:i,device:n,via:r,on:"on"===t.states[i]?.state}))}return s}function Jr(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function Zr(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const o=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===o){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const n=e.attributes?.linkquality??e.attributes?.lqi;if(null!=n){const t=parseFloat(n);isNaN(t)||i.push(t)}}return Qe(i)}function Xr(t,e){for(const i of e){if(!Lr(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function Qr(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function ta(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function ea(t,e){for(const i of e){if(!ta(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function ia(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden)continue;let o=null;if("group"===s.platform)o=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if("Group"!==e?.model)continue;o=e.area_id||s.area_id||null}}if(!o)continue;const n=t.states[e];i.push({eid:e,name:s.name||n?.attributes?.friendly_name||e,area:o})}return i}function sa(t,e,i,s,o){const n=bt(e,i,o);if(n!==yt)return n;const r=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&r.push(i)}return function(t){for(const e of t){const t=vt[e];if(t)return t}return null}(r)??yt}function oa(t){const e=new Set,i=new Set;for(const s of t||[]){if(!0!==s?.removed)continue;const t=String(s.binding||"").indexOf(":");if(t<1)continue;const o=s.binding.slice(0,t),n=s.binding.slice(t+1);n&&("device"===o?e.add(n):"entity"===o&&i.add(n))}return{devices:e,entities:i}}function na(t,e,i){if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function ra(t,e,i,s){const o=Br(e.binding,e.controls,t.entities).filter(t=>!na(i,t,s));t.marker=e,t.controls=o,e.hidden&&(t.hidden=!0),e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function aa(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,showAll:r,firstSpaceId:a,loc:l,iconRules:h}=t,c=!1!==o.group_lights,d=oa(s),p=ia(e,c).filter(t=>!na(e,t.eid,d)),u=new Set(p.map(t=>t.area)),_=Or(e),m=new Set;for(const t of s){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||m.add(t.binding)}const g=(t,e)=>s.find(i=>i.binding===t+":"+e),f={},v=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(m.has("device:"+t.id))continue;const a=g("device",t.id);if(a&&a.hidden&&!o.filter_seeded)continue;const d=_[t.id]||[],p=Fr(e,t,d),y=!o.filter_seeded;if(y&&!r){if(n.has(p))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===p&&t.via_device_id)continue}const b=(t.name_by_user||t.name||l("device.unnamed")).trim(),w=b+"|"+s;let $=sa(e,b,t.model,d,h);if(d.some(t=>t.startsWith("lock."))&&($="mdi:lock"),y&&!r&&c&&"mdi:lightbulb"===$&&u.has(s))continue;f[w]=(f[w]||0)+1;const k=f[w]>1?b+" "+f[w]:b,x={id:t.id,name:k,model:t.model||"",area:s,space:i[s],icon:$,entities:d,bindingKind:"device",bindingRef:t.id,pdfs:[]};x.primary=Wr(e,d,$),"mdi:thermometer"!==$&&"mdi:air-filter"!==$||(x.temp=Xr(e,d)),x.primary&&ta(e,x.primary)&&(x.hum=ea(e,d)),v.push(x)}for(const t of p)i[t.area]&&(m.has("entity:"+t.eid)||v.push({id:"lg_"+t.eid,name:t.name,model:l("device.light_group"),area:t.area,space:i[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of s){if(t.removed)continue;if(t.hidden&&!o.filter_seeded)continue;const[s,n]=t.binding.split(":");if("device"===s){const s=e.devices[n],o=t.area||s?.area_id||"",r=o&&i[o]||t.space||a,c=s&&_[s.id]||[];let p=s?sa(e,s.name_by_user||s.name||"",s.model,c,h):"mdi:help-circle";c.some(t=>t.startsWith("lock."))&&(p="mdi:lock");const u={id:t.id,name:s?.name_by_user||s?.name||l("device.fallback"),model:s?.model||"",area:o,space:r,icon:p,entities:c,bindingKind:"device",bindingRef:n};u.primary=Wr(e,c,p),"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Xr(e,c)),u.primary&&ta(e,u.primary)&&(u.hum=ea(e,c)),u.primary&&ta(e,u.primary)&&(u.hum=ea(e,c)),ra(u,t,e,d),v.push(u)}else if("entity"===s){if(na(e,n,d))continue;const s=e.entities[n],o=t.area||s?.area_id||s?.device_id&&e.devices[s.device_id]?.area_id||"",r=o&&i[o]||t.space||a,l=e.states[n],c=s?.name||l?.attributes?.friendly_name||n;let p=sa(e,c,"",[n],h);n.startsWith("lock.")&&(p="mdi:lock");const u={id:t.id,name:c,model:"",area:o,space:r,icon:p,entities:[n],primary:n,bindingKind:"entity",bindingRef:n};"mdi:thermometer"!==p&&"mdi:air-filter"!==p||(u.temp=Xr(e,[n])),ta(e,n)&&(u.hum=ea(e,[n])),ra(u,t,e,d),v.push(u)}else{const s=t.area||"",o=t.space||s&&i[s]||a,n={id:t.id,name:t.name||l("device.virtual"),model:t.model||"",area:s,space:o,icon:t.icon||"mdi:map-marker",entities:[],bindingKind:"virtual",virtual:!0};ra(n,t,e,d),v.push(n)}}return v}function la(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const o=e.slice(0,s),n=e.slice(s+1),r=oa(i);return"device"===o?r.devices.has(n):"entity"===o&&na(t,n,r)}(t,e,s))return null;const o=e.indexOf(":");if(o<0)return null;const n=e.slice(0,o),r=e.slice(o+1);if(!r)return null;if("entity"===n){const e=parseFloat(t.states[r]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===n){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===r).map(([t])=>t);return"temp"===i?Xr(t,e):ea(t,e)}return null}const ha=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");var ca={"btn.properties":"Properties","btn.keep_as_walls":"Keep as closed walls","title.markup_partition":"Partition: draw one independent physical wall","title.markup_select":"Select and edit walls, columns and saved outlines","title.markup_column":"Column: click a grid point to place a square column","markup.partition":"Partition","markup.column":"Column","markup.hint_partition":"two clicks draw one partition · Shift locks to 45°","markup.hint_column":"click a grid point to place a square column","history.draft_segment":"Add room-draft segment","history.draft_merge":"Join unfinished room outlines","history.draft_segment_delete":"Remove room-draft segment","history.partition_add":"Add partition","history.column_add":"Add column","history.physical_edit":"Change physical object","history.physical_delete":"Delete physical object","history.physical_move":"Move physical object","history.contour_to_partitions":"Convert contour to closed walls","toast.column_duplicate":"A column with the same centre and outer size already exists","confirm.delete_draft":"Delete the whole unfinished room contour?","confirm.delete_draft_segment":"Delete this draft segment? The remaining contour may split in two.","physical.partition_properties":"Partition properties","physical.column_properties":"Column properties","physical.draft_properties":"Draft segment properties","physical.shape":"Shape","physical.square":"Square","physical.circle":"Circle","physical.diameter":"Diameter","physical.side":"Side","physical.rotation":"Rotation angle","physical.length":"Length","physical.allowed_range":"Allowed: 1–{max} {unit}","physical.delete_segment":"Delete segment","physical.delete_draft":"Delete entire outline","physical.partition_size_title":"Thickness of the partition created by the second click (1–100 cm).","physical.column_size_title":"Outer side of the square column placed by the click (1–150 cm).","card.title":"House plan","count.devices":"{n} dev.","empty.no_spaces":"No spaces yet.","empty.add_first":"Add the first space and upload a floor plan.","empty.install":'Install the House Plan integration and add it in "Devices & services".',"btn.add_space":"Add space","btn.cancel":"Cancel","btn.save":"Save","btn.close":"Close","btn.delete":"Delete","btn.edit":"Edit","btn.open_in_ha":"Open in HA","btn.reset":"Reset","btn.attach":"Attach…","btn.upload":"Upload…","btn.replace":"Replace…","title.zoom_in":"Zoom in","title.zoom_out":"Zoom out","title.zoom_fit":"Fit all","title.add_device":"Add a device to the plan","title.show_all":"Show hidden devices (ghosted, this tab only)","title.markup":"Room markup: grid, lines, outlines","title.configure_space":"Configure space","title.add_space":"Add space","title.markup_add":"Room outline: connect grid dots until the room closes; Shift locks the direction in 45° steps","title.markup_merge":"Merge: click one room, then the neighbour it shares a wall with","title.markup_split":"Split a room: click the room, then two points on its walls","title.markup_delroom":"Delete room: click inside a room and confirm","title.markup_closewall":"Physical wall: click a dashed virtual stretch","title.need_plan":"Upload a floor-plan image","markup.add":"Room outline","markup.merge":"Merge","markup.split":"Split","markup.resize":"Resize","title.markup_resize":"Resize rooms: drag a wall handle; click a room for a corner scale frame","markup.hint_resize":"drag a wall handle · click a room — corner frame · Esc cancels a drag · Ctrl+Z — undo a step","markup.opening":"Opening","markup.closewall":"Physical wall","markup.delete_room":"Delete room","history.undo":"Undo","history.redo":"Redo","history.undo_named":"Undo: {name} (Ctrl+Z)","history.redo_named":"Redo: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Nothing to undo","history.redo_empty":"Nothing to redo","history.undone":"Undone: {name}","history.redone":"Redone: {name}","history.add_room":"Create room","history.split_room":"Split room","history.merge_rooms":"Merge rooms","history.resize_room":"Resize room","history.open_boundary":"Open boundary stretch","history.close_boundary":"Close boundary opening","history.wall_thickness":"Change wall thickness","history.add_opening":"Add door or window opening","history.edit_opening":"Edit door or window opening","history.move_opening":"Move door or window opening","history.delete_opening":"Delete door or window opening","history.delete_room":"Delete room","history.decor_add":"Add decor object","history.decor_edit":"Edit decor object","history.decor_move":"Move decor object","history.decor_transform":"Transform decor object","history.decor_delete":"Delete decor object","history.backdrop_transform":"Transform plan backdrop","title.markup_opening":"Doors & windows: click a wall to place, click an opening to edit","opening.new":"New opening","opening.edit":"Door / window","opening.door":"Door","opening.window":"Window","opening.type_label":"Type","opening.length_label":"Length, cm","opening.contact_label":"Open/close sensor","opening.lock_label":"Lock","opening.none":"— none —","opening.invert":"Invert open/closed","opening.flip_h":"Hinge on the other jamb","opening.flip_v":"Opens to the other side","opening.open":"Open","opening.closed":"Closed","opening.locked":"Locked","opening.unlocked":"Unlocked","opening.state_unknown":"unavailable","opening.no_entities":"No sensors bound — a static symbol on the plan.","toast.opening_no_wall":"Click next to a room wall — openings sit on walls","markup.delete":"Delete","markup.hint_points":"points: {n} · Shift — 45° steps · Esc/Ctrl+Z — undo a dot · first point or Ctrl/Cmd+click — close","markup.hint_start":"click a grid dot to start the outline","tip.lqi":"average zigbee signal:","tip.area":"area: {value}","info.device_header":"Device on the plan","info.model":"Model","info.state":"State","info.link":"Link","info.manuals":"Manuals","info.none":"No additional information","marker.new_device":"New device","marker.name_label":"Name (shown on the plan)","marker.name_ph":"Name","marker.binding_label":"Bind to an HA device","marker.virtual_option":"Virtual device (no binding)","marker.search_ph":"Search device / group…","marker.nothing_found":"nothing found","marker.room_label":"Room","marker.room_override":" (override placement)","marker.room_choose":"— select a room —","marker.room_auto":"— by device area (auto) —","marker.icon_label":"Icon","marker.icon_ph":"mdi:… (empty = auto)","marker.display_label":"Display","display.badge":"Icon","display.icon_ripple":"Icon + activity","marker.display_hint":"A yellow plate means actual work. Activity adds events, presence and movement; alarms are always visible.","marker.activity_color":"Effect color","marker.ripple_size":"Effect size","marker.size_label":"Icon size / rotation","marker.angle_label":"Rotate","marker.model_label":"Model","marker.model_ph":"e.g. Aqara T&H","marker.link_label":"Link","marker.desc_label":"Description","marker.desc_ph":"Notes, specs…","marker.manuals_label":"Manuals (PDF etc.)","marker.sub_device":"device","marker.sub_z2m_group":" · Z2M group","marker.sub_group":"group","marker.sub_helper":"helper","space.new":"New space","space.header":"Space","space.title_label":"Title","space.title_ph":"e.g. Garage","space.plan_label":"Floor plan (background)","space.no_plan":"no plan image","space.plan_alt":"plan","room.new":"New room","room.name_label":"Display name","room.name_ph":"e.g. Terrace","room.area_label":"Home Assistant area (unassigned)","room.no_area_option":"— no area —","room.default_name":"Room","device.unnamed":"unnamed","device.light_group":"light group","device.fallback":"device","device.virtual":"virtual device","confirm.delete_room":'Delete room "{name}"?',"confirm.merge_rooms":'Merge rooms "{a}" and "{b}"?',"confirm.remove_marker":'Delete "{name}" from the plan? The device will disappear completely and stop contributing to plan data. You can add it again later.',"confirm.erase_decor":"Erase the {kind} object? You can undo this action from the editor history.","confirm.delete_space":'Delete space "{title}" with all its rooms and markup?',"toast.pos_save_failed":"Failed to save position: {err}","toast.no_entity":"The device has no suitable entity","toast.markup_needs_server":"Markup is available after the config is moved to the server","toast.conflict":"Config was changed in another window — data refreshed, repeat your last action","toast.cfg_save_failed":"Failed to save config: {err}","toast.room_overlap":"The outline overlaps room “{name}” — rooms must not overlap","toast.contour_min_edges":"Draw at least two edges before closing the outline","toast.contour_cannot_close":"The outline cannot close because it is degenerate or intersects itself","toast.merge_not_adjacent":"Only rooms that share a wall can be merged","toast.rooms_merged":"Rooms merged into “{name}”","toast.split_pick_wall":"Start the cut on the room’s wall","toast.split_bad_cut":"The cut must run wall to wall inside the room, without crossing walls or itself","merge.header":"Merge rooms","merge.hint":"The merged room keeps one name and one area. The other area is released — its devices leave the plan until another room claims it.","merge.keep":"Keep","merge.no_area":"no area","room.split_header":"New room from the split","toast.room_saved":"Room saved ({n}). Devices added: {added}. Outline the next one or exit markup.","toast.room_saved_no_area":"Room saved ({n}, no area). Outline the next one or exit markup.","toast.marker_needs_server":"Device editing is available after the config is moved to the server","toast.virtual_name_required":"Enter a name for the virtual device","toast.marker_saved":"Device saved","toast.marker_removed":"Device deleted from the plan","toast.integration_missing":"The House Plan integration is not installed — management unavailable","toast.plan_formats":"Supported formats: SVG, PNG, JPG, WebP","toast.plan_required":"Upload a floor plan — it is required","toast.space_added_onboard":"Space added. Outline the rooms: click grid dots and close the contour.","toast.space_added":"Space added","toast.space_saved":"Space saved","toast.space_deleted":"Space deleted","toast.delete_failed":"Delete failed: {err}","toast.error":"Error: {err}","toast.file_failed":'File "{name}" was not uploaded: {err}',"toast.files_attached":"Files attached: {n}","err.unknown":"unknown error","err.code":"code {code}","err.too_large":"file larger than {mb} MB","err.bad_ext":"unsupported type (PDF/image expected)","err.unauthorized":"administrator rights required","editor.title":"Title","editor.default_floor":"Default space","editor.icon_size":"Icon size, % of plan width","editor.show_temperature":"Show temperature","editor.live_states":"Live states (on/off, open…)","editor.show_signal":"Show zigbee signal (LQI)","editor.language":"Interface language","editor.lang_auto":"Auto (HA profile)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Icon rules: which MDI icon devices get by name","rules.title":"Icon rules","rules.hint":"Rules are checked top-down against “device name + model” (case-insensitive regex); the first match wins. When nothing matches, the entity device class decides, then the generic chip icon.","rules.pattern_ph":"regex, e.g. plug|socket","rules.icon_ph":"mdi:power-socket-de","rules.add":"Add rule","rules.reset":"Reset to defaults","rules.test_ph":"Try a device name…","rules.invalid":"invalid regex","rules.saved":"Icon rules saved","btn.up":"Up","btn.down":"Down","tap.info":"Device card","tap.more_info":"HA more-info dialog","tap.toggle":"Toggle (lights/switches)","marker.tap_label":"Tap action for this device","tap.toggle_note":"Toggle never applies to locks and alarms; hold the icon to open the info card.","import.title":"Create spaces from HA floors","import.hint":"Your Home Assistant already knows these floors. Pick the ones to turn into plan spaces — you will upload a floor-plan image for each one next. Rooms are then outlined by hand on the plan.","import.start":"Create {n} space(s)","import.manual":"Start from scratch","import.progress":"Floor {i} of {n}","import.done":"Spaces created. Outline the rooms: click grid dots and close the contour.","btn.skip":"Skip","space.scale_label":"Scale (grid cell size)","space.scale_unit":"cm per cell","space.display_section":"Display","space.show_borders":"Always show room borders","space.show_names":"Show room names (drag to move)","space.room_color":"Border & name color","space.opacity":"Opacity","space.fill_label":"Room fill","fill.none":"None","fill.lqi":"Zigbee signal","fill.light":"Lights","space.source_file":"I have a floor-plan image","space.source_draw":"No image — I'll outline rooms by hand","space.orientation":"Canvas","orient.landscape":"Landscape","orient.portrait":"Portrait","orient.square":"Square","fill.temp":"Temperature","space.temp_min":"Comfort from","space.temp_max":"to","tip.temp_avg":"average temperature:","space_card.button":"Open the space plan","space_card.not_found":"Space “{id}” not found","space_card.loading":"Loading…","editor.space":"Space","editor.show_button":"Show button","editor.button_label":"Button label","editor.button_target":"Target dashboard path","marker.sub_entity":"entity","title.general_settings":"General settings","gs.title":"General settings","gs.hint":"Fill colors apply to every space; each color has its own opacity. Which fill mode a space uses is set in that space's dialog.","gs.light_group":"Fill: lights","gs.light_on":"Lights on","gs.light_off":"All lights off","gs.temp_group":"Fill: temperature","gs.temp_cold":"Cold","gs.temp_ok":"Comfortable","gs.temp_hot":"Hot","gs.lqi_group":"Fill: zigbee signal","gs.lqi_low":"Weak signal","gs.lqi_high":"Strong signal","gs.reset":"Reset to defaults","gs.saved":"General settings saved","space.show_lqi":"Show zigbee signal (LQI) next to devices","space.hide_decor":"Hide the decorative layer","space.hide_decor_tip":"Lines, shapes, labels and furniture stay where they are — visible in the backdrop editor, not on the plan.","space.hide_openings":"Hide doors and windows","space.hide_openings_tip":"The symbols are not drawn, but the openings keep working: light passes through, the sun comes in at a window, contact sensors still open them. The plan editor always shows them.","gs.light_none":"No light sources","mode.plan":"Plan editor","mode.devices":"Device editor","display.value":"Value instead of an icon","marker.subarea":"no area, manual","device.new":"New device — open its editor to dismiss","opening.unlock_action":"Unlock","opening.lock_action":"Lock","opening.lock_pending":"Working…","title.close_editor":"Close editor (back to view)","devbar.add":"Add","devbar.show_all":"Show hidden","devbar.rules":"Icon rules","space.roomcard_section":"Room card shows:","space.label_temp":"Temperature","space.label_hum":"Humidity","space.label_lqi":"Average Zigbee signal","space.label_light":"Lights on/off","roomcard.light_on":"On","roomcard.light_off":"Off","roomcard.light_partial":"{on} of {total}","toast.split_pick_inside":"Intermediate cut points must be inside the room","mode.decor":"Background editor","decor.select":"Select","decor.line":"Line","decor.rect":"Rectangle","decor.ellipse":"Oval","decor.text":"Text","decor.erase":"Erase","decor.erase_confirm_title":"Erase object?","decor.color":"Color","decor.width":"Line width","decor.fill":"Fill","decor.fill_color":"Fill color","decor.length":"Length","decor.size":"Size","decor.angle":"Rotation angle, °","decor.text_size":"Text size","decor.backdrop_properties":"Plan backdrop properties","decor.text_title":"Text label","decor.object_title":"Edit {kind}","decor.text_label":"Text","decor.live_group":"Insert HA variable","decor.live_entity":"Entity","decor.live_entity_ph":"choose an entity","decor.live_attr":"Value","decor.live_attr_ph":"choose state or attribute","decor.live_state":"State","decor.backdrop":"Backdrop image","decor.backdrop_hint":"Drag to move; pull a corner to resize; use the top handle to rotate. Shift changes proportions or frees the angle.","decor.backdrop_reset":"Reset the picture","decor.backdrop_reset_done":"The picture is back at its original place and size","marker.icon_auto":"Auto: {icon} (by icon rules; pick one to override)","marker.icon_pin_auto":"Pin","mode.plan_tip":"Plan editor — the geometry of the home: draw and split/merge rooms, bind them to HA areas, place doors and windows, move room cards, set the scale","mode.devices_tip":"Device editor — everything about icons: drag to position, click to edit binding/icon/display, add virtual devices, icon rules","mode.decor_tip":"Background editor — purely visual decor under the plan: lines, rectangles, ovals and text labels that never react to clicks","fill.glow":"Light sources (dark house, glowing lamps)","gs.glow_group":"Light-sources fill","gs.glow_base":"House darkness","gs.glow_light":"Default light color / intensity","gs.wall_group":"Walls","gs.wall_fill":"Wall fill","gs.glow_radius":"Glow radius","gs.unit_m":"m","gs.unit_ft":"ft","marker.controls_label":"Controls other light sources","marker.controls_hint":"With tap action “Toggle”, a click flips every source added here. The marker’s own bound entity is controlled directly and cannot be added; use the switch below to classify it as a light.","marker.controls_filter":"Search lights and switches…","info.controls":"Controls","marker.glow_radius_label":"Glow radius (light-sources fill)","marker.glow_radius_hint":"empty = default from general settings","markup.wallthick":"Thickness","markup.select":"Select","title.markup_wallthick":"Thickness — click a wall to set how thick it is. Empty or zero removes the thickness.","markup.hint_wallthick":"click a wall · Esc closes without applying","wallthick.field":"Thickness","wallthick.unit_cm":"cm","wallthick.unit_in":"in","wallthick.apply_room":"Apply to all walls of this room","markup.draw_wall_title":"Thickness of each new room-wall segment (1–100 cm). Shared walls keep the neighbour's value.","toast.wallthick_pick":"Click a wall (not an open boundary)","toast.wallthick_open":"Open boundaries have no thickness","toast.wallthick_set":"Wall thickness set","toast.wallthick_cleared":"Wall thickness removed","toast.physical_range":"Enter a value from 1 to {max} {unit}","toast.physical_angle":"Enter a rotation angle from 0° up to, but not including, 90°","toast.physical_limit":"The space has reached the limit for this type of geometry","markup.openwall":"Virtual wall","title.markup_openwall":"Virtual wall — click a point on a shared wall, then a second point on the same wall (within the nearest corners) to make that stretch virtual. Crosshair cursor.","toast.openwall_pick":"Click a wall shared by two rooms","toast.openwall_shared_only":"Only a shared wall between two rooms can be opened","toast.openwall_short":"Stretch too short — pick a farther point","toast.openwall_opened_span":"Virtual stretch opened","toast.openwall_closed_span":"Virtual stretch closed","toast.closewall_pick":"Click a dashed virtual stretch","toast.closewall_use_tool":"Use “Physical wall” for this stretch","toast.delete_room_pick":"Click inside the room you want to delete","toast.openwall_openings_removed":"Openings on the virtual stretch were removed","toast.openwall_opened":"Boundary “{a}” ↔ “{b}” is now open","toast.openwall_closed":"Boundary “{a}” ↔ “{b}” is closed again","toast.opening_on_virtual":"Openings cannot sit on a virtual wall","marker.from_ha_option":"Pick from the HA list","marker.show_entities":"Show entities","marker.show_entities_tip":"Adds not only devices to the list, but all their entities too","marker.pick_ph":"Choose a device…","room.open_area":"Open the HA area","kiosk.title":"This screen's sizes","kiosk.hint":"Stored on this device only — every wall tablet or TV can have its own comfortable sizes.","kiosk.icon_scale":"Device icon size","kiosk.font_scale":"Room card text size","editor.kiosk":"Wall device (kiosk) mode","editor.cycle":"Auto-switch spaces every N seconds (kiosk, 0 = off)","room.settings_title":"Room settings","room.settings_section":"Room settings (override the space)","room.fill_label":"Fill in THIS room","fill.inherit":"As the space","room.temp_src_label":"Temperature source","room.hum_src_label":"Humidity source","room.src_average":"Average over the room's sensors (default)","room.src_pick":"A specific HA device or entity","room.src_ph":"Choose a source…","toast.room_updated":"Room updated","space.card_font":"Room-card font size (whole space)","room.sizes_section":"Font sizes","room.name_scale":"Room name size","room.label_scale":"Metrics size","preview.room_name":"Living room","toast.cfg_reload_failed":"Could not reload the plan from the server: {err}","room.settings_short":"Room settings","room.unnamed":"Unnamed room","marker.use_climate_temp":"Use the device's temperature sensor","marker.use_climate_temp_tip":"Air conditioners and thermostats know the room temperature (current_temperature). Shows it as a badge next to the icon and counts it in the room average — like a regular thermometer.","marker.is_light":"This device is a light source","marker.is_light_tip":"Makes the icon glow in the “Light sources” fill even without a light entity — for a smart switch driving ordinary fixtures. The glow follows the switch (or the lights bound above).","confirm.unlock":"Unlock “{name}”?","toast.files_migrate_failed":"Attachments could not be moved to the new binding, links keep pointing at the old files: {err}","space.pick_saved":"Already uploaded","space.pick_saved_hint":"Plans stored on the server, including ones you detached earlier","space.no_saved":"No plans stored on the server yet.","space.loading":"Loading…","space.used_by":"in use: {list}","space.in_use":"A space still uses this plan — detach it first","btn.use":"Use","confirm.delete_plan":'Delete the plan file "{name}" from the server? This cannot be undone.',"toast.plans_list_failed":"Could not list the stored plans: {err}","toast.plan_delete_failed":"Could not delete the plan: {err}","marker.hide":"Hide","marker.hide_tip":'The device will disappear from the plan after saving but will still count toward the room signal. Restore it through "Show hidden" in the device editor.',"marker.show":"Show","marker.show_tip":"The device will appear on the plan again after saving.","marker.delete_tip":"Completely delete the device from the plan and every aggregate. You can add it again later.","tap.run":"Run automation/script/scene","tap.cover":"Open/close (curtains/blinds)","marker.run_target_label":"What to run","marker.run_search_ph":"Search: automation, script or scene…","marker.run_target_gone":"Target {id} not found — pick again","marker.tap_confirm":"Ask for confirmation","marker.tap_confirm_tip":"Show a confirmation dialog before acting — a guard against accidental taps.","run.automation":"automation","run.script":"script","run.scene":"scene","confirm.tap_run":'Run "{name}"?',"confirm.tap_toggle":'Toggle "{name}"?',"confirm.tap_cover":'Open/close "{name}"?',"toast.run_started":"Started: {name}","toast.run_target_missing":"Run target not found — check the device settings","toast.run_target_required":"Pick an automation, script or scene","btn.run":"Run","vac.section":"Robot vacuum: live position","vac.status_found":"Position source found: {name}","vac.status_none":"The integration reports no coordinates — the robot will only be shown at its base","vac.autocal":"Set up automatically","vac.live":"Live position on the plan","vac.trail":"Show the robot's path","vac.cal_maps":"Calibrated maps: {maps}","vac.autocal_no_rooms":"The integration reports no room list — open “Fit manually”","vac.autocal_no_match":"Room names did not match (need ≥3 in common) — open “Fit manually”","vac.autocal_res_warn":"Matched {rooms} rooms but the fit is rough — verify and refine via “Fit manually” if needed","vac.autocal_done":"Done: bound via {rooms} rooms. Start a cleanup and check","vac.cal_need_pos":"The robot is not reporting coordinates — start a cleanup and pause it","vac.cal_done":"Calibration saved. Start a cleanup and check","vac.cal_cancelled":"Calibration cancelled","vac.fit":"Fit manually","vac.fit_hint":"Drag the robot map into place, stretch by the corners","vac.fit_rotate":"Rotate 90°","vac.fit_mirror":"Mirror","vac.trail_never":"Never","vac.trail_cleaning":"While cleaning","vac.trail_always":"Always","gs.bg_group":"Stage background","gs.bg_color":"Background around the plan","gs.bg_default":"Theme default","gs.bg_theme":"theme default","gs.bg_mode":"Plan background","gs.bg_static":"Static color","gs.bg_daynight":"Follows the sun (day/night)","gs.bg_daynight_hint":"The stage follows the sun: neutral day, warm golden hour, deep night. Needs the compass below.","gs.sun_group":"Sun","gs.sun_missing":"The sun.sun entity was not found — the sun features stay off.","gs.north":"North on the plan","gs.north_ph":"not set","gs.north_hint":"Point the arrow at north (1° steps, 15° with Shift) or type the degrees — until then the sun features stay off.","gs.north_clear":"Clear","gs.north_letter":"N","gs.sun_rays":"Sunlight through windows","gs.weather":"Weather entity","gs.weather_ph":"weather.home (optional)","gs.about_group":"About","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · docs & issues","gs.about_telegram":"Telegram chat","space.bg_color":"Background around the plan","space.bg_inherit":"Inherit general","space.bg_inherited":"inherits general settings","space.bg_mode":"Plan background","space.north":"North on the plan (override)","space.north_inherited":"inherited: {v}","space.sun_rays":"Sunlight through windows","space.sun_inherit":"Inherit general","space.sun_on":"On","space.sun_off":"Off","canvas.far_objects":"{n} object(s) far from the plan","canvas.show_far":"Show","canvas.home_tip":"The plan is over there — click to fit it","gs.grid_group":"Plan maintenance","gs.grid_hint":"Updates data models, aligns plan elements to the grid and merges redundant wall fragments. An exact report is shown before anything is stored.","gs.align_all":"Optimize plans","gs.align_title":"Optimize plans","gs.align_none":"All plans already use the current optimized data model.","gs.align_count":"{n} of {total} elements will move, by at most {cm} cm.","gs.align_where":"The largest shift is in “{s}”.","gs.align_turned":"Openings whose angle is corrected: {n}.","gs.align_removed_drafts":"Invalid outlines collapsed by the grid and removed: {n}.","gs.optimize_changes":"Model migrations: {m}; plans canonicalized: {c}; merged real-wall fragments: {w}; virtual fragments: {s}.","gs.align_warn":"Elements deliberately placed between grid nodes will move. One undo is available after the operation, only until the next plan edit.","gs.align_run":"Optimize","gs.align_done":"Plans optimized: {n} elements moved, {m} records maintained","gs.optimize_undo":"Undo last optimization","gs.optimize_undone":"The last optimization was undone","decor.furniture":"Furniture","furn.title":"Furniture library","furn.symbol":"Symbol","furn.group_furniture":"Furniture","furn.group_appliance":"Appliances","furn.group_sanitary":"Plumbing","furn.group_other":"Other","furn.width":"Width","furn.depth":"Depth","furn.pick_hint":"Pick a symbol, then click on the plan.","furn.place_hint":"Click on the plan — the piece lands against the nearest wall. Shift places it free.","furn.sym_sofa":"Sofa","furn.sym_armchair":"Armchair","furn.sym_coffee_table":"Coffee table","furn.sym_table_dining":"Dining table","furn.sym_table_round":"Round table","furn.sym_chair":"Chair","furn.sym_desk":"Desk","furn.sym_bed_double":"Double bed","furn.sym_bed_single":"Single bed","furn.sym_nightstand":"Nightstand","furn.sym_wardrobe":"Wardrobe","furn.sym_bookshelf":"Bookshelf","furn.sym_fridge":"Fridge","furn.sym_stove":"Cooker","furn.sym_dishwasher":"Dishwasher","furn.sym_washer":"Washing machine","furn.sym_dryer":"Tumble dryer","furn.sym_tv":"TV","furn.sym_ac":"Air conditioner","furn.sym_water_heater":"Water heater","furn.sym_toilet":"Toilet","furn.sym_bathtub":"Bathtub","furn.sym_shower":"Shower","furn.sym_sink":"Washbasin","furn.sym_kitchen_sink":"Kitchen sink","furn.sym_bidet":"Bidet","furn.sym_stairs":"Stairs","furn.sym_fireplace":"Fireplace","furn.sym_plant":"Plant","furn.sym_rug":"Rug"};const da={en:ca,ru:{"btn.properties":"Свойства","btn.keep_as_walls":"Оставить замкнутыми стенами","title.markup_partition":"Перегородка: нарисуйте одну независимую физическую стену","title.markup_select":"Выбор и редактирование стен, колонн и сохранённых контуров","title.markup_column":"Колонна: кликните точку сетки для квадратной колонны","markup.partition":"Перегородка","markup.column":"Колонна","markup.hint_partition":"два клика рисуют одну перегородку · Shift фиксирует шаг 45°","markup.hint_column":"кликните точку сетки, чтобы поставить квадратную колонну","history.draft_segment":"Добавление сегмента черновика комнаты","history.draft_merge":"Соединение незавершённых контуров комнаты","history.draft_segment_delete":"Удаление сегмента черновика комнаты","history.partition_add":"Добавление перегородки","history.column_add":"Добавление колонны","history.physical_edit":"Изменение физического объекта","history.physical_delete":"Удаление физического объекта","history.physical_move":"Перемещение физического объекта","history.contour_to_partitions":"Преобразование контура в замкнутые стены","toast.column_duplicate":"Колонна с тем же центром и внешним размером уже существует","confirm.delete_draft":"Удалить весь незавершённый контур комнаты?","confirm.delete_draft_segment":"Удалить этот сегмент черновика? Оставшийся контур может разделиться на два.","physical.partition_properties":"Свойства перегородки","physical.column_properties":"Свойства колонны","physical.draft_properties":"Свойства сегмента черновика","physical.shape":"Форма","physical.square":"Квадрат","physical.circle":"Круг","physical.diameter":"Диаметр","physical.side":"Сторона","physical.rotation":"Угол поворота","physical.length":"Длина","physical.allowed_range":"Допустимо: 1–{max} {unit}","physical.delete_segment":"Удалить сегмент","physical.delete_draft":"Удалить весь контур","physical.partition_size_title":"Толщина перегородки, создаваемой вторым кликом (1–100 см).","physical.column_size_title":"Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).","card.title":"План дома","count.devices":"{n} устр.","empty.no_spaces":"Пространств пока нет.","empty.add_first":"Добавьте первое пространство и загрузите план этажа.","empty.install":"Установите интеграцию House Plan и добавьте запись в «Устройства и службы».","btn.add_space":"Добавить пространство","btn.cancel":"Отмена","btn.save":"Сохранить","btn.close":"Закрыть","btn.delete":"Удалить","btn.edit":"Редактировать","btn.open_in_ha":"Открыть в HA","btn.reset":"Сброс","btn.attach":"Прикрепить…","btn.upload":"Загрузить…","btn.replace":"Заменить…","title.zoom_in":"Приблизить","title.zoom_out":"Отдалить","title.zoom_fit":"Вписать всё","title.add_device":"Добавить устройство на план","title.show_all":"Показать скрытые устройства (полупрозрачными, только в этой вкладке)","title.markup":"Разметка комнат: сетка, линии, контуры","title.configure_space":"Настроить пространство","title.add_space":"Добавить пространство","title.markup_add":"Контур комнаты: соединяйте точки сетки линиями до замыкания комнаты; Shift фиксирует направление с шагом 45°","title.markup_merge":"Объединить: клик по одной комнате, затем по соседней с общей стеной","title.markup_split":"Разделить комнату: клик по комнате, затем две точки на её стенах","title.markup_delroom":"Удалить комнату: кликните внутри комнаты и подтвердите удаление","title.markup_closewall":"Физическая стена: кликните по пунктирному виртуальному участку","title.need_plan":"Загрузите подложку (план этажа)","markup.add":"Контур комнаты","markup.merge":"Объединить","markup.split":"Разделить","markup.resize":"Размер","title.markup_resize":"Изменение размера комнат: тяните ручку стены; клик по комнате — угловая рамка масштаба","markup.hint_resize":"тяните ручку стены · клик по комнате — угловая рамка · Esc отменяет перетаскивание · Ctrl+Z — отмена шага","markup.opening":"Проём","markup.closewall":"Физическая стена","markup.delete_room":"Удалить комнату","history.undo":"Отменить","history.redo":"Повторить","history.undo_named":"Отменить: {name} (Ctrl+Z)","history.redo_named":"Повторить: {name} (Ctrl+Shift+Z / Ctrl+Y)","history.undo_empty":"Нет операций для отмены","history.redo_empty":"Нет операций для повтора","history.undone":"Отменено: {name}","history.redone":"Повторено: {name}","history.add_room":"Создание комнаты","history.split_room":"Разделение комнаты","history.merge_rooms":"Объединение комнат","history.resize_room":"Изменение размера комнаты","history.open_boundary":"Открытие участка границы","history.close_boundary":"Закрытие проёма в границе","history.wall_thickness":"Изменение толщины стены","history.add_opening":"Добавление дверного или оконного проёма","history.edit_opening":"Изменение дверного или оконного проёма","history.move_opening":"Перемещение дверного или оконного проёма","history.delete_opening":"Удаление дверного или оконного проёма","history.delete_room":"Удаление комнаты","history.decor_add":"Добавление объекта декора","history.decor_edit":"Изменение объекта декора","history.decor_move":"Перемещение объекта декора","history.decor_transform":"Трансформация объекта декора","history.decor_delete":"Удаление объекта декора","history.backdrop_transform":"Трансформация подложки плана","title.markup_opening":"Двери и окна: клик по стене — добавить, клик по проёму — редактировать","opening.new":"Новый проём","opening.edit":"Дверь / окно","opening.door":"Дверь","opening.window":"Окно","opening.type_label":"Тип","opening.length_label":"Длина, см","opening.contact_label":"Датчик открытия","opening.lock_label":"Замок","opening.none":"— нет —","opening.invert":"Инвертировать открыто/закрыто","opening.flip_h":"Петли с другой стороны","opening.flip_v":"Открывается в другую сторону","opening.open":"Открыто","opening.closed":"Закрыто","opening.locked":"Заперто","opening.unlocked":"Не заперто","opening.state_unknown":"недоступно","opening.no_entities":"Датчики не привязаны — статичный символ на плане.","toast.opening_no_wall":"Кликните рядом со стеной комнаты — проёмы ставятся на стены","markup.delete":"Удалить","markup.hint_points":"точек: {n} · Shift — шаг 45° · Esc/Ctrl+Z — убрать точку · клик по первой или Ctrl/Cmd+клик — замкнуть","markup.hint_start":"кликните точку сетки, чтобы начать контур","tip.lqi":"средний сигнал zigbee:","tip.area":"площадь: {value}","info.device_header":"Устройство на плане","info.model":"Модель","info.state":"Состояние","info.link":"Ссылка","info.manuals":"Инструкции","info.none":"Нет дополнительной информации","marker.new_device":"Новое устройство","marker.name_label":"Имя (отображается на плане)","marker.name_ph":"Название","marker.binding_label":"Привязка к устройству HA","marker.virtual_option":"Виртуальное устройство (без привязки)","marker.search_ph":"Поиск устройства / группы…","marker.nothing_found":"ничего не найдено","marker.room_label":"Комната","marker.room_override":" (переопределить размещение)","marker.room_choose":"— выберите комнату —","marker.room_auto":"— по зоне устройства (авто) —","marker.icon_label":"Иконка","marker.icon_ph":"mdi:… (пусто = авто)","marker.display_label":"Отображение","display.badge":"Значок","display.icon_ripple":"Значок + активность","marker.display_hint":"Жёлтая подложка означает фактическую работу. Активность добавляет события, присутствие и движение; тревога видна всегда.","marker.activity_color":"Цвет эффекта","marker.ripple_size":"Размер эффекта","marker.size_label":"Размер / поворот значка","marker.angle_label":"Поворот","marker.model_label":"Модель","marker.model_ph":"напр. Aqara T&H","marker.link_label":"Ссылка","marker.desc_label":"Описание","marker.desc_ph":"Заметки, характеристики…","marker.manuals_label":"Инструкции (PDF и т.п.)","marker.sub_device":"устройство","marker.sub_z2m_group":" · Z2M-группа","marker.sub_group":"группа","marker.sub_helper":"хелпер","space.new":"Новое пространство","space.header":"Пространство","space.title_label":"Название","space.title_ph":"Например: Гараж","space.plan_label":"Подложка (план)","space.no_plan":"нет подложки","space.plan_alt":"план","room.new":"Новая комната","room.name_label":"Отображаемое имя","room.name_ph":"Например: Терраса","room.area_label":"Зона Home Assistant (свободные)","room.no_area_option":"— без зоны —","room.default_name":"Комната","device.unnamed":"без имени","device.light_group":"группа света","device.fallback":"устройство","device.virtual":"виртуальное устройство","confirm.delete_room":"Удалить комнату «{name}»?","confirm.merge_rooms":"Слить комнаты «{a}» и «{b}»?","confirm.remove_marker":"Удалить «{name}» с плана? Устройство исчезнет полностью и перестанет участвовать в данных плана. Позже его можно будет добавить заново.","confirm.erase_decor":"Стереть объект «{kind}»? Действие можно отменить из истории редактора.","confirm.delete_space":"Удалить пространство «{title}» со всеми комнатами и разметкой?","toast.pos_save_failed":"Не удалось сохранить позицию: {err}","toast.no_entity":"У устройства нет подходящей сущности","toast.markup_needs_server":"Разметка доступна после переноса конфига на сервер","toast.conflict":"Конфиг изменён в другом окне — данные обновлены, повторите последнее действие","toast.cfg_save_failed":"Не удалось сохранить конфиг: {err}","toast.room_overlap":"Контур накладывается на комнату «{name}» — комнаты не должны накладываться","toast.contour_min_edges":"Чтобы замкнуть контур, сначала нарисуйте минимум две грани","toast.contour_cannot_close":"Контур нельзя замкнуть: он вырожден или пересекает сам себя","toast.merge_not_adjacent":"Объединять можно только комнаты с общей стеной","toast.rooms_merged":"Комнаты объединены в «{name}»","toast.split_pick_wall":"Начните разрез на стене комнаты","toast.split_bad_cut":"Разрез — от стены до стены внутри комнаты, без пересечения стен и самого себя","merge.header":"Объединение комнат","merge.hint":"У объединённой комнаты одно имя и одна зона. Вторая зона освобождается — её устройства уйдут с плана, пока их не заберёт другая комната.","merge.keep":"Оставить","merge.no_area":"без зоны","room.split_header":"Новая комната после разделения","toast.room_saved":"Комната сохранена ({n}). Устройств добавлено: {added}. Обведите следующую или выйдите из разметки.","toast.room_saved_no_area":"Комната сохранена ({n}, без зоны). Обведите следующую или выйдите из разметки.","toast.marker_needs_server":"Редактирование устройств доступно после переноса конфига на сервер","toast.virtual_name_required":"Укажите имя виртуального устройства","toast.marker_saved":"Устройство сохранено","toast.marker_removed":"Устройство удалено с плана","toast.integration_missing":"Интеграция House Plan не установлена — управление недоступно","toast.plan_formats":"Поддерживаются SVG, PNG, JPG, WebP","toast.plan_required":"Загрузите подложку — план этажа обязателен","toast.space_added_onboard":"Пространство добавлено. Обведите комнаты: кликайте по точкам сетки и замкните контур.","toast.space_added":"Пространство добавлено","toast.space_saved":"Пространство сохранено","toast.space_deleted":"Пространство удалено","toast.delete_failed":"Ошибка удаления: {err}","toast.error":"Ошибка: {err}","toast.file_failed":"Файл «{name}» не загружен: {err}","toast.files_attached":"Прикреплено файлов: {n}","err.unknown":"неизвестная ошибка","err.code":"код {code}","err.too_large":"файл больше {mb} МБ","err.bad_ext":"недопустимый тип (нужен PDF/изображение)","err.unauthorized":"нужны права администратора","editor.title":"Заголовок","editor.default_floor":"Пространство по умолчанию","editor.icon_size":"Размер иконок, % ширины плана","editor.show_temperature":"Показывать температуру","editor.live_states":"Живые состояния (вкл/выкл, открыто…)","editor.show_signal":"Показывать сигнал zigbee (LQI)","editor.language":"Язык интерфейса","editor.lang_auto":"Авто (профиль HA)","editor.lang_en":"English","editor.lang_ru":"Русский","title.icon_rules":"Правила иконок: какая MDI-иконка достаётся устройству по имени","rules.title":"Правила иконок","rules.hint":"Правила проверяются сверху вниз по строке «имя устройства + модель» (regex без учёта регистра); срабатывает первое совпадение. Если ничего не подошло — решает device class сущности, затем — иконка-заглушка.","rules.pattern_ph":"regex, напр. розетк|plug","rules.icon_ph":"mdi:power-socket-de","rules.add":"Добавить правило","rules.reset":"Сбросить к умолчаниям","rules.test_ph":"Проверьте имя устройства…","rules.invalid":"некорректный regex","rules.saved":"Правила иконок сохранены","btn.up":"Вверх","btn.down":"Вниз","tap.info":"Карточка устройства","tap.more_info":"Диалог HA (more-info)","tap.toggle":"Переключить (свет/розетки)","marker.tap_label":"Действие по нажатию для этого устройства","tap.toggle_note":"Toggle никогда не применяется к замкам и сигнализациям; долгое нажатие всегда открывает инфо-карточку.","import.title":"Создать пространства из этажей HA","import.hint":"Home Assistant уже знает эти этажи. Отметьте, какие превратить в пространства плана — далее для каждого попросим картинку плана. Комнаты затем обводятся вручную по плану.","import.start":"Создать: {n}","import.manual":"Начать с нуля","import.progress":"Этаж {i} из {n}","import.done":"Пространства созданы. Обведите комнаты: кликайте по точкам сетки и замкните контур.","btn.skip":"Пропустить","space.scale_label":"Масштаб (размер клетки сетки)","space.scale_unit":"см на клетку","space.display_section":"Отображение","space.show_borders":"Всегда отображать границы комнат","space.show_names":"Отображать названия комнат (перетаскиваются)","space.room_color":"Цвет границ и названий","space.opacity":"Прозрачность","space.fill_label":"Заливка комнат","fill.none":"Нет","fill.lqi":"По силе зигби-сигнала","fill.light":"По освещению","space.source_file":"У меня есть картинка плана","space.source_draw":"Нет подложки — нарисую комнаты вручную","space.orientation":"Холст","orient.landscape":"Альбомный","orient.portrait":"Портретный","orient.square":"Квадрат","fill.temp":"По температуре","space.temp_min":"Комфорт от","space.temp_max":"до","tip.temp_avg":"средняя температура:","space_card.button":"Перейти к пространству","space_card.not_found":"Пространство «{id}» не найдено","space_card.loading":"Загрузка…","editor.space":"Пространство","editor.show_button":"Показывать кнопку","editor.button_label":"Текст кнопки","editor.button_target":"Путь дашборда (куда вести)","marker.sub_entity":"сущность","title.general_settings":"Общие настройки","gs.title":"Общие настройки","gs.hint":"Цвета заливок действуют на все пространства; у каждого цвета своя прозрачность. Какой режим заливки использует пространство — задаётся в его диалоге.","gs.light_group":"Заливка: освещение","gs.light_on":"Свет включён","gs.light_off":"Весь свет выключен","gs.temp_group":"Заливка: температура","gs.temp_cold":"Холодно","gs.temp_ok":"Комфорт","gs.temp_hot":"Жарко","gs.lqi_group":"Заливка: зигби-сигнал","gs.lqi_low":"Слабый сигнал","gs.lqi_high":"Сильный сигнал","gs.reset":"Сбросить к умолчаниям","gs.saved":"Общие настройки сохранены","space.show_lqi":"Показывать зигби-сигнал (LQI) у устройств","space.hide_decor":"Скрыть декоративный слой","space.hide_decor_tip":"Линии, фигуры, надписи и мебель остаются на месте — их видно в редакторе подложки, но не на плане.","space.hide_openings":"Скрыть проёмы","space.hide_openings_tip":"Двери и окна не рисуются, но продолжают работать: свет проходит, солнце светит в окна, датчики открытия срабатывают. В редакторе плана проёмы видно всегда.","gs.light_none":"Нет источников света","mode.plan":"Редактор плана","mode.devices":"Редактор устройств","display.value":"Значение вместо иконки","marker.subarea":"без зоны, вручную","device.new":"Новое устройство — откройте его редактор, чтобы снять отметку","opening.unlock_action":"Открыть замок","opening.lock_action":"Закрыть замок","opening.lock_pending":"Выполняется…","title.close_editor":"Закрыть редактор (вернуться к просмотру)","devbar.add":"Добавить","devbar.show_all":"Показать скрытые","devbar.rules":"Правила иконок","space.roomcard_section":"В карточке комнаты:","space.label_temp":"Температура","space.label_hum":"Влажность","space.label_lqi":"Средний Zigbee-сигнал","space.label_light":"Свет вкл/выкл","roomcard.light_on":"Вкл","roomcard.light_off":"Выкл","roomcard.light_partial":"{on} из {total}","toast.split_pick_inside":"Промежуточные точки разреза — внутри комнаты","mode.decor":"Редактор подложки","decor.select":"Выбрать","decor.line":"Линия","decor.rect":"Прямоугольник","decor.ellipse":"Овал","decor.text":"Надпись","decor.erase":"Стереть","decor.erase_confirm_title":"Стереть объект?","decor.color":"Цвет","decor.width":"Толщина линии","decor.fill":"Заливка","decor.fill_color":"Цвет заливки","decor.length":"Длина","decor.size":"Размер","decor.angle":"Угол поворота, °","decor.text_size":"Размер текста","decor.backdrop_properties":"Свойства картинки-подложки","decor.text_title":"Надпись","decor.object_title":"Редактирование: {kind}","decor.text_label":"Текст","decor.live_group":"Вставить переменную HA","decor.live_entity":"Сущность","decor.live_entity_ph":"выберите сущность","decor.live_attr":"Значение","decor.live_attr_ph":"выберите состояние или атрибут","decor.live_state":"Состояние","decor.backdrop":"Картинка-подложка","decor.backdrop_hint":"Тяните картинку — перемещение; угол — размер; верхнюю ручку — поворот. Shift меняет пропорции или снимает шаг угла.","decor.backdrop_reset":"Вернуть картинку","decor.backdrop_reset_done":"Картинка вернулась на своё место и в свой размер","marker.icon_auto":"Авто: {icon} (по правилам иконок; выберите свою, чтобы заменить)","marker.icon_pin_auto":"Закрепить","mode.plan_tip":"Редактор плана — геометрия дома: рисование и объединение/разделение комнат, привязка к зонам HA, двери и окна, карточки комнат, масштаб","mode.devices_tip":"Редактор устройств — всё про значки: перетаскивание, клик — настройка привязки/иконки/отображения, виртуальные устройства, правила иконок","mode.decor_tip":"Редактор подложки — чисто визуальный декор под планом: линии, прямоугольники, овалы и надписи, не реагирующие на клики","fill.glow":"Свет по источникам (тёмный дом, пятна света)","gs.glow_group":"Заливка «Свет по источникам»","gs.glow_base":"Темнота дома","gs.glow_light":"Цвет света по умолчанию / интенсивность","gs.wall_group":"Стены","gs.wall_fill":"Цвет заливки стен","gs.glow_radius":"Радиус свечения","gs.unit_m":"м","gs.unit_ft":"фут","marker.controls_label":"Управляет другими источниками света","marker.controls_hint":"При действии «Переключить» клик разом переключает все добавленные здесь источники. Собственная сущность маркера управляется напрямую и сюда не добавляется; чтобы считать её светом, включите флаг ниже.","marker.controls_filter":"Поиск ламп и выключателей…","info.controls":"Управляет","marker.glow_radius_label":"Радиус свечения (заливка «Свет по источникам»)","marker.glow_radius_hint":"пусто = по умолчанию из общих настроек","markup.wallthick":"Толщина","markup.select":"Выбрать","title.markup_wallthick":"Толщина — клик по стене задаёт толщину. Пустое поле или ноль убирает толщину.","markup.hint_wallthick":"клик по стене · Esc закрывает без применения","wallthick.field":"Толщина","wallthick.unit_cm":"см","wallthick.unit_in":"дюйм","wallthick.apply_room":"Применить ко всем стенам комнаты","markup.draw_wall_title":"Толщина каждого нового сегмента стены комнаты (1–100 см). Общие стены сохраняют значение соседа.","toast.wallthick_pick":"Кликните по стене (не по открытой границе)","toast.wallthick_open":"У открытой границы нет толщины","toast.wallthick_set":"Толщина стены задана","toast.wallthick_cleared":"Толщина стены убрана","toast.physical_range":"Введите значение от 1 до {max} {unit}","toast.physical_angle":"Введите угол поворота от 0° до 90°, не включая 90°","toast.physical_limit":"В пространстве достигнут лимит объектов этого типа","markup.openwall":"Виртуальная стена","title.markup_openwall":"Виртуальная стена — клик по точке на общей стене, затем второй клик на той же стене (не дальше ближайших углов) открывает виртуальный участок. Курсор — крестик.","toast.openwall_pick":"Кликните по стене, разделяющей две комнаты","toast.openwall_shared_only":"Открыть можно только общую стену двух комнат","toast.openwall_short":"Слишком короткий отрезок — кликните дальше","toast.openwall_opened_span":"Виртуальный отрезок открыт","toast.openwall_closed_span":"Виртуальный отрезок закрыт","toast.closewall_pick":"Кликните по пунктирному виртуальному участку","toast.closewall_use_tool":"Для этого участка выберите «Физическая стена»","toast.delete_room_pick":"Кликните внутри комнаты, которую нужно удалить","toast.openwall_openings_removed":"Проёмы на виртуальном отрезке удалены","toast.openwall_opened":"Граница «{a}» ↔ «{b}» теперь открыта","toast.openwall_closed":"Граница «{a}» ↔ «{b}» снова закрыта","toast.opening_on_virtual":"Проёмы на виртуальной стене запрещены","marker.from_ha_option":"Выбрать из списка HA","marker.show_entities":"Отображать сущности","marker.show_entities_tip":"Добавляет в список не только устройства, но и все их сущности","marker.pick_ph":"Выберите устройство…","room.open_area":"Открыть зону в HA","kiosk.title":"Размеры на этом экране","kiosk.hint":"Хранится только на этом устройстве — у каждого настенного планшета или ТВ свои удобные размеры.","kiosk.icon_scale":"Размер значков устройств","kiosk.font_scale":"Размер текста карточек комнат","editor.kiosk":"Режим настенного устройства (киоск)","editor.cycle":"Автосмена пространств каждые N секунд (киоск, 0 = выкл)","room.settings_title":"Настройки комнаты","room.settings_section":"Настройки комнаты (переопределяют пространство)","room.fill_label":"Заливка в ЭТОЙ комнате","fill.inherit":"Как у пространства","room.temp_src_label":"Источник температуры","room.hum_src_label":"Источник влажности","room.src_average":"Средняя по датчикам комнаты (по умолчанию)","room.src_pick":"Конкретное устройство или сущность HA","room.src_ph":"Выберите источник…","toast.room_updated":"Комната обновлена","space.card_font":"Размер шрифта карточек комнат (всё пространство)","room.sizes_section":"Размеры шрифтов","room.name_scale":"Размер названия","room.label_scale":"Размер подписей","preview.room_name":"Гостиная","toast.cfg_reload_failed":"Не удалось перечитать план с сервера: {err}","room.settings_short":"Настройки комнаты","room.unnamed":"Комната без имени","marker.use_climate_temp":"Использовать датчик температуры устройства","marker.use_climate_temp_tip":"Кондиционеры и термостаты знают температуру комнаты (current_temperature). Показывает её плашкой у значка и учитывает в средней температуре комнаты — как обычный термометр.","marker.is_light":"Это устройство — источник света","marker.is_light_tip":"Даёт ореол в заливке «Свет по источникам» даже без light-сущности — для умного выключателя с обычными светильниками. Ореол следует за выключателем (или за привязанными выше лампами).","confirm.unlock":"Открыть замок «{name}»?","toast.files_migrate_failed":"Не удалось перенести вложения к новой привязке, ссылки остались на старые файлы: {err}","space.pick_saved":"Уже загруженные","space.pick_saved_hint":"Планы, сохранённые на сервере, включая отцеплённые ранее","space.no_saved":"На сервере пока нет сохранённых планов.","space.loading":"Загрузка…","space.used_by":"используется: {list}","space.in_use":"План используется пространством — сначала отцепите его","btn.use":"Выбрать","confirm.delete_plan":"Удалить файл плана «{name}» с сервера? Действие необратимо.","toast.plans_list_failed":"Не удалось получить список планов: {err}","toast.plan_delete_failed":"Не удалось удалить план: {err}","marker.hide":"Скрыть","marker.hide_tip":"Устройство исчезнет с плана после сохранения, но продолжит участвовать в расчёте сигнала комнаты. Вернуть его можно через кнопку «Показать скрытые» в редакторе устройств.","marker.show":"Показать","marker.show_tip":"Устройство снова появится на плане после сохранения.","marker.delete_tip":"Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.","tap.run":"Запустить автоматизацию/скрипт/сцену","tap.cover":"Открыть/закрыть (шторы/жалюзи)","marker.run_target_label":"Что запускать","marker.run_search_ph":"Поиск: автоматизация, скрипт или сцена…","marker.run_target_gone":"Цель {id} не найдена — выберите заново","marker.tap_confirm":"Спрашивать подтверждение","marker.tap_confirm_tip":"Перед выполнением показать диалог подтверждения — защита от случайных нажатий.","run.automation":"автоматизация","run.script":"скрипт","run.scene":"сцена","confirm.tap_run":"Запустить «{name}»?","confirm.tap_toggle":"Переключить «{name}»?","confirm.tap_cover":"Открыть/закрыть «{name}»?","toast.run_started":"Запущено: {name}","toast.run_target_missing":"Цель запуска не найдена — проверьте настройки устройства","toast.run_target_required":"Выберите автоматизацию, скрипт или сцену","btn.run":"Выполнить","vac.section":"Робот-пылесос: живая позиция","vac.status_found":"Источник координат найден: {name}","vac.status_none":"Интеграция не отдаёт координаты — робот будет показан только на базе","vac.autocal":"Настроить автоматически","vac.live":"Живая позиция на плане","vac.trail":"Показывать путь робота","vac.cal_maps":"Откалиброваны карты: {maps}","vac.autocal_no_rooms":"Интеграция не отдаёт список комнат — откройте «Подогнать вручную»","vac.autocal_no_match":"Не совпали имена комнат (нужно ≥3 общих) — откройте «Подогнать вручную»","vac.autocal_res_warn":"Совпало комнат: {rooms}, но привязка грубовата — проверьте и при необходимости откройте «Подогнать вручную»","vac.autocal_done":"Готово: привязка по {rooms} комнатам. Запустите уборку и проверьте","vac.cal_need_pos":"Робот сейчас не отдаёт координаты — запустите уборку и поставьте на паузу","vac.cal_done":"Калибровка сохранена. Запустите уборку и проверьте","vac.cal_cancelled":"Калибровка отменена","vac.fit":"Подогнать вручную","vac.fit_hint":"Перетащите карту робота на место, растяните за уголки","vac.fit_rotate":"Повернуть 90°","vac.fit_mirror":"Отразить","vac.trail_never":"Не показывать никогда","vac.trail_cleaning":"Во время уборки","vac.trail_always":"Показывать всегда","gs.bg_group":"Фон сцены","gs.bg_color":"Цвет фона вокруг плана","gs.bg_default":"Как в теме","gs.bg_theme":"по умолчанию — из темы","gs.bg_mode":"Фон плана","gs.bg_static":"Статичный цвет","gs.bg_daynight":"Следует за солнцем (день/ночь)","gs.bg_daynight_hint":"Фон следует за солнцем: нейтральный день, тёплый золотой час, глубокая ночь. Нужен компас ниже.","gs.sun_group":"Солнце","gs.sun_missing":"Сущность sun.sun не найдена — солнечные функции выключены.","gs.north":"Север на плане","gs.north_ph":"не задан","gs.north_hint":"Направьте стрелку на север (шаг 1°, с Shift 15°) или введите градусы — до этого солнечные функции молчат.","gs.north_clear":"Сбросить","gs.north_letter":"С","gs.sun_rays":"Солнце в окнах","gs.weather":"Сущность погоды","gs.weather_ph":"weather.home (необязательно)","gs.about_group":"О карточке","gs.about_version":"Houseplan Card v{v}","gs.about_github":"GitHub · документация и issues","gs.about_telegram":"Чат в Telegram","space.bg_color":"Цвет фона вокруг плана","space.bg_inherit":"Наследовать общий","space.bg_inherited":"наследуется из общих настроек","space.bg_mode":"Фон плана","space.north":"Север на плане (переопределение)","space.north_inherited":"наследуется: {v}","space.sun_rays":"Солнце в окнах","space.sun_inherit":"Наследовать общий","space.sun_on":"Вкл","space.sun_off":"Выкл","canvas.far_objects":"Объектов далеко от плана: {n}","canvas.show_far":"Показать","canvas.home_tip":"План там — нажмите, чтобы вписать","gs.grid_group":"Обслуживание планов","gs.grid_hint":"Обновляет модели данных, выравнивает элементы по сетке и объединяет лишние фрагменты стен. Перед записью будет показан точный отчёт.","gs.align_all":"Оптимизировать планы","gs.align_title":"Оптимизировать планы","gs.align_none":"Все планы уже используют актуальную и оптимальную модель данных.","gs.align_count":"Сдвинется элементов: {n} из {total}, максимум на {cm} см.","gs.align_where":"Наибольший сдвиг — в пространстве «{s}».","gs.align_turned":"Проёмов с исправлением угла: {n}.","gs.align_removed_drafts":"Схлопнувшиеся на сетке некорректные контуры удалены: {n}.","gs.optimize_changes":"Миграций модели: {m}; канонизировано планов: {c}; объединено отрезков реальных стен: {w}; виртуальных: {s}.","gs.align_warn":"Элементы, намеренно поставленные между узлами, будут сдвинуты. После операции доступна одна отмена — только до следующего изменения плана.","gs.align_run":"Оптимизировать","gs.align_done":"Планы оптимизированы: сдвинуто элементов — {n}, обслужено записей — {m}","gs.optimize_undo":"Отменить последнюю оптимизацию","gs.optimize_undone":"Последняя оптимизация отменена","decor.furniture":"Мебель","furn.title":"Библиотека мебели","furn.symbol":"Символ","furn.group_furniture":"Мебель","furn.group_appliance":"Техника","furn.group_sanitary":"Сантехника","furn.group_other":"Прочее","furn.width":"Ширина","furn.depth":"Глубина","furn.pick_hint":"Выберите символ и кликните по плану.","furn.place_hint":"Кликните по плану — предмет встанет к ближайшей стене. Shift — свободно.","furn.sym_sofa":"Диван","furn.sym_armchair":"Кресло","furn.sym_coffee_table":"Журнальный столик","furn.sym_table_dining":"Обеденный стол","furn.sym_table_round":"Круглый стол","furn.sym_chair":"Стул","furn.sym_desk":"Письменный стол","furn.sym_bed_double":"Двуспальная кровать","furn.sym_bed_single":"Односпальная кровать","furn.sym_nightstand":"Тумбочка","furn.sym_wardrobe":"Шкаф","furn.sym_bookshelf":"Стеллаж","furn.sym_fridge":"Холодильник","furn.sym_stove":"Плита","furn.sym_dishwasher":"Посудомоечная машина","furn.sym_washer":"Стиральная машина","furn.sym_dryer":"Сушильная машина","furn.sym_tv":"Телевизор","furn.sym_ac":"Кондиционер","furn.sym_water_heater":"Бойлер","furn.sym_toilet":"Унитаз","furn.sym_bathtub":"Ванна","furn.sym_shower":"Душ","furn.sym_sink":"Раковина","furn.sym_kitchen_sink":"Кухонная мойка","furn.sym_bidet":"Биде","furn.sym_stairs":"Лестница","furn.sym_fireplace":"Камин","furn.sym_plant":"Растение","furn.sym_rug":"Ковёр"}};function pa(t,e){if(e&&e in da)return e;return(t?.locale?.language||t?.language||"en").toLowerCase().startsWith("ru")?"ru":"en"}function ua(t,e,i){return wi(da[t][e]??ca[e]??e,i)}class _a extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[],e=this._lang;return[{name:"title",selector:{text:{}}},t.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:t}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:[{value:"",label:ua(e,"editor.lang_auto")},{value:"en",label:ua(e,"editor.lang_en")},{value:"ru",label:ua(e,"editor.lang_ru")}]}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={title:ua(t,"editor.title"),default_floor:ua(t,"editor.default_floor"),language:ua(t,"editor.language"),icon_size:ua(t,"editor.icon_size"),show_temperature:ua(t,"editor.show_temperature"),live_states:ua(t,"editor.live_states"),show_signal:ua(t,"editor.show_signal"),kiosk:ua(t,"editor.kiosk"),cycle:ua(t,"editor.cycle")};return W`e[t.name]||t.name} @value-changed=${this._valueChanged} - >`}_valueChanged(t){const e={...this._config,...t.detail.value},i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ua.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",ua);const _a=n` + >`}_valueChanged(t){const e={...this._config,...t.detail.value},i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}_a.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",_a);const ma=n` :host { --hp-bg: var(--card-background-color, #16212e); --hp-line: var(--divider-color, #2b3d4f); @@ -1297,6 +1297,21 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .stage.mode-decor.dtool-text .decorlayer .dshape.dtext { cursor: text; } + /* Erasing a hairline must not require pixel-perfect aim. The duplicate + geometry is invisible and exists only while Erase is active. A + non-scaling stroke keeps the target comfortably wide at every zoom. */ + .decorlayer .derasehit { + fill: none; + stroke: transparent; + stroke-width: 16px; + stroke-linecap: round; + stroke-linejoin: round; + vector-effect: non-scaling-stroke; + pointer-events: none; + } + .stage.mode-decor.dtool-erase .decorlayer .derasehit { + pointer-events: stroke; + } .decorlayer .dsel { filter: drop-shadow(0 0 3px var(--hp-accent)); } @@ -2820,17 +2835,21 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .editorchrome-inner.nav-enter, .editorchrome-inner.nav-exit { animation: none; } } - /* Device info can have Edit + Open in HA + Close. A small HA dialog is - narrower than those three Russian-labelled actions; without wrapping, - HA's end-aligned footer pushed the first button outside the surface. */ + /* Device info can have Edit + Open in HA + Close. It uses a wide dialog; + wrapping remains as a phone fallback, but without a flex spacer (which + used to strand Edit alone on the first line). */ hp-dialog .row.infofooter { align-items: center; + justify-content: flex-start; flex-wrap: wrap; gap: var(--sp-3); } hp-dialog .row.infofooter .btn { flex-shrink: 0; } + hp-dialog .row.infofooter .infofooter-close { + margin-left: auto; + } @media (max-width: 480px) { hp-dialog .row.infofooter { padding: var(--sp-4) var(--sp-5); @@ -2966,16 +2985,16 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow z-index: 120; max-width: 90vw; } - `;function ma(t){const e=rn(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=xi(t.cfg.spaces.find(e=>e.id===t.spaceId)),o=t.iconSize??2.5,n=o>8?2.5:o,r={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(r[t.area]=e.id);const a=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):_t,l=gt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:mt),h=ra({hass:t.hass,areaToSpace:r,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:a,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>pa(t.lang,e),iconRules:l}),c=h.filter(e=>e.space===t.spaceId),d=c.filter(t=>!t.hidden),p=function(t,e,i){const s={},o=i/100*Cn(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=zn(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),c=a/h,d=l/Math.max(Math.ceil(e.length/h),1),p=e.map((t,e)=>({x:n.x+r+c*(e%h+.5),y:n.y+r+d*(Math.floor(e/h)+.5)}));ei(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=un(p[e]))}return s}(c,i,n),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qo,e=i.y*Qo;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},m=Array.isArray(_.walls)?_.walls:[],g=Number(_.cell_cm)>0?Number(_.cell_cm):5,f=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Bo(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...Go(o,e,i));for(const o of t.wall_columns||[])s.push(jo(o,e,i));return s}(i,g,cn);for(const t of f){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&u.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const v=Sn(i,u),y=[v.x,v.y,v.w,v.h],b=i.rooms.filter(t=>t.area||s.showBorders||"light"===Vi(s.fill,t)).map(e=>{let o="room "+(i.bg?"overlay":"yard"),n="";const r=Vi(s.fill,e);if(s.showBorders||"none"!==r){o+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a="light"===r?zi("light",null,Yr(Kr(t.hass,c,e)),null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):e.area?zi(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Jr(t,o.entities);null!=e&&s.push(e)}return Qe(s)}(t.hass,c,e.area):null,"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=Zr(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,c,e.area):null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):null;a?(o+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!s.showNames,l=Tn(e),h=e.id||G,d=e.area||G,p=e.poly?B`e.id===t.spaceId);if(!i)return null;const s=xi(t.cfg.spaces.find(e=>e.id===t.spaceId)),o=t.iconSize??2.5,n=o>8?2.5:o,r={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(r[t.area]=e.id);const a=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):_t,l=gt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:mt),h=aa({hass:t.hass,areaToSpace:r,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:a,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>ua(t.lang,e),iconRules:l}),c=h.filter(e=>e.space===t.spaceId),d=c.filter(t=>!t.hidden),p=function(t,e,i){const s={},o=i/100*Cn(e)*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=zn(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),c=a/h,d=l/Math.max(Math.ceil(e.length/h),1),p=e.map((t,e)=>({x:n.x+r+c*(e%h+.5),y:n.y+r+d*(Math.floor(e/h)+.5)}));ei(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=un(p[e]))}return s}(c,i,n),u=[];for(const e of d){const i=t.layout[e.id];if(i&&i.s===t.spaceId){const t=i.x*Qo,e=i.y*Qo;u.push({minX:t,minY:e,maxX:t,maxY:e})}}const _=t.cfg.spaces.find(e=>e.id===t.spaceId)||{},m=Array.isArray(_.walls)?_.walls:[],g=Number(_.cell_cm)>0?Number(_.cell_cm):5,f=function(t,e,i){const s=[];for(const o of t.partitions||[]){const t=Bo(o.a,o.b,o.cm,e,i);t&&s.push(t)}for(const o of t.room_drafts||[])s.push(...Go(o,e,i));for(const o of t.wall_columns||[])s.push(jo(o,e,i));return s}(i,g,cn);for(const t of f){const e=t.map(t=>t[0]),i=t.map(t=>t[1]);e.length&&u.push({minX:Math.min(...e),minY:Math.min(...i),maxX:Math.max(...e),maxY:Math.max(...i)})}const v=Sn(i,u),y=[v.x,v.y,v.w,v.h],b=i.rooms.filter(t=>t.area||s.showBorders||"light"===Vi(s.fill,t)).map(e=>{let o="room "+(i.bg?"overlay":"yard"),n="";const r=Vi(s.fill,e);if(s.showBorders||"none"!==r){o+=" styled";const i=[`--room-stroke:${s.color}`,`--room-stroke-op:${s.showBorders?s.opacity:0}`],a="light"===r?zi("light",null,Jr(Yr(t.hass,c,e)),null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):e.area?zi(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Zr(t,o.entities);null!=e&&s.push(e)}return Qe(s)}(t.hass,c,e.area):null,"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=Xr(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,c,e.area):null,s.tempMin,s.tempMax,Di(t.cfg?.settings)):null;a?(o+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!s.showNames,l=Tn(e),h=e.id||G,d=e.area||G,p=e.poly?B``:B``;return B`${p}${a?B`${e.name}`:G}`}),w=d.map(e=>{const s=function(t,e,i,s,o){const n=e[t.id];return n&&n.s===t.space?{x:n.x*Qo,y:n.y*Qo}:s[t.id]?s[t.id]:un(Mn(o))}(e,t.layout,t.cfg,p,i),o=(s.x-y[0])/y[2]*100,n=(s.y-y[1])/y[3]*100,r=Number(e.marker?.size)>0?Number(e.marker.size):1,a=Number(e.marker?.angle)||0,l=[`left:${o}%`,`top:${n}%`];return 1!==r&&l.push(`--dev-scale:${r}`),W`
-
`}),k=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const o=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Qo,y:s.y*Qo};const o=Tn(t);return un({x:o[0],y:o[1]})}(e,i.id,t.layout,t.cfg),n=(o.x-y[0])/y[2]*100,r=(o.y-y[1])/y[3]*100,a=Math.min(1,s.opacity+.25);return W`
`}),$=s.showNames?i.rooms.filter(t=>t.name).map(e=>{const o=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Qo,y:s.y*Qo};const o=Tn(t);return un({x:o[0],y:o[1]})}(e,i.id,t.layout,t.cfg),n=(o.x-y[0])/y[2]*100,r=(o.y-y[1])/y[3]*100,a=Math.min(1,s.opacity+.25);return W`
${e.name}
`}):[],$=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",x=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let S="";if("daynight"===As(t.cfg?.settings,x)&&null!==Rs(t.cfg?.settings,x)){const e=Is(t.hass);e&&(S=$s(e.elevation).bg)}const M=S||Si(t.cfg?.settings,s),C=m.length?Oo(i.rooms,m,[],dn,g,cn,Qo):ze(i.rooms),D=Di(t.cfg.settings),P=(m.length||f.length)&&s.showBorders?Eo(i.rooms,m,[],[],dn,g,cn,Qo,f):null,z=t.stageWidth&&y[2]?t.stageWidth/y[2]:1,T=!!P&&Js(P.depthUnits,z),R=s.color||"#607d8b";return W` + style="left:${n}%;top:${r}%;color:${s.color};opacity:${a}">${e.name}
`}):[],k=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"",x=t.cfg.spaces.find(e=>e.id===t.spaceId)?.settings||{};let S="";if("daynight"===As(t.cfg?.settings,x)&&null!==Rs(t.cfg?.settings,x)){const e=Is(t.hass);e&&(S=ks(e.elevation).bg)}const M=S||Si(t.cfg?.settings,s),C=m.length?Oo(i.rooms,m,[],dn,g,cn,Qo):ze(i.rooms),D=Di(t.cfg.settings),P=(m.length||f.length)&&s.showBorders?Eo(i.rooms,m,[],[],dn,g,cn,Qo,f):null,z=t.stageWidth&&y[2]?t.stageWidth/y[2]:1,T=!!P&&Js(P.depthUnits,z),R=s.color||"#607d8b";return W`
${P?B` @@ -2985,7 +3004,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:G} ${C.map(t=>"poly"in t?B``:B``)} - ${$?B``:G} ${b} @@ -2999,15 +3018,15 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow `:G} ${""} -
${w}${k}
+
${w}${$}
- `}let ga=null,fa=null,va=!1;const ya=new Set;function ba(){if(ga)return ga;try{const t=JSON.parse(localStorage.getItem("houseplan_card_cfg_v1")||"null");if(t&&t.config&&Array.isArray(t.config.spaces))return{config:t.config,rev:t.rev||0,layout:t.layout||{}}}catch{}return null}function wa(t){return ga?Promise.resolve(ga):fa||(fa=async function(t){const[e,i]=await Promise.all([t.callWS({type:"houseplan/config/get"}),t.callWS({type:"houseplan/layout/get"})]);if(ga={config:e?.config??null,rev:e?.rev??0,layout:i?.layout??{}},!va&&t.connection?.subscribeEvents){va=!0;const e=()=>{ga=null,ya.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{va=!1}}return ga}(t).finally(()=>{fa=null}),fa)}class ka extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return da(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[];return[t.length?{name:"space",selector:{select:{mode:"dropdown",options:t}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={space:pa(t,"editor.space"),title:pa(t,"editor.title"),show_button:pa(t,"editor.show_button"),button_label:pa(t,"editor.button_label"),button_target:pa(t,"editor.button_target"),icon_size:pa(t,"editor.icon_size")};return W`{fa=null,ba.forEach(t=>t())};try{await t.connection.subscribeEvents(e,"houseplan_config_updated"),await t.connection.subscribeEvents(e,"houseplan_layout_updated")}catch{ya=!1}}return fa}(t).finally(()=>{va=null}),va)}class ka extends lt{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1}setConfig(t){this._config=t}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const t=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(t?.config?.spaces||[]).map(t=>({value:t.id,label:t.title||t.id}))}catch{this._spaces=[]}finally{this._spacesLoading=!1}}}get _lang(){return pa(this.hass,this._config?.language)}get _schema(){const t=this._spaces||[];return[t.length?{name:"space",selector:{select:{mode:"dropdown",options:t}}}:{name:"space",selector:{text:{}}},{name:"title",selector:{text:{}}},{name:"show_button",selector:{boolean:{}}},{name:"button_label",selector:{text:{}}},{name:"button_target",selector:{text:{}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}}]}render(){if(!this.hass||!this._config)return G;this._loadSpaces();const t=this._lang,e={space:ua(t,"editor.space"),title:ua(t,"editor.title"),show_button:ua(t,"editor.show_button"),button_label:ua(t,"editor.button_label"),button_target:ua(t,"editor.button_target"),icon_size:ua(t,"editor.icon_size")};return W`e[t.name]||t.name} @value-changed=${this._valueChanged} - >`}_valueChanged(t){const e={...this._config||{},...t.detail.value};delete e.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ka.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",ka);const $a=t=>{history.pushState(null,"",t),((t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class xa extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new lr(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");$a(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=ba();return{type:"custom:houseplan-space-card",space:rn(e?.config||null)[0]?.id||"",show_button:!0}}setConfig(t){if(!t||!t.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",...t},this._snap=this._snap||ba()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},ya.add(t),()=>ya.delete(t)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._unsub?.(),this._unsub=void 0,this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._signer.dispose(),super.disconnectedCallback()}willUpdate(t){!this.hass||this._loading||this._snap&&!t.has("hass")||this._snap&&this._loadedOnce||this._load()}updated(){const t=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(t===this._observedStage)return;if(this._stageObserver?.disconnect(),this._observedStage=t,!t)return void(this._stageObserver=void 0);const e=()=>{const e=t.clientWidth;e>0&&Math.abs(e-this._stageWidth)>.5&&(this._stageWidth=e,this.requestUpdate())};this._stageObserver=new ResizeObserver(e),this._stageObserver.observe(t),e()}async _load(){if(this.hass&&!this._loading){this._loading=!0;try{const t=await wa(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return da(this.hass,this._config?.language)}getCardSize(){const t=rn(this._snap?.config||null).find(t=>t.id===this._config?.space);if(t){const e=t.vb[3]/t.vb[2];return Math.max(3,Math.round(8*e))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(t){return W`
${t}
`}_referenced(){return ji(this._snap?.config)}render(){if(!this._config)return G;const t=this._snap?.config;if(!t)return W`
${pa(this._lang,"space_card.loading")}
`;const e=this._config.space,i=ma({hass:this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:e,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:t=>this._signer.display(this.hass,t)});if(!i)return this._errorCard(pa(this._lang,"space_card.not_found",{id:e}));const s=rn(t).find(t=>t.id===e),o=void 0!==this._config.title?this._config.title:s?.title||"",n=!1!==this._config.show_button,r=this._config.button_label||pa(this._lang,"space_card.button");return W` + >
`}_valueChanged(t){const e={...this._config||{},...t.detail.value};delete e.aspect_ratio;const i=new Event("config-changed",{bubbles:!0,composed:!0});i.detail={config:e},this.dispatchEvent(i)}}ka.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-space-card-editor")||customElements.define("houseplan-space-card-editor",ka);const xa=t=>{history.pushState(null,"",t),((t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)})(window,"location-changed",{replace:!1})};class Sa extends lt{constructor(){super(...arguments),this._snap=null,this._loading=!1,this._stageWidth=0,this._loadedOnce=!1,this._signer=new lr(()=>this.requestUpdate()),this._goToSpace=()=>{const t=(this._config?.button_target||"/plan-doma").replace(/#.*$/,"");xa(`${t}#space=${encodeURIComponent(this._config.space)}`)}}static getConfigElement(){return document.createElement("houseplan-space-card-editor")}static getStubConfig(t){const e=wa();return{type:"custom:houseplan-space-card",space:rn(e?.config||null)[0]?.id||"",show_button:!0}}setConfig(t){if(!t||!t.space)throw new Error('houseplan-space-card: "space" is required');this._config={show_button:!0,button_target:"/plan-doma",...t},this._snap=this._snap||wa()}connectedCallback(){var t;super.connectedCallback(),this._unsub=(t=()=>{this._loading=!1,this._snap=null,this.requestUpdate()},ba.add(t),()=>ba.delete(t)),this._signer.start(()=>this.hass,()=>this._referenced())}disconnectedCallback(){this._unsub?.(),this._unsub=void 0,this._stageObserver?.disconnect(),this._stageObserver=void 0,this._observedStage=void 0,this._signer.dispose(),super.disconnectedCallback()}willUpdate(t){!this.hass||this._loading||this._snap&&!t.has("hass")||this._snap&&this._loadedOnce||this._load()}updated(){const t=this.renderRoot.querySelector(".hp-static-stage")||void 0;if(t===this._observedStage)return;if(this._stageObserver?.disconnect(),this._observedStage=t,!t)return void(this._stageObserver=void 0);const e=()=>{const e=t.clientWidth;e>0&&Math.abs(e-this._stageWidth)>.5&&(this._stageWidth=e,this.requestUpdate())};this._stageObserver=new ResizeObserver(e),this._stageObserver.observe(t),e()}async _load(){if(this.hass&&!this._loading){this._loading=!0;try{const t=await $a(this.hass);this._snap=t,this._loadedOnce=!0}catch{}finally{this._loading=!1,this.requestUpdate()}}}get _lang(){return pa(this.hass,this._config?.language)}getCardSize(){const t=rn(this._snap?.config||null).find(t=>t.id===this._config?.space);if(t){const e=t.vb[3]/t.vb[2];return Math.max(3,Math.round(8*e))+(!1===this._config?.show_button?0:1)}return 6}_errorCard(t){return W`
${t}
`}_referenced(){return ji(this._snap?.config)}render(){if(!this._config)return G;const t=this._snap?.config;if(!t)return W`
${ua(this._lang,"space_card.loading")}
`;const e=this._config.space,i=ga({hass:this.hass,cfg:t,layout:this._snap?.layout||{},spaceId:e,iconSize:this._config.icon_size,stageWidth:this._stageWidth,lang:this._lang,displayUrl:t=>this._signer.display(this.hass,t)});if(!i)return this._errorCard(ua(this._lang,"space_card.not_found",{id:e}));const s=rn(t).find(t=>t.id===e),o=void 0!==this._config.title?this._config.title:s?.title||"",n=!1!==this._config.show_button,r=this._config.button_label||ua(this._lang,"space_card.button");return W` ${o?W`
${o}
`:G} ${i} @@ -3015,7 +3034,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:G}
- `}}xa.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},xa.styles=[_a,n` + `}}Sa.properties={hass:{attribute:!1},_config:{state:!0},_snap:{state:!0}},Sa.styles=[ma,n` .hp-static-title { font-weight: 700; padding: 10px 14px 6px; @@ -3090,7 +3109,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow text-align: center; color: var(--secondary-text-color, #9aa4ad); } - `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",xa),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-space-card"===t.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only static schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Sa=4.166666666666667e-9;function Ma(t){if(!Number.isFinite(t))return t;const e=Math.round(t/dn)*dn;return Math.abs(e-t)<=Sa?t:e}const Ca=(t,e,i,s)=>Math.hypot(i-t,s-e),Da=(t,e,i,s,o,n,r,a)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+r-(t+i))),Math.max(Math.abs(n-e),Math.abs(n+a-(e+s)))),Pa=(t,e,i,s,o,n,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,h=Math.cos(i*l)*a,c=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(Ca(t+h,e+c,o+d,n+p),Ca(t-h,e-c,o-d,n-p)),_=Math.max(Ca(t+h,e+c,o-d,n-p),Ca(t-h,e-c,o+d,n+p));return Math.min(u,_)},za=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const Ta=t=>JSON.parse(JSON.stringify(t)),Ra=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Aa=(t,e,i)=>Math.min(i,Math.max(e,t)),Na=t=>rn({spaces:[t]})[0],Ea=t=>{const e=[];for(const i of t||[]){const t=Pe(i);if(t?.length)for(let i=0;i{let e=0;for(const i of t.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",e++),Array.isArray(i.controls)){jr(i.binding,i.controls)&&!0!==i.is_light&&(i.is_light=!0,e++);const t="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=t?i.controls.filter(e=>e!==t):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,e++)}const t=i.vacuum;t&&Ra(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const i of t.spaces||[]){if(Ra(i,"segments")&&(delete i.segments,e++),Ra(i,"plan_scale")){const t=Number(i.plan_scale);Number.isFinite(t)&&t>=en&&t<=sn&&(Ra(i,"plan_scale_x")||(i.plan_scale_x=t),Ra(i,"plan_scale_y")||(i.plan_scale_y=t),delete i.plan_scale,e++)}const t=Number(i.cell_cm);Ra(i,"cell_cm")&&(!Number.isFinite(t)||t<.1||t>1e3)&&(i.cell_cm=Number.isFinite(t)&&t>0?Aa(t,.1,1e3):5,e++);const s=Number(i.cell_cm),o=Number.isFinite(s)&&s>0?s:5;for(const t of i.wall_columns||[])if("circle"===t.shape)Ra(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Ra(t,"angle")){const i=((Number(t.angle)||0)%90+90)%90;t.angle!==i&&(t.angle=i,e++)}for(const t of i.decor||[]){if(Ra(t,"width")){const i=Number(t.width);(Ra(t,"width_cm")||Number.isFinite(i))&&(Ra(t,"width_cm")||(t.width_cm=Number(Aa(i/cn*o,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Ra(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Ra(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Aa(yi*bi(t)/cn*o,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Ra(t,"scale")||Ra(t,"size"))&&(delete t.scale,delete t.size,e++);let i=String(t.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1]));if(!(Ra(t,"entity")||Ra(t,"attr")||Ra(t,"unit")))continue;const n=String(t.unit??"").trim(),r="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||n?"":mi(t.entity,r);if(s||a){if(a){const e=i.indexOf("{}");i=e>=0?i.slice(0,e)+a+i.slice(e+2):`${i}${i?" ":""}${a}`,t.text=i}delete t.entity,delete t.attr,delete t.unit,e++}}}return e})(i);for(const t of i.spaces||[]){const e=Na(t);if(!e)continue;if(!Kn(t.open_spans).length){const i=tr(e.rooms,null,Qo,.02*cn,!0);i.length&&(t.open_spans=er(i,Qo),r++)}}const a=Ta(i.spaces||[]),l=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let o=0,n=0,r=0,a=0,l="",h=0,c=0;const d={};let p=5;for(const t of i){const e=za(t);null!=t?.id&&(d[String(t.id)]=e),e>p&&(p=e)}const u=(t,e,i,s=!1)=>{if(!(t>Sa||s))return;o++,t>r&&(r=t);const n=t*hn*e;n>a&&(a=n,l=i)};for(const t of i){const e=za(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){n++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[Ma(e[0]),Ma(e[1])];return t=Math.max(t,Ca(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,o=s.w||0,n=s.h||0,r=Ma(e+o),a=Ma(i+n),l=Ma(e),h=Ma(i),c=Math.max(dn,r-l),d=Math.max(dn,a-h);t=Da(e,i,o,n,l,h,c,d),s.x=l,s.y=h,s.w=c,s.h=d}u(t,e,i)}for(const s of t.room_drafts||[]){n++;let t=0;const o=(s.points||[]).map(e=>{const i=[Ma(e[0]),Ma(e[1])];return t=Math.max(t,Ca(e[0],e[1],i[0],i[1])),i}),r=o.length?[o[0]]:[],a=[];for(let t=0;t+1t.points?.length>=2),c+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const s of t.partitions||[]){n++;const t=[Ma(s.a[0]),Ma(s.a[1])],o=[Ma(s.b[0]),Ma(s.b[1])];let r=Math.max(Ca(s.a[0],s.a[1],t[0],t[1]),Ca(s.b[0],s.b[1],o[0],o[1]));Ca(t[0],t[1],o[0],o[1])>Sa?(s.a=t,s.b=o):r=0,u(r,e,i)}for(const s of t.wall_columns||[]){n++;const t=[Ma(s.center[0]),Ma(s.center[1])],o=Ca(s.center[0],s.center[1],t[0],t[1]);s.center=t,u(o,e,i)}for(const s of t.decor||[]){n++;let t=0;if("line"===s.kind){const e=[Ma(s.x1),Ma(s.y1)],i=[Ma(s.x2),Ma(s.y2)];t=Math.max(Ca(s.x1,s.y1,e[0],e[1]),Ca(s.x2,s.y2,i[0],i[1])),s.x1=e[0],s.y1=e[1],s.x2=i[0],s.y2=i[1]}else{const e=Ma(s.x),i=Ma(s.y);if(null!=s.w&&null!=s.h){const o=Ma(s.x+s.w),n=Ma(s.y+s.h),r=Math.max(dn,o-e),a=Math.max(dn,n-i);t=Da(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=Ca(s.x,s.y,e,i);s.x=e,s.y=i}u(t,e,i)}for(const s of t.openings||[]){n++;const o=Re([s.x,s.y],t.rooms||[],.025,{step:dn,length:Number(s.length)||0});if(!o)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===o.angle),l=Pa(s.x,s.y,Number.isFinite(r)?r:o.angle,Number(s.length)||0,o.x,o.y,o.angle);s.x=o.x,s.y=o.y,s.angle=o.angle,a&&h++,u(l,e,i,a)}}for(const[t,e]of Object.entries(s)){if(!e||"object"!=typeof e)continue;const i=e;if("number"!=typeof i.x||"number"!=typeof i.y)continue;n++;const o=Ma(i.x),r=Ma(i.y),a=Ca(i.x,i.y,o,r);s[t]={...i,x:o,y:r};const l="string"==typeof i.s?i.s:"";u(a,d[l]??p,l)}return{spaces:i,layout:s,report:{moved:o,total:n,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:h,removedDrafts:c},changed:o>0}}(i.spaces||[],e||{});i.spaces=l.spaces;const h={...l.report};let c=0,d=0,p=0;for(let t=0;t[t.id,t.open_to||[]]),walls:e.walls||[]}),n=Na(e),r=Na(s);if(!n||!r)continue;const l=Ea(n.rooms),u=Ea(r.rooms),_=.02*cn;let m=ar(Kn(e.open_spans),l,u,Qo);const g=m.length;m=rr(m,r.rooms,Qo,_),m=m.map(t=>{const e=Gn(t,Qo),i=[e[0],e[1]],o=[e[2],e[3]],n=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=e[2]-e[0],a=e[3]-e[1],l=Math.hypot(r,a)||1,c=u.map(([t,e])=>[t[0],t[1],e[0],e[1]]).filter(t=>{const e=t[2]-t[0],s=t[3]-t[1],h=Math.hypot(e,s)||1;return Math.abs(r*s-a*e)/(l*h)<=1e-6&&Yn(n,t).d<=4*_&&Yn(i,t).d<=4*_&&Yn(o,t).d<=4*_}).sort((t,e)=>Math.hypot(e[2]-e[0],e[3]-e[1])-Math.hypot(t[2]-t[0],t[3]-t[1]))[0];if(h.total++,!c)return t;const d=[[c[0],c[1]],[c[2],c[3]]],p=Zn(i,c,d,cn,2*_),m=Zn(o,c,d,cn,2*_);if(Math.hypot(m[0]-p[0],m[1]-p[1])<.5*cn)return t;const g=Math.max(Math.hypot(p[0]-i[0],p[1]-i[1]),Math.hypot(m[0]-o[0],m[1]-o[1]));if(g>1e-6*cn){h.moved++;const t=g/Qo;t>h.maxShift&&(h.maxShift=t);const e=g/cn*(Number(s.cell_cm)>0?Number(s.cell_cm):5);e>h.maxShiftCm&&(h.maxShiftCm=e,h.maxSpace=String(s.id||""))}return jn(p,m,Qo)}),m=rr(m,r.rooms,Qo,_),d+=Math.max(0,g-m.length);const f=m.map(t=>Gn(t,Qo));m.length?s.open_spans=m:delete s.open_spans,ir(s.rooms||[],r.rooms,f,_);const v=Array.isArray(e.walls)?e.walls.length:0;let y=_o(e.walls,l,u,dn,Qo);y=zo(r.rooms,y,f,dn,Number(s.cell_cm)>0?Number(s.cell_cm):5,cn,Qo),y=po(y,s.rooms||[],dn,1,f.map(t=>[t[0]/Qo,t[1]/Qo,t[2]/Qo,t[3]/Qo])),c+=Math.max(0,v-y.length),y.length?s.walls=y:delete s.walls;const b=JSON.stringify({spans:s.open_spans||[],links:(s.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:s.walls||[]});b!==o&&p++}const u=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o;n<5&&u&&(i.model_version=5);const _=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o,m=Number.isInteger(Number(i.model_version))?Number(i.model_version):n;return{config:i,layout:l.layout,report:{...h,modelFrom:n,modelTo:m,migrated:r,canonicalized:p,wallsMerged:c,spansMerged:d},changed:_}}class Oa{constructor(t=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(t))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(t){this._undo.push(t),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const t=this._undo.pop()??null;return t&&this._redo.push(t),t}redo(){const t=this._redo.pop()??null;return t&&this._undo.push(t),t}clear(){this._undo=[],this._redo=[]}}const Fa="1.60.2-beta.1",La=2e3,qa=1e3,Ha=1500,Ua=15e3,Wa=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const i=Number(e);return Number.isFinite(i)?i:null},Ba=(t,e)=>{if(!t.has(e))return{hit:!1};const i=t.get(e);return t.delete(e),t.set(e,i),{hit:!0,value:i}},ja=(t,e,i,s)=>{for(t.delete(e),t.set(e,i);t.size>s;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}};let Ga="undefined"!=typeof document&&"hidden"===document.visibilityState?Date.now():0,Va=0;const Ka=new Map;let Ya=0;const Ja=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Za=1e4;const Xa="houseplan_card_layout_v1",Qa="houseplan_card_cfg_v1",tl="houseplan_card_zoom_v1",el="houseplan_card_nav_v1",il="houseplan_card_kiosk_v1",sl=1e3,ol=3300,nl=(t,e)=>{const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return{x:i,y:s,w:Math.max(t.x+t.w,e.x+e.w)-i,h:Math.max(t.y+t.h,e.y+e.h)-s}},rl=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},al=(t,e)=>{let i,s=null;const o=(...o)=>{clearTimeout(i),s=o,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return o.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},o.pending=()=>void 0!==i,o},ll=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class hl extends lt{constructor(){super(...arguments),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._unsubCfg=null,this._unsubLayout=null,this._layoutRev=0,this._canOptimizeUndo=!1,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Os},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._navMotion="",this._editorSwapAnimations=[],this._editorChromeMode="plan",this._tool="draw",this._geometryHistory=new Oa(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._cleanFloorCache=new Map,this._glowClipCache=new Map,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._path=[],this._cursorPt=null,this._mergeSel=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._openWallAnchor=null,this._splitSel=null,this._pendingSplit=null,this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._alignDialog=null,this._settingsDialog=null,this._sunRaysCache=null,this._skyElev=null,this._skySnap=!1,this._skySnapRaf=0,this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=t=>this._onKey(t),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++Ya,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{if("hidden"!==document.visibilityState){if("visible"===document.visibilityState){const t=Date.now();Ga&&t-Ga>=Ua&&(Va=t),Ga=0,this._vacJumpOnce=!0,this._skyElev=null,this._tip=null,this._hoverRoom=null,t-Va<=15e3&&this._beginResumeSettle(),this.requestUpdate()}}else Ga||(Ga=Date.now())},this._resumeSettling=!1,this._resumeRaf=0,this._resumeStarted=0,this._resumeLastSize="",this._resumeLastChange=0,this._viewportInvalidAt=0,this._vacFit=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._lastTap=0,this._onHashChange=()=>{const t=this._hashSpace();t&&this._model.find(e=>e.id===t)&&t!==this._space&&(this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._selId=null,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=null,this._decorSnapCache=null,this._showHidden=!1,this._connHooked=null,this._onConnReady=()=>{this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._loading||(this._loadOk&&this._unsubCfg?this._reloadConfigOnly():this._loadFromServer())},this._signer=new lr(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=al(()=>{if(this._serverStorage){const t=[...this._dirtyPos];this._dirtyPos.clear();for(const e of t){const t=this._layout[e];t&&(this._sentPos.set(e,t),this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t}).then(t=>this._noteLayoutRev(t)).catch(t=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))).finally(()=>{this._sentPos.get(e)===t&&this._sentPos.delete(e)}))}this._cacheSnapshot()}else localStorage.setItem(Xa,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.undo();t&&(this._applyGeometryState(t.before)?this._showToast(this._t("history.undone",{name:t.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.redo();t&&(this._applyGeometryState(t.after)?this._showToast(this._t("history.redone",{name:t.name})):this._geometryHistory.clear())},this._saveConfigDebounced=al(()=>{this._serverCfg&&this._writeConfig().catch(t=>{"conflict"===t?.code?(this._showToast(this._t("toast.conflict")),this._cancelPath(),this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(t)}))})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=Wa(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Lo:100);const s=this._imperial?2.54*i:i,o="column"===t.kind?Lo:100;if(!Number.isFinite(s)||s<1||s>o)return void this._showPhysicalRange(o);if("column"===t.kind){const e=this._spaceModel().wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Wa(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:s}:{id:t.id,shape:"square",center:e.center,cm:s,angle:i};if(this._spaceModel().wall_columns.some(e=>e.id!==t.id&&Xo(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const n=this._geometrySnapshot();if("partition"===t.kind){const i=(e.partitions||[]).find(e=>e.id===t.id);i&&(i.cm=s)}else if("column"===t.kind){const i=(e.wall_columns||[]).find(e=>e.id===t.id);i&&(i.cm=qo(s),i.shape="circle"===t.shape?"circle":"square","square"===i.shape?i.angle=Wa(t.angle||"0"):delete i.angle)}else{const i=(e.room_drafts||[]).find(e=>e.id===t.id);i?.segments?.[t.segment||0]&&(i.segments[t.segment||0].cm=s)}this._recordGeometry(this._t("history.physical_edit"),n),this._physicalDialog=null,this._saveConfig()},this._deletePhysicalSelection=()=>{const t=this._physicalSel,e=this._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot(),s="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[s]=(e[s]||[]).filter(e=>e.id!==t.id),e[s].length||delete e[s],this._activeDraftId===t.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftWhole=()=>{const t="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,e=this._curSpaceCfg;if(!t||!e||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===t&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftSegment=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(i<0)return;const s=e.room_drafts[i],o=Math.max(0,Math.min(s.segments.length-1,t.segment||0)),n=[],r=s.points.slice(0,o+1),a=s.points.slice(o+1);if(r.length>=2&&n.push({id:s.id,points:r,segments:s.segments.slice(0,o)}),a.length>=2&&n.push({id:n.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:a,segments:s.segments.slice(o+1)}),2===n.length&&e.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(i,1,...n),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=n.length?{kind:"draft",id:n[0].id}:null,this._recordGeometry(this._t("history.draft_segment_delete"),l),this._saveConfig()},this._keepClosedAsPartitions=()=>{if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const t=this._curSpaceCfg,e=this._path.slice(0,-1);if((t.partitions||[]).length+e.length>2e3)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys];t.partitions||=[];const o=Date.now().toString(36);for(let i=0;it.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._recordGeometry(this._t("history.contour_to_partitions"),i),this._saveConfig(),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this._spaceDialog;if(t)if(t.pickSaved)this._spaceDialog={...t,pickSaved:!1};else{this._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.hass.callWS({type:"houseplan/plans/list"}),e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(t)}))}}},this._aspectJob=null,this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const t=this._glowRadiusCm,e=this._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:e,bgColor:Si(this._settings,{bgColor:null})||null,northDeg:Rs(this._settings,{}),bgMode:As(this._settings,{}),sunRays:Ns(this._settings,{}),weatherEntity:Es(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=Ia(this._serverCfg,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),o=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,config:e.config,layout:e.layout,cm:i,where:o,changed:e.changed,busy:!1}},this._optimizeUndoBusy=!1,this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:mt).map(t=>({...t}));this._rulesDialog={rules:e,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}_startNavMotion(t){if(clearTimeout(this._navMotionTimer),this._navMotionTimer=void 0,window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return this._cancelEditorSwapAnimations(),void(this._navMotion="");this._navMotion=t,this._navMotionTimer=window.setTimeout(()=>{this._navMotionTimer=void 0,this._navMotion="",this.requestUpdate()},190)}_cancelEditorSwapAnimations(){for(const t of this._editorSwapAnimations)t.cancel();this._editorSwapAnimations=[],this.renderRoot.querySelector(".editorchrome")?.classList.remove("resizing")}_animateEditorSwap(t){this.updateComplete.then(()=>{if(!this.isConnected||"swap"!==this._navMotion||window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return;const e=this.renderRoot.querySelector(".editorchrome"),i=e?.querySelector(".editorchrome-inner");if(!e||!i)return;this._cancelEditorSwapAnimations();const s=i.getBoundingClientRect().height;e.classList.add("resizing");const o={duration:190,easing:"cubic-bezier(0.2, 0.7, 0.2, 1)"},n=[];if(t>0&&s>0&&Math.abs(t-s)>.5){const i=e.animate([{height:`${t}px`},{height:`${s}px`}],o);i.id="hp-editor-height-swap",n.push(i)}const r=i.animate([{opacity:.42,transform:"translateY(5px) scale(0.995)"},{opacity:1,transform:"translateY(0) scale(1)"}],o);r.id="hp-editor-content-swap",n.push(r),this._editorSwapAnimations=n,Promise.allSettled(n.map(t=>t.finished)).then(()=>{this._editorSwapAnimations===n&&(this._editorSwapAnimations=[],e.classList.remove("resizing"))})})}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate())}_pickSpace(t){if(t===this._space)return;const e=this._model.map(t=>t.id),i=e.indexOf(this._space),s=e.indexOf(t);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(t,i>=0&&s0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const t=this._model.map(t=>t.id),e=t.indexOf(this._space);this._slideTo(t[(e+1)%t.length],"left"),this._showKioskDots()}}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}_hashSpace(){const t=/(?:^|[#&])space=([^&]+)/.exec(window.location.hash||"");return t?decodeURIComponent(t[1]):""}connectedCallback(){document.addEventListener("visibilitychange",this._vacVisHandler),super.connectedCallback(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>ji(this._serverCfg)),this._config?.kiosk&&Number(this._config?.cycle)>0&&(clearInterval(this._cycleTimer),this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle))),window.addEventListener("hashchange",this._onHashChange),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0));const t=Date.now();"visible"===document.visibilityState&&Ga&&(t-Ga>=Ua&&(Va=t),Ga=0),(this._warmLongReturn||t-Va<=15e3)&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._resumeRaf&&(cancelAnimationFrame(this._resumeRaf),this._resumeRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._activityRt.values())clearTimeout(t.timer);window.removeEventListener("keydown",this._keyHandler),clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._navMotionTimer),this._cancelEditorSwapAnimations(),this._slideTimer=void 0,this._navMotionTimer=void 0,this._slide="",this._navMotion="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._resumeSettling=!1,super.disconnectedCallback()}_onKey(t){if("Escape"===t.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void t.stopPropagation();if("Escape"===t.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void(this._infoCard=null);if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return void(this._alignDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}const e=(t.composedPath?.()||[t.target]).some(t=>t?.matches?.('input, textarea, select, [contenteditable="true"]')),i=t.ctrlKey||t.metaKey,s=t.key.toLowerCase(),o=/^[a-z]$/.test(s),n="z"===s||!o&&"KeyZ"===t.code,r="y"===s||!o&&"KeyY"===t.code,a=i&&(n&&t.shiftKey||r),l=i&&n&&!t.shiftKey;if("decor"===this._mode){if((l||a)&&e)return;return a?(t.preventDefault(),void this._redoGeometry()):l?(t.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||e?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():this._furnPalette?this._furnPalette=null:this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(t.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!l&&!a||!e)){if(("Delete"===t.key||"Backspace"===t.key)&&this._physicalSel&&!e)return t.preventDefault(),void this._deletePhysicalSelection();if(a)return t.preventDefault(),void this._redoGeometry();if(l)return t.preventDefault(),this._rszDrag?void this._rszCancelDrag():"draw"!==this._tool&&"partition"!==this._tool||!this._path.length?"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry():void(this._activeDraftId&&this._path.length>1?this._undoGeometry():this._undoPoint());if("Escape"===t.key)return this._physicalDrag||this._physicalRotate?(t.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(t.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length||"partition"===this._tool&&this._path.length?(t.preventDefault(),void this._undoPoint()):this._physicalSel?(t.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():void(this._rszSel?this._rszSel=null:this._tool="draw")):"split"===this._tool?(t.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(t.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(t.preventDefault(),void(this._wallDialog=null)):"openwall"===this._tool||"closewall"===this._tool?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"partition"!==this._tool&&"column"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const t=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const e=this._curSpaceCfg,i=(e.room_drafts||[]).findIndex(t=>t.id===this._activeDraftId);return void(i>=0&&(this._path.length<2?(e.room_drafts.splice(i,1),e.room_drafts.length||delete e.room_drafts,this._activeDraftId=null):e.room_drafts[i]={id:this._activeDraftId,points:this._path.map(t=>[t[0]/sl,t[1]/sl]),segments:this._draftSegmentCms.map(t=>({cm:t}))},this._recordGeometry(this._t("history.draft_segment_delete"),t),this._saveConfig()))}this._path=this._path.slice(0,-1)}}static getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(t){for(const t of Ka.values())for(const e of t)clearTimeout(e.evict);Ka.clear(),Za=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of Ka.values())for(const o of s)t++,o.dlg&&(e++,i.push(o.dlg.kind));return{keys:Ka.size,slots:t,dlgs:e,drafts:i}}setConfig(t){this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...t},this._config.kiosk&&(this._booting=!1,this._bootFading=!1),t.default_floor&&(this._space=t.default_floor);try{this._zoomBySpace=JSON.parse(localStorage.getItem(tl)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(il)||"null");this._kioskScale={icon:Ki(t?.icon),font:Ki(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(Qa)||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._layout=e.layout||{},this._serverStorage=!0;const i=this._hashSpace(),s=this._savedNav();i&&this._model.find(t=>t.id===i)?(this._space=i,this._hashApplied=!0):s?.space&&this._model.find(t=>t.id===s.space)?(this._space=s.space,this._navApplied=!0):t.default_floor?this._space=t.default_floor:this._model.find(t=>t.id===this._space)||(this._space=this._model[0]?.id||this._space),s?.mode&&"view"!==s.mode&&!t.kiosk&&(this._canEdit?this._mode=s.mode:this._pendingNavMode=s.mode)}}catch{}"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const t=Ja(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=Ka.get(t);if(!s||!s.length)return;const o=s.find(t=>t.owner===this._warmGen);if(o)return this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Ua,clearTimeout(o.evict),o.evict=0,o.freed=0,o.live=!0,this._warmSlot=o,void(this._warmKey=t);const{slot:n,sure:r}=((t,e,i,s)=>{const o=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let n=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=o(i);t<=0||(t>r?(n=i,r=t,a=1):t===r&&a++)}return!n||a>1?{slot:n||l,sure:!1}:{slot:n,sure:!0}})(s,this._warmGen,e,i);n&&(this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=Ua,this._booting=!1,this._bootFading=!1,this._hdrH=n.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)),this._warmKey=t,r?(clearTimeout(n.evict),n.evict=0,n.owner=this._warmGen,n.place=e?new WeakRef(e):null,n.idx=i,n.live=!0,this._warmSlot=n,this._warmVp=n.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:n.hdrH,stageH:n.stageH,vp:null,dlg:null,freed:0,evict:0},s.push(this._warmSlot),this._warmTrim(s)))}_warmIdx(t){const e=t?.children;if(!e)return-1;for(let t=0;t4;){const e=t.findIndex(t=>!t.live);if(e<0)break;clearTimeout(t[e].evict),t.splice(e,1)}}_warmScheduleEvict(t,e){if(clearTimeout(t.evict),!t.dlg)return;const i=t.freed,s=t.owner;t.evict=window.setTimeout(()=>{if(t.evict=0,t.freed!==i||t.owner!==s)return;t.dlg=null;const o=Ka.get(e);if(!t.live&&o&&o.length>1){const e=o.indexOf(t);e>=0&&o.splice(e,1)}},Za+250)}_warmAdoptViewport(t){const e=this._warmVp;e&&(!this._hashApplied&&this._model.find(t=>t.id===e.space)?(this._space=e.space,this._navApplied=!0,this._mode="view"!==e.mode&&this._canEdit&&!t.kiosk?e.mode:"view",this._pendingNavMode="view"===e.mode||this._canEdit||t.kiosk?null:e.mode,this._zoom=e.zoom,this._view=e.view?{...e.view}:null,this._viewModeSnap=e.snap?{...e.snap}:null,this._tool=e.tool,this._decorTool=e.decorTool,this._showHidden=e.showHidden,this._showFar!==e.showFar&&(this._showFar=e.showFar,this._frame=null),this._selId=e.selId,this._rszSel=e.rszSel,this._decorSel=e.decorSel,this._warmVpArmed=!0):this._warmVp=null)}_warmPatch(t,e=!1){if(this._config?.kiosk)return;const i=Ja(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!e)return;const t=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,place:t?new WeakRef(t):null,idx:this._warmIdx(t),live:!0,hdrH:this._hdrH,stageH:0,vp:null,dlg:null,freed:0,evict:0};const s=Ka.get(i)||[];for(s.push(this._warmSlot),Ka.set(i,s),this._warmTrim(s);Ka.size>8;){const t=Ka.keys().next().value;if(void 0===t||t===i)break;for(const e of Ka.get(t)||[])clearTimeout(e.evict);Ka.delete(t)}}Object.assign(this._warmSlot,t)}}_warmViewportState(){return{space:this._space,mode:this._mode,zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._rszSel,decorSel:this._decorSel}}_warmDialogState(){const t=(t,e)=>({kind:t,space:this._space,mode:this._mode,data:e});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog?null:this._openingInfo?t("openingInfo",this._openingInfo.id):this._infoCard?t("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:t("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:t("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:t("marker",this._markerDialog):this._openingDialog?t("opening",this._openingDialog):this._backdropDialog?t("backdrop",this._backdropDialog):this._decorShapeDialog?t("decorShape",this._decorShapeDialog):this._decorTextDialog?t("decorText",this._decorTextDialog):this._roomDialog?t("room",{editId:this._roomEditId,fill:this._roomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:t("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._resumeSettling||this._config?.kiosk)return;const t={vp:this._warmViewportState()};if(this._warmRevivePending||(t.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const e=this.parentNode;t.place=e?new WeakRef(e):null,t.idx=this._warmIdx(e)}this._warmPatch(t)}_warmReviveDialog(){this._warmRevivePending=!1;const t=this._warmSlot;if(this._warmReviveTimer=void 0,!t||!t.dlg)return;const e=t.dlg,i=t.freed;if(t.dlg=null,t.freed=0,clearTimeout(t.evict),t.evict=0,i&&!(Date.now()-i>Za)&&e.space===this._space&&e.mode===this._mode){switch(e.kind){case"space":this._spaceDialog={...e.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...e.data,busy:!1};break;case"settings":this._settingsDialog={...e.data,busy:!1};break;case"rules":this._rulesDialog={...e.data,busy:!1};break;case"opening":this._openingDialog={...e.data};break;case"backdrop":this._backdropDialog={...e.data};break;case"decorShape":this._decorShapeDialog={...e.data};break;case"decorText":{this._decorTextDialog={...e.data};const t=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:t,end:t};break}case"room":{const t=e.data;this._roomEditId=t.editId,this._roomFill=t.fill,this._roomTempSrc=t.tempSrc,this._roomHumSrc=t.humSrc,this._roomSrcOpen=t.srcOpen,this._roomSrcFilter=t.srcFilter,this._roomNameScale=t.nameScale,this._roomLabelScale=t.labelScale,this._areaSel=t.areaSel,this._nameSel=t.nameSel,this._pendingSplit=t.pendingSplit,this._path=t.path,this._roomDialog=!0;break}case"info":{const t=this._devices.find(t=>t.id===e.data);t&&(this._infoCard=t);break}case"openingInfo":{const t=(this._curSpaceCfg?.openings||[]).find(t=>t.id===e.data);t&&(this._openingInfo=t);break}}this.requestUpdate()}}_cacheSnapshot(){if(this._serverCfg)try{localStorage.setItem(Qa,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,layout:this._layout}))}catch{}}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const t=this._serverCfg?.spaces||[];let e=t.length+":";for(const i of t){e+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const t of i.rooms||[]){const i=t.poly?.[0],s=t.poly?.[t.poly.length-1];e+=(t.poly?.length||0)+"."+(t.id||"")+"."+(t.open_to||[]).join("+")+"."+(t.area||"")+"."+JSON.stringify(t.settings||0)+"."+(t.x??"")+","+(t.y??"")+","+(t.w??"")+","+(t.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(s?s[0]+"/"+s[1]:"")+";"}}return e}get _model(){if(!this._serverCfg)return[];const t=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===t)return this._modelCache.model;const e=this._buildModel();return this._modelCache={key:t,model:e},e}_buildModel(){if(!this._serverCfg)return[];const t=this._renderCfg;return rn(t).map((e,i)=>{const s=t.spaces[i]?.plan_url;return e.bg&&s?{...e,bg:{...e.bg,href:s}}:e})}_spaceModel(t){const e=this._model;return e.find(e=>e.id===(t??this._space))||e[0]}get _areaToSpace(){const t={};for(const e of this._model)for(const i of e.rooms)i.area&&(t[i.area]={space:e.id,room:i});return t}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const t=this._serverCfg,e=function(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,iconRules:r}=t,a=!1!==o.group_lights,l=sa(s),h=ea(e,a).filter(t=>!oa(e,t.eid,l)),c=new Set(h.map(t=>t.area)),d=Ir(e),p=new Set(s.map(t=>t.binding)),u=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(p.has("device:"+t.id))continue;const o=d[t.id]||[],l=Or(e,t,o);let h=n.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;if(!h&&a&&c.has(s)){const i=(t.name_by_user||t.name||"").trim();"mdi:lightbulb"===ia(e,i,t.model,o,r)&&(h=!0)}h&&u.push("device:"+t.id)}return u}({hass:this.hass,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!e.length&&t.settings?.filter_seeded)return;t.markers=t.markers||[];const i=[];for(const s of e){const e="h"+s.slice(s.indexOf(":")+1);t.markers.push({id:e,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...t.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(t=>!i.includes(t))),t.settings=s,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const t=this._settings.icon_rules;if(!t||!Array.isArray(t)||!t.length)return;const e=JSON.stringify(t);return e!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=e,this._rulesCompiled=gt(t)),this._rulesCompiled}get _fillColors(){return Di(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):_t}willUpdate(t){t.has("_serverCfg")&&this._cfgEpoch++,this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick())}updated(){this._skyRelease(),this._warmSnapshot(),this._dtMeasure();const t=this._stageEl;t&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(t)),t&&this._booting&&!this._bootTimer&&this._bootWatch();const e=this.renderRoot.querySelector(".hdr");if(e&&t&&!this._roHdr){const i=()=>{const e=this.renderRoot.querySelector("ha-card");if(!e)return;const i=t.getBoundingClientRect().top-e.getBoundingClientRect().top,s=Math.min(Math.max(e.getBoundingClientRect().top,0),120),o=Math.round(i+s);o>=0&&o!==this._hdrH&&(this._hdrH=o),o>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:o,stageH:t.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(e),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(t&&!this._view&&this._refitView(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const t=function(t){const e=t?.floors;if(!e||"object"!=typeof e)return[];const i=[];for(const t of Object.values(e))t&&t.floor_id&&i.push({id:t.floor_id,name:t.name||t.floor_id,level:t.level??null});return i.sort((t,e)=>{const i=t.level??1e9,s=e.level??1e9;return i!==s?i-s:t.name.localeCompare(e.name)}),i}(this.hass);t.length?this._importDialog={floors:t.map(t=>({...t,checked:!0}))}:this._openSpaceDialog("create")}}async _loadFromServer(){this._loading=!0,this._loadTries++;try{const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._loadOk=!0,this._serverStorage=!0,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._canOptimizeUndo=!(!t?.can_optimize_undo&&!e?.can_optimize_undo),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;this._geometryHistory.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=i&&Array.isArray(i.spaces)?i:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._layout=e?.layout||{},this._layoutRev=e?.rev??0,this._unsubCfg||(this._unsubCfg=await this.hass.connection.subscribeEvents(t=>{const e=Number(t?.data?.rev??-1);e!==this._cfgRev&&this._reloadConfigOnly(!1,e)},"houseplan_config_updated")),this.hass.callWS({type:"houseplan/trail/get"}).then(t=>{this._vacSrvTrails=t?.trails||{},this.requestUpdate()}).catch(()=>{}),this._unsubTrail||(this._unsubTrail=await this.hass.connection.subscribeEvents(async()=>{try{const t=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=t?.trails||{},this.requestUpdate()}catch{}},"houseplan_trail_updated")),this._unsubLayout||(this._unsubLayout=await this.hass.connection.subscribeEvents(t=>this._onLayoutEvent(Number(t?.data?.rev??-1)),"houseplan_layout_updated"));const s=this._hashSpace(),o=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):o?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===o.space)?(this._space=o.space,this._navApplied=!0):this._norm&&!this._model.find(t=>t.id===this._space)&&(this._space=this._model[0]?.id||this._space),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:this._restoreZoom()}catch(t){if(this._serverCfg)this._scheduleLoadRetry();else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(Xa)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}async _reloadConfigOnly(t=!1,e){if(!t){if(void 0!==e&&e<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,e),400))}try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._geometryHistory.clear(),this._clearGeometryGesture(),this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._canOptimizeUndo=!!t?.can_optimize_undo,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null),this._cacheSnapshot(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(t){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(t)}))}}_scheduleLoadRetry(){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),t.addEventListener?.("ready",this._onConnReady),this._connHooked=t)}_display(t){return this._signer.display(this.hass,t)}_resign(){this._signer.resign(this.hass,ji(this._serverCfg))}_onLayoutEvent(t){t<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{t<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(t){const e=t?.rev;"number"==typeof e&&e>this._layoutRev&&(this._layoutRev=e)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;const t=new Map;for(const e of this._dirtyPos)this._layout[e]&&t.set(e,this._layout[e]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[e,i]of this._sentPos)t.set(e,i);try{const e=await this.hass.callWS({type:"houseplan/layout/get"}),i={...e?.layout||{}};for(const[e,s]of t)i[e]=s;this._layout=i,this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!!e?.can_optimize_undo,this._cacheSnapshot(),this.requestUpdate()}catch{}}_maybeRebuildDevices(){const t=this.hass;if(!t?.devices||!t?.entities||!t?.areas)return;const e=Object.keys(t.devices).length+":"+Object.keys(t.entities).length+":"+Object.keys(t.areas).length+":"+(this._norm?"n":"l")+":"+da(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=ra({hass:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:t=>this._t(t),iconRules:this._iconRules}),this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime())}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const t=this._devices.filter(t=>!t.marker&&!t.virtual).map(t=>t.id).sort(),e=t.join(",");if(e===this._newSyncKey)return;this._newSyncKey=e;const i=this._settings,{fresh:s,known:o}=function(t,e){if(!Array.isArray(e))return{fresh:[],known:[...t]};const i=new Set(e),s=t.filter(t=>!i.has(t));return{fresh:s,known:s.length?[...e,...s]:e}}(t,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const t=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:o,new_device_ids:t}},this._saveConfig()}}get _newIds(){const t=this._settings.new_device_ids;return new Set(Array.isArray(t)?t:[])}_ackNewDevice(t){if(!this._newIds.has(t)||!this._serverCfg)return;const e=this._settings;this._serverCfg={...this._serverCfg,settings:{...e,new_device_ids:(e.new_device_ids||[]).filter(e=>e!==t)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(t){if(!t)return null;const e=[];for(const i of this._devices){if(i.area!==t||i.virtual)continue;const s=Jr(this.hass,i.entities);null!=s&&e.push(s)}return Qe(e)}_roomBounds(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}_defaultPositions(){const t={},e=this._config?.icon_size??2.5;for(const i of this._model){const s=e/100*Cn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const o=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!o.length)continue;const n=this._roomBounds(e),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(o.length*a/Math.max(l,1)))),c=Math.ceil(o.length/h),d=a/h,p=l/Math.max(c,1),u=o.map((t,e)=>({x:n.x+r+d*(e%h+.5),y:n.y+r+p*(Math.floor(e/h)+.5)}));ei(u,n,s,.5*r),o.forEach((e,i)=>t[e.id]=un(u[i]))}}return t}_pos(t){const e=this._spaceModel(t.space),i=this._layout[t.id];if(i)if(this._norm){if(i.s===t.space)return{x:i.x*sl,y:i.y*sl}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:un(Mn(e))}_savePos(t,e,i){if(this._norm){const s=this._gridPitch,o=Math.round(e/s)*s,n=Math.round(i/s)*s,r=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:mn(o/sl),y:mn(n/sl),...r?{k:r}:{}}}}else this._layout={...this._layout,[t.id]:{x:Math.round(e),y:Math.round(i)}};this._dirtyPos.add(t.id),this._persistLayout()}_coverIndicator(t){return"cover"===t.tapAction?ci(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_displayOf(t){const e=t.marker?.display;return"ripple"===e?"icon_ripple":e||"badge"}_visualSamples(t){const e=[],i=this._coverIndicator(t),s=Kr(this.hass,[t]);if(i)e.push(i);else if(s.length)e.push(...s.map(t=>t.eid));else{const i=Hr(this.hass,t.entities);i.length?e.push(...i):t.primary&&e.push(t.primary)}for(const i of t.entities||[]){"alarm"!==Nr(this.hass,i).status||e.includes(i)||e.push(i)}return e.map(t=>Nr(this.hass,t))}_deviceVisual(t){if(t.hidden)return{availability:"available",status:"neutral",activity:"none"};const e=this._visualSamples(t),i=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return kr;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(e);if("alarm"===i.status)return i;if(!this._config?.live_states)return{availability:"available",status:"neutral",activity:"none"};if("unavailable"===i.availability)return i;const s=this._activityRt.get(t.id),o=this._activitySourceKey(e);return s?.sources===o&&s.flashTs&&s.flashKind&&Date.now()-s.flashTst?.device_id===e).map(([t])=>t)}_bindingHasClimate(t){return this._bindingEntities(t).some(t=>t.startsWith("climate."))}_bindingCoverEntity(t){return ci(this._bindingEntities(t))}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!hi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&Qr(this.hass,t.primary)?ta(this.hass,t.entities):null}_openMoreInfo(t){t?rl(this,"hass-more-info",{entityId:t}):this._showToast(this._t("toast.no_entity"))}_ctxDevice(t,e){"view"===this._mode&&(t.preventDefault(),t.stopPropagation(),e.primary?this._openMoreInfo(e.primary):this._infoCard=e)}_clickDevice(t,e){if(t.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(e);const i=this._coverIndicator(e),s=this._actEntity(e),o=s?s.split(".")[0]:null,n=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=Kr(this.hass,[e]).filter(t=>"controls"===t.via).map(t=>t.eid);if("toggle"===e.tapAction&&r.length){const t=(a=r.map(t=>this.hass.states[t]?.state),a.some(t=>"on"===t)?"turn_off":"turn_on");return void n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant",t,{entity_id:r}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}var a;const l=function(t,e,i,s){const o=t||e||("light"===i?"toggle":"info");return"more-info"===o?"more-info":"run"===o?"run"===t?"run":"info":"cover"===o?"cover"!==t||"cover"!==i||hi.has(String(s||""))?"info":"cover":"toggle"!==o||!i||li.has(i)?"info":"toggle"===t?"toggle":ai.has(i)?"cover"===i&&hi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,o,s?this.hass.states[s]?.attributes?.device_class:null);if("run"===l){const t=e.marker?.tap_target||"",i=function(t){const e=String(t||"").split(".")[0];return"automation"===e?{domain:"automation",service:"trigger"}:"script"===e?{domain:"script",service:"turn_on"}:"scene"===e?{domain:"scene",service:"turn_on"}:null}(t),s=this.hass.states[t];if(!i||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||t;return void n(this._t("confirm.tap_run",{name:o}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>{this._stampActivity(e.id,"event",this._activitySourceKey(this._visualSamples(e))),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}if("cover"===l&&i){const t=function(t){const e=String(t||"");return"closed"===e?"open_cover":"open"===e?"close_cover":"opening"===e||"closing"===e?"stop_cover":"toggle"}(this.hass.states[i]?.state);return void n(this._t("confirm.tap_cover",{name:e.name}),()=>{this.hass.callService("cover",t,{entity_id:i}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}"toggle"===l&&e.primary?n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant","toggle",{entity_id:e.primary}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}):"more-info"===l&&e.primary?this._openMoreInfo(e.primary):this._infoCard=e}_t(t,e){return pa(da(this.hass,this._config?.language),t,e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(t){const e=[];for(const i of this._devices){if(i.space!==t.id||i.hidden)continue;const s=this._pos(i);e.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(t.id===this._space){for(const t of this._openingsR){const i=Number(t.angle)*Math.PI/180,s=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2,n=vn([[t.rx-s,t.ry-o],[t.rx+s,t.ry+o]]);n&&e.push(n)}const i=this._decorH;for(const t of this._decorList){const s=vn("line"===t.kind?[[t.x1*sl,t.y1*i],[t.x2*sl,t.y2*i]]:"text"===t.kind?[[t.x*sl,t.y*i]]:js({x:t.x*sl,y:t.y*i,w:t.w*sl,h:t.h*i,angle:t.angle}));s&&e.push(s)}for(const i of this._physicalBodiesR(t)){const t=vn(i);t&&e.push(t)}}return bn(t,e)}_frameOf(){const t=this._spaceModel(),e=this._frame,i="view"!==this._mode;if(e&&e.id===t.id&&this._bdDrag)return e;if(e&&e.id===t.id&&e.model===t&&e.layout===this._layout&&e.devs===this._devices&&e.far===this._showFar&&e.grow===i)return e;const s=xn(this._contentItems(t));let o=s.all||Sn(t),n=this._showFar?o:s.core||Sn(t);return e&&e.id===t.id&&i&&e.grow&&(n=nl(e.rect,n),o=nl(e.all,o)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:n,all:o,outliers:s.outliers},this._frame}_baseVb(){const t=this._frameOf().rect;return[t.x,t.y,t.w,t.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
+ `],customElements.get("houseplan-space-card")||customElements.define("houseplan-space-card",Sa),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-space-card"===t.type)||window.customCards.push({type:"houseplan-space-card",name:"House Plan — Space (static)",description:"Read-only static schematic of a single houseplan space, with a deep-link button.",preview:!1,documentation:"https://github.com/Matysh/houseplan-card"});const Ma=4.166666666666667e-9;function Ca(t){if(!Number.isFinite(t))return t;const e=Math.round(t/dn)*dn;return Math.abs(e-t)<=Ma?t:e}const Da=(t,e,i,s)=>Math.hypot(i-t,s-e),Pa=(t,e,i,s,o,n,r,a)=>Math.hypot(Math.max(Math.abs(o-t),Math.abs(o+r-(t+i))),Math.max(Math.abs(n-e),Math.abs(n+a-(e+s)))),za=(t,e,i,s,o,n,r)=>{const a=Math.max(Number(s)||0,0)/2,l=Math.PI/180,h=Math.cos(i*l)*a,c=Math.sin(i*l)*a,d=Math.cos(r*l)*a,p=Math.sin(r*l)*a,u=Math.max(Da(t+h,e+c,o+d,n+p),Da(t-h,e-c,o-d,n-p)),_=Math.max(Da(t+h,e+c,o-d,n-p),Da(t-h,e-c,o+d,n+p));return Math.min(u,_)},Ta=t=>{const e=Number(t?.cell_cm);return e>0?e:5};const Ra=t=>JSON.parse(JSON.stringify(t)),Aa=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),Na=(t,e,i)=>Math.min(i,Math.max(e,t)),Ea=t=>rn({spaces:[t]})[0],Ia=t=>{const e=[];for(const i of t||[]){const t=Pe(i);if(t?.length)for(let i=0;i{let e=0;for(const i of t.markers||[]){if("ripple"===i.display&&(i.display="icon_ripple",e++),Array.isArray(i.controls)){Gr(i.binding,i.controls)&&!0!==i.is_light&&(i.is_light=!0,e++);const t="string"==typeof i.binding&&i.binding.startsWith("entity:")?i.binding.slice(7):"",s=t?i.controls.filter(e=>e!==t):i.controls;JSON.stringify(s)!==JSON.stringify(i.controls)&&(i.controls=s.length?s:null,e++)}const t=i.vacuum;t&&Aa(t,"trail")&&(["never","cleaning","always"].includes(t.trail_mode)||(t.trail_mode=!1===t.trail?"never":"cleaning"),delete t.trail,e++)}for(const i of t.spaces||[]){if(Aa(i,"segments")&&(delete i.segments,e++),Aa(i,"plan_scale")){const t=Number(i.plan_scale);Number.isFinite(t)&&t>=en&&t<=sn&&(Aa(i,"plan_scale_x")||(i.plan_scale_x=t),Aa(i,"plan_scale_y")||(i.plan_scale_y=t),delete i.plan_scale,e++)}const t=Number(i.cell_cm);Aa(i,"cell_cm")&&(!Number.isFinite(t)||t<.1||t>1e3)&&(i.cell_cm=Number.isFinite(t)&&t>0?Na(t,.1,1e3):5,e++);const s=Number(i.cell_cm),o=Number.isFinite(s)&&s>0?s:5;for(const t of i.wall_columns||[])if("circle"===t.shape)Aa(t,"angle")&&(delete t.angle,e++);else if("square"===t.shape&&Aa(t,"angle")){const i=((Number(t.angle)||0)%90+90)%90;t.angle!==i&&(t.angle=i,e++)}for(const t of i.decor||[]){if(Aa(t,"width")){const i=Number(t.width);(Aa(t,"width_cm")||Number.isFinite(i))&&(Aa(t,"width_cm")||(t.width_cm=Number(Na(i/cn*o,.1,100).toFixed(6))),delete t.width,e++)}if("rect"!==t?.kind&&"ellipse"!==t?.kind||!0!==t.fill||(Aa(t,"fill_color")||(t.fill_color=t.color||"#607d8b",e++),Aa(t,"fill_opacity")||(t.fill_opacity=.25,e++)),"text"!==t?.kind)continue;void 0===t.size_cm?(t.size_cm=Number(Na(yi*bi(t)/cn*o,.1,2e3).toFixed(6)),delete t.scale,delete t.size,e++):(Aa(t,"scale")||Aa(t,"size"))&&(delete t.scale,delete t.size,e++);let i=String(t.text??"");const s=[...i.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1]));if(!(Aa(t,"entity")||Aa(t,"attr")||Aa(t,"unit")))continue;const n=String(t.unit??"").trim(),r="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,a=s||n?"":mi(t.entity,r);if(s||a){if(a){const e=i.indexOf("{}");i=e>=0?i.slice(0,e)+a+i.slice(e+2):`${i}${i?" ":""}${a}`,t.text=i}delete t.entity,delete t.attr,delete t.unit,e++}}}return e})(i);for(const t of i.spaces||[]){const e=Ea(t);if(!e)continue;if(!Kn(t.open_spans).length){const i=tr(e.rooms,null,Qo,.02*cn,!0);i.length&&(t.open_spans=er(i,Qo),r++)}}const a=Ra(i.spaces||[]),l=function(t,e){const i=JSON.parse(JSON.stringify(t||[])),s=JSON.parse(JSON.stringify(e||{}));let o=0,n=0,r=0,a=0,l="",h=0,c=0;const d={};let p=5;for(const t of i){const e=Ta(t);null!=t?.id&&(d[String(t.id)]=e),e>p&&(p=e)}const u=(t,e,i,s=!1)=>{if(!(t>Ma||s))return;o++,t>r&&(r=t);const n=t*hn*e;n>a&&(a=n,l=i)};for(const t of i){const e=Ta(t),i=null!=t?.id?String(t.id):"";for(const s of t.rooms||[]){n++;let t=0;if(s.poly?.length)s.poly=s.poly.map(e=>{const i=[Ca(e[0]),Ca(e[1])];return t=Math.max(t,Da(e[0],e[1],i[0],i[1])),i});else if(null!=s.x&&null!=s.y){const e=s.x,i=s.y,o=s.w||0,n=s.h||0,r=Ca(e+o),a=Ca(i+n),l=Ca(e),h=Ca(i),c=Math.max(dn,r-l),d=Math.max(dn,a-h);t=Pa(e,i,o,n,l,h,c,d),s.x=l,s.y=h,s.w=c,s.h=d}u(t,e,i)}for(const s of t.room_drafts||[]){n++;let t=0;const o=(s.points||[]).map(e=>{const i=[Ca(e[0]),Ca(e[1])];return t=Math.max(t,Da(e[0],e[1],i[0],i[1])),i}),r=o.length?[o[0]]:[],a=[];for(let t=0;t+1t.points?.length>=2),c+=e-t.room_drafts.length,t.room_drafts.length||delete t.room_drafts}for(const s of t.partitions||[]){n++;const t=[Ca(s.a[0]),Ca(s.a[1])],o=[Ca(s.b[0]),Ca(s.b[1])];let r=Math.max(Da(s.a[0],s.a[1],t[0],t[1]),Da(s.b[0],s.b[1],o[0],o[1]));Da(t[0],t[1],o[0],o[1])>Ma?(s.a=t,s.b=o):r=0,u(r,e,i)}for(const s of t.wall_columns||[]){n++;const t=[Ca(s.center[0]),Ca(s.center[1])],o=Da(s.center[0],s.center[1],t[0],t[1]);s.center=t,u(o,e,i)}for(const s of t.decor||[]){n++;let t=0;if("line"===s.kind){const e=[Ca(s.x1),Ca(s.y1)],i=[Ca(s.x2),Ca(s.y2)];t=Math.max(Da(s.x1,s.y1,e[0],e[1]),Da(s.x2,s.y2,i[0],i[1])),s.x1=e[0],s.y1=e[1],s.x2=i[0],s.y2=i[1]}else{const e=Ca(s.x),i=Ca(s.y);if(null!=s.w&&null!=s.h){const o=Ca(s.x+s.w),n=Ca(s.y+s.h),r=Math.max(dn,o-e),a=Math.max(dn,n-i);t=Pa(s.x,s.y,s.w,s.h,e,i,r,a),s.w=r,s.h=a}else t=Da(s.x,s.y,e,i);s.x=e,s.y=i}u(t,e,i)}for(const s of t.openings||[]){n++;const o=Re([s.x,s.y],t.rooms||[],.025,{step:dn,length:Number(s.length)||0});if(!o)continue;const r=Number(s.angle),a=!(Number.isFinite(r)&&r===o.angle),l=za(s.x,s.y,Number.isFinite(r)?r:o.angle,Number(s.length)||0,o.x,o.y,o.angle);s.x=o.x,s.y=o.y,s.angle=o.angle,a&&h++,u(l,e,i,a)}}for(const[t,e]of Object.entries(s)){if(!e||"object"!=typeof e)continue;const i=e;if("number"!=typeof i.x||"number"!=typeof i.y)continue;n++;const o=Ca(i.x),r=Ca(i.y),a=Da(i.x,i.y,o,r);s[t]={...i,x:o,y:r};const l="string"==typeof i.s?i.s:"";u(a,d[l]??p,l)}return{spaces:i,layout:s,report:{moved:o,total:n,maxShift:r,maxShiftCm:a,maxSpace:l,rotated:h,removedDrafts:c},changed:o>0}}(i.spaces||[],e||{});i.spaces=l.spaces;const h={...l.report};let c=0,d=0,p=0;for(let t=0;t[t.id,t.open_to||[]]),walls:e.walls||[]}),n=Ea(e),r=Ea(s);if(!n||!r)continue;const l=Ia(n.rooms),u=Ia(r.rooms),_=.02*cn;let m=ar(Kn(e.open_spans),l,u,Qo);const g=m.length;m=rr(m,r.rooms,Qo,_),m=m.map(t=>{const e=Gn(t,Qo),i=[e[0],e[1]],o=[e[2],e[3]],n=[(e[0]+e[2])/2,(e[1]+e[3])/2],r=e[2]-e[0],a=e[3]-e[1],l=Math.hypot(r,a)||1,c=u.map(([t,e])=>[t[0],t[1],e[0],e[1]]).filter(t=>{const e=t[2]-t[0],s=t[3]-t[1],h=Math.hypot(e,s)||1;return Math.abs(r*s-a*e)/(l*h)<=1e-6&&Yn(n,t).d<=4*_&&Yn(i,t).d<=4*_&&Yn(o,t).d<=4*_}).sort((t,e)=>Math.hypot(e[2]-e[0],e[3]-e[1])-Math.hypot(t[2]-t[0],t[3]-t[1]))[0];if(h.total++,!c)return t;const d=[[c[0],c[1]],[c[2],c[3]]],p=Zn(i,c,d,cn,2*_),m=Zn(o,c,d,cn,2*_);if(Math.hypot(m[0]-p[0],m[1]-p[1])<.5*cn)return t;const g=Math.max(Math.hypot(p[0]-i[0],p[1]-i[1]),Math.hypot(m[0]-o[0],m[1]-o[1]));if(g>1e-6*cn){h.moved++;const t=g/Qo;t>h.maxShift&&(h.maxShift=t);const e=g/cn*(Number(s.cell_cm)>0?Number(s.cell_cm):5);e>h.maxShiftCm&&(h.maxShiftCm=e,h.maxSpace=String(s.id||""))}return jn(p,m,Qo)}),m=rr(m,r.rooms,Qo,_),d+=Math.max(0,g-m.length);const f=m.map(t=>Gn(t,Qo));m.length?s.open_spans=m:delete s.open_spans,ir(s.rooms||[],r.rooms,f,_);const v=Array.isArray(e.walls)?e.walls.length:0;let y=_o(e.walls,l,u,dn,Qo);y=zo(r.rooms,y,f,dn,Number(s.cell_cm)>0?Number(s.cell_cm):5,cn,Qo),y=po(y,s.rooms||[],dn,1,f.map(t=>[t[0]/Qo,t[1]/Qo,t[2]/Qo,t[3]/Qo])),c+=Math.max(0,v-y.length),y.length?s.walls=y:delete s.walls;const b=JSON.stringify({spans:s.open_spans||[],links:(s.rooms||[]).map(t=>[t.id,t.open_to||[]]),walls:s.walls||[]});b!==o&&p++}const u=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o;n<5&&u&&(i.model_version=5);const _=JSON.stringify(i)!==s||JSON.stringify(l.layout)!==o,m=Number.isInteger(Number(i.model_version))?Number(i.model_version):n;return{config:i,layout:l.layout,report:{...h,modelFrom:n,modelTo:m,migrated:r,canonicalized:p,wallsMerged:c,spansMerged:d},changed:_}}class Fa{constructor(t=50){this._undo=[],this._redo=[],this._limit=Math.max(30,Math.floor(t))}get canUndo(){return this._undo.length>0}get canRedo(){return this._redo.length>0}get undoName(){return this._undo[this._undo.length-1]?.name??null}get redoName(){return this._redo[this._redo.length-1]?.name??null}get size(){return this._undo.length}push(t){this._undo.push(t),this._undo.length>this._limit&&this._undo.splice(0,this._undo.length-this._limit),this._redo=[]}undo(){const t=this._undo.pop()??null;return t&&this._redo.push(t),t}redo(){const t=this._redo.pop()??null;return t&&this._undo.push(t),t}clear(){this._undo=[],this._redo=[]}}const La="1.60.2-beta.2",qa=2e3,Ha=1e3,Ua=1500,Wa=15e3,Ba=t=>{const e=String(t??"").trim().replace(",",".");if(!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(e))return null;const i=Number(e);return Number.isFinite(i)?i:null},ja=(t,e)=>{if(!t.has(e))return{hit:!1};const i=t.get(e);return t.delete(e),t.set(e,i),{hit:!0,value:i}},Ga=(t,e,i,s)=>{for(t.delete(e),t.set(e,i);t.size>s;){const e=t.keys().next().value;if(void 0===e)break;t.delete(e)}};let Va="undefined"!=typeof document&&"hidden"===document.visibilityState?Date.now():0,Ka=0;const Ya=new Map;let Ja=0;const Za=t=>`${window.innerWidth}x${window.innerHeight}|${location.pathname}|${JSON.stringify(t??{})}`;let Xa=1e4;const Qa="houseplan_card_layout_v1",tl="houseplan_card_cfg_v1",el="houseplan_card_zoom_v1",il="houseplan_card_nav_v1",sl="houseplan_card_kiosk_v1",ol=1e3,nl=3300,rl=(t,e)=>{const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return{x:i,y:s,w:Math.max(t.x+t.w,e.x+e.w)-i,h:Math.max(t.y+t.h,e.y+e.h)-s}},al=(t,e,i)=>{const s=new Event(e,{bubbles:!0,composed:!0});s.detail=i??{},t.dispatchEvent(s)},ll=(t,e)=>{let i,s=null;const o=(...o)=>{clearTimeout(i),s=o,i=window.setTimeout(()=>{i=void 0;const e=s;s=null,e&&t(...e)},e)};return o.flush=()=>{if(void 0===i)return;clearTimeout(i),i=void 0;const e=s;s=null,e&&t(...e)},o.pending=()=>void 0!==i,o},hl=t=>{try{t.target?.setPointerCapture?.(t.pointerId)}catch{}};class cl extends lt{constructor(){super(...arguments),this._space="f1",this._layout={},this._serverStorage=!1,this._loadOk=!1,this._serverCanWrite=null,this._loading=!1,this._loadTries=0,this._serverCfg=null,this._cfgRev=0,this._unsubCfg=null,this._unsubLayout=null,this._layoutRev=0,this._canOptimizeUndo=!1,this._devices=[],this._regSignature="",this._defPos={},this._newSyncKey="",this._tip=null,this._hoverRoom=null,this._selId=null,this._toast="",this._mode="view",this._pendingNavMode=null,this._decorTool="select",this._decorStyle={...Os},this._decorDraft=null,this._decorMove=null,this._decorSel=null,this._decorEraseConfirm=null,this._decorTextDialog=null,this._decorShapeDialog=null,this._backdropDialog=null,this._decorTextSelection={start:0,end:0},this._furnPalette=null,this._dtBox=null,this._dtDrag=null,this._bdDrag=null,this._slide="",this._navMotion="",this._editorSwapAnimations=[],this._editorChromeMode="plan",this._tool="draw",this._geometryHistory=new Fa(50),this._wallDialog=null,this._drawWallField=null,this._activeDraftId=null,this._resumeDraftBySpace={},this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._physicalRotate=null,this._physicalLastTap=null,this._physicalPickCycle=null,this._wallUnionCache=null,this._physicalBodiesCache=null,this._cleanFloorCache=new Map,this._glowClipCache=new Map,this._duplicateColumnId=null,this._duplicateColumnTimer=0,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._path=[],this._cursorPt=null,this._mergeSel=null,this._openingDialog=null,this._openingInfo=null,this._opDrag=null,this._opMeasure=null,this._mergeDialog=null,this._openWallAnchor=null,this._splitSel=null,this._pendingSplit=null,this._areaSel="",this._nameSel="",this._roomDialog=!1,this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1,this._zoom=1,this._view=null,this._zoomBySpace={},this._viewModeSnap=null,this._pointers=new Map,this._panStart=null,this._panLock=null,this._pinchStart=null,this._suppressClick=!1,this._hdrH=118,this._booting=!0,this._bootFading=!1,this._bootLastH=-1,this._bootStart=0,this._bootLastChange=0,this._bootSoft=!1,this._tapConfirm=null,this._onboardingShown=!1,this._rulesDialog=null,this._alignDialog=null,this._settingsDialog=null,this._sunRaysCache=null,this._skyElev=null,this._skySnap=!1,this._skySnapRaf=0,this._compassDrag=!1,this._importDialog=null,this._importQueue=[],this._importTotal=0,this._rulesCompiledSrc="",this._infoCard=null,this._markerDialog=null,this._spaceDialog=null,this._keyHandler=t=>this._onKey(t),this._warmVp=null,this._warmVpArmed=!1,this._warmLongReturn=!1,this._warmRevivePending=!1,this._warmGen=++Ja,this._warmKey=null,this._warmSlot=null,this._hashApplied=!1,this._navApplied=!1,this._kioskScale={icon:1,font:1},this._kioskDialog=!1,this._activityRt=new Map,this._vacRt=new Map,this._vacViewKey="",this._vacLastView=null,this._vacRaf=0,this._vacSrvTrails={},this._vacJumpOnce=!1,this._vacVisHandler=()=>{if("hidden"!==document.visibilityState){if("visible"===document.visibilityState){const t=Date.now();Va&&t-Va>=Wa&&(Ka=t),Va=0,this._vacJumpOnce=!0,this._skyElev=null,this._tip=null,this._hoverRoom=null,t-Ka<=15e3&&this._beginResumeSettle(),this.requestUpdate()}}else Va||(Va=Date.now())},this._resumeSettling=!1,this._resumeRaf=0,this._resumeStarted=0,this._resumeLastSize="",this._resumeLastChange=0,this._viewportInvalidAt=0,this._vacFit=null,this._kioskDots=!1,this._cyclePausedUntil=0,this._swipeStart=null,this._lastTap=0,this._onHashChange=()=>{const t=this._hashSpace();t&&this._model.find(e=>e.id===t)&&t!==this._space&&(this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._selId=null,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),this.requestUpdate())},this._drag=null,this._rlResize=null,this._holdFired=!1,this._cfgEpoch=0,this._modelCache=null,this._decorSnapCache=null,this._showHidden=!1,this._connHooked=null,this._onConnReady=()=>{this._loadTries=0,clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._loading||(this._loadOk&&this._unsubCfg?this._reloadConfigOnly():this._loadFromServer())},this._signer=new lr(()=>this.requestUpdate()),this._dirtyPos=new Set,this._sentPos=new Map,this._persistLayout=ll(()=>{if(this._serverStorage){const t=[...this._dirtyPos];this._dirtyPos.clear();for(const e of t){const t=this._layout[e];t&&(this._sentPos.set(e,t),this.hass.callWS({type:"houseplan/layout/update",device_id:e,pos:t}).then(t=>this._noteLayoutRev(t)).catch(t=>this._showToast(this._t("toast.pos_save_failed",{err:this._errText(t)}))).finally(()=>{this._sentPos.get(e)===t&&this._sentPos.delete(e)}))}this._cacheSnapshot()}else localStorage.setItem(Qa,JSON.stringify(this._layout))},600),this._frame=null,this._showFar=!1,this._writesPending=0,this._writeChain=Promise.resolve(),this._undoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.undo();t&&(this._applyGeometryState(t.before)?this._showToast(this._t("history.undone",{name:t.name})):this._geometryHistory.clear())},this._redoGeometry=()=>{if(this._physicalDrag||this._physicalRotate)return void this._cancelPhysicalGesture();if(this._decorDraft)return this._decorDraft=null,void this.requestUpdate();if(this._decorMove||this._dtDrag||this._bdDrag)return void this._cancelDecorGesture();if(this._rszDrag)return void this._rszCancelDrag();const t=this._geometryHistory.redo();t&&(this._applyGeometryState(t.after)?this._showToast(this._t("history.redone",{name:t.name})):this._geometryHistory.clear())},this._saveConfigDebounced=ll(()=>{this._serverCfg&&this._writeConfig().catch(t=>{"conflict"===t?.code?(this._showToast(this._t("toast.conflict")),this._cancelPath(),this._reloadConfigOnly(!0)):this._showToast(this._t("toast.cfg_save_failed",{err:this._errText(t)}))})},500),this._draftSegmentCms=[],this._closingWallCm=null,this._savePhysicalDialog=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||!e)return;const i=Ba(t.cm);if(null==i)return void this._showPhysicalRange("column"===t.kind?Lo:100);const s=this._imperial?2.54*i:i,o="column"===t.kind?Lo:100;if(!Number.isFinite(s)||s<1||s>o)return void this._showPhysicalRange(o);if("column"===t.kind){const e=this._spaceModel().wall_columns.find(e=>e.id===t.id);if(!e)return;const i=Ba(t.angle||"0");if("circle"!==t.shape&&(null==i||i<0||i>=90))return void this._showToast(this._t("toast.physical_angle"));const o="circle"===t.shape?{id:t.id,shape:"circle",center:e.center,cm:s}:{id:t.id,shape:"square",center:e.center,cm:s,angle:i};if(this._spaceModel().wall_columns.some(e=>e.id!==t.id&&Xo(e,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"))}const n=this._geometrySnapshot();if("partition"===t.kind){const i=(e.partitions||[]).find(e=>e.id===t.id);i&&(i.cm=s)}else if("column"===t.kind){const i=(e.wall_columns||[]).find(e=>e.id===t.id);i&&(i.cm=qo(s),i.shape="circle"===t.shape?"circle":"square","square"===i.shape?i.angle=Ba(t.angle||"0"):delete i.angle)}else{const i=(e.room_drafts||[]).find(e=>e.id===t.id);i?.segments?.[t.segment||0]&&(i.segments[t.segment||0].cm=s)}this._recordGeometry(this._t("history.physical_edit"),n),this._physicalDialog=null,this._saveConfig()},this._deletePhysicalSelection=()=>{const t=this._physicalSel,e=this._curSpaceCfg;if(!t||!e)return;if("draft"===t.kind)return void this._deleteDraftWhole();const i=this._geometrySnapshot(),s="partition"===t.kind?"partitions":"column"===t.kind?"wall_columns":"room_drafts";e[s]=(e[s]||[]).filter(e=>e.id!==t.id),e[s].length||delete e[s],this._activeDraftId===t.id&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftWhole=()=>{const t="draft"===this._physicalDialog?.kind?this._physicalDialog.id:"draft"===this._physicalSel?.kind?this._physicalSel.id:null,e=this._curSpaceCfg;if(!t||!e||!confirm(this._t("confirm.delete_draft")))return;const i=this._geometrySnapshot();e.room_drafts=(e.room_drafts||[]).filter(e=>e.id!==t),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===t&&this._cancelPath(),this._physicalSel=null,this._physicalDialog=null,this._recordGeometry(this._t("history.physical_delete"),i),this._saveConfig()},this._deleteDraftSegment=()=>{const t=this._physicalDialog,e=this._curSpaceCfg;if(!t||"draft"!==t.kind||!e)return;if(!confirm(this._t("confirm.delete_draft_segment")))return;const i=(e.room_drafts||[]).findIndex(e=>e.id===t.id);if(i<0)return;const s=e.room_drafts[i],o=Math.max(0,Math.min(s.segments.length-1,t.segment||0)),n=[],r=s.points.slice(0,o+1),a=s.points.slice(o+1);if(r.length>=2&&n.push({id:s.id,points:r,segments:s.segments.slice(0,o)}),a.length>=2&&n.push({id:n.length?`${s.id}-${Date.now().toString(36)}`:s.id,points:a,segments:s.segments.slice(o+1)}),2===n.length&&e.room_drafts.length>=200)return void this._showToast(this._t("toast.physical_limit"));const l=this._geometrySnapshot();e.room_drafts.splice(i,1,...n),e.room_drafts.length||delete e.room_drafts,this._activeDraftId===s.id&&this._cancelPath(),this._physicalDialog=null,this._physicalSel=n.length?{kind:"draft",id:n[0].id}:null,this._recordGeometry(this._t("history.draft_segment_delete"),l),this._saveConfig()},this._keepClosedAsPartitions=()=>{if(!this._contourClosed||this._pendingSplit||!this._curSpaceCfg)return;const t=this._curSpaceCfg,e=this._path.slice(0,-1);if((t.partitions||[]).length+e.length>2e3)return void this._showToast(this._t("toast.physical_limit"));const i=this._geometrySnapshot(),s=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys];t.partitions||=[];const o=Date.now().toString(36);for(let i=0;it.id!==this._activeDraftId),t.room_drafts.length||delete t.room_drafts),this._recordGeometry(this._t("history.contour_to_partitions"),i),this._saveConfig(),this._roomDialog=!1,this._path=[],delete this._resumeDraftBySpace[this._space],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null},this._toggleServerPlans=async()=>{const t=this._spaceDialog;if(t)if(t.pickSaved)this._spaceDialog={...t,pickSaved:!1};else{this._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.hass.callWS({type:"houseplan/plans/list"}),e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:t?.plans||[],savedBusy:!1})}catch(t){const e=this._spaceDialog;e&&(this._spaceDialog={...e,saved:[],savedBusy:!1}),this._showToast(this._t("toast.plans_list_failed",{err:this._errText(t)}))}}},this._aspectJob=null,this._sunShown=!1,this._sunOut=!1,this._sunOutTimer=0,this._openSettingsDialog=()=>{if(!this._norm)return;const t=this._glowRadiusCm,e=this._imperial?Math.round(t/30.48*10)/10:Math.round(t)/100;this._settingsDialog={colors:JSON.parse(JSON.stringify(this._fillColors)),glowRadius:e,bgColor:Si(this._settings,{bgColor:null})||null,northDeg:Rs(this._settings,{}),bgMode:As(this._settings,{}),sunRays:Ns(this._settings,{}),weatherEntity:Es(this._settings)||"",busy:!1}},this._openAlignDialog=()=>{if(!this._norm||!this._serverCfg)return;const t=this._serverCfg.spaces||[],e=Oa(this._serverCfg,this._layout||{}),i=Math.ceil(10*e.report.maxShiftCm)/10,s=t.find(t=>null!=t?.id&&String(t.id)===e.report.maxSpace),o=t.length>1&&s?String(s.title||s.id):"";this._alignDialog={report:e.report,config:e.config,layout:e.layout,cm:i,where:o,changed:e.changed,busy:!1}},this._optimizeUndoBusy=!1,this._openRulesDialog=()=>{if(!this._norm)return;const t=this._settings.icon_rules,e=(t&&t.length?t:mt).map(t=>({...t}));this._rulesDialog={rules:e,test:"",busy:!1}},this._climateCache=null,this._gearPtCache=new WeakMap}get _canEdit(){return!!this._norm&&(!0===this._serverCanWrite||!1!==this._serverCanWrite&&!0===this.hass?.user?.is_admin)}get _kiosk(){return!!this._config?.kiosk}_showKioskDots(){this._kioskDots=!0,clearTimeout(this._kioskDotsTimer),this._kioskDotsTimer=window.setTimeout(()=>this._kioskDots=!1,2500)}_startNavMotion(t){if(clearTimeout(this._navMotionTimer),this._navMotionTimer=void 0,window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return this._cancelEditorSwapAnimations(),void(this._navMotion="");this._navMotion=t,this._navMotionTimer=window.setTimeout(()=>{this._navMotionTimer=void 0,this._navMotion="",this.requestUpdate()},190)}_cancelEditorSwapAnimations(){for(const t of this._editorSwapAnimations)t.cancel();this._editorSwapAnimations=[],this.renderRoot.querySelector(".editorchrome")?.classList.remove("resizing")}_animateEditorSwap(t){this.updateComplete.then(()=>{if(!this.isConnected||"swap"!==this._navMotion||window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches)return;const e=this.renderRoot.querySelector(".editorchrome"),i=e?.querySelector(".editorchrome-inner");if(!e||!i)return;this._cancelEditorSwapAnimations();const s=i.getBoundingClientRect().height;e.classList.add("resizing");const o={duration:190,easing:"cubic-bezier(0.2, 0.7, 0.2, 1)"},n=[];if(t>0&&s>0&&Math.abs(t-s)>.5){const i=e.animate([{height:`${t}px`},{height:`${s}px`}],o);i.id="hp-editor-height-swap",n.push(i)}const r=i.animate([{opacity:.42,transform:"translateY(5px) scale(0.995)"},{opacity:1,transform:"translateY(0) scale(1)"}],o);r.id="hp-editor-content-swap",n.push(r),this._editorSwapAnimations=n,Promise.allSettled(n.map(t=>t.finished)).then(()=>{this._editorSwapAnimations===n&&(this._editorSwapAnimations=[],e.classList.remove("resizing"))})})}_slideTo(t,e){if(t===this._space)return;const i=window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._space=t,this._path=[],this._cursorPt=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,"plan"===this._mode&&"draw"===this._tool&&this._resumeLastDraft(),this._restoreZoom(),i||(this._slide=e,clearTimeout(this._slideTimer),this._slideTimer=window.setTimeout(()=>{this._slideTimer=void 0,this._slide="",this.requestUpdate()},190),this.requestUpdate())}_pickSpace(t){if(t===this._space)return;const e=this._model.map(t=>t.id),i=e.indexOf(this._space),s=e.indexOf(t);this._navApplied=!0,this._showFar=!1,this._frame=null,this._slideTo(t,i>=0&&s0&&Date.now()>=this._cyclePausedUntil&&this._model.length>1&&this._zoom<=1.001){const t=this._model.map(t=>t.id),e=t.indexOf(this._space);this._slideTo(t[(e+1)%t.length],"left"),this._showKioskDots()}}get _editing(){return"plan"===this._mode||"devices"===this._mode||"decor"===this._mode}get _markup(){return"plan"===this._mode}_hashSpace(){const t=/(?:^|[#&])space=([^&]+)/.exec(window.location.hash||"");return t?decodeURIComponent(t[1]):""}connectedCallback(){document.addEventListener("visibilitychange",this._vacVisHandler),super.connectedCallback(),window.addEventListener("keydown",this._keyHandler),this._signer.start(()=>this.hass,()=>ji(this._serverCfg)),this._config?.kiosk&&Number(this._config?.cycle)>0&&(clearInterval(this._cycleTimer),this._cycleTimer=window.setInterval(()=>this._cycleTick(),1e3*Number(this._config.cycle))),window.addEventListener("hashchange",this._onHashChange),this._booting?this._bootWatch():this._bootFading&&(clearTimeout(this._bootTimer),this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220)),this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)),!this._loadOk&&this._serverCfg&&this.hass&&this._scheduleLoadRetry(),!this._warmSlot&&this._config&&this._warmAdopt(),this._warmVp&&!this._warmRevivePending&&void 0===this._warmReviveTimer&&(this._warmRevivePending=!0,this._warmReviveTimer=window.setTimeout(()=>this._warmReviveDialog(),0));const t=Date.now();"visible"===document.visibilityState&&Va&&(t-Va>=Wa&&(Ka=t),Va=0),(this._warmLongReturn||t-Ka<=15e3)&&this._beginResumeSettle(),this._warmLongReturn=!1,this.requestUpdate()}disconnectedCallback(){document.removeEventListener("visibilitychange",this._vacVisHandler),this._vacRaf&&(cancelAnimationFrame(this._vacRaf),this._vacRaf=0),this._resumeRaf&&(cancelAnimationFrame(this._resumeRaf),this._resumeRaf=0),this._skySnapRaf&&(cancelAnimationFrame(this._skySnapRaf),this._skySnapRaf=0);for(const t of this._activityRt.values())clearTimeout(t.timer);window.removeEventListener("keydown",this._keyHandler),clearInterval(this._cycleTimer),clearTimeout(this._kioskDotsTimer),clearTimeout(this._kioskHoldTimer),clearTimeout(this._reloadRetry),clearTimeout(this._loadRetryTimer),this._loadRetryTimer=void 0,this._connHooked?.removeEventListener?.("ready",this._onConnReady),this._connHooked=null,this._signer.dispose(),clearTimeout(this._toastTimer),clearTimeout(this._slideTimer),clearTimeout(this._navMotionTimer),this._cancelEditorSwapAnimations(),this._slideTimer=void 0,this._navMotionTimer=void 0,this._slide="",this._navMotion="",clearTimeout(this._bootTimer),this._bootTimer=void 0,clearTimeout(this._bootSoftTimer),this._saveConfigDebounced.flush(),window.removeEventListener("hashchange",this._onHashChange),clearTimeout(this._holdTimer),this._roViewport?.disconnect(),this._roViewport=void 0,this._roHdr?.disconnect(),this._roHdr=void 0,this._onWinResize&&(window.removeEventListener("resize",this._onWinResize),this._onWinResize=void 0),this._unsubCfg&&(this._unsubCfg(),this._unsubCfg=null),this._unsubLayout&&(this._unsubLayout(),this._unsubLayout=null),clearTimeout(this._layoutSyncTimer),clearTimeout(this._duplicateColumnTimer),this._warmSnapshot(),this._warmRevivePending=!1,clearTimeout(this._warmReviveTimer),this._warmReviveTimer=void 0,this._warmRelease(),this._resumeSettling=!1,super.disconnectedCallback()}_onKey(t){if("Escape"===t.key&&this._vacFit)return this._vacFit=null,this._showToast(this._t("vac.cal_cancelled")),void t.stopPropagation();if("Escape"===t.key){if(this._tapConfirm)return void(this._tapConfirm=null);if(this._decorEraseConfirm)return void(this._decorEraseConfirm=null);if(this._openingInfo)return void(this._openingInfo=null);if(this._infoCard)return void(this._infoCard=null);if(this._rulesDialog)return void(this._rulesDialog=null);if(this._alignDialog)return void(this._alignDialog=null);if(this._settingsDialog)return void(this._settingsDialog=null);if(this._markerDialog)return void(this._markerDialog=null);if(this._openingDialog)return void(this._openingDialog=null);if(this._physicalDialog)return void(this._physicalDialog=null);if(this._backdropDialog)return void(this._backdropDialog=null);if(this._decorShapeDialog)return void(this._decorShapeDialog=null);if(this._decorTextDialog)return void(this._decorTextDialog=null);if(this._spaceDialog&&!this._roomDialog)return this._spaceDialog=null,this._importQueue=[],void(this._importTotal=0)}const e=(t.composedPath?.()||[t.target]).some(t=>t?.matches?.('input, textarea, select, [contenteditable="true"]')),i=t.ctrlKey||t.metaKey,s=t.key.toLowerCase(),o=/^[a-z]$/.test(s),n="z"===s||!o&&"KeyZ"===t.code,r="y"===s||!o&&"KeyY"===t.code,a=i&&(n&&t.shiftKey||r),l=i&&n&&!t.shiftKey;if("decor"===this._mode){if((l||a)&&e)return;return a?(t.preventDefault(),void this._redoGeometry()):l?(t.preventDefault(),this._decorDraft?void(this._decorDraft=null):this._decorMove||this._dtDrag||this._bdDrag?void this._cancelDecorGesture():void this._undoGeometry()):"Delete"!==t.key&&"Backspace"!==t.key||!this._decorSel||e?void("Escape"===t.key&&(t.preventDefault(),this._decorDraft?this._decorDraft=null:this._decorMove||this._dtDrag||this._bdDrag?this._cancelDecorGesture():this._furnPalette?this._furnPalette=null:this._decorSel?this._decorSel=null:"select"!==this._decorTool?this._decorTool="select":this._setMode("view"))):(t.preventDefault(),void this._decorDeleteSel())}if(this._markup&&(!l&&!a||!e)){if(("Delete"===t.key||"Backspace"===t.key)&&this._physicalSel&&!e)return t.preventDefault(),void this._deletePhysicalSelection();if(a)return t.preventDefault(),void this._redoGeometry();if(l)return t.preventDefault(),this._rszDrag?void this._rszCancelDrag():"draw"!==this._tool&&"partition"!==this._tool||!this._path.length?"split"===this._tool&&this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},void(this._splitSel.pts.length||(this._cursorPt=null))):void this._undoGeometry():void(this._activeDraftId&&this._path.length>1?this._undoGeometry():this._undoPoint());if("Escape"===t.key)return this._physicalDrag||this._physicalRotate?(t.preventDefault(),void this._cancelPhysicalGesture()):this._roomDialog?(t.preventDefault(),void this._roomDialogCancel()):"draw"===this._tool&&this._path.length||"partition"===this._tool&&this._path.length?(t.preventDefault(),void this._undoPoint()):this._physicalSel?(t.preventDefault(),void(this._physicalSel=null)):"resize"===this._tool?(t.preventDefault(),this._rszDrag?void this._rszCancelDrag():void(this._rszSel?this._rszSel=null:this._tool="draw")):"split"===this._tool?(t.preventDefault(),void(this._splitSel?.pts?.length?(this._splitSel={...this._splitSel,pts:this._splitSel.pts.slice(0,-1)},this._splitSel.pts.length||(this._cursorPt=null)):this._splitSel?this._splitSel=null:this._tool="draw")):"merge"===this._tool?(t.preventDefault(),void(this._mergeSel?this._mergeSel=null:this._tool="draw")):this._wallDialog?(t.preventDefault(),void(this._wallDialog=null)):"openwall"===this._tool||"closewall"===this._tool?(t.preventDefault(),void(this._openWallAnchor?this._openWallAnchor=null:this._tool="draw")):void("opening"!==this._tool&&"wallthick"!==this._tool&&"delroom"!==this._tool&&"partition"!==this._tool&&"column"!==this._tool||(t.preventDefault(),this._tool="draw"))}}_undoPoint(){if(this._path.length){if(this._contourClosed)return this._path=this._path.slice(0,-1),void(this._closingWallCm=null);if(this._activeDraftId&&this._path.length>1&&this._curSpaceCfg){const t=this._geometrySnapshot();this._path=this._path.slice(0,-1),this._draftSegmentCms=this._draftSegmentCms.slice(0,-1);const e=this._curSpaceCfg,i=(e.room_drafts||[]).findIndex(t=>t.id===this._activeDraftId);return void(i>=0&&(this._path.length<2?(e.room_drafts.splice(i,1),e.room_drafts.length||delete e.room_drafts,this._activeDraftId=null):e.room_drafts[i]={id:this._activeDraftId,points:this._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentCms.map(t=>({cm:t}))},this._recordGeometry(this._t("history.draft_segment_delete"),t),this._saveConfig()))}this._path=this._path.slice(0,-1)}}static getConfigElement(){return document.createElement("houseplan-card-editor")}static getStubConfig(){return{type:"custom:houseplan-card"}}static _warmBootReset(t){for(const t of Ya.values())for(const e of t)clearTimeout(e.evict);Ya.clear(),Xa=t&&t>0?t:1e4}static _warmBootStats(){let t=0,e=0;const i=[];for(const s of Ya.values())for(const o of s)t++,o.dlg&&(e++,i.push(o.dlg.kind));return{keys:Ya.size,slots:t,dlgs:e,drafts:i}}setConfig(t){this._config={icon_size:2.5,show_temperature:!0,live_states:!0,show_signal:!0,...t},this._config.kiosk&&(this._booting=!1,this._bootFading=!1),t.default_floor&&(this._space=t.default_floor);try{this._zoomBySpace=JSON.parse(localStorage.getItem(el)||"{}")||{}}catch{this._zoomBySpace={}}try{const t=JSON.parse(localStorage.getItem(sl)||"null");this._kioskScale={icon:Ki(t?.icon),font:Ki(t?.font)}}catch{}try{const e=JSON.parse(localStorage.getItem(tl)||"null");if(e&&e.config&&Array.isArray(e.config.spaces)){this._serverCfg=e.config,this._cfgEpoch++,this._cfgRev=e.rev||0,this._layout=e.layout||{},this._serverStorage=!0;const i=this._hashSpace(),s=this._savedNav();i&&this._model.find(t=>t.id===i)?(this._space=i,this._hashApplied=!0):s?.space&&this._model.find(t=>t.id===s.space)?(this._space=s.space,this._navApplied=!0):t.default_floor?this._space=t.default_floor:this._model.find(t=>t.id===this._space)||(this._space=this._model[0]?.id||this._space),s?.mode&&"view"!==s.mode&&!t.kiosk&&(this._canEdit?this._mode=s.mode:this._pendingNavMode=s.mode)}}catch{}"view"!==this._mode||this._view||(this._zoom=this._zoomBySpace[this._space]||1),this.isConnected&&(this._warmAdopt(),this._warmLongReturn&&this._beginResumeSettle(),this._warmLongReturn=!1)}_warmAdopt(){if(this._config?.kiosk)return;const t=Za(this._config);if(this._warmKey===t&&this._warmSlot)return;this._warmSlot&&this._warmRelease();const e=this.parentNode,i=this._warmIdx(e),s=Ya.get(t);if(!s||!s.length)return;const o=s.find(t=>t.owner===this._warmGen);if(o)return this._warmLongReturn=!!o.freed&&Date.now()-o.freed>=Wa,clearTimeout(o.evict),o.evict=0,o.freed=0,o.live=!0,this._warmSlot=o,void(this._warmKey=t);const{slot:n,sure:r}=((t,e,i,s)=>{const o=t=>{const e=!!i&&t.place?.deref()===i;return e&&t.idx===s?4:t.live?0:e?3:2};let n=null,r=0,a=0,l=null;for(const i of t){if(i.owner===e)continue;l=i;const t=o(i);t<=0||(t>r?(n=i,r=t,a=1):t===r&&a++)}return!n||a>1?{slot:n||l,sure:!1}:{slot:n,sure:!0}})(s,this._warmGen,e,i);n&&(this._warmLongReturn=!!n.freed&&Date.now()-n.freed>=Wa,this._booting=!1,this._bootFading=!1,this._hdrH=n.hdrH,this._bootSoft=!0,this.isConnected&&(clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)),this._warmKey=t,r?(clearTimeout(n.evict),n.evict=0,n.owner=this._warmGen,n.place=e?new WeakRef(e):null,n.idx=i,n.live=!0,this._warmSlot=n,this._warmVp=n.vp,this._warmAdoptViewport(this._config)):(this._warmSlot={owner:this._warmGen,place:e?new WeakRef(e):null,idx:i,live:!0,hdrH:n.hdrH,stageH:n.stageH,vp:null,dlg:null,freed:0,evict:0},s.push(this._warmSlot),this._warmTrim(s)))}_warmIdx(t){const e=t?.children;if(!e)return-1;for(let t=0;t4;){const e=t.findIndex(t=>!t.live);if(e<0)break;clearTimeout(t[e].evict),t.splice(e,1)}}_warmScheduleEvict(t,e){if(clearTimeout(t.evict),!t.dlg)return;const i=t.freed,s=t.owner;t.evict=window.setTimeout(()=>{if(t.evict=0,t.freed!==i||t.owner!==s)return;t.dlg=null;const o=Ya.get(e);if(!t.live&&o&&o.length>1){const e=o.indexOf(t);e>=0&&o.splice(e,1)}},Xa+250)}_warmAdoptViewport(t){const e=this._warmVp;e&&(!this._hashApplied&&this._model.find(t=>t.id===e.space)?(this._space=e.space,this._navApplied=!0,this._mode="view"!==e.mode&&this._canEdit&&!t.kiosk?e.mode:"view",this._pendingNavMode="view"===e.mode||this._canEdit||t.kiosk?null:e.mode,this._zoom=e.zoom,this._view=e.view?{...e.view}:null,this._viewModeSnap=e.snap?{...e.snap}:null,this._tool=e.tool,this._decorTool=e.decorTool,this._showHidden=e.showHidden,this._showFar!==e.showFar&&(this._showFar=e.showFar,this._frame=null),this._selId=e.selId,this._rszSel=e.rszSel,this._decorSel=e.decorSel,this._warmVpArmed=!0):this._warmVp=null)}_warmPatch(t,e=!1){if(this._config?.kiosk)return;const i=Za(this._config);if(!this._warmSlot||this._warmKey===i){if(!this._warmSlot){if(!e)return;const t=this.parentNode;this._warmKey=i,this._warmSlot={owner:this._warmGen,place:t?new WeakRef(t):null,idx:this._warmIdx(t),live:!0,hdrH:this._hdrH,stageH:0,vp:null,dlg:null,freed:0,evict:0};const s=Ya.get(i)||[];for(s.push(this._warmSlot),Ya.set(i,s),this._warmTrim(s);Ya.size>8;){const t=Ya.keys().next().value;if(void 0===t||t===i)break;for(const e of Ya.get(t)||[])clearTimeout(e.evict);Ya.delete(t)}}Object.assign(this._warmSlot,t)}}_warmViewportState(){return{space:this._space,mode:this._mode,zoom:this._zoom,view:this._view?{...this._view}:null,snap:this._viewModeSnap?{...this._viewModeSnap}:null,tool:this._tool,decorTool:this._decorTool,showHidden:this._showHidden,showFar:this._showFar,selId:this._selId,rszSel:this._rszSel,decorSel:this._decorSel}}_warmDialogState(){const t=(t,e)=>({kind:t,space:this._space,mode:this._mode,data:e});return this._tapConfirm||this._alignDialog||this._mergeDialog||this._importDialog?null:this._openingInfo?t("openingInfo",this._openingInfo.id):this._infoCard?t("info",this._infoCard.id):this._rulesDialog?this._rulesDialog.busy?null:t("rules",this._rulesDialog):this._settingsDialog?this._settingsDialog.busy?null:t("settings",this._settingsDialog):this._markerDialog?this._markerDialog.busy?null:t("marker",this._markerDialog):this._openingDialog?t("opening",this._openingDialog):this._backdropDialog?t("backdrop",this._backdropDialog):this._decorShapeDialog?t("decorShape",this._decorShapeDialog):this._decorTextDialog?t("decorText",this._decorTextDialog):this._roomDialog?t("room",{editId:this._roomEditId,fill:this._roomFill,tempSrc:this._roomTempSrc,humSrc:this._roomHumSrc,srcOpen:this._roomSrcOpen,srcFilter:this._roomSrcFilter,nameScale:this._roomNameScale,labelScale:this._roomLabelScale,areaSel:this._areaSel,nameSel:this._nameSel,pendingSplit:this._pendingSplit,path:this._path}):this._spaceDialog?this._spaceDialog.busy?null:t("space",this._spaceDialog):null}_warmSnapshot(){if(this._booting||this._resumeSettling||this._config?.kiosk)return;const t={vp:this._warmViewportState()};if(this._warmRevivePending||(t.dlg=this._warmDialogState()),this.isConnected&&this._warmSlot?.owner===this._warmGen){const e=this.parentNode;t.place=e?new WeakRef(e):null,t.idx=this._warmIdx(e)}this._warmPatch(t)}_warmReviveDialog(){this._warmRevivePending=!1;const t=this._warmSlot;if(this._warmReviveTimer=void 0,!t||!t.dlg)return;const e=t.dlg,i=t.freed;if(t.dlg=null,t.freed=0,clearTimeout(t.evict),t.evict=0,i&&!(Date.now()-i>Xa)&&e.space===this._space&&e.mode===this._mode){switch(e.kind){case"space":this._spaceDialog={...e.data,busy:!1,savedBusy:!1};break;case"marker":this._markerDialog={...e.data,busy:!1};break;case"settings":this._settingsDialog={...e.data,busy:!1};break;case"rules":this._rulesDialog={...e.data,busy:!1};break;case"opening":this._openingDialog={...e.data};break;case"backdrop":this._backdropDialog={...e.data};break;case"decorShape":this._decorShapeDialog={...e.data};break;case"decorText":{this._decorTextDialog={...e.data};const t=String(this._decorTextDialog?.text??"").length;this._decorTextSelection={start:t,end:t};break}case"room":{const t=e.data;this._roomEditId=t.editId,this._roomFill=t.fill,this._roomTempSrc=t.tempSrc,this._roomHumSrc=t.humSrc,this._roomSrcOpen=t.srcOpen,this._roomSrcFilter=t.srcFilter,this._roomNameScale=t.nameScale,this._roomLabelScale=t.labelScale,this._areaSel=t.areaSel,this._nameSel=t.nameSel,this._pendingSplit=t.pendingSplit,this._path=t.path,this._roomDialog=!0;break}case"info":{const t=this._devices.find(t=>t.id===e.data);t&&(this._infoCard=t);break}case"openingInfo":{const t=(this._curSpaceCfg?.openings||[]).find(t=>t.id===e.data);t&&(this._openingInfo=t);break}}this.requestUpdate()}}_cacheSnapshot(){if(this._serverCfg)try{localStorage.setItem(tl,JSON.stringify({config:this._serverCfg,rev:this._cfgRev,layout:this._layout}))}catch{}}getCardSize(){return 12}get _norm(){return!(!this._serverCfg||!this._serverCfg.spaces.length)}_cfgFingerprint(){const t=this._serverCfg?.spaces||[];let e=t.length+":";for(const i of t){e+=(i.id||"")+","+(i.plan_aspect||"")+","+(i.plan_url||"").length+","+(i.plan_x??"")+","+(i.plan_y??"")+","+(i.plan_scale??"")+","+(i.plan_scale_x??"")+","+(i.plan_scale_y??"")+","+(i.plan_angle??"")+","+(i.rooms?.length||0)+","+(i.openings?.length||0)+","+(i.decor?.length||0)+";";for(const t of i.rooms||[]){const i=t.poly?.[0],s=t.poly?.[t.poly.length-1];e+=(t.poly?.length||0)+"."+(t.id||"")+"."+(t.open_to||[]).join("+")+"."+(t.area||"")+"."+JSON.stringify(t.settings||0)+"."+(t.x??"")+","+(t.y??"")+","+(t.w??"")+","+(t.h??"")+","+(i?i[0]+"/"+i[1]:"")+","+(s?s[0]+"/"+s[1]:"")+";"}}return e}get _model(){if(!this._serverCfg)return[];const t=this._cfgEpoch+"|"+this._cfgFingerprint();if(this._modelCache&&this._modelCache.key===t)return this._modelCache.model;const e=this._buildModel();return this._modelCache={key:t,model:e},e}_buildModel(){if(!this._serverCfg)return[];const t=this._renderCfg;return rn(t).map((e,i)=>{const s=t.spaces[i]?.plan_url;return e.bg&&s?{...e,bg:{...e.bg,href:s}}:e})}_spaceModel(t){const e=this._model;return e.find(e=>e.id===(t??this._space))||e[0]}get _areaToSpace(){const t={};for(const e of this._model)for(const i of e.rooms)i.area&&(t[i.area]={space:e.id,room:i});return t}get _settings(){return this._serverCfg?.settings||{}}get _showAll(){return this._settings.filter_seeded?this._showHidden:!!this._settings.show_all}_toggleShowAll(){if(this._serverCfg){if(this._settings.filter_seeded)return this._showHidden=!this._showHidden,void this.requestUpdate();this._serverCfg={...this._serverCfg,settings:{...this._serverCfg.settings,show_all:!this._settings.show_all}},this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}}_seedHiddenDevices(){if(!this._serverCfg||!this._norm||!this._canEdit)return;const t=this._serverCfg,e=function(t){const{hass:e,areaToSpace:i,markers:s,settings:o,excluded:n,iconRules:r}=t,a=!1!==o.group_lights,l=oa(s),h=ia(e,a).filter(t=>!na(e,t.eid,l)),c=new Set(h.map(t=>t.area)),d=Or(e),p=new Set(s.map(t=>t.binding)),u=[];for(const t of Object.values(e.devices)){const s=t.area_id;if(!s||!i[s])continue;if("service"===t.entry_type)continue;if(p.has("device:"+t.id))continue;const o=d[t.id]||[],l=Fr(e,t,o);let h=n.has(l)||"Group"===t.model||/scene/i.test(t.model||"")||/bridge/i.test((t.model||"")+(t.name||""))||"myheat"===l&&!!t.via_device_id;if(!h&&a&&c.has(s)){const i=(t.name_by_user||t.name||"").trim();"mdi:lightbulb"===sa(e,i,t.model,o,r)&&(h=!0)}h&&u.push("device:"+t.id)}return u}({hass:this.hass,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,firstSpaceId:this._model[0]?.id||"",iconRules:this._iconRules});if(!e.length&&t.settings?.filter_seeded)return;t.markers=t.markers||[];const i=[];for(const s of e){const e="h"+s.slice(s.indexOf(":")+1);t.markers.push({id:e,binding:s,hidden:!0}),i.push(s.slice(s.indexOf(":")+1))}const s={...t.settings||{},filter_seeded:!0};delete s.show_all,i.length&&Array.isArray(s.new_device_ids)&&(s.new_device_ids=s.new_device_ids.filter(t=>!i.includes(t))),t.settings=s,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate()}get _iconRules(){const t=this._settings.icon_rules;if(!t||!Array.isArray(t)||!t.length)return;const e=JSON.stringify(t);return e!==this._rulesCompiledSrc&&(this._rulesCompiledSrc=e,this._rulesCompiled=gt(t)),this._rulesCompiled}get _fillColors(){return Di(this._settings)}get _excluded(){const t=this._settings.exclude_integrations;return t?new Set(t):_t}willUpdate(t){t.has("_serverCfg")&&this._cfgEpoch++,this._skyPlan(),t.has("hass")&&this.hass&&(this._hookConnection(),!this._loadOk&&!this._loading&&this._loadTries<8&&this._loadFromServer(),this._maybeRebuildDevices(),this._vacTick(),this._activityTick())}updated(){this._skyRelease(),this._warmSnapshot(),this._dtMeasure();const t=this._stageEl;t&&!this._roViewport&&(this._roViewport=new ResizeObserver(()=>this._refitView()),this._roViewport.observe(t)),t&&this._booting&&!this._bootTimer&&this._bootWatch();const e=this.renderRoot.querySelector(".hdr");if(e&&t&&!this._roHdr){const i=()=>{const e=this.renderRoot.querySelector("ha-card");if(!e)return;const i=t.getBoundingClientRect().top-e.getBoundingClientRect().top,s=Math.min(Math.max(e.getBoundingClientRect().top,0),120),o=Math.round(i+s);o>=0&&o!==this._hdrH&&(this._hdrH=o),o>=0&&!this._booting&&!this._config?.kiosk&&t.clientHeight>0&&this._warmPatch({hdrH:o,stageH:t.clientHeight})};this._roHdr=new ResizeObserver(()=>requestAnimationFrame(i)),this._roHdr.observe(e),this._onWinResize=()=>requestAnimationFrame(i),window.addEventListener("resize",this._onWinResize),i()}if(t&&!this._view&&this._refitView(),this._serverStorage&&this._loadOk&&0===this._model.length&&!this._spaceDialog&&!this._importDialog&&!this._onboardingShown){this._onboardingShown=!0;const t=function(t){const e=t?.floors;if(!e||"object"!=typeof e)return[];const i=[];for(const t of Object.values(e))t&&t.floor_id&&i.push({id:t.floor_id,name:t.name||t.floor_id,level:t.level??null});return i.sort((t,e)=>{const i=t.level??1e9,s=e.level??1e9;return i!==s?i-s:t.name.localeCompare(e.name)}),i}(this.hass);t.length?this._importDialog={floors:t.map(t=>({...t,checked:!0}))}:this._openSpaceDialog("create")}}async _loadFromServer(){this._loading=!0,this._loadTries++;try{const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._loadOk=!0,this._serverStorage=!0,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._canOptimizeUndo=!(!t?.can_optimize_undo&&!e?.can_optimize_undo),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null);const i=t?.config;this._geometryHistory.clear(),this._serverCfg&&this._clearGeometryGesture(),this._serverCfg=i&&Array.isArray(i.spaces)?i:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._layout=e?.layout||{},this._layoutRev=e?.rev??0,this._unsubCfg||(this._unsubCfg=await this.hass.connection.subscribeEvents(t=>{const e=Number(t?.data?.rev??-1);e!==this._cfgRev&&this._reloadConfigOnly(!1,e)},"houseplan_config_updated")),this.hass.callWS({type:"houseplan/trail/get"}).then(t=>{this._vacSrvTrails=t?.trails||{},this.requestUpdate()}).catch(()=>{}),this._unsubTrail||(this._unsubTrail=await this.hass.connection.subscribeEvents(async()=>{try{const t=await this.hass.callWS({type:"houseplan/trail/get"});this._vacSrvTrails=t?.trails||{},this.requestUpdate()}catch{}},"houseplan_trail_updated")),this._unsubLayout||(this._unsubLayout=await this.hass.connection.subscribeEvents(t=>this._onLayoutEvent(Number(t?.data?.rev??-1)),"houseplan_layout_updated"));const s=this._hashSpace(),o=this._savedNav();!this._hashApplied&&s&&this._model.find(t=>t.id===s)?(this._space=s,this._hashApplied=!0):o?.space&&!this._navApplied&&!this._hashApplied&&this._model.find(t=>t.id===o.space)?(this._space=o.space,this._navApplied=!0):this._norm&&!this._model.find(t=>t.id===this._space)&&(this._space=this._model[0]?.id||this._space),this._cacheSnapshot(),this._warmVpArmed&&this._space===this._warmVp?.space?this._warmVpArmed=!1:this._restoreZoom()}catch(t){if(this._serverCfg)this._scheduleLoadRetry();else if(this._loadTries>=8){this._serverStorage=!1;try{this._layout=JSON.parse(localStorage.getItem(Qa)||"{}")||{}}catch{this._layout={}}}}finally{this._loading=!1}this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}async _reloadConfigOnly(t=!1,e){if(!t){if(void 0!==e&&e<=this._cfgRev)return;if(this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),this._cfgWriting)return clearTimeout(this._reloadRetry),void(this._reloadRetry=window.setTimeout(()=>this._reloadConfigOnly(!1,e),400))}try{const t=await this.hass.callWS({type:"houseplan/config/get"}),e=t?.config;this._geometryHistory.clear(),this._clearGeometryGesture(),this._serverCfg=e&&Array.isArray(e.spaces)?e:null,this._cfgEpoch++,this._cfgRev=t?.rev||0,this._canOptimizeUndo=!!t?.can_optimize_undo,"boolean"==typeof t?.can_write&&(this._serverCanWrite=t.can_write),this._pendingNavMode&&this._canEdit&&!this._config?.kiosk&&(this._mode=this._pendingNavMode,this._pendingNavMode=null),this._cacheSnapshot(),this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate()}catch(t){this._showToast(this._t("toast.cfg_reload_failed",{err:this._errText(t)}))}}_scheduleLoadRetry(){if(void 0!==this._loadRetryTimer)return;const t=Math.min(8e3,500*2**Math.min(4,Math.max(1,this._loadTries-7)));this._loadRetryTimer=window.setTimeout(()=>{this._loadRetryTimer=void 0,this._loadOk||this._loading||!this.hass||this._loadFromServer()},t)}_hookConnection(){const t=this.hass?.connection;t&&t!==this._connHooked&&(this._connHooked?.removeEventListener?.("ready",this._onConnReady),t.addEventListener?.("ready",this._onConnReady),this._connHooked=t)}_display(t){return this._signer.display(this.hass,t)}_resign(){this._signer.resign(this.hass,ji(this._serverCfg))}_onLayoutEvent(t){t<=this._layoutRev||(clearTimeout(this._layoutSyncTimer),this._layoutSyncTimer=window.setTimeout(()=>{t<=this._layoutRev||this._reloadLayoutOnly()},200))}_noteLayoutRev(t){const e=t?.rev;"number"==typeof e&&e>this._layoutRev&&(this._layoutRev=e)}async _reloadLayoutOnly(){if(!this._serverStorage||!this.hass?.callWS)return;const t=new Map;for(const e of this._dirtyPos)this._layout[e]&&t.set(e,this._layout[e]);this._persistLayout.pending()&&this._persistLayout.flush();for(const[e,i]of this._sentPos)t.set(e,i);try{const e=await this.hass.callWS({type:"houseplan/layout/get"}),i={...e?.layout||{}};for(const[e,s]of t)i[e]=s;this._layout=i,this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!!e?.can_optimize_undo,this._cacheSnapshot(),this.requestUpdate()}catch{}}_maybeRebuildDevices(){const t=this.hass;if(!t?.devices||!t?.entities||!t?.areas)return;const e=Object.keys(t.devices).length+":"+Object.keys(t.entities).length+":"+Object.keys(t.areas).length+":"+(this._norm?"n":"l")+":"+pa(t,this._config?.language);e===this._regSignature&&this._devices.length||(this._regSignature=e,this._devices=aa({hass:t,areaToSpace:Object.fromEntries(Object.entries(this._areaToSpace).map(([t,e])=>[t,e.space])),markers:this._markers,settings:this._settings,excluded:this._excluded,showAll:this._showAll,firstSpaceId:this._model[0]?.id||"",loc:t=>this._t(t),iconRules:this._iconRules}),this._defPos=this._defaultPositions(),this._syncNewDevices(),this._seedHiddenDevices(),this._syncActivityRuntime())}_syncNewDevices(){if(!this._norm||!this._loadOk||!this._serverCfg)return;const t=this._devices.filter(t=>!t.marker&&!t.virtual).map(t=>t.id).sort(),e=t.join(",");if(e===this._newSyncKey)return;this._newSyncKey=e;const i=this._settings,{fresh:s,known:o}=function(t,e){if(!Array.isArray(e))return{fresh:[],known:[...t]};const i=new Set(e),s=t.filter(t=>!i.has(t));return{fresh:s,known:s.length?[...e,...s]:e}}(t,i.known_devices);if(!Array.isArray(i.known_devices)||s.length){const t=[...new Set([...i.new_device_ids||[],...s])];this._serverCfg={...this._serverCfg,settings:{...i,known_devices:o,new_device_ids:t}},this._saveConfig()}}get _newIds(){const t=this._settings.new_device_ids;return new Set(Array.isArray(t)?t:[])}_ackNewDevice(t){if(!this._newIds.has(t)||!this._serverCfg)return;const e=this._settings;this._serverCfg={...this._serverCfg,settings:{...e,new_device_ids:(e.new_device_ids||[]).filter(e=>e!==t)}},this._saveConfig(),this.requestUpdate()}get _markers(){return this._serverCfg?.markers||[]}_roomLqi(t){if(!t)return null;const e=[];for(const i of this._devices){if(i.area!==t||i.virtual)continue;const s=Zr(this.hass,i.entities);null!=s&&e.push(s)}return Qe(e)}_roomBounds(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}_defaultPositions(){const t={},e=this._config?.icon_size??2.5;for(const i of this._model){const s=e/100*Cn(i)*1.3;for(const e of i.rooms){if(!e.area)continue;const o=this._devices.filter(t=>t.area===e.area&&t.space===i.id);if(!o.length)continue;const n=this._roomBounds(e),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,h=Math.max(1,Math.round(Math.sqrt(o.length*a/Math.max(l,1)))),c=Math.ceil(o.length/h),d=a/h,p=l/Math.max(c,1),u=o.map((t,e)=>({x:n.x+r+d*(e%h+.5),y:n.y+r+p*(Math.floor(e/h)+.5)}));ei(u,n,s,.5*r),o.forEach((e,i)=>t[e.id]=un(u[i]))}}return t}_pos(t){const e=this._spaceModel(t.space),i=this._layout[t.id];if(i)if(this._norm){if(i.s===t.space)return{x:i.x*ol,y:i.y*ol}}else if(void 0===i.s)return{x:i.x,y:i.y};return this._defPos[t.id]?this._defPos[t.id]:un(Mn(e))}_savePos(t,e,i){if(this._norm){const s=this._gridPitch,o=Math.round(e/s)*s,n=Math.round(i/s)*s,r=this._layout[t.id]?.k;this._layout={...this._layout,[t.id]:{s:t.space,x:mn(o/ol),y:mn(n/ol),...r?{k:r}:{}}}}else this._layout={...this._layout,[t.id]:{x:Math.round(e),y:Math.round(i)}};this._dirtyPos.add(t.id),this._persistLayout()}_coverIndicator(t){return"cover"===t.tapAction?ci(t.entities):null}_actEntity(t){return this._coverIndicator(t)||t.primary}_displayOf(t){const e=t.marker?.display;return"ripple"===e?"icon_ripple":e||"badge"}_visualSamples(t){let e=[];const i=this._coverIndicator(t),s=Yr(this.hass,[t]);if(i)e.push(Er(this.hass,i));else if(s.length)e.push(...s.map(t=>Er(this.hass,t.eid)));else{const i=Ur(this.hass,t.entities),s=i.length?i:t.primary?[t.primary]:[];e=function(t,e,i){const s=e.map(e=>Er(t,e)),o=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category);return 1===e.length&&o.length>1&&Ar(t,e[0])?s.map(t=>"unavailable"===t.availability?t:"off"===t.state?{...t,availability:"unavailable",status:"neutral",activity:"none",edge:"none"}:{...t,status:"neutral",activity:"none",edge:"none"}):s}(this.hass,s,t.entities)}for(const i of t.entities||[]){const t=Er(this.hass,i);"alarm"!==t.status||e.some(t=>t.eid===i)||e.push(t)}return e}_deviceVisual(t){if(t.hidden)return{availability:"available",status:"neutral",activity:"none"};const e=this._visualSamples(t),i=function(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return $r;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}(e);if("alarm"===i.status)return i;if(!this._config?.live_states)return{availability:"available",status:"neutral",activity:"none"};if("unavailable"===i.availability)return i;const s=this._activityRt.get(t.id),o=this._activitySourceKey(e);return s?.sources===o&&s.flashTs&&s.flashKind&&Date.now()-s.flashTst?.device_id===e).map(([t])=>t)}_bindingHasClimate(t){return this._bindingEntities(t).some(t=>t.startsWith("climate."))}_bindingCoverEntity(t){return ci(this._bindingEntities(t))}_bindingCoverTap(t){const e=this._bindingCoverEntity(t);if(!e)return!1;const i=String(this.hass?.states?.[e]?.attributes?.device_class||"");return!hi.has(i)}_liveHum(t){return this._config?.show_temperature&&t.primary&&ta(this.hass,t.primary)?ea(this.hass,t.entities):null}_openMoreInfo(t){t?al(this,"hass-more-info",{entityId:t}):this._showToast(this._t("toast.no_entity"))}_ctxDevice(t,e){"view"===this._mode&&(t.preventDefault(),t.stopPropagation(),e.primary?this._openMoreInfo(e.primary):this._infoCard=e)}_clickDevice(t,e){if(t.stopPropagation(),this._drag?.moved||this._suppressClick||this._holdFired)return;if("plan"===this._mode)return;if("devices"===this._mode)return void this._openMarkerDialog(e);const i=this._coverIndicator(e),s=this._actEntity(e),o=s?s.split(".")[0]:null,n=(t,i)=>{e.marker?.tap_confirm?this._tapConfirm={text:t,exec:i}:i()},r=Yr(this.hass,[e]).filter(t=>"controls"===t.via).map(t=>t.eid);if("toggle"===e.tapAction&&r.length){const t=(a=r.map(t=>this.hass.states[t]?.state),a.some(t=>"on"===t)?"turn_off":"turn_on");return void n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant",t,{entity_id:r}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}var a;const l=function(t,e,i,s){const o=t||e||("light"===i?"toggle":"info");return"more-info"===o?"more-info":"run"===o?"run"===t?"run":"info":"cover"===o?"cover"!==t||"cover"!==i||hi.has(String(s||""))?"info":"cover":"toggle"!==o||!i||li.has(i)?"info":"toggle"===t?"toggle":ai.has(i)?"cover"===i&&hi.has(String(s||""))?"info":"toggle":"info"}(e.tapAction,void 0,o,s?this.hass.states[s]?.attributes?.device_class:null);if("run"===l){const t=e.marker?.tap_target||"",i=function(t){const e=String(t||"").split(".")[0];return"automation"===e?{domain:"automation",service:"trigger"}:"script"===e?{domain:"script",service:"turn_on"}:"scene"===e?{domain:"scene",service:"turn_on"}:null}(t),s=this.hass.states[t];if(!i||!s)return void this._showToast(this._t("toast.run_target_missing"));const o=s.attributes?.friendly_name||t;return void n(this._t("confirm.tap_run",{name:o}),()=>{this.hass.callService(i.domain,i.service,{entity_id:t}).then(()=>{this._stampActivity(e.id,"event",this._activitySourceKey(this._visualSamples(e))),this.requestUpdate(),this._showToast(this._t("toast.run_started",{name:o}))}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}if("cover"===l&&i){const t=function(t){const e=String(t||"");return"closed"===e?"open_cover":"open"===e?"close_cover":"opening"===e||"closing"===e?"stop_cover":"toggle"}(this.hass.states[i]?.state);return void n(this._t("confirm.tap_cover",{name:e.name}),()=>{this.hass.callService("cover",t,{entity_id:i}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))})}"toggle"===l&&e.primary?n(this._t("confirm.tap_toggle",{name:e.name}),()=>{this.hass.callService("homeassistant","toggle",{entity_id:e.primary}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}):"more-info"===l&&e.primary?this._openMoreInfo(e.primary):this._infoCard=e}_t(t,e){return ua(pa(this.hass,this._config?.language),t,e)}get _stageEl(){return this.renderRoot.querySelector(".stage")}_contentItems(t){const e=[];for(const i of this._devices){if(i.space!==t.id||i.hidden)continue;const s=this._pos(i);e.push({minX:s.x,minY:s.y,maxX:s.x,maxY:s.y})}if(t.id===this._space){for(const t of this._openingsR){const i=Number(t.angle)*Math.PI/180,s=Math.cos(i)*t.rlen/2,o=Math.sin(i)*t.rlen/2,n=vn([[t.rx-s,t.ry-o],[t.rx+s,t.ry+o]]);n&&e.push(n)}const i=this._decorH;for(const t of this._decorList){const s=vn("line"===t.kind?[[t.x1*ol,t.y1*i],[t.x2*ol,t.y2*i]]:"text"===t.kind?[[t.x*ol,t.y*i]]:js({x:t.x*ol,y:t.y*i,w:t.w*ol,h:t.h*i,angle:t.angle}));s&&e.push(s)}for(const i of this._physicalBodiesR(t)){const t=vn(i);t&&e.push(t)}}return bn(t,e)}_frameOf(){const t=this._spaceModel(),e=this._frame,i="view"!==this._mode;if(e&&e.id===t.id&&this._bdDrag)return e;if(e&&e.id===t.id&&e.model===t&&e.layout===this._layout&&e.devs===this._devices&&e.far===this._showFar&&e.grow===i)return e;const s=xn(this._contentItems(t));let o=s.all||Sn(t),n=this._showFar?o:s.core||Sn(t);return e&&e.id===t.id&&i&&e.grow&&(n=rl(e.rect,n),o=rl(e.all,o)),this._frame={id:t.id,model:t,layout:this._layout,devs:this._devices,far:this._showFar,grow:i,rect:n,all:o,outliers:s.outliers},this._frame}_baseVb(){const t=this._frameOf().rect;return[t.x,t.y,t.w,t.h]}get _outliers(){return this._showFar?0:this._frameOf().outliers}_fitFar(){this._showFar=!0,this._frame=null,this._resetZoom()}_fitAll(){this._showFar=!0,this._frame=null,this._resetZoom()}_renderFarHint(){return this._kiosk||"view"!==this._mode||this._booting||!this._outliers?G:W`
${this._t("canvas.far_objects",{n:this._outliers})} @@ -3098,7 +3117,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow style="left:${s.toFixed(1)}%;top:${o.toFixed(1)}%" @click=${t=>{t.stopPropagation(),this._fitAll()}}> - `}_stageAspect(){const t=this._stageEl,e=this._baseVb();return t&&t.clientHeight?t.clientWidth/t.clientHeight:e[2]/e[3]}_viewOr(t){return this._view&&this._view.w?this._view:ti(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),o=i?.clientWidth||1,n=i?.clientHeight||1;return[s.x+t/o*s.w,s.y+e/n*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const o=1*Math.max(e,s),n=i-o,r=i+s-e+o;return Math.max(Math.min(n,r),Math.min(Math.max(n,r),t))};return{w:t.w,h:t.h,x:i(t.x,t.w,e.x,e.w),y:i(t.y,t.h,e.y,e.h)}}_applyView(t,e,i){const s=this._baseVb(),o=ti(s,this._stageAspect()),n=Math.min(hl.ZOOM_MAX,Math.max(hl.ZOOM_MIN,t)),r=o.w/n,a=o.h/n,l=this._viewOr(s),h=e??l.x+l.w/2,c=i??l.y+l.h/2;this._zoom=n,this._view=this._clampView({x:h-r/2,y:c-a/2,w:r,h:a},o)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const t=()=>{if(!this._booting)return;const e=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=e);const s=e-this._bootStart;s>=1200||s>=700&&i>0&&e-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(t,100)};this._bootTimer=window.setTimeout(t,100)}_bootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1;const t=this._stageEl?.clientHeight??0;!this._config?.kiosk&&t>0&&this._warmPatch({hdrH:this._hdrH,stageH:t,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ha)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._kiosk||"view"!==this._mode||this._booting||this._resumeSettling||(this._resumeSettling=!0,this._resumeStarted=performance.now(),this._resumeLastSize="",this._resumeLastChange=this._resumeStarted,this._viewportInvalidAt=0,this.requestUpdate(),this._resumeRaf&&cancelAnimationFrame(this._resumeRaf),this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick()))}_resumeSettleTick(){if(this._resumeRaf=0,!this._resumeSettling||!this.isConnected)return;if(this._kiosk||"view"!==this._mode)return this._resumeSettling=!1,void this.requestUpdate();const t=performance.now(),e=this._stageEl,i=!!e&&e.clientWidth>0&&e.clientHeight>0;if(i){const i=`${e.clientWidth}x${e.clientHeight}`;i!==this._resumeLastSize&&(this._resumeLastSize=i,this._resumeLastChange=t)}const s=t-this._resumeStarted;if(i&&s>=220&&t-this._resumeLastChange>=80||s>=750){const t=this._view;return this._resumeSettling=!1,i&&this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),void this.requestUpdate()}this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick())}_refitView(){const t=this._stageEl;if(!t||"visible"!==document.visibilityState||t.clientWidth<=0||t.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));if(this._viewportInvalidAt){const t=Date.now()-this._viewportInvalidAt;if(this._viewportInvalidAt=0,t>=Ua&&!this._resumeSettling&&(this._beginResumeSettle(),this._resumeSettling))return}if(this._resumeSettling)return;const e=this._view;this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const o=ti(this._baseVb(),this._stageAspect()),n=Math.min(hl.ZOOM_MAX,Math.max(hl.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),h=o.w/n,c=o.h/n;this._zoom=n,this._view=this._clampView({x:l[0]-t/r*h,y:l[1]-e/a*c,w:h,h:c},o)}_onWheel(t){const e=this._stageEl;if(!e)return;t.preventDefault();const i=e.getBoundingClientRect(),s=t.deltaY<0?1.15:1/1.15;this._zoomAt(t.clientX-i.left,t.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(t){const e=this._stageEl;e&&(this._zoomAt(e.clientWidth/2,e.clientHeight/2,this._zoom*(t>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const t=this._baseVb();this._zoom=1,this._view=ti(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(tl,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const t=this._zoomBySpace[this._space]||1;this._zoom=t;const e=this._stageEl;if(e&&e.clientHeight){const e=this._baseVb();return this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const e=this._baseVb();this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),this.requestUpdate()})}_stagePointerDown(t){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:t.clientX,y:t.clientY,id:t.pointerId},t.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&t.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&t.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(t))return;this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:t.clientX,sy:t.clientY,vx:e.x,vy:e.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y);this._pinchStart={dist:e,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(t){if(this._physicalRotate?.pid!==t.pointerId)if(this._physicalDrag?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId){if(this._decorDraft?.pid===t.pointerId){const e=this._decorDraft;let i=this._decorSnap(this._svgPoint(t),t.pointerType);if(t.shiftKey&&("rect"===e.kind||"ellipse"===e.kind)){const t=i[0]-e.a[0],s=i[1]-e.a[1],o=Math.max(Math.abs(t),Math.abs(s));i=this._snap([e.a[0]+(t<0?-o:o),e.a[1]+(s<0?-o:o)])}return void(this._decorDraft={...e,b:i})}if(this._decorMove?.pid!==t.pointerId)if(this._pointers.has(t.pointerId)){if(this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(t),this._pinchStart&&this._pointers.size>=2){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),s=(t[0].x+t[1].x)/2-i.left,o=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,o,this._pinchStart.zoom*e),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const e=t.clientX-this._panStart.sx,i=t.clientY-this._panStart.sy;Math.abs(e)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer)),null===this._panLock&&Math.abs(e)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(e)>1.5*Math.abs(i)?"swipe":"pan");const s=this._stageEl;if("pan"===this._panLock&&s){const t=this._baseVb(),o=this._viewOr(t),n=ti(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*o.w,y:this._panStart.vy-i/(s.clientHeight||1)*o.h,w:o.w,h:o.h},n)}}}else this._markupMove(t);else this._decorMoveUpdate(t)}else this._bdMove(t);else this._dtMove(t);else this._physicalMove(t);else this._physicalRotateMove(t)}_stagePointerUp(t){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const e=this._swipeStart;if(this._swipeStart=null,e&&e.id===t.pointerId){const i=t.clientX-e.x,s=t.clientY-e.y;if(Math.abs(i)+Math.abs(s)<8){const t=Date.now();t-this._lastTap<350&&this._resetZoom(),this._lastTap=t}const o="pan"===this._panLock?null:function(t,e,i,s,o,n=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);o&&(this._slideTo(o,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid!==t.pointerId)if(this._physicalRotate?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId){if(this._decorMove?.pid===t.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0))}else this._decorCommitDraft();else this._bdUp();else this._dtUp();else this._physicalRotateUp(t);else this._physicalUp(t)}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),rl(window,"location-changed",{replace:!1}))}_pointerDown(t,e){if("plan"===this._mode)return;if("view"===this._mode)return this._holdFired=!1,clearTimeout(this._holdTimer),void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._infoCard=e},600));t.preventDefault();const i=this._pos(e);this._drag={id:e.id,sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},ll(t),this._tip=null}_pointerMove(t,e){if(!this._drag||this._drag.id!==e.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),o=i.getBoundingClientRect(),n=this._viewOr(s),r=(t.clientX-this._drag.sx)/o.width*n.w,a=(t.clientY-this._drag.sy)/o.height*n.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=_n(this._drag.ox+r),h=_n(this._drag.oy+a);this._savePos(e,l,h)}_pointerUp(t,e){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==e.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=e.id,window.setTimeout(()=>this._drag=null,0))}_showToast(t){this._toast=t,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}get _noHover(){return hl._noHoverMq||hl._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(hl._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,o){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:o})}get _gridPitch(){return cn}get _cellCm(){const t=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(t)&&t>0?t:5}_fmtLen(t,e){const i=function(t,e,i,s){return Math.hypot(e[0]-t[0],e[1]-t[1])/i*s}(t,e,this._gridPitch,this._cellCm);return Ce(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const t=this._rszPreview;return t&&t.space===this._space?t.sp:this._serverCfg?.spaces.find(t=>t.id===this._space)}get _renderCfg(){const t=this._rszPreview;return t&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(e=>e.id===t.space?t.sp:e)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,sl}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Te(t?.rooms||[]).map(t=>[t[0]*sl,t[1]*e,t[2]*sl,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(el)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(el,JSON.stringify({space:this._space,mode:this._mode}))}catch{}}_setMode(t){if(this._kiosk&&"view"!==t)return;if(this._mode===t)return;if(this._bootSoftCancel(),("plan"===t||"decor"===t)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));const e=this._mode,i="view"!==e&&"view"!==t,s=i&&this.renderRoot.querySelector(".editorchrome-inner")?.getBoundingClientRect().height||0;i||this._cancelEditorSwapAnimations(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const o=!this._spaceModel().bg&&"view"===t!=("view"===this._mode);if("view"===this._mode&&"view"!==t){const t=this._view;this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?t.x+t.w/2:void 0,cy:t?t.y+t.h/2:void 0}}if("plan"===e&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=t,this._editorChromeMode="view"===t?e:t,this._startNavMotion("view"===e?"enter":"view"===t?"exit":"swap"),i&&this._animateEditorSwap(s),o&&(this._zoom=1,this._view=null),"view"===t){const t=this._viewModeSnap;this._viewModeSnap=null,t&&t.space===this._space?(this._zoom=t.zoom,this._view=null,requestAnimationFrame(()=>{this._stageEl&&"view"===this._mode&&this._space===t.space&&(this._applyView(t.zoom,t.cx,t.cy),this._saveZoom(),this.requestUpdate())})):t&&this._restoreZoom()}this._path=[],this._cursorPt=null,this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=Xs(Ys,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?Xs(Ys,this._imperial):this._drawWallField}get _drawWallCm(){const t=Wa(this._drawWallFieldValue);if(null==t||t<=0)return null;const e=this._imperial?2.54*t:t,i="column"===this._tool?Lo:100;return e>=1&&e<=i?e:null}get _drawWallMaxCm(){return"column"===this._tool?Lo:100}_showPhysicalRange(t=this._drawWallMaxCm){this._showToast(this._t("toast.physical_range",{max:Xs(t,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_limitReached(t){const e=this._curSpaceCfg;if(!e)return!0;const i="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=2e3:(e.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this._gridPitch;return[_n(Me(t[0],e)),_n(Me(t[1],e))]}_snapDrawPoint(t,e=!1){const i=this._path[this._path.length-1],s=e&&i?function(t,e,i,s=1/0){if(!(i>0&&t?.every(Number.isFinite)&&e?.every(Number.isFinite)))return[e[0],e[1]];const o=e[0]-t[0],n=e[1]-t[1];if(Math.abs(o)+Math.abs(n)<=1e-12)return[t[0],t[1]];const r=Math.PI/4;let a=Math.atan2(n,o);a<0&&(a+=2*Math.PI);const l=Math.floor(a/r+.5)%8,[h,c]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=h*h+c*c;let p=Math.max(0,Math.round((o*h+n*c)/(i*d)));if(Number.isFinite(s)&&s>=0){const e=(t,e)=>{if(!e)return 1/0;const o=e>0?s-t:t+s;return Math.max(0,Math.floor((o+1e-9*i)/i))};p=Math.min(p,e(t[0],h),e(t[1],c))}return[t[0]+h*p*i,t[1]+c*p*i]}(i,t,this._gridPitch,ln):t;return this._snap(s)}_samePt(t,e){return Ee(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this._serverCfg?.spaces||[]){delete t.segments;const e=new Set,i=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),s=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=an);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&i(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&s(t.id)).map(t=>({...t,cm:Math.max(1,Math.min(100,Number(t.cm)||15))})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&i(t.id)&&o(t.center)&&s(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qo(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Ho(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&i(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&s(t.id)).map(t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],i=[];for(let s=1;st.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Kn(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=po(t.walls,t.rooms||[],dn,1,e),t.walls.length||delete t.walls}}}get _cfgWriting(){return this._writesPending>0}_writeConfig(){this._writesPending++,this._writeChain=this._writeChain.catch(()=>{}).then(async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const t=await this.hass.callWS({type:"houseplan/config/set",config:this._serverCfg,expected_rev:this._cfgRev});this._cfgRev=t?.rev??this._cfgRev+1});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshot(t=this._space){const e=this._serverCfg?.spaces.find(e=>e.id===t);if(!e)return null;const i=t=>JSON.parse(JSON.stringify(t)),s={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==e[t]&&(s[t]=e[t]);return{spaceId:t,rooms:i(e.rooms||[]),...Array.isArray(e.openings)?{openings:i(e.openings)}:{},...Array.isArray(e.walls)?{walls:i(e.walls)}:{},...Array.isArray(e.open_spans)?{open_spans:i(e.open_spans)}:{},...Array.isArray(e.room_drafts)?{room_drafts:i(e.room_drafts)}:{},...Array.isArray(e.partitions)?{partitions:i(e.partitions)}:{},...Array.isArray(e.wall_columns)?{wall_columns:i(e.wall_columns)}:{},...Array.isArray(e.decor)?{decor:i(e.decor)}:{},plan_transform:s}}_recordGeometry(t,e){if(!e)return;const i=this._geometrySnapshot(e.spaceId);i&&JSON.stringify(e)!==JSON.stringify(i)&&(this._geometryHistory.push({name:t,before:e,after:i}),this.requestUpdate())}_clearGeometryGesture(){this._path=[],this._cursorPt=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._openWallAnchor=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===t.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid!==t.pointerId&&this._physicalRotate?.pid!==t.pointerId)return this._decorDraft?.pid===t.pointerId?(this._decorDraft=null,void this.requestUpdate()):void(this._decorMove?.pid!==t.pointerId&&this._dtDrag?.pid!==t.pointerId&&this._bdDrag?.pid!==t.pointerId?(this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null)):this._cancelDecorGesture());this._cancelPhysicalGesture()}_applyGeometryState(t){const e=this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(!e)return!1;const i=t=>JSON.parse(JSON.stringify(t));e.rooms=i(t.rooms),void 0!==t.openings?e.openings=i(t.openings):delete e.openings,void 0!==t.walls?e.walls=i(t.walls):delete e.walls,void 0!==t.open_spans?e.open_spans=i(t.open_spans):delete e.open_spans,void 0!==t.room_drafts?e.room_drafts=i(t.room_drafts):delete e.room_drafts,void 0!==t.partitions?e.partitions=i(t.partitions):delete e.partitions,void 0!==t.wall_columns?e.wall_columns=i(t.wall_columns):delete e.wall_columns,void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];return Object.assign(e,i(t.plan_transform||{})),this._clearGeometryGesture(),this._space!==t.spaceId&&(this._space=t.spaceId,this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&qe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&Ve(t,i)})}_pointInRoom(t,e){return e.poly?Ie(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!cs(t))return!0;const e=t.length,i=.001;for(let s=0;st?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(e.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._rszDrag||e.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...this._spaceModel().rooms].reverse().find(t=>this._pointInRoom(i,t));return void(this._rszSel=t?.id||null)}if("delroom"===this._tool)return void this._deleteRoomClick(i);if("opening"===this._tool)return void this._openingClick(i);if("merge"===this._tool)return void this._mergeClick(i);if("wallthick"===this._tool)return void this._wallThickClick(i);if("openwall"===this._tool)return void this._openWallClick(i);if("closewall"===this._tool)return void this._closeWallClick(i);if("split"===this._tool)return void this._splitClick(i);if("partition"===this._tool)return void this._partitionClick(i,t.shiftKey);if("column"===this._tool)return void this._columnClick(i);const s=this._snapDrawPoint(i,t.shiftKey);if(t.ctrlKey||t.metaKey)return t.preventDefault(),void this._closeRoomContour(!0);const o=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length){const t=this._draftEndAt(s);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(t=>[...t]):t.draft.points.map(t=>[...t]),void(this._draftSegmentCms=t.reverse?[...t.draft.segments].reverse().map(t=>t.cm):t.draft.segments.map(t=>t.cm))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[s]))}const n=this._path[this._path.length-1];if(this._samePt(s,n))return;if(o)return void this._closeRoomContour();const r=this._draftEndAt(s,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(null==this._drawWallCm)return void this._showPhysicalRange(100);if(this._path.length>=500)return void this._showToast(this._t("toast.physical_limit"));const a=this._curSpaceCfg;!this._activeDraftId&&(a?.room_drafts||[]).length>=200||this._draftSegmentCount(a)>=2e3?this._showToast(this._t("toast.physical_limit")):(this._path=[...this._path,s],this._persistActiveDraftSegment())}_draftEndAt(t,e){const i=this._viewOr(this._baseVb()),s=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?i.w/this._stageEl.clientWidth*12:0);for(const i of this._spaceModel().room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const o=i.points[0],n=i.points[i.points.length-1];if(Math.hypot(t[0]-n[0],t[1]-n[1])<=s)return{draft:i,reverse:!1};if(Math.hypot(t[0]-o[0],t[1]-o[1])<=s)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this._curSpaceCfg;if(!e||!this._path.length)return;const i=Array.isArray(e.room_drafts)?e.room_drafts:[],s=this._activeDraftId?i.find(t=>t.id===this._activeDraftId):null,o=i.find(e=>e.id===t.draft.id);if(!o)return;const n=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),r=t.reverse?(o.segments||[]).map(t=>({...t})):[...o.segments||[]].reverse().map(t=>({...t})),a=this._path[this._path.length-1],l=this._samePt(a,n[0]),h=l?null:this._drawWallCm;if(!l&&null==h)return void this._showPhysicalRange(100);const c=this._draftSegmentCms.map((t,e)=>({...s?.segments?.[e]||{},cm:t})),d=[...this._path.map(t=>[...t]),...l?n.slice(1):n],p=[...c,...null==h?[]:[{cm:h}],...r],u=d.length>=4&&this._samePt(d[0],d[d.length-1]),_=u?d.slice(0,-1):d,m=u?p.slice(0,-1):p;if(_.length>500)return void this._showToast(this._t("toast.physical_limit"));const g=(s?.segments?.length||0)+(o.segments?.length||0);if(this._draftSegmentCount(e)-g+m.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(u){if(this._contourSelfIntersects(_)||Ye(_)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(_);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const f=this._geometrySnapshot(),v=this._activeDraftId||t.draft.id,y={...s||o,id:v,points:_.map(t=>[t[0]/sl,t[1]/sl]),segments:m};e.room_drafts=i.filter(e=>e.id!==t.draft.id&&(!this._activeDraftId||e.id!==this._activeDraftId)),e.room_drafts.push(y),this._activeDraftId=v,this._resumeDraftBySpace[this._space]=v,this._draftSegmentCms=m.map(t=>Number(t.cm)),this._path=_,this._physicalSel=null,this._recordGeometry(this._t("history.draft_merge"),f),this._saveConfig(),u&&(this._closingWallCm=Number(p[p.length-1]?.cm)||Ys,this._path=[..._,[..._[0]]],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0)}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const t=this._drawWallCm;if(null==t)return;this._draftSegmentCms=[...this._draftSegmentCms,t];const e=this._geometrySnapshot(),i=this._curSpaceCfg;i.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const s=i.room_drafts.findIndex(t=>t.id===this._activeDraftId),o={...s>=0?i.room_drafts[s]:{},id:this._activeDraftId,points:this._path.map(t=>[t[0]/sl,t[1]/sl]),segments:this._draftSegmentCms.map((t,e)=>({...s>=0?i.room_drafts[s]?.segments?.[e]:{},cm:t}))};s>=0?i.room_drafts[s]=o:i.room_drafts.push(o),this._recordGeometry(this._t("history.draft_segment"),e),this._saveConfig()}_partitionClick(t,e){const i=this._snapDrawPoint(t,e);if(!this._path.length)return void(this._path=[i]);const s=this._path[0];if(this._samePt(s,i))return;const o=this._drawWallCm;if(null==o)return void this._showPhysicalRange(100);if(!this._curSpaceCfg||this._limitReached("partition"))return;const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.partitions||=[];const a="partition-"+Date.now().toString(36);r.partitions.push({id:a,a:[s[0]/sl,s[1]/sl],b:[i[0]/sl,i[1]/sl],cm:o}),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._recordGeometry(this._t("history.partition_add"),n),this._saveConfig()}_columnClick(t){const e=this._snap(t),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Lo);if(!this._curSpaceCfg||this._limitReached("column"))return;const s={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qo(i),angle:0},o=(this._spaceModel().wall_columns||[]).find(t=>Xo(t,s,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...s,center:[e[0]/sl,e[1]/sl]}),this._recordGeometry(this._t("history.column_add"),n),this._saveConfig()}_openPhysicalDialog(t,e,i){const s=this._spaceModel();if("partition"===t){const i=s.partitions.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===t){const i=s.wall_columns.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),shape:i.shape,angle:String("square"===i.shape?Ho(i.angle):0)})}else{const o=s.room_drafts.find(t=>t.id===e),n=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,i||0));o?.segments[n]&&(this._physicalDialog={kind:t,id:e,segment:n,cm:Xs(o.segments[n].cm,this._imperial),length:this._fmtLen(o.points[n],o.points[n+1])})}}_physicalDown(t,e,i){t.stopPropagation(),ll(t);const s=this._spaceModel(),o=this._svgPoint(t),n=[];for(const t of[...s.wall_columns].reverse())Zo(o,jo(t,this._cellCm,this._gridPitch))&&n.push({kind:"column",id:t.id});for(const t of[...s.partitions].reverse()){const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);e&&Zo(o,e)&&n.push({kind:"partition",id:t.id})}n.some(t=>t.kind===e&&t.id===i)||n.unshift({kind:e,id:i});const r=n.map(t=>`${t.kind}:${t.id}`).sort().join("|"),a=performance.now(),l=this._physicalPickCycle,h=n.length>1&&l?.signature===r&&a-l.at>380&&a-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%n.length:Math.max(0,n.findIndex(t=>t.kind===e&&t.id===i));this._physicalPickCycle={signature:r,index:h,x:t.clientX,y:t.clientY,at:a},e=n[h].kind,i=n[h].id;const c="partition"===e?s.partitions.find(t=>t.id===i):s.wall_columns.find(t=>t.id===i);c&&(this._physicalSel={kind:e,id:i},this._physicalDrag={pid:t.pointerId,kind:e,id:i,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,i){const s="partition"===t?[e.a,e.b]:[e.center],o=s.map(t=>t[0]),n=s.map(t=>t[1]);return[Math.max(-an*sl-Math.min(...o),Math.min(an*sl-Math.max(...o),i[0])),Math.max(-an*sl-Math.min(...n),Math.min(an*sl-Math.max(...n),i[1]))]}_physicalMove(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s="partition"===e.kind?e.base.a:e.base.center,o=this._snap([s[0]+i[0]-e.start[0],s[1]+i[1]-e.start[1]]),n=this._clampPhysicalDelta(e.kind,e.base,[o[0]-s[0],o[1]-s[1]]);this._physicalDrag={...e,delta:n,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);if(!this._curSpaceCfg)return;const i=this._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;t&&(t.a=[(s.a[0]+e.delta[0])/sl,(s.a[1]+e.delta[1])/sl],t.b=[(s.b[0]+e.delta[0])/sl,(s.b[1]+e.delta[1])/sl])}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),s=e.base,o={...s,center:[s.center[0]+e.delta[0],s.center[1]+e.delta[1]]};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));t&&(t.center=[(s.center[0]+e.delta[0])/sl,(s.center[1]+e.delta[1])/sl])}this._recordGeometry(this._t("history.physical_move"),e.before),this._saveConfig()}_registerPhysicalTap(t,e,i){const s=performance.now(),o=this._physicalLastTap?.kind===t&&this._physicalLastTap.id===e&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:t,id:e,segment:i,at:s},o&&(this._physicalLastTap=null,this._openPhysicalDialog(t,e,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape)return;t.preventDefault(),t.stopPropagation(),ll(t);const i=this._svgPoint(t);this._physicalSel={kind:"column",id:e.id},this._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,baseAngle:Ho(e.angle),angle:Ho(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const i=this._svgPoint(t),s=180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,o=e.baseAngle+s-e.startAngle,n=Ho(t.shiftKey?o:5*Math.round(o/5));this._physicalRotate={...e,angle:n,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;if(t.preventDefault(),t.stopPropagation(),this._physicalRotate=null,!e.moved||!this._curSpaceCfg)return;const i=this._spaceModel().wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const s={...i,angle:e.angle};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,s,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);o&&(o.angle=e.angle,this._recordGeometry(this._t("history.physical_edit"),e.before),this._saveConfig())}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Pe(e):null;i&&t.push({id:e.id,poly:i})}return t}_rszOpenings(){return this._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen}))}_rszOpts(){return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch}}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const o=JSON.parse(i.snap),n={...s,rooms:o.rooms,openings:o.openings,walls:o.walls,open_spans:o.open_spans};Array.isArray(o.open_spans)&&o.open_spans.length||delete n.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=n.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/sl,t[1]/r]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[t,i]of Object.entries(e)){const e=(n.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/sl,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=n.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const o=s.poly.map(t=>[t[0]*sl,t[1]*r]);if(e.poly.length===o.length)for(let t=0;tt.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const o=[...s.poly[i]],n=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:o,b:n,n:ns(s.poly,i)}}(s,e,i);o&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:o,rooms:s,openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszCornerDown(t,e,i,s){"resize"!==this._tool||this._rszDrag||(t.stopPropagation(),t.preventDefault(),ll(t),this._rszDrag={kind:"scale",pid:t.pointerId,roomId:e,fixed:s,span0:Math.hypot(i[0]-s[0],i[1]-s[1])||1,rooms:this._rszRooms(),openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszMove(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t);if("edge"===e.kind){const t=e.plan,s=(i[0]-t.a[0])*t.n[0]+(i[1]-t.a[1])*t.n[1],o=this._snap([t.a[0]+t.n[0]*s,t.a[1]+t.n[1]*s]);let n=(o[0]-t.a[0])*t.n[0]+(o[1]-t.a[1])*t.n[1];if(n=function(t,e,i,s,o,n){if(!Number.isFinite(s)||Math.abs(s)<1e-9)return 0;const r=Math.sign(s);let a=Math.abs(s);const l=Math.max(o,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(gs(t,e,i,s,n))return s}return 0}(e.rooms,e.openings,t,n,this._gridPitch,this._rszOpts()),n===e.d&&e.moved)return;e.d=n,e.moved=!0;const r=us(e.rooms,e.openings,t,n,this._rszOpts().eps);e.changed=Object.keys(r.polys),this._rszApplyPreview(r.polys,r.openings),this._rszLive=this._rszEdgeLabels(r,t)}else{const t=e.fixed,s=this._snap(i);let o=Math.hypot(s[0]-t[0],s[1]-t[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,o,n){if(!Number.isFinite(o)||o<=0)return 1;if(vs(t,e,i,s,o,n))return o;let r=1,a=o;for(let o=0;o<28;o++){const o=(r+a)/2;vs(t,e,i,s,o,n)?r=o:a=o}return r}(e.rooms,e.openings,e.roomId,t,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const n=e.rooms.find(t=>t.id===e.roomId),r=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),a=fs(n,e.openings,r,t,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:a.poly},a.openings),this._rszLive=this._rszScaleLabels(a.poly)}this.requestUpdate()}_rszUp(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._rszPreview;this._rszDrag=null,this._rszLive=null,this._rszPreview=null;if(!(e.moved&&("edge"===e.kind?Math.abs(e.d)>1e-9:Math.abs(e.k-1)>1e-9))||!i)return this._cfgEpoch++,void this.requestUpdate();const s=JSON.parse(e.snap),o=this._curSpaceCfg;if(o){o.rooms=i.sp.rooms,o.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?o.walls=i.sp.walls:delete o.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?o.open_spans=i.sp.open_spans:delete o.open_spans;for(const t of e.changed){const e=o.rooms.find(e=>e.id===t);e?.poly&&(e.poly=hs(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(o.walls)&&o.walls.length&&(o.walls=po(o.walls,o.rooms||[],dn,1,this._cfgOpenCuts()),o.walls.length||delete o.walls)}this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._recordGeometry(this._t("history.resize_room"),s),this._saveConfig(),this.requestUpdate()}_rszCancelDrag(){this._rszDrag&&(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._cfgEpoch++,this.requestUpdate())}_rszPointerCancel(t){const e=this._rszDrag;e&&e.pid===t.pointerId&&(t.stopPropagation(),this._rszCancelDrag())}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],o=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,n=o.length,r=e.edge,a=(r+1)%n;for(const[t,e]of[[o[(r-1+n)%n],o[r]],[o[r],o[a]],[o[a],o[(a+1)%n]]])s.push({x:(t[0]+e[0])/2,y:(t[1]+e[1])/2,text:this._fmtLen(t,e)});const l="mi"===this.hass?.config?.unit_system?.length,h=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],c=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs),p=this._physicalBodiesR();for(const e of h){const o=t.polys[e]||i.rooms.find(t=>t.id===e).poly,n=c.length&&Ao(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,c,d,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||o,r=We(n),a=p.length?Ko(Vo(n,p))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(n,this._gridPitch,this._cellCm);s.push({x:r[0],y:r[1],text:bs(a,l),area:!0})}return s}_rszScaleLabels(t){const e="mi"===this.hass?.config?.unit_system?.length,i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.max(...i)-Math.min(...i),n=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Ao([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||t,l=We(a),h=this._physicalBodiesR(),c=h.length?Ko(Vo(a,h))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(a,this._gridPitch,this._cellCm);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[o,0])} × ${this._fmtLen([0,0],[n,0])}`},{x:l[0],y:l[1],text:bs(c,e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),o=`M ${s(-.7*i)} 0 H ${s(.7*i)} M 0 ${s(-.22*i)} V ${s(-i)} M ${s(-.32*i)} ${s(-.6*i)} L 0 ${s(-i)} L ${s(.32*i)} ${s(-.6*i)} M 0 ${s(.22*i)} V ${s(i)} M ${s(-.32*i)} ${s(.6*i)} L 0 ${s(i)} L ${s(.32*i)} ${s(.6*i)}`,n=[],r=this._rszRooms();for(const t of r)for(let i=0;i`}_stageAspect(){const t=this._stageEl,e=this._baseVb();return t&&t.clientHeight?t.clientWidth/t.clientHeight:e[2]/e[3]}_viewOr(t){return this._view&&this._view.w?this._view:ti(t,this._stageAspect())}_screenToVb(t,e){const i=this._stageEl,s=this._viewOr(this._baseVb()),o=i?.clientWidth||1,n=i?.clientHeight||1;return[s.x+t/o*s.w,s.y+e/n*s.h]}_clampView(t,e){const i=(t,e,i,s)=>{const o=1*Math.max(e,s),n=i-o,r=i+s-e+o;return Math.max(Math.min(n,r),Math.min(Math.max(n,r),t))};return{w:t.w,h:t.h,x:i(t.x,t.w,e.x,e.w),y:i(t.y,t.h,e.y,e.h)}}_applyView(t,e,i){const s=this._baseVb(),o=ti(s,this._stageAspect()),n=Math.min(cl.ZOOM_MAX,Math.max(cl.ZOOM_MIN,t)),r=o.w/n,a=o.h/n,l=this._viewOr(s),h=e??l.x+l.w/2,c=i??l.y+l.h/2;this._zoom=n,this._view=this._clampView({x:h-r/2,y:c-a/2,w:r,h:a},o)}_bootWatch(){clearTimeout(this._bootTimer),this._bootStart=Date.now(),this._bootLastH=-1,this._bootLastChange=this._bootStart;const t=()=>{if(!this._booting)return;const e=Date.now(),i=this._stageEl?this._stageEl.clientHeight:0;i!==this._bootLastH&&(this._bootLastH=i,this._bootLastChange=e);const s=e-this._bootStart;s>=1200||s>=700&&i>0&&e-this._bootLastChange>=250?this._bootSettled():this._bootTimer=window.setTimeout(t,100)};this._bootTimer=window.setTimeout(t,100)}_bootSettled(){if(!this._booting)return;this._refitView(),this._booting=!1;const t=this._stageEl?.clientHeight??0;!this._config?.kiosk&&t>0&&this._warmPatch({hdrH:this._hdrH,stageH:t,vp:this._warmViewportState()},!0),this._bootFading=!0,this._bootTimer=window.setTimeout(()=>{this._bootFading=!1},220),this._bootSoft=!0,clearTimeout(this._bootSoftTimer),this._bootSoftTimer=window.setTimeout(()=>{this._bootSoft=!1},Ua)}_bootSoftCancel(){this._bootSoft&&(clearTimeout(this._bootSoftTimer),this._bootSoft=!1)}_beginResumeSettle(){this._kiosk||"view"!==this._mode||this._booting||this._resumeSettling||(this._resumeSettling=!0,this._resumeStarted=performance.now(),this._resumeLastSize="",this._resumeLastChange=this._resumeStarted,this._viewportInvalidAt=0,this.requestUpdate(),this._resumeRaf&&cancelAnimationFrame(this._resumeRaf),this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick()))}_resumeSettleTick(){if(this._resumeRaf=0,!this._resumeSettling||!this.isConnected)return;if(this._kiosk||"view"!==this._mode)return this._resumeSettling=!1,void this.requestUpdate();const t=performance.now(),e=this._stageEl,i=!!e&&e.clientWidth>0&&e.clientHeight>0;if(i){const i=`${e.clientWidth}x${e.clientHeight}`;i!==this._resumeLastSize&&(this._resumeLastSize=i,this._resumeLastChange=t)}const s=t-this._resumeStarted;if(i&&s>=220&&t-this._resumeLastChange>=80||s>=750){const t=this._view;return this._resumeSettling=!1,i&&this._applyView(this._zoom,t?t.x+t.w/2:void 0,t?t.y+t.h/2:void 0),void this.requestUpdate()}this._resumeRaf=requestAnimationFrame(()=>this._resumeSettleTick())}_refitView(){const t=this._stageEl;if(!t||"visible"!==document.visibilityState||t.clientWidth<=0||t.clientHeight<=0)return void(this._viewportInvalidAt||(this._viewportInvalidAt=Date.now()));if(this._viewportInvalidAt){const t=Date.now()-this._viewportInvalidAt;if(this._viewportInvalidAt=0,t>=Wa&&!this._resumeSettling&&(this._beginResumeSettle(),this._resumeSettling))return}if(this._resumeSettling)return;const e=this._view;this._applyView(this._zoom,e?e.x+e.w/2:void 0,e?e.y+e.h/2:void 0),this.requestUpdate()}_zoomAt(t,e,i){const s=this._stageEl;if(!s)return;const o=ti(this._baseVb(),this._stageAspect()),n=Math.min(cl.ZOOM_MAX,Math.max(cl.ZOOM_MIN,i)),r=s.clientWidth,a=s.clientHeight,l=this._screenToVb(t,e),h=o.w/n,c=o.h/n;this._zoom=n,this._view=this._clampView({x:l[0]-t/r*h,y:l[1]-e/a*c,w:h,h:c},o)}_onWheel(t){const e=this._stageEl;if(!e)return;t.preventDefault();const i=e.getBoundingClientRect(),s=t.deltaY<0?1.15:1/1.15;this._zoomAt(t.clientX-i.left,t.clientY-i.top,this._zoom*s),this._saveZoom()}_stepZoom(t){const e=this._stageEl;e&&(this._zoomAt(e.clientWidth/2,e.clientHeight/2,this._zoom*(t>0?1.4:1/1.4)),this._saveZoom())}_resetZoom(){const t=this._baseVb();this._zoom=1,this._view=ti(t,this._stageAspect()),this._saveZoom()}_saveZoom(){if("view"===this._mode){this._zoomBySpace={...this._zoomBySpace,[this._space]:this._zoom};try{localStorage.setItem(el,JSON.stringify(this._zoomBySpace))}catch{}}}_restoreZoom(){const t=this._zoomBySpace[this._space]||1;this._zoom=t;const e=this._stageEl;if(e&&e.clientHeight){const e=this._baseVb();return this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),void this.requestUpdate()}this._view=null,requestAnimationFrame(()=>{if(!this._stageEl)return;const e=this._baseVb();this._applyView(t,e[0]+e[2]/2,e[1]+e[3]/2),this.requestUpdate()})}_stagePointerDown(t){if(this._vacFit)return;if(this._kiosk&&(this._cyclePausedUntil=Date.now()+6e4,0===this._pointers.size?(this._swipeStart={x:t.clientX,y:t.clientY,id:t.pointerId},t.target.closest?.(".dev, .roomlabel, .oplock")||(clearTimeout(this._kioskHoldTimer),this._kioskHoldTimer=window.setTimeout(()=>{this._kioskDialog=!0,this._swipeStart=null},3e3))):(this._swipeStart=null,clearTimeout(this._kioskHoldTimer))),this._drag)return;if(this._markup&&t.target.closest?.(".roomlabel, .rlhandle, .rszhandle, .dev, .oplock, .op-hit, button"))return;if("devices"===this._mode&&t.target.closest(".dev"))return;if("decor"===this._mode&&this._decorPointerDown(t))return;this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY});const e=this._viewOr(this._baseVb());if(1===this._pointers.size)this._panStart={sx:t.clientX,sy:t.clientY,vx:e.x,vy:e.y},this._panLock=null,this._suppressClick=!1;else if(2===this._pointers.size){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y);this._pinchStart={dist:e,zoom:this._zoom},this._panStart=null,this._panLock=null}}get _swipeZone(){return this._kiosk&&this._zoom<=1.001&&this._model.length>1}_stagePointerMove(t){if(this._physicalRotate?.pid!==t.pointerId)if(this._physicalDrag?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId){if(this._decorDraft?.pid===t.pointerId){const e=this._decorDraft;let i=this._decorSnap(this._svgPoint(t),t.pointerType);if(t.shiftKey&&("rect"===e.kind||"ellipse"===e.kind)){const t=i[0]-e.a[0],s=i[1]-e.a[1],o=Math.max(Math.abs(t),Math.abs(s));i=this._snap([e.a[0]+(t<0?-o:o),e.a[1]+(s<0?-o:o)])}return void(this._decorDraft={...e,b:i})}if(this._decorMove?.pid!==t.pointerId)if(this._pointers.has(t.pointerId)){if(this._pointers.set(t.pointerId,{x:t.clientX,y:t.clientY}),this._markup&&1===this._pointers.size&&this._markupMove(t),this._pinchStart&&this._pointers.size>=2){const t=[...this._pointers.values()],e=Math.hypot(t[0].x-t[1].x,t[0].y-t[1].y)/(this._pinchStart.dist||1),i=this._stageEl.getBoundingClientRect(),s=(t[0].x+t[1].x)/2-i.left,o=(t[0].y+t[1].y)/2-i.top;this._zoomAt(s,o,this._pinchStart.zoom*e),this._suppressClick=!0,this._saveZoom()}else if(this._panStart){const e=t.clientX-this._panStart.sx,i=t.clientY-this._panStart.sy;Math.abs(e)+Math.abs(i)>4&&(this._suppressClick=!0,clearTimeout(this._holdTimer)),null===this._panLock&&Math.abs(e)+Math.abs(i)>8&&(this._panLock=this._swipeZone&&Math.abs(e)>1.5*Math.abs(i)?"swipe":"pan");const s=this._stageEl;if("pan"===this._panLock&&s){const t=this._baseVb(),o=this._viewOr(t),n=ti(t,this._stageAspect());this._view=this._clampView({x:this._panStart.vx-e/(s.clientWidth||1)*o.w,y:this._panStart.vy-i/(s.clientHeight||1)*o.h,w:o.w,h:o.h},n)}}}else this._markupMove(t);else this._decorMoveUpdate(t)}else this._bdMove(t);else this._dtMove(t);else this._physicalMove(t);else this._physicalRotateMove(t)}_stagePointerUp(t){if(this._kiosk){clearTimeout(this._kioskHoldTimer);const e=this._swipeStart;if(this._swipeStart=null,e&&e.id===t.pointerId){const i=t.clientX-e.x,s=t.clientY-e.y;if(Math.abs(i)+Math.abs(s)<8){const t=Date.now();t-this._lastTap<350&&this._resetZoom(),this._lastTap=t}const o="pan"===this._panLock?null:function(t,e,i,s,o,n=60){if(i>1.001||s.length<2)return null;if(Math.abs(t)t.id),this._space);o&&(this._slideTo(o,i<0?"left":"right"),this._saveNav(),this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._showKioskDots())}}if(this._physicalDrag?.pid!==t.pointerId)if(this._physicalRotate?.pid!==t.pointerId)if(this._dtDrag?.pid!==t.pointerId)if(this._bdDrag?.pid!==t.pointerId)if(this._decorDraft?.pid!==t.pointerId){if(this._decorMove?.pid===t.pointerId)return this._decorMove.moved&&(this._recordGeometry(this._t("history.decor_move"),this._decorMove.before),this._saveConfig()),void(this._decorMove=null);this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null,setTimeout(()=>this._suppressClick=!1,0))}else this._decorCommitDraft();else this._bdUp();else this._dtUp();else this._physicalRotateUp(t);else this._physicalUp(t)}_clickRoom(t){var e;!this._suppressClick&&t.area&&(e="/config/areas/area/"+t.area,history.pushState(null,"",e),al(window,"location-changed",{replace:!1}))}_pointerDown(t,e){if("plan"===this._mode)return;if("view"===this._mode)return this._holdFired=!1,clearTimeout(this._holdTimer),void(this._holdTimer=window.setTimeout(()=>{this._holdFired=!0,this._infoCard=e},600));t.preventDefault();const i=this._pos(e);this._drag={id:e.id,sx:t.clientX,sy:t.clientY,ox:i.x,oy:i.y,moved:!1},hl(t),this._tip=null}_pointerMove(t,e){if(!this._drag||this._drag.id!==e.id)return;const i=this.renderRoot.querySelector(".stage");if(!i)return;const s=this._baseVb(),o=i.getBoundingClientRect(),n=this._viewOr(s),r=(t.clientX-this._drag.sx)/o.width*n.w,a=(t.clientY-this._drag.sy)/o.height*n.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0,clearTimeout(this._holdTimer));const l=_n(this._drag.ox+r),h=_n(this._drag.oy+a);this._savePos(e,l,h)}_pointerUp(t,e){if(clearTimeout(this._holdTimer),!this._drag||this._drag.id!==e.id)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&(this._selId=e.id,window.setTimeout(()=>this._drag=null,0))}_showToast(t){this._toast=t,clearTimeout(this._toastTimer),this._toastTimer=window.setTimeout(()=>{this._toast=""},3500)}get _noHover(){return cl._noHoverMq||cl._touchSeen}_notePointer(t){"touch"!==t.pointerType&&"pen"!==t.pointerType||(cl._touchSeen=!0,this._tip&&(this._tip=null))}_showTip(t,e,i,s,o){this._noHover||this._drag||(this._tip={x:t.clientX,y:t.clientY,title:e,meta:i,lqi:s,temp:o})}get _gridPitch(){return cn}get _cellCm(){const t=Number(this._curSpaceCfg?.cell_cm);return Number.isFinite(t)&&t>0?t:5}_fmtLen(t,e){const i=function(t,e,i,s){return Math.hypot(e[0]-t[0],e[1]-t[1])/i*s}(t,e,this._gridPitch,this._cellCm);return Ce(i,"mi"===this.hass?.config?.unit_system?.length)}get _curSpaceCfg(){const t=this._rszPreview;return t&&t.space===this._space?t.sp:this._serverCfg?.spaces.find(t=>t.id===this._space)}get _renderCfg(){const t=this._rszPreview;return t&&this._serverCfg?{...this._serverCfg,spaces:this._serverCfg.spaces.map(e=>e.id===t.space?t.sp:e)}:this._serverCfg}get _spaceH(){return this._curSpaceCfg,ol}get _segments(){const t=this._curSpaceCfg,e=this._spaceH;return Te(t?.rooms||[]).map(t=>[t[0]*ol,t[1]*e,t[2]*ol,t[3]*e])}_savedNav(){try{return JSON.parse(localStorage.getItem(il)||"null")}catch{return null}}_saveNav(){try{localStorage.setItem(il,JSON.stringify({space:this._space,mode:this._mode}))}catch{}}_setMode(t){if(this._kiosk&&"view"!==t)return;if(this._mode===t)return;if(this._bootSoftCancel(),("plan"===t||"decor"===t)&&!this._norm)return void this._showToast(this._t("toast.markup_needs_server"));const e=this._mode,i="view"!==e&&"view"!==t,s=i&&this.renderRoot.querySelector(".editorchrome-inner")?.getBoundingClientRect().height||0;i||this._cancelEditorSwapAnimations(),(this._decorMove||this._dtDrag||this._bdDrag)&&this._cancelDecorGesture();const o=!this._spaceModel().bg&&"view"===t!=("view"===this._mode);if("view"===this._mode&&"view"!==t){const t=this._view;this._viewModeSnap={space:this._space,zoom:this._zoom,cx:t?t.x+t.w/2:void 0,cy:t?t.y+t.h/2:void 0}}if("plan"===e&&this._activeDraftId&&(this._resumeDraftBySpace[this._space]=this._activeDraftId),this._mode=t,this._editorChromeMode="view"===t?e:t,this._startNavMotion("view"===e?"enter":"view"===t?"exit":"swap"),i&&this._animateEditorSwap(s),o&&(this._zoom=1,this._view=null),"view"===t){const t=this._viewModeSnap;this._viewModeSnap=null,t&&t.space===this._space?(this._zoom=t.zoom,this._view=null,requestAnimationFrame(()=>{this._stageEl&&"view"===this._mode&&this._space===t.space&&(this._applyView(t.zoom,t.cx,t.cy),this._saveZoom(),this.requestUpdate())})):t&&this._restoreZoom()}this._path=[],this._cursorPt=null,this._tool="draw",this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._selId=null,this._physicalSel=null,this._physicalDialog=null,this._physicalDrag=null,this._rszSel=null,this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._tip=null,this._hoverRoom=null,this._decorDraft=null,this._decorSel=null,this._decorMove=null,this._backdropDialog=null,this._decorTool="select",this._bdDrag=null,this._dtDrag=null,this._dtBox=null,"plan"===t&&(this._primeDrawWallField(),this._resumeLastDraft()),this._saveNav()}_primeDrawWallField(){null===this._drawWallField&&(this._drawWallField=Xs(Ys,this._imperial))}get _drawWallFieldValue(){return null===this._drawWallField?Xs(Ys,this._imperial):this._drawWallField}get _drawWallCm(){const t=Ba(this._drawWallFieldValue);if(null==t||t<=0)return null;const e=this._imperial?2.54*t:t,i="column"===this._tool?Lo:100;return e>=1&&e<=i?e:null}get _drawWallMaxCm(){return"column"===this._tool?Lo:100}_showPhysicalRange(t=this._drawWallMaxCm){this._showToast(this._t("toast.physical_range",{max:Xs(t,this._imperial),unit:this._t(this._imperial?"wallthick.unit_in":"wallthick.unit_cm")}))}_draftSegmentCount(t=this._curSpaceCfg){return(t?.room_drafts||[]).reduce((t,e)=>t+(Array.isArray(e.segments)?e.segments.length:0),0)}_limitReached(t){const e=this._curSpaceCfg;if(!e)return!0;const i="draft"===t?(e.room_drafts||[]).length>=200:"partition"===t?(e.partitions||[]).length>=2e3:(e.wall_columns||[]).length>=500;return i&&this._showToast(this._t("toast.physical_limit")),i}_svgPoint(t){const e=this.renderRoot.querySelector(".stage").getBoundingClientRect();return this._screenToVb(t.clientX-e.left,t.clientY-e.top)}_snap(t){const e=this._gridPitch;return[_n(Me(t[0],e)),_n(Me(t[1],e))]}_snapDrawPoint(t,e=!1){const i=this._path[this._path.length-1],s=e&&i?function(t,e,i,s=1/0){if(!(i>0&&t?.every(Number.isFinite)&&e?.every(Number.isFinite)))return[e[0],e[1]];const o=e[0]-t[0],n=e[1]-t[1];if(Math.abs(o)+Math.abs(n)<=1e-12)return[t[0],t[1]];const r=Math.PI/4;let a=Math.atan2(n,o);a<0&&(a+=2*Math.PI);const l=Math.floor(a/r+.5)%8,[h,c]=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]][l],d=h*h+c*c;let p=Math.max(0,Math.round((o*h+n*c)/(i*d)));if(Number.isFinite(s)&&s>=0){const e=(t,e)=>{if(!e)return 1/0;const o=e>0?s-t:t+s;return Math.max(0,Math.floor((o+1e-9*i)/i))};p=Math.min(p,e(t[0],h),e(t[1],c))}return[t[0]+h*p*i,t[1]+c*p*i]}(i,t,this._gridPitch,ln):t;return this._snap(s)}_samePt(t,e){return Ee(t,e)}_dropLegacySegments(){for(const t of this._serverCfg?.markers||[])"ripple"===t.display&&(t.display="icon_ripple");for(const t of this._serverCfg?.spaces||[]){delete t.segments;const e=new Set,i=t=>"string"==typeof t&&t.length>=1&&t.length<=64&&!e.has(t),s=t=>(e.add(t),!0),o=t=>Array.isArray(t)&&2===t.length&&t.every(t=>Number.isFinite(Number(t))&&Math.abs(Number(t))<=an);if(Array.isArray(t.partitions)&&(t.partitions=t.partitions.filter(t=>t&&i(t.id)&&o(t.a)&&o(t.b)&&Math.hypot(t.a[0]-t.b[0],t.a[1]-t.b[1])>1e-9&&s(t.id)).map(t=>({...t,cm:Math.max(1,Math.min(100,Number(t.cm)||15))})),t.partitions.length||delete t.partitions),Array.isArray(t.wall_columns)&&(t.wall_columns=t.wall_columns.filter(t=>t&&i(t.id)&&o(t.center)&&s(t.id)).map(t=>({id:t.id,shape:"circle"===t.shape?"circle":"square",center:[Number(t.center[0]),Number(t.center[1])],cm:qo(Number(t.cm)||15),..."circle"===t.shape?{}:{angle:Ho(t.angle)}})),t.wall_columns.length||delete t.wall_columns),Array.isArray(t.room_drafts)&&(t.room_drafts=t.room_drafts.filter(t=>t&&i(t.id)&&Array.isArray(t.points)&&t.points.length>=2&&t.points.every(o)&&s(t.id)).map(t=>{const e=[[Number(t.points[0][0]),Number(t.points[0][1])]],i=[];for(let s=1;st.points.length>=2),t.room_drafts.length||delete t.room_drafts),Array.isArray(t.walls)){const e=Kn(t.open_spans).map(t=>[t.a[0],t.a[1],t.b[0],t.b[1]]);t.walls=po(t.walls,t.rooms||[],dn,1,e),t.walls.length||delete t.walls}}}get _cfgWriting(){return this._writesPending>0}_writeConfig(){this._writesPending++,this._writeChain=this._writeChain.catch(()=>{}).then(async()=>{if(!this._serverCfg)return;this._dropLegacySegments();const t=await this.hass.callWS({type:"houseplan/config/set",config:this._serverCfg,expected_rev:this._cfgRev});this._cfgRev=t?.rev??this._cfgRev+1});return this._writeChain.finally(()=>{this._writesPending--})}_saveConfig(){this._cfgEpoch++,this._saveConfigDebounced()}_geometrySnapshot(t=this._space){const e=this._serverCfg?.spaces.find(e=>e.id===t);if(!e)return null;const i=t=>JSON.parse(JSON.stringify(t)),s={};for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])void 0!==e[t]&&(s[t]=e[t]);return{spaceId:t,rooms:i(e.rooms||[]),...Array.isArray(e.openings)?{openings:i(e.openings)}:{},...Array.isArray(e.walls)?{walls:i(e.walls)}:{},...Array.isArray(e.open_spans)?{open_spans:i(e.open_spans)}:{},...Array.isArray(e.room_drafts)?{room_drafts:i(e.room_drafts)}:{},...Array.isArray(e.partitions)?{partitions:i(e.partitions)}:{},...Array.isArray(e.wall_columns)?{wall_columns:i(e.wall_columns)}:{},...Array.isArray(e.decor)?{decor:i(e.decor)}:{},plan_transform:s}}_recordGeometry(t,e){if(!e)return;const i=this._geometrySnapshot(e.spaceId);i&&JSON.stringify(e)!==JSON.stringify(i)&&(this._geometryHistory.push({name:t,before:e,after:i}),this.requestUpdate())}_clearGeometryGesture(){this._path=[],this._cursorPt=null,this._mergeSel=null,this._mergeDialog=null,this._splitSel=null,this._pendingSplit=null,this._openWallAnchor=null,this._wallDialog=null,this._physicalDialog=null,this._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null,this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._openingDialog=null,this._rszSel=null,this._rszDrag=null,this._rszPreview=null,this._rszLive=null,this._decorDraft=null,this._decorMove=null,this._dtDrag=null,this._bdDrag=null}_stagePointerCancel(t){if(clearTimeout(this._kioskHoldTimer),this._swipeStart?.id===t.pointerId&&(this._swipeStart=null),this._physicalDrag?.pid!==t.pointerId&&this._physicalRotate?.pid!==t.pointerId)return this._decorDraft?.pid===t.pointerId?(this._decorDraft=null,void this.requestUpdate()):void(this._decorMove?.pid!==t.pointerId&&this._dtDrag?.pid!==t.pointerId&&this._bdDrag?.pid!==t.pointerId?(this._pointers.delete(t.pointerId),this._pointers.size<2&&(this._pinchStart=null),0===this._pointers.size&&(this._panStart=null,this._panLock=null)):this._cancelDecorGesture());this._cancelPhysicalGesture()}_applyGeometryState(t){const e=this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(!e)return!1;const i=t=>JSON.parse(JSON.stringify(t));e.rooms=i(t.rooms),void 0!==t.openings?e.openings=i(t.openings):delete e.openings,void 0!==t.walls?e.walls=i(t.walls):delete e.walls,void 0!==t.open_spans?e.open_spans=i(t.open_spans):delete e.open_spans,void 0!==t.room_drafts?e.room_drafts=i(t.room_drafts):delete e.room_drafts,void 0!==t.partitions?e.partitions=i(t.partitions):delete e.partitions,void 0!==t.wall_columns?e.wall_columns=i(t.wall_columns):delete e.wall_columns,void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];return Object.assign(e,i(t.plan_transform||{})),this._clearGeometryGesture(),this._space!==t.spaceId&&(this._space=t.spaceId,this._saveNav(),this._restoreZoom()),this._modelCache=null,this._frame=null,this._regSignature="",this._maybeRebuildDevices(),this._saveConfig(),this.requestUpdate(),!0}_roomAt(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&qe(t,i)})}_overlapRoom(t){return this._spaceModel().rooms.find(e=>{const i=Pe(e);return!!i&&Ve(t,i)})}_pointInRoom(t,e){return e.poly?Ie(t,e.poly):null!=e.x&&t[0]>=e.x&&t[0]<=e.x+e.w&&t[1]>=e.y&&t[1]<=e.y+e.h}_contourSelfIntersects(t){if(!cs(t))return!0;const e=t.length,i=.001;for(let s=0;st?.classList?.contains?.("roomlabel")||t?.classList?.contains?.("rlhandle")))return;if(e.some(t=>t?.classList?.contains?.("physical-hit")))return;const i=this._svgPoint(t);if("select"===this._tool)return void(this._physicalSel=null);if("resize"===this._tool){if(this._rszDrag||e.some(t=>t?.classList?.contains?.("rszhandle")))return;const t=[...this._spaceModel().rooms].reverse().find(t=>this._pointInRoom(i,t));return void(this._rszSel=t?.id||null)}if("delroom"===this._tool)return void this._deleteRoomClick(i);if("opening"===this._tool)return void this._openingClick(i);if("merge"===this._tool)return void this._mergeClick(i);if("wallthick"===this._tool)return void this._wallThickClick(i);if("openwall"===this._tool)return void this._openWallClick(i);if("closewall"===this._tool)return void this._closeWallClick(i);if("split"===this._tool)return void this._splitClick(i);if("partition"===this._tool)return void this._partitionClick(i,t.shiftKey);if("column"===this._tool)return void this._columnClick(i);const s=this._snapDrawPoint(i,t.shiftKey);if(t.ctrlKey||t.metaKey)return t.preventDefault(),void this._closeRoomContour(!0);const o=this._path.length>=3&&this._samePt(s,this._path[0]);if(!this._path.length){const t=this._draftEndAt(s);return t?(this._activeDraftId=t.draft.id,this._resumeDraftBySpace[this._space]=t.draft.id,this._path=t.reverse?[...t.draft.points].reverse().map(t=>[...t]):t.draft.points.map(t=>[...t]),void(this._draftSegmentCms=t.reverse?[...t.draft.segments].reverse().map(t=>t.cm):t.draft.segments.map(t=>t.cm))):(this._activeDraftId=null,this._draftSegmentCms=[],void(this._path=[s]))}const n=this._path[this._path.length-1];if(this._samePt(s,n))return;if(o)return void this._closeRoomContour();const r=this._draftEndAt(s,this._activeDraftId||void 0);if(r)return void this._mergeDraftEndpoint(r);if(null==this._drawWallCm)return void this._showPhysicalRange(100);if(this._path.length>=500)return void this._showToast(this._t("toast.physical_limit"));const a=this._curSpaceCfg;!this._activeDraftId&&(a?.room_drafts||[]).length>=200||this._draftSegmentCount(a)>=2e3?this._showToast(this._t("toast.physical_limit")):(this._path=[...this._path,s],this._persistActiveDraftSegment())}_draftEndAt(t,e){const i=this._viewOr(this._baseVb()),s=Math.max(.15*this._gridPitch,this._stageEl?.clientWidth?i.w/this._stageEl.clientWidth*12:0);for(const i of this._spaceModel().room_drafts||[]){if(i.id===e)continue;if(i.points.length<2)continue;const o=i.points[0],n=i.points[i.points.length-1];if(Math.hypot(t[0]-n[0],t[1]-n[1])<=s)return{draft:i,reverse:!1};if(Math.hypot(t[0]-o[0],t[1]-o[1])<=s)return{draft:i,reverse:!0}}return null}_mergeDraftEndpoint(t){const e=this._curSpaceCfg;if(!e||!this._path.length)return;const i=Array.isArray(e.room_drafts)?e.room_drafts:[],s=this._activeDraftId?i.find(t=>t.id===this._activeDraftId):null,o=i.find(e=>e.id===t.draft.id);if(!o)return;const n=t.reverse?t.draft.points.map(t=>[...t]):[...t.draft.points].reverse().map(t=>[...t]),r=t.reverse?(o.segments||[]).map(t=>({...t})):[...o.segments||[]].reverse().map(t=>({...t})),a=this._path[this._path.length-1],l=this._samePt(a,n[0]),h=l?null:this._drawWallCm;if(!l&&null==h)return void this._showPhysicalRange(100);const c=this._draftSegmentCms.map((t,e)=>({...s?.segments?.[e]||{},cm:t})),d=[...this._path.map(t=>[...t]),...l?n.slice(1):n],p=[...c,...null==h?[]:[{cm:h}],...r],u=d.length>=4&&this._samePt(d[0],d[d.length-1]),_=u?d.slice(0,-1):d,m=u?p.slice(0,-1):p;if(_.length>500)return void this._showToast(this._t("toast.physical_limit"));const g=(s?.segments?.length||0)+(o.segments?.length||0);if(this._draftSegmentCount(e)-g+m.length>2e3)return void this._showToast(this._t("toast.physical_limit"));if(u){if(this._contourSelfIntersects(_)||Ye(_)<=1e-6)return void this._showToast(this._t("toast.contour_cannot_close"));const t=this._overlapRoom(_);if(t)return void this._showToast(this._t("toast.room_overlap",{name:t.name||""}))}const f=this._geometrySnapshot(),v=this._activeDraftId||t.draft.id,y={...s||o,id:v,points:_.map(t=>[t[0]/ol,t[1]/ol]),segments:m};e.room_drafts=i.filter(e=>e.id!==t.draft.id&&(!this._activeDraftId||e.id!==this._activeDraftId)),e.room_drafts.push(y),this._activeDraftId=v,this._resumeDraftBySpace[this._space]=v,this._draftSegmentCms=m.map(t=>Number(t.cm)),this._path=_,this._physicalSel=null,this._recordGeometry(this._t("history.draft_merge"),f),this._saveConfig(),u&&(this._closingWallCm=Number(p[p.length-1]?.cm)||Ys,this._path=[..._,[..._[0]]],this._cursorPt=null,this._nameSel="",this._areaSel="",this._resetRoomDialogFields(),this._roomDialog=!0)}_persistActiveDraftSegment(){if(this._path.length<2||!this._curSpaceCfg)return;const t=this._drawWallCm;if(null==t)return;this._draftSegmentCms=[...this._draftSegmentCms,t];const e=this._geometrySnapshot(),i=this._curSpaceCfg;i.room_drafts||=[],this._activeDraftId||(this._activeDraftId="draft-"+Date.now().toString(36)),this._resumeDraftBySpace[this._space]=this._activeDraftId;const s=i.room_drafts.findIndex(t=>t.id===this._activeDraftId),o={...s>=0?i.room_drafts[s]:{},id:this._activeDraftId,points:this._path.map(t=>[t[0]/ol,t[1]/ol]),segments:this._draftSegmentCms.map((t,e)=>({...s>=0?i.room_drafts[s]?.segments?.[e]:{},cm:t}))};s>=0?i.room_drafts[s]=o:i.room_drafts.push(o),this._recordGeometry(this._t("history.draft_segment"),e),this._saveConfig()}_partitionClick(t,e){const i=this._snapDrawPoint(t,e);if(!this._path.length)return void(this._path=[i]);const s=this._path[0];if(this._samePt(s,i))return;const o=this._drawWallCm;if(null==o)return void this._showPhysicalRange(100);if(!this._curSpaceCfg||this._limitReached("partition"))return;const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.partitions||=[];const a="partition-"+Date.now().toString(36);r.partitions.push({id:a,a:[s[0]/ol,s[1]/ol],b:[i[0]/ol,i[1]/ol],cm:o}),this._path=[],this._activeDraftId=null,this._draftSegmentCms=[],this._closingWallCm=null,this._cursorPt=null,this._recordGeometry(this._t("history.partition_add"),n),this._saveConfig()}_columnClick(t){const e=this._snap(t),i=this._drawWallCm;if(null==i)return void this._showPhysicalRange(Lo);if(!this._curSpaceCfg||this._limitReached("column"))return;const s={id:"column-"+Date.now().toString(36),shape:"square",center:e,cm:qo(i),angle:0},o=(this._spaceModel().wall_columns||[]).find(t=>Xo(t,s,.02*this._gridPitch));if(o)return clearTimeout(this._duplicateColumnTimer),this._duplicateColumnId=o.id,this._duplicateColumnTimer=window.setTimeout(()=>{this._duplicateColumnId=null},900),void this._showToast(this._t("toast.column_duplicate"));const n=this._geometrySnapshot(),r=this._curSpaceCfg;r.wall_columns||=[],r.wall_columns.push({...s,center:[e[0]/ol,e[1]/ol]}),this._recordGeometry(this._t("history.column_add"),n),this._saveConfig()}_openPhysicalDialog(t,e,i){const s=this._spaceModel();if("partition"===t){const i=s.partitions.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),length:this._fmtLen(i.a,i.b)})}else if("column"===t){const i=s.wall_columns.find(t=>t.id===e);i&&(this._physicalDialog={kind:t,id:e,cm:Xs(i.cm,this._imperial),shape:i.shape,angle:String("square"===i.shape?Ho(i.angle):0)})}else{const o=s.room_drafts.find(t=>t.id===e),n=Math.max(0,Math.min(o?.segments.length?o.segments.length-1:0,i||0));o?.segments[n]&&(this._physicalDialog={kind:t,id:e,segment:n,cm:Xs(o.segments[n].cm,this._imperial),length:this._fmtLen(o.points[n],o.points[n+1])})}}_physicalDown(t,e,i){t.stopPropagation(),hl(t);const s=this._spaceModel(),o=this._svgPoint(t),n=[];for(const t of[...s.wall_columns].reverse())Zo(o,jo(t,this._cellCm,this._gridPitch))&&n.push({kind:"column",id:t.id});for(const t of[...s.partitions].reverse()){const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);e&&Zo(o,e)&&n.push({kind:"partition",id:t.id})}n.some(t=>t.kind===e&&t.id===i)||n.unshift({kind:e,id:i});const r=n.map(t=>`${t.kind}:${t.id}`).sort().join("|"),a=performance.now(),l=this._physicalPickCycle,h=n.length>1&&l?.signature===r&&a-l.at>380&&a-l.at<=1200&&Math.hypot(t.clientX-l.x,t.clientY-l.y)<=10?(l.index+1)%n.length:Math.max(0,n.findIndex(t=>t.kind===e&&t.id===i));this._physicalPickCycle={signature:r,index:h,x:t.clientX,y:t.clientY,at:a},e=n[h].kind,i=n[h].id;const c="partition"===e?s.partitions.find(t=>t.id===i):s.wall_columns.find(t=>t.id===i);c&&(this._physicalSel={kind:e,id:i},this._physicalDrag={pid:t.pointerId,kind:e,id:i,start:this._svgPoint(t),startClient:[t.clientX,t.clientY],before:this._geometrySnapshot(),moved:!1,base:JSON.parse(JSON.stringify(c)),delta:[0,0]})}_clampPhysicalDelta(t,e,i){const s="partition"===t?[e.a,e.b]:[e.center],o=s.map(t=>t[0]),n=s.map(t=>t[1]);return[Math.max(-an*ol-Math.min(...o),Math.min(an*ol-Math.max(...o),i[0])),Math.max(-an*ol-Math.min(...n),Math.min(an*ol-Math.max(...n),i[1]))]}_physicalMove(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t),s="partition"===e.kind?e.base.a:e.base.center,o=this._snap([s[0]+i[0]-e.start[0],s[1]+i[1]-e.start[1]]),n=this._clampPhysicalDelta(e.kind,e.base,[o[0]-s[0],o[1]-s[1]]);this._physicalDrag={...e,delta:n,moved:e.moved||Math.hypot(t.clientX-e.startClient[0],t.clientY-e.startClient[1])>=5}}_physicalUp(t){const e=this._physicalDrag;if(!e||e.pid!==t.pointerId)return;if(t.stopPropagation(),this._physicalDrag=null,!e.moved)return void this._registerPhysicalTap(e.kind,e.id);if(!this._curSpaceCfg)return;const i=this._curSpaceCfg;if("partition"===e.kind){const t=(i.partitions||[]).find(t=>t.id===e.id),s=e.base;t&&(t.a=[(s.a[0]+e.delta[0])/ol,(s.a[1]+e.delta[1])/ol],t.b=[(s.b[0]+e.delta[0])/ol,(s.b[1]+e.delta[1])/ol])}else{const t=(i.wall_columns||[]).find(t=>t.id===e.id),s=e.base,o={...s,center:[s.center[0]+e.delta[0],s.center[1]+e.delta[1]]};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,o,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));t&&(t.center=[(s.center[0]+e.delta[0])/ol,(s.center[1]+e.delta[1])/ol])}this._recordGeometry(this._t("history.physical_move"),e.before),this._saveConfig()}_registerPhysicalTap(t,e,i){const s=performance.now(),o=this._physicalLastTap?.kind===t&&this._physicalLastTap.id===e&&this._physicalLastTap.segment===i&&s-this._physicalLastTap.at<=360;this._physicalLastTap={kind:t,id:e,segment:i,at:s},o&&(this._physicalLastTap=null,this._openPhysicalDialog(t,e,i))}_cancelPhysicalGesture(){this._physicalDrag=null,this._physicalRotate=null,this.requestUpdate()}_physicalRotateDown(t,e){if("square"!==e.shape)return;t.preventDefault(),t.stopPropagation(),hl(t);const i=this._svgPoint(t);this._physicalSel={kind:"column",id:e.id},this._physicalRotate={pid:t.pointerId,id:e.id,center:[...e.center],startAngle:180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,baseAngle:Ho(e.angle),angle:Ho(e.angle),before:this._geometrySnapshot(),moved:!1}}_physicalRotateMove(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;t.preventDefault(),t.stopPropagation();const i=this._svgPoint(t),s=180*Math.atan2(i[1]-e.center[1],i[0]-e.center[0])/Math.PI,o=e.baseAngle+s-e.startAngle,n=Ho(t.shiftKey?o:5*Math.round(o/5));this._physicalRotate={...e,angle:n,moved:e.moved||Math.abs(o-e.baseAngle)>=.5}}_physicalRotateUp(t){const e=this._physicalRotate;if(!e||e.pid!==t.pointerId)return;if(t.preventDefault(),t.stopPropagation(),this._physicalRotate=null,!e.moved||!this._curSpaceCfg)return;const i=this._spaceModel().wall_columns.find(t=>t.id===e.id);if(!i||"square"!==i.shape)return;const s={...i,angle:e.angle};if(this._spaceModel().wall_columns.some(t=>t.id!==e.id&&Xo(t,s,.02*this._gridPitch)))return void this._showToast(this._t("toast.column_duplicate"));const o=this._curSpaceCfg.wall_columns?.find(t=>t.id===e.id);o&&(o.angle=e.angle,this._recordGeometry(this._t("history.physical_edit"),e.before),this._saveConfig())}_rszRooms(){const t=[];for(const e of this._spaceModel().rooms){const i=e.id?Pe(e):null;i&&t.push({id:e.id,poly:i})}return t}_rszOpenings(){return this._openingsR.map(t=>({id:t.id,x:t.rx,y:t.ry,length:t.rlen}))}_rszOpts(){return{minDim:this._cmToUnits(30),eps:.05*this._gridPitch}}_rszSnapshot(){return JSON.stringify(this._geometrySnapshot()||{spaceId:this._space,rooms:[],openings:[],walls:[],open_spans:[]})}_rszApplyPreview(t,e){const i=this._rszDrag,s=this._serverCfg?.spaces.find(t=>t.id===this._space);if(!i||!s)return;const o=JSON.parse(i.snap),n={...s,rooms:o.rooms,openings:o.openings,walls:o.walls,open_spans:o.open_spans};Array.isArray(o.open_spans)&&o.open_spans.length||delete n.open_spans;const r=this._spaceH;for(const[e,i]of Object.entries(t)){const t=n.rooms.find(t=>t.id===e);t&&(t.poly=i.map(t=>[t[0]/ol,t[1]/r]),delete t.x,delete t.y,delete t.w,delete t.h)}for(const[t,i]of Object.entries(e)){const e=(n.openings||[]).find(e=>e.id===t);e&&(e.x=i[0]/ol,e.y=i[1]/r)}const a=[],l=[];for(const t of i.changed){const e=i.rooms.find(e=>e.id===t),s=n.rooms.find(e=>e.id===t);if(!e||!s?.poly)continue;const o=s.poly.map(t=>[t[0]*ol,t[1]*r]);if(e.poly.length===o.length)for(let t=0;tt.id===e);if(!s||!s.poly||s.poly.length<3)return null;if(i<0||i>=s.poly.length)return null;const o=[...s.poly[i]],n=[...s.poly[(i+1)%s.poly.length]];return{roomId:e,edge:i,a:o,b:n,n:ns(s.poly,i)}}(s,e,i);o&&(this._rszDrag={kind:"edge",pid:t.pointerId,roomId:e,plan:o,rooms:s,openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszCornerDown(t,e,i,s){"resize"!==this._tool||this._rszDrag||(t.stopPropagation(),t.preventDefault(),hl(t),this._rszDrag={kind:"scale",pid:t.pointerId,roomId:e,fixed:s,span0:Math.hypot(i[0]-s[0],i[1]-s[1])||1,rooms:this._rszRooms(),openings:this._rszOpenings(),snap:this._rszSnapshot(),moved:!1,d:0,k:1,changed:[]})}_rszMove(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._svgPoint(t);if("edge"===e.kind){const t=e.plan,s=(i[0]-t.a[0])*t.n[0]+(i[1]-t.a[1])*t.n[1],o=this._snap([t.a[0]+t.n[0]*s,t.a[1]+t.n[1]*s]);let n=(o[0]-t.a[0])*t.n[0]+(o[1]-t.a[1])*t.n[1];if(n=function(t,e,i,s,o,n){if(!Number.isFinite(s)||Math.abs(s)<1e-9)return 0;const r=Math.sign(s);let a=Math.abs(s);const l=Math.max(o,1e-6);for(let s=0;s<4096&&a>1e-9;s++,a-=l){const s=r*a;if(gs(t,e,i,s,n))return s}return 0}(e.rooms,e.openings,t,n,this._gridPitch,this._rszOpts()),n===e.d&&e.moved)return;e.d=n,e.moved=!0;const r=us(e.rooms,e.openings,t,n,this._rszOpts().eps);e.changed=Object.keys(r.polys),this._rszApplyPreview(r.polys,r.openings),this._rszLive=this._rszEdgeLabels(r,t)}else{const t=e.fixed,s=this._snap(i);let o=Math.hypot(s[0]-t[0],s[1]-t[1])/(e.span0||1);if(o=Math.max(.05,Math.min(20,o)),o=function(t,e,i,s,o,n){if(!Number.isFinite(o)||o<=0)return 1;if(vs(t,e,i,s,o,n))return o;let r=1,a=o;for(let o=0;o<28;o++){const o=(r+a)/2;vs(t,e,i,s,o,n)?r=o:a=o}return r}(e.rooms,e.openings,e.roomId,t,o,this._rszOpts()),o===e.k&&e.moved)return;e.k=o,e.moved=!0;const n=e.rooms.find(t=>t.id===e.roomId),r=e.rooms.filter(t=>t.id!==e.roomId).map(t=>t.poly),a=fs(n,e.openings,r,t,o,2*this._rszOpts().eps);e.changed=[e.roomId],this._rszApplyPreview({[e.roomId]:a.poly},a.openings),this._rszLive=this._rszScaleLabels(a.poly)}this.requestUpdate()}_rszUp(t){const e=this._rszDrag;if(!e||e.pid!==t.pointerId)return;t.stopPropagation();const i=this._rszPreview;this._rszDrag=null,this._rszLive=null,this._rszPreview=null;if(!(e.moved&&("edge"===e.kind?Math.abs(e.d)>1e-9:Math.abs(e.k-1)>1e-9))||!i)return this._cfgEpoch++,void this.requestUpdate();const s=JSON.parse(e.snap),o=this._curSpaceCfg;if(o){o.rooms=i.sp.rooms,o.openings=i.sp.openings,Array.isArray(i.sp.walls)&&(i.sp.walls.length?o.walls=i.sp.walls:delete o.walls),Array.isArray(i.sp.open_spans)&&i.sp.open_spans.length?o.open_spans=i.sp.open_spans:delete o.open_spans;for(const t of e.changed){const e=o.rooms.find(e=>e.id===t);e?.poly&&(e.poly=hs(e.poly,1e-9))}this._commitOpenSpans(),Array.isArray(o.walls)&&o.walls.length&&(o.walls=po(o.walls,o.rooms||[],dn,1,this._cfgOpenCuts()),o.walls.length||delete o.walls)}this._suppressClick=!0,setTimeout(()=>this._suppressClick=!1,0),this._recordGeometry(this._t("history.resize_room"),s),this._saveConfig(),this.requestUpdate()}_rszCancelDrag(){this._rszDrag&&(this._rszDrag=null,this._rszLive=null,this._rszPreview=null,this._cfgEpoch++,this.requestUpdate())}_rszPointerCancel(t){const e=this._rszDrag;e&&e.pid===t.pointerId&&(t.stopPropagation(),this._rszCancelDrag())}_rszEdgeLabels(t,e){const i=this._rszDrag,s=[],o=t.polys[e.roomId]||i.rooms.find(t=>t.id===e.roomId).poly,n=o.length,r=e.edge,a=(r+1)%n;for(const[t,e]of[[o[(r-1+n)%n],o[r]],[o[r],o[a]],[o[a],o[(a+1)%n]]])s.push({x:(t[0]+e[0])/2,y:(t[1]+e[1])/2,text:this._fmtLen(t,e)});const l="mi"===this.hass?.config?.unit_system?.length,h=Object.keys(t.polys).length?Object.keys(t.polys):[e.roomId],c=this._spaceWalls,d=this._openPairs().flatMap(t=>t.segs),p=this._physicalBodiesR();for(const e of h){const o=t.polys[e]||i.rooms.find(t=>t.id===e).poly,n=c.length&&Ao(Object.entries(t.polys).map(([t,e])=>({id:t,poly:e})),e,c,d,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||o,r=We(n),a=p.length?Ko(Vo(n,p))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(n,this._gridPitch,this._cellCm);s.push({x:r[0],y:r[1],text:bs(a,l),area:!0})}return s}_rszScaleLabels(t){const e="mi"===this.hass?.config?.unit_system?.length,i=t.map(t=>t[0]),s=t.map(t=>t[1]),o=Math.max(...i)-Math.min(...i),n=Math.max(...s)-Math.min(...s),r=this._spaceWalls,a=r.length&&this._rszSel&&Ao([{id:this._rszSel,poly:t}],this._rszSel,r,[],this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||t,l=We(a),h=this._physicalBodiesR(),c=h.length?Ko(Vo(a,h))*Math.pow(this._cellCm/this._gridPitch,2)/1e4:ys(a,this._gridPitch,this._cellCm);return[{x:Math.min(...i),y:Math.min(...s),text:`${this._fmtLen([0,0],[o,0])} × ${this._fmtLen([0,0],[n,0])}`},{x:l[0],y:l[1],text:bs(c,e),area:!0}]}_renderResizeLayer(t){const e=Math.max(.013*t.w,5),i=e/2,s=t=>t.toFixed(1),o=`M ${s(-.7*i)} 0 H ${s(.7*i)} M 0 ${s(-.22*i)} V ${s(-i)} M ${s(-.32*i)} ${s(-.6*i)} L 0 ${s(-i)} L ${s(.32*i)} ${s(-.6*i)} M 0 ${s(.22*i)} V ${s(i)} M ${s(-.32*i)} ${s(.6*i)} L 0 ${s(i)} L ${s(.32*i)} ${s(.6*i)}`,n=[],r=this._rszRooms();for(const t of r)for(let i=0;ithis._rszEdgeDown(e,t.id,i)} @pointermove=${t=>this._rszMove(t)} @pointerup=${t=>this._rszUp(t)} @@ -3108,7 +3127,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointermove=${t=>this._rszMove(t)} @pointerup=${t=>this._rszUp(t)} @pointercancel=${t=>this._rszPointerCancel(t)} - @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),n.push(B``)}return B`${n}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*sl,ry:t.y*e,rlen:t.length*sl}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return sl}_decorResolvedStyle(t){return function(t,e,i,s=Os){const o="rect"===t?.kind||"ellipse"===t?.kind,n=t,r=/^#[0-9a-f]{6}$/i.test(String(n?.color||""))?String(n.color):s.color;return{color:r,opacity:Fs(n?.opacity,s.opacity),widthCm:Ws(t,e,i,s.widthCm),fill:!!o&&!0===n?.fill,fillColor:/^#[0-9a-f]{6}$/i.test(String(n?.fill_color||""))?String(n.fill_color):n?.fill?r:s.fillColor,fillOpacity:o&&n?.fill?Fs(n?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Os)}_decorWidthUnits(t){return((t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return Hs(o,e,i);const n=Number(t?.width);return Number.isFinite(n)&&n>0?n:Hs(s,e,i)})(t,this._cellCm,this._gridPitch,Os.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Us(yi*bi(t),this._cellCm,this._gridPitch)}return Us(yi,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return yi;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Hs(e,this._cellCm,this._gridPitch):yi*bi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(La,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(an*this._cellCm,e)):.1}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*sl,y:t.y*this._decorH,w:t.w*sl,h:t.h*this._decorH,angle:Ls(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*sl,e.y1*this._decorH],i=[e.x2*sl,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*sl,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Gs(t))}for(const t of this._spaceModel().rooms){const e=Pe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,o=this._viewOr(this._baseVb()),n="touch"===e||"pen"===e?14:8,r=s?o.w/Math.max(1,s.clientWidth)*n:this._gridPitch;return function(t,e,i,s){const o=s(t);let n=null;for(const o of e.points){const e=s(o),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!n||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},ll(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:mn(e[0]/sl),y:mn(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:0,sizeCm:Us(yi,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,o=e.y+e.h/2,n=-Ls(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-o,l=[s+r*Math.cos(n)-a*Math.sin(n),o+r*Math.sin(n)+a*Math.cos(n)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const o=Math.abs(i[0]-e[0]),n=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(o,n)>=s:o>=s&&n>=s}(t.kind,t.a,t.b,e))return;const i=sl,s=this._decorH,o=this._decorStyle,n=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=mn;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Bs(o,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),n=a(Math.min(t.a[1],t.b[1])/s),h=Math.abs(t.b[0]-t.a[0])/i,c=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:n,w:h,h:c,...Bs(o,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),n),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},ll(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,o=("line"===s.kind?s.x1:s.x)*sl,n=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([o+(i[0]-e.start[0]),n+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-o)/sl,l=(r[1]-n)/this._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=an;a=Math.max(-_-c,Math.min(_-c-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){if("decor"!==this._mode||"select"!==this._decorTool)return;if(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,"text"===e.kind)return void this._decorOpenText(e);if(!["line","rect","ellipse","furniture"].includes(e.kind))return;const i=this._decorResolvedStyle(e),s="line"===e.kind?e:null,o=this._decorBoxOf(e);this._decorShapeDialog={id:e.id,kind:e.kind,color:i.color,opacity:i.opacity,widthCm:i.widthCm,angle:Ls(s?Yi([s.x1*sl,s.y1*this._decorH],[s.x2*sl,s.y2*this._decorH]):e.angle),...s?{lengthCm:Us(Math.hypot((s.x2-s.x1)*sl,(s.y2-s.y1)*this._decorH),this._cellCm,this._gridPitch)}:{},...o?{sizeWCm:Us(o.w,this._cellCm,this._gridPitch),sizeHCm:Us(o.h,this._cellCm,this._gridPitch)}:{},..."furniture"===e.kind?{symbol:e.symbol}:{},..."rect"===e.kind||"ellipse"===e.kind?{fill:i.fill,fillColor:i.fillColor,fillOpacity:i.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1])),s=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||s?"":mi(t.entity,o),r=!(!String(t.entity??"").trim()||i||n&&!s);if(n&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+n+e.slice(t+2):`${e}${e?" ":""}${n}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:t.color||this._decorStyle.color,opacity:Fs(t.opacity,this._decorStyle.opacity),angle:Ls(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=mi(e.pickerEntity,t);if(!i)return;const s=e.text,o=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),n=Math.max(o,Math.min(s.length,this._decorTextSelection.end));if(s.length-(n-o)+i.length>200)return;const r=s.slice(0,o)+i+s.slice(n),a=o+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,o={color:t.color,opacity:Fs(t.opacity),size_cm:Number(Math.max(.1,Math.min(La,t.sizeCm)).toFixed(4)),...Ls(t.angle)?{angle:Ls(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:n,...r}=i;return{...r,text:e,...o}}const{entity:s,attr:n,unit:r,...a}=i,{angle:l,size:h,scale:c,...d}=a;return{...d,text:e,...o}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...o}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:Fs(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Fs(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,o=Bs(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*sl,s=(e.y1+e.y2)/2*this._decorH,n=Hs(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Ls(t.angle)*Math.PI/180,a=Math.cos(r)*n/2,l=Math.sin(r)*n/2,h=this._snap([i-a,s-l]),c=this._snap([i+a,s+l]),{width:d,...p}=e;return{...p,...o,x1:mn(h[0]/sl),y1:mn(h[1]/this._decorH),x2:mn(c[0]/sl),y2:mn(c[1]/this._decorH)}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*sl,s=e.h*this._decorH,n=Math.max(this._gridPitch,Me(Hs(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Me(Hs(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*sl+i/2,l=e.y*this._decorH+s/2,h=Ls(t.angle),c=qs({x:a-n/2,y:l-r/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...o,x:mn(c[0]/sl),y:mn(c[1]/this._decorH),w:n/sl,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...h?{angle:h}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*sl,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*sl,(t.y+t.h/2)*this._decorH]:[t.x*sl,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const o={...s};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(La,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const o=this._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[n,r]=this._dtPivot(o),a=this._svgPoint(t),l=this._decorBoxOf(o);this._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:n,ay:r,r0:Math.hypot(a[0]-n,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-n)/Math.PI,textSizeCm0:"text"===o.kind?this._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},ll(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),o=mn(s[0]/sl),n=mn(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(o-a)>1e-9||Math.abs(n-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:n}:{x2:o,y2:n})}if("scale"===e.kind&&e.orig){const s=Vs(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),o=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(La,e.textSizeCm0*(t/e.r0))),o=Math.abs(s-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let o=e.angle0+(s-e.a0);t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360);const n=Math.abs(o-e.angle0)>1e-6;(n||e.moved)&&(e.moved||=n,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*sl,s=t.y1*this._decorH,o=t.x2*sl,n=t.y2*this._decorH;e={id:t.id,x:Math.min(i,o),y:Math.min(s,n),w:Math.abs(o-i),h:Math.abs(n-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*sl,y:t.y*this._decorH,w:t.w*sl,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=En(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const o=sl,n=this._decorH,r=qn(On(i.w,this._cellCm,this._gridPitch,o)),a=qn(On(i.h,this._cellCm,this._gridPitch,o)),l=this._geometrySnapshot(),h=this._decorSnap(t);let c=h[0],d=h[1],p=0;const u=e?null:Bn(c,d,a*n,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(c=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:mn(c/o-r/2),y:mn(d/n-a/2),w:r,h:a,...Bs(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const o=sl,n=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*o+(r[0]-e.start[0]),l=(i.y+i.h/2)*n+(r[1]-e.start[1]);let h,c,d=Number(i.angle)||0;const p=t.shiftKey?null:Bn(a,l,i.h*n,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)h=p.cx/o-i.w/2,c=p.cy/n-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*o,l-i.h/2*n],t.pointerType,e.id);h=s[0]/o,c=s[1]/n}h=mn(h),c=mn(c),(Math.abs(h-i.x)>1e-9||Math.abs(c-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:h,y:c};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=sl,o=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const n=qs(e,i.angle,t=>this._snap(t));return{...i,x:mn(n[0]/s),y:mn(n[1]/o),w:Math.max(this._gridPitch/s,Math.min(2*an,e.w/s)),h:Math.max(this._gridPitch/o,Math.min(2*an,e.h/o))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=sl,s=this._decorH,o=e.w*i,n=e.h*s,r=function(t,e,i,s,o){const n=t+i/2,r=e+s/2,a=(Number(o)||0)*Math.PI/180,l=Math.cos(a),h=Math.sin(a),c=(t,e)=>{const i=t-n,s=e-r;return[n+i*l-s*h,r+i*h+s*l]};return[c(t,e),c(t+i,e),c(t+i,e+s),c(t,e+s)]}(e.x*i,e.y*s,o,n,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),h=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[o,0])},{x:h[0],y:h[1],text:this._fmtLen([0,0],[0,n])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}> + @lostpointercapture=${t=>this._rszPointerCancel(t)}>`),n.push(B``)}return B`${n}`}get _openingsR(){const t=this._curSpaceCfg,e=this._spaceH;return(t?.openings||[]).map(t=>({...t,rx:t.x*ol,ry:t.y*e,rlen:t.length*ol}))}_cmToUnits(t){return t/this._cellCm*this._gridPitch}get _decorList(){const t=this._curSpaceCfg;return Array.isArray(t?.decor)?t.decor:[]}get _decorH(){return ol}_decorResolvedStyle(t){return function(t,e,i,s=Os){const o="rect"===t?.kind||"ellipse"===t?.kind,n=t,r=/^#[0-9a-f]{6}$/i.test(String(n?.color||""))?String(n.color):s.color;return{color:r,opacity:Fs(n?.opacity,s.opacity),widthCm:Ws(t,e,i,s.widthCm),fill:!!o&&!0===n?.fill,fillColor:/^#[0-9a-f]{6}$/i.test(String(n?.fill_color||""))?String(n.fill_color):n?.fill?r:s.fillColor,fillOpacity:o&&n?.fill?Fs(n?.fill_opacity,.25):s.fillOpacity}}(t,this._cellCm,this._gridPitch,Os)}_decorWidthUnits(t){return((t,e,i,s=Os.widthCm)=>{const o=Number(t?.width_cm);if(Number.isFinite(o)&&o>0)return Hs(o,e,i);const n=Number(t?.width);return Number.isFinite(n)&&n>0?n:Hs(s,e,i)})(t,this._cellCm,this._gridPitch,Os.widthCm)}_decorTextSizeCm(t){if("text"===t?.kind){const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?e:Us(yi*bi(t),this._cellCm,this._gridPitch)}return Us(yi,this._cellCm,this._gridPitch)}_decorTextUnits(t){if("text"!==t.kind)return yi;const e=Number(t.size_cm);return Number.isFinite(e)&&e>0?Hs(e,this._cellCm,this._gridPitch):yi*bi(t)}_decorSmallField(t){return Math.round(100*(this._imperial?t/2.54:t))/100}_decorSmallCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(100,e)):.1}_decorTextCm(t){const e=this._imperial?2.54*t:t;return Number.isFinite(e)?Math.max(.1,Math.min(qa,e)):.1}_decorLargeField(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_decorLargeCm(t){const e=this._imperial?30.48*t:100*t;return Number.isFinite(e)?Math.max(.1,Math.min(an*this._cellCm,e)):.1}_decorBoxOf(t){return"rect"!==t.kind&&"ellipse"!==t.kind&&"furniture"!==t.kind?null:{x:t.x*ol,y:t.y*this._decorH,w:t.w*ol,h:t.h*this._decorH,angle:Ls(t.angle)||void 0}}_decorSnapGeometry(t){const e=t||"",i=this._decorSnapCache;if(i&&i.epoch===this._cfgEpoch&&i.space===this._space&&i.height===this._decorH&&i.exclude===e)return i.geometry;const s=[];for(const e of this._decorList)if(e.id!==t)if("line"===e.kind){const t=[e.x1*ol,e.y1*this._decorH],i=[e.x2*ol,e.y2*this._decorH];s.push({points:[t,i,[(t[0]+i[0])/2,(t[1]+i[1])/2]],segments:[{a:t,b:i}]})}else if("text"===e.kind)s.push({points:[[e.x*ol,e.y*this._decorH]],segments:[]});else{const t=this._decorBoxOf(e);t&&s.push(Gs(t))}for(const t of this._spaceModel().rooms){const e=Pe(t);e?.length&&s.push({points:e.flatMap((t,i)=>{const s=e[(i+1)%e.length];return[t,[(t[0]+s[0])/2,(t[1]+s[1])/2]]}),segments:e.map((t,i)=>({a:t,b:e[(i+1)%e.length]}))})}const o=function(t){return{points:t.flatMap(t=>t.points),segments:t.flatMap(t=>t.segments)}}(s);return this._decorSnapCache={epoch:this._cfgEpoch,space:this._space,height:this._decorH,exclude:e,geometry:o},o}_decorSnap(t,e="mouse",i){const s=this._stageEl,o=this._viewOr(this._baseVb()),n="touch"===e||"pen"===e?14:8,r=s?o.w/Math.max(1,s.clientWidth)*n:this._gridPitch;return function(t,e,i,s){const o=s(t);let n=null;for(const o of e.points){const e=s(o),r=Math.hypot(e[0]-t[0],e[1]-t[1]);r<=i&&(!n||rthis._snap(t)).point}_replaceDecor(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>i.id===t?{...i,...e}:i),this.requestUpdate())}_cancelDecorGesture(){const t=this._decorMove?.before||this._dtDrag?.before||this._bdDrag?.before,e=t&&this._serverCfg?.spaces.find(e=>e.id===t.spaceId);if(t&&e){const i=t=>JSON.parse(JSON.stringify(t));void 0!==t.decor?e.decor=i(t.decor):delete e.decor;for(const t of["plan_x","plan_y","plan_scale","plan_scale_x","plan_scale_y","plan_angle"])delete e[t];Object.assign(e,i(t.plan_transform||{})),this._cfgEpoch++}this._decorMove=null,this._dtDrag=null,this._bdDrag=null,this.requestUpdate()}_decorPointerDown(t){const e=this._decorTool;if("select"===e||"erase"===e?t.target.closest?.(".dshape"):null)return!0;if("line"===e||"rect"===e||"ellipse"===e){t.preventDefault();const i=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorDraft={kind:e,a:i,b:i,pid:t.pointerId},hl(t),!0}if("text"===e){const e=this._decorSnap(this._svgPoint(t),t.pointerType);return this._decorTextDialog={x:mn(e[0]/ol),y:mn(e[1]/this._decorH),text:"",color:this._decorStyle.color,opacity:this._decorStyle.opacity,angle:0,sizeCm:Us(yi,this._cellCm,this._gridPitch)},this._decorTextSelection={start:0,end:0},!0}if("furniture"===e)return!!this._furnPalette&&(t.preventDefault(),this._furnPlace(this._svgPoint(t),t.shiftKey),!0);if("select"===e&&(this._decorSel=null),this._bdMovable){const e=this._bdRect,i=this._svgPoint(t),s=e.x+e.w/2,o=e.y+e.h/2,n=-Ls(e.angle)*Math.PI/180,r=i[0]-s,a=i[1]-o,l=[s+r*Math.cos(n)-a*Math.sin(n),o+r*Math.sin(n)+a*Math.cos(n)];if(l[0]>=e.x&&l[0]<=e.x+e.w&&l[1]>=e.y&&l[1]<=e.y+e.h)return t.preventDefault(),this._bdStart(t)}return!1}_decorCommitDraft(){const t=this._decorDraft;if(this._decorDraft=null,!t)return;const e=.5*this._gridPitch;if(!function(t,e,i,s){const o=Math.abs(i[0]-e[0]),n=Math.abs(i[1]-e[1]);return"line"===t?Math.hypot(o,n)>=s:o>=s&&n>=s}(t.kind,t.a,t.b,e))return;const i=ol,s=this._decorH,o=this._decorStyle,n=this._geometrySnapshot(),r="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),a=mn;let l;if("line"===t.kind)l={id:r,kind:"line",x1:a(t.a[0]/i),y1:a(t.a[1]/s),x2:a(t.b[0]/i),y2:a(t.b[1]/s),...Bs(o,!1)};else{const e=a(Math.min(t.a[0],t.b[0])/i),n=a(Math.min(t.a[1],t.b[1])/s),h=Math.abs(t.b[0]-t.a[0])/i,c=Math.abs(t.b[1]-t.a[1])/s;l={id:r,kind:t.kind,x:e,y:n,w:h,h:c,...Bs(o,!0)}}this._curSpaceCfg.decor=[...this._decorList,l],this._decorSel=r,this._recordGeometry(this._t("history.decor_add"),n),this._saveConfig(),this.requestUpdate()}_decorShapeDown(t,e){if("decor"!==this._mode)return;const i=this._decorTool;if("text"===i){if("text"!==e.kind)return;return t.stopPropagation(),t.preventDefault(),void this._decorOpenText(e)}"select"!==i&&"erase"!==i||(t.stopPropagation(),t.preventDefault(),"erase"!==i?(this._decorSel=e.id,this._decorMove={id:e.id,start:this._svgPoint(t),orig:JSON.parse(JSON.stringify(e)),pid:t.pointerId,moved:!1,before:this._geometrySnapshot()},hl(t)):this._decorEraseConfirm={id:e.id,kind:e.kind})}_decorMoveUpdate(t){const e=this._decorMove;if("furniture"===e.orig?.kind)return void this._furnMoveUpdate(t);const i=this._svgPoint(t),s=e.orig,o=("line"===s.kind?s.x1:s.x)*ol,n=("line"===s.kind?s.y1:s.y)*this._decorH,r=this._decorSnap([o+(i[0]-e.start[0]),n+(i[1]-e.start[1])],t.pointerType,e.id);let a=(r[0]-o)/ol,l=(r[1]-n)/this._decorH;const h=e.orig,c="line"===h.kind?Math.min(h.x1,h.x2):h.x,d="line"===h.kind?Math.min(h.y1,h.y2):h.y,p="line"===h.kind?Math.abs(h.x2-h.x1):h.w||0,u="line"===h.kind?Math.abs(h.y2-h.y1):h.h||0,_=an;a=Math.max(-_-c,Math.min(_-c-p,a)),l=Math.max(-_-d,Math.min(_-d-u,l)),(a||l)&&(e.moved=!0);this._curSpaceCfg.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i=e.orig;return"line"===t.kind?{...t,x1:i.x1+a,y1:i.y1+l,x2:i.x2+a,y2:i.y2+l}:{...t,x:i.x+a,y:i.y+l}}),this.requestUpdate()}_decorShapeDbl(t,e){if("decor"!==this._mode||"select"!==this._decorTool)return;if(t.preventDefault(),t.stopPropagation(),this._decorMove=null,this._decorSel=e.id,"text"===e.kind)return void this._decorOpenText(e);if(!["line","rect","ellipse","furniture"].includes(e.kind))return;const i=this._decorResolvedStyle(e),s="line"===e.kind?e:null,o=this._decorBoxOf(e);this._decorShapeDialog={id:e.id,kind:e.kind,color:i.color,opacity:i.opacity,widthCm:i.widthCm,angle:Ls(s?Yi([s.x1*ol,s.y1*this._decorH],[s.x2*ol,s.y2*this._decorH]):e.angle),...s?{lengthCm:Us(Math.hypot((s.x2-s.x1)*ol,(s.y2-s.y1)*this._decorH),this._cellCm,this._gridPitch)}:{},...o?{sizeWCm:Us(o.w,this._cellCm,this._gridPitch),sizeHCm:Us(o.h,this._cellCm,this._gridPitch)}:{},..."furniture"===e.kind?{symbol:e.symbol}:{},..."rect"===e.kind||"ellipse"===e.kind?{fill:i.fill,fillColor:i.fillColor,fillOpacity:i.fillOpacity}:{}}}_decorOpenText(t){if("text"!==t.kind)return;let e=String(t.text??"");const i=[...e.matchAll(/\{([^{}\r\n]+)\}/g)].some(t=>!!_i(t[1])),s=String(t.unit??"").trim(),o="state"===String(t.attr??"").trim().toLowerCase()?null:t.attr,n=i||s?"":mi(t.entity,o),r=!(!String(t.entity??"").trim()||i||n&&!s);if(n&&!r){const t=e.indexOf("{}");e=t>=0?e.slice(0,t)+n+e.slice(t+2):`${e}${e?" ":""}${n}`}this._decorTextDialog={id:t.id,x:t.x,y:t.y,text:e,color:t.color||this._decorStyle.color,opacity:Fs(t.opacity,this._decorStyle.opacity),angle:Ls(t.angle),sizeCm:this._decorTextSizeCm(t),pickerEntity:t.entity||"",preserveLegacy:r||void 0},this._decorTextSelection={start:e.length,end:e.length}}_decorRememberTextSelection(t){this._decorTextSelection={start:t.selectionStart??t.value.length,end:t.selectionEnd??t.value.length}}_decorInsertLiveVariable(t){const e=this._decorTextDialog;if(!e)return;const i=mi(e.pickerEntity,t);if(!i)return;const s=e.text,o=Math.max(0,Math.min(s.length,this._decorTextSelection.start)),n=Math.max(o,Math.min(s.length,this._decorTextSelection.end));if(s.length-(n-o)+i.length>200)return;const r=s.slice(0,o)+i+s.slice(n),a=o+i.length;this._decorTextSelection={start:a,end:a},this._decorTextDialog={...e,text:r,preserveLegacy:void 0},this.updateComplete.then(()=>{const t=this.renderRoot.querySelector("textarea.dtarea");t&&(t.focus(),t.setSelectionRange(a,a))})}_decorSaveText(){const t=this._decorTextDialog,e=String(t?.text??"").replace(/\r\n?/g,"\n").trim();if(!t||!e)return void(this._decorTextDialog=null);const i=this._geometrySnapshot(),s=this._curSpaceCfg,o={color:t.color,opacity:Fs(t.opacity),size_cm:Number(Math.max(.1,Math.min(qa,t.sizeCm)).toFixed(4)),...Ls(t.angle)?{angle:Ls(t.angle)}:{}};if(t.id)s.decor=this._decorList.map(i=>{if(i.id!==t.id)return i;if("text"!==i.kind)return i;if(t.preserveLegacy){const{angle:t,size:s,scale:n,...r}=i;return{...r,text:e,...o}}const{entity:s,attr:n,unit:r,...a}=i,{angle:l,size:h,scale:c,...d}=a;return{...d,text:e,...o}});else{const i="dc"+Date.now().toString(36)+Math.random().toString(36).slice(2,5);s.decor=[...this._decorList,{id:i,kind:"text",x:t.x,y:t.y,text:e,...o}],this._decorSel=i}this._decorTextDialog=null,this._recordGeometry(this._t(t.id?"history.decor_edit":"history.decor_add"),i),this._saveConfig(),this.requestUpdate()}_decorSaveShape(){const t=this._decorShapeDialog;if(!t)return;const e=this._geometrySnapshot(),i={color:t.color,opacity:Fs(t.opacity),widthCm:Math.max(.1,Math.min(100,Number(t.widthCm)||.1)),fill:!!t.fill,fillColor:t.fillColor||t.color,fillOpacity:Fs(t.fillOpacity,.25)};this._curSpaceCfg.decor=this._decorList.map(e=>{if(e.id!==t.id)return e;const s="rect"===t.kind||"ellipse"===t.kind,o=Bs(i,s);if("line"===e.kind){const i=(e.x1+e.x2)/2*ol,s=(e.y1+e.y2)/2*this._decorH,n=Hs(Math.max(.1,Number(t.lengthCm)||.1),this._cellCm,this._gridPitch),r=Ls(t.angle)*Math.PI/180,a=Math.cos(r)*n/2,l=Math.sin(r)*n/2,h=this._snap([i-a,s-l]),c=this._snap([i+a,s+l]),{width:d,...p}=e;return{...p,...o,x1:mn(h[0]/ol),y1:mn(h[1]/this._decorH),x2:mn(c[0]/ol),y2:mn(c[1]/this._decorH)}}if("rect"===e.kind||"ellipse"===e.kind||"furniture"===e.kind){const i=e.w*ol,s=e.h*this._decorH,n=Math.max(this._gridPitch,Me(Hs(Number(t.sizeWCm),this._cellCm,this._gridPitch),this._gridPitch)),r=Math.max(this._gridPitch,Me(Hs(Number(t.sizeHCm),this._cellCm,this._gridPitch),this._gridPitch)),a=e.x*ol+i/2,l=e.y*this._decorH+s/2,h=Ls(t.angle),c=qs({x:a-n/2,y:l-r/2},h,t=>this._snap(t)),{width:d,angle:p,...u}=e;return{...u,...o,x:mn(c[0]/ol),y:mn(c[1]/this._decorH),w:n/ol,h:r/this._decorH,..."furniture"===e.kind&&t.symbol?{symbol:t.symbol}:{},...h?{angle:h}:{}}}return e}),this._decorStyle={...i,fill:"rect"===t.kind||"ellipse"===t.kind?i.fill:this._decorStyle.fill,fillColor:"rect"===t.kind||"ellipse"===t.kind?i.fillColor:this._decorStyle.fillColor,fillOpacity:"rect"===t.kind||"ellipse"===t.kind?i.fillOpacity:this._decorStyle.fillOpacity},this._decorShapeDialog=null,this._recordGeometry(this._t("history.decor_edit"),e),this._saveConfig(),this.requestUpdate()}get _dtSel(){return"decor"===this._mode&&"select"===this._decorTool&&this._decorSel&&this._decorList.find(t=>t.id===this._decorSel)||null}_dtPivot(t){return"line"===t.kind?[(t.x1+t.x2)/2*ol,(t.y1+t.y2)/2*this._decorH]:"furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind?[(t.x+t.w/2)*ol,(t.y+t.h/2)*this._decorH]:[t.x*ol,t.y*this._decorH]}_dtApply(t,e){const i=this._curSpaceCfg;i&&(i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const s={...i};"text"===i.kind&&void 0!==e.textSizeCm&&(delete s.size,delete s.scale);const o={...s};return void 0!==e.textSizeCm&&(o.size_cm=Number(Math.max(.1,Math.min(qa,e.textSizeCm)).toFixed(4))),void 0!==e.angle&&(e.angle?o.angle=Number(e.angle.toFixed(2)):delete o.angle),o}),this._cfgEpoch++,this.requestUpdate())}_dtStart(t,e,i,s){const o=this._dtSel;if(!o)return;t.stopPropagation(),t.preventDefault();const[n,r]=this._dtPivot(o),a=this._svgPoint(t),l=this._decorBoxOf(o);this._dtDrag={id:o.id,kind:e,pid:t.pointerId,ax:n,ay:r,r0:Math.hypot(a[0]-n,a[1]-r),a0:180*Math.atan2(a[1]-r,a[0]-n)/Math.PI,textSizeCm0:"text"===o.kind?this._decorTextSizeCm(o):1,angle0:"line"===o.kind?0:Number(o.angle)||0,sgx:i?.[0],sgy:i?.[1],orig:l||void 0,origShape:JSON.parse(JSON.stringify(o)),before:this._geometrySnapshot(),lineEnd:s,moved:!1},hl(t)}_dtMove(t){const e=this._dtDrag;if(!e)return;const i=this._svgPoint(t);if(void 0!==e.lineEnd&&"line"===e.origShape.kind){const s=this._decorSnap(i,t.pointerType,e.id),o=mn(s[0]/ol),n=mn(s[1]/this._decorH),r=e.origShape,a=0===e.lineEnd?r.x1:r.x2,l=0===e.lineEnd?r.y1:r.y2;return(Math.abs(o-a)>1e-9||Math.abs(n-l)>1e-9)&&(e.moved=!0),void this._replaceDecor(e.id,0===e.lineEnd?{x1:o,y1:n}:{x2:o,y2:n})}if("scale"===e.kind&&e.orig){const s=Vs(e.orig,e.sgx??1,e.sgy??1,i[0],i[1],!t.shiftKey,this._gridPitch,this._gridPitch),o=Math.abs(s.x-e.orig.x)>1e-6||Math.abs(s.y-e.orig.y)>1e-6||Math.abs(s.w-e.orig.w)>1e-6||Math.abs(s.h-e.orig.h)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._decorApplyBox(e.id,s)}if("scale"===e.kind){const t=Math.hypot(i[0]-e.ax,i[1]-e.ay);if(e.r0<1e-6)return;const s=Math.max(.1,Math.min(qa,e.textSizeCm0*(t/e.r0))),o=Math.abs(s-e.textSizeCm0)>1e-6;if(!o&&!e.moved)return;return e.moved||=o,void this._dtApply(e.id,{textSizeCm:s})}const s=180*Math.atan2(i[1]-e.ay,i[0]-e.ax)/Math.PI;let o=e.angle0+(s-e.a0);t.shiftKey||(o=5*Math.round(o/5)),o=(o%360+360)%360,o>180&&(o-=360);const n=Math.abs(o-e.angle0)>1e-6;(n||e.moved)&&(e.moved||=n,this._dtApply(e.id,{angle:o}))}_dtUp(){const t=this._dtDrag;this._dtDrag=null,t?.moved&&(this._recordGeometry(this._t("history.decor_transform"),t.before),this._saveConfig()),this.requestUpdate()}_dtMeasure(){const t=this._dtSel;if(!t)return void(this._dtBox&&(this._dtBox=null,this.requestUpdate()));let e;if("line"===t.kind){const i=t.x1*ol,s=t.y1*this._decorH,o=t.x2*ol,n=t.y2*this._decorH;e={id:t.id,x:Math.min(i,o),y:Math.min(s,n),w:Math.abs(o-i),h:Math.abs(n-s)}}else if("furniture"===t.kind||"rect"===t.kind||"ellipse"===t.kind)e={id:t.id,x:t.x*ol,y:t.y*this._decorH,w:t.w*ol,h:t.h*this._decorH};else{const i=this.renderRoot.querySelector(`text.dtext[data-id="${t.id}"]`);if(!i||"function"!=typeof i.getBBox)return;let s;try{s=i.getBBox()}catch{return}if(!s||!s.width&&!s.height)return;e={id:t.id,x:s.x,y:s.y,w:s.width,h:s.height}}const i=this._dtBox;i&&i.id===e.id&&Math.abs(i.x-e.x)<.01&&Math.abs(i.y-e.y)<.01&&Math.abs(i.w-e.w)<.01&&Math.abs(i.h-e.h)<.01||(this._dtBox=e,this.requestUpdate())}_deleteDecor(t){if(!this._decorList.some(e=>e.id===t))return;const e=this._geometrySnapshot();this._curSpaceCfg.decor=this._decorList.filter(e=>e.id!==t),this._decorSel===t&&(this._decorSel=null),this._recordGeometry(this._t("history.decor_delete"),e),this._saveConfig(),this.requestUpdate()}_decorDeleteSel(){this._decorSel&&this._deleteDecor(this._decorSel)}_confirmDecorErase(){const t=this._decorEraseConfirm;this._decorEraseConfirm=null,t&&this._deleteDecor(t.id)}get _furnWalls(){const t=this._physicalBodiesR().flatMap(t=>t.map((e,i)=>{const s=t[(i+1)%t.length];return[e[0],e[1],s[0],s[1]]}));return[...this._segments,...t]}get _furnWallReach(){return 6*this._gridPitch}_furnFieldValue(t){return Math.round(100*(this._imperial?t/30.48:t/100))/100}_furnFieldToCm(t){return e=this._imperial?30.48*t:100*t,Number.isFinite(e)?Math.max(1,Math.min(1e4,e)):1;var e}_furnPick(t){const e=function(t){const e=En(t);return e?{w:e.w,h:e.h}:{w:60,h:60}}(t);this._furnPalette={symbol:t,w:e.w,h:e.h}}_furnPlace(t,e=!1){const i=this._furnPalette,s=this._curSpaceCfg;if(!i||!s)return;const o=ol,n=this._decorH,r=qn(On(i.w,this._cellCm,this._gridPitch,o)),a=qn(On(i.h,this._cellCm,this._gridPitch,o)),l=this._geometrySnapshot(),h=this._decorSnap(t);let c=h[0],d=h[1],p=0;const u=e?null:Bn(c,d,a*n,this._furnWalls,this._furnWallReach,this._gridPitch);u&&(c=u.cx,d=u.cy,p=u.angle);const _="df"+Date.now().toString(36)+Math.random().toString(36).slice(2,5),m={id:_,kind:"furniture",symbol:i.symbol,x:mn(c/o-r/2),y:mn(d/n-a/2),w:r,h:a,...Bs(this._decorStyle,!1)};p&&(m.angle=Number(p.toFixed(2))),s.decor=[...this._decorList,m],this._decorSel=_,this._decorTool="select",this._furnPalette=null,this._recordGeometry(this._t("history.decor_add"),l),this._saveConfig(),this.requestUpdate()}_furnMoveUpdate(t){const e=this._decorMove;if("furniture"!==e.orig.kind)return;const i=e.orig,s=this._curSpaceCfg;if(!s)return;const o=ol,n=this._decorH,r=this._svgPoint(t),a=(i.x+i.w/2)*o+(r[0]-e.start[0]),l=(i.y+i.h/2)*n+(r[1]-e.start[1]);let h,c,d=Number(i.angle)||0;const p=t.shiftKey?null:Bn(a,l,i.h*n,this._furnWalls,this._furnWallReach,this._gridPitch);if(p)h=p.cx/o-i.w/2,c=p.cy/n-i.h/2,d=p.angle;else{const s=this._decorSnap([a-i.w/2*o,l-i.h/2*n],t.pointerType,e.id);h=s[0]/o,c=s[1]/n}h=mn(h),c=mn(c),(Math.abs(h-i.x)>1e-9||Math.abs(c-i.y)>1e-9||Math.abs(d-(Number(i.angle)||0))>1e-9)&&(e.moved=!0),s.decor=this._decorList.map(t=>{if(t.id!==e.id)return t;const i={...t,x:h,y:c};return d?i.angle=Number(d.toFixed(2)):delete i.angle,i}),this.requestUpdate()}_decorApplyBox(t,e){const i=this._curSpaceCfg;if(!i)return;const s=ol,o=this._decorH;i.decor=this._decorList.map(i=>{if(i.id!==t)return i;const n=qs(e,i.angle,t=>this._snap(t));return{...i,x:mn(n[0]/s),y:mn(n[1]/o),w:Math.max(this._gridPitch/s,Math.min(2*an,e.w/s)),h:Math.max(this._gridPitch/o,Math.min(2*an,e.h/o))}}),this._cfgEpoch++,this.requestUpdate()}get _furnLive(){const t=this._dtDrag;if(!t||"scale"!==t.kind||!t.orig)return null;const e=this._decorList.find(e=>e.id===t.id);if(!e||"furniture"!==e.kind)return null;const i=ol,s=this._decorH,o=e.w*i,n=e.h*s,r=function(t,e,i,s,o){const n=t+i/2,r=e+s/2,a=(Number(o)||0)*Math.PI/180,l=Math.cos(a),h=Math.sin(a),c=(t,e)=>{const i=t-n,s=e-r;return[n+i*l-s*h,r+i*h+s*l]};return[c(t,e),c(t+i,e),c(t+i,e+s),c(t,e+s)]}(e.x*i,e.y*s,o,n,Number(e.angle)||0),a=(t,e)=>[(t[0]+e[0])/2,(t[1]+e[1])/2],l=a(r[0],r[1]),h=a(r[0],r[3]);return[{x:l[0],y:l[1],text:this._fmtLen([0,0],[o,0])},{x:h[0],y:h[1],text:this._fmtLen([0,0],[0,n])}]}_renderFurnPalette(){const t=this._furnPalette,e=this._t(this._imperial?"gs.unit_ft":"gs.unit_m");return W`
t.stopPropagation()}>
${this._t("furn.title")} @@ -3143,7 +3162,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @input=${e=>this._furnPalette={...t,h:this._furnFieldToCm(Number(e.target.value))}} /> ${this._t("furn.place_hint")}
`:W`
${this._t("furn.pick_hint")}
`} -
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...tn(t.plan_aspect,sl)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?on(t,sl):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),o=Number(t?.plan_scale_x),n=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(o)&&o>0?o:r,sy:Number.isFinite(n)&&n>0?n:r,angle:Ls(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Us(e.w,this._cellCm,this._gridPitch),heightCm:Us(e.h,this._cellCm,this._gridPitch),angle:Ls(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=this._geometrySnapshot(),o=Math.min(e.w*sn,Math.max(e.w*en,Me(Hs(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),n=Math.min(e.h*sn,Math.max(e.h*en,Me(Hs(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=qs({x:i.x+i.w/2-o/2,y:i.y+i.h/2-n/2},t.angle,t=>this._snap(t));this._bdApply((r[0]-e.x)/sl,(r[1]-e.y)/sl,o/e.w,n/e.h,t.angle),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),s),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,o){const n=this._curSpaceCfg;if(!n)return;n.plan_x=Number(mn(t).toFixed(6)),n.plan_y=Number(mn(e).toFixed(6)),delete n.plan_scale,n.plan_scale_x=Number(Math.min(sn,Math.max(en,i)).toFixed(6)),n.plan_scale_y=Number(Math.min(sn,Math.max(en,s)).toFixed(6));const r=Ls(o);r?n.plan_angle=Number(r.toFixed(2)):delete n.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,o=this._bdRect;if(!s||!o)return!1;const n=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?o.x:o.x+o.w,h=a>0?o.y:o.y+o.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:s,p0:this._bdParams,fx:l,fy:h,sgx:r,sgy:a,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},ll(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,n=this._snap([t+(i[0]-e.sx),o+(i[1]-e.sy)]),r=Math.abs(n[0]-t)>1e-9||Math.abs(n[1]-o)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((n[0]-s.x)/sl,(n[1]-s.y)/sl,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,n=180*Math.atan2(e.sy-o,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-o,i[0]-s)/Math.PI-n);t.shiftKey||(r=5*Math.round(r/5)),r=Ls(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const o=Vs(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*en),n=o.w/Math.max(1e-9,s.w),r=o.h/Math.max(1e-9,s.h),a=Math.abs(n-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=qs(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/sl,(l[1]-s.y)/sl,n,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return G;const i=.02*Math.max(t.w,t.h),s=i/4,o=e.x+e.w/2,n=e.y+e.h/2,r=Ls(e.angle),a=2.2*i;return B` +
`}get _bdBase(){const t=this._curSpaceCfg;return t?.plan_url?{...tn(t.plan_aspect,ol)}:null}get _bdRect(){const t=this._curSpaceCfg;return t?.plan_url?on(t,ol):null}get _bdParams(){const t=this._curSpaceCfg,e=Number(t?.plan_x),i=Number(t?.plan_y),s=Number(t?.plan_scale),o=Number(t?.plan_scale_x),n=Number(t?.plan_scale_y),r=Number.isFinite(s)&&s>0?s:1;return{dx:Number.isFinite(e)?e:0,dy:Number.isFinite(i)?i:0,sx:Number.isFinite(o)&&o>0?o:r,sy:Number.isFinite(n)&&n>0?n:r,angle:Ls(t?.plan_angle)}}_openBackdropDialog(t){if(!this._bdMovable||!this._bdRect)return;t?.preventDefault(),t?.stopPropagation(),this._bdDrag=null;const e=this._bdRect;this._backdropDialog={widthCm:Us(e.w,this._cellCm,this._gridPitch),heightCm:Us(e.h,this._cellCm,this._gridPitch),angle:Ls(e.angle)}}_saveBackdropDialog(){const t=this._backdropDialog,e=this._bdBase,i=this._bdRect;if(!t||!e||!i)return;const s=this._geometrySnapshot(),o=Math.min(e.w*sn,Math.max(e.w*en,Me(Hs(t.widthCm,this._cellCm,this._gridPitch),this._gridPitch))),n=Math.min(e.h*sn,Math.max(e.h*en,Me(Hs(t.heightCm,this._cellCm,this._gridPitch),this._gridPitch))),r=qs({x:i.x+i.w/2-o/2,y:i.y+i.h/2-n/2},t.angle,t=>this._snap(t));this._bdApply((r[0]-e.x)/ol,(r[1]-e.y)/ol,o/e.w,n/e.h,t.angle),this._backdropDialog=null,this._recordGeometry(this._t("history.backdrop_transform"),s),this._saveConfig()}get _bdActive(){return"decor"===this._mode&&!!this._bdRect&&"backdrop"===this._decorTool}get _bdMovable(){return"decor"===this._mode&&"backdrop"===this._decorTool&&!!this._bdRect}_bdApply(t,e,i,s,o){const n=this._curSpaceCfg;if(!n)return;n.plan_x=Number(mn(t).toFixed(6)),n.plan_y=Number(mn(e).toFixed(6)),delete n.plan_scale,n.plan_scale_x=Number(Math.min(sn,Math.max(en,i)).toFixed(6)),n.plan_scale_y=Number(Math.min(sn,Math.max(en,s)).toFixed(6));const r=Ls(o);r?n.plan_angle=Number(r.toFixed(2)):delete n.plan_angle,this._cfgEpoch++,this.requestUpdate()}_bdStart(t,e,i=!1){const s=this._bdBase,o=this._bdRect;if(!s||!o)return!1;const n=this._svgPoint(t),r=e?e[0]:0,a=e?e[1]:0,l=r>0?o.x:o.x+o.w,h=a>0?o.y:o.y+o.h;return this._bdDrag={kind:i?"rotate":e?"scale":"move",pid:t.pointerId,sx:n[0],sy:n[1],base:s,p0:this._bdParams,fx:l,fy:h,sgx:r,sgy:a,rect0:{x:o.x,y:o.y,w:o.w,h:o.h,angle:o.angle},before:this._geometrySnapshot(),moved:!1},hl(t),!0}_bdMove(t){const e=this._bdDrag;if(!e)return;const i=this._svgPoint(t),s=e.base;if("move"===e.kind){const t=e.rect0.x,o=e.rect0.y,n=this._snap([t+(i[0]-e.sx),o+(i[1]-e.sy)]),r=Math.abs(n[0]-t)>1e-9||Math.abs(n[1]-o)>1e-9;if(!r&&!e.moved)return;return e.moved||=r,void this._bdApply((n[0]-s.x)/ol,(n[1]-s.y)/ol,e.p0.sx,e.p0.sy,e.p0.angle)}if("rotate"===e.kind){const s=e.rect0.x+e.rect0.w/2,o=e.rect0.y+e.rect0.h/2,n=180*Math.atan2(e.sy-o,e.sx-s)/Math.PI;let r=e.p0.angle+(180*Math.atan2(i[1]-o,i[0]-s)/Math.PI-n);t.shiftKey||(r=5*Math.round(r/5)),r=Ls(r);const a=Math.abs(r-e.p0.angle)>1e-9;if(!a&&!e.moved)return;return e.moved||=a,void this._bdApply(e.p0.dx,e.p0.dy,e.p0.sx,e.p0.sy,r)}const o=Vs(e.rect0,e.sgx,e.sgy,i[0],i[1],!t.shiftKey,this._gridPitch,Math.min(s.w,s.h)*en),n=o.w/Math.max(1e-9,s.w),r=o.h/Math.max(1e-9,s.h),a=Math.abs(n-e.p0.sx)>1e-9||Math.abs(r-e.p0.sy)>1e-9||Math.abs(o.x-e.rect0.x)>1e-9||Math.abs(o.y-e.rect0.y)>1e-9;if(!a&&!e.moved)return;e.moved||=a;const l=qs(o,e.p0.angle,t=>this._snap(t));this._bdApply((l[0]-s.x)/ol,(l[1]-s.y)/ol,n,r,e.p0.angle)}get _bdMoved(){if("decor"!==this._mode||!this._bdRect)return!1;const t=this._bdParams;return 0!==t.dx||0!==t.dy||1!==t.sx||1!==t.sy||0!==t.angle}_bdReset(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot();delete t.plan_x,delete t.plan_y,delete t.plan_scale,delete t.plan_scale_x,delete t.plan_scale_y,delete t.plan_angle,this._bdDrag=null,this._recordGeometry(this._t("history.backdrop_transform"),e),this._saveConfig(),this._showToast(this._t("decor.backdrop_reset_done")),this.requestUpdate()}_bdUp(){const t=this._bdDrag;this._bdDrag=null,t?.moved&&(this._recordGeometry(this._t("history.backdrop_transform"),t.before),this._saveConfig()),this.requestUpdate()}get _bdLive(){if(!this._bdDrag)return null;const t=this._bdRect;return t?{x:t.x+t.w/2,y:t.y+t.h/2,text:`${this._fmtLen([0,0],[t.w,0])} × ${this._fmtLen([0,0],[0,t.h])}`}:null}_renderBackdropFrame(t){const e=this._bdRect;if(!this._bdActive||!e)return G;const i=.02*Math.max(t.w,t.h),s=i/4,o=e.x+e.w/2,n=e.y+e.h/2,r=Ls(e.angle),a=2.2*i;return B` {e.stopPropagation(),e.preventDefault(),this._bdStart(e,[t,o])}}>`})} - `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return G;const s=.018*Math.max(t.w,t.h),o=s/4;if("line"===e.kind){const t=[e.x1*sl,e.y1*this._decorH],i=[e.x2*sl,e.y2*this._decorH];return B` + `}_renderTextFrame(t){const e=this._dtSel,i=this._dtBox;if(!e||!i||i.id!==e.id)return G;const s=.018*Math.max(t.w,t.h),o=s/4;if("line"===e.kind){const t=[e.x1*ol,e.y1*this._decorH],i=[e.x2*ol,e.y2*this._decorH];return B` ${[t,i].map((t,e)=>B`this._dtStart(t,"scale",void 0,e)}> @@ -3168,30 +3187,42 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow cx="${t<0?i.x:i.x+i.w}" cy="${e<0?i.y:i.y+i.h}" r="${s.toFixed(1)}" @pointerdown=${i=>this._dtStart(i,"scale",[t,e])}>`)} - `}_renderDecorLayer(){const t=sl,e=this._decorH,i="decor"===this._mode,s=this._decorList.map(s=>{const o="dshape"+(i&&this._decorSel===s.id?" dsel":""),n=this._decorResolvedStyle(s),r=this._decorWidthUnits(s),a=t=>this._decorShapeDown(t,s),l=t=>this._decorShapeDbl(t,s);if("line"===s.kind)return B``;if("rect"===s.kind){const i=(s.x+s.w/2)*t,h=(s.y+s.h/2)*e,c=Ls(s.angle);return B``}if("ellipse"===s.kind){const i=(s.x+s.w/2)*t,h=(s.y+s.h/2)*e,c=Ls(s.angle);return B``}if("furniture"===s.kind){const i=s.w*t,h=s.h*e,c=Un(s.symbol,i,h);if(!c)return G;const d=Number(s.angle)||0,p=s.x*t+i/2,u=s.y*e+h/2,_=`${d?`rotate(${d} ${p} ${u}) `:""}translate(${s.x*t} ${s.y*e})`;return B``}_renderDecorLayer(){const t=ol,e=this._decorH,i="decor"===this._mode,s=i&&"erase"===this._decorTool,o=this._decorList.map(o=>{const n="dshape"+(i&&this._decorSel===o.id?" dsel":""),r=this._decorResolvedStyle(o),a=this._decorWidthUnits(o),l=t=>this._decorShapeDown(t,o),h=t=>this._decorShapeDbl(t,o);if("line"===o.kind)return B` + ${s?B``:G}`;if("rect"===o.kind){const i=(o.x+o.w/2)*t,c=(o.y+o.h/2)*e,d=Ls(o.angle);return B` + ${s?B``:G}`}if("ellipse"===o.kind){const i=(o.x+o.w/2)*t,c=(o.y+o.h/2)*e,d=Ls(o.angle);return B` + ${s?B``:G}`}if("furniture"===o.kind){const i=o.w*t,c=o.h*e,d=Un(o.symbol,i,c);if(!d)return G;const p=Number(o.angle)||0,u=o.x*t+i/2,_=o.y*e+c/2,m=`${p?`rotate(${p} ${u} ${_}) `:""}translate(${o.x*t} ${o.y*e})`;return B``}if("text"===s.kind){const i=this._decorTextUnits(s),r=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=_i(e);return o?(n=!0,s(o.entity||"")?vi(i,o):pi):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf(ui);return t>=0?o.slice(0,t)+pi+o.slice(t+2):o?`${o} ${pi}`:pi}const l=vi(i,e),h=o.indexOf(ui);return h>=0?o.slice(0,h)+l+o.slice(h+2):o?`${o} ${l}`:l}(s.text,s,this.hass,t=>this._planEntityAvailable(t))),h=s.x*t,c=s.y*e,d=Number(s.angle)||0,p=c-(r.length-1)*i*1.2/2;return B`${r.map((t,e)=>B`${t}`)}`}return G});let o=G;const n=this._decorDraft;if(n){const t=this._decorStyle,e=Hs(t.widthCm,this._cellCm,this._gridPitch);if("line"===n.kind)o=B``;else{const i=Math.min(n.a[0],n.b[0]),s=Math.min(n.a[1],n.b[1]),r=Math.abs(n.b[0]-n.a[0]),a=Math.abs(n.b[1]-n.a[1]);o="rect"===n.kind?B``:B``}}return B`${s}${o}`}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._decorSel?this._decorList.find(t=>t.id===this._decorSel):null,i="rect"===this._decorTool||"ellipse"===this._decorTool||"rect"===e?.kind||"ellipse"===e?.kind,s=this._geometryHistory.undoName,o=this._geometryHistory.redoName;return W`
+ @pointerdown=${l} @dblclick=${h}> + ${s?B``:G}`}if("text"===o.kind){const i=this._decorTextUnits(o),s=function(t){return String(t??"").replace(/\r\n?/g,"\n").split("\n")}(function(t,e,i,s=()=>!0){const o=t??"";let n=!1;const r=o.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const o=_i(e);return o?(n=!0,s(o.entity||"")?vi(i,o):pi):t});if(n)return r;const a=(e?.entity||"").trim();if(!a)return o;if(!s(a)){const t=o.indexOf(ui);return t>=0?o.slice(0,t)+pi+o.slice(t+2):o?`${o} ${pi}`:pi}const l=vi(i,e),h=o.indexOf(ui);return h>=0?o.slice(0,h)+l+o.slice(h+2):o?`${o} ${l}`:l}(o.text,o,this.hass,t=>this._planEntityAvailable(t))),a=o.x*t,c=o.y*e,d=Number(o.angle)||0,p=c-(s.length-1)*i*1.2/2;return B`${s.map((t,e)=>B`${t}`)}`}return G});let n=G;const r=this._decorDraft;if(r){const t=this._decorStyle,e=Hs(t.widthCm,this._cellCm,this._gridPitch);if("line"===r.kind)n=B``;else{const i=Math.min(r.a[0],r.b[0]),s=Math.min(r.a[1],r.b[1]),o=Math.abs(r.b[0]-r.a[0]),a=Math.abs(r.b[1]-r.a[1]);n="rect"===r.kind?B``:B``}}return B`${o}${n}`}_renderDecorBar(){const t=[["select","mdi:cursor-default-outline","decor.select"],...this._bdRect?[["backdrop","mdi:image-move","decor.backdrop"]]:[],["line","mdi:vector-line","decor.line"],["rect","mdi:rectangle-outline","decor.rect"],["ellipse","mdi:ellipse-outline","decor.ellipse"],["text","mdi:format-text","decor.text"],["furniture","mdi:sofa-outline","decor.furniture"],["erase","mdi:eraser","decor.erase"]],e=this._decorSel?this._decorList.find(t=>t.id===this._decorSel):null,i="rect"===this._decorTool||"ellipse"===this._decorTool||"rect"===e?.kind||"ellipse"===e?.kind,s=this._geometryHistory.undoName,o=this._geometryHistory.redoName;return W`
${t.map(([t,e,i])=>W`
-
`:W``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=Re(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,o){for(const n of s)if(!(Zi([t,e],n)>o)&&uo([n[0],n[1]],[n[2],n[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:o.x,y:o.y,angle:o.angle},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{ll(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=Re(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,o=s?.openings?.find(t=>t.id===e.id);if(!o)return;const n=this._opRuler(i,o.length*sl);this._opMeasure=n.measure;const r=n.x/sl,a=n.y/this._spaceH;o.x===r&&o.y===a&&o.angle===i.angle||(this._opDrag.dirty=!0),o.x=r,o.y=a,o.angle=i.angle,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let o=t.x,n=t.y,r=Ae([o,n],t.angle,e,i,s);if(r&&r.centered&&(o!==r.wallCenter[0]||n!==r.wallCenter[1]))[o,n]=r.wallCenter,r=Ae([o,n],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[h,c]=r.wallB,d=h-a,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-a)*d+(n-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=a+_/u*d,n=l+_/u*p,r=Ae([o,n],t.angle,e,i,s)||r}}if(!r)return{x:o,y:n,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ce(t/this._gridPitch*this._cellCm,a)});return{x:o,y:n,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,o={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/sl,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/sl,contact:t.contact||null,lock:"door"===t.type&&t.lock||null,invert:t.invert||void 0,flip_h:t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const n=e.openings.findIndex(t=>t.id===o.id);n>=0?e.openings[n]=o:e.openings.push(o),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],o=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||o)&&t.push([e,s?.attributes?.friendly_name||e,o?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 o=e.find(t=>t.id===this._mergeSel),n=o?Pe(o):null,r=Pe(i),a=n&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=ke(Je(t),Je(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}(n,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,o="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===o);r?(r.poly=t.poly.map(t=>[t[0]/sl,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==n),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?Pe(i):null;if(!i||!s)return void(this._splitSel=null);const o=.02*this._gridPitch,n=6*this._gridPitch,r=Fe(t,s),a=r&&function(t,e,i){let s=null,o=1/0;for(let n=0;n=o)continue;o=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*c,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?a:null,h=!!l&&Le(l,s,o),c=this._splitSel.pts;if(!c.length)return h?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const e=this._snap(t);return qe(e,s,o)?void(this._splitSel={...this._splitSel,pts:[...c,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],o=e[e.length-1];if(Ee(s,o,i))return null;const n=Ze(t,s,i),r=Ze(t,o,i);if(n<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!qe(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!==n);e++)a=(a+1)%t.length;return r.push(o),Xe(r,i)};let h,c;if(n===r){const r=Xe([...e],i);if(r.length<3||Ye(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}h=Xe(a,i),c=r}else h=Xe([...l(s,n,o,r),...[...a].reverse()],i),c=Xe([...l(o,r,s,n),...a],i);return h.length<3||c.length<3||Ye(h)<=i||Ye(c)<=i||Math.abs(Ye(h)+Ye(c)-Ye(t))>Math.max(i,1e-6*Ye(t))?null:[h,c]}(s,[...c,l],o);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=Ye(p)>=Ye(u)?p:u,m=_===p?u:p;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;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"openwall"===this._tool||"closewall"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const e=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!e&&!i)return;const s=this._svgPoint(t);this._cursorPt=e?this._snapDrawPoint(s,t.shiftKey):this._snap(s)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),o=Re(t,this._spaceModel().rooms,e);if(!o)return null;const n=this._opRuler(o,s);return{x:n.x,y:n.y,angle:n.angle,rlen:s,measure:n.measure}}get _opMeasureView(){return this._opMeasure||this._openingPreview?.measure||null}_saveRoom(){this._areaSel&&this._commitRoom()}_saveRoomNoArea(){this._nameSel.trim()&&(this._areaSel="",this._commitRoom())}_commitRoom(){const t=this._curSpaceCfg;if(!t)return;const e=this._geometrySnapshot(),i=this._spaceH,s=!!this._pendingSplit;let o;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]/sl,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,o=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;o=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:o.map(t=>[t[0]/sl,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!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(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,o,n=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Mo(e,i,n,o,r,t);if(!a)return t?t.slice():[];const l=Co(t,a,o,r);let h=t?t.slice():[];for(let t=0;t0||(h=mo(h,e,i,s,o,r))}return h}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,sl);for(const t of Po(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=o.findIndex((t,e)=>{const i=o[(e+1)%o.length];return Zi(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(n=mo(n,t.a,t.b,e[a]||i,this._wallKeyPitch,sl))}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 a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=sl,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/sl,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const h=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:h,added:l}):this._t("toast.room_saved_no_area",{n:h}))}_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._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_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){t&&this._ackNewDevice(t.id),this._norm?this._markerDialog=t?{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.hass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:"ripple"===t.marker?.display?"icon_ripple":t.marker?.display||"badge",rippleColor: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:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:Br(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",isLight:!0===t.marker?.is_light||jr(t.marker?.binding,t.marker?.controls,t.entities),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}:{name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",isLight:!1,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)}:this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of di)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&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.hass,e=sa(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 o=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&o.add(t.name.trim()+"|"+(t.area||""));const n=[];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)&&o.has(r+"|"+(e.area_id||""))||n.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[o,a]of Object.entries(t.entities)){const l="entity:"+o;if(s.has(l))continue;if(oa(t,o,e)&&!i.has(l))continue;const h=r.has(a.platform),c="group"===a.platform;if(!h&&!c)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[o];n.push({value:l,label:a.name||d?.attributes?.friendly_name||o,sub:o.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const o=new Set(n.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||o.has(l)||a.hidden&&!i.has(l))continue;if(oa(t,r,e)&&!i.has(l))continue;const h=t.states[r],c=a.name||h?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";n.push({value:l,label:c,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?n.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):n;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=>Go(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),o=(t.wall_columns||[]).map(t=>jo(t,this._cellCm,this._gridPitch)),n=[...i,...s,...o];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:o,all:n},n}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,o=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=Ba(this._cleanFloorCache,o);if(t.hit)return t.value}const n=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...n),Math.min(...r),Math.max(...n),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]}),h=l.length?Vo(e,l):null,c={floor:e,geom:h,path:h?Wo(h):"",area:Ko(h||[[[...e,e[0]]]])};return this._rszPreview||ja(this._cleanFloorCache,o,c,600),c}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";if("device"===e){const t=this.hass.devices?.[i];if(!t)return"mdi:help-circle";const e=Object.entries(this.hass.entities||{}).filter(([,t])=>t?.device_id===i).map(([t])=>t);return e.some(t=>t.startsWith("lock."))?"mdi:lock":ia(this.hass,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=this.hass.entities?.[i],e=this.hass.states?.[i],s=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":ia(this.hass,s,"",[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)}}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",o=[];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}`}:{}}),n=await i.json().catch(()=>({}));if(!i.ok||n.error){const t={too_large:this._t("err.too_large",{mb:n.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[n.error]||n.error||"HTTP "+i.status)}o.push({name:n.name||t.name,url:n.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}o.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...o]},this._showToast(this._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=function(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}(t.room);let o=s?.space||null,n=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||o||(o=this._space),i=function(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=l.length>0,c=Br(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,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,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:c.length?c:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Wa(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=n,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==o||u.area!==n||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const o="/files/"+e+"/",n="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(o))return t;const e=t.url.split(o)[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(o+i)[0]+n+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(o).join(n)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=o||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!h&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(h||!this._layout[i]||m){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):n?t.rooms.find(t=>t.area===n):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(o||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const k=new Set(l);a&&a!==i&&k.add(a),k.delete(i);for(const t of k)delete this._layout[t],await this.hass.callWS({type:"houseplan/layout/delete",device_id:t}).then(t=>this._noteLayoutRev(t)).catch(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).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 o=s.markers,n="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!n)return;const r=function(t,e,i,s){const o=new Set([e]),n=t.filter(t=>{const n=t.id===e||!s&&t.binding===i;return n&&o.add(t.id),!n});return{markers:s?n:[...n,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:o}}(s.markers,e.id,n,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){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._infoCard=null),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=o),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/sl,y:i/sl}}_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=xi(i);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:s.fill,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Rs({},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:ki,roomOpacity:$i,fillMode:"glow",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 o=new Uint8Array(await i.arrayBuffer());let n="";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``}_openingClick(t){const e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return void this._editOpening(i);const s=Re(t,this._spaceModel().rooms,e);if(!s)return void this._showToast(this._t("toast.opening_no_wall"));if(function(t,e,i,s,o){for(const n of s)if(!(Zi([t,e],n)>o)&&uo([n[0],n[1]],[n[2],n[3]],i))return!0;return!1}(s.x,s.y,s.angle,this._openCuts(),e))return void this._showToast(this._t("toast.opening_on_virtual"));const o=this._opRuler(s,this._cmToUnits(90));this._openingDialog={type:"door",lengthCm:90,contact:"",lock:"",invert:!1,flipH:!1,flipV:!1,x:o.x,y:o.y,angle:o.angle},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{hl(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=Re(this._svgPoint(t),this._spaceModel().rooms,4*this._gridPitch);if(!i)return;this._opDrag.moved=!0;const s=this._curSpaceCfg,o=s?.openings?.find(t=>t.id===e.id);if(!o)return;const n=this._opRuler(i,o.length*ol);this._opMeasure=n.measure;const r=n.x/ol,a=n.y/this._spaceH;o.x===r&&o.y===a&&o.angle===i.angle||(this._opDrag.dirty=!0),o.x=r,o.y=a,o.angle=i.angle,this.requestUpdate()}_opRuler(t,e){const i=this._spaceModel().rooms,s=this._gridPitch/2;let o=t.x,n=t.y,r=Ae([o,n],t.angle,e,i,s);if(r&&r.centered&&(o!==r.wallCenter[0]||n!==r.wallCenter[1]))[o,n]=r.wallCenter,r=Ae([o,n],t.angle,e,i,s);else if(r){const[a,l]=r.wallA,[h,c]=r.wallB,d=h-a,p=c-l,u=Math.hypot(d,p);if(u>0){const h=this._gridPitch,c=Math.min(e/2,u/2);let _=Math.round(((o-a)*d+(n-l)*p)/u/h)*h;_=Math.max(c,Math.min(u-c,_)),o=a+_/u*d,n=l+_/u*p,r=Ae([o,n],t.angle,e,i,s)||r}}if(!r)return{x:o,y:n,angle:t.angle,measure:null};const a="mi"===this.hass?.config?.unit_system?.length,l=(t,e)=>({x:e[0],y:e[1],text:Ce(t/this._gridPitch*this._cellCm,a)});return{x:o,y:n,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,o={id:t.id||"o"+Date.now().toString(36),type:t.type,x:t.x/ol,y:t.y/s,angle:t.angle,length:this._cmToUnits(Math.max(20,t.lengthCm))/ol,contact:t.contact||null,lock:"door"===t.type&&t.lock||null,invert:t.invert||void 0,flip_h:t.flipH||void 0,flip_v:t.flipV||void 0};e.openings=e.openings||[];const n=e.openings.findIndex(t=>t.id===o.id);n>=0?e.openings[n]=o:e.openings.push(o),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],o=["door","window","opening","garage_door","garage"].includes(s?.attributes?.device_class||"");("cover"!==i||o)&&t.push([e,s?.attributes?.friendly_name||e,o?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 o=e.find(t=>t.id===this._mergeSel),n=o?Pe(o):null,r=Pe(i),a=n&&r?function(t,e){if(!t||!e||t.length<3||e.length<3)return null;const i=$e(Je(t),Je(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}(n,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,o="a"===t.pick?t.aId:t.bId,n="a"===t.pick?t.bId:t.aId,r=e.rooms.find(t=>t.id===o);r?(r.poly=t.poly.map(t=>[t[0]/ol,t[1]/s]),delete r.x,delete r.y,delete r.w,delete r.h,e.rooms=e.rooms.filter(t=>t.id!==n),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?Pe(i):null;if(!i||!s)return void(this._splitSel=null);const o=.02*this._gridPitch,n=6*this._gridPitch,r=Fe(t,s),a=r&&function(t,e,i){let s=null,o=1/0;for(let n=0;n=o)continue;o=_;const m=Math.sqrt(p),g=(i>0?Math.max(0,Math.min(m,Math.round(u*m/i)*i)):u*m)/m;s=[r+g*c,a+g*d]}return s}(r,s,this._gridPitch)||r,l=r&&a&&Math.hypot(r[0]-t[0],r[1]-t[1])<=n?a:null,h=!!l&&Le(l,s,o),c=this._splitSel.pts;if(!c.length)return h?void(this._splitSel={...this._splitSel,pts:[l]}):void this._showToast(this._t("toast.split_pick_wall"));if(!h){const e=this._snap(t);return qe(e,s,o)?void(this._splitSel={...this._splitSel,pts:[...c,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],o=e[e.length-1];if(Ee(s,o,i))return null;const n=Ze(t,s,i),r=Ze(t,o,i);if(n<0||r<0)return null;const a=e.slice(1,-1);for(const e of a)if(!qe(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!==n);e++)a=(a+1)%t.length;return r.push(o),Xe(r,i)};let h,c;if(n===r){const r=Xe([...e],i);if(r.length<3||Ye(r)<=i)return null;const a=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)a.push(t)}h=Xe(a,i),c=r}else h=Xe([...l(s,n,o,r),...[...a].reverse()],i),c=Xe([...l(o,r,s,n),...a],i);return h.length<3||c.length<3||Ye(h)<=i||Ye(c)<=i||Math.abs(Ye(h)+Ye(c)-Ye(t))>Math.max(i,1e-6*Ye(t))?null:[h,c]}(s,[...c,l],o);if(!d)return void this._showToast(this._t("toast.split_bad_cut"));this._resetRoomDialogFields();const[p,u]=d,_=Ye(p)>=Ye(u)?p:u,m=_===p?u:p;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;if("column"===this._tool)return void(this._cursorPt=this._snap(this._svgPoint(t)));if("opening"===this._tool||"openwall"===this._tool||"closewall"===this._tool||"wallthick"===this._tool)return void(this._cursorPt=this._svgPoint(t));const e=("draw"===this._tool||"partition"===this._tool)&&this._path.length&&!this._contourClosed,i="split"===this._tool&&!!this._splitSel?.pts?.length;if(!e&&!i)return;const s=this._svgPoint(t);this._cursorPt=e?this._snapDrawPoint(s,t.shiftKey):this._snap(s)}get _openingPreview(){if("opening"!==this._tool||!this._cursorPt)return null;const t=this._cursorPt,e=1.5*this._gridPitch,i=this._openingsR.find(i=>Math.hypot(t[0]-i.rx,t[1]-i.ry)<=Math.max(i.rlen/2,e));if(i)return null;const s=this._cmToUnits(90),o=Re(t,this._spaceModel().rooms,e);if(!o)return null;const n=this._opRuler(o,s);return{x:n.x,y:n.y,angle:n.angle,rlen:s,measure:n.measure}}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;let o;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]/ol,t[1]/i]),delete e.x,delete e.y,delete e.w,delete e.h,o=this._pendingSplit.newPoly}else{if(!this._contourClosed)return;o=this._path.slice(0,-1)}const n=this._areaSel?this.hass.areas[this._areaSel]?.name:"",r={id:"r"+Date.now().toString(36),name:this._nameSel||n||this._t("room.default_name"),area:this._areaSel||null,poly:o.map(t=>[t[0]/ol,t[1]/i]),...this._roomSettingsFromDialog()?{settings:this._roomSettingsFromDialog()}:{}};if(t.rooms.push(r),!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(),!s){const e=[...this._draftSegmentCms,this._closingWallCm||this._drawWallCm||Ys],i=e[0]||this._drawWallCm;if(null!=i){this._cfgEpoch++;const s=this._openCuts();let n=function(t,e,i,s,o,n=[],r=1){if(null==s||s<1)return t?t.slice():[];const a=Mo(e,i,n,o,r,t);if(!a)return t?t.slice():[];const l=Co(t,a,o,r);let h=t?t.slice():[];for(let t=0;t0||(h=mo(h,e,i,s,o,r))}return h}(t.walls,this._spaceModel().rooms,r.id,i,this._wallKeyPitch,s,ol);for(const t of Po(this._spaceModel().rooms,n,s,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)){if(t.roomId!==r.id||"outer"!==t.kind)continue;const s=[(t.a[0]+t.b[0])/2,(t.a[1]+t.b[1])/2],a=o.findIndex((t,e)=>{const i=o[(e+1)%o.length];return Zi(s,[t[0],t[1],i[0],i[1]])<=.02*this._gridPitch});a>=0&&(n=mo(n,t.a,t.b,e[a]||i,this._wallKeyPitch,ol))}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 a=this._areaSel;this._areaSel="",this._nameSel="",this._roomDialog=!1,this._regSignature="",this._maybeRebuildDevices();let l=0;if(a){const t=ol,e={...this._layout};for(const i of this._devices){if(i.area!==a||i.space!==this._space)continue;if(l++,this._layout[i.id])continue;const s=this._defPos[i.id];s&&(e[i.id]={s:this._space,x:s.x/ol,y:s.y/t},this._dirtyPos.add(i.id))}this._layout=e,this._persistLayout()}const h=this._model.find(t=>t.id===this._space)?.rooms.length||0;this._showToast(a?this._t("toast.room_saved",{n:h,added:l}):this._t("toast.room_saved_no_area",{n:h}))}_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._physicalSel=null,this._physicalDrag=null,this._physicalRotate=null}_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){t&&this._ackNewDevice(t.id),this._norm?this._markerDialog=t?{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.hass.entities[t.bindingRef||""]?.device_id,bindingFilter:"",icon:t.marker?.icon||"",autoIcon:t.icon||"",display:"ripple"===t.marker?.display?"icon_ripple":t.marker?.display||"badge",rippleColor: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:t.marker?.tap_action||"",tapTarget:t.marker?.tap_target||"",tapConfirm:!0===t.marker?.tap_confirm,runFilter:"",defaultTap:"light"===t.primary?.split(".")[0]?"toggle":"info",controls:jr(t.marker?.binding,t.marker?.controls,t.entities),controlsFilter:"",isLight:!0===t.marker?.is_light||Gr(t.marker?.binding,t.marker?.controls,t.entities),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}:{name:"",binding:"virtual",bindingMode:"virtual",bindingOpen:!1,showEntities:!1,bindingFilter:"",icon:"",autoIcon:"",display:"badge",rippleColor:"",rippleSize:3,size:1,angle:0,tapAction:"",tapTarget:"",tapConfirm:!1,runFilter:"",defaultTap:"info",controls:[],controlsFilter:"",isLight:!1,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)}:this._showToast(this._t("toast.marker_needs_server"))}_runCandidates(){const t=[];for(const e of di)for(const[i,s]of Object.entries(this.hass.states))i.startsWith(e+".")&&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.hass,e=oa(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 o=new Set;for(const t of this._devices)"device"===t.bindingKind&&t.name&&o.add(t.name.trim()+"|"+(t.area||""));const n=[];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)&&o.has(r+"|"+(e.area_id||""))||n.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[o,a]of Object.entries(t.entities)){const l="entity:"+o;if(s.has(l))continue;if(na(t,o,e)&&!i.has(l))continue;const h=r.has(a.platform),c="group"===a.platform;if(!h&&!c)continue;if(a.hidden&&!i.has(l))continue;const d=t.states[o];n.push({value:l,label:a.name||d?.attributes?.friendly_name||o,sub:o.split(".")[0]+" · "+("group"===a.platform?this._t("marker.sub_group"):this._t("marker.sub_helper"))})}if(this._markerDialog?.showEntities){const o=new Set(n.map(t=>t.value));for(const[r,a]of Object.entries(t.entities)){const l="entity:"+r;if(s.has(l)||o.has(l)||a.hidden&&!i.has(l))continue;if(na(t,r,e)&&!i.has(l))continue;const h=t.states[r],c=a.name||h?.attributes?.friendly_name||r,d=a.device_id?t.devices[a.device_id]:null,p=d&&(d.name_by_user||d.name)||"";n.push({value:l,label:c,sub:r.split(".")[0]+" · "+this._t("marker.sub_entity")+(p?" · "+p:"")})}}const a=(this._markerDialog?.bindingFilter||"").toLowerCase().trim(),l=a?n.filter(t=>(t.label+" "+t.sub+" "+t.value).toLowerCase().includes(a)):n;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=>Go(t,this._cellCm,this._gridPitch)),s=(t.partitions||[]).flatMap(t=>{const e=Bo(t.a,t.b,t.cm,this._cellCm,this._gridPitch);return e?[e]:[]}),o=(t.wall_columns||[]).map(t=>jo(t,this._cellCm,this._gridPitch)),n=[...i,...s,...o];return this._physicalBodiesCache={key:e,drafts:i,partitions:s,columns:o,all:n},n}_cleanFloor(t,e,i=this._spaceModel()){const s=t.id||`#${i.rooms.indexOf(t)}`,o=`${i.id}|${this._cfgEpoch}|${s}`;if(!this._rszPreview){const t=ja(this._cleanFloorCache,o);if(t.hit)return t.value}const n=e.map(t=>t[0]),r=e.map(t=>t[1]),a=[Math.min(...n),Math.min(...r),Math.max(...n),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]}),h=l.length?Vo(e,l):null,c={floor:e,geom:h,path:h?Wo(h):"",area:Ko(h||[[[...e,e[0]]]])};return this._rszPreview||Ga(this._cleanFloorCache,o,c,600),c}_autoIconForBinding(t){if("virtual"===t)return"mdi:map-marker";const[e,i]=t.split(":");if(!i)return"";if("device"===e){const t=this.hass.devices?.[i];if(!t)return"mdi:help-circle";const e=Object.entries(this.hass.entities||{}).filter(([,t])=>t?.device_id===i).map(([t])=>t);return e.some(t=>t.startsWith("lock."))?"mdi:lock":sa(this.hass,t.name_by_user||t.name||"",t.model,e,this._iconRules)}if("entity"===e){const t=this.hass.entities?.[i],e=this.hass.states?.[i],s=t?.name||e?.attributes?.friendly_name||i;return i.startsWith("lock.")?"mdi:lock":sa(this.hass,s,"",[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)}}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",o=[];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}`}:{}}),n=await i.json().catch(()=>({}));if(!i.ok||n.error){const t={too_large:this._t("err.too_large",{mb:n.max_mb||50}),bad_ext:this._t("err.bad_ext"),unauthorized:this._t("err.unauthorized")};throw new Error(t[n.error]||n.error||"HTTP "+i.status)}o.push({name:n.name||t.name,url:n.url})}catch(e){this._showToast(this._t("toast.file_failed",{name:t.name,err:this._errText(e)}))}o.length&&this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:[...this._markerDialog.pdfs,...o]},this._showToast(this._t("toast.files_attached",{n:o.length})))}_removeMarkerPdf(t){this._markerDialog&&(this._markerDialog={...this._markerDialog,pdfs:this._markerDialog.pdfs.filter(e=>e.url!==t)})}async _saveMarker(){const t=this._markerDialog;if(t&&!t.busy&&("ha"!==t.bindingMode||t.binding&&"virtual"!==t.binding))if("virtual"!==t.binding||t.name.trim())if("run"!==t.tapAction||t.tapTarget){this._markerDialog={...t,busy:!0};try{const e=this._serverCfg;let i;e.markers=e.markers||[];const s=function(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}(t.room);let o=s?.space||null,n=s?.area||null;const r=s?.roomId||null;"virtual"!==t.binding||o||(o=this._space),i=function(t,e,i){const[s,o]=t.split(":");return"device"===s?o:"entity"===s?"lg_"+o:e&&e.startsWith("v_")?e:i()}(t.binding,t.devId,()=>"v_"+Date.now().toString(36));const a=t.devId,l="virtual"===t.binding?[]:e.markers.filter(e=>e.removed&&e.binding===t.binding).map(t=>t.id),h=l.length>0,c=jr(t.binding,t.controls,this._bindingEntities(t.binding)),d=e.markers.find(t=>t.id===i||t.id===a)?.vacuum||null,p={id:i,vacuum:d,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,tap_action:t.tapAction||null,tap_target:"run"===t.tapAction&&t.tapTarget||null,tap_confirm:!!t.tapConfirm||null,controls:c.length?c:null,is_light:!!t.isLight||null,use_climate_temp:!!t.useClimateTemp||null,glow_radius_cm:(()=>{const e=Ba(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=n,p.room_id=r);const u=a?this._devices.find(t=>t.id===a):null,_=u?.marker?.room_id??null,m=!!t.room&&null!=u&&(u.space!==o||u.area!==n||_!==r);let g=!1;const f=t.uploadId||a;if(f&&f!==i&&p.pdfs?.length)try{const t=await this.hass.callWS({type:"houseplan/files/migrate",from_id:f,to_id:i}),e=t?.mapping||{};p.pdfs=function(t,e,i,s){if(!e||!i||e===i)return t;const o="/files/"+e+"/",n="/files/"+i+"/";return t.map(t=>{if(!t.url.includes(o))return t;const e=t.url.split(o)[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(o+i)[0]+n+encodeURIComponent(e)+r}:t}return{...t,url:t.url.split(o).join(n)}})}(p.pdfs,f,i,e),g=Object.keys(e).length>0}catch(t){this._showToast(this._t("toast.files_migrate_failed",{err:this._errText(t)}))}e.markers=e.markers.filter(t=>t.id!==i&&t.id!==a&&("virtual"===p.binding||t.binding!==p.binding)),e.markers.push(p);let v=null;const y=o||u?.space||this._space,b=a?this._layout[a]:null,w=b?{s:b.s||u?.space||this._space,x:b.x,y:b.y}:a&&u&&this._defPos[a]?this._normPos(u.space,this._defPos[a].x,this._defPos[a].y):null;if(!h&&w&&w.s===y)i===a&&this._layout[i]&&!m||(v={s:w.s,x:w.x,y:w.y},this._layout={...this._layout,[i]:v});else if(h||!this._layout[i]||m){const t=this._spaceModel(o||void 0);let e=t.vb[0]+t.vb[2]/2,s=t.vb[1]+t.vb[3]/2;const a=r?t.rooms.find(t=>t.id===r):n?t.rooms.find(t=>t.area===n):void 0;a&&([e,s]=this._roomCenter(a)),v=this._normPos(o||this._space,e,s),this._layout={...this._layout,[i]:v}}await this._saveConfigNow(),v&&this._noteLayoutRev(await this.hass.callWS({type:"houseplan/layout/update",device_id:i,pos:v}));const $=new Set(l);a&&a!==i&&$.add(a),$.delete(i);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(()=>{});g&&f&&await this.hass.callWS({type:"houseplan/files/cleanup",marker_id:f}).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 o=s.markers,n="virtual"===e.bindingKind?"virtual":e.bindingKind&&e.bindingRef?`${e.bindingKind}:${e.bindingRef}`:"";if(!n)return;const r=function(t,e,i,s){const o=new Set([e]),n=t.filter(t=>{const n=t.id===e||!s&&t.binding===i;return n&&o.add(t.id),!n});return{markers:s?n:[...n,{id:e,binding:i,removed:!0,hidden:!0}],cleanupIds:o}}(s.markers,e.id,n,"virtual"===e.bindingKind);s.markers=r.markers;const a=r.cleanupIds;this._markerDialog={...t,busy:!0};try{await this._saveConfigNow();for(const t of a){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._infoCard=null),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=o),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/ol,y:i/ol}}_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=xi(i);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:s.fill,bgColor:s.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:Rs({},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:$i,roomOpacity:ki,fillMode:"glow",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 o=new Uint8Array(await i.arrayBuffer());let n="";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`
@@ -3417,7 +3448,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`)} -
`}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!o&&this._aspectJob&&(o=await this._aspectJob||null);const n=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},n.spaces.push(r);else{if(r=n.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=o),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(qa,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:ki,roomOpacity:$i,fillMode:"glow",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})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return W``}async _saveSpaceDialog(){const t=this._spaceDialog;if(!t||t.busy||!t.title.trim())return;if("file"===t.source&&!t.planFile&&!t.planUrl)return void this._showToast(this._t("toast.plan_required"));const e="create"===t.mode&&0===(this._serverCfg?.spaces.length||0);this._spaceDialog={...t,busy:!0};try{const i="create"===t.mode?"s"+Date.now().toString(36):t.spaceId;let s=null;if("file"===t.source&&t.planFile){s={url:(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:t.planFile.ext,data:t.planFile.b64})).url,aspect:t.planFile.aspect}}let o=t.savedAspect||null;!s&&"file"===t.source&&t.planUrl&&!o&&this._aspectJob&&(o=await this._aspectJob||null);const n=this._serverCfg;let r;if("create"===t.mode)r={id:i,title:t.title.trim(),plan_url:null,view_box:[0,0,1,1],rooms:[]},n.spaces.push(r);else{if(r=n.spaces.find(t=>t.id===i),!r)throw new Error("space "+i+" is gone from the config");r.title=t.title.trim()}s?(r.plan_url=s.url,r.plan_aspect=s.aspect):"file"===t.source&&t.planUrl&&t.planUrl!==r.plan_url&&(r.plan_url=t.planUrl,r.plan_aspect=o),"draw"===t.source&&(r.plan_url=null,r.plan_aspect=null,delete r.plan_x,delete r.plan_y,delete r.plan_scale,delete r.plan_scale_x,delete r.plan_scale_y,delete r.plan_angle);const a="draw"===t.source;r.settings={...r.settings||{},show_borders:!(!a||"create"!==t.mode)||t.showBorders,show_names:!(!a||"create"!==t.mode)||t.showNames,hide_decor:t.hideDecor||void 0,hide_openings:t.hideOpenings||void 0,room_color:t.roomColor,room_opacity:t.roomOpacity,bg_color:t.bgColor||void 0,bg_mode:t.bgMode||void 0,north_deg:t.northDeg??void 0,sun_rays:t.sunRays??void 0,fill_mode:t.fillMode,temp_min:Number.isFinite(t.tempMin)?Math.min(t.tempMin,t.tempMax):20,temp_max:Number.isFinite(t.tempMax)?Math.max(t.tempMin,t.tempMax):25,show_lqi:t.showLqi,card_font_scale:1!==t.cardFontScale?t.cardFontScale:void 0,label_temp:t.labelTemp,label_hum:t.labelHum,label_lqi:t.labelLqi,label_light:t.labelLight},r.cell_cm=Number.isFinite(t.cellCm)&&t.cellCm>0?Math.max(.1,Math.min(Ha,t.cellCm)):5,await this._saveConfigNow(),this._spaceDialog=null,"create"===t.mode&&(this._space=r.id),this._regSignature="",this._maybeRebuildDevices(),this._importQueue.length?this._openNextImport():e||this._importTotal>0?(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._showToast(this._t(e&&!this._importTotal?"toast.space_added_onboard":"import.done"))):(this._showToast("create"===t.mode?this._t("toast.space_added"):this._t("toast.space_saved")),"create"===t.mode&&("plan"!==this._mode?this._setMode("plan"):(this._tool="draw",this._path=[],this._cursorPt=null,this._primeDrawWallField(),this._saveNav())))}catch(t){this._spaceDialog&&(this._spaceDialog={...this._spaceDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}async _deleteSpace(){const t=this._spaceDialog;if(!t||"edit"!==t.mode)return;const e=this._serverCfg.spaces.find(e=>e.id===t.spaceId);if(confirm(this._t("confirm.delete_space",{title:e.title}))){this._serverCfg.spaces=this._serverCfg.spaces.filter(e=>e.id!==t.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===t.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast(this._t("toast.space_deleted"))}catch(t){this._showToast(this._t("toast.delete_failed",{err:this._errText(t)}))}}}async _saveConfigNow(){this._cfgEpoch++;try{await this._writeConfig()}catch(t){throw"conflict"===t?.code&&await this._reloadConfigOnly(),t}}_startImport(){const t=this._importDialog;if(!t)return;const e=t.floors.filter(t=>t.checked).map(t=>t.name);this._importDialog=null,e.length?(this._importQueue=e,this._importTotal=e.length,this._openNextImport()):this._openSpaceDialog("create")}_openNextImport(){const t=this._importQueue.shift();void 0!==t&&(this._spaceDialog={mode:"create",title:t,planUrl:null,planFile:null,source:"file",showBorders:!1,showNames:!1,hideDecor:!1,hideOpenings:!1,roomColor:$i,roomOpacity:ki,fillMode:"glow",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})}_skipImport(){this._spaceDialog=null,this._importQueue.length?this._openNextImport():this._importTotal>0&&this._model.length&&(this._importTotal=0,this._space=this._serverCfg.spaces[0]?.id||this._space,this._mode="plan",this._showToast(this._t("import.done")))}_renderImportDialog(){const t=this._importDialog,e=t.floors.filter(t=>t.checked).length;return W`this._importDialog=null}>
${this._t("import.hint")}
@@ -3437,7 +3468,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._t("import.start",{n:e})}
-
`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays,weather_entity:(t.weatherEntity||"").trim()||void 0}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Rs(this._sunGlobal(),this._sunSpace())}_effBgMode(){return As(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Ns(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Is(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Is(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const o=Es(this._sunGlobal()),n=function(t){if(!t)return 1;const e=zs[String(t).toLowerCase()];return void 0===e?1:e}(o?this.hass?.states?.[o]?.state:null),r=function(t=1){return.3*ks(t)}(n);if(r<=0)return this._sunFadeReset(),e;if(a=s.elevation,Number(a)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var a;const l=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const e=t.rooms.map(t=>({id:t.id||"",poly:Pe(t)})).filter(t=>!!t.id&&!!t.poly),o=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),n=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},h={};if(n.length){for(const i of e){const e=Ao(t.rooms,i.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,sl);e&&(a[i.id]=e)}for(const e of o){const i=Fo(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},n,this._wallKeyPitch,this._cellCm,this._gridPitch,sl);i.cm>0&&(h[e.id]=Qs(i.cm,this._cellCm,this._gridPitch))}}let c=Ds(e,o,s.azimuth,s.elevation,i,n.length?a:void 0,n.length?h:void 0);const d=this._physicalBodiesR(t);d.length&&(c=c.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.slice(0,-1).concat(o.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(d,t.dir,t.len),i=t.polys.map(t=>Vo(t,e));return{...t,paths:i.map(Wo).filter(Boolean),polys:i.flatMap(Yo)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:l,rays:c,rims:c.map(t=>function(t,e=1e-4){const[i,s]=t.dir,o=-s,n=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((c[0]-a[0])*o+(c[1]-a[1])*n)>e)continue;const d=(h[0]-a[0])*i+(h[1]-a[1])*s,p=(c[0]-a[0])*i+(c[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const h=[];for(const t of l){const i=h[h.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):h.push([t[0],t[1]])}for(const[t,e]of h)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const h=this._sunRaysCache.rays,c=this._sunRaysCache.rims;if(!h.length)return e;const d=(p=$s(s.elevation).warmth,Pi("#ffe9c2","#ff9a45",ks(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*ks(t)}(n),m=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` +
`}_sunGlobal(){const t=this._settingsDialog;return t?{...this._settings,north_deg:t.northDeg??void 0,bg_mode:t.bgMode,sun_rays:t.sunRays,weather_entity:(t.weatherEntity||"").trim()||void 0}:this._settings}_sunSpace(){const t=this._spaceDialog,e=this._curSpaceCfg?.settings||{};return t&&"edit"===t.mode&&t.spaceId===this._space?{...e,north_deg:t.northDeg??void 0,bg_mode:t.bgMode??void 0,sun_rays:t.sunRays??void 0}:e}_effNorth(){return Rs(this._sunGlobal(),this._sunSpace())}_effBgMode(){return As(this._sunGlobal(),this._sunSpace())}_effSunRays(){return Ns(this._sunGlobal(),this._sunSpace())}_sunNow(){return null!==this._effNorth()?Is(this.hass):null}_renderSunRays(t){const e=B``;if(this._editing||!this._effSunRays())return this._sunFadeReset(),e;const i=this._effNorth(),s=null!==i?Is(this.hass):null;if(!s||s.elevation<=0)return this._sunFadeReset(),e;const o=Es(this._sunGlobal()),n=function(t){if(!t)return 1;const e=zs[String(t).toLowerCase()];return void 0===e?1:e}(o?this.hass?.states?.[o]?.state:null),r=function(t=1){return.3*$s(t)}(n);if(r<=0)return this._sunFadeReset(),e;if(a=s.elevation,Number(a)>=3)this._sunOutTimer&&(clearTimeout(this._sunOutTimer),this._sunOutTimer=0),this._sunOut=!1,this._sunShown=!0;else{if(!this._sunShown)return e;this._sunOut||(this._sunOut=!0,this._sunOutTimer=window.setTimeout(()=>{this._sunOutTimer=0,this._sunShown=!1,this._sunOut=!1,this.requestUpdate()},2e3))}var a;const l=`${t.id}|${s.azimuth}|${s.elevation}|${i}|${this._cfgEpoch}`;if(!this._sunRaysCache||this._sunRaysCache.key!==l){const e=t.rooms.map(t=>({id:t.id||"",poly:Pe(t)})).filter(t=>!!t.id&&!!t.poly),o=this._openingsR.filter(t=>"window"===t.type).map(t=>({id:t.id,x:t.rx,y:t.ry,angle:t.angle,length:t.rlen})),n=this._spaceWalls,r=this._openPairs().flatMap(t=>t.segs),a={},h={};if(n.length){for(const i of e){const e=Ao(t.rooms,i.id,n,r,this._wallKeyPitch,this._cellCm,this._gridPitch,ol);e&&(a[i.id]=e)}for(const e of o){const i=Fo(t.rooms,{x:e.x,y:e.y,angle:e.angle,length:e.length},n,this._wallKeyPitch,this._cellCm,this._gridPitch,ol);i.cm>0&&(h[e.id]=Qs(i.cm,this._cellCm,this._gridPitch))}}let c=Ds(e,o,s.azimuth,s.elevation,i,n.length?a:void 0,n.length?h:void 0);const d=this._physicalBodiesR(t);d.length&&(c=c.map(t=>{const e=function(t,e,i){return i>0?t.map(t=>function(t){const e=[...t].sort((t,e)=>t[0]-e[0]||t[1]-e[1]);if(e.length<=2)return e;const i=(t,e,i)=>(e[0]-t[0])*(i[1]-t[1])-(e[1]-t[1])*(i[0]-t[0]),s=[];for(const t of e){for(;s.length>=2&&i(s[s.length-2],s[s.length-1],t)<=0;)s.pop();s.push(t)}const o=[];for(let t=e.length-1;t>=0;t--){const s=e[t];for(;o.length>=2&&i(o[o.length-2],o[o.length-1],s)<=0;)o.pop();o.push(s)}return s.slice(0,-1).concat(o.slice(0,-1))}([...t,...t.map(t=>[t[0]+e[0]*i,t[1]+e[1]*i])])).filter(t=>t.length>=3):t}(d,t.dir,t.len),i=t.polys.map(t=>Vo(t,e));return{...t,paths:i.map(Wo).filter(Boolean),polys:i.flatMap(Yo)}}).filter(t=>t.paths?.length||t.polys.length)),this._sunRaysCache={key:l,rays:c,rims:c.map(t=>function(t,e=1e-4){const[i,s]=t.dir,o=-s,n=i,r=[];for(const a of[t.a,t.b]){const l=[];for(const r of t.polys)for(let t=0;te)continue;if(Math.abs((c[0]-a[0])*o+(c[1]-a[1])*n)>e)continue;const d=(h[0]-a[0])*i+(h[1]-a[1])*s,p=(c[0]-a[0])*i+(c[1]-a[1])*s;Math.abs(p-d)<=e||l.push(dt[0]-e[0]);const h=[];for(const t of l){const i=h[h.length-1];i&&t[0]<=i[1]+e?i[1]=Math.max(i[1],t[1]):h.push([t[0],t[1]])}for(const[t,e]of h)r.push([[a[0]+i*t,a[1]+s*t],[a[0]+i*e,a[1]+s*e]])}return r}(t))}}const h=this._sunRaysCache.rays,c=this._sunRaysCache.rims;if(!h.length)return e;const d=(p=ks(s.elevation).warmth,Pi("#ffe9c2","#ff9a45",$s(p)));var p;const u=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]],_=function(t=1){return.42*$s(t)}(n),m=[[0,1],[.26,.86],[.46,.6],[.64,.32],[.77,.1],[.85,0],[1,0]];return B` ${h.map((t,e)=>{const i=(t.a[0]+t.b[0])/2,s=(t.a[1]+t.b[1])/2,o=i+t.normal[0]*t.depth,n=s+t.normal[1]*t.depth;return B` ${u.map(([t,e])=>B`${this._t("gs.north_letter")} ${null===t?"—":t+"°"} - `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return $s(Ps(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Si(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Mi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const o=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(o)&&o>0&&300!==Math.round(o)?s.glow_radius_cm=Math.round(o):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays;const n=(t.weatherEntity||"").trim();n?s.weather_entity=n:delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?W``:W``}_rangeInput(t,e,i,s,o){const n=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?W``:W``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return W`
+ `}_stageBg(t){if("daynight"===this._effBgMode()){const t=this._sunNow();if(t)return ks(Ps(t.elevation)).bg}const e=this._settingsDialog,i=this._spaceDialog,s=e?e.bgColor||"":Si(this._settings,{bgColor:null});return(i&&"edit"===i.mode&&i.spaceId===this._space?i.bgColor||"":t.bgColor||"")||s}_stageBgHex(){const t=this._stageEl;if(t){const e=getComputedStyle(t).backgroundColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);if(e)return"#"+e.slice(1,4).map(t=>(+t).toString(16).padStart(2,"0")).join("")}return"#111111"}async _runAlignToGrid(){const t=this._alignDialog;if(t&&!t.busy&&this._serverCfg){this._alignDialog={...t,busy:!0};try{this._saveConfigDebounced.pending()&&this._saveConfigDebounced.flush(),await this._writeChain;const e=await this.hass.callWS({type:"houseplan/plan/optimize",config:t.config,layout:t.layout,expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});this._serverCfg=t.config,this._layout=t.layout,this._geometryHistory.clear(),this._cfgRev=e?.config_rev??this._cfgRev+1,this._layoutRev=e?.layout_rev??this._layoutRev+1,this._canOptimizeUndo=!!e?.can_undo,this._dirtyPos.clear(),this._sentPos.clear(),this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this._alignDialog=null,this.requestUpdate(),this._showToast(this._t("gs.align_done",{n:String(t.report.moved),m:String(t.report.migrated+t.report.canonicalized+t.report.wallsMerged+t.report.spansMerged)}))}catch(t){this._alignDialog&&(this._alignDialog={...this._alignDialog,busy:!1}),"conflict"===t?.code&&await Promise.all([this._reloadConfigOnly(!0),this._reloadLayoutOnly()]),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}async _undoPlanOptimization(){if(this._canOptimizeUndo&&!this._optimizeUndoBusy){this._optimizeUndoBusy=!0,this.requestUpdate();try{await this.hass.callWS({type:"houseplan/plan/optimize_undo",expected_config_rev:this._cfgRev,expected_layout_rev:this._layoutRev});const[t,e]=await Promise.all([this.hass.callWS({type:"houseplan/config/get"}),this.hass.callWS({type:"houseplan/layout/get"})]);this._serverCfg=t?.config||this._serverCfg,this._cfgRev=t?.rev??this._cfgRev,this._layout=e?.layout||this._layout,this._geometryHistory.clear(),this._layoutRev=e?.rev??this._layoutRev,this._canOptimizeUndo=!1,this._cfgEpoch++,this._modelCache=null,this._frame=null,this._cacheSnapshot(),this.requestUpdate(),this._showToast(this._t("gs.optimize_undone"))}catch(t){this._canOptimizeUndo=!1,this._showToast(this._t("toast.error",{err:this._errText(t)}))}finally{this._optimizeUndoBusy=!1,this.requestUpdate()}}}_setFillColor(t,e){const i=this._settingsDialog;this._settingsDialog={...i,colors:{...i.colors,[t]:{...i.colors[t],...e}}}}async _saveSettingsDialog(){const t=this._settingsDialog;if(t&&!t.busy){this._settingsDialog={...t,busy:!0};try{const e=this._serverCfg,i=JSON.stringify(t.colors)===JSON.stringify(Mi),s={...e.settings};i?delete s.fill_colors:s.fill_colors=t.colors;const o=this._imperial?30.48*t.glowRadius:100*t.glowRadius;Number.isFinite(o)&&o>0&&300!==Math.round(o)?s.glow_radius_cm=Math.round(o):delete s.glow_radius_cm,t.bgColor?s.bg_color=t.bgColor:delete s.bg_color,null!==t.northDeg&&Number.isInteger(t.northDeg)&&t.northDeg>=0&&t.northDeg<=359?s.north_deg=t.northDeg:delete s.north_deg,"daynight"===t.bgMode?s.bg_mode="daynight":delete s.bg_mode,t.sunRays?s.sun_rays=!0:delete s.sun_rays;const n=(t.weatherEntity||"").trim();n?s.weather_entity=n:delete s.weather_entity,this._serverCfg={...e,settings:s},await this._saveConfigNow(),this._settingsDialog=null,this.requestUpdate(),this._showToast(this._t("gs.saved"))}catch(t){this._settingsDialog&&(this._settingsDialog={...this._settingsDialog,busy:!1}),this._showToast(this._t("toast.error",{err:this._errText(t)}))}}}_boolInput(t,e,i=!1){const s=t=>e(!!t.target.checked);return customElements.get("ha-switch")?W``:W``}_rangeInput(t,e,i,s,o){const n=t=>{const e=Number(t.target.value);Number.isFinite(e)&&o(e)};return customElements.get("ha-slider")?W``:W``}_renderColorRow(t,e){const i=this._settingsDialog.colors[t];return W`
${this._t(e)} this._setFillColor(t,{c:e.target.value})} /> ${this._rangeInput(0,100,1,Math.round(100*i.a),e=>this._setFillColor(t,{a:e/100}))} ${Math.round(100*i.a)}% -
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t){const e=this._fillColors,i=this._glowRadiusCm/this._cellCm*this._gridPitch,s=this._gridPitch,o=t.rooms.map(t=>({r:t,poly:Pe(t)})).filter(t=>!!t.poly),n=this._openingsR.filter(t=>"door"===t.type),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._physicalBodiesR(t),h=new Map;if(r.length)for(const e of n){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,o=Math.sin(i)*e.rlen/2,n=To(t.rooms,r,a,[e.rx-s,e.ry-o,e.rx+s,e.ry+o],this._wallKeyPitch,this._cellCm,this._gridPitch,sl);n>0&&h.set(e.id,Qs(n,this._cellCm,this._gridPitch))}const c=new Map;for(const e of Kr(this.hass,this._devices.filter(e=>e.space===t.id)))e.on&&e.device.id&&!c.has(e.device.id)&&c.set(e.device.id,e.eid);const d=[];for(const p of this._devices){if(p.space!==t.id)continue;const u=c.get(p.id);if(!u)continue;const _=Ei(this.hass.states[u],e.glow_light.c);if(!_)continue;const m=Number(p.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:i,f=this._pos(p),v=[...o].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?Ba(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=l.length?Jo(l,[f.x,f.y],g):[],i=v.r.id?qi(v.r.id,t.rooms):new Set([v.r.id]),c=o.filter(t=>t.r.id&&i.has(t.r.id)),d=c.length?c:[v],p=d.map(i=>{const s=r.length&&i.r.id&&Ao(t.rooms,i.r.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||i.poly;return e.length?Wo(Vo(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=o.filter(t=>!d.includes(t)).map(t=>t.poly);for(const t of n){const i=d.some(e=>{const i=Fe([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!i)continue;const o=t.angle*Math.PI/180,n=Math.cos(o)*t.rlen/2,r=Math.sin(o)*t.rlen/2;if(!Oi([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*s))continue;const a=Ii([f.x,f.y],[t.rx-n,t.ry-r],[t.rx+n,t.ry+r],g,170,h.get(t.id)||0);a&&p.push(e.length?Wo(Vo(a,e)):"M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,ja(this._glowClipCache,b,y,256)}d.push({pos:f,c:_.c,alpha:e.glow_light.a*_.bri,clip:y,r:g})}return d.length?B` +
`}get _glowRadiusCm(){const t=Number(this._settings.glow_radius_cm);return Number.isFinite(t)&&t>0?t:300}get _imperial(){return"mi"===this.hass?.config?.unit_system?.length}get _glowRadiusPlaceholder(){const t=this._glowRadiusCm;return this._imperial?String(Math.round(t/30.48*10)/10):String(t/100)}_renderGlowLayer(t){const e=this._fillColors,i=this._glowRadiusCm/this._cellCm*this._gridPitch,s=this._gridPitch,o=t.rooms.map(t=>({r:t,poly:Pe(t)})).filter(t=>!!t.poly),n=this._openingsR.filter(t=>"door"===t.type),r=this._spaceWalls,a=this._openPairs().flatMap(t=>t.segs),l=this._physicalBodiesR(t),h=new Map;if(r.length)for(const e of n){const i=e.angle*Math.PI/180,s=Math.cos(i)*e.rlen/2,o=Math.sin(i)*e.rlen/2,n=To(t.rooms,r,a,[e.rx-s,e.ry-o,e.rx+s,e.ry+o],this._wallKeyPitch,this._cellCm,this._gridPitch,ol);n>0&&h.set(e.id,Qs(n,this._cellCm,this._gridPitch))}const c=new Map;for(const e of Yr(this.hass,this._devices.filter(e=>e.space===t.id)))e.on&&e.device.id&&!c.has(e.device.id)&&c.set(e.device.id,e.eid);const d=[];for(const p of this._devices){if(p.space!==t.id)continue;const u=c.get(p.id);if(!u)continue;const _=Ei(this.hass.states[u],e.glow_light.c);if(!_)continue;const m=Number(p.marker?.glow_radius_cm),g=Number.isFinite(m)&&m>0?m/this._cellCm*this._gridPitch:i,f=this._pos(p),v=[...o].reverse().find(t=>this._pointInRoom([f.x,f.y],t.r));let y=null;const b=v?`${t.id}|${this._cfgEpoch}|${f.x.toFixed(4)},${f.y.toFixed(4)}|${g.toFixed(4)}`:"",w=v?ja(this._glowClipCache,b):{hit:!1};if(w.hit)y=w.value;else if(v){const e=l.length?Jo(l,[f.x,f.y],g):[],i=v.r.id?qi(v.r.id,t.rooms):new Set([v.r.id]),c=o.filter(t=>t.r.id&&i.has(t.r.id)),d=c.length?c:[v],p=d.map(i=>{const s=r.length&&i.r.id&&Ao(t.rooms,i.r.id,r,a,this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||i.poly;return e.length?Wo(Vo(s,e)):"M "+s.map(t=>t[0]+" "+t[1]).join(" L ")+" Z"}),u=o.filter(t=>!d.includes(t)).map(t=>t.poly);for(const t of n){const i=d.some(e=>{const i=Fe([t.rx,t.ry],e.poly);return i&&Math.hypot(i[0]-t.rx,i[1]-t.ry)<=.75*s});if(!i)continue;const o=t.angle*Math.PI/180,n=Math.cos(o)*t.rlen/2,r=Math.sin(o)*t.rlen/2;if(!Oi([t.rx,t.ry],t.angle,[f.x,f.y],u,.6*s))continue;const a=Ii([f.x,f.y],[t.rx-n,t.ry-r],[t.rx+n,t.ry+r],g,170,h.get(t.id)||0);a&&p.push(e.length?Wo(Vo(a,e)):"M "+a.map(t=>t[0]+" "+t[1]).join(" L ")+" Z")}y=p,Ga(this._glowClipCache,b,y,256)}d.push({pos:f,c:_.c,alpha:e.glow_light.a*_.bri,clip:y,r:g})}return d.length?B` ${d.map((t,e)=>B` @@ -3533,7 +3564,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._t("gs.glow_radius")} {const e=Wa(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> + @input=${t=>{const e=Ba(t.target.value);null!=e&&e>0&&(this._settingsDialog={...this._settingsDialog,glowRadius:e})}} /> ${this._imperial?this._t("gs.unit_ft"):this._t("gs.unit_m")}
@@ -3594,7 +3625,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
`:G} -
${this._t("gs.about_version",{v:Fa})}
+
${this._t("gs.about_version",{v:La})}
${this._t("gs.about_github")} @@ -3651,7 +3682,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t.busy?"…":this._t("btn.save")}
- `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(il,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const t=this._kioskScale,e=(e,i)=>W` + `}_saveKioskScale(t){this._kioskScale={...this._kioskScale,...t};try{localStorage.setItem(sl,JSON.stringify(this._kioskScale))}catch{}this.requestUpdate()}_renderKioskDialog(){const t=this._kioskScale,e=(e,i)=>W`
${this._rangeInput(50,300,5,Math.round(100*t[e]),t=>this._saveKioskScale({[e]:t/100}))} ${Math.round(100*t[e])}% @@ -3682,7 +3713,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${this._spaceDialog?this._renderSpaceDialog():G} ${this._importDialog?this._renderImportDialog():G} ${this._toast?W`
${this._toast}
`:G} - `;const e=this._spaceModel(),i=e.vb,s="devices"===this._mode&&this._showAll,o=this._devices.filter(t=>t.space===e.id&&(!t.hidden||s)),n=xi(this._curSpaceCfg),r=n.showLqi??this._config.show_signal??!0,a=this._config.icon_size??2.5,l=a>8?2.5:a,h=this._viewOr(i),c=this._editing?"":this._stageBg(n),d=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow(),p=d?$s(Ps(d.elevation)).planDim:0,u=this._opMeasureView,_=this._decorMeasure,m=this._bdLive,g=this._furnLive,f="view"===this._mode?this._editorChromeMode:this._mode;return W` + `;const e=this._spaceModel(),i=e.vb,s="devices"===this._mode&&this._showAll,o=this._devices.filter(t=>t.space===e.id&&(!t.hidden||s)),n=xi(this._curSpaceCfg),r=n.showLqi??this._config.show_signal??!0,a=this._config.icon_size??2.5,l=a>8?2.5:a,h=this._viewOr(i),c=this._editing?"":this._stageBg(n),d=this._editing||"daynight"!==this._effBgMode()?null:this._sunNow(),p=d?ks(Ps(d.elevation)).planDim:0,u=this._opMeasureView,_=this._decorMeasure,m=this._bdLive,g=this._furnLive,f="view"===this._mode?this._editorChromeMode:this._mode;return W`
@@ -3761,14 +3792,14 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow preserveAspectRatio="none" />`:G} ${""} ${n.hideDecor&&"decor"!==this._mode?G:this._renderDecorLayer()} - ${(()=>{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Pe(t)),i.get(t));return e.rooms.filter(t=>t.area||"view"===this._mode||this._markup||n.showBorders).map(i=>{let o="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Vi(n.fill,i);if(!this._markup&&(n.showBorders||"none"!==l)){o+=" styled";const t=[];t.push(`--room-stroke:${n.color}`,`--room-stroke-op:${n.showBorders?n.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?zi("temp",null,"none",this._roomTemp(i),n.tempMin,n.tempMax,this._fillColors):"light"===l?zi("light",null,Yr(Kr(this.hass,this._devices,i)),null,n.tempMin,n.tempMax,this._fillColors):i.area?zi(l,"lqi"===l?this._roomLqi(i.area):null,"none",null,n.tempMin,n.tempMax,this._fillColors):null;e?(o+=" filled",t.push(`--room-fill:${e.c}`,`--room-fill-op:${e.a.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),a=t.join(";")}let h;const c=t=>{"view"===this._mode&&(void 0===h&&(h=this._roomArea(i)),this._showTip(t,i.name||this._t("room.unnamed"),h?this._t("tip.area",{value:h}):"",r?this._roomLqi(i.area):null,this._roomTemp(i)))},d=!e.bg&&!n.showNames&&!this._markup,p=this._roomCenter(i),u=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),_=i.id&&!u?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],m=u?[]:this._thickWallCuts(),g=_.concat(m);g.length&&(o+=" noedge");const f=s(i),v=this._spaceWalls,y=v.length&&i.id&&f&&Ao(e.rooms,i.id,v,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||f,b=y?Ke(y,(w=i,e.rooms.filter(t=>t!==w).map(s).filter(Boolean))):[];var w;const k=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",$=y?this._cleanFloor(i,y,e).path:"",x=i.id||G,S=i.area||G,M=$&&y?B`{const t=this._openPairs(),i=new Map,s=t=>(i.has(t)||i.set(t,Pe(t)),i.get(t));return e.rooms.filter(t=>t.area||"view"===this._mode||this._markup||n.showBorders).map(i=>{let o="room "+(e.bg?"overlay":"yard")+(this._markup?" outlined":"");!this._markup||i.id!==this._mergeSel&&i.id!==this._splitSel?.roomId||(o+=" picked");let a="";const l=Vi(n.fill,i);if(!this._markup&&(n.showBorders||"none"!==l)){o+=" styled";const t=[];t.push(`--room-stroke:${n.color}`,`--room-stroke-op:${n.showBorders?n.opacity:0}`);const e="glow"===l?this._fillColors.glow_base:"temp"===l?zi("temp",null,"none",this._roomTemp(i),n.tempMin,n.tempMax,this._fillColors):"light"===l?zi("light",null,Jr(Yr(this.hass,this._devices,i)),null,n.tempMin,n.tempMax,this._fillColors):i.area?zi(l,"lqi"===l?this._roomLqi(i.area):null,"none",null,n.tempMin,n.tempMax,this._fillColors):null;e?(o+=" filled",t.push(`--room-fill:${e.c}`,`--room-fill-op:${e.a.toFixed(3)}`)):t.push("--room-fill:transparent","--room-fill-op:0"),a=t.join(";")}let h;const c=t=>{"view"===this._mode&&(void 0===h&&(h=this._roomArea(i)),this._showTip(t,i.name||this._t("room.unnamed"),h?this._t("tip.area",{value:h}):"",r?this._roomLqi(i.area):null,this._roomTemp(i)))},d=!e.bg&&!n.showNames&&!this._markup,p=this._roomCenter(i),u=this._markup&&(i.id===this._mergeSel||i.id===this._splitSel?.roomId),_=i.id&&!u?t.filter(t=>t.a.id===i.id||t.b.id===i.id).flatMap(t=>t.segs):[],m=u?[]:this._thickWallCuts(),g=_.concat(m);g.length&&(o+=" noedge");const f=s(i),v=this._spaceWalls,y=v.length&&i.id&&f&&Ao(e.rooms,i.id,v,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,ol)||f,b=y?Ke(y,(w=i,e.rooms.filter(t=>t!==w).map(s).filter(Boolean))):[];var w;const $=t=>"M "+t.map(t=>t[0]+" "+t[1]).join(" L ")+" Z",k=y?this._cleanFloor(i,y,e).path:"",x=i.id||G,S=i.area||G,M=k&&y?B`this._hoverRoom={space:e.id,room:i}} @mousemove=${c} @mouseleave=${()=>{this._tip=null,this._hoverRoom=null}}>`:b.length&&y?B`this._hoverRoom={space:e.id,room:i}} @mousemove=${c} @mouseleave=${()=>{this._tip=null,this._hoverRoom=null}}>`:y&&y!==f?B``:G} ${this._toast?W`
${this._toast}
`:G} - `}_vacSource(t){const e=t.marker?.vacuum;if(!1===e?.live)return null;if(e?.source&&this.hass?.states[e.source])return e.source;for(const e of t.entities||[])if(ur(this.hass?.states[e]))return e;return null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return t.map(t=>t.eid).sort().join("\n")}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s={sources:i||"",last:{},flashTs:0,flashKind:null,timer:0,gen:0},this._activityRt.set(t,s)),null!=i&&(s.sources=i),s.flashTs&&Date.now()-s.flashTsthis.requestUpdate(),3360))}_syncActivityRuntime(){if(!this.hass)return;const t=new Set;for(const e of this._devices){if(e.hidden)continue;t.add(e.id);const i=this._visualSamples(e),s=this._activitySourceKey(i);let o=this._activityRt.get(e.id);if(o){if(o.sources!==s){clearTimeout(o.timer),o.sources=s,o.last={},o.flashTs=0,o.flashKind=null;for(const t of i)o.last[t.eid]=t.state}}else{o={sources:s,last:{},flashTs:0,flashKind:null,timer:0,gen:0};for(const t of i)o.last[t.eid]=t.state;this._activityRt.set(e.id,o)}}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e))}_activityTick(){if(this.hass){this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;const e=this._visualSamples(t),i=this._activitySourceKey(e),s=this._activityRt.get(t.id);if(!s||s.sources!==i)continue;"transition"===s.flashKind&&e.some(t=>"transition"===t.activity)&&(clearTimeout(s.timer),s.flashTs=0,s.flashKind=null);let o=null;for(const t of e){const e=Er(s.last[t.eid],t);("event"===e||!o&&e)&&(o=e),s.last[t.eid]=t.state}o&&this._stampActivity(t.id,o,i)}}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=fr(this.hass.states[i||""]?.state),o=pr(this.hass.states[e]?.attributes);let n=this._vacRt.get(t.id);n||(n={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,n)),s&&!n.moving&&(n.trail=[],n.lastPos=null);const r="never"!==wr(t.marker?.vacuum)&&!o?.path;!s&&n.moving&&(n.endedTs=Date.now(),r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=null),n.moving=s;const a=o?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==n.lastKey){const e=Date.now();n.jump=n.lastTs>0&&e-n.lastTs>1e4,n.lastKey=t,n.lastTs=e,r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return G;const i=e.marker?.vacuum||{},s=this._vacSource(e),o=s?pr(this.hass?.states[s]?.attributes):null,n=!!(o&&o.rooms.length>=3),r=o?.pos?wi(this._t("vac.status_found"),{name:s||""}):this._t("vac.status_none"),a=Object.keys(i.calibration||{}),l=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._saveConfig(),this.requestUpdate())};return W` + `}_vacSource(t){const e=t.marker?.vacuum;if(!1===e?.live)return null;if(e?.source&&this.hass?.states[e.source])return e.source;for(const e of t.entities||[])if(ur(this.hass?.states[e]))return e;return null}_vacEntity(t){return t.primary?.startsWith("vacuum.")?t.primary:(t.entities||[]).find(t=>t.startsWith("vacuum."))||null}_isVacDev(t){return!!this._vacEntity(t)}_activitySourceKey(t){return t.map(t=>t.eid).sort().join("\n")}_stampActivity(t,e,i){let s=this._activityRt.get(t);s||(s={sources:i||"",last:{},flashTs:0,flashKind:null,timer:0,gen:0},this._activityRt.set(t,s)),null!=i&&(s.sources=i),s.flashTs&&Date.now()-s.flashTsthis.requestUpdate(),3360))}_syncActivityRuntime(){if(!this.hass)return;const t=new Set;for(const e of this._devices){if(e.hidden)continue;t.add(e.id);const i=this._visualSamples(e),s=this._activitySourceKey(i);let o=this._activityRt.get(e.id);if(o){if(o.sources!==s){clearTimeout(o.timer),o.sources=s,o.last={},o.flashTs=0,o.flashKind=null;for(const t of i)o.last[t.eid]=t.state}}else{o={sources:s,last:{},flashTs:0,flashKind:null,timer:0,gen:0};for(const t of i)o.last[t.eid]=t.state;this._activityRt.set(e.id,o)}}for(const[e,i]of this._activityRt)t.has(e)||(clearTimeout(i.timer),this._activityRt.delete(e))}_activityTick(){if(this.hass){this._syncActivityRuntime();for(const t of this._devices){if(t.hidden)continue;const e=this._visualSamples(t),i=this._activitySourceKey(e),s=this._activityRt.get(t.id);if(!s||s.sources!==i)continue;"transition"===s.flashKind&&e.some(t=>"transition"===t.activity)&&(clearTimeout(s.timer),s.flashTs=0,s.flashKind=null);let o=null;for(const t of e){const e=Ir(s.last[t.eid],t);("event"===e||!o&&e)&&(o=e),s.last[t.eid]=t.state}o&&this._stampActivity(t.id,o,i)}}}_vacTick(){if(this.hass)for(const t of this._devices){if(t.hidden||!this._isVacDev(t))continue;const e=this._vacSource(t);if(!e)continue;const i=this._vacEntity(t),s=fr(this.hass.states[i||""]?.state),o=pr(this.hass.states[e]?.attributes);let n=this._vacRt.get(t.id);n||(n={trail:[],lastKey:"",lastTs:0,moving:!1,jump:!1,endedTs:0,lastPos:null},this._vacRt.set(t.id,n)),s&&!n.moving&&(n.trail=[],n.lastPos=null);const r="never"!==wr(t.marker?.vacuum)&&!o?.path;!s&&n.moving&&(n.endedTs=Date.now(),r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=null),n.moving=s;const a=o?.pos;if(s&&a){const t=a.x+":"+a.y;if(t!==n.lastKey){const e=Date.now();n.jump=n.lastTs>0&&e-n.lastTs>1e4,n.lastKey=t,n.lastTs=e,r&&n.lastPos&&(n.trail=gr(n.trail,n.lastPos,40)),n.lastPos=[a.x,a.y]}}}}_vacEnsureMarker(t){const e=this._serverCfg;if(!e)return null;e.markers=e.markers||[];const i=e.markers.find(e=>e.id===t.id);if(i)return i;if("device"!==t.bindingKind&&"entity"!==t.bindingKind||!t.bindingRef)return null;const s={id:t.id,binding:t.bindingKind+":"+t.bindingRef,space:t.space||null,area:t.area||null,hidden:!!t.hidden};return e.markers.push(s),s}_renderVacSection(t){const e=this._devices.find(e=>e.id===t.devId);if(!e||!this._isVacDev(e))return G;const i=e.marker?.vacuum||{},s=this._vacSource(e),o=s?pr(this.hass?.states[s]?.attributes):null,n=!!(o&&o.rooms.length>=3),r=o?.pos?wi(this._t("vac.status_found"),{name:s||""}):this._t("vac.status_none"),a=Object.keys(i.calibration||{}),l=t=>{const i=this._vacEnsureMarker(e);i&&(i.vacuum={...i.vacuum||{},...t},this._regSignature="",this._saveConfig(),this.requestUpdate())};return W`
${r}
@@ -3910,7 +3941,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?W`
${wi(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:G} `:G} -
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return o=e.mapId,n=s,"default"!==o?o:null!=n?String(n):"default";var o,n}_vacSaveMatrix(t,e,i,s){const o=this._devices.find(e=>e.id===t),n=o?this._vacEnsureMarker(o):this._serverCfg?.markers?.find(e=>e.id===t);if(!n)return!1;const r={...n.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).map(t=>({r:t,poly:Pe(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=We(e);return{name:t.name,cx:i[0],cy:i[1]}}),n=mr(i.rooms,o);n?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix)&&(n.residual>50&&this._showToast(wi(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._showToast(wi(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),o=t.marker?.vacuum?.calibration?.[s],n=this._spaceModel(t.space),r=n?.vb||[0,0,sl,sl],a=o&&6===o.length&&function(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}(o)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1,h={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},c=yr(h),[d,p]=hr(c,(o+n)/2,(r+a)/2);return h.ox=e[0]+e[2]/2-d,h.oy=e[1]+e[3]/2-p,h}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,yr(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=pr(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),o={...e.p,...t};this._vacFit={...e,p:br(o,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,o=s?.clientWidth||1,n=s?.clientHeight||1;return[e/o*t.w,i/n*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[o,n]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+o,oy:s.p0.oy+n}};else{const t=pr(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=yr(s.p0),[a,l]=hr(r,e[0],e[1]),[h,c]=hr(r,s.fx,s.fy),d=Math.hypot(a-h,l-c)||1,[p,u]=[2*a-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*n-c)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:br(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return G;const i=pr(this.hass?.states[e.source]?.attributes);if(!i)return G;const s=yr(e.p),o=[],n=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>hr(s,t,e));e.forEach(([t,e])=>{n.push(t),r.push(e)});const[i,a]=hr(s,t.cx,t.cy);o.push(B` +
`}_vacMapId(t,e){const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return o=e.mapId,n=s,"default"!==o?o:null!=n?String(n):"default";var o,n}_vacSaveMatrix(t,e,i,s){const o=this._devices.find(e=>e.id===t),n=o?this._vacEnsureMarker(o):this._serverCfg?.markers?.find(e=>e.id===t);if(!n)return!1;const r={...n.vacuum||{}};return r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate(),!0}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).map(t=>({r:t,poly:Pe(t)})).filter(({r:t,poly:e})=>t.name&&e).map(({r:t,poly:e})=>{const i=We(e);return{name:t.name,cx:i[0],cy:i[1]}}),n=mr(i.rooms,o);n?this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix)&&(n.residual>50&&this._showToast(wi(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._showToast(wi(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartFit(t){const e=this._vacSource(t),i=e?pr(this.hass?.states[e]?.attributes):null;if(!e||!i)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._vacMapId(t,i),o=t.marker?.vacuum?.calibration?.[s],n=this._spaceModel(t.space),r=n?.vb||[0,0,ol,ol],a=o&&6===o.length&&function(t){const e=t[0]*t[4]-t[1]*t[3];if(!Number.isFinite(e)||Math.abs(e)<1e-12)return null;const i=e<0,s=Math.sqrt(Math.abs(e));let o=180*Math.atan2(-t[1],t[4])/Math.PI;return o=(90*Math.round(o/90)%360+360)%360,{ox:t[2],oy:t[5],s:s,rot:o,mir:i}}(o)||function(t,e){const i=[],s=[];for(const e of t)null!=e.x0?(i.push(e.x0,e.x1),s.push(e.y0,e.y1)):(i.push(e.cx),s.push(e.cy));if(!i.length)return{ox:e[0]+e[2]/2,oy:e[1]+e[3]/2,s:e[2]/1e4,rot:0,mir:!0};const o=Math.min(...i),n=Math.max(...i),r=Math.min(...s),a=Math.max(...s),l=Math.max(n-o,a-r)||1,h={ox:0,oy:0,s:.6*Math.min(e[2],e[3])/l,rot:0,mir:!0},c=yr(h),[d,p]=hr(c,(o+n)/2,(r+a)/2);return h.ox=e[0]+e[2]/2-d,h.oy=e[1]+e[3]/2-p,h}(i.rooms,r);this._markerDialog=null,t.space!==this._space&&(this._space=t.space),this._vacFit={markerId:t.id,source:e,mapId:s,p:a,drag:null}}_vacFitSave(){const t=this._vacFit;if(!t)return;const e=this._vacSaveMatrix(t.markerId,t.source,t.mapId,yr(t.p));this._vacFit=null,e&&this._showToast(this._t("vac.cal_done"))}_vacFitTurn(t){const e=this._vacFit;if(!e)return;const i=pr(this.hass?.states[e.source]?.attributes),s=this._vacGhostCentre(i?.rooms||[]),o={...e.p,...t};this._vacFit={...e,p:br(o,e.p,s[0],s[1])}}_vacGhostCentre(t){const e=[],i=[];for(const s of t)e.push(s.x0??s.cx,s.x1??s.cx),i.push(s.y0??s.cy,s.y1??s.cy);return e.length?[(Math.min(...e)+Math.max(...e))/2,(Math.min(...i)+Math.max(...i))/2]:[0,0]}_vacDelta(t,e,i){const s=this._stageEl,o=s?.clientWidth||1,n=s?.clientHeight||1;return[e/o*t.w,i/n*t.h]}_vacFitPointer(t,e){const i=this._vacFit;if(!i)return;if(t.stopPropagation(),"pointerdown"===t.type){const e=t.target,s=e.getAttribute?.("data-corner");try{t.currentTarget.setPointerCapture?.(t.pointerId)}catch{}return void(this._vacFit={...i,drag:s?{kind:"scale",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:Number(s.split(",")[0]),fy:Number(s.split(",")[1])}:{kind:"move",sx:t.clientX,sy:t.clientY,p0:{...i.p},fx:0,fy:0}})}const s=i.drag;if(s){if("pointermove"===t.type){const[o,n]=this._vacDelta(e,t.clientX-s.sx,t.clientY-s.sy);if("move"===s.kind)this._vacFit={...i,p:{...s.p0,ox:s.p0.ox+o,oy:s.p0.oy+n}};else{const t=pr(this.hass?.states[i.source]?.attributes),e=this._vacGhostCentre(t?.rooms||[]),r=yr(s.p0),[a,l]=hr(r,e[0],e[1]),[h,c]=hr(r,s.fx,s.fy),d=Math.hypot(a-h,l-c)||1,[p,u]=[2*a-h,2*l-c],_=Math.hypot(p+2*o-h,u+2*n-c)/2,m=Math.max(.05,_/d),g={...s.p0,s:s.p0.s*m};this._vacFit={...i,p:br(g,s.p0,s.fx,s.fy)}}return}"pointerup"!==t.type&&"pointercancel"!==t.type||(this._vacFit={...i,drag:null})}}_renderVacFit(t){const e=this._vacFit;if(!e)return G;const i=pr(this.hass?.states[e.source]?.attributes);if(!i)return G;const s=yr(e.p),o=[],n=[],r=[];for(const t of i.rooms){if(null==t.x0)continue;const e=[[t.x0,t.y0],[t.x1,t.y0],[t.x1,t.y1],[t.x0,t.y1]].map(([t,e])=>hr(s,t,e));e.forEach(([t,e])=>{n.push(t),r.push(e)});const[i,a]=hr(s,t.cx,t.cy);o.push(B` ${t.name}`)}let a=G;if(i.pos){const[e,o]=hr(s,i.pos.x,i.pos.y);a=B``}const l=[];if(n.length){const e=(()=>{const t=s[0]*s[4]-s[1]*s[3];return(e,i)=>[(s[4]*(e-s[2])-s[1]*(i-s[5]))/t,(-s[3]*(e-s[2])+s[0]*(i-s[5]))/t]})(),i=Math.min(...n),o=Math.max(...n),a=Math.min(...r),h=Math.max(...r),c=.022*t.w,d=c/4;for(const[t,s,n,r]of[[i,a,o,h],[o,a,i,h],[o,h,i,a],[i,h,o,a]]){const i=e(n,r);l.push(B` `)}}return W`
`)}return this._vacLastView=e,o.length&&!this._vacRaf&&this._vacRafLoop(),o.length||n.length?W` ${n.length?B`${n}`:G} - ${o}`:G}_renderDevice(t,e,i=!0){const s=this._pos(t),o=(s.x-e.x)/e.w*100,n=(s.y-e.y)/e.h*100,r=this._deviceVisual(t),a=t.hidden?"":this._stateClass(t,r),l=t.hidden?null:this._liveTemp(t),h=t.hidden?null:this._liveHum(t),c=!i||t.virtual||t.hidden?null:Jr(this.hass,t.entities),d=t.marker,p=this._displayOf(t),u="icon_ripple"===p&&!t.hidden&&this._config?.live_states&&"alarm"!==r.status?r.activity:"none",_=this._actEntity(t),m=_?this.hass.states[_]:void 0,g="value"!==p||t.hidden||null!=l||null!=h||!m||isNaN(parseFloat(m.state))?null:gi(this.hass,_),f="value"!==p||t.hidden?null:null!=l?l+"°":null!=h?h+"%":g?g.formatted?fi(g,m.attributes?.unit_of_measurement):parseFloat(m.state)+(m.attributes?.unit_of_measurement?" "+m.attributes.unit_of_measurement:""):null,v=_?_.split(".")[0]:null,y=this._config?.live_states&&!t.hidden?Ni(t.icon,v,m?.attributes?.device_class,m?.state,!!d?.icon):t.icon,b=Kr(this.hass,[t]),w=this._config?.live_states&&!t.hidden&&b.map(t=>function(t){if(!t||"on"!==t.state)return null;const e=t.attributes?.rgb_color;return Array.isArray(e)&&e.length>=3&&e.every(t=>Number.isFinite(t))?`rgb(${e[0]}, ${e[1]}, ${e[2]})`:null}(this.hass.states[t.eid])).find(t=>t)||null,k=Number(d?.size)>0?Number(d.size):1,$=Number(d?.angle)||0,x=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${o}%`,`top:${n}%`];return 1!==k&&S.push(`--dev-scale:${k}`),"icon_ripple"===p&&(S.push(`--ripple-scale:${x}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):w&&S.push(`--ripple-color:${w}`)),W`
function(t){if(!t||"on"!==t.state)return null;const e=t.attributes?.rgb_color;return Array.isArray(e)&&e.length>=3&&e.every(t=>Number.isFinite(t))?`rgb(${e[0]}, ${e[1]}, ${e[2]})`:null}(this.hass.states[t.eid])).find(t=>t)||null,$=Number(d?.size)>0?Number(d.size):1,k=Number(d?.angle)||0,x=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${o}%`,`top:${n}%`];return 1!==$&&S.push(`--dev-scale:${$}`),"icon_ripple"===p&&(S.push(`--ripple-scale:${x}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):w&&S.push(`--ripple-color:${w}`)),W`
${"none"!==u?W``:G} ${this._newIds.has(t.id)?W``:G} - ${null!=f?W`${f}`:W``} + ${null!=f?W`${f}`:W``} ${null!=l&&null==f?W`${l}°`:G} ${null!=h&&null==f?W`${h}%`:G} ${null!=c?W`${c}`:G} -
`}_roomArea(t){const e=Pe(t);if(!e)return null;const i=this._spaceWalls,s=i.length&&t.id&&Ao(this._spaceModel().rooms,t.id,i,this._openPairs().flatMap(t=>t.segs),this._wallKeyPitch,this._cellCm,this._gridPitch,sl)||e,o=this._cleanFloor(t,s),n=this._cellCm/this._gridPitch;return bs(o.area*n*n/1e4,"mi"===this.hass?.config?.unit_system?.length)}_roomTemp(t){const e=t.settings?.temp_source;return e?aa(this.hass,e,"temp",this._markers):t.area?this._climate().get(t.area)?.temp??null:null}_roomHum(t){const e=t.settings?.hum_source;return e?aa(this.hass,e,"hum",this._markers):t.area?this._climate().get(t.area)?.hum??null:null}_climate(){const t=this._serverCfg?.markers,e=this._climateCache;if(e&&e.h===this.hass&&e.r===this._iconRules&&e.mk===t)return e.m;const i=function(t,e,i){const s=new Map;if(!t?.entities)return s;const o=sa(i),n=new Set;for(const t of i||[]){if(t?.removed||!0!==t?.use_climate_temp)continue;const e=(t.binding||"").indexOf(":");e>0&&n.add(t.binding.slice(e+1))}const r=new Map;for(const[e,i]of Object.entries(t.entities)){if(i.device_id&&o.devices.has(i.device_id)||!i.device_id&&o.entities.has(e))continue;const s=i.device_id?t.devices?.[i.device_id]:null,a=i.area_id||s?.area_id||null;if(!a)continue;if(i.entity_category)continue;if(!(n.size>0&&e.startsWith("climate.")&&(n.has(e)||i.device_id&&n.has(i.device_id)))){if(_t.has(i.platform))continue;if(la.test(e))continue}let l=r.get(a);l||(l=new Map,r.set(a,l));const h=i.device_id||e;let c=l.get(h);if(!c){const o=t.states?.[e];c={name:(s?s.name_by_user||s.name:i.name||o?.attributes?.friendly_name||e)||e,model:s?.model,ents:[]},l.set(h,c)}c.ents.push(e)}for(const[i,o]of r){const r=[],a=[];for(const[i,s]of o){const o=ia(t,s.name,s.model,s.ents,e),l="mdi:thermometer"===o||"mdi:air-filter"===o;if(l){const e=Zr(t,s.ents);null!=e&&r.push(e)}if(n.size>0&&(n.has(i)||s.ents.some(t=>n.has(t)))){const e=Xr(t,s.ents);null!=e&&r.push(e)}if(l||"mdi:water-percent"===o){const e=ta(t,s.ents);null!=e&&a.push(e)}}(r.length||a.length)&&s.set(i,{temp:r.length?Math.round(r.reduce((t,e)=>t+e,0)/r.length*10)/10:null,hum:a.length?Math.round(a.reduce((t,e)=>t+e,0)/a.length):null})}return s}(this.hass,this._iconRules,t);return this._climateCache={h:this.hass,r:this._iconRules,mk:t,m:i},i}_resetRoomDialogFields(){this._roomEditId=null,this._roomFill="",this._roomTempSrc="",this._roomHumSrc="",this._roomSrcOpen=null,this._roomSrcFilter="",this._roomNameScale=1,this._roomLabelScale=1}_openRoomEdit(t){t.id&&(this._roomEditId=t.id,this._nameSel=t.name||"",this._areaSel=t.area||"",this._roomFill=t.settings?.fill_mode||"",this._roomTempSrc=t.settings?.temp_source||"",this._roomHumSrc=t.settings?.hum_source||"",this._roomNameScale=Ki(t.settings?.name_scale),this._roomLabelScale=Ki(t.settings?.label_scale),this._roomSrcOpen=null,this._roomSrcFilter="",this._roomDialog=!0)}_roomSettingsFromDialog(){const t={};return this._roomFill&&(t.fill_mode=this._roomFill),this._roomTempSrc&&(t.temp_source=this._roomTempSrc),this._roomHumSrc&&(t.hum_source=this._roomHumSrc),1!==this._roomNameScale&&(t.name_scale=this._roomNameScale),1!==this._roomLabelScale&&(t.label_scale=this._roomLabelScale),Object.keys(t).length?t:null}_saveRoomEdit(){const t=this._curSpaceCfg,e=t?.rooms.find(t=>t.id===this._roomEditId);if(!e)return this._roomDialog=!1,void(this._roomEditId=null);e.name=this._nameSel.trim()||e.name,e.area=this._areaSel||null;const i=this._roomSettingsFromDialog();i?e.settings=i:delete e.settings,this._saveConfig(),this._roomDialog=!1,this._roomEditId=null,this._nameSel="",this._areaSel="",this._regSignature="",this._maybeRebuildDevices(),this.requestUpdate(),this._showToast(this._t("toast.room_updated"))}_roomSrcCandidates(){const t=this.hass,e=sa(this._markers),i=this._roomSrcFilter.trim().toLowerCase(),s=[];for(const o of Object.values(t.devices)){if("service"===o.entry_type)continue;if(e.devices.has(o.id))continue;const t=(o.name_by_user||o.name||o.id).trim();i&&!t.toLowerCase().includes(i)||s.push({value:"device:"+o.id,label:t,sub:o.model||this._t("marker.sub_device")})}for(const[o,n]of Object.entries(t.entities)){if(!o.startsWith("sensor.")||n.hidden)continue;if(oa(t,o,e))continue;const r=n.name||t.states[o]?.attributes?.friendly_name||o;i&&!(r+" "+o).toLowerCase().includes(i)||s.push({value:"entity:"+o,label:r,sub:o})}return s.sort((t,e)=>t.label.localeCompare(e.label)),s.slice(0,200)}_roomSrcLabel(t){const e=t.indexOf(":"),i=t.slice(0,e),s=t.slice(e+1);return"device"===i?this.hass.devices[s]?.name_by_user||this.hass.devices[s]?.name||s:this.hass.entities[s]?.name||this.hass.states[s]?.attributes?.friendly_name||s}_labelPos(t,e){const i=this._layout["rl_"+(t.id||"")];if(i&&i.s===e)return{x:i.x*sl,y:i.y*sl};const s=this._snap(this._roomCenter(t));return{x:s[0],y:s[1]}}_labelDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=this._labelPos(e,i);this._drag={id:"rl_"+(e.id||""),sx:t.clientX,sy:t.clientY,ox:s.x,oy:s.y,moved:!1},ll(t),this._tip=null}_labelMove(t,e,i){const s="rl_"+(e.id||"");if(!this._drag||this._drag.id!==s)return;const o=this._stageEl;if(!o)return;const n=this._spaceModel(i).vb,r=o.getBoundingClientRect(),a=this._viewOr(n),l=(t.clientX-this._drag.sx)/r.width*a.w,h=(t.clientY-this._drag.sy)/r.height*a.h;Math.abs(t.clientX-this._drag.sx)+Math.abs(t.clientY-this._drag.sy)>3&&(this._drag.moved=!0);const c=_n(this._drag.ox+l),d=_n(this._drag.oy+h);this._savePos({id:s,space:i},c,d)}_labelUp(t){const e="rl_"+(t.id||"");if(!this._drag||this._drag.id!==e)return;const i=this._drag.moved;this._drag=i?this._drag:null,i&&window.setTimeout(()=>this._drag=null,0)}_labelScale(t){const e=this._layout["rl_"+(t.id||"")]?.k;return"number"==typeof e&&Number.isFinite(e)?Math.min(3,Math.max(.5,e)):1}_rlResizeDown(t,e,i){if("plan"!==this._mode)return;t.preventDefault(),t.stopPropagation();const s=t.target.closest(".roomlabel");if(!s)return;const o=s.getBoundingClientRect(),n=o.left+o.width/2,r=o.top+o.height/2,a=Math.max(8,Math.hypot(t.clientX-n,t.clientY-r));this._rlResize={id:"rl_"+(e.id||""),space:i,k0:this._labelScale(e),cx:n,cy:r,d0:a},ll(t)}_rlResizeMove(t){const e=this._rlResize;if(!e)return;t.stopPropagation();const i=Math.max(8,Math.hypot(t.clientX-e.cx,t.clientY-e.cy)),s=Math.min(3,Math.max(.5,e.k0*(i/e.d0))),o=this._layout[e.id];if(o)this._layout={...this._layout,[e.id]:{...o,k:s}};else{const t=e.id.slice(3),i=this._spaceModel(e.space).rooms.find(e=>e.id===t);if(!i)return;const o=this._labelPos(i,e.space);this._layout={...this._layout,[e.id]:{s:e.space,x:o.x/sl,y:o.y/sl,k:s}}}this._dirtyPos.add(e.id)}_rlResizeUp(){this._rlResize&&(this._rlResize=null,this._persistLayout())}_renderRoomGear(t,e,i){if(!t.id)return G;let s=null;if(t.poly?(s=this._gearPtCache.get(t.poly)||null,s||(s=We(t.poly),this._gearPtCache.set(t.poly,s))):null!=t.x&&null!=t.y&&(s=[t.x+(t.w||0)/2,t.y+(t.h||0)/2]),!s)return G;const o=(s[0]-i.x)/i.w*100,n=(s[1]-i.y)/i.h*100;return W``}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return G;const o=this._labelPos(t,e.id),n=(o.x-i.x)/i.w*100,r=(o.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),h=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&h.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&h.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&h.push(W`${e}`)}if(s.labelLight){const e=(c=Kr(this.hass,this._devices,t)).length?{on:c.filter(t=>t.on).length,total:c.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});h.push(W`${t}`)}}}var c;return W`
`}_renderRoomLabel(t,e,i,s){if(!t.name&&!this._markup)return G;const o=this._labelPos(t,e.id),n=(o.x-i.x)/i.w*100,r=(o.y-i.y)/i.h*100,a=Math.min(1,s.opacity+.25),l=this._labelScale(t),h=[];if(t.area||t.settings?.temp_source||t.settings?.hum_source||s.labelLight){if(s.labelTemp){const e=this._roomTemp(t);null!=e&&h.push(W`${e}°`)}if(s.labelHum){const e=this._roomHum(t);null!=e&&h.push(W`${e}%`)}if(s.labelLqi&&t.area){const e=this._roomLqi(t.area);null!=e&&h.push(W`${e}`)}if(s.labelLight){const e=(c=Yr(this.hass,this._devices,t)).length?{on:c.filter(t=>t.on).length,total:c.length}:null;if(e){const t=0===e.on?this._t("roomcard.light_off"):e.on===e.total?this._t("roomcard.light_on"):this._t("roomcard.light_partial",{on:e.on,total:e.total});h.push(W`${t}`)}}}var c;return W`
this._labelDown(i,t,e.id)} @@ -3974,12 +4005,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @pointerup=${()=>this._rlResizeUp()} @pointercancel=${()=>this._rlResizeUp()}>`):G}
`}get _measureAnchor(){return this._markup&&this._cursorPt?"draw"!==this._tool&&"partition"!==this._tool||!this._path.length||this._contourClosed?"split"===this._tool&&this._splitSel?.pts?.length?this._splitSel.pts[this._splitSel.pts.length-1]:null:this._path[this._path.length-1]:null}_renderMeasureLabel(t){const e=this._measureAnchor,i=this._cursorPt,s=(i[0]-t.x)/t.w*100,o=(i[1]-t.y)/t.h*100,n=Yi(e,i),r=Math.round(10*n)/10,a=Ji(n);return W`
- ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,o]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-o)<1e-6)return null;const n=(e+s)/2,r=(i+o)/2;if("line"===t.kind){const e=Yi(t.a,t.b);return{x:n,y:r,on45:Ji(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,o]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(o-i)/2]);return{x:n,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(o-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:n,y:r,on45:!1,text:`${a} × ${l} · ${bs(Us(Math.abs(s-e),this._cellCm,this._gridPitch)*Us(Math.abs(o-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=sl,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Pe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return B``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,o=null;for(const n of e)if(!(Math.abs(n[0]-t[0])<1e-6&&Math.abs(n[1]-t[1])<1e-6)){if(Math.abs(n[0]-t[0])<=i){const e=Math.abs(n[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!o||e + ${this._fmtLen(e,i)} · ${r}°
`}get _decorMeasure(){const t=this._decorDraft;if(!t||"decor"!==this._mode)return null;const[e,i]=t.a,[s,o]=t.b;if(Math.abs(e-s)<1e-6&&Math.abs(i-o)<1e-6)return null;const n=(e+s)/2,r=(i+o)/2;if("line"===t.kind){const e=Yi(t.a,t.b);return{x:n,y:r,on45:Ji(e),text:`${this._fmtLen(t.a,t.b)} · ${Math.round(10*e)/10}°`}}const a=this._fmtLen([e,i],[s,i]),l=this._fmtLen([s,i],[s,o]);if("ellipse"===t.kind){const t=this._fmtLen([0,0],[Math.abs(s-e)/2,0]),a=this._fmtLen([0,0],[0,Math.abs(o-i)/2]);return{x:n,y:r,on45:!1,text:Math.abs(Math.abs(s-e)-Math.abs(o-i))<1e-6?`R ${t}`:`Rx ${t} × Ry ${a}`}}return{x:n,y:r,on45:!1,text:`${a} × ${l} · ${bs(Us(Math.abs(s-e),this._cellCm,this._gridPitch)*Us(Math.abs(o-i),this._cellCm,this._gridPitch)/1e4,this._imperial)}`}}get _alignPoint(){if(this._markup){if("draw"===this._tool&&this._path.length&&!this._contourClosed&&this._cursorPt)return this._cursorPt;if("split"===this._tool&&this._splitSel?.pts?.length&&this._cursorPt)return this._cursorPt;if(this._drag?.id.startsWith("rl_")&&this._drag.moved){const t=this._drag.id.slice(3),e=this._spaceModel().rooms.find(e=>e.id===t);return e?(()=>{const t=this._labelPos(e,this._space);return[t.x,t.y]})():null}return null}if("devices"===this._mode&&this._drag?.moved){const t=this._devices.find(t=>t.id===this._drag.id);return t?(()=>{const e=this._pos(t);return[e.x,e.y]})():null}if("decor"===this._mode){if(this._decorDraft)return this._decorDraft.b;if(this._decorMove){const t=this._decorList.find(t=>t.id===this._decorMove.id);if(!t)return null;const e=ol,i=this._decorH;return"line"===t.kind?[t.x1*e,t.y1*i]:[t.x*e,t.y*i]}return null}return null}_alignCandidates(){const t=[],e=this._spaceModel();if(this._markup){if(this._drag?.id.startsWith("rl_")){const i=this._drag.id.slice(3);for(const s of e.rooms){if(!s.name||s.id===i)continue;const e=this._labelPos(s,this._space);t.push([e.x,e.y])}return t}for(const i of e.rooms){const e=Pe(i);if(e)for(const i of e)t.push(i)}if("draw"===this._tool)for(const e of this._path)t.push(e);if("split"===this._tool&&this._splitSel?.pts)for(const e of this._splitSel.pts)t.push(e);return t}if("devices"===this._mode){for(const e of this._devices){if(e.space!==this._space||e.id===this._drag?.id)continue;const i=this._pos(e);t.push([i.x,i.y])}return t}if("decor"===this._mode){const e=this._decorMove?.id;return t.push(...this._decorSnapGeometry(e).points),this._decorDraft&&t.push(this._decorDraft.a),t}return t}_renderAlignGuides(){const t=this._alignPoint;if(!t)return B``;const e=this._drag?.id.startsWith("rl_")?.5*this._gridPitch:.05*this._gridPitch,i=function(t,e,i){let s=null,o=null;for(const n of e)if(!(Math.abs(n[0]-t[0])<1e-6&&Math.abs(n[1]-t[1])<1e-6)){if(Math.abs(n[0]-t[0])<=i){const e=Math.abs(n[1]-t[1]);e>1e-6&&(!s||e1e-6&&(!o||e ${i.map(e=>{const[i,n,r,a]="x"===e.axis?[e.at,e.from[1],e.at,t[1]+Math.sign(t[1]-e.from[1])*o]:[e.from[0],e.at,t[0]+Math.sign(t[0]-e.from[0])*o,e.at];return B` `})} `}_renderOpeningCenterTick(t){const e=(t.angle+90)*Math.PI/180;return B``}_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._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ne(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&!oa(this.hass,t,sa(this._markers))}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,o=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,n=this._openingAmt(t),r=n>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a=s.length?Fo(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,sl):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,c=t.flip_v?-1:1;let d,p=0,u=0;if(a.cm>0&&(a.ox||a.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);p=a.ox*i-a.oy*s,u=a.ox*s+a.oy*i,u*=c,p*=h}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``}_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._planEntityAvailable(t.contact)?this.hass.states[t.contact]?.state:null;return Ne(t.type,e,!!t.invert)}_planEntityAvailable(t){return!!t&&!na(this.hass,t,oa(this._markers))}_renderOpenings(t){const e=this._openingsR;if(!e.length)return B``;const i=t.color,s=this._spaceWalls,o=this._spaceModel().rooms;return B`${e.map(t=>{const e=t.rlen/2,n=this._openingAmt(t),r=n>0&&!!t.contact&&this._planEntityAvailable(t.contact)?"var(--hp-open)":i,a=s.length?Fo(o,{x:t.rx,y:t.ry,angle:t.angle,length:t.rlen,flip_v:t.flip_v},s,this._wallKeyPitch,this._cellCm,this._gridPitch,ol):{ox:0,oy:0,cm:0},l=a.cm>0?a.cm/this._cellCm*this._gridPitch/2:4,h=t.flip_h?-1:1,c=t.flip_v?-1:1;let d,p=0,u=0;if(a.cm>0&&(a.ox||a.oy)){const e=-t.angle*Math.PI/180,i=Math.cos(e),s=Math.sin(e);p=a.ox*i-a.oy*s,u=a.ox*s+a.oy*i,u*=c,p*=h}if("window"===t.type){const t=Math.PI/2*e,i=a.cm>0?B``:G;d=B` ${this._t("opening.length_label")} {const i=Wa(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> + @input=${e=>{const i=Ba(e.target.value);null!=i&&(this._openingDialog={...t,lengthCm:i})}} /> ${e(this._contactCandidates(),t.contact,e=>this._openingDialog={...t,contact:e})} @@ -4309,7 +4340,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @click=${()=>this._setMode("view")}> -
`}_cardEntities(t){const e=this.hass,i=[],s=new Set,o=t=>{if(!t||s.has(t)||!e.states[t])return;const o=e.entities[t];if("config"===o?.entity_category||"diagnostic"===o?.entity_category)return;s.add(t);const n=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(n)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(n)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(n)&&i.push({eid:t,kind:"value"})};for(const i of Kr(e,[t]))o(i.eid);t.primary&&o(t.primary);for(const e of t.entities)o(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?gi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Fi);return W``}_cardEntities(t){const e=this.hass,i=[],s=new Set,o=t=>{if(!t||s.has(t)||!e.states[t])return;const o=e.entities[t];if("config"===o?.entity_category||"diagnostic"===o?.entity_category)return;s.add(t);const n=t.split(".")[0];["light","switch","fan","humidifier","siren","input_boolean"].includes(n)?i.push({eid:t,kind:"toggle"}):["cover","valve","lock","climate","media_player","vacuum","water_heater"].includes(n)?i.push({eid:t,kind:"open"}):["sensor","binary_sensor","number","select"].includes(n)&&i.push({eid:t,kind:"value"})};for(const i of Yr(e,[t]))o(i.eid);t.primary&&o(t.primary);for(const e of t.entities)o(e);return i.slice(0,12)}_cardToggle(t){const e=t.split(".")[0];"lock"!==e&&"alarm_control_panel"!==e&&this.hass.callService("homeassistant","toggle",{entity_id:t}).catch(t=>this._showToast(this._t("toast.error",{err:this._errText(t)})))}_renderInfoCard(){const t=this._infoCard,e=t.primary?this.hass.states[t.primary]:void 0,i=e?gi(this.hass,t.primary)?.text??e.state:null,s=(t.controls??t.marker?.controls??[]).filter(Fi);return W`this._infoCard=null}>
${(()=>{const e=this._cardEntities(t);return e.length?W`
@@ -4344,8 +4375,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t.primary?W``:G} - - +
`}_renderMarkerDialog(){const t=this._markerDialog,e="virtual"===t.bindingMode,i=this._bindingCandidates(),s=this._bindingEntities(t.binding),o=(()=>{if(e)return null;const s=i.find(e=>e.value===t.binding);if(s)return s.label;const[o,n]=t.binding.split(":");return"device"===o?this.hass.devices[n]?.name_by_user||this.hass.devices[n]?.name||n:this.hass.states[n]?.attributes?.friendly_name||n})();return W`
@@ -4385,7 +4415,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @input=${e=>this._markerDialog={...t,bindingFilter:e.target.value}} />
${i.map(e=>W`
this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:Wr(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> + @click=${()=>this._markerDialog={...t,binding:e.value,bindingOpen:!1,controls:Br(e.value,t.controls,this._bindingEntities(e.value)),autoIcon:this._autoIconForBinding(e.value)}}> ${e.label}${e.sub}
`)} ${i.length?G:W`
${this._t("marker.nothing_found")}
`} @@ -4438,7 +4468,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .value=${t.controlsFilter} @input=${e=>this._markerDialog={...t,controlsFilter:e.target.value}} /> ${t.controlsFilter.trim()?W`
- ${Object.keys(this.hass.states).filter(e=>Wr(t.binding,[e],s).length>0&&!t.controls.includes(e)).filter(e=>{const i=t.controlsFilter.trim().toLowerCase(),s=String(this.hass.states[e]?.attributes?.friendly_name||"");return e.toLowerCase().includes(i)||s.toLowerCase().includes(i)}).slice(0,8).map(e=>W`
`)}
-
`:G}`:G}`}_renderRoomDialog(){const t=!!this._roomEditId,e=[...this._freeAreas];if(t&&this._areaSel&&!e.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&e.unshift(t)}return W``:G}`:G}`}_renderRoomDialog(){const t=!!this._roomEditId,e=!!this._areaSel||!!this._nameSel.trim(),i=[...this._freeAreas];if(t&&this._areaSel&&!i.some(t=>t.area_id===this._areaSel)){const t=this.hass.areas[this._areaSel];t&&i.unshift(t)}return W`
@@ -4763,7 +4793,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow @@ -4794,16 +4824,11 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${t?W``:W` - ${this._pendingSplit?G:W``:W`${this._pendingSplit?G:W``} - `}
-
`}}hl.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},hl.ZOOM_MAX=8,hl.ZOOM_MIN=1/3,hl._touchSeen=!1,hl._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,hl.styles=_a,customElements.get("houseplan-card")||customElements.define("houseplan-card",hl),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${Fa} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); +
`}}cl.properties={_hdrH:{state:!0},_booting:{state:!0},_bootFading:{state:!0},_bootSoft:{state:!0},_tapConfirm:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_hoverRoom:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_wallDialog:{state:!0},_drawWallField:{state:!0},_activeDraftId:{state:!0},_physicalSel:{state:!0},_physicalDialog:{state:!0},_physicalDrag:{state:!0},_physicalRotate:{state:!0},_duplicateColumnId:{state:!0},_rszSel:{state:!0},_rszLive:{state:!0},_opMeasure:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_openWallAnchor:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorEraseConfirm:{state:!0},_decorTextDialog:{state:!0},_decorShapeDialog:{state:!0},_backdropDialog:{state:!0},_furnPalette:{state:!0},_bdDrag:{state:!0},_dtBox:{state:!0},_dtDrag:{state:!0},_kioskDialog:{state:!0},_vacFit:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_alignDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},cl.ZOOM_MAX=8,cl.ZOOM_MIN=1/3,cl._touchSeen=!1,cl._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,cl.styles=ma,customElements.get("houseplan-card")||customElements.define("houseplan-card",cl),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info(`%c HOUSEPLAN-CARD %c v${La} `,"background:#3ea6ff;color:#04121f;font-weight:700",""); diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6c6bd7ee..b99adbc5 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -478,12 +478,18 @@ hash falls back to the default. - **Resolved device state** (2026-08-06): HA provides states per entity, not one state per device. `resolvedDeviceStateEntities` therefore starts from uncategorised registry entities, resolves one functional role (whole-device - domains, then semantic binary signals, then switches), and aggregates + domains, then semantic binary signals, then one representative switch), and aggregates passive readings as the final fallback. `_visualSamples` consumes the full result; `primaryEntity` only selects its first member where a single action target is required. Integration option switches can no longer make an otherwise healthy device working or unavailable merely by list order. For - `climate`, a recognized explicit `hvac_action` is authoritative: idle remains + A switch-only device never aggregates sibling option switches: integrations + which fail to categorise night mode, voice enhancement or child lock cannot + paint the whole marker as working. When generic HA metadata identifies a + dedicated Power switch in such a composite controller, `on` is a neutral + powered lifecycle and `off` reuses the faded unavailable presentation; a + lone relay keeps normal working-state yellow. For `climate`, a recognized + explicit `hvac_action` is authoritative: idle remains neutral and heating/cooling/preheating/defrosting are working. Unknown vendor pseudo-actions are ignored; when no recognized action exists, a current non-off state advertised by `hvac_modes` (or a standard HA HVAC mode) is the diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f9637cc4..ffdf6d69 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,23 @@ ## Unreleased +## v1.60.2-beta.2 — 2026-08-08 + +- Composite switch-only devices now use one representative lifecycle entity + instead of treating every uncategorised feature toggle as whole-device + activity. A dedicated Power switch is selected from generic Home Assistant + metadata: powered-on appliances stay neutral, while off/unavailable ones use + the existing faded presentation. Standalone relays retain their normal + yellow working state. +- Device information uses the wide responsive dialog shell and a wrapping + footer which keeps Edit, Open in HA and Close aligned on narrow screens. +- New Room has one save path: choosing “No area” in the area list and entering + a name enables the regular Save action and stores `area: null`; the duplicate + “No area” footer button has been removed. +- Erase in the Background editor gives thin lines and outlined shapes an + invisible 16 px screen-space target without changing their appearance. The + target remains usable at every zoom level. + ## v1.60.2-beta.1 — 2026-08-07 - The final review follow-up makes geometry cache invalidation root-safe and diff --git a/docs/CHANGELOG.ru.md b/docs/CHANGELOG.ru.md index babc3dff..1cb23d60 100755 --- a/docs/CHANGELOG.ru.md +++ b/docs/CHANGELOG.ru.md @@ -8,6 +8,24 @@ ## В разработке +## v1.60.2-beta.2 — 2026-08-08 + +- Составные устройства, представленные только набором switch-сущностей, теперь + используют один репрезентативный источник состояния вместо трактовки каждого + служебного переключателя как работы всего устройства. Отдельная сущность + Power определяется по общим метаданным Home Assistant: включённое устройство + остаётся нейтральным, а выключенное или недоступное отображается приглушённым. + Одиночные реле по-прежнему подсвечиваются жёлтым при работе. +- Информационная карточка устройства переведена на широкий адаптивный диалог; + кнопки «Редактировать», «Открыть в HA» и «Закрыть» корректно располагаются и + переносятся на узких экранах. +- В диалоге «Новая комната» оставлен единый сценарий сохранения: выбор + «— без зоны —» и введённое имя активируют обычную кнопку «Сохранить», комната + записывается с `area: null`. Дублирующая кнопка «Без зоны» удалена. +- Инструмент «Стереть» в редакторе подложки получил невидимую экранную область + попадания 16 px для тонких линий и контуров. Внешний вид объектов не меняется, + а удобная ширина сохраняется при любом масштабе. + ## v1.60.2-beta.1 — 2026-08-07 - Финальные правки по ревью сделали инвалидацию геометрических кэшей diff --git a/docs/FILTERING.md b/docs/FILTERING.md index 3ca904eb..45eec882 100644 --- a/docs/FILTERING.md +++ b/docs/FILTERING.md @@ -128,7 +128,13 @@ source precedence is (`_visualSamples` / `_actEntity`): separately de-duplicate and keep only currently controllable entities. 3. otherwise the device's **resolved state role** (`resolvedDeviceStateEntities`): functional device domains first, then - semantic binary signals, then switches, then passive readings together. + semantic binary signals, then one representative switch, then passive + readings together. A switch-only device does not aggregate sibling feature + toggles into its working state; this covers integrations which expose power, + modes and options as uncategorised peer switches. If HA metadata identifies + a dedicated Power entity in that composite controller, Power=on is neutral + and Power=off uses the existing faded unavailable style. A lone relay is + unchanged and remains yellow while on. `primaryEntity` is only the first entity of this same set for actions which require one target; it no longer defines marker availability by itself. diff --git a/docs/STATUS.md b/docs/STATUS.md index 05f6f392..0c1d6d53 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -11,15 +11,15 @@ > (versions, publication, infrastructure), DEVELOPMENT.md for new gotchas, > ARCHITECTURE.md for design changes, ROADMAP.md when plans move. -## Snapshot (2026-08-07) +## Snapshot (2026-08-08) | Item | State | |---|---| -| Version | **v1.60.2-beta.1** everywhere (manifest, const.py, package.json, CARD_VERSION) — pre-release with persistent unfinished outlines, independent partitions/columns and the complete follow-up review hardening. The tag points at `dev`; `main` remains on v1.60.0 | +| Version | **v1.60.2-beta.2** everywhere (manifest, const.py, package.json, CARD_VERSION) — pre-release with corrected composite-device lifecycle state, responsive device information, a single no-area room save path and a wider Erase hit target. The tag points at `dev`; `main` remains on v1.60.0 | | Workflow | Owner's rule since 2026-08-07: ordinary fixes/features are made **locally, without tests and without commits**. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Nothing is copied to the home instance by hand | | GitHub | https://github.com/Matysh/houseplan-card — `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) | -| CI | v1.60.2-beta.1 uses the targeted local pre-release gate; exact commands belong in the release handoff rather than this snapshot. The exact-SHA Ubuntu Validate remains mandatory and may run the broader CI matrix automatically: `release.yml` withholds the asset until every matching run finishes green. Stable releases additionally require the complete local frontend/backend/smoke gate before tagging | +| CI | v1.60.2-beta.2 uses the targeted local pre-release gate; exact commands belong in the release handoff rather than this snapshot. The exact-SHA Ubuntu Validate remains mandatory and may run the broader CI matrix automatically: `release.yml` withholds the asset until every matching run finishes green. Stable releases additionally require the complete local frontend/backend/smoke gate before tagging | | HACS | Custom repository works. **Inclusion PR: hacs/default#9004** — open, valid, labeled, mergeable clean, never drafted. Queue: 1212 open, 835 older than ours. Merge rate COLLAPSED: 75 in July but almost all in the first decade, 0 in the last week (checked 2026-07-29) — maintainers process in rare bursts; ETA unknowable, months at best. Nothing actionable on our side | | Home instance | ha.jbstudio.pro (SSH port **22222**, key `ha_jb`; HA config root is `/mnt/data/supervisor/homeassistant` — `/config` does NOT exist in this SSH environment), last direct copy was **v1.57.0**; from v1.58.0 on it updates itself through HACS by tag (no scp) | | Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover | diff --git a/docs/TESTING-DEMO.md b/docs/TESTING-DEMO.md index badadd82..7f6ea63f 100644 --- a/docs/TESTING-DEMO.md +++ b/docs/TESTING-DEMO.md @@ -120,8 +120,8 @@ - Промах далеко от стены → тост — Стенд: кликнуть в центр комнаты. - Esc/Ctrl+Z снимает точку, Reset чистит — Стенд: во время рисования. - Замыкание (≥4 точек) → диалог комнаты — Стенд: нарисовать комнату во «Дворе». -- В диалоге только свободные зоны; выбор зоны подставляет имя — Стенд: все 6 зон заняты — список пуст: создать комнату «без зоны». -- «Без зоны» требует имя, `area: null` — Стенд: там же. +- В диалоге только свободные зоны; выбор зоны подставляет имя — Стенд: все 6 зон заняты — в списке остаётся «— без зоны —». +- При «— без зоны —» обычная кнопка «Сохранить» активна после ввода имени и создаёт комнату с `area: null`; отдельной кнопки «Без зоны» нет — Стенд: там же. - Cancel диалога возвращает контур — Стенд: там же. - Сохранение с зоной раскладывает устройства зоны — Стенд: не проверяется как есть (у demo-сущностей нет device registry — автораскладка по зонам не работает, маркеры на стенде явные); полноценно — локально/на даче. - Erase удаляет линию, удаление комнаты — полигон — Стенд: инструментом «ластик линий» в тулбаре (клик по своей линии). diff --git a/docs/USER-GUIDE.ru.md b/docs/USER-GUIDE.ru.md index 4480fa22..caffd5f4 100644 --- a/docs/USER-GUIDE.ru.md +++ b/docs/USER-GUIDE.ru.md @@ -236,7 +236,7 @@ title: План дома 5. Замкните контур кликом по первой точке или `Ctrl` + кликом в любом месте. Быстрое замыкание доступно после двух нарисованных граней и не сработает, если замыкающая стена создаёт самопересечение. -6. В диалоге задайте название и выберите свободную HA-зону либо сохраните без зоны. +6. В диалоге задайте название, выберите свободную HA-зону либо оставьте в списке «— без зоны —», затем нажмите «Сохранить». Во время рисования показываются длина сегмента и угол. Углы, кратные 45°, выделяются. Удерживайте `Shift`, чтобы зафиксировать текущий отрезок по ближайшему такому направлению; предпросмотр и поставленная точка останутся на сетке. Новая комната не может частично перекрывать другую, но полностью вложенная «островная» комната поддерживается. @@ -528,6 +528,7 @@ House Plan читает реестры устройств, сущностей и |---|---|---|---|---| | `light.*` | Жёлтый, пока `on` | Работа, пока `on` | Стандартная лампочка off/on | Toggle | | `switch.*`, `fan.*`, `humidifier.*` | Жёлтый, пока `on` | Работа, пока `on` | Обычно нет | Внутренняя карточка, если Toggle не выбран явно | +| Составное switch-only устройство с отдельной сущностью Power | Нейтральное при Power=`on`; приглушённое при Power=`off`/`unknown`/`unavailable` | Нет: соседние переключатели режимов и функций не считаются работой всего устройства | Обычно нет | Внутренняя карточка | | Motion/vibration/sound binary sensor | Нейтральный | Событие около 3.3 с при переходе off→on | Нет | Внутренняя карточка | | Occupancy/presence binary sensor | Нейтральный | Пока присутствие активно | Нет | Внутренняя карточка | | Door/window/garage contact | Оранжевый, пока `on` | Короткое событие при открытии | Закрыто/открыто для известных классов | Внутренняя карточка | diff --git a/package-lock.json b/package-lock.json index ccea5ab7..cc5ca3b1 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "houseplan-card", - "version": "1.60.2-beta.1", + "version": "1.60.2-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "houseplan-card", - "version": "1.60.2-beta.1", + "version": "1.60.2-beta.2", "license": "MIT", "dependencies": { "lit": "^3.1.3", diff --git a/package.json b/package.json index e19bdda8..6c0a342f 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "houseplan-card", - "version": "1.60.2-beta.1", + "version": "1.60.2-beta.2", "description": "Interactive house plan Lovelace card for Home Assistant", "license": "MIT", "type": "module", diff --git a/src/device-visual.ts b/src/device-visual.ts index 8c3e4e21..e7f0d7f4 100644 --- a/src/device-visual.ts +++ b/src/device-visual.ts @@ -84,6 +84,26 @@ const unavailable = (state: string): boolean => const lower = (v: unknown): string => String(v ?? '').trim().toLowerCase(); +/** + * A dedicated whole-device power switch, as opposed to a relay whose `on` + * state is the useful work itself. This uses generic HA registry/state + * metadata only; no integration, model or device-name exception is involved. + */ +export function isDevicePowerSwitch(hass: any, eid: string): boolean { + if (!eid.startsWith('switch.')) return false; + const reg = hass?.entities?.[eid] || {}; + const st = hass?.states?.[eid]; + const objectId = eid.slice('switch.'.length).toLowerCase(); + if (/(?:^|_)(?:main_)?power$/.test(objectId)) return true; + const exact = [reg.translation_key, reg.original_name, reg.name] + .map(lower) + .some((value) => ['power', 'main power', 'power switch', 'питание'].includes(value)); + if (exact) return true; + const friendly = lower(st?.attributes?.friendly_name); + return /(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(friendly) + || /(?:^|[\s._-])питание$/.test(friendly); +} + /** Best recognised actual-work attribute exposed by integrations. Vendor * mode/options such as `current_operation: eco` are not action signals and * must not suppress a climate entity's enabled-mode fallback. */ @@ -216,6 +236,35 @@ export function entityVisualSample(hass: any, eid: string): EntityVisualSample { return base; } +/** + * Classify a resolved device role with the small amount of topology that an + * entity alone cannot provide. A switch-only controller with several peer + * switches and a dedicated Power entity is a powered appliance: its feature + * toggles do not define device activity, Power=on is neutral, and Power=off + * reuses the existing unavailable/faded presentation. A lone relay remains a + * normal working switch. + */ +export function entityVisualSamplesForDevice( + hass: any, + resolvedEids: readonly string[], + allEids: readonly string[], +): EntityVisualSample[] { + const samples = resolvedEids.map((eid) => entityVisualSample(hass, eid)); + const uncategorisedSwitches = allEids.filter((eid) => + eid.startsWith('switch.') && !hass?.entities?.[eid]?.entity_category, + ); + const lifecycle = resolvedEids.length === 1 + && uncategorisedSwitches.length > 1 + && isDevicePowerSwitch(hass, resolvedEids[0]); + if (!lifecycle) return samples; + return samples.map((sample) => { + if (sample.availability === 'unavailable') return sample; + return sample.state === 'off' + ? { ...sample, availability: 'unavailable', status: 'neutral', activity: 'none', edge: 'none' } + : { ...sample, status: 'neutral', activity: 'none', edge: 'none' }; + }); +} + /** Combine the entities that jointly describe one marker. */ export function combineVisualSamples(samples: EntityVisualSample[]): DeviceVisualState { if (!samples.length) return { availability: 'available', status: 'neutral', activity: 'none' }; diff --git a/src/devices.ts b/src/devices.ts index 7004b74e..4e6cec28 100644 --- a/src/devices.ts +++ b/src/devices.ts @@ -4,7 +4,7 @@ */ import { iconFor, iconFromDeviceClasses, DOMAIN_PRIORITY, FALLBACK_ICON, type CompiledIconRule, EXCLUDED_DOMAINS } from './rules'; import { averageLqi, isControllable } from './logic'; -import { isSemanticBinaryEntity } from './device-visual'; +import { isDevicePowerSwitch, isSemanticBinaryEntity } from './device-visual'; import type { DevItem, Marker, ServerConfig } from './types'; /** Build context: a slice of hass + config resolution. */ @@ -85,7 +85,8 @@ const visibleFirst = (items: DeviceEntityCandidate[]): DeviceEntityCandidate[] = * 1) uncategorised HA entities (all entities only as a fallback); * 2) a whole-device state domain, if present; * 3) semantic binary signals (presence/contact/motion/safety/running); - * 4) switches only when the device has no stronger state-bearing role; + * 4) one representative switch when the device has no stronger + * state-bearing role; * 5) passive entities together, so one unavailable sensor does not make the * whole marker unavailable while another reading is alive. */ @@ -106,7 +107,18 @@ export function resolvedDeviceStateEntities(hass: any, entIds: readonly string[] if (semanticBinary.length) return visibleFirst(semanticBinary).map((item) => item.eid); const switches = pool.filter((item) => item.eid.startsWith('switch.')); - if (switches.length) return visibleFirst(switches).map((item) => item.eid); + if (switches.length) { + // A switch-only integration commonly exposes one power relay plus a set + // of feature toggles (night mode, voice enhancement, child lock, etc.). + // HA gives us no device-level state and some third-party integrations do + // not mark those feature entities as `entity_category: config`. Combining + // every switch would therefore mean "any option enabled = device working". + // Prefer a dedicated HA Power entity when metadata identifies one, then + // use the same single representative that primaryEntity/actions use. + // Entity-bound markers remain exact because their list has one member. + const ordered = visibleFirst(switches); + return [(ordered.find((item) => isDevicePowerSwitch(hass, item.eid)) || ordered[0]).eid]; + } // Passive/fallback entities are all useful for aggregate availability. Keep // the historical domain order only as a stable ordering for primaryEntity. diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts index db3c2c94..81e3b0e3 100755 --- a/src/houseplan-card.ts +++ b/src/houseplan-card.ts @@ -104,7 +104,7 @@ import { optimizePlans, type OptimizeReport } from './plan-optimizer'; import { langOf, t, type I18nKey } from './i18n'; import { CommandStack } from './command-stack'; import { - combineVisualSamples, edgeActivity, entityVisualSample, + combineVisualSamples, edgeActivity, entityVisualSample, entityVisualSamplesForDevice, type DeviceActivity, type DeviceVisualState, type EntityVisualSample, } from './device-visual'; import type { DecorShape, DecorStyle } from './editors/decor/types'; @@ -116,7 +116,7 @@ import { type DecorBox, type SnapGeometry, } from './editors/decor/geometry'; -const CARD_VERSION = '1.60.2-beta.1'; +const CARD_VERSION = '1.60.2-beta.2'; /** Keeps every previously valid scale at the maximum 20 cm grid scale lossless. */ const DECOR_TEXT_CM_MAX = 2000; const CELL_CM_MIN = 0.1; @@ -2732,22 +2732,24 @@ class HouseplanCard extends LitElement { * functional source. */ private _visualSamples(d: DevItem): EntityVisualSample[] { - const ids: string[] = []; + let samples: EntityVisualSample[] = []; const cover = this._coverIndicator(d); const lights = resolvedLightSources(this.hass, [d]); - if (cover) ids.push(cover); - else if (lights.length) ids.push(...lights.map((source) => source.eid)); + if (cover) samples.push(entityVisualSample(this.hass, cover)); + else if (lights.length) { + samples.push(...lights.map((source) => entityVisualSample(this.hass, source.eid))); + } else { const stateEntities = resolvedDeviceStateEntities(this.hass, d.entities); - if (stateEntities.length) ids.push(...stateEntities); - else if (d.primary) ids.push(d.primary); + const ids = stateEntities.length ? stateEntities : d.primary ? [d.primary] : []; + samples = entityVisualSamplesForDevice(this.hass, ids, d.entities); } // Safety wins independently of the presentation source. for (const eid of d.entities || []) { const sample = entityVisualSample(this.hass, eid); - if (sample.status === 'alarm' && !ids.includes(eid)) ids.push(eid); + if (sample.status === 'alarm' && !samples.some((item) => item.eid === eid)) samples.push(sample); } - return ids.map((eid) => entityVisualSample(this.hass, eid)); + return samples; } /** One semantic result feeds the plate and every non-critical activity effect. */ @@ -6694,6 +6696,7 @@ class HouseplanCard extends LitElement { private _renderDecorLayer(): TemplateResult { const W = NORM_W, H = this._decorH; const editing = this._mode === 'decor'; + const erasing = editing && this._decorTool === 'erase'; const shapes = this._decorList.map((sh) => { const cls = 'dshape' + (editing && this._decorSel === sh.id ? ' dsel' : ''); const style = this._decorResolvedStyle(sh); @@ -6707,7 +6710,10 @@ class HouseplanCard extends LitElement { return svg``; + @pointerdown=${down} @dblclick=${dbl}> + ${erasing ? svg`` : nothing}`; if (sh.kind === 'rect') { const cx = (sh.x + sh.w / 2) * W, cy = (sh.y + sh.h / 2) * H; const ang = normalizeAngle(sh.angle); @@ -6716,7 +6722,10 @@ class HouseplanCard extends LitElement { stroke="${style.color}" stroke-opacity="${style.opacity}" stroke-width="${strokeWidth}" fill="${style.fill ? style.fillColor : 'none'}" fill-opacity="${style.fill ? style.fillOpacity : 0}" transform=${ang ? `rotate(${ang} ${cx} ${cy})` : nothing} - @pointerdown=${down} @dblclick=${dbl}>`; + @pointerdown=${down} @dblclick=${dbl}> + ${erasing ? svg`` : nothing}`; } if (sh.kind === 'ellipse') { const cx = (sh.x + sh.w / 2) * W, cy = (sh.y + sh.h / 2) * H; @@ -6726,7 +6735,10 @@ class HouseplanCard extends LitElement { rx="${(sh.w / 2) * W}" ry="${(sh.h / 2) * H}" stroke="${style.color}" stroke-opacity="${style.opacity}" stroke-width="${strokeWidth}" fill="${style.fill ? style.fillColor : 'none'}" fill-opacity="${style.fill ? style.fillOpacity : 0}" transform=${ang ? `rotate(${ang} ${cx} ${cy})` : nothing} - @pointerdown=${down} @dblclick=${dbl}>`; + @pointerdown=${down} @dblclick=${dbl}> + ${erasing ? svg`` : nothing}`; } if (sh.kind === 'furniture') { // One path per piece, generated at the shape's REAL size, so the @@ -6744,7 +6756,10 @@ class HouseplanCard extends LitElement { data-kind="${sh.kind}" data-symbol="${sh.symbol}" d="${d}" transform=${tr} stroke="${style.color}" stroke-opacity="${style.opacity}" stroke-width="${strokeWidth}" fill="none" stroke-linecap="round" stroke-linejoin="round" - @pointerdown=${down} @dblclick=${dbl}>`; + @pointerdown=${down} @dblclick=${dbl}> + ${erasing ? svg`` : nothing}`; } if (sh.kind === 'text') { // The label is painted from the LIVE value on every render — the same @@ -8131,16 +8146,10 @@ class HouseplanCard extends LitElement { return this._opMeasure || this._openingPreview?.measure || null; } - /** Save a room with a mandatory binding to an HA area. */ + /** Save a room with an optional HA-area binding. + * An area supplies the fallback name; a room without one needs a name. */ private _saveRoom(): void { - if (!this._areaSel) return; - this._commitRoom(); - } - - /** Save a decorative room without an area (only a name is required). */ - private _saveRoomNoArea(): void { - if (!this._nameSel.trim()) return; - this._areaSel = ''; + if (!this._areaSel && !this._nameSel.trim()) return; this._commitRoom(); } @@ -12780,7 +12789,7 @@ class HouseplanCard extends LitElement { const st = d.primary ? this.hass.states[d.primary] : undefined; const stateTxt = st ? hassValue(this.hass, d.primary)?.text ?? st.state : null; const controls = (d.controls ?? d.marker?.controls ?? []).filter(isControllable); - return html` (this._infoCard = null)}>
${(() => { @@ -12852,8 +12861,7 @@ class HouseplanCard extends LitElement { ${this._t('btn.open_in_ha')} ` : nothing} - - +
`; } @@ -13468,6 +13476,7 @@ class HouseplanCard extends LitElement { private _renderRoomDialog(): TemplateResult { const edit = !!this._roomEditId; + const canSaveNew = !!this._areaSel || !!this._nameSel.trim(); // the free-areas list must include the edited room's CURRENT area const areas = [...this._freeAreas]; if (edit && this._areaSel && !areas.some((a) => a.area_id === this._areaSel)) { @@ -13532,15 +13541,10 @@ class HouseplanCard extends LitElement { ? html`` - : html` - ${!this._pendingSplit ? html`` : nothing} - `}
diff --git a/src/i18n/en.json b/src/i18n/en.json index ab6f32e5..8159a7bd 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -51,7 +51,6 @@ "btn.attach": "Attach…", "btn.upload": "Upload…", "btn.replace": "Replace…", - "btn.no_area": "No area", "title.zoom_in": "Zoom in", "title.zoom_out": "Zoom out", "title.zoom_fit": "Fit all", @@ -65,8 +64,6 @@ "title.markup_split": "Split a room: click the room, then two points on its walls", "title.markup_delroom": "Delete room: click inside a room and confirm", "title.markup_closewall": "Physical wall: click a dashed virtual stretch", - "title.no_area_room": "Decorative room without an HA area (e.g. a hallway)", - "title.choose_area": "Select a Home Assistant area", "title.need_plan": "Upload a floor-plan image", "markup.add": "Room outline", "markup.merge": "Merge", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index d617e13d..a6b9e3ba 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -51,7 +51,6 @@ "btn.attach": "Прикрепить…", "btn.upload": "Загрузить…", "btn.replace": "Заменить…", - "btn.no_area": "Без зоны", "title.zoom_in": "Приблизить", "title.zoom_out": "Отдалить", "title.zoom_fit": "Вписать всё", @@ -65,8 +64,6 @@ "title.markup_split": "Разделить комнату: клик по комнате, затем две точки на её стенах", "title.markup_delroom": "Удалить комнату: кликните внутри комнаты и подтвердите удаление", "title.markup_closewall": "Физическая стена: кликните по пунктирному виртуальному участку", - "title.no_area_room": "Декоративная комната без привязки к зоне (например, холл)", - "title.choose_area": "Выберите зону Home Assistant", "title.need_plan": "Загрузите подложку (план этажа)", "markup.add": "Контур комнаты", "markup.merge": "Объединить", diff --git a/src/styles.ts b/src/styles.ts index 227c4593..af290cac 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -992,6 +992,21 @@ export const cardStyles = css` .stage.mode-decor.dtool-text .decorlayer .dshape.dtext { cursor: text; } + /* Erasing a hairline must not require pixel-perfect aim. The duplicate + geometry is invisible and exists only while Erase is active. A + non-scaling stroke keeps the target comfortably wide at every zoom. */ + .decorlayer .derasehit { + fill: none; + stroke: transparent; + stroke-width: 16px; + stroke-linecap: round; + stroke-linejoin: round; + vector-effect: non-scaling-stroke; + pointer-events: none; + } + .stage.mode-decor.dtool-erase .decorlayer .derasehit { + pointer-events: stroke; + } .decorlayer .dsel { filter: drop-shadow(0 0 3px var(--hp-accent)); } @@ -2515,17 +2530,21 @@ export const cardStyles = css` .editorchrome-inner.nav-enter, .editorchrome-inner.nav-exit { animation: none; } } - /* Device info can have Edit + Open in HA + Close. A small HA dialog is - narrower than those three Russian-labelled actions; without wrapping, - HA's end-aligned footer pushed the first button outside the surface. */ + /* Device info can have Edit + Open in HA + Close. It uses a wide dialog; + wrapping remains as a phone fallback, but without a flex spacer (which + used to strand Edit alone on the first line). */ hp-dialog .row.infofooter { align-items: center; + justify-content: flex-start; flex-wrap: wrap; gap: var(--sp-3); } hp-dialog .row.infofooter .btn { flex-shrink: 0; } + hp-dialog .row.infofooter .infofooter-close { + margin-left: auto; + } @media (max-width: 480px) { hp-dialog .row.infofooter { padding: var(--sp-4) var(--sp-5); diff --git a/test/device-visual.test.mjs b/test/device-visual.test.mjs index 5aa727f8..3f332214 100644 --- a/test/device-visual.test.mjs +++ b/test/device-visual.test.mjs @@ -4,6 +4,7 @@ import { combineVisualSamples, edgeActivity, entityVisualSample, + entityVisualSamplesForDevice, } from '../test-build/device-visual.js'; const hass = (states) => ({ @@ -78,6 +79,35 @@ test('media players express power without treating playback as work', () => { ); }); +test('a composite Power switch is a neutral lifecycle, while a lone relay still works', () => { + const h = { + ...hass({ + 'switch.soundbar_power': { state: 'off', attributes: { friendly_name: 'Soundbar Power' } }, + 'switch.soundbar_pure_voice': { state: 'on' }, + 'switch.relay': { state: 'on' }, + }), + entities: { + 'switch.soundbar_power': {}, + 'switch.soundbar_pure_voice': {}, + 'switch.relay': {}, + }, + }; + const soundbarIds = ['switch.soundbar_power', 'switch.soundbar_pure_voice']; + assert.deepEqual( + combineVisualSamples(entityVisualSamplesForDevice(h, ['switch.soundbar_power'], soundbarIds)), + { availability: 'unavailable', status: 'neutral', activity: 'none' }, + ); + h.states['switch.soundbar_power'].state = 'on'; + assert.deepEqual( + combineVisualSamples(entityVisualSamplesForDevice(h, ['switch.soundbar_power'], soundbarIds)), + { availability: 'available', status: 'neutral', activity: 'none' }, + ); + assert.equal( + combineVisualSamples(entityVisualSamplesForDevice(h, ['switch.relay'], ['switch.relay'])).status, + 'working', + ); +}); + test('off media sources share unavailable styling without hiding an active peer', () => { const off = entityVisualSample(hass({ 'media_player.one': { state: 'off' } }), 'media_player.one'); const on = entityVisualSample(hass({ 'media_player.two': { state: 'on' } }), 'media_player.two'); diff --git a/test/devices.test.mjs b/test/devices.test.mjs index ac68680b..6714def4 100644 --- a/test/devices.test.mjs +++ b/test/devices.test.mjs @@ -346,6 +346,45 @@ test('media role shields a marker from auxiliary light and switch entities', () ); }); +test('switch-only device uses one representative instead of aggregating feature toggles', () => { + const hass = mkHass({ + entities: { + 'switch.soundbar_power': { entity_id: 'switch.soundbar_power' }, + 'switch.soundbar_smart_mode': { entity_id: 'switch.soundbar_smart_mode' }, + 'switch.soundbar_pure_voice': { entity_id: 'switch.soundbar_pure_voice' }, + }, + states: { + 'switch.soundbar_power': { state: 'off', attributes: {} }, + 'switch.soundbar_smart_mode': { state: 'off', attributes: {} }, + 'switch.soundbar_pure_voice': { state: 'on', attributes: {} }, + }, + }); + const entities = [ + 'switch.soundbar_power', + 'switch.soundbar_smart_mode', + 'switch.soundbar_pure_voice', + ]; + assert.deepEqual(resolvedDeviceStateEntities(hass, entities), ['switch.soundbar_power']); + assert.equal(primaryEntity(hass, entities, 'mdi:soundbar'), 'switch.soundbar_power'); +}); + +test('switch-only resolver prefers a dedicated Power entity over registry order', () => { + const hass = mkHass({ + entities: { + 'switch.receiver_night_mode': { entity_id: 'switch.receiver_night_mode' }, + 'switch.receiver_power': { entity_id: 'switch.receiver_power' }, + }, + states: { + 'switch.receiver_night_mode': { state: 'on', attributes: {} }, + 'switch.receiver_power': { state: 'off', attributes: { friendly_name: 'Receiver Power' } }, + }, + }); + assert.deepEqual( + resolvedDeviceStateEntities(hass, ['switch.receiver_night_mode', 'switch.receiver_power']), + ['switch.receiver_power'], + ); +}); + test('resolvedDeviceStateEntities: passive readings aggregate availability instead of picking the first', () => { const hass = mkHass({ entities: {